From 6d4b3c58000a65c35f08057468ae915e23c98a58 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Aug 2007 19:14:52 -0600 Subject: [PATCH] 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(). mysql-test/r/innodb.result: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1655: Fix the innodb.test failure mentioned in r1654. storage/innobase/dict/dict0dict.c: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. Revision r1719: Merge r1264 from branches/zip: Avoid memory fragmentation when adding column definitions to tables. dict_mem_table_add_col(): Add the parameter "heap" for temporary memory allocation. Allow it and "name" to be NULL. These parameters are NULL when creating dummy indexes. dict_add_col_name(): Remove calls to ut_malloc() and ut_free(). dict_table_get_col_name(): Allow table->col_names to be NULL. dict_table_add_system_columns(), dict_table_add_to_cache(): Add the parameter "heap". --- Additional changes that had to be merged from branches/zip: dict_table_add_system_columns(): New function, factored out from dict_table_add_to_cache(). mlog_parse_index(): Add some consistency checks, and make use of dict_table_add_system_columns(). storage/innobase/dict/dict0mem.c: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. Revision r1719: Merge r1264 from branches/zip: Avoid memory fragmentation when adding column definitions to tables. dict_mem_table_add_col(): Add the parameter "heap" for temporary memory allocation. Allow it and "name" to be NULL. These parameters are NULL when creating dummy indexes. dict_add_col_name(): Remove calls to ut_malloc() and ut_free(). dict_table_get_col_name(): Allow table->col_names to be NULL. dict_table_add_system_columns(), dict_table_add_to_cache(): Add the parameter "heap". --- Additional changes that had to be merged from branches/zip: dict_table_add_system_columns(): New function, factored out from dict_table_add_to_cache(). mlog_parse_index(): Add some consistency checks, and make use of dict_table_add_system_columns(). storage/innobase/handler/ha_innodb.cc: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. Revision r1718: Replace mysql_byte with uchar and remove the #define mysql_byte from ha_innodb.cc. This cleanup was made possible as of r1550: Revision r1658: check_trx_exists(): Remove a redundant function call and assignment that was added by someone at MySQL. Revision r1656: Revision r1719: Merge r1264 from branches/zip: Avoid memory fragmentation when adding column definitions to tables. dict_mem_table_add_col(): Add the parameter "heap" for temporary memory allocation. Allow it and "name" to be NULL. These parameters are NULL when creating dummy indexes. dict_add_col_name(): Remove calls to ut_malloc() and ut_free(). dict_table_get_col_name(): Allow table->col_names to be NULL. dict_table_add_system_columns(), dict_table_add_to_cache(): Add the parameter "heap". --- Additional changes that had to be merged from branches/zip: dict_table_add_system_columns(): New function, factored out from dict_table_add_to_cache(). mlog_parse_index(): Add some consistency checks, and make use of dict_table_add_system_columns(). Revision r1654: One test case in innodb.test fails because of auto-increment changes in r1562:1653: $diff innodb.result innodb.reject 504c504 < 3 test2 this will work --- > 4 test2 this will work storage/innobase/handler/ha_innodb.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. Revision r1654: One test case in innodb.test fails because of auto-increment changes in r1562:1653: $diff innodb.result innodb.reject 504c504 < 3 test2 this will work --- > 4 test2 this will work storage/innobase/ibuf/ibuf0ibuf.c: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1715: ibuf0ibuf.c: Remove the unused prototype for dict_index_print_low() that was inadvertently added in r832. Revision r1719: Merge r1264 from branches/zip: Avoid memory fragmentation when adding column definitions to tables. dict_mem_table_add_col(): Add the parameter "heap" for temporary memory allocation. Allow it and "name" to be NULL. These parameters are NULL when creating dummy indexes. dict_add_col_name(): Remove calls to ut_malloc() and ut_free(). dict_table_get_col_name(): Allow table->col_names to be NULL. dict_table_add_system_columns(), dict_table_add_to_cache(): Add the parameter "heap". --- Additional changes that had to be merged from branches/zip: dict_table_add_system_columns(): New function, factored out from dict_table_add_to_cache(). mlog_parse_index(): Add some consistency checks, and make use of dict_table_add_system_columns(). storage/innobase/include/dict0dict.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. Revision r1719: Merge r1264 from branches/zip: Avoid memory fragmentation when adding column definitions to tables. dict_mem_table_add_col(): Add the parameter "heap" for temporary memory allocation. Allow it and "name" to be NULL. These parameters are NULL when creating dummy indexes. dict_add_col_name(): Remove calls to ut_malloc() and ut_free(). dict_table_get_col_name(): Allow table->col_names to be NULL. dict_table_add_system_columns(), dict_table_add_to_cache(): Add the parameter "heap". --- Additional changes that had to be merged from branches/zip: dict_table_add_system_columns(): New function, factored out from dict_table_add_to_cache(). mlog_parse_index(): Add some consistency checks, and make use of dict_table_add_system_columns(). storage/innobase/include/dict0mem.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. Revision r1719: Merge r1264 from branches/zip: Avoid memory fragmentation when adding column definitions to tables. dict_mem_table_add_col(): Add the parameter "heap" for temporary memory allocation. Allow it and "name" to be NULL. These parameters are NULL when creating dummy indexes. dict_add_col_name(): Remove calls to ut_malloc() and ut_free(). dict_table_get_col_name(): Allow table->col_names to be NULL. dict_table_add_system_columns(), dict_table_add_to_cache(): Add the parameter "heap". --- Additional changes that had to be merged from branches/zip: dict_table_add_system_columns(): New function, factored out from dict_table_add_to_cache(). mlog_parse_index(): Add some consistency checks, and make use of dict_table_add_system_columns(). storage/innobase/include/lock0lock.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1664: lock_number_of_rows_locked(): Fix a typo in comment, and make the comments in lock0lock.c and lock0lock.h identical. The typo was incorrectly fixed in r1623. storage/innobase/include/row0mysql.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. storage/innobase/include/row0sel.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. storage/innobase/include/trx0trx.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. storage/innobase/include/ut0mem.h: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1713: Fix typo in comment. storage/innobase/log/log0recv.c: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1657: recv_init_crash_recovery(): remove trailing white space storage/innobase/row/row0mysql.c: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. storage/innobase/row/row0sel.c: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. storage/innobase/trx/trx0trx.c: Apply InnoDB snapshot innodb-5.1-ss1726. Revision r1645: Fix for bug# 16979, this is a major change in InnoDB auto-inc handling. There is one test that fails as of this commit. The updated test case should be part of the snapshot from MySQL shortly. Fix for bug# 27950 - Init AUTOINC from delete_row(). Fix for bug# 28781 - Use value specified by MySQL, in update_row(). Summary of structural changes: ============================== InnoDB needs to hold a table level lock for AUTOINC allocations to overcome the non-determinism inherent in MySQL SBR for INSERT ... SELECT. In this fix for simple INSERT statements (including multi-value inserts), we try and avoid acquiring the special AUTOINC table level lock unless another transaction has already reserved the AUTOINC table level lock, in which case we fall back to the old behavior of acquiring the AUTOINC table level lock. The max AUTOINC value is now read directly using the low level interface of InnoDB. --- mysql-test/r/innodb.result | 2 +- storage/innobase/dict/dict0dict.c | 163 +++--- storage/innobase/dict/dict0mem.c | 76 +-- storage/innobase/handler/ha_innodb.cc | 720 ++++++++++++++++---------- storage/innobase/handler/ha_innodb.h | 8 + storage/innobase/ibuf/ibuf0ibuf.c | 20 +- storage/innobase/include/dict0dict.h | 50 +- storage/innobase/include/dict0mem.h | 8 +- storage/innobase/include/lock0lock.h | 2 +- storage/innobase/include/row0mysql.h | 1 + storage/innobase/include/row0sel.h | 10 + storage/innobase/include/trx0trx.h | 3 + storage/innobase/include/ut0mem.h | 2 +- storage/innobase/log/log0recv.c | 7 +- storage/innobase/row/row0mysql.c | 4 + storage/innobase/row/row0sel.c | 166 ++++++ storage/innobase/trx/trx0trx.c | 2 + 17 files changed, 788 insertions(+), 456 deletions(-) diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 804c4b81c17..a883b434c0b 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -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 diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index e2a9535dc8b..595dfb06ee5 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -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( diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index 9aa49dee745..e0b8bf15dd7 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -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; + } - col = (dict_col_t*) dict_table_get_nth_col(table, table->n_def - 1); + table->col_names = dict_add_col_name(table->col_names, + i, name, heap); + } - col->ind = table->n_def - 1; + col = (dict_col_t*) dict_table_get_nth_col(table, i); + + 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++; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index f606134578a..339a417a696 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -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 @@ retry: 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 @@ skip_field: } } +/************************************************************************ +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 @@ no_commit: 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 @@ no_commit: 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 */ - - 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 */ - - dict_table_autoinc_update(prebuilt->table, auto_inc); + /* 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; } - } - - /* 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. */ - - 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))))) { + /* Get the value that MySQL attempted to store in the table.*/ auto_inc = table->next_number_field->val_int(); - if (auto_inc != 0) { - dict_table_autoinc_update(prebuilt->table, auto_inc); + switch (error) { + case DB_DUPLICATE_KEY: + + /* 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))) { + + goto set_max_autoinc; + } + break; + + case SQLCOM_REPLACE: + case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE_SELECT: + goto set_max_autoinc; + break; + + default: + break; + } + + 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 @@ no_commit: 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,17 +4210,57 @@ 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)); } +/************************************************************************ +Get the index for a handle. Does not change active index.*/ + +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; + 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)); + + if (keynr != MAX_KEY && table->s->keys > 0) { + key = table->key_info + keynr; + + index = dict_table_get_index_noninline( + prebuilt->table, key->name); + } else { + index = dict_table_get_first_index_noninline(prebuilt->table); + } + + 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. */ @@ -4132,32 +4272,16 @@ ha_innobase::change_active_index( index, even if it was internally generated by InnoDB */ { - KEY* key=0; DBUG_ENTER("change_active_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; - - prebuilt->index = dict_table_get_index_noninline( - prebuilt->table, key->name); - } else { - prebuilt->index = dict_table_get_first_index_noninline( - prebuilt->table); - } + prebuilt->index = innobase_get_index(keynr); if (!prebuilt->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(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); + dict_table_autoinc_lock(prebuilt->table); + auto_inc = dict_table_autoinc_read(prebuilt->table); - if (auto_inc != 0) { - /* Already initialized */ - *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); } - error = row_lock_table_autoinc_for_mysql(prebuilt); + if (auto_inc == 0) { + dict_index_t* index; + ulint error = DB_SUCCESS; + const char* autoinc_col_name; - if (error != DB_SUCCESS) { - error = convert_error_code_to_mysql(error, user_thd); + ut_a(!innodb_table->autoinc_inited); - goto func_exit_early; - } + index = innobase_get_index(table->s->next_number_index); - /* Check again if someone has initialized the counter meanwhile */ - auto_inc = dict_table_autoinc_read(prebuilt->table); + autoinc_col_name = table->found_next_number_field->field_name; - if (auto_inc != 0) { - *ret = auto_inc; + error = row_search_max_autoinc( + index, autoinc_col_name, &auto_inc); - error = 0; - - goto func_exit_early; - } - - (void) extra(HA_EXTRA_KEYREAD); - index_init(table->s->next_number_index, 1); - - /* 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. */ - - /* Fetch all the columns in the key */ - - 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; - - /* 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 = 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); + dict_table_autoinc_unlock(prebuilt->table); - index_end(); - - *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 @@ func_exit_early: 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 */ diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 270b000dfc4..fe5ebd57990 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -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: diff --git a/storage/innobase/ibuf/ibuf0ibuf.c b/storage/innobase/ibuf/ibuf0ibuf.c index 4e291924e0e..44972356304 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.c +++ b/storage/innobase/ibuf/ibuf0ibuf.c @@ -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. */ diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index eb31043ecc3..2f038b21e8e 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -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. */ diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 647035c2fff..de6ee1227bf 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -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 diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 059c459c374..8b08b6284f6 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -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 diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 1448efe94fe..aabb7f5f047 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -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 */ }; diff --git a/storage/innobase/include/row0sel.h b/storage/innobase/include/row0sel.h index 96273a18cd5..4bde648f18e 100644 --- a/storage/innobase/include/row0sel.h +++ b/storage/innobase/include/row0sel.h @@ -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{ diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 2459e76c4c1..0455e9ea17a 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -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. */ diff --git a/storage/innobase/include/ut0mem.h b/storage/innobase/include/ut0mem.h index 90c16f4fad5..e56895bc142 100644 --- a/storage/innobase/include/ut0mem.h +++ b/storage/innobase/include/ut0mem.h @@ -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( diff --git a/storage/innobase/log/log0recv.c b/storage/innobase/log/log0recv.c index ce2fc3ed535..aef58b7b576 100644 --- a/storage/innobase/log/log0recv.c +++ b/storage/innobase/log/log0recv.c @@ -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 { diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index d51b7e1e0b5..b8d201e3da2 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -655,6 +655,8 @@ row_create_prebuilt( prebuilt->old_vers_heap = NULL; + prebuilt->last_value = 0; + return(prebuilt); } @@ -2894,6 +2896,8 @@ next_rec: 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); diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index e70b3b8671f..ee79bc94906 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -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); +} diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index b312e008cd2..ca9195599d9 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -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);