Apply InnoDB snapshot innodb-5.1-ss1726.

Bug #16979: AUTO_INC lock in InnoDB works a table level lock
  - this is a major change in InnoDB auto-inc handling.
Bug #27950: Duplicate entry error in auto-inc after mysqld restart
  - Init AUTOINC from delete_row().
Bug #28781: InnoDB increments auto-increment value incorrectly with ON DUPLICATE KEY UPDATE
  - Use value specified by MySQL, in update_row().
parent 7f429ecc
......@@ -501,7 +501,7 @@ ERROR 23000: Duplicate entry 'test2' for key 'ggid'
select * from t1;
id ggid email passwd
1 this will work
3 test2 this will work
4 test2 this will work
select * from t1 where id=1;
id ggid email passwd
1 this will work
......
......@@ -410,14 +410,27 @@ dict_table_get_col_name(
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
s = table->col_names;
for (i = 0; i < col_nr; i++) {
s += strlen(s) + 1;
if (s) {
for (i = 0; i < col_nr; i++) {
s += strlen(s) + 1;
}
}
return(s);
}
/************************************************************************
Acquire the autoinc lock.*/
void
dict_table_autoinc_lock(
/*====================*/
dict_table_t* table)
{
mutex_enter(&table->autoinc_mutex);
}
/************************************************************************
Initializes the autoinc counter. It is not an error to initialize an already
initialized counter. */
......@@ -428,54 +441,8 @@ dict_table_autoinc_initialize(
dict_table_t* table, /* in: table */
ib_longlong value) /* in: next value to assign to a row */
{
mutex_enter(&(table->autoinc_mutex));
table->autoinc_inited = TRUE;
table->autoinc = value;
mutex_exit(&(table->autoinc_mutex));
}
/************************************************************************
Gets the next autoinc value (== autoinc counter value), 0 if not yet
initialized. If initialized, increments the counter by 1. */
ib_longlong
dict_table_autoinc_get(
/*===================*/
/* out: value for a new row, or 0 */
dict_table_t* table) /* in: table */
{
ib_longlong value;
mutex_enter(&(table->autoinc_mutex));
if (!table->autoinc_inited) {
value = 0;
} else {
value = table->autoinc;
table->autoinc = table->autoinc + 1;
}
mutex_exit(&(table->autoinc_mutex));
return(value);
}
/************************************************************************
Decrements the autoinc counter value by 1. */
void
dict_table_autoinc_decrement(
/*=========================*/
dict_table_t* table) /* in: table */
{
mutex_enter(&(table->autoinc_mutex));
table->autoinc = table->autoinc - 1;
mutex_exit(&(table->autoinc_mutex));
}
/************************************************************************
......@@ -490,32 +457,6 @@ dict_table_autoinc_read(
{
ib_longlong value;
mutex_enter(&(table->autoinc_mutex));
if (!table->autoinc_inited) {
value = 0;
} else {
value = table->autoinc;
}
mutex_exit(&(table->autoinc_mutex));
return(value);
}
/************************************************************************
Peeks the autoinc counter value, 0 if not yet initialized. Does not
increment the counter. The read not protected by any mutex! */
ib_longlong
dict_table_autoinc_peek(
/*====================*/
/* out: value of the counter */
dict_table_t* table) /* in: table */
{
ib_longlong value;
if (!table->autoinc_inited) {
value = 0;
......@@ -527,7 +468,7 @@ dict_table_autoinc_peek(
}
/************************************************************************
Updates the autoinc counter if the value supplied is equal or bigger than the
Updates the autoinc counter if the value supplied is greater than the
current value. If not inited, does nothing. */
void
......@@ -537,15 +478,21 @@ dict_table_autoinc_update(
dict_table_t* table, /* in: table */
ib_longlong value) /* in: value which was assigned to a row */
{
mutex_enter(&(table->autoinc_mutex));
if (table->autoinc_inited && value > table->autoinc) {
if (table->autoinc_inited) {
if (value >= table->autoinc) {
table->autoinc = value + 1;
}
table->autoinc = value;
}
}
mutex_exit(&(table->autoinc_mutex));
/************************************************************************
Release the autoinc lock.*/
void
dict_table_autoinc_unlock(
/*======================*/
dict_table_t* table) /* in: release autoinc lock for this table */
{
mutex_exit(&table->autoinc_mutex);
}
/************************************************************************
......@@ -842,28 +789,18 @@ dict_table_get(
}
/**************************************************************************
Adds a table object to the dictionary cache. */
Adds system columns to a table object. */
void
dict_table_add_to_cache(
/*====================*/
dict_table_t* table) /* in: table */
dict_table_add_system_columns(
/*==========================*/
dict_table_t* table, /* in/out: table */
mem_heap_t* heap) /* in: temporary heap */
{
ulint fold;
ulint id_fold;
ulint i;
ulint row_len;
ut_ad(table);
ut_ad(mutex_own(&(dict_sys->mutex)));
ut_ad(table->n_def == table->n_cols - DATA_N_SYS_COLS);
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
ut_ad(table->cached == FALSE);
fold = ut_fold_string(table->name);
id_fold = ut_fold_dulint(table->id);
table->cached = TRUE;
ut_ad(!table->cached);
/* NOTE: the system columns MUST be added in the following order
(so that they can be indexed by the numerical value of DATA_ROW_ID,
......@@ -871,19 +808,19 @@ dict_table_add_to_cache(
The clustered index will not always physically contain all
system columns. */
dict_mem_table_add_col(table, "DB_ROW_ID", DATA_SYS,
dict_mem_table_add_col(table, heap, "DB_ROW_ID", DATA_SYS,
DATA_ROW_ID | DATA_NOT_NULL,
DATA_ROW_ID_LEN);
#if DATA_ROW_ID != 0
#error "DATA_ROW_ID != 0"
#endif
dict_mem_table_add_col(table, "DB_TRX_ID", DATA_SYS,
dict_mem_table_add_col(table, heap, "DB_TRX_ID", DATA_SYS,
DATA_TRX_ID | DATA_NOT_NULL,
DATA_TRX_ID_LEN);
#if DATA_TRX_ID != 1
#error "DATA_TRX_ID != 1"
#endif
dict_mem_table_add_col(table, "DB_ROLL_PTR", DATA_SYS,
dict_mem_table_add_col(table, heap, "DB_ROLL_PTR", DATA_SYS,
DATA_ROLL_PTR | DATA_NOT_NULL,
DATA_ROLL_PTR_LEN);
#if DATA_ROLL_PTR != 2
......@@ -895,10 +832,34 @@ dict_table_add_to_cache(
#if DATA_N_SYS_COLS != 3
#error "DATA_N_SYS_COLS != 3"
#endif
}
/**************************************************************************
Adds a table object to the dictionary cache. */
void
dict_table_add_to_cache(
/*====================*/
dict_table_t* table, /* in: table */
mem_heap_t* heap) /* in: temporary heap */
{
ulint fold;
ulint id_fold;
ulint i;
ulint row_len;
/* The lower limit for what we consider a "big" row */
#define BIG_ROW_SIZE 1024
ut_ad(mutex_own(&(dict_sys->mutex)));
dict_table_add_system_columns(table, heap);
table->cached = TRUE;
fold = ut_fold_string(table->name);
id_fold = ut_fold_dulint(table->id);
row_len = 0;
for (i = 0; i < table->n_def; i++) {
ulint col_len = dict_col_get_max_size(
......
......@@ -90,6 +90,11 @@ dict_mem_table_create(
mutex_create(&table->autoinc_mutex, SYNC_DICT_AUTOINC_MUTEX);
table->autoinc_inited = FALSE;
/* The actual increment value will be set by MySQL, we simply
default to 1 here.*/
table->autoinc_increment = 1;
#ifdef UNIV_DEBUG
table->magic_n = DICT_TABLE_MAGIC_N;
#endif /* UNIV_DEBUG */
......@@ -108,18 +113,11 @@ dict_mem_table_free(
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
mutex_free(&(table->autoinc_mutex));
if (table->col_names && (table->n_def < table->n_cols)) {
ut_free((void*)table->col_names);
}
mem_heap_free(table->heap);
}
/********************************************************************
Add 'name' to end of the col_names array (see dict_table_t::col_names). Call
ut_free on col_names (if not NULL), allocate new array (if heap, from it,
otherwise with ut_malloc), and copy col_names + name to it. */
Append 'name' to 'col_names' (@see dict_table_t::col_names). */
static
const char*
dict_add_col_name(
......@@ -129,21 +127,19 @@ dict_add_col_name(
NULL */
ulint cols, /* in: number of existing columns */
const char* name, /* in: new column name */
mem_heap_t* heap) /* in: heap, or NULL */
mem_heap_t* heap) /* in: heap */
{
ulint i;
ulint old_len;
ulint new_len;
ulint total_len;
const char* s;
char* res;
ulint old_len;
ulint new_len;
ulint total_len;
char* res;
ut_a(((cols == 0) && !col_names) || ((cols > 0) && col_names));
ut_a(*name);
ut_ad(!cols == !col_names);
/* Find out length of existing array. */
if (col_names) {
s = col_names;
const char* s = col_names;
ulint i;
for (i = 0; i < cols; i++) {
s += strlen(s) + 1;
......@@ -157,11 +153,7 @@ dict_add_col_name(
new_len = strlen(name) + 1;
total_len = old_len + new_len;
if (heap) {
res = mem_heap_alloc(heap, total_len);
} else {
res = ut_malloc(total_len);
}
res = mem_heap_alloc(heap, total_len);
if (old_len > 0) {
memcpy(res, col_names, old_len);
......@@ -169,10 +161,6 @@ dict_add_col_name(
memcpy(res + old_len, name, new_len);
if (col_names) {
ut_free((char*)col_names);
}
return(res);
}
......@@ -183,7 +171,8 @@ void
dict_mem_table_add_col(
/*===================*/
dict_table_t* table, /* in: table */
const char* name, /* in: column name */
mem_heap_t* heap, /* in: temporary memory heap, or NULL */
const char* name, /* in: column name, or NULL */
ulint mtype, /* in: main datatype */
ulint prtype, /* in: precise type */
ulint len) /* in: precision */
......@@ -191,21 +180,32 @@ dict_mem_table_add_col(
dict_col_t* col;
ulint mbminlen;
ulint mbmaxlen;
mem_heap_t* heap;
ulint i;
ut_ad(table && name);
ut_ad(table);
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
ut_ad(!heap == !name);
table->n_def++;
i = table->n_def++;
heap = table->n_def < table->n_cols ? NULL : table->heap;
table->col_names = dict_add_col_name(table->col_names,
table->n_def - 1,
name, heap);
if (name) {
if (UNIV_UNLIKELY(table->n_def == table->n_cols)) {
heap = table->heap;
}
if (UNIV_LIKELY(i) && UNIV_UNLIKELY(!table->col_names)) {
/* All preceding column names are empty. */
char* s = mem_heap_alloc(heap, table->n_def);
memset(s, 0, table->n_def);
table->col_names = s;
}
table->col_names = dict_add_col_name(table->col_names,
i, name, heap);
}
col = (dict_col_t*) dict_table_get_nth_col(table, table->n_def - 1);
col = (dict_col_t*) dict_table_get_nth_col(table, i);
col->ind = table->n_def - 1;
col->ind = i;
col->ord_part = 0;
col->mtype = (unsigned int) mtype;
......@@ -318,7 +318,7 @@ dict_mem_index_add_field(
{
dict_field_t* field;
ut_ad(index && name);
ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
index->n_def++;
......
......@@ -62,12 +62,6 @@ static bool innodb_inited = 0;
*/
static handlerton *innodb_hton_ptr;
/* Store MySQL definition of 'byte': in Linux it is char while InnoDB
uses unsigned char; the header univ.i which we include next defines
'byte' as a macro which expands to 'unsigned char' */
typedef uchar mysql_byte;
#define INSIDE_HA_INNOBASE_CC
/* Include necessary InnoDB headers */
......@@ -148,7 +142,7 @@ static HASH innobase_open_tables;
bool nw_panic = FALSE;
#endif
static mysql_byte* innobase_get_key(INNOBASE_SHARE *share, size_t *length,
static uchar* innobase_get_key(INNOBASE_SHARE *share, size_t *length,
my_bool not_used __attribute__((unused)));
static INNOBASE_SHARE *get_share(const char *table_name);
static void free_share(INNOBASE_SHARE *share);
......@@ -1919,6 +1913,8 @@ innobase_commit(
trx_mark_sql_stat_end(trx);
}
trx->n_autoinc_rows = 0; /* Reset the number AUTO-INC rows required */
if (trx->declared_to_be_inside_innodb) {
/* Release our possible ticket in the FIFO */
......@@ -2415,7 +2411,7 @@ ha_innobase::open(
upd_and_key_val_buff_len =
table->s->reclength + table->s->max_key_length
+ MAX_REF_PARTS * 3;
if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
if (!(uchar*) my_multi_malloc(MYF(MY_WME),
&upd_buff, upd_and_key_val_buff_len,
&key_val_buff, upd_and_key_val_buff_len,
NullS)) {
......@@ -2849,8 +2845,8 @@ inline
uint
innobase_read_from_2_little_endian(
/*===============================*/
/* out: value */
const mysql_byte* buf) /* in: from where to read */
/* out: value */
const uchar* buf) /* in: from where to read */
{
return (uint) ((ulint)(buf[0]) + 256 * ((ulint)(buf[1])));
}
......@@ -2866,7 +2862,7 @@ ha_innobase::store_key_val_for_row(
char* buff, /* in/out: buffer for the key value (in MySQL
format) */
uint buff_len,/* in: buffer length */
const mysql_byte* record)/* in: row in MySQL format */
const uchar* record)/* in: row in MySQL format */
{
KEY* key_info = table->key_info + keynr;
KEY_PART_INFO* key_part = key_info->key_part;
......@@ -3063,7 +3059,7 @@ ha_innobase::store_key_val_for_row(
CHARSET_INFO* cs;
ulint true_len;
ulint key_len;
const mysql_byte* src_start;
const uchar* src_start;
int error=0;
enum_field_types real_type;
......@@ -3342,6 +3338,93 @@ build_template(
}
}
/************************************************************************
This special handling is really to overcome the limitations of MySQL's
binlogging. We need to eliminate the non-determinism that will arise in
INSERT ... SELECT type of statements, since MySQL binlog only stores the
min value of the autoinc interval. Once that is fixed we can get rid of
the special lock handling.*/
ulong
ha_innobase::innobase_autoinc_lock(void)
/*====================================*/
/* out: DB_SUCCESS if all OK else
error code */
{
ulint error = DB_SUCCESS;
if (thd_sql_command(user_thd) == SQLCOM_INSERT) {
dict_table_autoinc_lock(prebuilt->table);
/* We peek at the dict_table_t::auto_inc_lock to check if
another statement has locked it */
if (prebuilt->trx->auto_inc_lock != NULL) {
/* Release the mutex to avoid deadlocks */
dict_table_autoinc_unlock(prebuilt->table);
goto acquire_auto_inc_lock;
}
} else {
acquire_auto_inc_lock:
error = row_lock_table_autoinc_for_mysql(prebuilt);
if (error == DB_SUCCESS) {
dict_table_autoinc_lock(prebuilt->table);
}
}
return(ulong(error));
}
/************************************************************************
Reset the autoinc value in the table.*/
ulong
ha_innobase::innobase_reset_autoinc(
/*================================*/
/* out: DB_SUCCESS if all went well
else error code */
ulonglong autoinc) /* in: value to store */
{
ulint error;
error = innobase_autoinc_lock();
if (error == DB_SUCCESS) {
dict_table_autoinc_initialize(prebuilt->table, autoinc);
dict_table_autoinc_unlock(prebuilt->table);
}
return(ulong(error));
}
/************************************************************************
Store the autoinc value in the table. The autoinc value is only set if
it's greater than the existing autoinc value in the table.*/
ulong
ha_innobase::innobase_set_max_autoinc(
/*==================================*/
/* out: DB_SUCCES if all went well
else error code */
ulonglong auto_inc) /* in: value to store */
{
ulint error;
error = innobase_autoinc_lock();
if (error == DB_SUCCESS) {
dict_table_autoinc_update(prebuilt->table, auto_inc);
dict_table_autoinc_unlock(prebuilt->table);
}
return(ulong(error));
}
/************************************************************************
Stores a row in an InnoDB database, to the table specified in this
handle. */
......@@ -3349,12 +3432,10 @@ handle. */
int
ha_innobase::write_row(
/*===================*/
/* out: error code */
mysql_byte* record) /* in: a row in MySQL format */
/* out: error code */
uchar* record) /* in: a row in MySQL format */
{
int error;
longlong auto_inc;
longlong dummy;
int error = 0;
ibool auto_inc_used= FALSE;
ulint sql_command;
trx_t* trx = thd_to_trx(user_thd);
......@@ -3453,62 +3534,20 @@ ha_innobase::write_row(
num_write_row++;
/* This is the case where the table has an auto-increment column */
if (table->next_number_field && record == table->record[0]) {
/* This is the case where the table has an
auto-increment column */
/* Initialize the auto-inc counter if it has not been
initialized yet */
if (0 == dict_table_autoinc_peek(prebuilt->table)) {
/* This call initializes the counter */
error = innobase_read_and_init_auto_inc(&dummy);
if (error) {
/* Deadlock or lock wait timeout */
goto func_exit;
}
/* We have to set sql_stat_start to TRUE because
the above call probably has called a select, and
has reset that flag; row_insert_for_mysql has to
know to set the IX intention lock on the table,
something it only does at the start of each
statement */
prebuilt->sql_stat_start = TRUE;
}
/* We have to use the transactional lock mechanism on the
auto-inc counter of the table to ensure that replication and
roll-forward of the binlog exactly imitates also the given
auto-inc values. The lock is released at each SQL statement's
end. This lock also prevents a race where two threads would
call ::get_auto_increment() simultaneously. */
error = row_lock_table_autoinc_for_mysql(prebuilt);
if (error != DB_SUCCESS) {
/* Deadlock or lock wait timeout */
error = convert_error_code_to_mysql(error, user_thd);
if ((error = update_auto_increment())) {
goto func_exit;
}
/* We must use the handler code to update the auto-increment
value to be sure that we increment it correctly. */
if ((error= update_auto_increment()))
goto func_exit;
auto_inc_used = 1;
auto_inc_used = TRUE;
}
if (prebuilt->mysql_template == NULL
|| prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {
|| prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {
/* Build the template used in converting quickly between
the two database formats */
......@@ -3519,40 +3558,63 @@ ha_innobase::write_row(
error = row_insert_for_mysql((byte*) record, prebuilt);
if (error == DB_SUCCESS && auto_inc_used) {
/* Handle duplicate key errors */
if (auto_inc_used) {
ulonglong auto_inc;
/* Fetch the value that was set in the autoincrement field */
/* Note the number of rows processed for this statement, used
by get_auto_increment() to determine the number of AUTO-INC
values to reserve. This is only useful for a mult-value INSERT
and is a statement level counter.*/
if (trx->n_autoinc_rows > 0) {
--trx->n_autoinc_rows;
}
/* Get the value that MySQL attempted to store in the table.*/
auto_inc = table->next_number_field->val_int();
if (auto_inc != 0) {
/* This call will update the counter according to the
value that was inserted in the table */
switch (error) {
case DB_DUPLICATE_KEY:
dict_table_autoinc_update(prebuilt->table, auto_inc);
}
}
/* A REPLACE command and LOAD DATA INFILE REPLACE
handle a duplicate key error themselves, but we
must update the autoinc counter if we are performing
those statements. */
switch (sql_command) {
case SQLCOM_LOAD:
if ((trx->duplicates
& (TRX_DUP_IGNORE | TRX_DUP_REPLACE))) {
/* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate
key error themselves, and we must update the autoinc counter if we are
performing those statements. */
goto set_max_autoinc;
}
break;
if (error == DB_DUPLICATE_KEY && auto_inc_used
&& (sql_command == SQLCOM_REPLACE
|| sql_command == SQLCOM_REPLACE_SELECT
|| (sql_command == SQLCOM_INSERT
&& ((trx->duplicates
& (TRX_DUP_IGNORE | TRX_DUP_REPLACE))
== TRX_DUP_IGNORE))
|| (sql_command == SQLCOM_LOAD
&& ((trx->duplicates
& (TRX_DUP_IGNORE | TRX_DUP_REPLACE))
== (TRX_DUP_IGNORE | TRX_DUP_REPLACE))))) {
case SQLCOM_REPLACE:
case SQLCOM_INSERT_SELECT:
case SQLCOM_REPLACE_SELECT:
goto set_max_autoinc;
break;
auto_inc = table->next_number_field->val_int();
default:
break;
}
if (auto_inc != 0) {
dict_table_autoinc_update(prebuilt->table, auto_inc);
break;
case DB_SUCCESS:
/* If the actual value inserted is greater than
the upper limit of the interval, then we try and
update the table upper limit. Note: last_value
will be 0 if get_auto_increment() was not called.*/
if (auto_inc > prebuilt->last_value) {
set_max_autoinc:
auto_inc += prebuilt->table->autoinc_increment;
innobase_set_max_autoinc(auto_inc);
}
break;
}
}
......@@ -3560,8 +3622,6 @@ ha_innobase::write_row(
error = convert_error_code_to_mysql(error, user_thd);
/* Tell InnoDB server that there might be work for
utility threads: */
func_exit:
innobase_active_small();
......@@ -3577,16 +3637,16 @@ calc_row_difference(
/*================*/
/* out: error number or 0 */
upd_t* uvect, /* in/out: update vector */
mysql_byte* old_row, /* in: old row in MySQL format */
mysql_byte* new_row, /* in: new row in MySQL format */
uchar* old_row, /* in: old row in MySQL format */
uchar* new_row, /* in: new row in MySQL format */
struct st_table* table, /* in: table in MySQL data
dictionary */
mysql_byte* upd_buff, /* in: buffer to use */
uchar* upd_buff, /* in: buffer to use */
ulint buff_len, /* in: buffer length */
row_prebuilt_t* prebuilt, /* in: InnoDB prebuilt struct */
THD* thd) /* in: user thread */
{
mysql_byte* original_upd_buff = upd_buff;
uchar* original_upd_buff = upd_buff;
Field* field;
enum_field_types field_mysql_type;
uint n_fields;
......@@ -3730,8 +3790,8 @@ int
ha_innobase::update_row(
/*====================*/
/* out: error number or 0 */
const mysql_byte* old_row,/* in: old row in MySQL format */
mysql_byte* new_row)/* in: new row in MySQL format */
const uchar* old_row, /* in: old row in MySQL format */
uchar* new_row) /* in: new row in MySQL format */
{
upd_t* uvect;
int error = 0;
......@@ -3753,7 +3813,7 @@ ha_innobase::update_row(
/* Build an update vector from the modified fields in the rows
(uses upd_buff of the handle) */
calc_row_difference(uvect, (mysql_byte*) old_row, new_row, table,
calc_row_difference(uvect, (uchar*) old_row, new_row, table,
upd_buff, (ulint)upd_and_key_val_buff_len,
prebuilt, user_thd);
......@@ -3766,6 +3826,32 @@ ha_innobase::update_row(
error = row_update_for_mysql((byte*) old_row, prebuilt);
/* We need to do some special AUTOINC handling for the following case:
INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE ...
We need to use the AUTOINC counter that was actually used by
MySQL in the UPDATE statement, which can be different from the
value used in the INSERT statement.*/
if (error == DB_SUCCESS
&& table->next_number_field
&& new_row == table->record[0]
&& thd_sql_command(user_thd) == SQLCOM_INSERT
&& (trx->duplicates & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))
== TRX_DUP_IGNORE) {
longlong auto_inc;
auto_inc = table->next_number_field->val_int();
if (auto_inc != 0) {
auto_inc += prebuilt->table->autoinc_increment;
innobase_set_max_autoinc(auto_inc);
}
}
innodb_srv_conc_exit_innodb(trx);
error = convert_error_code_to_mysql(error, user_thd);
......@@ -3784,8 +3870,8 @@ Deletes a row given as the parameter. */
int
ha_innobase::delete_row(
/*====================*/
/* out: error number or 0 */
const mysql_byte* record) /* in: a row in MySQL format */
/* out: error number or 0 */
const uchar* record) /* in: a row in MySQL format */
{
int error = 0;
trx_t* trx = thd_to_trx(user_thd);
......@@ -3794,6 +3880,19 @@ ha_innobase::delete_row(
ut_a(prebuilt->trx == trx);
/* Only if the table has an AUTOINC column */
if (table->found_next_number_field && record == table->record[0]) {
ulonglong dummy = 0;
error = innobase_get_auto_increment(&dummy);
if (error == DB_SUCCESS) {
dict_table_autoinc_unlock(prebuilt->table);
} else {
goto error_exit;
}
}
if (!prebuilt->upd_node) {
row_get_prebuilt_update_vector(prebuilt);
}
......@@ -3808,6 +3907,7 @@ ha_innobase::delete_row(
innodb_srv_conc_exit_innodb(trx);
error_exit:
error = convert_error_code_to_mysql(error, user_thd);
/* Tell the InnoDB server that there might be work for
......@@ -4011,9 +4111,9 @@ ha_innobase::index_read(
/*====================*/
/* out: 0, HA_ERR_KEY_NOT_FOUND,
or error number */
mysql_byte* buf, /* in/out: buffer for the returned
uchar* buf, /* in/out: buffer for the returned
row */
const mysql_byte* key_ptr,/* in: key value; if this is NULL
const uchar* key_ptr, /* in: key value; if this is NULL
we position the cursor at the
start or end of index; this can
also contain an InnoDB row id, in
......@@ -4110,54 +4210,78 @@ row with the current key value or prefix. */
int
ha_innobase::index_read_last(
/*=========================*/
/* out: 0, HA_ERR_KEY_NOT_FOUND, or an
error code */
mysql_byte* buf, /* out: fetched row */
const mysql_byte* key_ptr, /* in: key value, or a prefix of a full
key value */
uint key_len) /* in: length of the key val or prefix
in bytes */
/* out: 0, HA_ERR_KEY_NOT_FOUND, or an
error code */
uchar* buf, /* out: fetched row */
const uchar* key_ptr,/* in: key value, or a prefix of a full
key value */
uint key_len)/* in: length of the key val or prefix
in bytes */
{
return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST));
}
/************************************************************************
Changes the active index of a handle. */
Get the index for a handle. Does not change active index.*/
int
ha_innobase::change_active_index(
/*=============================*/
/* out: 0 or error code */
uint keynr) /* in: use this index; MAX_KEY means always clustered
index, even if it was internally generated by
InnoDB */
dict_index_t*
ha_innobase::innobase_get_index(
/*============================*/
/* out: NULL or index instance. */
uint keynr) /* in: use this index; MAX_KEY means always
clustered index, even if it was internally
generated by InnoDB */
{
KEY* key=0;
DBUG_ENTER("change_active_index");
KEY* key = 0;
dict_index_t* index = 0;
DBUG_ENTER("innobase_get_index");
ha_statistic_increment(&SSV::ha_read_key_count);
ut_ad(user_thd == ha_thd());
ut_a(prebuilt->trx == thd_to_trx(user_thd));
active_index = keynr;
if (keynr != MAX_KEY && table->s->keys > 0) {
key = table->key_info + active_index;
key = table->key_info + keynr;
prebuilt->index = dict_table_get_index_noninline(
index = dict_table_get_index_noninline(
prebuilt->table, key->name);
} else {
prebuilt->index = dict_table_get_first_index_noninline(
prebuilt->table);
index = dict_table_get_first_index_noninline(prebuilt->table);
}
if (!prebuilt->index) {
if (!index) {
sql_print_error(
"Innodb could not find key n:o %u with name %s "
"from dict cache for table %s",
keynr, key ? key->name : "NULL",
prebuilt->table->name);
}
DBUG_RETURN(index);
}
/************************************************************************
Changes the active index of a handle. */
int
ha_innobase::change_active_index(
/*=============================*/
/* out: 0 or error code */
uint keynr) /* in: use this index; MAX_KEY means always clustered
index, even if it was internally generated by
InnoDB */
{
DBUG_ENTER("change_active_index");
ut_ad(user_thd == ha_thd());
ut_a(prebuilt->trx == thd_to_trx(user_thd));
active_index = keynr;
prebuilt->index = innobase_get_index(keynr);
if (!prebuilt->index) {
DBUG_RETURN(1);
}
......@@ -4188,10 +4312,10 @@ int
ha_innobase::index_read_idx(
/*========================*/
/* out: error number or 0 */
mysql_byte* buf, /* in/out: buffer for the returned
uchar* buf, /* in/out: buffer for the returned
row */
uint keynr, /* in: use this index */
const mysql_byte* key, /* in: key value; if this is NULL
const uchar* key, /* in: key value; if this is NULL
we position the cursor at the
start or end of index */
uint key_len, /* in: key value length */
......@@ -4214,7 +4338,7 @@ ha_innobase::general_fetch(
/*=======================*/
/* out: 0, HA_ERR_END_OF_FILE, or error
number */
mysql_byte* buf, /* in/out: buffer for next row in MySQL
uchar* buf, /* in/out: buffer for next row in MySQL
format */
uint direction, /* in: ROW_SEL_NEXT or ROW_SEL_PREV */
uint match_mode) /* in: 0, ROW_SEL_EXACT, or
......@@ -4261,7 +4385,7 @@ ha_innobase::index_next(
/*====================*/
/* out: 0, HA_ERR_END_OF_FILE, or error
number */
mysql_byte* buf) /* in/out: buffer for next row in MySQL
uchar* buf) /* in/out: buffer for next row in MySQL
format */
{
ha_statistic_increment(&SSV::ha_read_next_count);
......@@ -4277,8 +4401,8 @@ ha_innobase::index_next_same(
/*=========================*/
/* out: 0, HA_ERR_END_OF_FILE, or error
number */
mysql_byte* buf, /* in/out: buffer for the row */
const mysql_byte* key, /* in: key value */
uchar* buf, /* in/out: buffer for the row */
const uchar* key, /* in: key value */
uint keylen) /* in: key value length */
{
ha_statistic_increment(&SSV::ha_read_next_count);
......@@ -4293,10 +4417,8 @@ positioned using index_read. */
int
ha_innobase::index_prev(
/*====================*/
/* out: 0, HA_ERR_END_OF_FILE, or error
number */
mysql_byte* buf) /* in/out: buffer for previous row in MySQL
format */
/* out: 0, HA_ERR_END_OF_FILE, or error number */
uchar* buf) /* in/out: buffer for previous row in MySQL format */
{
ha_statistic_increment(&SSV::ha_read_prev_count);
......@@ -4310,9 +4432,8 @@ corresponding row to buf. */
int
ha_innobase::index_first(
/*=====================*/
/* out: 0, HA_ERR_END_OF_FILE,
or error code */
mysql_byte* buf) /* in/out: buffer for the row */
/* out: 0, HA_ERR_END_OF_FILE, or error code */
uchar* buf) /* in/out: buffer for the row */
{
int error;
......@@ -4337,8 +4458,8 @@ corresponding row to buf. */
int
ha_innobase::index_last(
/*====================*/
/* out: 0, HA_ERR_END_OF_FILE, or error code */
mysql_byte* buf) /* in/out: buffer for the row */
/* out: 0, HA_ERR_END_OF_FILE, or error code */
uchar* buf) /* in/out: buffer for the row */
{
int error;
......@@ -4407,7 +4528,7 @@ int
ha_innobase::rnd_next(
/*==================*/
/* out: 0, HA_ERR_END_OF_FILE, or error number */
mysql_byte* buf)/* in/out: returns the row in this buffer,
uchar* buf) /* in/out: returns the row in this buffer,
in MySQL format */
{
int error;
......@@ -4434,14 +4555,12 @@ Fetches a row from the table based on a row reference. */
int
ha_innobase::rnd_pos(
/*=================*/
/* out: 0, HA_ERR_KEY_NOT_FOUND,
or error code */
mysql_byte* buf, /* in/out: buffer for the row */
mysql_byte* pos) /* in: primary key value of the row in the
MySQL format, or the row id if the clustered
index was internally generated by InnoDB;
the length of data in pos has to be
ref_length */
/* out: 0, HA_ERR_KEY_NOT_FOUND, or error code */
uchar* buf, /* in/out: buffer for the row */
uchar* pos) /* in: primary key value of the row in the
MySQL format, or the row id if the clustered
index was internally generated by InnoDB; the
length of data in pos has to be ref_length */
{
int error;
uint keynr = active_index;
......@@ -4494,7 +4613,7 @@ was positioned the last time. */
void
ha_innobase::position(
/*==================*/
const mysql_byte* record) /* in: row in MySQL format */
const uchar* record) /* in: row in MySQL format */
{
uint len;
......@@ -4636,7 +4755,7 @@ create_table_def(
}
}
dict_mem_table_add_col(table,
dict_mem_table_add_col(table, table->heap,
(char*) field->field_name,
col_type,
dtype_form_prtype(
......@@ -5000,7 +5119,10 @@ ha_innobase::create(
maximum value in the column. */
auto_inc_value = create_info->auto_increment_value;
dict_table_autoinc_lock(innobase_table);
dict_table_autoinc_initialize(innobase_table, auto_inc_value);
dict_table_autoinc_unlock(innobase_table);
}
/* Tell the InnoDB server that there might be work for
......@@ -5351,7 +5473,7 @@ ha_innobase::records_in_range(
{
KEY* key;
dict_index_t* index;
mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc(
uchar* key_val_buff2 = (uchar*) my_malloc(
table->s->reclength
+ table->s->max_key_length + 100,
MYF(MY_FAE));
......@@ -5393,7 +5515,7 @@ ha_innobase::records_in_range(
(ulint)upd_and_key_val_buff_len,
index,
(byte*) (min_key ? min_key->key :
(const mysql_byte*) 0),
(const uchar*) 0),
(ulint) (min_key ? min_key->length : 0),
prebuilt->trx);
......@@ -5401,7 +5523,7 @@ ha_innobase::records_in_range(
range_end, (byte*) key_val_buff2,
buff2_len, index,
(byte*) (max_key ? max_key->key :
(const mysql_byte*) 0),
(const uchar*) 0),
(ulint) (max_key ? max_key->length : 0),
prebuilt->trx);
......@@ -6801,12 +6923,12 @@ bool innobase_show_status(handlerton *hton, THD* thd,
locking.
****************************************************************************/
static mysql_byte* innobase_get_key(INNOBASE_SHARE* share, size_t *length,
static uchar* innobase_get_key(INNOBASE_SHARE* share, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length=share->table_name_length;
return (mysql_byte*) share->table_name;
return (uchar*) share->table_name;
}
static INNOBASE_SHARE* get_share(const char* table_name)
......@@ -6816,7 +6938,7 @@ static INNOBASE_SHARE* get_share(const char* table_name)
uint length=(uint) strlen(table_name);
if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables,
(mysql_byte*) table_name,
(uchar*) table_name,
length))) {
share = (INNOBASE_SHARE *) my_malloc(sizeof(*share)+length+1,
......@@ -6827,7 +6949,7 @@ static INNOBASE_SHARE* get_share(const char* table_name)
strmov(share->table_name,table_name);
if (my_hash_insert(&innobase_open_tables,
(mysql_byte*) share)) {
(uchar*) share)) {
pthread_mutex_unlock(&innobase_share_mutex);
my_free(share,0);
......@@ -6849,7 +6971,7 @@ static void free_share(INNOBASE_SHARE* share)
pthread_mutex_lock(&innobase_share_mutex);
if (!--share->use_count) {
hash_delete(&innobase_open_tables, (mysql_byte*) share);
hash_delete(&innobase_open_tables, (uchar*) share);
thr_lock_delete(&share->lock);
pthread_mutex_destroy(&share->mutex);
my_free(share, MYF(0));
......@@ -7070,15 +7192,15 @@ the value of the auto-inc counter. */
int
ha_innobase::innobase_read_and_init_auto_inc(
/*=========================================*/
/* out: 0 or error code: deadlock or lock wait
timeout */
longlong* ret) /* out: auto-inc value */
/* out: 0 or error code:
deadlock or lock wait timeout */
longlong* value) /* out: the autoinc value */
{
longlong auto_inc;
ulint old_select_lock_type;
ibool trx_was_not_started = FALSE;
ibool stmt_start;
int error;
int mysql_error = 0;
dict_table_t* innodb_table = prebuilt->table;
ibool trx_was_not_started = FALSE;
ut_a(prebuilt);
ut_a(prebuilt->table);
......@@ -7099,103 +7221,47 @@ ha_innobase::innobase_read_and_init_auto_inc(
trx_search_latch_release_if_reserved(prebuilt->trx);
auto_inc = dict_table_autoinc_read(prebuilt->table);
if (auto_inc != 0) {
/* Already initialized */
*ret = auto_inc;
error = 0;
goto func_exit_early;
}
error = row_lock_table_autoinc_for_mysql(prebuilt);
if (error != DB_SUCCESS) {
error = convert_error_code_to_mysql(error, user_thd);
goto func_exit_early;
}
dict_table_autoinc_lock(prebuilt->table);
/* Check again if someone has initialized the counter meanwhile */
auto_inc = dict_table_autoinc_read(prebuilt->table);
if (auto_inc != 0) {
*ret = auto_inc;
error = 0;
goto func_exit_early;
/* Was the AUTOINC counter reset during normal processing, if
so then we simply start count from 1. No need to go to the index.*/
if (auto_inc == 0 && innodb_table->autoinc_inited) {
++auto_inc;
dict_table_autoinc_initialize(innodb_table, auto_inc);
}
(void) extra(HA_EXTRA_KEYREAD);
index_init(table->s->next_number_index, 1);
if (auto_inc == 0) {
dict_index_t* index;
ulint error = DB_SUCCESS;
const char* autoinc_col_name;
/* Starting from 5.0.9, we use a consistent read to read the auto-inc
column maximum value. This eliminates the spurious deadlocks caused
by the row X-lock that we previously used. Note the following flaw
in our algorithm: if some other user meanwhile UPDATEs the auto-inc
column, our consistent read will not return the largest value. We
accept this flaw, since the deadlocks were a bigger trouble. */
ut_a(!innodb_table->autoinc_inited);
/* Fetch all the columns in the key */
index = innobase_get_index(table->s->next_number_index);
prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
old_select_lock_type = prebuilt->select_lock_type;
prebuilt->select_lock_type = LOCK_NONE;
autoinc_col_name = table->found_next_number_field->field_name;
/* Eliminate an InnoDB error print that happens when we try to SELECT
from a table when no table has been locked in ::external_lock(). */
prebuilt->trx->n_mysql_tables_in_use++;
error = row_search_max_autoinc(
index, autoinc_col_name, &auto_inc);
error = index_last(table->record[1]);
prebuilt->trx->n_mysql_tables_in_use--;
prebuilt->select_lock_type = old_select_lock_type;
if (error) {
if (error == HA_ERR_END_OF_FILE) {
/* The table was empty, initialize to 1 */
auto_inc = 1;
error = 0;
if (error == DB_SUCCESS) {
++auto_inc;
dict_table_autoinc_initialize(innodb_table, auto_inc);
} else {
/* This should not happen in a consistent read */
sql_print_error("Consistent read of auto-inc column "
"returned %lu", (ulong) error);
auto_inc = -1;
fprintf(stderr, " InnoDB error: Couldn't read the "
"max AUTOINC value from index (%s).\n",
index->name);
goto func_exit;
mysql_error = 1;
}
} else {
/* Initialize to max(col) + 1; we use
'found_next_number_field' below because MySQL in SHOW TABLE
STATUS does not seem to set 'next_number_field'. The comment
in table.h says that 'next_number_field' is set when it is
'active'.
Since 5.1 MySQL enforces that we announce fields which we will
read; as we only do a val_*() call, dbug_tmp_use_all_columns()
with read_set is sufficient. */
my_bitmap_map *old_map;
old_map= dbug_tmp_use_all_columns(table, table->read_set);
auto_inc = (longlong) table->found_next_number_field->
val_int_offset(table->s->rec_buff_length) + 1;
dbug_tmp_restore_column_map(table->read_set, old_map);
}
dict_table_autoinc_initialize(prebuilt->table, auto_inc);
*value = auto_inc;
func_exit:
(void) extra(HA_EXTRA_NO_KEYREAD);
index_end();
dict_table_autoinc_unlock(prebuilt->table);
*ret = auto_inc;
func_exit_early:
/* Since MySQL does not seem to call autocommit after SHOW TABLE
STATUS (even if we would register the trx here), we commit our
transaction here if it was started here. This is to eliminate a
......@@ -7210,6 +7276,63 @@ ha_innobase::innobase_read_and_init_auto_inc(
prebuilt->sql_stat_start = stmt_start;
return(mysql_error);
}
/*******************************************************************************
Read the next autoinc value, initialize the table if it's not initialized.
On return if there is no error then the tables AUTOINC lock is locked.*/
ulong
ha_innobase::innobase_get_auto_increment(
ulonglong* value) /* out: autoinc value */
{
ulint error;
do {
error = innobase_autoinc_lock();
if (error == DB_SUCCESS) {
ib_longlong autoinc;
/* Determine the first value of the interval */
autoinc = dict_table_autoinc_read(prebuilt->table);
/* We need to initialize the AUTO-INC value, for
that we release all locks.*/
if (autoinc <= 0) {
trx_t* trx;
trx = prebuilt->trx;
dict_table_autoinc_unlock(prebuilt->table);
if (trx->auto_inc_lock) {
/* If we had reserved the AUTO-INC
lock in this SQL statement we release
it before retrying.*/
row_unlock_table_autoinc_for_mysql(trx);
}
/* Just to make sure */
ut_a(!trx->auto_inc_lock);
int mysql_error;
mysql_error = innobase_read_and_init_auto_inc(
&autoinc);
if (!mysql_error) {
/* Should have read the proper value */
ut_a(autoinc > 0);
} else {
error = DB_ERROR;
}
} else {
*value = (ulonglong) autoinc;
}
}
} while (*value == 0 && error == DB_SUCCESS);
return(error);
}
......@@ -7221,37 +7344,87 @@ 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) */
void ha_innobase::get_auto_increment(
void
ha_innobase::get_auto_increment(
/*=================================*/
ulonglong offset, /* in */
ulonglong increment, /* in */
ulonglong nb_desired_values, /* in */
ulonglong *first_value, /* out */
ulonglong *nb_reserved_values) /* out */
ulonglong offset, /* in: */
ulonglong increment, /* in: table autoinc increment */
ulonglong nb_desired_values, /* in: number of values reqd */
ulonglong *first_value, /* out: the autoinc value */
ulonglong *nb_reserved_values) /* out: count of reserved values */
{
longlong nr;
int error;
ulint error;
ulonglong autoinc = 0;
/* Prepare prebuilt->trx in the table handle */
update_thd(ha_thd());
error = innobase_read_and_init_auto_inc(&nr);
error = innobase_get_auto_increment(&autoinc);
if (error) {
/* This should never happen in the current (5.0.6) code, since
we call this function only after the counter has been
initialized. */
if (error != DB_SUCCESS) {
/* This should never happen in the code > ver 5.0.6,
since we call this function only after the counter
has been initialized. */
ut_print_timestamp(stderr);
sql_print_error("Error %lu in ::get_auto_increment()",
(ulong) error);
*first_value= (~(ulonglong) 0);
sql_print_error("Error %lu in ::get_auto_increment()", error);
*first_value = (~(ulonglong) 0);
return;
}
*first_value= (ulonglong) nr;
/* table-level autoinc lock reserves up to +inf */
*nb_reserved_values= ULONGLONG_MAX;
/* This is a hack, since nb_desired_values seems to be accurate only
for the first call to get_auto_increment() for multi-row INSERT and
meaningless for other statements e.g, LOAD etc. Subsequent calls to
this method for the same statement results in different values which
don't make sense. Therefore we store the value the first time we are
called and count down from that as rows are written (see write_row()).
We make one exception, if the *first_value is precomputed by MySQL
we use that value. And set the number of reserved values to 1 if
this is the first time we were called for the SQL statement, this
will force MySQL to call us for the next value. If we are in the
middle of a multi-row insert we preserve the existing counter.*/
if (*first_value == 0) {
/* Called for the first time ? */
if (prebuilt->trx->n_autoinc_rows == 0) {
prebuilt->trx->n_autoinc_rows = nb_desired_values;
/* It's possible for nb_desired_values to be 0:
e.g., INSERT INTO T1(C) SELECT C FROM T2; */
if (nb_desired_values == 0) {
++prebuilt->trx->n_autoinc_rows;
}
}
*first_value = autoinc;
} else if (prebuilt->trx->n_autoinc_rows == 0) {
prebuilt->trx->n_autoinc_rows = 1;
}
ut_a(prebuilt->trx->n_autoinc_rows > 0);
*nb_reserved_values = prebuilt->trx->n_autoinc_rows;
/* Compute the last value in the interval */
prebuilt->last_value = *first_value + (*nb_reserved_values * increment);
ut_a(prebuilt->last_value >= *first_value);
/* Update the table autoinc variable */
dict_table_autoinc_update(prebuilt->table, prebuilt->last_value);
/* The increment to be used to increase the AUTOINC value, we use
this in write_row() and update_row() to increase the autoinc counter
for columns that are filled by the user.*/
prebuilt->table->autoinc_increment = increment;
dict_table_autoinc_unlock(prebuilt->table);
}
/* See comment in handler.h */
......@@ -7272,7 +7445,7 @@ ha_innobase::reset_auto_increment(ulonglong value)
DBUG_RETURN(error);
}
dict_table_autoinc_initialize(prebuilt->table, value);
innobase_reset_autoinc(value);
DBUG_RETURN(0);
}
......@@ -7299,9 +7472,9 @@ ha_innobase::cmp_ref(
/*=================*/
/* out: < 0 if ref1 < ref2, 0 if equal, else
> 0 */
const mysql_byte* ref1, /* in: an (internal) primary key value in the
const uchar* ref1, /* in: an (internal) primary key value in the
MySQL key value format */
const mysql_byte* ref2) /* in: an (internal) primary key value in the
const uchar* ref2) /* in: an (internal) primary key value in the
MySQL key value format */
{
enum_field_types mysql_type;
......@@ -7558,6 +7731,7 @@ innobase_xa_prepare(
row_unlock_table_autoinc_for_mysql(trx);
}
/* Store the current undo_no of the transaction so that we
know where to roll back if we have to roll back the next
SQL statement */
......
......@@ -32,7 +32,10 @@ typedef struct st_innobase_share {
} INNOBASE_SHARE;
struct dict_index_struct;
struct row_prebuilt_struct;
typedef struct dict_index_struct dict_index_t;
typedef struct row_prebuilt_struct row_prebuilt_t;
/* The class defining a handle to an Innodb table */
......@@ -70,6 +73,11 @@ class ha_innobase: public handler
int change_active_index(uint keynr);
int general_fetch(uchar* buf, uint direction, uint match_mode);
int innobase_read_and_init_auto_inc(longlong* ret);
ulong innobase_autoinc_lock();
ulong innobase_set_max_autoinc(ulonglong auto_inc);
ulong innobase_reset_autoinc(ulonglong auto_inc);
ulong innobase_get_auto_increment(ulonglong* value);
dict_index_t* innobase_get_index(uint keynr);
/* Init values for the class: */
public:
......
......@@ -462,7 +462,8 @@ ibuf_data_init_for_space(
page_t* root;
page_t* header_page;
mtr_t mtr;
char buf[50];
char* buf;
mem_heap_t* heap;
dict_table_t* table;
dict_index_t* index;
ulint n_used;
......@@ -516,16 +517,20 @@ ibuf_data_init_for_space(
ibuf_exit();
heap = mem_heap_create(450);
buf = mem_heap_alloc(heap, 50);
sprintf(buf, "SYS_IBUF_TABLE_%lu", (ulong) space);
/* use old-style record format for the insert buffer */
table = dict_mem_table_create(buf, space, 2, 0);
dict_mem_table_add_col(table, "PAGE_NO", DATA_BINARY, 0, 0);
dict_mem_table_add_col(table, "TYPES", DATA_BINARY, 0, 0);
dict_mem_table_add_col(table, heap, "PAGE_NO", DATA_BINARY, 0, 0);
dict_mem_table_add_col(table, heap, "TYPES", DATA_BINARY, 0, 0);
table->id = ut_dulint_add(DICT_IBUF_ID_MIN, space);
dict_table_add_to_cache(table);
dict_table_add_to_cache(table, heap);
mem_heap_free(heap);
index = dict_mem_index_create(
buf, "CLUST_IND", space,
......@@ -1139,7 +1144,7 @@ ibuf_dummy_index_add_col(
ulint len) /* in: length of the column */
{
ulint i = index->table->n_def;
dict_mem_table_add_col(index->table, "DUMMY",
dict_mem_table_add_col(index->table, NULL, NULL,
dtype_get_mtype(type),
dtype_get_prtype(type),
dtype_get_len(type));
......@@ -1161,11 +1166,6 @@ ibuf_dummy_index_free(
dict_mem_table_free(table);
}
void
dict_index_print_low(
/*=================*/
dict_index_t* index); /* in: index */
/*************************************************************************
Builds the entry to insert into a non-clustered index when we have the
corresponding record in an ibuf index. */
......
......@@ -171,6 +171,13 @@ dict_col_name_is_reserved(
/* out: TRUE if name is reserved */
const char* name); /* in: column name */
/************************************************************************
Acquire the autoinc lock.*/
void
dict_table_autoinc_lock(
/*====================*/
dict_table_t* table); /* in: table */
/************************************************************************
Initializes the autoinc counter. It is not an error to initialize an already
initialized counter. */
......@@ -180,22 +187,6 @@ dict_table_autoinc_initialize(
dict_table_t* table, /* in: table */
ib_longlong value); /* in: next value to assign to a row */
/************************************************************************
Gets the next autoinc value (== autoinc counter value), 0 if not yet
initialized. If initialized, increments the counter by 1. */
ib_longlong
dict_table_autoinc_get(
/*===================*/
/* out: value for a new row, or 0 */
dict_table_t* table); /* in: table */
/************************************************************************
Decrements the autoinc counter value by 1. */
void
dict_table_autoinc_decrement(
/*=========================*/
dict_table_t* table); /* in: table */
/************************************************************************
Reads the next autoinc value (== autoinc counter value), 0 if not yet
initialized. */
......@@ -205,15 +196,6 @@ dict_table_autoinc_read(
/* out: value for a new row, or 0 */
dict_table_t* table); /* in: table */
/************************************************************************
Peeks the autoinc counter value, 0 if not yet initialized. Does not
increment the counter. The read not protected by any mutex! */
ib_longlong
dict_table_autoinc_peek(
/*====================*/
/* out: value of the counter */
dict_table_t* table); /* in: table */
/************************************************************************
Updates the autoinc counter if the value supplied is equal or bigger than the
current value. If not inited, does nothing. */
......@@ -223,13 +205,29 @@ dict_table_autoinc_update(
dict_table_t* table, /* in: table */
ib_longlong value); /* in: value which was assigned to a row */
/************************************************************************
Release the autoinc lock.*/
void
dict_table_autoinc_unlock(
/*======================*/
dict_table_t* table); /* in: table */
/**************************************************************************
Adds system columns to a table object. */
void
dict_table_add_system_columns(
/*==========================*/
dict_table_t* table, /* in/out: table */
mem_heap_t* heap); /* in: temporary heap */
/**************************************************************************
Adds a table object to the dictionary cache. */
void
dict_table_add_to_cache(
/*====================*/
dict_table_t* table); /* in: table */
dict_table_t* table, /* in: table */
mem_heap_t* heap); /* in: temporary heap */
/**************************************************************************
Removes a table object from the dictionary cache. */
......
......@@ -72,7 +72,8 @@ void
dict_mem_table_add_col(
/*===================*/
dict_table_t* table, /* in: table */
const char* name, /* in: column name */
mem_heap_t* heap, /* in: temporary memory heap, or NULL */
const char* name, /* in: column name, or NULL */
ulint mtype, /* in: main datatype */
ulint prtype, /* in: precise type */
ulint len); /* in: precision */
......@@ -410,6 +411,11 @@ struct dict_table_struct{
SELECT MAX(auto inc column) */
ib_longlong autoinc;/* autoinc counter value to give to the
next inserted row */
ib_longlong autoinc_increment;
/* The increment step of the auto increment
column. Value must be greater than or equal
to 1 */
#ifdef UNIV_DEBUG
ulint magic_n;/* magic number */
# define DICT_TABLE_MAGIC_N 76333786
......
......@@ -609,7 +609,7 @@ lock_validate(void);
/* out: TRUE if ok */
/*************************************************************************
Return approximate number or record locks (bits set in the bitmap) for
this transaction. Since delete-marked records maybe removed, the
this transaction. Since delete-marked records may be removed, the
record count will not be precise. */
ulint
......
......@@ -670,6 +670,7 @@ struct row_prebuilt_struct {
to this heap */
mem_heap_t* old_vers_heap; /* memory heap where a previous
version is built in consistent read */
ulonglong last_value; /* last value of AUTO-INC interval */
ulint magic_n2; /* this should be the same as
magic_n */
};
......
......@@ -171,7 +171,17 @@ row_search_check_if_query_cache_permitted(
trx_t* trx, /* in: transaction object */
const char* norm_name); /* in: concatenation of database name,
'/' char, table name */
/***********************************************************************
Read the max AUTOINC value from an index. */
ulint
row_search_max_autoinc(
/*===================*/
/* out: DB_SUCCESS if all OK else
error code */
dict_index_t* index, /* in: index to search */
const char* col_name, /* in: autoinc column name */
ib_longlong* value); /* out: AUTOINC value read */
/* A structure for caching column values for prefetched rows */
struct sel_buf_struct{
......
......@@ -681,6 +681,9 @@ struct trx_struct{
trx_undo_arr_t* undo_no_arr; /* array of undo numbers of undo log
records which are currently processed
by a rollback operation */
ulint n_autoinc_rows; /* no. of AUTO-INC rows required for
an SQL statement. This is useful for
multi-row INSERTs */
/*------------------------------*/
char detailed_error[256]; /* detailed error message for last
error, or empty. */
......
......@@ -63,7 +63,7 @@ ut_test_malloc(
/* out: TRUE if succeeded */
ulint n); /* in: try to allocate this many bytes */
/**************************************************************************
Frees a memory bloock allocated with ut_malloc. */
Frees a memory block allocated with ut_malloc. */
void
ut_free(
......
......@@ -115,7 +115,7 @@ dulint recv_max_page_lsn;
Initialize crash recovery environment. Can be called iff
recv_needed_recovery == FALSE. */
static
void
void
recv_init_crash_recovery(void);
/*===========================*/
......@@ -2450,7 +2450,7 @@ void
recv_init_crash_recovery(void)
/*==========================*/
{
ut_a(!recv_needed_recovery);
ut_a(!recv_needed_recovery);
recv_needed_recovery = TRUE;
......@@ -2481,7 +2481,6 @@ recv_init_crash_recovery(void)
"InnoDB: buffer...\n");
trx_sys_doublewrite_init_or_restore_pages(TRUE);
}
}
/************************************************************
......@@ -2805,7 +2804,7 @@ recv_recovery_from_checkpoint_start(
recv_synchronize_groups(up_to_date_group);
if (!recv_needed_recovery) {
ut_a(ut_dulint_cmp(checkpoint_lsn,
ut_a(ut_dulint_cmp(checkpoint_lsn,
recv_sys->recovered_lsn) == 0);
} else {
......
......@@ -655,6 +655,8 @@ row_create_prebuilt(
prebuilt->old_vers_heap = NULL;
prebuilt->last_value = 0;
return(prebuilt);
}
......@@ -2894,6 +2896,8 @@ row_truncate_table_for_mysql(
dict_table_change_id_in_cache(table, new_id);
}
/* MySQL calls ha_innobase::reset_auto_increment() which does
the same thing. */
dict_table_autoinc_initialize(table, 0);
dict_update_statistics(table);
......
......@@ -4519,3 +4519,169 @@ row_search_check_if_query_cache_permitted(
return(ret);
}
/***********************************************************************
Read the AUTOINC column from the current row. */
static
ib_longlong
row_search_autoinc_read_column(
/*===========================*/
/* out: value read from the column */
dict_index_t* index, /* in: index to read from */
const rec_t* rec, /* in: current rec */
ulint col_no, /* in: column number */
ibool unsigned_type) /* in: signed or unsigned flag */
{
ulint len;
byte* ptr;
const byte* data;
ib_longlong value;
mem_heap_t* heap = NULL;
byte dest[sizeof(value)];
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
*offsets_ = sizeof offsets_ / sizeof *offsets_;
/* TODO: We have to cast away the const of rec for now. This needs
to be fixed later.*/
offsets = rec_get_offsets(
(rec_t*) rec, index, offsets, ULINT_UNDEFINED, &heap);
/* TODO: We have to cast away the const of rec for now. This needs
to be fixed later.*/
data = rec_get_nth_field((rec_t*)rec, offsets, col_no, &len);
ut_a(len != UNIV_SQL_NULL);
ut_a(len <= sizeof value);
/* Convert integer data from Innobase to a little-endian format,
sign bit restored to normal */
for (ptr = dest + len; ptr != dest; ++data) {
--ptr;
*ptr = *data;
}
if (!unsigned_type) {
dest[len - 1] ^= 128;
}
/* The assumption here is that the AUTOINC value can't be negative.*/
switch (len) {
case 8:
value = *(ib_longlong*) ptr;
break;
case 4:
value = *(ib_uint32_t*) ptr;
break;
case 2:
value = *(uint16 *) ptr;
break;
case 1:
value = *ptr;
break;
default:
ut_error;
}
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
ut_a(value >= 0);
return(value);
}
/***********************************************************************
Get the last row. */
static
const rec_t*
row_search_autoinc_get_rec(
/*=======================*/
/* out: current rec or NULL */
btr_pcur_t* pcur, /* in: the current cursor */
mtr_t* mtr) /* in: mini transaction */
{
do {
const rec_t* rec = btr_pcur_get_rec(pcur);
if (page_rec_is_user_rec(rec)) {
return(rec);
}
} while (btr_pcur_move_to_prev(pcur, mtr));
return(NULL);
}
/***********************************************************************
Read the max AUTOINC value from an index. */
ulint
row_search_max_autoinc(
/*===================*/
/* out: DB_SUCCESS if all OK else
error code, DB_RECORD_NOT_FOUND if
column name can't be found in index */
dict_index_t* index, /* in: index to search */
const char* col_name, /* in: name of autoinc column */
ib_longlong* value) /* out: AUTOINC value read */
{
ulint i;
ulint n_cols;
dict_field_t* dfield = NULL;
ulint error = DB_SUCCESS;
n_cols = dict_index_get_n_ordering_defined_by_user(index);
/* Search the index for the AUTOINC column name */
for (i = 0; i < n_cols; ++i) {
dfield = dict_index_get_nth_field(index, i);
if (strcmp(col_name, dfield->name) == 0) {
break;
}
}
*value = 0;
/* Must find the AUTOINC column name */
if (i < n_cols && dfield) {
mtr_t mtr;
btr_pcur_t pcur;
mtr_start(&mtr);
/* Open at the high/right end (FALSE), and INIT
cursor (TRUE) */
btr_pcur_open_at_index_side(
FALSE, index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr);
if (page_get_n_recs(btr_pcur_get_page(&pcur)) > 0) {
const rec_t* rec;
rec = row_search_autoinc_get_rec(&pcur, &mtr);
if (rec != NULL) {
ibool unsigned_type = (
dfield->col->prtype & DATA_UNSIGNED);
*value = row_search_autoinc_read_column(
index, rec, i, unsigned_type);
}
}
btr_pcur_close(&pcur);
mtr_commit(&mtr);
} else {
error = DB_RECORD_NOT_FOUND;
}
return(error);
}
......@@ -195,6 +195,8 @@ trx_create(
memset(&trx->xid, 0, sizeof(trx->xid));
trx->xid.formatID = -1;
trx->n_autoinc_rows = 0;
trx_reset_new_rec_lock_info(trx);
return(trx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment