First push for WL#3146 "less locking in auto_increment". It is a 0-real-change patch.

New prototype for get_auto_increment() (but new arguments not yet used), to be able
to reserve a finite interval of auto_increment values from cooperating engines.
A hint on how many values to reserve is found in handler::estimation_rows_to_insert,
filled by ha_start_bulk_insert(), new wrapper around start_bulk_insert().
NOTE: this patch changes nothing, for all engines. But it makes the API ready for those
engines which will want to do reservation.
More csets will come to complete WL#3146.


sql/ha_berkeley.cc:
  update to new prototype of get_auto_increment
sql/ha_berkeley.h:
  update to new prototype of get_auto_increment
sql/ha_heap.cc:
  update to new prototype of get_auto_increment
sql/ha_heap.h:
  update to new prototype of get_auto_increment
sql/ha_innodb.cc:
  update to new prototype of get_auto_increment
sql/ha_innodb.h:
  update to new prototype of get_auto_increment
sql/ha_myisam.cc:
  update to new prototype of get_auto_increment
sql/ha_myisam.h:
  update to new prototype of get_auto_increment
sql/ha_ndbcluster.cc:
  update to new prototype of get_auto_increment
sql/ha_ndbcluster.h:
  update to new prototype of get_auto_increment
sql/ha_partition.cc:
  update to new prototype of get_auto_increment
sql/ha_partition.h:
  update to new prototype of get_auto_increment
sql/handler.cc:
  new prototype of get_auto_increment, comments, preparation for when the MySQL layer is capable
  of getting finite auto_increment intervals from cooperating engines.
sql/handler.h:
  a wrapper around start_bulk_insert(): ha_bulk_insert(), which stores the argument (number
  of rows expected for insertion) into a member of handler: estimation_rows_to_insert.
  This member will soon be used to decide how much auto_increment values we want to reserve
  from cooperating engines.
  New prototype for get_auto_increment, preparing for cooperating engines to reserve finite auto_increment
  intervals.
  release_auto_increment() will be used by the MySQL layer to inform the engine that it has
  not used all of the interval (engine can reclaim it).
sql/log.cc:
  note for the future
sql/log_event.cc:
  name of wrapper
sql/sql_insert.cc:
  name of wrapper. When done with inserting, return unused auto_inc values to engine.
sql/sql_load.cc:
  name of wrapper. When done with inserting, return unused auto_inc values to engine.
sql/sql_select.cc:
  name of wrapper
sql/sql_table.cc:
  name of wrapper
storage/archive/ha_archive.cc:
  update to new prototype of get_auto_increment.
  Archive reserves only one value (Archive's god - Brian - told me that's the truth).
storage/archive/ha_archive.h:
  update to new prototype of get_auto_increment()
This commit is contained in:
unknown 2006-06-02 22:21:32 +02:00
parent 772758b6ec
commit e63f3779d4
22 changed files with 304 additions and 68 deletions

View File

@ -2255,8 +2255,12 @@ ha_rows ha_berkeley::records_in_range(uint keynr, key_range *start_key,
} }
ulonglong ha_berkeley::get_auto_increment() void ha_berkeley::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{ {
/* Ideally in case of real error (not "empty table") nr should be ~ULL(0) */
ulonglong nr=1; // Default if error or new key ulonglong nr=1; // Default if error or new key
int error; int error;
(void) ha_berkeley::extra(HA_EXTRA_KEYREAD); (void) ha_berkeley::extra(HA_EXTRA_KEYREAD);
@ -2267,9 +2271,18 @@ ulonglong ha_berkeley::get_auto_increment()
if (!table_share->next_number_key_offset) if (!table_share->next_number_key_offset)
{ // Autoincrement at key-start { // Autoincrement at key-start
error=ha_berkeley::index_last(table->record[1]); error=ha_berkeley::index_last(table->record[1]);
/* has taken read lock on page of max key so reserves to infinite */
*nb_reserved_values= ULONGLONG_MAX;
} }
else else
{ {
/*
MySQL needs to call us for next row: assume we are inserting ("a",null)
here, we return 3, and next this statement will want to insert ("b",null):
there is no reason why ("b",3+1) would be the good row to insert: maybe it
already exists, maybe 3+1 is too large...
*/
*nb_reserved_values= 1;
DBT row,old_key; DBT row,old_key;
bzero((char*) &row,sizeof(row)); bzero((char*) &row,sizeof(row));
KEY *key_info= &table->key_info[active_index]; KEY *key_info= &table->key_info[active_index];
@ -2310,7 +2323,7 @@ ulonglong ha_berkeley::get_auto_increment()
table->next_number_field->val_int_offset(table_share->rec_buff_length)+1; table->next_number_field->val_int_offset(table_share->rec_buff_length)+1;
ha_berkeley::index_end(); ha_berkeley::index_end();
(void) ha_berkeley::extra(HA_EXTRA_NO_KEYREAD); (void) ha_berkeley::extra(HA_EXTRA_NO_KEYREAD);
return nr; *first_value= nr;
} }
void ha_berkeley::print_error(int error, myf errflag) void ha_berkeley::print_error(int error, myf errflag)

View File

@ -149,7 +149,10 @@ class ha_berkeley: public handler
int5store(to,share->auto_ident); int5store(to,share->auto_ident);
pthread_mutex_unlock(&share->mutex); pthread_mutex_unlock(&share->mutex);
} }
ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
void print_error(int error, myf errflag); void print_error(int error, myf errflag);
uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; } uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; }
bool primary_key_is_clustered() { return true; } bool primary_key_is_clustered() { return true; }

