mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling: new names for auto_increment variables in THD, new methods to manipulate them (see sql_class.h), some move into handler::, causing less backup/restore work when executing substatements. This makes the logic hopefully clearer, less work is is needed in mysql_insert(). By cleaning up, using different variables for different purposes (instead of one for 3 things...), we fix those bugs, which someone may want to fix in 5.0 too: BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate statement-based" BUG#20341 "stored function inserting into one auto_increment puts bad data in slave" BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE" (now if a row is updated, LAST_INSERT_ID() will return its id) and re-fixes: BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT" (already fixed differently by Ramil in 4.1) Test of documented behaviour of mysql_insert_id() (there was no test). The behaviour changes introduced are: - LAST_INSERT_ID() now returns "the first autogenerated auto_increment value successfully inserted", instead of "the first autogenerated auto_increment value if any row was successfully inserted", see auto_increment.test. Same for mysql_insert_id(), see mysql_client_test.c. - LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY UPDATE, see auto_increment.test. Same for mysql_insert_id(), see mysql_client_test.c. - LAST_INSERT_ID() does not change if no autogenerated value was successfully inserted (it used to then be 0), see auto_increment.test. - if in INSERT SELECT no autogenerated value was successfully inserted, mysql_insert_id() now returns the id of the last inserted row (it already did this for INSERT VALUES), see mysql_client_test.c. - if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X (it already did this for INSERT VALUES), see mysql_client_test.c. - NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE, the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID influences not only the first row now. Additionally, when unlocking a table we check that the thread is not keeping a next_insert_id (as the table is unlocked that id is potentially out-of-date); forgetting about this next_insert_id is done in a new handler::ha_release_auto_increment(). Finally we prepare for engines capable of reserving finite-length intervals of auto_increment values: we store such intervals in THD. The next step (to be done by the replication team in 5.1) is to read those intervals from THD and actually store them in the statement-based binary log. NDB will be a good engine to test that.
This commit is contained in:
@@ -250,3 +250,99 @@ typedef struct user_conn {
|
||||
#define STATUS_UPDATED 16 /* Record is updated by formula */
|
||||
#define STATUS_NULL_ROW 32 /* table->null_row is set */
|
||||
#define STATUS_DELETED 64
|
||||
|
||||
/*
|
||||
Such interval is "discrete": it is the set of
|
||||
{ auto_inc_interval_min + k * increment,
|
||||
0 <= k <= (auto_inc_interval_values-1) }
|
||||
Where "increment" is maintained separately by the user of this class (and is
|
||||
currently only thd->variables.auto_increment_increment).
|
||||
It mustn't derive from Sql_alloc, because SET INSERT_ID needs to
|
||||
allocate memory which must stay allocated for use by the next statement.
|
||||
*/
|
||||
class Discrete_interval {
|
||||
private:
|
||||
ulonglong interval_min;
|
||||
ulonglong interval_values;
|
||||
ulonglong interval_max; // excluded bound. Redundant.
|
||||
public:
|
||||
Discrete_interval *next; // used when linked into Discrete_intervals_list
|
||||
void replace(ulonglong start, ulonglong val, ulonglong incr)
|
||||
{
|
||||
interval_min= start;
|
||||
interval_values= val;
|
||||
interval_max= (val == ULONGLONG_MAX) ? val : start + val * incr;
|
||||
}
|
||||
Discrete_interval(ulonglong start, ulonglong val, ulonglong incr) :
|
||||
next(NULL) { replace(start, val, incr); };
|
||||
Discrete_interval() : next(NULL) { replace(0, 0, 0); };
|
||||
ulonglong minimum() const { return interval_min; };
|
||||
ulonglong values() const { return interval_values; };
|
||||
ulonglong maximum() const { return interval_max; };
|
||||
/*
|
||||
If appending [3,5] to [1,2], we merge both in [1,5] (they should have the
|
||||
same increment for that, user of the class has to ensure that). That is
|
||||
just a space optimization. Returns 0 if merge succeeded.
|
||||
*/
|
||||
bool merge_if_contiguous(ulonglong start, ulonglong val, ulonglong incr)
|
||||
{
|
||||
if (interval_max == start)
|
||||
{
|
||||
if (val == ULONGLONG_MAX)
|
||||
{
|
||||
interval_values= interval_max= val;
|
||||
}
|
||||
else
|
||||
{
|
||||
interval_values+= val;
|
||||
interval_max= start + val * incr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
|
||||
/* List of Discrete_interval objects */
|
||||
class Discrete_intervals_list {
|
||||
private:
|
||||
Discrete_interval *head;
|
||||
Discrete_interval *tail;
|
||||
/*
|
||||
When many intervals are provided at the beginning of the execution of a
|
||||
statement (in a replication slave or SET INSERT_ID), "current" points to
|
||||
the interval being consumed by the thread now (so "current" goes from
|
||||
"head" to "tail" then to NULL).
|
||||
*/
|
||||
Discrete_interval *current;
|
||||
uint elements; // number of elements
|
||||
public:
|
||||
Discrete_intervals_list() : head(NULL), current(NULL), elements(0) {};
|
||||
void empty_no_free()
|
||||
{
|
||||
head= current= NULL;
|
||||
elements= 0;
|
||||
}
|
||||
void empty()
|
||||
{
|
||||
for (Discrete_interval *i= head; i;)
|
||||
{
|
||||
Discrete_interval *next= i->next;
|
||||
delete i;
|
||||
i= next;
|
||||
}
|
||||
empty_no_free();
|
||||
}
|
||||
const Discrete_interval* get_next()
|
||||
{
|
||||
Discrete_interval *tmp= current;
|
||||
if (current != NULL)
|
||||
current= current->next;
|
||||
return tmp;
|
||||
}
|
||||
~Discrete_intervals_list() { empty(); };
|
||||
bool append(ulonglong start, ulonglong val, ulonglong incr);
|
||||
ulonglong minimum() const { return (head ? head->minimum() : 0); };
|
||||
ulonglong maximum() const { return (head ? tail->maximum() : 0); };
|
||||
uint nb_elements() const { return elements; }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user