From f7b6a99d50709525bfeca917f879008955c060d8 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 29 Sep 2004 23:25:32 +0400 Subject: [PATCH 01/16] Fix for BUG#4785 part two: * If at least one of indexes is disabled, use data file size as an estimate for key file size. * Added handling for joined tables. --- myisam/myisampack.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/myisam/myisampack.c b/myisam/myisampack.c index 4b784641266..5efd6b3a7fd 100644 --- a/myisam/myisampack.c +++ b/myisam/myisampack.c @@ -111,6 +111,8 @@ typedef struct st_isam_mrg { uint ref_length; uint max_blob_length; my_off_t records; + /* true if at least one source file has at least one disabled index */ + my_bool src_file_has_indexes_disabled; } PACK_MRG_INFO; @@ -413,9 +415,16 @@ static bool open_isam_files(PACK_MRG_INFO *mrg,char **names,uint count) mrg->current=0; mrg->file=(MI_INFO**) my_malloc(sizeof(MI_INFO*)*count,MYF(MY_FAE)); mrg->free_file=1; + mrg->src_file_has_indexes_disabled= 0; for (i=0; i < count ; i++) { - if (!(mrg->file[i]=open_isam_file(names[i],O_RDONLY))) + if ((mrg->file[i]=open_isam_file(names[i],O_RDONLY))) + { + mrg->src_file_has_indexes_disabled |= + (mrg->file[i]->s->state.key_map != + (1ULL << mrg->file[i]->s->base.keys) - 1); + } + else goto error; } /* Check that files are identical */ @@ -2040,12 +2049,21 @@ static int save_state(MI_INFO *isam_file,PACK_MRG_INFO *mrg,my_off_t new_length, share->state.dellink= HA_OFFSET_ERROR; share->state.split=(ha_rows) mrg->records; share->state.version=(ulong) time((time_t*) 0); - share->state.key_map=0; + if (share->state.key_map != (1ULL << share->base.keys) - 1) + { + /* + Some indexes are disabled, cannot use current key_file_length value + as an estimate of upper bound of index file size. Use packed data file + size instead. + */ + share->state.state.key_file_length= new_length; + } /* - Don't save key_file_length here, keep key_file_length of original file - so "myisamchk -rq" can use this value (this is necessary because index - size cannot be easily calculated for fulltext keys) + If there are no disabled indexes, keep key_file_length value from + original file so "myisamchk -rq" can use this value (this is necessary + because index size cannot be easily calculated for fulltext keys) */ + share->state.key_map=0; for (key=0 ; key < share->base.keys ; key++) share->state.key_root[key]= HA_OFFSET_ERROR; for (key=0 ; key < share->state.header.max_block_size ; key++) @@ -2054,8 +2072,7 @@ static int save_state(MI_INFO *isam_file,PACK_MRG_INFO *mrg,my_off_t new_length, share->changed=1; /* Force write of header */ share->state.open_count=0; share->global_changed=0; - VOID(my_chsize(share->kfile, share->state.state.key_file_length, 0, - MYF(0))); + VOID(my_chsize(share->kfile, share->base.keystart, 0, MYF(0))); if (share->base.keys) isamchk_neaded=1; DBUG_RETURN(mi_state_info_write(share->kfile,&share->state,1+2)); @@ -2078,7 +2095,12 @@ static int save_state_mrg(File file,PACK_MRG_INFO *mrg,my_off_t new_length, state.state.del=0; state.state.empty=0; state.state.records=state.split=(ha_rows) mrg->records; - state.state.key_file_length=isam_file->s->base.keystart; + /* See comment above in save_state about key_file_length handling. */ + if (mrg->src_file_has_indexes_disabled) + { + isam_file->s->state.state.key_file_length= + max(isam_file->s->state.state.key_file_length, new_length); + } state.dellink= HA_OFFSET_ERROR; state.version=(ulong) time((time_t*) 0); state.key_map=0; From 634df1c19c5ab526fecd532285dd880b425da9f8 Mon Sep 17 00:00:00 2001 From: "dellis@goetia.(none)" <> Date: Sun, 3 Oct 2004 10:00:26 -0500 Subject: [PATCH 02/16] mysqld.cc: BUG #5731 Restrict key_buffer_size to 4GB on 64 bit platforms --- BitKeeper/etc/logging_ok | 1 + sql/mysqld.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 8f9c0f60122..66002912edf 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -24,6 +24,7 @@ bk@admin.bk brian@brian-akers-computer.local carsten@tsort.bitbybit.dk davida@isil.mysql.com +dellis@goetia.(none) dlenev@brandersnatch.localdomain dlenev@build.mysql.com dlenev@mysql.com diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 834cff0d869..c256c335399 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4144,7 +4144,7 @@ replicating a LOAD DATA INFILE command", {"key_buffer_size", OPT_KEY_BUFFER_SIZE, "The size of the buffer used for index blocks. Increase this to get better index handling (for all reads and multiple writes) to as much as you can afford; 64M on a 256M machine that mainly runs MySQL is quite common.", (gptr*) &keybuff_size, (gptr*) &keybuff_size, 0, GET_ULL, - REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, + REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, (uint32) ~0, MALLOC_OVERHEAD, IO_SIZE, 0}, {"long_query_time", OPT_LONG_QUERY_TIME, "Log all queries that have taken more than long_query_time seconds to execute to file.", From d2a5548431e2baa4d7f76145673be58c424b2cfe Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Fri, 8 Oct 2004 16:32:56 +0300 Subject: [PATCH 03/16] Using MySQL 4.0 with privilege tables from 5.0 caused a crash. (Fixed this by backporting some logic from 4.1) --- sql/sql_acl.cc | 56 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6e782f81ae5..b3bc5a1e4f2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -106,7 +106,7 @@ static HASH acl_check_hosts, hash_tables; static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; -static ulong get_access(TABLE *form,uint fieldnr); +static ulong get_access(TABLE *form, uint fieldnr, uint *next_field); static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); @@ -191,7 +191,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) ACL_HOST host; update_hostname(&host.host,get_field(&mem, table,0)); host.db= get_field(&mem, table,1); - host.access= get_access(table,2); + host.access= get_access(table,2,0); host.access= fix_rights_for_db(host.access); host.sort= get_sort(2,host.host.hostname,host.db); #ifndef TO_BE_REMOVED @@ -241,14 +241,15 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) user.host.hostname ? user.host.hostname : ""); /* purecov: tested */ continue; /* purecov: tested */ } + uint next_field; get_salt_from_password(user.salt,user.password); - user.access=get_access(table,3) & GLOBAL_ACLS; + user.access=get_access(table,3,&next_field) & GLOBAL_ACLS; user.sort=get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ { - char *ssl_type=get_field(&mem, table, 24); + char *ssl_type=get_field(&mem, table, next_field++); if (!ssl_type) user.ssl_type=SSL_TYPE_NONE; else if (!strcmp(ssl_type, "ANY")) @@ -258,16 +259,16 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) else /* !strcmp(ssl_type, "SPECIFIED") */ user.ssl_type=SSL_TYPE_SPECIFIED; - user.ssl_cipher= get_field(&mem, table, 25); - user.x509_issuer= get_field(&mem, table, 26); - user.x509_subject= get_field(&mem, table, 27); + user.ssl_cipher= get_field(&mem, table, next_field++); + user.x509_issuer= get_field(&mem, table, next_field++); + user.x509_subject= get_field(&mem, table, next_field++); - char *ptr = get_field(&mem, table, 28); - user.user_resource.questions=atoi(ptr); - ptr = get_field(&mem, table, 29); - user.user_resource.updates=atoi(ptr); - ptr = get_field(&mem, table, 30); - user.user_resource.connections=atoi(ptr); + char *ptr = get_field(&mem, table, next_field++); + user.user_resource.questions= ptr ? atoi(ptr) : 0; + ptr = get_field(&mem, table, next_field++); + user.user_resource.updates= ptr ? atoi(ptr): 0; + ptr = get_field(&mem, table, next_field++); + user.user_resource.connections=ptr ? atoi(ptr) : 0; if (user.user_resource.questions || user.user_resource.updates || user.user_resource.connections) mqh_used=1; @@ -313,7 +314,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) continue; } db.user=get_field(&mem, table,2); - db.access=get_access(table,3); + db.access=get_access(table,3,0); db.access=fix_rights_for_db(db.access); db.sort=get_sort(3,db.host.hostname,db.db,db.user); #ifndef TO_BE_REMOVED @@ -423,11 +424,24 @@ void acl_reload(THD *thd) /* Get all access bits from table after fieldnr - We know that the access privileges ends when there is no more fields - or the field is not an enum with two elements. + + IMPLEMENTATION + We know that the access privileges ends when there is no more fields + or the field is not an enum with two elements. + + SYNOPSIS + get_access() + form an open table to read privileges from. + The record should be already read in table->record[0] + fieldnr number of the first privilege (that is ENUM('N','Y') field + next_field on return - number of the field next to the last ENUM + (unless next_field == 0) + + RETURN VALUE + privilege mask */ -static ulong get_access(TABLE *form, uint fieldnr) +static ulong get_access(TABLE *form, uint fieldnr, uint *next_field) { ulong access_bits=0,bit; char buff[2]; @@ -437,12 +451,14 @@ static ulong get_access(TABLE *form, uint fieldnr) for (pos=form->field+fieldnr, bit=1; *pos && (*pos)->real_type() == FIELD_TYPE_ENUM && ((Field_enum*) (*pos))->typelib->count == 2 ; - pos++ , bit<<=1) + pos++, fieldnr++, bit<<=1) { (*pos)->val_str(&res,&res); if (toupper(res[0]) == 'Y') access_bits|= bit; } + if (next_field) + *next_field=fieldnr; return access_bits; } @@ -1395,7 +1411,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, if (priv & rights) // set requested privileges (*tmp_field)->store(&what,1); } - rights=get_access(table,3); + rights=get_access(table,3,0); DBUG_PRINT("info",("table->fields: %d",table->fields)); if (table->fields >= 31) /* From 4.0.0 we have more fields */ { @@ -1554,7 +1570,7 @@ static int replace_db_table(TABLE *table, const char *db, if (priv & store_rights) // do it if priv is chosen table->field [i]->store(&what,1); // set requested privileges } - rights=get_access(table,3); + rights=get_access(table,3,0); rights=fix_rights_for_db(rights); if (old_row_exists) From af3681f2d910caa0b08add974ca5375345d22782 Mon Sep 17 00:00:00 2001 From: "heikki@hundin.mysql.fi" <> Date: Sat, 9 Oct 2004 14:13:40 +0300 Subject: [PATCH 04/16] trx0rec.c: Fix bug #5960: if one updated a column so that its size changed, or updated it to an externally stored (TEXT or BLOB) value, then ANOTHER externally stored column would show up as 512 bytes of good data + 20 bytes of garbage in a consistent read that fetched the old version of the row --- innobase/trx/trx0rec.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/innobase/trx/trx0rec.c b/innobase/trx/trx0rec.c index 79fad312e8e..0e1842ef4a0 100644 --- a/innobase/trx/trx0rec.c +++ b/innobase/trx/trx0rec.c @@ -1257,7 +1257,7 @@ trx_undo_prev_version_build( ibool dummy_extern; byte* buf; ulint err; - ulint i; + #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ @@ -1367,7 +1367,18 @@ trx_undo_prev_version_build( } if (row_upd_changes_field_size_or_external(rec, index, update)) { + ulint* ext_vect; + ulint n_ext_vect; + /* We have to set the appropriate extern storage bits in the + old version of the record: the extern bits in rec for those + fields that update does NOT update, as well as the the bits for + those fields that update updates to become externally stored + fields. Store the info to ext_vect: */ + + ext_vect = mem_alloc(sizeof(ulint) * rec_get_n_fields(rec)); + n_ext_vect = btr_push_update_extern_fields(ext_vect, rec, + update); entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap); row_upd_index_replace_new_col_vals(entry, index, update, heap); @@ -1375,6 +1386,11 @@ trx_undo_prev_version_build( buf = mem_heap_alloc(heap, rec_get_converted_size(entry)); *old_vers = rec_convert_dtuple_to_rec(buf, entry); + + /* Now set the extern bits in the old version of the record */ + rec_set_field_extern_bits(*old_vers, ext_vect, n_ext_vect, + NULL); + mem_free(ext_vect); } else { buf = mem_heap_alloc(heap, rec_get_size(rec)); @@ -1383,15 +1399,5 @@ trx_undo_prev_version_build( row_upd_rec_in_place(*old_vers, update); } - for (i = 0; i < upd_get_n_fields(update); i++) { - - if (upd_get_nth_field(update, i)->extern_storage) { - - rec_set_nth_field_extern_bit(*old_vers, - upd_get_nth_field(update, i)->field_no, - TRUE, NULL); - } - } - return(DB_SUCCESS); } From 13af58aab6e009a024ce7f910ffb1ddd31e32b1c Mon Sep 17 00:00:00 2001 From: "guilhem@mysql.com" <> Date: Mon, 11 Oct 2004 11:01:38 +0200 Subject: [PATCH 05/16] Fix for BUG#5949 "error code 1223 in binlog when using innobackup": when one connection had done FLUSH TABLES WITH READ LOCK, some updates, and then COMMIT, it was accepted but my_error() was called and so, while client got no error, error was logged in binlog. We now don't call my_error() in this case; we assume the connection know what it does. This problem was specific to 4.0.21. The change is needed to make replication work with existing versions of innobackup. --- mysql-test/r/rpl_commit_after_flush.result | 13 +++++++++++++ mysql-test/t/rpl_commit_after_flush.test | 17 +++++++++++++++++ sql/lock.cc | 10 ++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 mysql-test/r/rpl_commit_after_flush.result create mode 100644 mysql-test/t/rpl_commit_after_flush.test diff --git a/mysql-test/r/rpl_commit_after_flush.result b/mysql-test/r/rpl_commit_after_flush.result new file mode 100644 index 00000000000..8cdc7e986ab --- /dev/null +++ b/mysql-test/r/rpl_commit_after_flush.result @@ -0,0 +1,13 @@ +slave stop; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +slave start; +create table t1 (a int) type=innodb; +begin; +insert into t1 values(1); +flush tables with read lock; +commit; +unlock tables; +drop table t1; diff --git a/mysql-test/t/rpl_commit_after_flush.test b/mysql-test/t/rpl_commit_after_flush.test new file mode 100644 index 00000000000..edbbd1bfad6 --- /dev/null +++ b/mysql-test/t/rpl_commit_after_flush.test @@ -0,0 +1,17 @@ +source include/master-slave.inc; +source include/have_innodb.inc; +create table t1 (a int) type=innodb; +begin; +insert into t1 values(1); +flush tables with read lock; +commit; +save_master_pos; +connection slave; +sync_with_master; +# cleanup +connection master; +unlock tables; +drop table t1; +save_master_pos; +connection slave; +sync_with_master; diff --git a/sql/lock.cc b/sql/lock.cc index dd2b61b65d2..bf0160291f8 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -751,9 +751,15 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, bool is_not_commi { if (thd->global_read_lock) // This thread had the read locks { - my_error(ER_CANT_UPDATE_WITH_READLOCK,MYF(0)); + if (is_not_commit) + my_error(ER_CANT_UPDATE_WITH_READLOCK,MYF(0)); (void) pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(1); + /* + We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does. + This allowance is needed to not break existing versions of innobackup + which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT. + */ + DBUG_RETURN(is_not_commit); } old_message=thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting for release of readlock"); From 41ab008a366b67d13431cf4977572713b9e1939e Mon Sep 17 00:00:00 2001 From: "heikki@hundin.mysql.fi" <> Date: Tue, 12 Oct 2004 18:12:00 +0300 Subject: [PATCH 06/16] ha_innodb.cc: Change error code to HA_ERR_ROW_IS_REFERENCED if we cannot DROP a parent table referenced by a FOREIGN KEY constraint; this error number is less misleading than the previous value HA_ERR_CANNOT_ADD_FOREIGN, but misleading still; we should introduce to 5.0 a proper MySQL error code --- sql/ha_innodb.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index f149af85b3f..fa9537b3217 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -285,7 +285,7 @@ convert_error_code_to_mysql( } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) { - return(HA_ERR_CANNOT_ADD_FOREIGN); /* TODO: This is a bit + return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit misleading, a new MySQL error code should be introduced */ } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { From edb93ca441df3257bccc320496a765b93779ce66 Mon Sep 17 00:00:00 2001 From: "marko@hundin.mysql.fi" <> Date: Wed, 13 Oct 2004 22:54:21 +0300 Subject: [PATCH 07/16] Added startup option and settable session variable innodb_table_locks_old_behavior: do not acquire an InnoDB table lock for LOCK TABLES, as in mysql-4.0.18 and earlier. --- sql/ha_innodb.cc | 3 ++- sql/mysqld.cc | 7 ++++++- sql/set_var.cc | 4 ++++ sql/sql_class.h | 3 +++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index fa9537b3217..44d050d14aa 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -4693,7 +4693,8 @@ ha_innobase::external_lock( } if (prebuilt->select_lock_type != LOCK_NONE) { - if (thd->in_lock_tables) { + if (thd->in_lock_tables && + !thd->variables.innodb_table_locks_old_behavior) { ulint error; error = row_lock_table_for_mysql(prebuilt); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 89d71ecbfa2..8b478882451 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3526,6 +3526,7 @@ enum options_mysqld { OPT_INNODB_FORCE_RECOVERY, OPT_INNODB_STATUS_FILE, OPT_INNODB_MAX_DIRTY_PAGES_PCT, + OPT_INNODB_TABLE_LOCKS_OLD_BEHAVIOR, OPT_BDB_CACHE_SIZE, OPT_BDB_LOG_BUFFER_SIZE, OPT_BDB_MAX_LOCK, @@ -3699,7 +3700,11 @@ struct my_option my_long_options[] = {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT, "Percentage of dirty pages allowed in bufferpool", (gptr*) &srv_max_buf_pool_modified_pct, (gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0}, - + {"innodb_table_locks_old_behavior", OPT_INNODB_TABLE_LOCKS_OLD_BEHAVIOR, + "Disable InnoDB locking in LOCK TABLES", + (gptr*) &global_system_variables.innodb_table_locks_old_behavior, + (gptr*) &global_system_variables.innodb_table_locks_old_behavior, + 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif /* End HAVE_INNOBASE_DB */ {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, diff --git a/sql/set_var.cc b/sql/set_var.cc index 4b66a621f62..9b2db7a0802 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -263,6 +263,8 @@ sys_var_thd_ulong sys_net_wait_timeout("wait_timeout", #ifdef HAVE_INNOBASE_DB sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct", &srv_max_buf_pool_modified_pct); +sys_var_thd_bool sys_innodb_table_locks_old_behavior("innodb_table_locks_old_behavior", + &SV::innodb_table_locks_old_behavior); #endif @@ -449,6 +451,7 @@ sys_var *sys_variables[]= &sys_os, #ifdef HAVE_INNOBASE_DB &sys_innodb_max_dirty_pages_pct, + &sys_innodb_table_locks_old_behavior, #endif &sys_unique_checks }; @@ -520,6 +523,7 @@ struct show_var_st init_vars[]= { {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, {sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS}, + {sys_innodb_table_locks_old_behavior.name, (char*) &sys_innodb_table_locks_old_behavior, SHOW_SYS}, #endif {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS}, {sys_join_buffer_size.name, (char*) &sys_join_buffer_size, SHOW_SYS}, diff --git a/sql/sql_class.h b/sql/sql_class.h index af53574eeaf..8c383d5848d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -338,6 +338,9 @@ struct system_variables my_bool low_priority_updates; my_bool new_mode; my_bool query_cache_wlock_invalidate; +#ifdef HAVE_INNOBASE_DB + my_bool innodb_table_locks_old_behavior; +#endif /* HAVE_INNOBASE_DB */ CONVERT *convert_set; }; From 150ab94e0adec270d7a106d629e8963f43cf612d Mon Sep 17 00:00:00 2001 From: "ingo@mysql.com" <> Date: Thu, 14 Oct 2004 20:02:56 +0200 Subject: [PATCH 08/16] BUG#5625 - MyISAM Index corruption on ALTER TABLE x ENABLE KEYS due to full tmpdir. Added a try to a normal repair() if repair_by_sort() failed. This was not done with ENABLE KEYS and OPTIMIZE TABLE. Fixed error code handling in mysql_alter_table(). --- sql/ha_myisam.cc | 18 ++++++++++++++++-- sql/sql_table.cc | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 2b7b8f436b1..71623238bd9 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -525,6 +525,7 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) { + int error; if (!file) return HA_ADMIN_INTERNAL_ERROR; MI_CHECK param; @@ -534,7 +535,14 @@ int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) param.testflag = (check_opt->flags | T_SILENT | T_FORCE_CREATE | T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX); param.sort_buffer_length= check_opt->sort_buffer_size; - return repair(thd,param,1); + if ((error= repair(thd,param,1)) && param.retry_repair) + { + sql_print_warning("Warning: Optimize table got errno %d, retrying", + my_errno); + param.testflag&= ~T_REP_BY_SORT; + error= repair(thd,param,1); + } + return error; } @@ -744,7 +752,13 @@ bool ha_myisam::activate_all_index(THD *thd) param.myf_rw&= ~MY_WAIT_IF_FULL; param.sort_buffer_length= thd->variables.myisam_sort_buff_size; param.tmpdir=mysql_tmpdir; - error=repair(thd,param,0) != HA_ADMIN_OK; + if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair) + { + sql_print_warning("Warning: Enabling keys got errno %d, retrying", + my_errno); + param.testflag&= ~(T_REP_BY_SORT | T_QUICK); + error= (repair(thd,param,0) != HA_ADMIN_OK); + } info(HA_STATUS_CONST); thd->proc_info=save_proc_info; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index a15f8b65006..0f2116dbcdb 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1614,7 +1614,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(pthread_mutex_lock(&LOCK_open)); wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); VOID(pthread_mutex_unlock(&LOCK_open)); - error= table->file->activate_all_index(thd); + error= (table->file->activate_all_index(thd) ? -1 : 0); /* COND_refresh will be signaled in close_thread_tables() */ break; case DISABLE: From b50688d5e9c7163da29336d803d24a3735aac5b6 Mon Sep 17 00:00:00 2001 From: "hartmut@mysql.com" <> Date: Fri, 15 Oct 2004 15:33:30 +0200 Subject: [PATCH 09/16] the $^ directive is a GNU make extension and not really needed here so lets get rid of it (Bug #6112) --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 37b0432b98d..d4073ea441b 100644 --- a/configure.in +++ b/configure.in @@ -40,12 +40,12 @@ do case $host_os in netware* | modesto*) echo "$i/errmsg.sys: $i/errmsg.txt - \$(top_builddir)/extra/comp_err.linux \$^ $i/errmsg.sys" \ + \$(top_builddir)/extra/comp_err.linux $i/errmsg.txt $i/errmsg.sys" \ >> $AVAILABLE_LANGUAGES_ERRORS_RULES ;; *) echo "$i/errmsg.sys: $i/errmsg.txt - \$(top_builddir)/extra/comp_err \$^ $i/errmsg.sys" \ + \$(top_builddir)/extra/comp_err $i/errmsg.txt $i/errmsg.sys" \ >> $AVAILABLE_LANGUAGES_ERRORS_RULES ;; esac From 1524f2d89dc35c15aeb74af77492e4665311457d Mon Sep 17 00:00:00 2001 From: "paul@ice.snake.net" <> Date: Fri, 15 Oct 2004 23:16:02 -0500 Subject: [PATCH 10/16] texi2html: Change parsing of @image argument. --- Docs/Support/texi2html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docs/Support/texi2html b/Docs/Support/texi2html index 5dda7c8bbd5..8067d8f72ce 100755 --- a/Docs/Support/texi2html +++ b/Docs/Support/texi2html @@ -1811,7 +1811,7 @@ sub fix_image { my($text) = @_; my($arg1, $ext); - $text =~ /^([^,]*)$/; + $text =~ /^([^,]*)/; die "error in image: '$text'" unless defined($1); $arg1 = $1; $arg1 =~ s/@@/@/g; From b9509655a52f629802358ac23d5533337a09ba1e Mon Sep 17 00:00:00 2001 From: "dellis@goetia.(none)" <> Date: Sun, 17 Oct 2004 18:44:51 -0500 Subject: [PATCH 11/16] mysqld.cc: BUG #5731 key_buffer_size not properly restricted to 4GB; use UINT_MAX32 for clarity. --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c256c335399..ff867b010b0 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4144,7 +4144,7 @@ replicating a LOAD DATA INFILE command", {"key_buffer_size", OPT_KEY_BUFFER_SIZE, "The size of the buffer used for index blocks. Increase this to get better index handling (for all reads and multiple writes) to as much as you can afford; 64M on a 256M machine that mainly runs MySQL is quite common.", (gptr*) &keybuff_size, (gptr*) &keybuff_size, 0, GET_ULL, - REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, (uint32) ~0, MALLOC_OVERHEAD, + REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, UINT_MAX32, MALLOC_OVERHEAD, IO_SIZE, 0}, {"long_query_time", OPT_LONG_QUERY_TIME, "Log all queries that have taken more than long_query_time seconds to execute to file.", From c01c8f99ea16fb2029efade30b8bf18ae314c03a Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Tue, 19 Oct 2004 09:50:47 +0500 Subject: [PATCH 12/16] libmysql.c: New function mysql_hex_string() --- libmysql/libmysql.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index a591ad9317d..9257bf0efd0 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -3153,6 +3153,39 @@ void my_net_local_init(NET *net) net->max_packet_size= max(net_buffer_length, max_allowed_packet); } +/* + This function is used to create HEX string that you + can use in a SQL statement in of the either ways: + INSERT INTO blob_column VALUES (0xAABBCC); (any MySQL version) + INSERT INTO blob_column VALUES (X'AABBCC'); (4.1 and higher) + + The string in "from" is encoded to a HEX string. + The result is placed in "to" and a terminating null byte is appended. + + The string pointed to by "from" must be "length" bytes long. + You must allocate the "to" buffer to be at least length*2+1 bytes long. + Each character needs two bytes, and you need room for the terminating + null byte. When mysql_hex_string() returns, the contents of "to" will + be a null-terminated string. The return value is the length of the + encoded string, not including the terminating null character. +*/ + +unsigned long +mysql_hex_string(char *to, const char *from, unsigned long length) +{ + char *to0= to; + const char *end; + static char hex[]= "0123456789ABCDEF"; + + for (end= from + length; from < end; from++) + { + *to++= hex[((unsigned char) *from) >> 4]; + *to++= hex[((unsigned char) *from) & 0x0F]; + } + *to= '\0'; + return to-to0; +} + /* Add escape characters to a string (blob?) to make it suitable for a insert to should at least have place for length*2+1 chars From 462aed6c991fe5645759b40fb25f83826d76e50d Mon Sep 17 00:00:00 2001 From: "paul@ice.snake.net" <> Date: Tue, 19 Oct 2004 07:44:20 -0500 Subject: [PATCH 13/16] libmysql.c: Add note to mysql_hex_string() comment. --- libmysql/libmysql.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 9257bf0efd0..73f8f8f8b0b 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -3168,6 +3168,9 @@ void my_net_local_init(NET *net) null byte. When mysql_hex_string() returns, the contents of "to" will be a null-terminated string. The return value is the length of the encoded string, not including the terminating null character. + + The return value does not contain any leading 0x or a leading X' and + trailing '. The caller must supply whichever of those is desired. */ unsigned long From 20ac420eb1900b3fa21a6b1cd772b51eb6f7c1c4 Mon Sep 17 00:00:00 2001 From: "heikki@hundin.mysql.fi" <> Date: Tue, 19 Oct 2004 18:58:53 +0300 Subject: [PATCH 14/16] trx0rec.c: test --- innobase/trx/trx0rec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/innobase/trx/trx0rec.c b/innobase/trx/trx0rec.c index 0e1842ef4a0..a5623b4d0c7 100644 --- a/innobase/trx/trx0rec.c +++ b/innobase/trx/trx0rec.c @@ -1377,7 +1377,7 @@ trx_undo_prev_version_build( fields. Store the info to ext_vect: */ ext_vect = mem_alloc(sizeof(ulint) * rec_get_n_fields(rec)); - n_ext_vect = btr_push_update_extern_fields(ext_vect, rec, + n_ext_vect = btr_push_update_extern_fields(ext_vect, rec, update); entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap); From 1f8b3d0f22070d450226ef4f6e0e31c8ef8cd401 Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Wed, 20 Oct 2004 11:24:08 +0300 Subject: [PATCH 15/16] Code cleanups (done during review of new code) Rename innodb_table_locks_old_behavior -> innodb_table_locks Set innodb_table_locks to off by default to get same behaviour as in MySQL 4.0.20 (This means that Innodb ignore table locks by default, which makes it easier to combine MyISAM and InnoDB to simulate a transaction) --- libmysql/libmysql.c | 10 +++---- myisam/myisampack.c | 12 ++++----- mysql-test/r/innodb-lock.result | 26 +++++++++++++++++++ mysql-test/t/innodb-lock.test | 46 ++++++++++++++++++++++++++++++++- sql/ha_innodb.cc | 2 +- sql/mysqld.cc | 10 +++---- sql/set_var.cc | 8 +++--- sql/sql_class.h | 2 +- 8 files changed, 91 insertions(+), 25 deletions(-) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 9257bf0efd0..09675bc7fab 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -3170,20 +3170,18 @@ void my_net_local_init(NET *net) encoded string, not including the terminating null character. */ -unsigned long -mysql_hex_string(char *to, const char *from, unsigned long length) +ulong mysql_hex_string(char *to, const char *from, ulong length) { char *to0= to; const char *end; - static char hex[]= "0123456789ABCDEF"; for (end= from + length; from < end; from++) { - *to++= hex[((unsigned char) *from) >> 4]; - *to++= hex[((unsigned char) *from) & 0x0F]; + *to++= _dig_vec[((unsigned char) *from) >> 4]; + *to++= _dig_vec[((unsigned char) *from) & 0x0F]; } *to= '\0'; - return to-to0; + return (ulong) (to-to0); } /* diff --git a/myisam/myisampack.c b/myisam/myisampack.c index 5efd6b3a7fd..0dde1916f03 100644 --- a/myisam/myisampack.c +++ b/myisam/myisampack.c @@ -418,14 +418,12 @@ static bool open_isam_files(PACK_MRG_INFO *mrg,char **names,uint count) mrg->src_file_has_indexes_disabled= 0; for (i=0; i < count ; i++) { - if ((mrg->file[i]=open_isam_file(names[i],O_RDONLY))) - { - mrg->src_file_has_indexes_disabled |= - (mrg->file[i]->s->state.key_map != - (1ULL << mrg->file[i]->s->base.keys) - 1); - } - else + if (!(mrg->file[i]=open_isam_file(names[i],O_RDONLY))) goto error; + + mrg->src_file_has_indexes_disabled|= ((mrg->file[i]->s->state.key_map != + (((ulonglong) 1) << + mrg->file[i]->s->base. keys) - 1)); } /* Check that files are identical */ for (j=0 ; j < count-1 ; j++) diff --git a/mysql-test/r/innodb-lock.result b/mysql-test/r/innodb-lock.result index cf00adb30ae..f87f221ea66 100644 --- a/mysql-test/r/innodb-lock.result +++ b/mysql-test/r/innodb-lock.result @@ -1,4 +1,30 @@ drop table if exists t1; +select @@innodb_table_locks; +@@innodb_table_locks +0 +set @@innodb_table_locks=1; +create table t1 (id integer, x integer) engine=INNODB; +insert into t1 values(0, 0); +set autocommit=0; +SELECT * from t1 where id = 0 FOR UPDATE; +id x +0 0 +set autocommit=0; +lock table t1 write; +update t1 set x=1 where id = 0; +select * from t1; +id x +0 1 +commit; +update t1 set x=2 where id = 0; +commit; +unlock tables; +select * from t1; +id x +0 2 +commit; +drop table t1; +set @@innodb_table_locks=0; create table t1 (id integer, x integer) engine=INNODB; insert into t1 values(0, 0); set autocommit=0; diff --git a/mysql-test/t/innodb-lock.test b/mysql-test/t/innodb-lock.test index 43a175508b4..11395b301c4 100644 --- a/mysql-test/t/innodb-lock.test +++ b/mysql-test/t/innodb-lock.test @@ -5,9 +5,17 @@ connect (con2,localhost,root,,); drop table if exists t1; # -# Testing of explicit table locks +# Check and select innodb lock type # +select @@innodb_table_locks; + +# +# Testing of explicit table locks with enforced table locks +# + +set @@innodb_table_locks=1; + connection con1; create table t1 (id integer, x integer) engine=INNODB; insert into t1 values(0, 0); @@ -38,3 +46,39 @@ select * from t1; commit; drop table t1; + +# +# Try with old lock method (where LOCK TABLE is ignored) +# + +set @@innodb_table_locks=0; + +create table t1 (id integer, x integer) engine=INNODB; +insert into t1 values(0, 0); +set autocommit=0; +SELECT * from t1 where id = 0 FOR UPDATE; + +connection con2; +set autocommit=0; + +# The following statement should hang because con1 is locking the page +--send +lock table t1 write; +--sleep 2; + +connection con1; +update t1 set x=1 where id = 0; +select * from t1; +commit; + +connection con2; +reap; +update t1 set x=2 where id = 0; +commit; +unlock tables; + +connection con1; +select * from t1; +commit; + +drop table t1; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 44d050d14aa..e747ff3210c 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -4694,7 +4694,7 @@ ha_innobase::external_lock( if (prebuilt->select_lock_type != LOCK_NONE) { if (thd->in_lock_tables && - !thd->variables.innodb_table_locks_old_behavior) { + thd->variables.innodb_table_locks) { ulint error; error = row_lock_table_for_mysql(prebuilt); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 226d4c113a5..b7eafcbcc14 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3526,7 +3526,7 @@ enum options_mysqld { OPT_INNODB_FORCE_RECOVERY, OPT_INNODB_STATUS_FILE, OPT_INNODB_MAX_DIRTY_PAGES_PCT, - OPT_INNODB_TABLE_LOCKS_OLD_BEHAVIOR, + OPT_INNODB_TABLE_LOCKS, OPT_BDB_CACHE_SIZE, OPT_BDB_LOG_BUFFER_SIZE, OPT_BDB_MAX_LOCK, @@ -3700,10 +3700,10 @@ struct my_option my_long_options[] = {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT, "Percentage of dirty pages allowed in bufferpool", (gptr*) &srv_max_buf_pool_modified_pct, (gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0}, - {"innodb_table_locks_old_behavior", OPT_INNODB_TABLE_LOCKS_OLD_BEHAVIOR, - "Disable InnoDB locking in LOCK TABLES", - (gptr*) &global_system_variables.innodb_table_locks_old_behavior, - (gptr*) &global_system_variables.innodb_table_locks_old_behavior, + {"innodb_table_locks", OPT_INNODB_TABLE_LOCKS, + "If Innodb should enforce LOCK TABLE", + (gptr*) &global_system_variables.innodb_table_locks, + (gptr*) &global_system_variables.innodb_table_locks, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif /* End HAVE_INNOBASE_DB */ {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, diff --git a/sql/set_var.cc b/sql/set_var.cc index 9b2db7a0802..a6a5ea72c71 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -263,8 +263,8 @@ sys_var_thd_ulong sys_net_wait_timeout("wait_timeout", #ifdef HAVE_INNOBASE_DB sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct", &srv_max_buf_pool_modified_pct); -sys_var_thd_bool sys_innodb_table_locks_old_behavior("innodb_table_locks_old_behavior", - &SV::innodb_table_locks_old_behavior); +sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks", + &SV::innodb_table_locks); #endif @@ -451,7 +451,7 @@ sys_var *sys_variables[]= &sys_os, #ifdef HAVE_INNOBASE_DB &sys_innodb_max_dirty_pages_pct, - &sys_innodb_table_locks_old_behavior, + &sys_innodb_table_locks, #endif &sys_unique_checks }; @@ -523,7 +523,7 @@ struct show_var_st init_vars[]= { {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, {sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS}, - {sys_innodb_table_locks_old_behavior.name, (char*) &sys_innodb_table_locks_old_behavior, SHOW_SYS}, + {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS}, #endif {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS}, {sys_join_buffer_size.name, (char*) &sys_join_buffer_size, SHOW_SYS}, diff --git a/sql/sql_class.h b/sql/sql_class.h index 8c383d5848d..4250ebdd568 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -339,7 +339,7 @@ struct system_variables my_bool new_mode; my_bool query_cache_wlock_invalidate; #ifdef HAVE_INNOBASE_DB - my_bool innodb_table_locks_old_behavior; + my_bool innodb_table_locks; #endif /* HAVE_INNOBASE_DB */ CONVERT *convert_set; From b32ffec8fdc9d05377e69c8b1abb92d562670324 Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Wed, 20 Oct 2004 16:04:28 +0300 Subject: [PATCH 16/16] Fix test case for innodb-lock --- mysql-test/r/innodb-lock.result | 30 +++++++++++------- mysql-test/t/innodb-lock-master.opt | 1 + mysql-test/t/innodb-lock.test | 47 ++++++++++++++++++----------- mysys/thr_lock.c | 12 ++++++-- sql/mysqld.cc | 2 +- sql/sql_base.cc | 2 ++ 6 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 mysql-test/t/innodb-lock-master.opt diff --git a/mysql-test/r/innodb-lock.result b/mysql-test/r/innodb-lock.result index f87f221ea66..407a85ed038 100644 --- a/mysql-test/r/innodb-lock.result +++ b/mysql-test/r/innodb-lock.result @@ -1,7 +1,7 @@ -drop table if exists t1; select @@innodb_table_locks; @@innodb_table_locks -0 +1 +drop table if exists t1; set @@innodb_table_locks=1; create table t1 (id integer, x integer) engine=INNODB; insert into t1 values(0, 0); @@ -25,24 +25,32 @@ id x commit; drop table t1; set @@innodb_table_locks=0; -create table t1 (id integer, x integer) engine=INNODB; -insert into t1 values(0, 0); -set autocommit=0; +create table t1 (id integer primary key, x integer) engine=INNODB; +insert into t1 values(0, 0),(1,1),(2,2); +commit; SELECT * from t1 where id = 0 FOR UPDATE; id x 0 0 set autocommit=0; +set @@innodb_table_locks=0; lock table t1 write; -update t1 set x=1 where id = 0; -select * from t1; +update t1 set x=10 where id = 2; +SELECT * from t1 where id = 2; id x -0 1 +2 2 +UPDATE t1 set x=3 where id = 2; commit; -update t1 set x=2 where id = 0; +SELECT * from t1; +id x +0 0 +1 1 +2 3 commit; unlock tables; +commit; select * from t1; id x -0 2 -commit; +0 0 +1 1 +2 10 drop table t1; diff --git a/mysql-test/t/innodb-lock-master.opt b/mysql-test/t/innodb-lock-master.opt new file mode 100644 index 00000000000..403fcde87ed --- /dev/null +++ b/mysql-test/t/innodb-lock-master.opt @@ -0,0 +1 @@ +--innodb-table-lock=1 diff --git a/mysql-test/t/innodb-lock.test b/mysql-test/t/innodb-lock.test index 11395b301c4..430369f4fda 100644 --- a/mysql-test/t/innodb-lock.test +++ b/mysql-test/t/innodb-lock.test @@ -1,9 +1,5 @@ -- source include/have_innodb.inc -connect (con1,localhost,root,,); -connect (con2,localhost,root,,); -drop table if exists t1; - # # Check and select innodb lock type # @@ -14,6 +10,14 @@ select @@innodb_table_locks; # Testing of explicit table locks with enforced table locks # +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +drop table if exists t1; + +# +# Testing of explicit table locks with enforced table locks +# + set @@innodb_table_locks=1; connection con1; @@ -48,37 +52,44 @@ commit; drop table t1; # -# Try with old lock method (where LOCK TABLE is ignored) +# Try with old lock method (where LOCK TABLE is ignored by InnoDB) # set @@innodb_table_locks=0; -create table t1 (id integer, x integer) engine=INNODB; -insert into t1 values(0, 0); -set autocommit=0; +create table t1 (id integer primary key, x integer) engine=INNODB; +insert into t1 values(0, 0),(1,1),(2,2); +commit; SELECT * from t1 where id = 0 FOR UPDATE; connection con2; set autocommit=0; +set @@innodb_table_locks=0; -# The following statement should hang because con1 is locking the page ---send +# The following statement should work becase innodb doesn't check table locks lock table t1 write; ---sleep 2; connection con1; -update t1 set x=1 where id = 0; -select * from t1; -commit; + +# This will be locked by MySQL +--send +update t1 set x=10 where id = 2; +--sleep 2 connection con2; -reap; -update t1 set x=2 where id = 0; + +# Note that we will get a deadlock if we try to select any rows marked +# for update by con1 ! + +SELECT * from t1 where id = 2; +UPDATE t1 set x=3 where id = 2; +commit; +SELECT * from t1; commit; unlock tables; connection con1; -select * from t1; +reap; commit; - +select * from t1; drop table t1; diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index d5236cb1ef9..935ed4ea282 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -552,8 +552,14 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) !lock->write_wait.data && lock->write.data->type == TL_WRITE_ALLOW_WRITE)) { - /* We have already got a write lock or all locks are - TL_WRITE_ALLOW_WRITE */ + /* + We have already got a write lock or all locks are + TL_WRITE_ALLOW_WRITE + */ + DBUG_PRINT("info", ("write_wait.data: 0x%lx old_type: %d", + (ulong) lock->write_wait.data, + lock->write.data->type)); + (*lock->write.last)=data; /* Add to running fifo */ data->prev=lock->write.last; lock->write.last= &data->next; @@ -568,6 +574,8 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) } else { + DBUG_PRINT("info", ("write_wait.data: 0x%lx", + (ulong) lock->write_wait.data)); if (!lock->write_wait.data) { /* no scheduled write locks */ if (lock_type == TL_WRITE_CONCURRENT_INSERT && diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b7eafcbcc14..5838bd909dd 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3704,7 +3704,7 @@ struct my_option my_long_options[] = "If Innodb should enforce LOCK TABLE", (gptr*) &global_system_variables.innodb_table_locks, (gptr*) &global_system_variables.innodb_table_locks, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, #endif /* End HAVE_INNOBASE_DB */ {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9810ec6c3d6..72400bf0abb 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -826,6 +826,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, DBUG_RETURN(0); } table->query_id=thd->query_id; + DBUG_PRINT("info",("Using temporary table")); goto reset; } } @@ -840,6 +841,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->query_id != thd->query_id) { table->query_id=thd->query_id; + DBUG_PRINT("info",("Using locked table")); goto reset; } }