* Sergei Golubchik <serg@stripped> [09/07/16 00:10]:
> > 2705 Konstantin Osipov 2008-09-12
> > WL#3288 "Foreign Keys: Storage engine API for foreign key support".
> > Prepare ground for standalong storage engine cursor.
> >
> > This patch enforces a strict life cycle of class READ_RECORD.
> > This allows, in future, to add READ_RECORD::handler_cursor,
> > allocate the cursor in init_read_record(), and be sure that
> > the object is destroyed properly in end_read_record().
>
> Could you describe the problem first ?
> What was wrong with READ_RECORD that you were fixing ?
Encapsulation and life cycle.
Initialization of READ_RECORD was not encapsulated. Anyone could
change the read functions in any place in the code, and that was
hard to track.
I introduced an explicit init_read_record() method, which ensures
that all members of READ_RECORD are set, and which everybody
should use to initialize READ_RECORD.
With life cycle, READ_RECORD effectively represents an interface
to access the table handler -- read matching rows from the handler
in a uniform manner.
However, even though one can set different read functions (by
setting read_record.read_record function pointers), there was only
one "cleanup" function, which was access-type agnostic --
end_read_record.
I changed the interface so that the cleanup function depends on
the type of read that is performed.
Additionally, there were no method to "reset" a read between two
iterations of a nested join loop. Instead, a combination of
"end_read_record", "init_read_record" calls was used. Or, no reset
was done at all, the code would just call read_first_record()
without any cleanup. That could lead to storage engine cursors
being left open between two iterations of a nested loop, e.g. when
read_first_record starts a completely different access type (e.g.
changes index scan to a "QUICK").
To solve that problem I introduced reset_read_record() "virtual"
method, and deployed it into the nested loop, so that it's always
called between scans.
To summarize, this patch enforces a strict life cycle that matches
READ_RECORD usage scenarios:
to start using the interface, one calls init_read_record().
then one can do:
[ read_first_record(), [ read_record() ]+, reset_read_record() ]+
And always end using the interface with end_read_record().
The code was changed to follow this cycle in all places and it was
protected with asserts.
PS BTW, now looking at bug#41756, if I had reset_read_record(), I
could always unlock the row that was read by join_read_key(), and
now I can't unlock the last row.
--