WL#3288: Multiple cursors per TABLE
Affects: Server-6.1
—
Status: Assigned
MySQL will extend the storage engine API with calls required to support consistent foreign keys in READ COMMITTED isolation mode. Sorrento architecture meeting decision #2 No phantoms in non-SERIALIZABLE mode. In order to avoid phantoms in foreign key constraints in modes different from SERIALIZABLE, the tables that participate in a foreign key constraint as referenced tables will be locked in a special lock mode. A new handler API will be developed in order to lock the specific rows participating in the foreign key check. Support for this API in the handler will be made a requirement for proper work of foreign keys with this handler. Note ---- Originally this task was part of the foreign keys plan. Since it involves a lot of refactoring in the server, and is not strictly necessary (although is very much desirable) to deliver foreign keys, it was removed from the plan and renamed. The replacement task with a more narrow scope is WL#4603.
This following API proposal was agreed on by the Architecture Board in
Orlando January 2008:
/**
Iterate over table records.
Represents an abstract API for different table access methods
(sequential scan, index scan) and selection criteria (all,
primary key, range). Encapsulates forward and backward
scrolling and updatability options.
Forward scrolling is the minimal subset of functionality
that is expected to be supported by all implementations in
all storage engines. If a particular implementation
does not support more advanced features, HA_ERR_WRONG_COMMAND
is returned.
The memory management of Handler_cursor is Sql_alloc based.
The user must supply a memory root whenever a cursor is
created.
*/
class Handler_cursor: public Sql_alloc
{
protected:
/**
Open the cursor and position it on the first record, if any.
Supported by all implementations.
@retval 0 in case of success.
@retval HA_ERR_END_OF_FILE There is no first row, the result set
is empty
*/
virtual int cursor_open()= 0;
/**
Similar to open, but applicable only to a cursor that has been
opened already. Supported by all implementations.
@return 0 if case of success.
@return HA_ERR_END_OF_FILE There is no first row, the result
set is empty.
*/
virtual int cursor_reset()= 0;
/**
Advance cursor position to the next record.
Read the record under the current position into a buffer.
@param[out] buff a buffer to read the record into.
Must be of the appropriate size,
defined by record format in the
table share.
@retval 0 success
@retval HA_ERR_END_OF_FILE This is not an error.
Returned when the cursor position has been
advanced beyond the last row or
on attempt to advance the position further
when it's already beyond the last row.
'buff' has been left intact.
@retval HA_ERR_* A storage engine error.
*/
virtual int cursor_read_next(void *buff)= 0;
/**
Advance cursor position to the previous record.
Read the record under the current position into a buffer.
@param[out] buff a buffer to read the record into.
@retval 0 success
@retval HA_ERR_WRONG_COMMAND The operation is not supported.
@retval HA_ERR_END_OF_FILE This is not an error.
Returned when the cursor position has been
advanced before the first row or
on attempt to advance the position further
when it's already before the first row.
'buff' has been left intact.
@retval HA_ERR_* A storage engine error.
*/
virtual int cursor_read_prev(void *buff)= 0;
/**
Update the record under the current position.
@param[in] buff New record value.
@retval 0 success
@retval HA_ERR_WRONG_COMMAND The operation is not supported.
@retval HA_ERR_END_OF_FILE The cursor does not
point to a record.
@retval HA_ERR_RECORD_IS_THE_SAME No update happened
since the old and new records are the same.
@retval HA_ERR_* A storage engine error.
*/
virtual int positioned_update(const void *buff)= 0;
/**
Delete the record under the current position.
@retval 0 success
@retval HA_ERR_WRONG_COMMAND The operation is not supported.
@retval HA_ERR_END_OF_FILE The cursor does not
point to a record.
@retval HA_ERR_* A storage engine error.
*/
virtual int positioned_delete()= 0;
virtual ~Handler_cursor();
/**
Capabilities flags. Must be initialized by the implementation.
*/
uint32 m_cursor_flags;
public:
enum {
CAN_READ_PREV= 1, /* cursor_read_prev() is supported */
CAN_MODIFY= 2 /* positioned_{update,delete}() are supported */
};
/** Redirect to protected counterparts */
virtual int ha_cursor_open();
virtual int ha_cursor_reset();
virtual int ha_cursor_read_next(void *buff);
virtual int ha_cursor_read_prev(void *buff);
virtual int ha_positioned_update(const void *buff);
virtual int ha_positioned_delete();
};
This API over time will replace the current cursor API present in the PSEA API:
handler::rnd_read, handler::rnd_read, handler::update_row(), handler::delete_row().
For backward compatibility with the old handlers, handler::create_cursor()
will provide a default implementation which will work using handler::this and
handler::clone() - this way existing handlers will continue to work. And
default Handler_cursor will work via handler::index_next/etc.
In order to completely decouple reading and writing functionality from st_table
+ handler pair, a new class will be introduced for reading and writing data
data in table->record format:
/**
Exchange data with the storage engine.
This class represents functionality necessary to encrypt and
decrypt data from PSEA record format (original MyISAM record
format).
It consists of:
- record buffer for the record
- Field array to read and write data from the record.
- auxiliary members to support MySQL semantics of auto_increment,
auto-updatable timestamp and other when writing records to a table.
How to use:
- use in conjunction with Handler_cursor
- create a Handler_cursor and Field_record
- pass the buffer for ::cursor_read_prev() method to read data
and use Fields to encrypt data
- fill the record buffer using the Field_record functionality
and pass the buffer to ::positioned_update method of Handler_cursor
*/
class Field_record: public Sql_alloc
{
Field *field[];
uchar *record;
Field *next_number_field;
Field *timestamp_field;
uchar *default_values;
/* The table for this record */
struct st_table *table;
};
Todo:
- address replication issues:
* should positioned_update() automatically advance the cursor
* read_set shold be a part of the spec - one should be able to scan
using one read set, and read using another
* old image must be available for the binary logging in update and delete
methods
Copyright (c) 2000, 2025, Oracle Corporation and/or its affiliates. All rights reserved.