View File

@ -693,10 +693,15 @@ void ha_heap::update_create_info(HA_CREATE_INFO *create_info)
create_info->auto_increment_value= auto_increment_value; create_info->auto_increment_value= auto_increment_value;
} }
ulonglong ha_heap::get_auto_increment() void ha_heap::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{ {
ha_heap::info(HA_STATUS_AUTO); ha_heap::info(HA_STATUS_AUTO);
return auto_increment_value; *first_value= auto_increment_value;
/* such table has only table-level locking so reserves up to +inf */
*nb_reserved_values= ULONGLONG_MAX;
} }

View File

@ -71,7 +71,10 @@ public:
int write_row(byte * buf); int write_row(byte * buf);
int update_row(const byte * old_data, byte * new_data); int update_row(const byte * old_data, byte * new_data);
int delete_row(const byte * buf); int delete_row(const byte * buf);
ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
int index_read(byte * buf, const byte * key, int index_read(byte * buf, const byte * key,
uint key_len, enum ha_rkey_function find_flag); uint key_len, enum ha_rkey_function find_flag);
int index_read_idx(byte * buf, uint idx, const byte * key, int index_read_idx(byte * buf, uint idx, const byte * key,

View File

@ -6963,17 +6963,21 @@ func_exit_early:
return(error); return(error);
} }
/*********************************************************************** /*******************************************************************************
This function initializes the auto-inc counter if it has not been This function initializes the auto-inc counter if it has not been
initialized yet. This function does not change the value of the auto-inc initialized yet. This function does not change the value of the auto-inc
counter if it already has been initialized. Returns the value of the counter if it already has been initialized. Returns the value of the
auto-inc counter. */ auto-inc counter in *first_value, and ULONGLONG_MAX in *nb_reserved_values (as
we have a table-level lock). offset, increment, nb_desired_values are ignored.
*first_value is set to -1 if error (deadlock or lock wait timeout) */
ulonglong void ha_innobase::get_auto_increment(
ha_innobase::get_auto_increment() /*=================================*/
/*=============================*/ ulonglong offset, /* in */
/* out: auto-increment column value, -1 if error ulonglong increment, /* in */
(deadlock or lock wait timeout) */ ulonglong nb_desired_values, /* in */
ulonglong *first_value, /* out */
ulonglong *nb_reserved_values) /* out */
{ {
longlong nr; longlong nr;
int error; int error;
@ -6988,10 +6992,13 @@ ha_innobase::get_auto_increment()
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
sql_print_error("Error %lu in ::get_auto_increment()", sql_print_error("Error %lu in ::get_auto_increment()",
(ulong) error); (ulong) error);
return(~(ulonglong) 0); *first_value= (~(ulonglong) 0);
return;
} }
return((ulonglong) nr); *first_value= (ulonglong) nr;
/* table-level autoinc lock reserves up to +inf */
*nb_reserved_values= ULONGLONG_MAX;
} }
/* See comment in handler.h */ /* See comment in handler.h */

View File

@ -181,7 +181,10 @@ class ha_innobase: public handler
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type); enum thr_lock_type lock_type);
void init_table_handle_for_HANDLER(); void init_table_handle_for_HANDLER();
ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
int reset_auto_increment(ulonglong value); int reset_auto_increment(ulonglong value);
virtual bool get_error_message(int error, String *buf); virtual bool get_error_message(int error, String *buf);

View File

@ -1690,7 +1690,10 @@ int ha_myisam::rename_table(const char * from, const char * to)
} }
ulonglong ha_myisam::get_auto_increment() void ha_myisam::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{ {
ulonglong nr; ulonglong nr;
int error; int error;
@ -1699,7 +1702,10 @@ ulonglong ha_myisam::get_auto_increment()
if (!table->s->next_number_key_offset) if (!table->s->next_number_key_offset)
{ // Autoincrement at key-start { // Autoincrement at key-start
ha_myisam::info(HA_STATUS_AUTO); ha_myisam::info(HA_STATUS_AUTO);
return auto_increment_value; *first_value= auto_increment_value;
/* MyISAM has only table-level lock, so reserves to +inf */
*nb_reserved_values= ULONGLONG_MAX;
return;
} }
/* it's safe to call the following if bulk_insert isn't on */ /* it's safe to call the following if bulk_insert isn't on */
@ -1720,7 +1726,14 @@ ulonglong ha_myisam::get_auto_increment()
val_int_offset(table->s->rec_buff_length)+1); val_int_offset(table->s->rec_buff_length)+1);
} }
extra(HA_EXTRA_NO_KEYREAD); extra(HA_EXTRA_NO_KEYREAD);
return nr; *first_value= nr;
/*
MySQL needs to call us for next row: assume we are inserting ("a",null)
here, we return 3, and next this statement will want to insert ("b",null):
there is no reason why ("b",3+1) would be the good row to insert: maybe it
already exists, maybe 3+1 is too large...
*/
*nb_reserved_values= 1;
} }

View File

@ -113,7 +113,10 @@ class ha_myisam: public handler
int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type); enum thr_lock_type lock_type);
ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
int rename_table(const char * from, const char * to); int rename_table(const char * from, const char * to);
int delete_table(const char *name); int delete_table(const char *name);
int check(THD* thd, HA_CHECK_OPT* check_opt); int check(THD* thd, HA_CHECK_OPT* check_opt);

View File

@ -5230,7 +5230,10 @@ int ha_ndbcluster::delete_table(const char *name)
} }
ulonglong ha_ndbcluster::get_auto_increment() void ha_ndbcluster::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{ {
int cache_size; int cache_size;
Uint64 auto_value; Uint64 auto_value;
@ -5264,9 +5267,13 @@ ulonglong ha_ndbcluster::get_auto_increment()
const NdbError err= ndb->getNdbError(); const NdbError err= ndb->getNdbError();
sql_print_error("Error %lu in ::get_auto_increment(): %s", sql_print_error("Error %lu in ::get_auto_increment(): %s",
(ulong) err.code, err.message); (ulong) err.code, err.message);
DBUG_RETURN(~(ulonglong) 0); *first_value= ~(ulonglong) 0;
DBUG_VOID_RETURN;
} }
DBUG_RETURN((longlong)auto_value); *first_value= (longlong)auto_value;
/* From the point of view of MySQL, NDB reserves one row at a time */
*nb_reserved_values= 1;
DBUG_VOID_RETURN;
} }

View File

@ -803,7 +803,10 @@ private:
int set_index_key(NdbOperation *, const KEY *key_info, const byte *key_ptr); int set_index_key(NdbOperation *, const KEY *key_info, const byte *key_ptr);
void print_results(); void print_results();
ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
int ndb_err(NdbTransaction*); int ndb_err(NdbTransaction*);
bool uses_blob_value(); bool uses_blob_value();
@ -865,7 +868,7 @@ private:
bool m_primary_key_update; bool m_primary_key_update;
bool m_write_op; bool m_write_op;
bool m_ignore_no_key; bool m_ignore_no_key;
ha_rows m_rows_to_insert; ha_rows m_rows_to_insert; // TODO: merge it with handler::estimation_rows_to_insert?
ha_rows m_rows_inserted; ha_rows m_rows_inserted;
ha_rows m_bulk_insert_rows; ha_rows m_bulk_insert_rows;
ha_rows m_rows_changed; ha_rows m_rows_changed;

View File

@ -2803,7 +2803,7 @@ void ha_partition::start_bulk_insert(ha_rows rows)
file= m_file; file= m_file;
do do
{ {
(*file)->start_bulk_insert(rows); (*file)->ha_start_bulk_insert(rows);
} while (*(++file)); } while (*(++file));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -2830,7 +2830,7 @@ int ha_partition::end_bulk_insert()
do do
{ {
int tmp; int tmp;
if ((tmp= (*file)->end_bulk_insert())) if ((tmp= (*file)->ha_end_bulk_insert()))
error= tmp; error= tmp;
} while (*(++file)); } while (*(++file));
DBUG_RETURN(error); DBUG_RETURN(error);
@ -4155,8 +4155,11 @@ void ha_partition::info(uint flag)
if (flag & HA_STATUS_AUTO) if (flag & HA_STATUS_AUTO)
{ {
ulonglong nb_reserved_values;
DBUG_PRINT("info", ("HA_STATUS_AUTO")); DBUG_PRINT("info", ("HA_STATUS_AUTO"));
auto_increment_value= get_auto_increment(); /* we don't want to reserve any values, it's pure information */
get_auto_increment(0, 0, 0, &auto_increment_value, &nb_reserved_values);
release_auto_increment();
} }
if (flag & HA_STATUS_VARIABLE) if (flag & HA_STATUS_VARIABLE)
{ {
@ -5302,20 +5305,56 @@ void ha_partition::restore_auto_increment()
partitions. partitions.
*/ */
ulonglong ha_partition::get_auto_increment() void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{ {
ulonglong auto_inc, max_auto_inc= 0; ulonglong first_value_part, last_value_part, nb_reserved_values_part,
last_value;
DBUG_ENTER("ha_partition::get_auto_increment"); DBUG_ENTER("ha_partition::get_auto_increment");
*first_value= 0;
last_value= ULONGLONG_MAX;
for (uint i= 0; i < m_tot_parts; i++)
{
m_file[i]->get_auto_increment(offset, increment, nb_desired_values,
&first_value_part, &nb_reserved_values_part);
if (first_value_part == ~(ulonglong)(0)) // error in one partition
{
*first_value= first_value_part;
break;
}
/*
Partition has reserved an interval. Intersect it with the intervals
already reserved for the previous partitions.
*/
last_value_part= (nb_reserved_values_part == ULONGLONG_MAX) ?
ULONGLONG_MAX : (first_value_part + nb_reserved_values_part * increment);
set_if_bigger(*first_value, first_value_part);
set_if_smaller(last_value, last_value_part);
}
if (last_value < *first_value) /* empty intersection, error */
{
*first_value= ~(ulonglong)(0);
}
*nb_reserved_values= (last_value == ULONGLONG_MAX) ?
ULONGLONG_MAX : ((last_value - *first_value) / increment);
DBUG_VOID_RETURN;
}
void ha_partition::release_auto_increment()
{
DBUG_ENTER("ha_partition::release_auto_increment");
for (uint i= 0; i < m_tot_parts; i++) for (uint i= 0; i < m_tot_parts; i++)
{ {
auto_inc= m_file[i]->get_auto_increment(); m_file[i]->release_auto_increment();
set_if_bigger(max_auto_inc, auto_inc);
} }
DBUG_RETURN(max_auto_inc); DBUG_VOID_RETURN;
} }
/**************************************************************************** /****************************************************************************
MODULE initialise handler for HANDLER call MODULE initialise handler for HANDLER call
****************************************************************************/ ****************************************************************************/

View File

@ -815,7 +815,11 @@ public:
------------------------------------------------------------------------- -------------------------------------------------------------------------
*/ */
virtual void restore_auto_increment(); virtual void restore_auto_increment();
virtual ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
virtual void release_auto_increment();
/* /*
------------------------------------------------------------------------- -------------------------------------------------------------------------

View File

@ -1639,7 +1639,7 @@ next_insert_id(ulonglong nr,struct system_variables *variables)
Update the auto_increment field if necessary Update the auto_increment field if necessary
SYNOPSIS SYNOPSIS
update_auto_increment() update_auto_increment()
RETURN RETURN
0 ok 0 ok
@ -1668,8 +1668,9 @@ next_insert_id(ulonglong nr,struct system_variables *variables)
statement. For the following rows we generate new numbers based on the statement. For the following rows we generate new numbers based on the
last used number. last used number.
- thd->next_insert_id != 0. This happens when we have read a statement - thd->next_insert_id != 0. This happens when we have read an Intvar event
from the binary log or when one has used SET LAST_INSERT_ID=#. of type INSERT_ID_EVENT from the binary log or when one has used SET
INSERT_ID=#.
In this case we will set the column to the value of next_insert_id. In this case we will set the column to the value of next_insert_id.
The next row will be given the id The next row will be given the id
@ -1685,8 +1686,20 @@ next_insert_id(ulonglong nr,struct system_variables *variables)
start counting from the inserted value. start counting from the inserted value.
thd->next_insert_id is cleared after it's been used for a statement. thd->next_insert_id is cleared after it's been used for a statement.
TODO
Replace all references to "next number" or NEXT_NUMBER to
"auto_increment", everywhere (see below: there is
table->auto_increment_field_not_null, and there also exists
table->next_number_field, it's not consistent).
*/ */
#define AUTO_INC_DEFAULT_NB_ROWS 1 // Some prefer 1024 here
#define AUTO_INC_DEFAULT_NB_MAX_BITS 16
#define AUTO_INC_DEFAULT_NB_MAX ((1 << AUTO_INC_DEFAULT_NB_MAX_BITS) - 1)
bool handler::update_auto_increment() bool handler::update_auto_increment()
{ {
ulonglong nr; ulonglong nr;
@ -1702,17 +1715,24 @@ bool handler::update_auto_increment()
*/ */
thd->prev_insert_id= thd->next_insert_id; thd->prev_insert_id= thd->next_insert_id;
auto_increment_field_not_null= table->auto_increment_field_not_null; auto_increment_field_not_null= table->auto_increment_field_not_null;
table->auto_increment_field_not_null= FALSE; table->auto_increment_field_not_null= FALSE; // to reset for next row
if ((nr= table->next_number_field->val_int()) != 0 || if ((nr= table->next_number_field->val_int()) != 0 ||
auto_increment_field_not_null && auto_increment_field_not_null &&
thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)
{ {
/* Clear flag for next row */ /*
/* Mark that we didn't generate a new value **/ The user did specify a value for the auto_inc column, we don't generate
a new value, write it down.
*/
auto_increment_column_changed=0; auto_increment_column_changed=0;
/* Update next_insert_id if we have already generated a value */ /*
Update next_insert_id if we had already generated a value in this
statement (case of INSERT VALUES(null),(3763),(null):
the last NULL needs to insert 3764, not the value of the first NULL plus
1).
*/
if (thd->clear_next_insert_id && nr >= thd->next_insert_id) if (thd->clear_next_insert_id && nr >= thd->next_insert_id)
{ {
if (variables->auto_increment_increment != 1) if (variables->auto_increment_increment != 1)
@ -1726,9 +1746,53 @@ bool handler::update_auto_increment()
} }
if (!(nr= thd->next_insert_id)) if (!(nr= thd->next_insert_id))
{ {
if ((nr= get_auto_increment()) == ~(ulonglong) 0) ulonglong nb_desired_values= 1, nb_reserved_values;
#ifdef TO_BE_ENABLED_SOON
/*
Reserved intervals will be stored in "THD::auto_inc_intervals".
handler::estimation_rows_to_insert will be the argument passed by
handler::ha_start_bulk_insert().
*/
uint estimation_known= test(estimation_rows_to_insert > 0);
uint nb_already_reserved_intervals= thd->auto_inc_intervals.nb_elements();
/*
If an estimation was given to the engine:
- use it.
- if we already reserved numbers, it means the estimation was
not accurate, then we'll reserve 2*AUTO_INC_DEFAULT_NB_VALUES the 2nd
time, twice that the 3rd time etc.
If no estimation was given, use those increasing defaults from the
start, starting from AUTO_INC_DEFAULT_NB_VALUES.
Don't go beyond a max to not reserve "way too much" (because reservation
means potentially losing unused values).
*/
if (nb_already_reserved_intervals == 0 && estimation_known)
nb_desired_values= estimation_rows_to_insert;
else /* go with the increasing defaults */
{
/* avoid overflow in formula, with this if() */
if (nb_already_reserved_intervals <= AUTO_INC_DEFAULT_NB_MAX_BITS)
{
nb_desired_values= AUTO_INC_DEFAULT_NB_VALUES *
(1 << nb_already_reserved_intervals);
set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX);
}
else
nb_desired_values= AUTO_INC_DEFAULT_NB_MAX;
}
#endif
/* This call ignores all its parameters but nr, currently */
get_auto_increment(variables->auto_increment_offset,
variables->auto_increment_increment,
nb_desired_values, &nr,
&nb_reserved_values);
if (nr == ~(ulonglong) 0)
result= 1; // Mark failure result= 1; // Mark failure
/*
That should not be needed when engines actually use offset and increment
above.
*/
if (variables->auto_increment_increment != 1) if (variables->auto_increment_increment != 1)
nr= next_insert_id(nr-1, variables); nr= next_insert_id(nr-1, variables);
/* /*
@ -1786,7 +1850,29 @@ void handler::restore_auto_increment()
} }
ulonglong handler::get_auto_increment() /*
Reserves an interval of auto_increment values from the handler.
SYNOPSIS
get_auto_increment()
offset
increment
nb_desired_values how many values we want
first_value (OUT) the first value reserved by the handler
nb_reserved_values (OUT) how many values the handler reserved
offset and increment means that we want values to be of the form
offset + N * increment, where N>=0 is integer.
If the function sets *first_value to ~(ulonglong)0 it means an error.
If the function sets *nb_reserved_values to ULONGLONG_MAX it means it has
reserved to "positive infinite".
*/
void handler::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{ {
ulonglong nr; ulonglong nr;
int error; int error;
@ -1796,6 +1882,12 @@ ulonglong handler::get_auto_increment()
if (!table->s->next_number_key_offset) if (!table->s->next_number_key_offset)
{ // Autoincrement at key-start { // Autoincrement at key-start
error=index_last(table->record[1]); error=index_last(table->record[1]);
/*
MySQL implicitely assumes such method does locking (as MySQL decides to
use nr+increment without checking again with the handler, in
handler::update_auto_increment()), so reserves to infinite.
*/
*nb_reserved_values= ULONGLONG_MAX;
} }
else else
{ {
@ -1805,6 +1897,13 @@ ulonglong handler::get_auto_increment()
table->s->next_number_key_offset); table->s->next_number_key_offset);
error= index_read(table->record[1], key, table->s->next_number_key_offset, error= index_read(table->record[1], key, table->s->next_number_key_offset,
HA_READ_PREFIX_LAST); HA_READ_PREFIX_LAST);
/*
MySQL needs to call us for next row: assume we are inserting ("a",null)
here, we return 3, and next this statement will want to insert
("b",null): there is no reason why ("b",3+1) would be the good row to
insert: maybe it already exists, maybe 3+1 is too large...
*/
*nb_reserved_values= 1;
} }
if (error) if (error)
@ -1814,7 +1913,7 @@ ulonglong handler::get_auto_increment()
val_int_offset(table->s->rec_buff_length)+1); val_int_offset(table->s->rec_buff_length)+1);
index_end(); index_end();
(void) extra(HA_EXTRA_NO_KEYREAD); (void) extra(HA_EXTRA_NO_KEYREAD);
return nr; *first_value= nr;
} }

View File

@ -777,6 +777,9 @@ class handler :public Sql_alloc
private: private:
virtual int reset() { return extra(HA_EXTRA_RESET); } virtual int reset() { return extra(HA_EXTRA_RESET); }
ha_rows estimation_rows_to_insert;
virtual void start_bulk_insert(ha_rows rows) {}
virtual int end_bulk_insert() {return 0; }
public: public:
const handlerton *ht; /* storage engine of this handler */ const handlerton *ht; /* storage engine of this handler */
byte *ref; /* Pointer to current row */ byte *ref; /* Pointer to current row */
@ -821,7 +824,7 @@ public:
MY_BITMAP *write_set; MY_BITMAP *write_set;
handler(const handlerton *ht_arg, TABLE_SHARE *share_arg) handler(const handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), ht(ht_arg), :table_share(share_arg), estimation_rows_to_insert(0), ht(ht_arg),
ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0), ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0),
delete_length(0), auto_increment_value(0), delete_length(0), auto_increment_value(0),
records(0), deleted(0), mean_rec_length(0), records(0), deleted(0), mean_rec_length(0),
@ -1242,7 +1245,11 @@ public:
*/ */
virtual int delete_all_rows() virtual int delete_all_rows()
{ return (my_errno=HA_ERR_WRONG_COMMAND); } { return (my_errno=HA_ERR_WRONG_COMMAND); }
virtual ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
virtual void release_auto_increment() { return; };
virtual void restore_auto_increment(); virtual void restore_auto_increment();
/* /*
@ -1303,8 +1310,16 @@ public:
virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int indexes_are_disabled(void) {return 0;} virtual int indexes_are_disabled(void) {return 0;}
virtual void start_bulk_insert(ha_rows rows) {} void ha_start_bulk_insert(ha_rows rows)
virtual int end_bulk_insert() {return 0; } {
estimation_rows_to_insert= rows;
start_bulk_insert(rows);
}
int ha_end_bulk_insert()
{
estimation_rows_to_insert= 0;
return end_bulk_insert();
}
virtual int discard_or_import_tablespace(my_bool discard) virtual int discard_or_import_tablespace(my_bool discard)
{return HA_ERR_WRONG_COMMAND;} {return HA_ERR_WRONG_COMMAND;}
virtual int net_read_dump(NET* net) { return HA_ERR_WRONG_COMMAND; } virtual int net_read_dump(NET* net) { return HA_ERR_WRONG_COMMAND; }

View File

@ -2946,6 +2946,11 @@ bool MYSQL_LOG::write(Log_event *event_info)
} }
if (thd->insert_id_used) if (thd->insert_id_used)
{ {
/*
If the auto_increment was second in a table's index (possible with
MyISAM or BDB) (table->next_number_key_offset != 0), such event is
in fact not necessary. We could avoid logging it.
*/
Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id); Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id);
if (e.write(file)) if (e.write(file))
goto err; goto err;

View File

@ -6142,7 +6142,7 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table)
how many rows are going to be inserted, then it can allocate needed memory how many rows are going to be inserted, then it can allocate needed memory
from the start. from the start.
*/ */
table->file->start_bulk_insert(0); table->file->ha_start_bulk_insert(0);
/* /*
We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
any TIMESTAMP column with data from the row but instead will use any TIMESTAMP column with data from the row but instead will use
@ -6165,7 +6165,7 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table)
int Write_rows_log_event::do_after_row_operations(TABLE *table, int error) int Write_rows_log_event::do_after_row_operations(TABLE *table, int error)
{ {
if (error == 0) if (error == 0)
error= table->file->end_bulk_insert(); error= table->file->ha_end_bulk_insert();
return error; return error;
} }

View File

@ -428,7 +428,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
the code to make the call of end_bulk_insert() below safe. the code to make the call of end_bulk_insert() below safe.
*/ */
if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode) if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode)
table->file->start_bulk_insert(values_list.elements); table->file->ha_start_bulk_insert(values_list.elements);
thd->no_trans_update= 0; thd->no_trans_update= 0;
thd->abort_on_warning= (!ignore && thd->abort_on_warning= (!ignore &&
@ -553,7 +553,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
else else
#endif #endif
{ {
if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error) if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
{ {
table->file->print_error(my_errno,MYF(0)); table->file->print_error(my_errno,MYF(0));
error=1; error=1;
@ -644,6 +644,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->row_count_func= info.copied+info.deleted+info.updated; thd->row_count_func= info.copied+info.deleted+info.updated;
::send_ok(thd, (ulong) thd->row_count_func, id, buff); ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
} }
if (table != NULL)
table->file->release_auto_increment();
thd->abort_on_warning= 0; thd->abort_on_warning= 0;
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
@ -652,6 +654,8 @@ abort:
if (lock_type == TL_WRITE_DELAYED) if (lock_type == TL_WRITE_DELAYED)
end_delayed_insert(thd); end_delayed_insert(thd);
#endif #endif
if (table != NULL)
table->file->release_auto_increment();
if (!joins_freed) if (!joins_freed)
free_underlaid_joins(thd, &thd->lex->select_lex); free_underlaid_joins(thd, &thd->lex->select_lex);
thd->abort_on_warning= 0; thd->abort_on_warning= 0;
@ -971,7 +975,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
uint key_nr; uint key_nr;
if (error != HA_WRITE_SKIP) if (error != HA_WRITE_SKIP)
goto err; goto err;
table->file->restore_auto_increment(); table->file->restore_auto_increment(); // it's too early here! BUG#20188
if ((int) (key_nr = table->file->get_dup_key(error)) < 0) if ((int) (key_nr = table->file->get_dup_key(error)) < 0)
{ {
error=HA_WRITE_SKIP; /* Database can't find key */ error=HA_WRITE_SKIP; /* Database can't find key */
@ -2248,7 +2252,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
We won't start bulk inserts at all if this statement uses functions or We won't start bulk inserts at all if this statement uses functions or
should invoke triggers since they may access to the same table too. should invoke triggers since they may access to the same table too.
*/ */
table->file->start_bulk_insert((ha_rows) 0); table->file->ha_start_bulk_insert((ha_rows) 0);
} }
restore_record(table,s->default_values); // Get empty record restore_record(table,s->default_values); // Get empty record
table->next_number_field=table->found_next_number_field; table->next_number_field=table->found_next_number_field;
@ -2299,7 +2303,7 @@ int select_insert::prepare2(void)
DBUG_ENTER("select_insert::prepare2"); DBUG_ENTER("select_insert::prepare2");
if (thd->lex->current_select->options & OPTION_BUFFER_RESULT && if (thd->lex->current_select->options & OPTION_BUFFER_RESULT &&
!thd->prelocked_mode) !thd->prelocked_mode)
table->file->start_bulk_insert((ha_rows) 0); table->file->ha_start_bulk_insert((ha_rows) 0);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@ -2373,6 +2377,7 @@ bool select_insert::send_data(List<Item> &values)
last_insert_id= thd->insert_id(); last_insert_id= thd->insert_id();
} }
} }
table->file->release_auto_increment();
DBUG_RETURN(error); DBUG_RETURN(error);
} }
@ -2402,7 +2407,7 @@ void select_insert::send_error(uint errcode,const char *err)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
if (!thd->prelocked_mode) if (!thd->prelocked_mode)
table->file->end_bulk_insert(); table->file->ha_end_bulk_insert();
/* /*
If at least one row has been inserted/modified and will stay in the table If at least one row has been inserted/modified and will stay in the table
(the table doesn't have transactions) we must write to the binlog (and (the table doesn't have transactions) we must write to the binlog (and
@ -2458,7 +2463,7 @@ bool select_insert::send_eof()
int error,error2; int error,error2;
DBUG_ENTER("select_insert::send_eof"); DBUG_ENTER("select_insert::send_eof");
error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0; error= (!thd->prelocked_mode) ? table->file->ha_end_bulk_insert():0;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
/* /*
@ -2724,7 +2729,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (info.ignore || info.handle_duplicates != DUP_ERROR) if (info.ignore || info.handle_duplicates != DUP_ERROR)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
if (!thd->prelocked_mode) if (!thd->prelocked_mode)
table->file->start_bulk_insert((ha_rows) 0); table->file->ha_start_bulk_insert((ha_rows) 0);
thd->no_trans_update= 0; thd->no_trans_update= 0;
thd->abort_on_warning= (!info.ignore && thd->abort_on_warning= (!info.ignore &&
(thd->variables.sql_mode & (thd->variables.sql_mode &

View File

@ -117,7 +117,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
{ {
char name[FN_REFLEN]; char name[FN_REFLEN];
File file; File file;
TABLE *table; TABLE *table= NULL;
int error; int error;
String *field_term=ex->field_term,*escaped=ex->escaped; String *field_term=ex->field_term,*escaped=ex->escaped;
String *enclosed=ex->enclosed; String *enclosed=ex->enclosed;
@ -366,7 +366,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
handle_duplicates == DUP_REPLACE) handle_duplicates == DUP_REPLACE)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
if (!thd->prelocked_mode) if (!thd->prelocked_mode)
table->file->start_bulk_insert((ha_rows) 0); table->file->ha_start_bulk_insert((ha_rows) 0);
table->copy_blobs=1; table->copy_blobs=1;
thd->no_trans_update= 0; thd->no_trans_update= 0;
@ -383,7 +383,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
error= read_sep_field(thd, info, table_list, fields_vars, error= read_sep_field(thd, info, table_list, fields_vars,
set_fields, set_values, read_info, set_fields, set_values, read_info,
*enclosed, skip_lines, ignore); *enclosed, skip_lines, ignore);
if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error) if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
{ {
table->file->print_error(my_errno, MYF(0)); table->file->print_error(my_errno, MYF(0));
error= 1; error= 1;
@ -505,6 +505,8 @@ err:
mysql_unlock_tables(thd, thd->lock); mysql_unlock_tables(thd, thd->lock);
thd->lock=0; thd->lock=0;
} }
if (table != NULL)
table->file->release_auto_increment();
thd->abort_on_warning= 0; thd->abort_on_warning= 0;
DBUG_RETURN(error); DBUG_RETURN(error);
} }

View File

@ -9298,7 +9298,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
all places where a corresponding end_bulk_insert() should be put. all places where a corresponding end_bulk_insert() should be put.
*/ */
table->file->info(HA_STATUS_VARIABLE); /* update table->file->records */ table->file->info(HA_STATUS_VARIABLE); /* update table->file->records */
new_table.file->start_bulk_insert(table->file->records); new_table.file->ha_start_bulk_insert(table->file->records);
#else #else
/* HA_EXTRA_WRITE_CACHE can stay until close, no need to disable it */ /* HA_EXTRA_WRITE_CACHE can stay until close, no need to disable it */
new_table.file->extra(HA_EXTRA_WRITE_CACHE); new_table.file->extra(HA_EXTRA_WRITE_CACHE);

View File

@ -6134,7 +6134,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
MODE_STRICT_ALL_TABLES)); MODE_STRICT_ALL_TABLES));
from->file->info(HA_STATUS_VARIABLE); from->file->info(HA_STATUS_VARIABLE);
to->file->start_bulk_insert(from->file->records); to->file->ha_start_bulk_insert(from->file->records);
save_sql_mode= thd->variables.sql_mode; save_sql_mode= thd->variables.sql_mode;
@ -6254,7 +6254,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
free_io_cache(from); free_io_cache(from);
delete [] copy; // This is never 0 delete [] copy; // This is never 0
if (to->file->end_bulk_insert() && error <= 0) if (to->file->ha_end_bulk_insert() && error <= 0)
{ {
to->file->print_error(my_errno,MYF(0)); to->file->print_error(my_errno,MYF(0));
error=1; error=1;

View File

@ -946,9 +946,13 @@ error:
} }
ulonglong ha_archive::get_auto_increment() void ha_archive::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values)
{ {
return share->auto_increment_value + 1; *nb_reserved_values= 1;
*first_value= share->auto_increment_value + 1;
} }
/* Initialized at each key walk (called multiple times unlike rnd_init()) */ /* Initialized at each key walk (called multiple times unlike rnd_init()) */

View File

@ -83,7 +83,10 @@ public:
{ {
return HA_ONLY_WHOLE_INDEX; return HA_ONLY_WHOLE_INDEX;
} }
ulonglong get_auto_increment(); virtual void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
uint max_supported_keys() const { return 1; } uint max_supported_keys() const { return 1; }
uint max_supported_key_length() const { return sizeof(ulonglong); } uint max_supported_key_length() const { return sizeof(ulonglong); }
uint max_supported_key_part_length() const { return sizeof(ulonglong); } uint max_supported_key_part_length() const { return sizeof(ulonglong); }