From 259990d70468e29074f86248aef1b38704a67630 Mon Sep 17 00:00:00 2001 From: "gni/root@dev3-127.(none)" <> Date: Sun, 8 Oct 2006 11:24:53 +0800 Subject: [PATCH 01/73] BUG #21858 Make sure retry when EINTR returns, which decreases memory leak chance. --- ndb/src/common/util/File.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ndb/src/common/util/File.cpp b/ndb/src/common/util/File.cpp index 12626f29e7d..00741d3a576 100644 --- a/ndb/src/common/util/File.cpp +++ b/ndb/src/common/util/File.cpp @@ -123,13 +123,25 @@ bool File_class::close() { bool rc = true; + int retval = 0; + if (m_file != NULL) { ::fflush(m_file); - rc = (::fclose(m_file) == 0 ? true : false); - m_file = NULL; // Try again? + retval = ::fclose(m_file); + while ( (retval != 0) && (errno == EINTR) ){ + retval = ::fclose(m_file); + } + if( retval == 0){ + rc = true; + } + else { + rc = false; + ndbout_c("ERROR: Close file error in File.cpp for %s",strerror(errno)); + } } - + m_file = NULL; + return rc; } From 5e733ee8fab00c5a5ecb12875ec05c70400156e2 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Fri, 1 Dec 2006 19:11:43 +0400 Subject: [PATCH 02/73] BUG#23196 - MySQL server does not exit / shutdown when storage engine returns errno 12 If there is not enough memory to store or update blob record (while allocating record buffer), myisam marks table as crashed. With this fix myisam attempts to roll an index back and return an error, not marking a table as crashed. Affects myisam tables with blobs only. No test case for this fix. --- myisam/mi_dynrec.c | 4 ++-- myisam/mi_update.c | 3 ++- myisam/mi_write.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/myisam/mi_dynrec.c b/myisam/mi_dynrec.c index 727f44341b1..260f461685e 100644 --- a/myisam/mi_dynrec.c +++ b/myisam/mi_dynrec.c @@ -81,7 +81,7 @@ int _mi_write_blob_record(MI_INFO *info, const byte *record) #endif if (!(rec_buff=(byte*) my_alloca(reclength))) { - my_errno=ENOMEM; + my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(-1); } reclength2= _mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER), @@ -115,7 +115,7 @@ int _mi_update_blob_record(MI_INFO *info, my_off_t pos, const byte *record) #endif if (!(rec_buff=(byte*) my_alloca(reclength))) { - my_errno=ENOMEM; + my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(-1); } reclength=_mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER), diff --git a/myisam/mi_update.c b/myisam/mi_update.c index 00eee18bfab..d5a580748fc 100644 --- a/myisam/mi_update.c +++ b/myisam/mi_update.c @@ -193,7 +193,8 @@ err: save_errno=my_errno; if (changed) key_changed|= HA_STATE_CHANGED; - if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL) + if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_OUT_OF_MEM || + my_errno == HA_ERR_RECORD_FILE_FULL) { info->errkey= (int) i; flag=0; diff --git a/myisam/mi_write.c b/myisam/mi_write.c index 720c96b2d38..76e164adc99 100644 --- a/myisam/mi_write.c +++ b/myisam/mi_write.c @@ -164,7 +164,7 @@ int mi_write(MI_INFO *info, byte *record) err: save_errno=my_errno; if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL || - my_errno == HA_ERR_NULL_IN_SPATIAL) + my_errno == HA_ERR_NULL_IN_SPATIAL || my_errno == HA_ERR_OUT_OF_MEM) { if (info->bulk_insert) { From 3cfd578ea7efa50805d2a069e6e180331ba41358 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Tue, 5 Dec 2006 18:44:14 +0400 Subject: [PATCH 03/73] BUG#23526 - show table status reports incorrect values for MyISAM tables This problem could happen when show table status get outdated copy of TABLE object from table cache. MyISAM updates state info when external_lock() method is called. Though I_S does not lock a table to avoid deadlocks. If I_S opens a table which is in a table cache it will likely get outdated state info copy. In this case shared state copy is more recent than local copy. This problem is fixed by correctly restoring myisam state info pointer back to original value, that is to shared state. Affects MyISAM only. No good deterministic test case for this fix. --- include/thr_lock.h | 1 + myisam/mi_locking.c | 9 +++++++++ myisam/mi_open.c | 1 + myisam/myisamdef.h | 1 + mysys/thr_lock.c | 12 ++++++++++-- 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/thr_lock.h b/include/thr_lock.h index 251d8e7c9cf..d34358d5a6b 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -122,6 +122,7 @@ typedef struct st_thr_lock { void (*get_status)(void*, int); /* When one gets a lock */ void (*copy_status)(void*,void*); void (*update_status)(void*); /* Before release of write */ + void (*restore_status)(void*); /* Before release of read */ my_bool (*check_status)(void *); } THR_LOCK; diff --git a/myisam/mi_locking.c b/myisam/mi_locking.c index 36b793363c5..ee776128eb8 100644 --- a/myisam/mi_locking.c +++ b/myisam/mi_locking.c @@ -326,6 +326,15 @@ void mi_update_status(void* param) } } + +void mi_restore_status(void *param) +{ + MI_INFO *info= (MI_INFO*) param; + info->state= &info->s->state.state; + info->append_insert_at_end= 0; +} + + void mi_copy_status(void* to,void *from) { ((MI_INFO*) to)->state= &((MI_INFO*) from)->save_state; diff --git a/myisam/mi_open.c b/myisam/mi_open.c index bf20eb3f270..1f7dd23f148 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -505,6 +505,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) share->lock.get_status=mi_get_status; share->lock.copy_status=mi_copy_status; share->lock.update_status=mi_update_status; + share->lock.restore_status= mi_restore_status; share->lock.check_status=mi_check_status; } } diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h index 12e2c8e4bec..14f4570771a 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -732,6 +732,7 @@ int mi_unique_comp(MI_UNIQUEDEF *def, const byte *a, const byte *b, my_bool null_are_equal); void mi_get_status(void* param, int concurrent_insert); void mi_update_status(void* param); +void mi_restore_status(void* param); void mi_copy_status(void* to,void *from); my_bool mi_check_status(void* param); void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows); diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 66848b94651..58b55583c5f 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -758,8 +758,16 @@ void thr_unlock(THR_LOCK_DATA *data) } else lock->write.last=data->prev; - if (lock_type >= TL_WRITE_CONCURRENT_INSERT && lock->update_status) - (*lock->update_status)(data->status_param); + if (lock_type >= TL_WRITE_CONCURRENT_INSERT) + { + if (lock->update_status) + (*lock->update_status)(data->status_param); + } + else + { + if (lock->restore_status) + (*lock->restore_status)(data->status_param); + } if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count--; data->type=TL_UNLOCK; /* Mark unlocked */ From 8d8d063f9145ef21ce20b8455dac8936099397ec Mon Sep 17 00:00:00 2001 From: "mskold/marty@mysql.com/linux.site" <> Date: Thu, 14 Dec 2006 16:27:14 +0100 Subject: [PATCH 04/73] Removed check for impossible error return --- sql/ha_ndbcluster.cc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index ff5634d291c..149a7c83895 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2956,13 +2956,10 @@ KEY* key_info; DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error); } else if (type == UNIQUE_INDEX) - { - error= unique_index_scan(key_info, - start_key->key, - start_key->length, - buf); - DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error); - } + DBUG_RETURN(unique_index_scan(key_info, + start_key->key, + start_key->length, + buf)); break; default: break; From 532fe009b8ef06d723fb58fb9130c2a99707aefb Mon Sep 17 00:00:00 2001 From: "mskold/marty@mysql.com/linux.site" <> Date: Fri, 15 Dec 2006 09:03:21 +0100 Subject: [PATCH 05/73] bug#19956 Problems with VARCHAR primary key and BLOB fields:added test case --- mysql-test/r/ndb_read_multi_range.result | 14 ++++++++++++++ mysql-test/t/ndb_read_multi_range.test | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/mysql-test/r/ndb_read_multi_range.result b/mysql-test/r/ndb_read_multi_range.result index 9941d2b28a3..e2a076ef99f 100644 --- a/mysql-test/r/ndb_read_multi_range.result +++ b/mysql-test/r/ndb_read_multi_range.result @@ -367,3 +367,17 @@ a b c 406994 67 2006-02-27 11:26:46 406995 67 2006-02-28 11:55:00 DROP TABLE t1, t11, t12, t21, t22; +CREATE TABLE t1 (id varchar(255) NOT NULL, +tag int(11) NOT NULL, +doc text NOT NULL, +type varchar(150) NOT NULL, +modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, +PRIMARY KEY (id) +) ENGINE=ndbcluster; +INSERT INTO t1 VALUES ('sakila',1,'Some text goes here','text',CURRENT_TIMESTAMP); +SELECT id, tag, doc, type FROM t1 WHERE id IN ('flipper','orka'); +id tag doc type +SELECT id, tag, doc, type FROM t1 WHERE id IN ('flipper','sakila'); +id tag doc type +sakila 1 Some text goes here text +DROP TABLE t1; diff --git a/mysql-test/t/ndb_read_multi_range.test b/mysql-test/t/ndb_read_multi_range.test index 855f7789032..99edab5d23c 100644 --- a/mysql-test/t/ndb_read_multi_range.test +++ b/mysql-test/t/ndb_read_multi_range.test @@ -238,3 +238,18 @@ select * from t12 order by 1,2,3; select * from t21 order by 1,2,3; select * from t22 order by 1,2,3; DROP TABLE t1, t11, t12, t21, t22; + +# bug#19956 +CREATE TABLE t1 (id varchar(255) NOT NULL, + tag int(11) NOT NULL, + doc text NOT NULL, + type varchar(150) NOT NULL, + modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) + ) ENGINE=ndbcluster; + +INSERT INTO t1 VALUES ('sakila',1,'Some text goes here','text',CURRENT_TIMESTAMP); +SELECT id, tag, doc, type FROM t1 WHERE id IN ('flipper','orka'); +SELECT id, tag, doc, type FROM t1 WHERE id IN ('flipper','sakila'); + +DROP TABLE t1; From 77085c8efe649bab401c27bf06ad7900341cf7c2 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Fri, 15 Dec 2006 16:01:56 +0400 Subject: [PATCH 06/73] BUG#24358 - Table access crashes server Having broken .frm, particulary number of field names does not match number of fields, causes server crash. Refuse to open a table if number of field names in a table is not equal to number of fields in a table. No test case, since it requires broken .frm file. --- sql/table.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/table.cc b/sql/table.cc index aff1e6d11f6..dab2f978327 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -373,6 +373,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, memcpy(comment_pos, disk_buff+read_length-com_length, com_length); fix_type_pointers(&int_array,&outparam->fieldnames,1,&names); + if (outparam->fieldnames.count != outparam->fields) + goto err_not_open; fix_type_pointers(&int_array,outparam->intervals,interval_count, &names); From 5ad9035605d0ae5294441a91d7f1269a73133b0c Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Wed, 20 Dec 2006 19:05:35 +0400 Subject: [PATCH 07/73] BUG#21310 - Trees in SQL causing a "crashed" table with MyISAM storage engine An update that used a join of a table to itself and modified the table on one side of the join reported the table as crashed or updated wrong rows. Fixed by creating temporary table for self-joined multi update statement. --- mysql-test/r/myisam.result | 16 +++++ mysql-test/t/myisam.test | 19 +++++ sql/lock.cc | 2 +- sql/sql_update.cc | 139 +++++++++++++++++-------------------- 4 files changed, 99 insertions(+), 77 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index b34c127595f..73cee4835bf 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -944,3 +944,19 @@ select * from t1; a 42 drop table t1; +CREATE TABLE t1(a VARCHAR(16)); +INSERT INTO t1 VALUES('aaaaaaaa'),(NULL); +UPDATE t1 AS ta1, t1 AS ta2 SET ta1.a='aaaaaaaaaaaaaaaa'; +SELECT * FROM t1; +a +aaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaa +DROP TABLE t1; +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1),(2); +UPDATE t1,t1 AS t2 SET t1.a=t1.a+2 WHERE t1.a=t2.a-1; +SELECT * FROM t1 ORDER BY a; +a +2 +3 +DROP TABLE t1; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 745e3a2e377..7868e7bad4e 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -890,4 +890,23 @@ connection default; select * from t1; drop table t1; +# +# BUG#21310 - Trees in SQL causing a "crashed" table with MyISAM storage +# engine +# + +# A simplified test case that reflect crashed table issue. +CREATE TABLE t1(a VARCHAR(16)); +INSERT INTO t1 VALUES('aaaaaaaa'),(NULL); +UPDATE t1 AS ta1, t1 AS ta2 SET ta1.a='aaaaaaaaaaaaaaaa'; +SELECT * FROM t1; +DROP TABLE t1; + +# A test case that reflect wrong result set. +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(1),(2); +UPDATE t1,t1 AS t2 SET t1.a=t1.a+2 WHERE t1.a=t2.a-1; +SELECT * FROM t1 ORDER BY a; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/lock.cc b/sql/lock.cc index ab4a81034ba..3b2b2857f65 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -528,7 +528,7 @@ int mysql_lock_have_duplicate(THD *thd, TABLE *table, TABLE_LIST *tables) for (; tables; tables= tables->next) { table2= tables->table; - if (table2->tmp_table == TMP_TABLE) + if (table2->tmp_table == TMP_TABLE || table == table2) continue; /* All tables in list must be in lock. */ diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6742fbad5c4..9a326535adc 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -23,8 +23,6 @@ #include "mysql_priv.h" #include "sql_select.h" -static bool safe_update_on_fly(JOIN_TAB *join_tab, List *fields); - /* Return 0 if row hasn't changed */ static bool compare_record(TABLE *table, ulong query_id) @@ -846,30 +844,72 @@ int multi_update::prepare(List ¬_used_values, for (i=0 ; i < table_count ; i++) set_if_bigger(max_fields, fields_for_table[i]->elements); copy_field= new Copy_field[max_fields]; - - /* - Mark all copies of tables that are updates to ensure that - init_read_record() will not try to enable a cache on them - - The problem is that for queries like - - UPDATE t1, t1 AS t2 SET t1.b=t2.c WHERE t1.a=t2.a; - - the row buffer may contain things that doesn't match what is on disk - which will cause an error when reading a row. - (This issue is mostly relevent for MyISAM tables) - */ - for (table_ref= all_tables; table_ref; table_ref=table_ref->next) - { - TABLE *table=table_ref->table; - if ((tables_to_update & table->map) && - mysql_lock_have_duplicate(thd, table, update_tables)) - table->no_cache= 1; // Disable row cache - } DBUG_RETURN(thd->is_fatal_error != 0); } +/* + Check if table is safe to update on fly + + SYNOPSIS + safe_update_on_fly() + thd Thread handler + join_tab How table is used in join + all_tables List of tables + fields Fields that are updated + + NOTES + We can update the first table in join on the fly if we know that + a row in this table will never be read twice. This is true under + the following conditions: + + - We are doing a table scan and the data is in a separate file (MyISAM) or + if we don't update a clustered key. + + - We are doing a range scan and we don't update the scan key or + the primary key for a clustered table handler. + + - Table is not joined to itself. + + WARNING + This code is a bit dependent of how make_join_readinfo() works. + + RETURN + 0 Not safe to update + 1 Safe to update +*/ + +static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, + TABLE_LIST *all_tables, List *fields) +{ + TABLE *table= join_tab->table; + /* First check if a table is not joined to itself. */ + if (mysql_lock_have_duplicate(thd, table, all_tables)) + return 0; + switch (join_tab->type) { + case JT_SYSTEM: + case JT_CONST: + case JT_EQ_REF: + return 1; // At most one matching row + case JT_REF: + return !check_if_key_used(table, join_tab->ref.key, *fields); + case JT_ALL: + /* If range search on index */ + if (join_tab->quick) + return !check_if_key_used(table, join_tab->quick->index, + *fields); + /* If scanning in clustered key */ + if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && + table->primary_key < MAX_KEY) + return !check_if_key_used(table, table->primary_key, *fields); + return 1; + default: + break; // Avoid compler warning + } + return 0; +} + + /* Initialize table for multi table @@ -905,7 +945,7 @@ multi_update::initialize_tables(JOIN *join) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (table == main_table) // First table in join { - if (safe_update_on_fly(join->join_tab, &temp_fields)) + if (safe_update_on_fly(thd, join->join_tab, all_tables, &temp_fields)) { table_to_update= main_table; // Update table on the fly continue; @@ -951,59 +991,6 @@ multi_update::initialize_tables(JOIN *join) DBUG_RETURN(0); } -/* - Check if table is safe to update on fly - - SYNOPSIS - safe_update_on_fly - join_tab How table is used in join - fields Fields that are updated - - NOTES - We can update the first table in join on the fly if we know that - a row in this tabel will never be read twice. This is true under - the folloing conditions: - - - We are doing a table scan and the data is in a separate file (MyISAM) or - if we don't update a clustered key. - - - We are doing a range scan and we don't update the scan key or - the primary key for a clustered table handler. - - WARNING - This code is a bit dependent of how make_join_readinfo() works. - - RETURN - 0 Not safe to update - 1 Safe to update -*/ - -static bool safe_update_on_fly(JOIN_TAB *join_tab, List *fields) -{ - TABLE *table= join_tab->table; - switch (join_tab->type) { - case JT_SYSTEM: - case JT_CONST: - case JT_EQ_REF: - return 1; // At most one matching row - case JT_REF: - return !check_if_key_used(table, join_tab->ref.key, *fields); - case JT_ALL: - /* If range search on index */ - if (join_tab->quick) - return !check_if_key_used(table, join_tab->quick->index, - *fields); - /* If scanning in clustered key */ - if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && - table->primary_key < MAX_KEY) - return !check_if_key_used(table, table->primary_key, *fields); - return 1; - default: - break; // Avoid compler warning - } - return 0; -} - multi_update::~multi_update() { From 263876f373d14378b0c1629fc17c33d0bfbc09e2 Mon Sep 17 00:00:00 2001 From: "df@kahlann.erinye.com" <> Date: Wed, 20 Dec 2006 17:42:44 +0100 Subject: [PATCH 08/73] BUG#20061 build script can not detect my cpu --- BUILD/check-cpu | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/BUILD/check-cpu b/BUILD/check-cpu index e207d12d972..bde6879e664 100755 --- a/BUILD/check-cpu +++ b/BUILD/check-cpu @@ -5,9 +5,13 @@ # check_cpu () { - if test -r /proc/cpuinfo ; then + CPUINFO=/proc/cpuinfo + if test -n "$TEST_CPUINFO" ; then + CPUINFO=$TEST_CPUINFO + fi + if test -r "$CPUINFO" -a "$CPUINFO" != " " ; then # on Linux (and others?) we can get detailed CPU information out of /proc - cpuinfo="cat /proc/cpuinfo" + cpuinfo="cat $CPUINFO" # detect CPU family cpu_family=`$cpuinfo | grep 'family' | cut -d ':' -f 2 | cut -d ' ' -f 2 | head -1` @@ -33,6 +37,7 @@ check_cpu () { done else # Fallback when there is no /proc/cpuinfo + CPUINFO=" " case "`uname -s`" in FreeBSD|OpenBSD) cpu_family=`uname -m`; @@ -84,6 +89,18 @@ check_cpu () { *Pentium*M*pro*) cpu_arg="pentium-m"; ;; + *Celeron\(R\)*\ M*) + cpu_arg="pentium-m"; + ;; + *Celeron*Coppermine*) + cpu_arg="pentium3" + ;; + *Celeron\(R\)*) + cpu_arg="pentium4" + ;; + *Celeron*) + cpu_arg="pentium2"; + ;; *Athlon*64*) cpu_arg="athlon64"; ;; @@ -113,7 +130,14 @@ check_cpu () { esac - if test -z "$cpu_arg"; then + if test -z "$cpu_arg" ; then + if test "$CPUINFO" != " " ; then + # fallback to uname if necessary + TEST_CPUINFO=" " + check_cpu_cflags="" + check_cpu + return + fi echo "BUILD/check-cpu: Oops, could not find out what kind of cpu this machine is using." >&2 check_cpu_cflags="" return From fb01230ac58ea792c8c466022d9222c795df20c8 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Wed, 27 Dec 2006 19:54:49 +0400 Subject: [PATCH 09/73] After merge fix. --- sql/sql_update.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 5b7b7f594da..ce0126f1f1c 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1079,10 +1079,11 @@ int multi_update::prepare(List ¬_used_values, */ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, - TABLE_LIST *all_tables, List *fields) + TABLE_LIST *table_ref, TABLE_LIST *all_tables, + List *fields) { TABLE *table= join_tab->table; - if (unique_table(thd, table, all_tables)) + if (unique_table(thd, table_ref, all_tables)) return 0; switch (join_tab->type) { case JT_SYSTEM: @@ -1142,7 +1143,8 @@ multi_update::initialize_tables(JOIN *join) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (table == main_table) // First table in join { - if (safe_update_on_fly(thd, join->join_tab, all_tables, &temp_fields)) + if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables, + &temp_fields)) { table_to_update= main_table; // Update table on the fly continue; From ec5331593d187a991c2de3ccd2340c4411eb702c Mon Sep 17 00:00:00 2001 From: "df@kahlann.erinye.com" <> Date: Thu, 28 Dec 2006 10:00:16 +0100 Subject: [PATCH 10/73] fix breakage on qnx --- cmd-line-utils/readline/undo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-line-utils/readline/undo.c b/cmd-line-utils/readline/undo.c index b4b5a6511ba..4d256f492b8 100644 --- a/cmd-line-utils/readline/undo.c +++ b/cmd-line-utils/readline/undo.c @@ -175,7 +175,7 @@ _rl_fix_last_undo_of_type (type, start, end) for (rl = rl_undo_list; rl; rl = rl->next) { - if (rl->what == (uint) type) + if (rl->what == (unsigned int) type) { rl->start = start; rl->end = end; From 866b02b9347e8e87f0c2ae186e55ded89602866a Mon Sep 17 00:00:00 2001 From: "mskold/marty@mysql.com/linux.site" <> Date: Tue, 2 Jan 2007 15:47:58 +0100 Subject: [PATCH 11/73] Added --ndb-use-transactions --- sql/mysqld.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1dd15398cd1..ce12db1c53a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4531,8 +4531,8 @@ enum options_mysqld OPT_LOG_BIN_TRUST_FUNCTION_CREATORS, OPT_SAFE_SHOW_DB, OPT_INNODB_SAFE_BINLOG, OPT_INNODB, OPT_ISAM, - OPT_ENGINE_CONDITION_PUSHDOWN, - OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, + OPT_ENGINE_CONDITION_PUSHDOWN, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, + OPT_NDB_USE_EXACT_COUNT, OPT_NDB_USE_TRANSACTIONS, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME, OPT_NDB_MGMD, OPT_NDB_NODEID, @@ -5158,6 +5158,17 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &global_system_variables.ndb_use_exact_count, (gptr*) &global_system_variables.ndb_use_exact_count, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"ndb-use-transactions", OPT_NDB_USE_TRANSACTIONS, + "Use transactions for large inserts, if enabled then large " + "inserts will be split into several smaller transactions", + (gptr*) &global_system_variables.ndb_use_transactions, + (gptr*) &global_system_variables.ndb_use_transactions, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"ndb_use_transactions", OPT_NDB_USE_TRANSACTIONS, + "same as --ndb-use-transactions.", + (gptr*) &global_system_variables.ndb_use_transactions, + (gptr*) &global_system_variables.ndb_use_transactions, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, {"ndb-shm", OPT_NDB_SHM, "Use shared memory connections when available.", (gptr*) &opt_ndb_shm, From d9fa993e07f6f0827aa2d217b675a9baa6b2b8e1 Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Wed, 3 Jan 2007 06:17:34 +0100 Subject: [PATCH 12/73] ndb - bug#25364 on master node failure during qmgr-commitreq make sure to remove all committed failed nodes from failed/prepfailed arrays --- .../kernel/signaldata/DumpStateOrd.hpp | 1 + ndb/src/kernel/blocks/ERROR_codes.txt | 3 + ndb/src/kernel/blocks/qmgr/Qmgr.hpp | 4 ++ ndb/src/kernel/blocks/qmgr/QmgrMain.cpp | 72 +++++++++++++++---- ndb/test/ndbapi/testNodeRestart.cpp | 43 +++++++++++ ndb/test/run-test/daily-basic-tests.txt | 4 ++ 6 files changed, 114 insertions(+), 13 deletions(-) diff --git a/ndb/include/kernel/signaldata/DumpStateOrd.hpp b/ndb/include/kernel/signaldata/DumpStateOrd.hpp index a2993ad5d03..04f94aaba58 100644 --- a/ndb/include/kernel/signaldata/DumpStateOrd.hpp +++ b/ndb/include/kernel/signaldata/DumpStateOrd.hpp @@ -68,6 +68,7 @@ public: // 100-105 TUP and ACC // 200-240 UTIL // 300-305 TRIX + QmgrErr935 = 935, NdbfsDumpFileStat = 400, NdbfsDumpAllFiles = 401, NdbfsDumpOpenFiles = 402, diff --git a/ndb/src/kernel/blocks/ERROR_codes.txt b/ndb/src/kernel/blocks/ERROR_codes.txt index 16f5da8a553..0bcc99a6334 100644 --- a/ndb/src/kernel/blocks/ERROR_codes.txt +++ b/ndb/src/kernel/blocks/ERROR_codes.txt @@ -21,6 +21,9 @@ Crash president when he starts to run in ArbitState 1-9. 910: Crash new president after node crash +935 : Crash master on node failure (delayed) + and skip sending GSN_COMMIT_FAILREQ to specified node + ERROR CODES FOR TESTING NODE FAILURE, GLOBAL CHECKPOINT HANDLING: ----------------------------------------------------------------- diff --git a/ndb/src/kernel/blocks/qmgr/Qmgr.hpp b/ndb/src/kernel/blocks/qmgr/Qmgr.hpp index e728ea81a7d..0c4bdc5d3c1 100644 --- a/ndb/src/kernel/blocks/qmgr/Qmgr.hpp +++ b/ndb/src/kernel/blocks/qmgr/Qmgr.hpp @@ -426,6 +426,10 @@ private: StopReq c_stopReq; bool check_multi_node_shutdown(Signal* signal); + +#ifdef ERROR_INSERT + Uint32 c_error_insert_extra; +#endif }; #endif diff --git a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp index cc981f37987..66ee7549b9d 100644 --- a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp +++ b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp @@ -3110,6 +3110,18 @@ Qmgr::sendCommitFailReq(Signal* signal) for (nodePtr.i = 1; nodePtr.i < MAX_NDB_NODES; nodePtr.i++) { jam(); ptrAss(nodePtr, nodeRec); + +#ifdef ERROR_INSERT + if (ERROR_INSERTED(935) && nodePtr.i == c_error_insert_extra) + { + ndbout_c("skipping node %d", c_error_insert_extra); + CLEAR_ERROR_INSERT_VALUE; + signal->theData[0] = 9999; + sendSignalWithDelay(CMVMI_REF, GSN_NDB_TAMPER, signal, 1000, 1); + continue; + } +#endif + if (nodePtr.p->phase == ZRUNNING) { jam(); nodePtr.p->sendCommitFailReqStatus = Q_ACTIVE; @@ -3180,6 +3192,33 @@ void Qmgr::execPREP_FAILREF(Signal* signal) return; }//Qmgr::execPREP_FAILREF() +static +Uint32 +clear_nodes(Uint32 dstcnt, Uint16 dst[], Uint32 srccnt, const Uint16 src[]) +{ + if (srccnt == 0) + return dstcnt; + + Uint32 pos = 0; + for (Uint32 i = 0; i 0) { - jam(); - guard0 = cnoFailedNodes - 1; - arrGuard(guard0 + cnoCommitFailedNodes, MAX_NDB_NODES); - for (Tj = 0; Tj <= guard0; Tj++) { - jam(); - cfailedNodes[Tj] = cfailedNodes[Tj + cnoCommitFailedNodes]; - }//for - }//if - }//if + + /** + * Remove committed nodes from failed/prepared + */ + cnoFailedNodes = clear_nodes(cnoFailedNodes, + cfailedNodes, + cnoCommitFailedNodes, + ccommitFailedNodes); + cnoPrepFailedNodes = clear_nodes(cnoPrepFailedNodes, + cprepFailedNodes, + cnoCommitFailedNodes, + ccommitFailedNodes); cnoCommitFailedNodes = 0; }//if /**----------------------------------------------------------------------- @@ -4658,6 +4696,14 @@ Qmgr::execDUMP_STATE_ORD(Signal* signal) default: ; }//switch + +#ifdef ERROR_INSERT + if (signal->theData[0] == 935 && signal->getLength() == 2) + { + SET_ERROR_INSERT_VALUE(935); + c_error_insert_extra = signal->theData[1]; + } +#endif }//Qmgr::execDUMP_STATE_ORD() void Qmgr::execSET_VAR_REQ(Signal* signal) diff --git a/ndb/test/ndbapi/testNodeRestart.cpp b/ndb/test/ndbapi/testNodeRestart.cpp index 082013f07cc..c0c5cc5163a 100644 --- a/ndb/test/ndbapi/testNodeRestart.cpp +++ b/ndb/test/ndbapi/testNodeRestart.cpp @@ -955,6 +955,46 @@ int runBug24717(NDBT_Context* ctx, NDBT_Step* step){ return NDBT_OK; } +int runBug25364(NDBT_Context* ctx, NDBT_Step* step){ + int result = NDBT_OK; + NdbRestarter restarter; + Ndb* pNdb = GETNDB(step); + int loops = ctx->getNumLoops(); + + if (restarter.getNumDbNodes() < 4) + return NDBT_OK; + + int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 }; + + for (; loops; loops --) + { + int master = restarter.getMasterNodeId(); + int victim = restarter.getRandomNodeOtherNodeGroup(master, rand()); + int second = restarter.getRandomNodeSameNodeGroup(victim, rand()); + + int dump[] = { 935, victim } ; + if (restarter.dumpStateOneNode(master, dump, 2)) + return NDBT_FAILED; + + if (restarter.dumpStateOneNode(master, val2, 2)) + return NDBT_FAILED; + + if (restarter.restartOneDbNode(second, false, true, true)) + return NDBT_FAILED; + + int nodes[2] = { master, second }; + if (restarter.waitNodesNoStart(nodes, 2)) + return NDBT_FAILED; + + restarter.startNodes(nodes, 2); + + if (restarter.waitNodesStarted(nodes, 2)) + return NDBT_FAILED; + } + + return NDBT_OK; +} + NDBT_TESTSUITE(testNodeRestart); TESTCASE("NoLoad", @@ -1271,6 +1311,9 @@ TESTCASE("Bug20185", TESTCASE("Bug24717", ""){ INITIALIZER(runBug24717); } +TESTCASE("Bug25364", ""){ + INITIALIZER(runBug25364); +} NDBT_TESTSUITE_END(testNodeRestart); int main(int argc, const char** argv){ diff --git a/ndb/test/run-test/daily-basic-tests.txt b/ndb/test/run-test/daily-basic-tests.txt index a1443970388..41070275935 100644 --- a/ndb/test/run-test/daily-basic-tests.txt +++ b/ndb/test/run-test/daily-basic-tests.txt @@ -469,6 +469,10 @@ max-time: 1000 cmd: testNodeRestart args: -n Bug24717 T1 +max-time: 1000 +cmd: testNodeRestart +args: -n Bug25364 T1 + # OLD FLEX max-time: 500 cmd: flexBench From 82723332432eedae70bd22659b1b3845068ac171 Mon Sep 17 00:00:00 2001 From: "mskold/marty@mysql.com/linux.site" <> Date: Wed, 3 Jan 2007 14:56:26 +0100 Subject: [PATCH 13/73] ndb_use_transactions is set from value for command line flag --- sql/sql_class.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 645ac6e28f3..8dd53262eea 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -290,9 +290,6 @@ void THD::init(void) variables.date_format); variables.datetime_format= date_time_format_copy((THD*) 0, variables.datetime_format); -#ifdef HAVE_NDBCLUSTER_DB - variables.ndb_use_transactions= 1; -#endif pthread_mutex_unlock(&LOCK_global_system_variables); server_status= SERVER_STATUS_AUTOCOMMIT; if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) From a66da6203da799aced03776a78cb9514d7ba8908 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Fri, 5 Jan 2007 10:26:51 +0100 Subject: [PATCH 14/73] Bug#24607 - MyISAM pointer size determined incorrectly The function mi_get_pointer_length() computed too small pointer size for very large tables. Inserted missing 'else' between the branches for very large tables. --- myisam/mi_create.c | 13 +++++++------ mysql-test/r/myisam.result | 5 +++++ mysql-test/t/myisam.test | 8 ++++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/myisam/mi_create.c b/myisam/mi_create.c index c1310a8012d..da9e0887b00 100644 --- a/myisam/mi_create.c +++ b/myisam/mi_create.c @@ -768,18 +768,19 @@ uint mi_get_pointer_length(ulonglong file_length, uint def) if (file_length) /* If not default */ { #ifdef NOT_YET_READY_FOR_8_BYTE_POINTERS - if (file_length >= (longlong) 1 << 56) + if (file_length >= ULL(1) << 56) def=8; + else #endif - if (file_length >= (longlong) 1 << 48) + if (file_length >= ULL(1) << 48) def=7; - if (file_length >= (longlong) 1 << 40) + else if (file_length >= ULL(1) << 40) def=6; - else if (file_length >= (longlong) 1 << 32) + else if (file_length >= ULL(1) << 32) def=5; - else if (file_length >= (1L << 24)) + else if (file_length >= ULL(1) << 24) def=4; - else if (file_length >= (1L << 16)) + else if (file_length >= ULL(1) << 16) def=3; else def=2; diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 73cee4835bf..749d5f47809 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -960,3 +960,8 @@ a 2 3 DROP TABLE t1; +CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=5100100100; +SHOW TABLE STATUS LIKE 't1'; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 9 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4294967295 avg_row_length=70100 +DROP TABLE t1; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 7868e7bad4e..d065d2e3ebf 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -909,4 +909,12 @@ UPDATE t1,t1 AS t2 SET t1.a=t1.a+2 WHERE t1.a=t2.a-1; SELECT * FROM t1 ORDER BY a; DROP TABLE t1; +# +# Bug#24607 - MyISAM pointer size determined incorrectly +# +CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=5100100100; +--replace_column 5 X 6 X 7 X 9 X 10 X 11 X 12 X 13 X 14 X 16 X +SHOW TABLE STATUS LIKE 't1'; +DROP TABLE t1; + # End of 4.1 tests From 26ea040e9c350e32bb6cb2faa2d41bb6be137fe1 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon." <> Date: Fri, 5 Jan 2007 15:33:39 +0100 Subject: [PATCH 15/73] remove unused errorcode corrected previous patch removing "if (signal)", which should acually be "if (signum)" --- ndb/src/common/debugger/EventLogger.cpp | 3 ++- ndb/src/kernel/blocks/dbtc/Dbtc.hpp | 1 - ndb/src/ndbapi/ndberror.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ndb/src/common/debugger/EventLogger.cpp b/ndb/src/common/debugger/EventLogger.cpp index e168c705d47..3efd52808e2 100644 --- a/ndb/src/common/debugger/EventLogger.cpp +++ b/ndb/src/common/debugger/EventLogger.cpp @@ -115,7 +115,8 @@ void getTextNDBStopForced(QQQQ) { int sphase = theData[4]; int extra = theData[5]; getRestartAction(theData[1],action_str); - reason_str.appfmt(" Initiated by signal %d.", signum); + if (signum) + reason_str.appfmt(" Initiated by signal %d.", signum); if (error) { ndbd_exit_classification cl; diff --git a/ndb/src/kernel/blocks/dbtc/Dbtc.hpp b/ndb/src/kernel/blocks/dbtc/Dbtc.hpp index c6089113382..d6c4529bb72 100644 --- a/ndb/src/kernel/blocks/dbtc/Dbtc.hpp +++ b/ndb/src/kernel/blocks/dbtc/Dbtc.hpp @@ -138,7 +138,6 @@ #define ZNOT_FOUND 626 #define ZALREADYEXIST 630 -#define ZINCONSISTENTHASHINDEX 892 #define ZNOTUNIQUE 893 #define ZINVALID_KEY 290 diff --git a/ndb/src/ndbapi/ndberror.c b/ndb/src/ndbapi/ndberror.c index 8800aedae5a..45248000703 100644 --- a/ndb/src/ndbapi/ndberror.c +++ b/ndb/src/ndbapi/ndberror.c @@ -208,7 +208,6 @@ ErrorBundle ErrorCodes[] = { /** * Internal errors */ - { 892, IE, "Inconsistent hash index. The index needs to be dropped and recreated" }, { 896, IE, "Tuple corrupted - wrong checksum or column data in invalid format" }, { 901, IE, "Inconsistent ordered index. The index needs to be dropped and recreated" }, { 202, IE, "202" }, From db0107b801df0f4fd0ef0f978a45c6d2830d748a Mon Sep 17 00:00:00 2001 From: "mskold/marty@mysql.com/linux.site" <> Date: Mon, 8 Jan 2007 10:38:53 +0100 Subject: [PATCH 16/73] bug#24820 CREATE INDEX ....USING HASH on NDB table creates ordered index, not HASH index: Added error checking --- mysql-test/r/ndb_index_ordered.result | 9 +++++++++ mysql-test/t/ndb_index_ordered.test | 14 ++++++++++++++ sql/ha_ndbcluster.cc | 10 ++++++++++ 3 files changed, 33 insertions(+) diff --git a/mysql-test/r/ndb_index_ordered.result b/mysql-test/r/ndb_index_ordered.result index 36bac7b0f9d..54458e1b459 100644 --- a/mysql-test/r/ndb_index_ordered.result +++ b/mysql-test/r/ndb_index_ordered.result @@ -658,3 +658,12 @@ insert into t1 (a, c) values (1,'aaa'),(3,'bbb'); select count(*) from t1 where c<'bbb'; count(*) 1 +create table nationaldish (DishID int(10) unsigned NOT NULL AUTO_INCREMENT, +CountryCode char(3) NOT NULL, +DishTitle varchar(64) NOT NULL, +calories smallint(5) unsigned DEFAULT NULL, +PRIMARY KEY (DishID) +) ENGINE=ndbcluster; +create index i using hash on nationaldish(countrycode,calories); +ERROR HY000: Can't create table './test/#sql-3c51_2.frm' (errno: 138) +drop table nationaldish; diff --git a/mysql-test/t/ndb_index_ordered.test b/mysql-test/t/ndb_index_ordered.test index e6827bdbe12..fa76202c7b7 100644 --- a/mysql-test/t/ndb_index_ordered.test +++ b/mysql-test/t/ndb_index_ordered.test @@ -356,3 +356,17 @@ insert into t1 (a, c) values (1,'aaa'),(3,'bbb'); select count(*) from t1 where c<'bbb'; # End of 4.1 tests + +# bug#24820 CREATE INDEX ....USING HASH on NDB table creates ordered index, not HASH index + +create table nationaldish (DishID int(10) unsigned NOT NULL AUTO_INCREMENT, + CountryCode char(3) NOT NULL, + DishTitle varchar(64) NOT NULL, + calories smallint(5) unsigned DEFAULT NULL, + PRIMARY KEY (DishID) + ) ENGINE=ndbcluster; + +--error ER_CANT_CREATE_TABLE +create index i using hash on nationaldish(countrycode,calories); + +drop table nationaldish; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 149a7c83895..34a3a001b21 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -1107,6 +1107,16 @@ int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase) error= create_unique_index(unique_index_name, key_info); break; case ORDERED_INDEX: + if (key_info->algorithm == HA_KEY_ALG_HASH) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_UNSUPPORTED_EXTENSION, + ER(ER_UNSUPPORTED_EXTENSION), + "Ndb does not support non-unique " + "hash based indexes"); + error= HA_ERR_UNSUPPORTED; + break; + } error= create_ordered_index(index_name, key_info); break; default: From 5ebcc10e368796a23bba37332786fa0ef99324ab Mon Sep 17 00:00:00 2001 From: "mskold/marty@mysql.com/linux.site" <> Date: Mon, 8 Jan 2007 13:53:37 +0100 Subject: [PATCH 17/73] bug#24820 CREATE INDEX ....USING HASH on NDB table creates ordered index, not HASH index: Changed test since error mesage wasn't predictable --- mysql-test/r/ndb_index_ordered.result | 7 +++---- mysql-test/t/ndb_index_ordered.test | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/ndb_index_ordered.result b/mysql-test/r/ndb_index_ordered.result index 54458e1b459..b3e55a23073 100644 --- a/mysql-test/r/ndb_index_ordered.result +++ b/mysql-test/r/ndb_index_ordered.result @@ -662,8 +662,7 @@ create table nationaldish (DishID int(10) unsigned NOT NULL AUTO_INCREMENT, CountryCode char(3) NOT NULL, DishTitle varchar(64) NOT NULL, calories smallint(5) unsigned DEFAULT NULL, -PRIMARY KEY (DishID) +PRIMARY KEY (DishID), +INDEX i USING HASH (countrycode,calories) ) ENGINE=ndbcluster; -create index i using hash on nationaldish(countrycode,calories); -ERROR HY000: Can't create table './test/#sql-3c51_2.frm' (errno: 138) -drop table nationaldish; +ERROR HY000: Can't create table './test/nationaldish.frm' (errno: 138) diff --git a/mysql-test/t/ndb_index_ordered.test b/mysql-test/t/ndb_index_ordered.test index fa76202c7b7..5867140fabb 100644 --- a/mysql-test/t/ndb_index_ordered.test +++ b/mysql-test/t/ndb_index_ordered.test @@ -359,14 +359,12 @@ select count(*) from t1 where c<'bbb'; # bug#24820 CREATE INDEX ....USING HASH on NDB table creates ordered index, not HASH index +--error ER_CANT_CREATE_TABLE create table nationaldish (DishID int(10) unsigned NOT NULL AUTO_INCREMENT, CountryCode char(3) NOT NULL, DishTitle varchar(64) NOT NULL, calories smallint(5) unsigned DEFAULT NULL, - PRIMARY KEY (DishID) + PRIMARY KEY (DishID), + INDEX i USING HASH (countrycode,calories) ) ENGINE=ndbcluster; ---error ER_CANT_CREATE_TABLE -create index i using hash on nationaldish(countrycode,calories); - -drop table nationaldish; From eeb82a8ed5a0415737c12ecc4ec97708461d5081 Mon Sep 17 00:00:00 2001 From: "df@kahlann.erinye.com" <> Date: Tue, 9 Jan 2007 09:32:56 +0100 Subject: [PATCH 18/73] BUG#24780 workaround for broken installations that depend on using /etc, but were configured differently --- scripts/mysqlaccess.sh | 4 ++++ scripts/mysqld_multi.sh | 3 +++ 2 files changed, 7 insertions(+) diff --git a/scripts/mysqlaccess.sh b/scripts/mysqlaccess.sh index 22e9d121f9a..bcaf9f8af8e 100644 --- a/scripts/mysqlaccess.sh +++ b/scripts/mysqlaccess.sh @@ -465,6 +465,9 @@ MySQLaccess::Report::Print_Header(); elsif (-f "@sysconfdir@/$script_conf") { require "@sysconfdir@/$script_conf"; } + elsif (-f "/etc/$script_conf") { + require "/etc/$script_conf"; + } # **************************** # Read in all parameters @@ -930,6 +933,7 @@ sub MergeConfigFile { sub MergeConfigFiles { my ($name,$pass,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwuid $<; MergeConfigFile("@sysconfdir@/my.cnf"); + MergeConfigFile("/etc/my.cnf"); MergeConfigFile("$dir/.my.cnf"); } diff --git a/scripts/mysqld_multi.sh b/scripts/mysqld_multi.sh index ce778c03206..8b435161e47 100644 --- a/scripts/mysqld_multi.sh +++ b/scripts/mysqld_multi.sh @@ -432,6 +432,9 @@ sub find_groups if (-f "@sysconfdir@/my.cnf" && -r "@sysconfdir@/my.cnf") { open(MY_CNF, "<@sysconfdir@/my.cnf") && (@tmp=) && close(MY_CNF); + } elsif (-f "/etc/my.cnf" && -r "/etc/my.cnf") + { + open(MY_CNF, ") && close(MY_CNF); } for ($i = 0; ($line = shift @tmp); $i++) { From c17bf5cb239fa8bb3039aef13203e9d99242442a Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Thu, 11 Jan 2007 16:05:03 +0300 Subject: [PATCH 19/73] Bug#17711: DELETE doesn't use index when ORDER BY, LIMIT and non-restricting WHERE is present. If a DELETE statement with ORDER BY and LIMIT contains a WHERE clause with conditions that for sure cannot be used for index access (like in WHERE @var:= field) the execution always follows the filesort path. It happens currently even when for the above case there is an index that can be used to speedup sorting by the order by list. Now if a DELETE statement with ORDER BY and LIMIT contains such WHERE clause conditions that cannot be used to build any quick select then the mysql_delete() tries to use an index like there is no WHERE clause at all. --- mysql-test/r/delete.result | 7 +++++++ mysql-test/t/delete.test | 10 ++++++++++ sql/sql_delete.cc | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index 7a6af8fd905..a5c22e66569 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -186,4 +186,11 @@ select count(*) from t1; count(*) 0 drop table t1; +create table t1(f1 int primary key); +insert into t1 values (4),(3),(1),(2); +delete from t1 where (@a:= f1) order by f1 limit 1; +select @a; +@a +1 +drop table t1; End of 4.1 tests diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index 2036b59d810..301b2cdbb99 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -174,4 +174,14 @@ delete from t1 where a is null; select count(*) from t1; drop table t1; +# +# Bug#17711: DELETE doesn't use index when ORDER BY, LIMIT and +# non-restricting WHERE is present. +# +create table t1(f1 int primary key); +insert into t1 values (4),(3),(1),(2); +delete from t1 where (@a:= f1) order by f1 limit 1; +select @a; +drop table t1; + --echo End of 4.1 tests diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1b00539ed71..b84b2f7eef4 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -142,7 +142,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(-1); // This will force out message } - if (!select && limit != HA_POS_ERROR) + if ((!select || table->quick_keys.is_clear_all()) && limit != HA_POS_ERROR) usable_index= get_index_for_order(table, (ORDER*)(order->first), limit); if (usable_index == MAX_KEY) From fc0e206cb538d622abf1854a50a14836ac275b74 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Thu, 11 Jan 2007 16:45:38 +0300 Subject: [PATCH 20/73] Bug#23409: Arguments of the ENCODE() and the DECODE() functions were not printed correctly. The Item_func::print method was used to print the Item_func_encode and the Item_func_decode objects. The last argument to ENCODE and DECODE functions is a plain C string and thus Item_func::print wasn't able to print it. The print() method is added to the Item_func_encode class. It correctly prints the Item_func_encode and the Item_func_decode objects. --- mysql-test/r/func_str.result | 14 +++++++++++++- mysql-test/t/func_str.test | 8 ++++++++ sql/item_strfunc.cc | 13 +++++++++++++ sql/item_strfunc.h | 11 ++++++++--- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 2c15e5581e8..af6a4d20cff 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -806,7 +806,7 @@ explain extended select md5('hello'), sha('abc'), sha1('abc'), soundex(''), 'moo id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 select md5(_latin1'hello') AS `md5('hello')`,sha(_latin1'abc') AS `sha('abc')`,sha(_latin1'abc') AS `sha1('abc')`,soundex(_latin1'') AS `soundex('')`,(soundex(_latin1'mood') = soundex(_latin1'mud')) AS `'mood' sounds like 'mud'`,aes_decrypt(aes_encrypt(_latin1'abc',_latin1'1'),_latin1'1') AS `aes_decrypt(aes_encrypt('abc','1'),'1')`,concat(_latin1'*',repeat(_latin1' ',5),_latin1'*') AS `concat('*',space(5),'*')`,reverse(_latin1'abc') AS `reverse('abc')`,rpad(_latin1'a',4,_latin1'1') AS `rpad('a',4,'1')`,lpad(_latin1'a',4,_latin1'1') AS `lpad('a',4,'1')`,concat_ws(_latin1',',_latin1'',NULL,_latin1'a') AS `concat_ws(',','',NULL,'a')`,make_set(255,_latin2'a',_latin2'b',_latin2'c') AS `make_set(255,_latin2'a',_latin2'b',_latin2'c')`,elt(2,1) AS `elt(2,1)`,locate(_latin1'a',_latin1'b',2) AS `locate("a","b",2)`,format(130,10) AS `format(130,10)`,char(0) AS `char(0)`,conv(130,16,10) AS `conv(130,16,10)`,hex(130) AS `hex(130)`,cast(_latin1'HE' as char charset binary) AS `binary 'HE'`,export_set(255,_latin2'y',_latin2'n',_latin2' ') AS `export_set(255,_latin2'y',_latin2'n',_latin2' ')`,field((_latin1'b' collate _latin1'latin1_bin'),_latin1'A',_latin1'B') AS `FIELD('b' COLLATE latin1_bin,'A','B')`,find_in_set(_latin1'B',_latin1'a,b,c,d') AS `FIND_IN_SET(_latin1'B',_latin1'a,b,c,d')`,collation(conv(130,16,10)) AS `collation(conv(130,16,10))`,coercibility(conv(130,16,10)) AS `coercibility(conv(130,16,10))`,length(_latin1'\n \r\0\\_\\%\\') AS `length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,concat(_latin1'monty',_latin1' was here ',_latin1'again') AS `concat('monty',' was here ','again')`,length(_latin1'hello') AS `length('hello')`,char(ascii(_latin1'h')) AS `char(ascii('h'))`,ord(_latin1'h') AS `ord('h')`,quote((1 / 0)) AS `quote(1/0)`,crc32(_latin1'123') AS `crc32("123")`,replace(_latin1'aaaa',_latin1'a',_latin1'b') AS `replace('aaaa','a','b')`,insert(_latin1'txs',2,1,_latin1'hi') AS `insert('txs',2,1,'hi')`,left(_latin2'a',1) AS `left(_latin2'a',1)`,right(_latin2'a',1) AS `right(_latin2'a',1)`,lcase(_latin2'a') AS `lcase(_latin2'a')`,ucase(_latin2'a') AS `ucase(_latin2'a')`,substr(_latin1'abcdefg',3,2) AS `SUBSTR('abcdefg',3,2)`,substr_index(_latin1'1abcd;2abcd;3abcd;4abcd',_latin1';',2) AS `substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2)`,trim(_latin2' a ') AS `trim(_latin2' a ')`,ltrim(_latin2' a ') AS `ltrim(_latin2' a ')`,rtrim(_latin2' a ') AS `rtrim(_latin2' a ')`,decode(encode(repeat(_latin1'a',100000))) AS `decode(encode(repeat("a",100000),"monty"),"monty")` +Note 1003 select md5(_latin1'hello') AS `md5('hello')`,sha(_latin1'abc') AS `sha('abc')`,sha(_latin1'abc') AS `sha1('abc')`,soundex(_latin1'') AS `soundex('')`,(soundex(_latin1'mood') = soundex(_latin1'mud')) AS `'mood' sounds like 'mud'`,aes_decrypt(aes_encrypt(_latin1'abc',_latin1'1'),_latin1'1') AS `aes_decrypt(aes_encrypt('abc','1'),'1')`,concat(_latin1'*',repeat(_latin1' ',5),_latin1'*') AS `concat('*',space(5),'*')`,reverse(_latin1'abc') AS `reverse('abc')`,rpad(_latin1'a',4,_latin1'1') AS `rpad('a',4,'1')`,lpad(_latin1'a',4,_latin1'1') AS `lpad('a',4,'1')`,concat_ws(_latin1',',_latin1'',NULL,_latin1'a') AS `concat_ws(',','',NULL,'a')`,make_set(255,_latin2'a',_latin2'b',_latin2'c') AS `make_set(255,_latin2'a',_latin2'b',_latin2'c')`,elt(2,1) AS `elt(2,1)`,locate(_latin1'a',_latin1'b',2) AS `locate("a","b",2)`,format(130,10) AS `format(130,10)`,char(0) AS `char(0)`,conv(130,16,10) AS `conv(130,16,10)`,hex(130) AS `hex(130)`,cast(_latin1'HE' as char charset binary) AS `binary 'HE'`,export_set(255,_latin2'y',_latin2'n',_latin2' ') AS `export_set(255,_latin2'y',_latin2'n',_latin2' ')`,field((_latin1'b' collate _latin1'latin1_bin'),_latin1'A',_latin1'B') AS `FIELD('b' COLLATE latin1_bin,'A','B')`,find_in_set(_latin1'B',_latin1'a,b,c,d') AS `FIND_IN_SET(_latin1'B',_latin1'a,b,c,d')`,collation(conv(130,16,10)) AS `collation(conv(130,16,10))`,coercibility(conv(130,16,10)) AS `coercibility(conv(130,16,10))`,length(_latin1'\n \r\0\\_\\%\\') AS `length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,concat(_latin1'monty',_latin1' was here ',_latin1'again') AS `concat('monty',' was here ','again')`,length(_latin1'hello') AS `length('hello')`,char(ascii(_latin1'h')) AS `char(ascii('h'))`,ord(_latin1'h') AS `ord('h')`,quote((1 / 0)) AS `quote(1/0)`,crc32(_latin1'123') AS `crc32("123")`,replace(_latin1'aaaa',_latin1'a',_latin1'b') AS `replace('aaaa','a','b')`,insert(_latin1'txs',2,1,_latin1'hi') AS `insert('txs',2,1,'hi')`,left(_latin2'a',1) AS `left(_latin2'a',1)`,right(_latin2'a',1) AS `right(_latin2'a',1)`,lcase(_latin2'a') AS `lcase(_latin2'a')`,ucase(_latin2'a') AS `ucase(_latin2'a')`,substr(_latin1'abcdefg',3,2) AS `SUBSTR('abcdefg',3,2)`,substr_index(_latin1'1abcd;2abcd;3abcd;4abcd',_latin1';',2) AS `substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2)`,trim(_latin2' a ') AS `trim(_latin2' a ')`,ltrim(_latin2' a ') AS `ltrim(_latin2' a ')`,rtrim(_latin2' a ') AS `rtrim(_latin2' a ')`,decode(encode(repeat(_latin1'a',100000),'monty'),'monty') AS `decode(encode(repeat("a",100000),"monty"),"monty")` SELECT lpad(12345, 5, "#"); lpad(12345, 5, "#") 12345 @@ -1064,4 +1064,16 @@ id select_type table type possible_keys key key_len ref rows Extra Warnings: Note 1003 select test.t1.s AS `s` from test.t1 where (trim(both _latin1'y' from test.t1.s) > _latin1'ab') DROP TABLE t1; +create table t1(f1 varchar(4)); +explain extended select encode(f1,'zxcv') as 'enc' from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found +Warnings: +Note 1003 select encode(test.t1.f1,'zxcv') AS `enc` from test.t1 +explain extended select decode(f1,'zxcv') as 'enc' from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found +Warnings: +Note 1003 select decode(test.t1.f1,'zxcv') AS `enc` from test.t1 +drop table t1; End of 4.1 tests diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 3c855a32eed..5897674d1d4 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -713,4 +713,12 @@ EXPLAIN EXTENDED SELECT s FROM t1 WHERE TRIM(BOTH 'y' FROM s) > 'ab'; DROP TABLE t1; +# +# Bug#23409: ENCODE() and DECODE() functions aren't printed correctly +# +create table t1(f1 varchar(4)); +explain extended select encode(f1,'zxcv') as 'enc' from t1; +explain extended select decode(f1,'zxcv') as 'enc' from t1; +drop table t1; + --echo End of 4.1 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index f7408cb5e9f..7c98f0c6380 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1583,6 +1583,19 @@ String *Item_func_encode::val_str(String *str) return res; } +void Item_func_encode::print(String *str) +{ + str->append(func_name()); + str->append('('); + args[0]->print(str); + str->append(','); + str->append('\''); + str->append(seed); + str->append('\''); + str->append(')'); +} + + String *Item_func_decode::val_str(String *str) { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index b7cecad9516..4bd8574ff04 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -343,19 +343,24 @@ class Item_func_encode :public Item_str_func { protected: SQL_CRYPT sql_crypt; + String seed; public: - Item_func_encode(Item *a, char *seed): - Item_str_func(a),sql_crypt(seed) {} + Item_func_encode(Item *a, char *seed_arg): + Item_str_func(a), sql_crypt(seed_arg) + { + seed.copy(seed_arg, strlen(seed_arg), default_charset_info); + } String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "encode"; } + void print(String *str); }; class Item_func_decode :public Item_func_encode { public: - Item_func_decode(Item *a, char *seed): Item_func_encode(a,seed) {} + Item_func_decode(Item *a, char *seed_arg): Item_func_encode(a, seed_arg) {} String *val_str(String *); const char *func_name() const { return "decode"; } }; From 6ca26ea1f4f05d246b6bcc79205ef5fe71edb9c4 Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Thu, 11 Jan 2007 20:51:16 +0100 Subject: [PATCH 21/73] ndb - bug#25554 fix bug when master failure during nr (recommit against 5.0) --- ndb/src/kernel/blocks/dbdih/DbdihMain.cpp | 3 +- ndb/src/kernel/blocks/qmgr/QmgrMain.cpp | 21 +++++++++ ndb/test/ndbapi/testNodeRestart.cpp | 53 +++++++++++++++++++++++ ndb/test/run-test/daily-basic-tests.txt | 4 ++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp b/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp index cd82b6fc425..5f573d40dfe 100644 --- a/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp +++ b/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp @@ -3555,7 +3555,6 @@ void Dbdih::endTakeOver(Uint32 takeOverPtrI) takeOverPtr.i = takeOverPtrI; ptrCheckGuard(takeOverPtr, MAX_NDB_NODES, takeOverRecord); - releaseTakeOver(takeOverPtrI); if ((takeOverPtr.p->toMasterStatus != TakeOverRecord::IDLE) && (takeOverPtr.p->toMasterStatus != TakeOverRecord::TO_WAIT_START_TAKE_OVER)) { jam(); @@ -3569,6 +3568,7 @@ void Dbdih::endTakeOver(Uint32 takeOverPtrI) }//if setAllowNodeStart(takeOverPtr.p->toStartingNode, true); initTakeOver(takeOverPtr); + releaseTakeOver(takeOverPtrI); }//Dbdih::endTakeOver() void Dbdih::releaseTakeOver(Uint32 takeOverPtrI) @@ -4710,6 +4710,7 @@ void Dbdih::handleTakeOverNewMaster(Signal* signal, Uint32 takeOverPtrI) break; } ndbrequire(ok); + endTakeOver(takeOverPtr.i); }//if }//Dbdih::handleTakeOverNewMaster() diff --git a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp index 66ee7549b9d..c5987ee8a57 100644 --- a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp +++ b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp @@ -2847,6 +2847,17 @@ void Qmgr::failReportLab(Signal* signal, Uint16 aFailedNode, systemErrorLab(signal, __LINE__); return; }//if + + if (getNodeState().startLevel < NodeState::SL_STARTED) + { + jam(); + CRASH_INSERTION(932); + char buf[100]; + BaseString::snprintf(buf, 100, "Node failure during restart"); + progError(__LINE__, NDBD_EXIT_SR_OTHERNODEFAILED, buf); + ndbrequire(false); + } + TnoFailedNodes = cnoFailedNodes; failReport(signal, failedNodePtr.i, (UintR)ZTRUE, aFailCause); if (cpresident == getOwnNodeId()) { @@ -2933,6 +2944,16 @@ void Qmgr::execPREP_FAILREQ(Signal* signal) return; }//if + if (getNodeState().startLevel < NodeState::SL_STARTED) + { + jam(); + CRASH_INSERTION(932); + char buf[100]; + BaseString::snprintf(buf, 100, "Node failure during restart"); + progError(__LINE__, NDBD_EXIT_SR_OTHERNODEFAILED, buf); + ndbrequire(false); + } + guard0 = cnoPrepFailedNodes - 1; arrGuard(guard0, MAX_NDB_NODES); for (Tindex = 0; Tindex <= guard0; Tindex++) { diff --git a/ndb/test/ndbapi/testNodeRestart.cpp b/ndb/test/ndbapi/testNodeRestart.cpp index c0c5cc5163a..43fb77342b5 100644 --- a/ndb/test/ndbapi/testNodeRestart.cpp +++ b/ndb/test/ndbapi/testNodeRestart.cpp @@ -995,6 +995,56 @@ int runBug25364(NDBT_Context* ctx, NDBT_Step* step){ return NDBT_OK; } +int runBug25554(NDBT_Context* ctx, NDBT_Step* step){ + + int result = NDBT_OK; + int loops = ctx->getNumLoops(); + int records = ctx->getNumRecords(); + NdbRestarter restarter; + + if (restarter.getNumDbNodes() < 4) + return NDBT_OK; + + for (int i = 0; i Date: Thu, 11 Jan 2007 23:18:01 +0300 Subject: [PATCH 22/73] Bug#23417: Too strict checks against GROUP BY in the ONLY_FULL_GROUP_BY mode. Currently in the ONLY_FULL_GROUP_BY mode no hidden fields are allowed in the select list. To ensure this each expression in the select list is checked to be a constant, an aggregate function or to occur in the GROUP BY list. The last two requirements are wrong and doesn't allow valid expressions like "MAX(b) - MIN(b)" or "a + 1" in a query with grouping by a. The correct check implemented by the patch will ensure that: any field reference in the [sub]expressions of the select list is under an aggregate function or is mentioned as member of the group list or is an outer reference or is part of the select list element that coincide with a grouping element. The Item_field objects now can contain the position of the select list expression which they belong to. The position is saved during the field's Item_field::fix_fields() call. The non_agg_fields list for non-aggregated fields is added to the SELECT_LEX class. The SELECT_LEX::cur_pos_in_select_list now contains the position in the select list of the expression being currently fixed. --- mysql-test/r/group_by.result | 102 +++++++++++++++++++++++++++++++++++ mysql-test/t/group_by.test | 51 ++++++++++++++++++ sql/item.cc | 23 +++++++- sql/item.h | 3 +- sql/mysql_priv.h | 3 +- sql/sql_base.cc | 4 ++ sql/sql_lex.cc | 2 + sql/sql_lex.h | 4 ++ sql/sql_select.cc | 82 +++++++++++++++++++--------- sql/sql_union.cc | 1 + 10 files changed, 247 insertions(+), 28 deletions(-) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 7d1e8832069..97375898f41 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -933,3 +933,105 @@ b sum(1) 18 6 19 6 DROP TABLE t1; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +INSERT INTO t1 VALUES (1,1),(2,1),(3,2),(4,2),(5,3),(6,3); +SET SQL_MODE = 'ONLY_FULL_GROUP_BY'; +SELECT MAX(a)-MIN(a) FROM t1 GROUP BY b; +MAX(a)-MIN(a) +1 +1 +1 +SELECT CEILING(MIN(a)) FROM t1 GROUP BY b; +CEILING(MIN(a)) +1 +3 +5 +SELECT CASE WHEN AVG(a)>=0 THEN 'Positive' ELSE 'Negative' END FROM t1 +GROUP BY b; +CASE WHEN AVG(a)>=0 THEN 'Positive' ELSE 'Negative' END +Positive +Positive +Positive +SELECT a + 1 FROM t1 GROUP BY a; +a + 1 +2 +3 +4 +5 +6 +7 +SELECT a + b FROM t1 GROUP BY b; +ERROR 42000: 'test.t1.a' isn't in GROUP BY +SELECT (SELECT t1_outer.a FROM t1 AS t1_inner GROUP BY b LIMIT 1) +FROM t1 AS t1_outer; +(SELECT t1_outer.a FROM t1 AS t1_inner GROUP BY b LIMIT 1) +1 +2 +3 +4 +5 +6 +SELECT 1 FROM t1 as t1_outer GROUP BY a +HAVING (SELECT t1_outer.a FROM t1 AS t1_inner GROUP BY b LIMIT 1); +1 +1 +1 +1 +1 +1 +1 +SELECT (SELECT t1_outer.a FROM t1 AS t1_inner LIMIT 1) +FROM t1 AS t1_outer GROUP BY t1_outer.b; +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +SELECT 1 FROM t1 as t1_outer GROUP BY a +HAVING (SELECT t1_outer.b FROM t1 AS t1_inner LIMIT 1); +ERROR 42S22: Unknown column 'test.t1_outer.b' in 'field list' +SELECT (SELECT SUM(t1_inner.a) FROM t1 AS t1_inner LIMIT 1) +FROM t1 AS t1_outer GROUP BY t1_outer.b; +(SELECT SUM(t1_inner.a) FROM t1 AS t1_inner LIMIT 1) +21 +21 +21 +SELECT (SELECT SUM(t1_inner.a) FROM t1 AS t1_inner GROUP BY t1_inner.b LIMIT 1) +FROM t1 AS t1_outer; +(SELECT SUM(t1_inner.a) FROM t1 AS t1_inner GROUP BY t1_inner.b LIMIT 1) +3 +3 +3 +3 +3 +3 +SELECT (SELECT SUM(t1_outer.a) FROM t1 AS t1_inner LIMIT 1) +FROM t1 AS t1_outer GROUP BY t1_outer.b; +ERROR 42000: 'test.t1_outer.a' isn't in GROUP BY +SELECT 1 FROM t1 as t1_outer +WHERE (SELECT t1_outer.b FROM t1 AS t1_inner GROUP BY t1_inner.b LIMIT 1); +1 +1 +1 +1 +1 +1 +1 +SELECT b FROM t1 GROUP BY b HAVING CEILING(b) > 0; +b +1 +2 +3 +SELECT 1 FROM t1 GROUP BY b HAVING b = 2 OR b = 3 OR SUM(a) > 12; +1 +1 +1 +SELECT 1 FROM t1 GROUP BY b HAVING ROW (b,b) = ROW (1,1); +1 +1 +SELECT 1 FROM t1 GROUP BY b HAVING a = 2; +ERROR 42S22: Unknown column 'a' in 'having clause' +SELECT 1 FROM t1 GROUP BY SUM(b); +ERROR HY000: Invalid use of group function +SELECT b FROM t1 AS t1_outer GROUP BY a HAVING t1_outer.a IN +(SELECT SUM(t1_inner.b)+t1_outer.b FROM t1 AS t1_inner GROUP BY t1_inner.a +HAVING SUM(t1_inner.b)+t1_outer.b > 5); +ERROR 42000: 'test.t1_outer.b' isn't in GROUP BY +DROP TABLE t1; +SET SQL_MODE = ''; diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index 3e926fba0c6..92c92bf3957 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -701,3 +701,54 @@ EXPLAIN SELECT SQL_BIG_RESULT b, sum(1) FROM t1 GROUP BY b; SELECT b, sum(1) FROM t1 GROUP BY b; SELECT SQL_BIG_RESULT b, sum(1) FROM t1 GROUP BY b; DROP TABLE t1; + +# +# Bug #23417: Too strict checks against GROUP BY in the ONLY_FULL_GROUP_BY mode +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +INSERT INTO t1 VALUES (1,1),(2,1),(3,2),(4,2),(5,3),(6,3); + +SET SQL_MODE = 'ONLY_FULL_GROUP_BY'; +SELECT MAX(a)-MIN(a) FROM t1 GROUP BY b; +SELECT CEILING(MIN(a)) FROM t1 GROUP BY b; +SELECT CASE WHEN AVG(a)>=0 THEN 'Positive' ELSE 'Negative' END FROM t1 + GROUP BY b; +SELECT a + 1 FROM t1 GROUP BY a; +--error ER_WRONG_FIELD_WITH_GROUP +SELECT a + b FROM t1 GROUP BY b; +SELECT (SELECT t1_outer.a FROM t1 AS t1_inner GROUP BY b LIMIT 1) + FROM t1 AS t1_outer; +SELECT 1 FROM t1 as t1_outer GROUP BY a + HAVING (SELECT t1_outer.a FROM t1 AS t1_inner GROUP BY b LIMIT 1); +--error ER_WRONG_FIELD_WITH_GROUP +SELECT (SELECT t1_outer.a FROM t1 AS t1_inner LIMIT 1) + FROM t1 AS t1_outer GROUP BY t1_outer.b; +--error ER_BAD_FIELD_ERROR +SELECT 1 FROM t1 as t1_outer GROUP BY a + HAVING (SELECT t1_outer.b FROM t1 AS t1_inner LIMIT 1); +SELECT (SELECT SUM(t1_inner.a) FROM t1 AS t1_inner LIMIT 1) + FROM t1 AS t1_outer GROUP BY t1_outer.b; +SELECT (SELECT SUM(t1_inner.a) FROM t1 AS t1_inner GROUP BY t1_inner.b LIMIT 1) + FROM t1 AS t1_outer; +--error ER_WRONG_FIELD_WITH_GROUP +SELECT (SELECT SUM(t1_outer.a) FROM t1 AS t1_inner LIMIT 1) + FROM t1 AS t1_outer GROUP BY t1_outer.b; + +SELECT 1 FROM t1 as t1_outer + WHERE (SELECT t1_outer.b FROM t1 AS t1_inner GROUP BY t1_inner.b LIMIT 1); + +SELECT b FROM t1 GROUP BY b HAVING CEILING(b) > 0; + +SELECT 1 FROM t1 GROUP BY b HAVING b = 2 OR b = 3 OR SUM(a) > 12; +SELECT 1 FROM t1 GROUP BY b HAVING ROW (b,b) = ROW (1,1); + +--error ER_BAD_FIELD_ERROR +SELECT 1 FROM t1 GROUP BY b HAVING a = 2; +--error ER_INVALID_GROUP_FUNC_USE +SELECT 1 FROM t1 GROUP BY SUM(b); +--error ER_WRONG_FIELD_WITH_GROUP +SELECT b FROM t1 AS t1_outer GROUP BY a HAVING t1_outer.a IN + (SELECT SUM(t1_inner.b)+t1_outer.b FROM t1 AS t1_inner GROUP BY t1_inner.a + HAVING SUM(t1_inner.b)+t1_outer.b > 5); +DROP TABLE t1; +SET SQL_MODE = ''; diff --git a/sql/item.cc b/sql/item.cc index cc653a7db14..f1423a87837 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3469,6 +3469,16 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) { if (*from_field) { + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && + select->cur_pos_in_select_list != UNDEF_POS) + { + /* + As this is an outer field it should be added to the list of + non aggregated fields of the outer select. + */ + marker= select->cur_pos_in_select_list; + select->non_agg_fields.push_back(this); + } if (*from_field != view_ref_found) { prev_subselect_item->used_tables_cache|= (*from_field)->table->map; @@ -3671,10 +3681,11 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) bool Item_field::fix_fields(THD *thd, Item **reference) { DBUG_ASSERT(fixed == 0); + Field *from_field= (Field *)not_found_field; + bool outer_fixed= false; + if (!field) // If field is not checked { - Field *from_field= (Field *)not_found_field; - bool outer_fixed= false; /* In case of view, find_field_in_tables() write pointer to view field expression to 'reference', i.e. it substitute that expression instead @@ -3766,6 +3777,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) goto error; else if (!ret) return FALSE; + outer_fixed= 1; } set_field(from_field); @@ -3809,6 +3821,13 @@ bool Item_field::fix_fields(THD *thd, Item **reference) } #endif fixed= 1; + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && + !outer_fixed && !thd->lex->in_sum_func && + thd->lex->current_select->cur_pos_in_select_list != UNDEF_POS) + { + thd->lex->current_select->non_agg_fields.push_back(this); + marker= thd->lex->current_select->cur_pos_in_select_list; + } return FALSE; error: diff --git a/sql/item.h b/sql/item.h index 13f0b95c1d1..6065e385a6c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -452,7 +452,8 @@ public: Item *next; uint32 max_length; uint name_length; /* Length of name */ - uint8 marker, decimals; + int8 marker; + uint8 decimals; my_bool maybe_null; /* If item may be null */ my_bool null_value; /* if item is null */ my_bool unsigned_flag; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 534d42e1c17..caf3e6479f9 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -410,7 +410,8 @@ MY_LOCALE *my_locale_by_name(const char *name); #define UNCACHEABLE_EXPLAIN 8 /* Don't evaluate subqueries in prepare even if they're not correlated */ #define UNCACHEABLE_PREPARE 16 - +/* Used to chack GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ +#define UNDEF_POS (-1) #ifdef EXTRA_DEBUG /* Sync points allow us to force the server to reach a certain line of code diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5dcd596f6c2..8ed8526f143 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4393,6 +4393,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, bzero(ref_pointer_array, sizeof(Item *) * fields.elements); Item **ref= ref_pointer_array; + thd->lex->current_select->cur_pos_in_select_list= 0; while ((item= it++)) { if (!item->fixed && item->fix_fields(thd, it.ref()) || @@ -4408,7 +4409,10 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, sum_func_list) item->split_sum_func(thd, ref_pointer_array, *sum_func_list); thd->used_tables|= item->used_tables(); + thd->lex->current_select->cur_pos_in_select_list++; } + thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS; + thd->lex->allow_sum_func= save_allow_sum_func; thd->set_query_id= save_set_query_id; DBUG_RETURN(test(thd->net.report_error)); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 45272645633..3ae95ef9036 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1180,6 +1180,8 @@ void st_select_lex::init_select() offset_limit= 0; /* denotes the default offset = 0 */ with_sum_func= 0; is_correlated= 0; + cur_pos_in_select_list= UNDEF_POS; + non_agg_fields.empty(); } /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index db119d527d9..1bf37c92b85 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -581,6 +581,10 @@ public: bool no_wrap_view_item; /* exclude this select from check of unique_table() */ bool exclude_from_table_unique_test; + /* List of fields that aren't under an aggregate function */ + List non_agg_fields; + /* index in the select list of the expression currently being fixed */ + int cur_pos_in_select_list; List udf_list; /* udf function calls stack */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index abb949ae473..fb4e2c3ab30 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13214,49 +13214,83 @@ setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, bool *hidden_group_fields) { *hidden_group_fields=0; + ORDER *ord; + if (!order) return 0; /* Everything is ok */ - if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) - { - Item *item; - List_iterator li(fields); - while ((item=li++)) - item->marker=0; /* Marker that field is not used */ - } uint org_fields=all_fields.elements; thd->where="group statement"; - for (; order; order=order->next) + for (ord= order; ord; ord= ord->next) { - if (find_order_in_list(thd, ref_pointer_array, tables, order, fields, + if (find_order_in_list(thd, ref_pointer_array, tables, ord, fields, all_fields, TRUE)) return 1; - (*order->item)->marker=1; /* Mark found */ - if ((*order->item)->with_sum_func) + (*ord->item)->marker= UNDEF_POS; /* Mark found */ + if ((*ord->item)->with_sum_func) { - my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*order->item)->full_name()); + my_error(ER_WRONG_GROUP_FIELD, MYF(0), (*ord->item)->full_name()); return 1; } } if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) { - /* Don't allow one to use fields that is not used in GROUP BY */ - Item *item; - List_iterator li(fields); + /* + Don't allow one to use fields that is not used in GROUP BY + For each select a list of field references that aren't under an + aggregate function is created. Each field in this list keeps the + position of the select list expression which it belongs to. - while ((item=li++)) + First we check an expression from the select list against the GROUP BY + list. If it's found there then it's ok. It's also ok if this expression + is a constant or an aggregate function. Otherwise we scan the list + of non-aggregated fields and if we'll find at least one field reference + that belongs to this expression and doesn't occur in the GROUP BY list + we throw an error. If there are no fields in the created list for a + select list expression this means that all fields in it are used under + aggregate functions. + */ + Item *item; + Item_field *field; + int cur_pos_in_select_list= 0; + List_iterator li(fields); + List_iterator naf_it(thd->lex->current_select->non_agg_fields); + + field= naf_it++; + while (field && (item=li++)) { - if (item->type() != Item::SUM_FUNC_ITEM && !item->marker && - !item->const_item()) + if (item->type() != Item::SUM_FUNC_ITEM && item->marker >= 0 && + !item->const_item() && + !(item->real_item()->type() == Item::FIELD_ITEM && + item->used_tables() & OUTER_REF_TABLE_BIT)) { - /* - TODO: change ER_WRONG_FIELD_WITH_GROUP to more detailed - ER_NON_GROUPING_FIELD_USED - */ - my_error(ER_WRONG_FIELD_WITH_GROUP, MYF(0), item->full_name()); - return 1; + while (field) + { + /* Skip fields from previous expressions. */ + if (field->marker < cur_pos_in_select_list) + goto next_field; + /* Found a field from the next expression. */ + if (field->marker > cur_pos_in_select_list) + break; + /* + Check whether the field occur in the GROUP BY list. + Throw the error later if the field isn't found. + */ + for (ord= order; ord; ord= ord->next) + if ((*ord->item)->eq((Item*)field, 0)) + goto next_field; + /* + TODO: change ER_WRONG_FIELD_WITH_GROUP to more detailed + ER_NON_GROUPING_FIELD_USED + */ + my_error(ER_WRONG_FIELD_WITH_GROUP, MYF(0), field->full_name()); + return 1; +next_field: + field= naf_it++; + } } + cur_pos_in_select_list++; } } if (org_fields != all_fields.elements) diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 55e52389a83..8b7dde2f818 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -732,6 +732,7 @@ bool st_select_lex::cleanup() { error= (bool) ((uint) error | (uint) lex_unit->cleanup()); } + non_agg_fields.empty(); DBUG_RETURN(error); } From 12c6e9d2b0a048db845bb72e0102fef0dfaf999f Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 12 Jan 2007 17:35:24 +0300 Subject: [PATCH 23/73] func_str.result: After merge fix --- mysql-test/r/func_str.result | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index c922f57d820..052451f8c54 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -817,7 +817,7 @@ explain extended select md5('hello'), sha('abc'), sha1('abc'), soundex(''), 'moo id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 select md5(_latin1'hello') AS `md5('hello')`,sha(_latin1'abc') AS `sha('abc')`,sha(_latin1'abc') AS `sha1('abc')`,soundex(_latin1'') AS `soundex('')`,(soundex(_latin1'mood') = soundex(_latin1'mud')) AS `'mood' sounds like 'mud'`,aes_decrypt(aes_encrypt(_latin1'abc',_latin1'1'),_latin1'1') AS `aes_decrypt(aes_encrypt('abc','1'),'1')`,concat(_latin1'*',repeat(_latin1' ',5),_latin1'*') AS `concat('*',space(5),'*')`,reverse(_latin1'abc') AS `reverse('abc')`,rpad(_latin1'a',4,_latin1'1') AS `rpad('a',4,'1')`,lpad(_latin1'a',4,_latin1'1') AS `lpad('a',4,'1')`,concat_ws(_latin1',',_latin1'',NULL,_latin1'a') AS `concat_ws(',','',NULL,'a')`,make_set(255,_latin2'a',_latin2'b',_latin2'c') AS `make_set(255,_latin2'a',_latin2'b',_latin2'c')`,elt(2,1) AS `elt(2,1)`,locate(_latin1'a',_latin1'b',2) AS `locate("a","b",2)`,format(130,10) AS `format(130,10)`,char(0) AS `char(0)`,conv(130,16,10) AS `conv(130,16,10)`,hex(130) AS `hex(130)`,cast(_latin1'HE' as char charset binary) AS `binary 'HE'`,export_set(255,_latin2'y',_latin2'n',_latin2' ') AS `export_set(255,_latin2'y',_latin2'n',_latin2' ')`,field((_latin1'b' collate latin1_bin),_latin1'A',_latin1'B') AS `FIELD('b' COLLATE latin1_bin,'A','B')`,find_in_set(_latin1'B',_latin1'a,b,c,d') AS `FIND_IN_SET(_latin1'B',_latin1'a,b,c,d')`,collation(conv(130,16,10)) AS `collation(conv(130,16,10))`,coercibility(conv(130,16,10)) AS `coercibility(conv(130,16,10))`,length(_latin1'\n \r\0\\_\\%\\') AS `length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,concat(_latin1'monty',_latin1' was here ',_latin1'again') AS `concat('monty',' was here ','again')`,length(_latin1'hello') AS `length('hello')`,char(ascii(_latin1'h')) AS `char(ascii('h'))`,ord(_latin1'h') AS `ord('h')`,quote((1 / 0)) AS `quote(1/0)`,crc32(_latin1'123') AS `crc32("123")`,replace(_latin1'aaaa',_latin1'a',_latin1'b') AS `replace('aaaa','a','b')`,insert(_latin1'txs',2,1,_latin1'hi') AS `insert('txs',2,1,'hi')`,left(_latin2'a',1) AS `left(_latin2'a',1)`,right(_latin2'a',1) AS `right(_latin2'a',1)`,lcase(_latin2'a') AS `lcase(_latin2'a')`,ucase(_latin2'a') AS `ucase(_latin2'a')`,substr(_latin1'abcdefg',3,2) AS `SUBSTR('abcdefg',3,2)`,substring_index(_latin1'1abcd;2abcd;3abcd;4abcd',_latin1';',2) AS `substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2)`,trim(_latin2' a ') AS `trim(_latin2' a ')`,ltrim(_latin2' a ') AS `ltrim(_latin2' a ')`,rtrim(_latin2' a ') AS `rtrim(_latin2' a ')`,decode(encode(repeat(_latin1'a',100000))) AS `decode(encode(repeat("a",100000),"monty"),"monty")` +Note 1003 select md5(_latin1'hello') AS `md5('hello')`,sha(_latin1'abc') AS `sha('abc')`,sha(_latin1'abc') AS `sha1('abc')`,soundex(_latin1'') AS `soundex('')`,(soundex(_latin1'mood') = soundex(_latin1'mud')) AS `'mood' sounds like 'mud'`,aes_decrypt(aes_encrypt(_latin1'abc',_latin1'1'),_latin1'1') AS `aes_decrypt(aes_encrypt('abc','1'),'1')`,concat(_latin1'*',repeat(_latin1' ',5),_latin1'*') AS `concat('*',space(5),'*')`,reverse(_latin1'abc') AS `reverse('abc')`,rpad(_latin1'a',4,_latin1'1') AS `rpad('a',4,'1')`,lpad(_latin1'a',4,_latin1'1') AS `lpad('a',4,'1')`,concat_ws(_latin1',',_latin1'',NULL,_latin1'a') AS `concat_ws(',','',NULL,'a')`,make_set(255,_latin2'a',_latin2'b',_latin2'c') AS `make_set(255,_latin2'a',_latin2'b',_latin2'c')`,elt(2,1) AS `elt(2,1)`,locate(_latin1'a',_latin1'b',2) AS `locate("a","b",2)`,format(130,10) AS `format(130,10)`,char(0) AS `char(0)`,conv(130,16,10) AS `conv(130,16,10)`,hex(130) AS `hex(130)`,cast(_latin1'HE' as char charset binary) AS `binary 'HE'`,export_set(255,_latin2'y',_latin2'n',_latin2' ') AS `export_set(255,_latin2'y',_latin2'n',_latin2' ')`,field((_latin1'b' collate latin1_bin),_latin1'A',_latin1'B') AS `FIELD('b' COLLATE latin1_bin,'A','B')`,find_in_set(_latin1'B',_latin1'a,b,c,d') AS `FIND_IN_SET(_latin1'B',_latin1'a,b,c,d')`,collation(conv(130,16,10)) AS `collation(conv(130,16,10))`,coercibility(conv(130,16,10)) AS `coercibility(conv(130,16,10))`,length(_latin1'\n \r\0\\_\\%\\') AS `length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,bit_length(_latin1'\n \r\0\\_\\%\\') AS `bit_length('\n\t\r\b\0\_\%\\')`,concat(_latin1'monty',_latin1' was here ',_latin1'again') AS `concat('monty',' was here ','again')`,length(_latin1'hello') AS `length('hello')`,char(ascii(_latin1'h')) AS `char(ascii('h'))`,ord(_latin1'h') AS `ord('h')`,quote((1 / 0)) AS `quote(1/0)`,crc32(_latin1'123') AS `crc32("123")`,replace(_latin1'aaaa',_latin1'a',_latin1'b') AS `replace('aaaa','a','b')`,insert(_latin1'txs',2,1,_latin1'hi') AS `insert('txs',2,1,'hi')`,left(_latin2'a',1) AS `left(_latin2'a',1)`,right(_latin2'a',1) AS `right(_latin2'a',1)`,lcase(_latin2'a') AS `lcase(_latin2'a')`,ucase(_latin2'a') AS `ucase(_latin2'a')`,substr(_latin1'abcdefg',3,2) AS `SUBSTR('abcdefg',3,2)`,substring_index(_latin1'1abcd;2abcd;3abcd;4abcd',_latin1';',2) AS `substring_index("1abcd;2abcd;3abcd;4abcd", ';', 2)`,trim(_latin2' a ') AS `trim(_latin2' a ')`,ltrim(_latin2' a ') AS `ltrim(_latin2' a ')`,rtrim(_latin2' a ') AS `rtrim(_latin2' a ')`,decode(encode(repeat(_latin1'a',100000),'monty'),'monty') AS `decode(encode(repeat("a",100000),"monty"),"monty")` SELECT lpad(12345, 5, "#"); lpad(12345, 5, "#") 12345 @@ -1089,12 +1089,12 @@ explain extended select encode(f1,'zxcv') as 'enc' from t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found Warnings: -Note 1003 select encode(test.t1.f1,'zxcv') AS `enc` from test.t1 +Note 1003 select encode(`test`.`t1`.`f1`,'zxcv') AS `enc` from `test`.`t1` explain extended select decode(f1,'zxcv') as 'enc' from t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found Warnings: -Note 1003 select decode(test.t1.f1,'zxcv') AS `enc` from test.t1 +Note 1003 select decode(`test`.`t1`.`f1`,'zxcv') AS `enc` from `test`.`t1` drop table t1; End of 4.1 tests create table t1 (d decimal default null); From 52367948559d0b357c932db9fd6b9838a9bd54ed Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Fri, 12 Jan 2007 22:11:40 +0300 Subject: [PATCH 24/73] BUG#24085: Wrong result for NULL IN (SELECT not_null_val FROM ...) When transforming "oe IN (SELECT ie ...)" wrap the pushed-down predicates iff "oe can be null", not "ie can be null". The fix doesn't cover row-based subqueries, those will be fixed in #24127. --- mysql-test/r/subselect.result | 4 +- mysql-test/r/subselect3.result | 90 +++++++++++++++++++++++++++++++--- mysql-test/t/subselect3.test | 71 ++++++++++++++++++++++++++- sql/item_subselect.cc | 39 +++++++++------ 4 files changed, 180 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 1bf6d6c7716..c87de4acf40 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -744,7 +744,7 @@ id select_type table type possible_keys key key_len ref rows Extra 3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL Warnings: -Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id`,(select 1 AS `1` having trigcond(((`test`.`t2`.`id`) = (1))) union select 3 AS `3` having trigcond(((`test`.`t2`.`id`) = (3))))) +Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id`,(select 1 AS `1` having ((`test`.`t2`.`id`) = (1)) union select 3 AS `3` having ((`test`.`t2`.`id`) = (3)))) SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 3); id SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); @@ -907,7 +907,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 func 2 Using where; Using index 2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a`,(`test`.`t1`.`a`,(select 1 AS `Not_used` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t2`.`a`) and trigcond((((`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`)))) having trigcond((`test`.`t2`.`a`)))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1` +Note 1003 select `test`.`t1`.`a` AS `a`,(`test`.`t1`.`a`,(select 1 AS `Not_used` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t2`.`a`) and (((`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`))) having (`test`.`t2`.`a`))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1` drop table t1,t2,t3; create table t1 (a float); select 10.5 IN (SELECT * from t1 LIMIT 1); diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index 5ab8e448b39..10c7e4bc695 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -15,9 +15,8 @@ insert into t2 values (4, NULL), (2, NULL); select a, oref, a in (select max(ie) -from t1 where oref=t2.oref group by grp) from t2; -a oref a in (select max(ie) -from t1 where oref=t2.oref group by grp) +from t1 where oref=t2.oref group by grp) Z from t2; +a oref Z 1 1 1 2 2 0 3 3 NULL @@ -25,14 +24,13 @@ NULL 4 0 NULL 2 NULL explain extended select a, oref, a in (select max(ie) -from t1 where oref=t2.oref group by grp) from t2; +from t1 where oref=t2.oref group by grp) Z from t2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort Warnings: Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 -Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,(`test`.`t2`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having trigcond(((`test`.`t2`.`a`) = (max(`test`.`t1`.`ie`)))))) AS `a in (select max(ie) -from t1 where oref=t2.oref group by grp)` from `test`.`t2` +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,(`test`.`t2`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having trigcond(((`test`.`t2`.`a`) = (max(`test`.`t1`.`ie`)))))) AS `Z` from `test`.`t2` explain extended select a, oref from t2 where a in (select max(ie) from t1 where oref=t2.oref group by grp); @@ -42,6 +40,16 @@ id select_type table type possible_keys key key_len ref rows Extra Warnings: Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref` from `test`.`t2` where (`test`.`t2`.`a`,(select max(`test`.`t1`.`ie`) AS `max(ie)` from `test`.`t1` where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) group by `test`.`t1`.`grp` having ((`test`.`t2`.`a`) = (max(`test`.`t1`.`ie`))))) +select a, oref, a in ( +select max(ie) from t1 where oref=t2.oref group by grp union +select max(ie) from t1 where oref=t2.oref group by grp +) Z from t2; +a oref Z +1 1 1 +2 2 0 +3 3 NULL +NULL 4 0 +NULL 2 NULL create table t3 (a int); insert into t3 values (NULL), (NULL); flush status; @@ -151,3 +159,73 @@ Warnings: Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1 Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`oref` AS `oref`,(`test`.`t3`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`oref`) and trigcond((((`test`.`t3`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)))) having trigcond((`test`.`t1`.`a`)))) AS `Z` from `test`.`t3` drop table t1, t2, t3; +create table t1 (a int NOT NULL, b int NOT NULL, key(a)); +insert into t1 values +(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); +create table t2 like t1; +insert into t2 select * from t1; +update t2 set b=1; +create table t3 (a int, oref int); +insert into t3 values (1, 1), (NULL,1), (NULL,0); +select a, oref, +t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; +a oref Z +1 1 1 +NULL 1 NULL +NULL 0 0 +This must show a trig_cond: +explain extended +select a, oref, +t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t1 ref a a 4 func 2 Using where +2 DEPENDENT SUBQUERY t2 ref a a 4 test.t1.b 1 Using where +Warnings: +Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`oref` AS `oref`,(`test`.`t3`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`oref`) and trigcond(((`test`.`t3`.`a`) = `test`.`t1`.`a`))))) AS `Z` from `test`.`t3` +drop table t1,t2,t3; +create table t1 (oref int, grp int); +insert into t1 (oref, grp) values +(1, 1), +(1, 1); +create table t2 (oref int, a int); +insert into t2 values +(1, NULL), +(2, NULL); +select a, oref, +a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2; +a oref Z +NULL 1 NULL +NULL 2 0 +This must show a trig_cond: +explain extended +select a, oref, +a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`oref` AS `oref`,(`test`.`t2`.`a`,(select count(0) AS `count(*)` from `test`.`t1` group by `test`.`t1`.`grp` having ((`test`.`t1`.`grp` = `test`.`t2`.`oref`) and trigcond(((`test`.`t2`.`a`) = (count(0))))))) AS `Z` from `test`.`t2` +drop table t1, t2; +create table t1 (a int, b int, primary key (a)); +insert into t1 values (1,1), (3,1),(100,1); +create table t2 (a int, b int); +insert into t2 values (1,1),(2,1),(NULL,1),(NULL,0); +select a,b, a in (select a from t1 where t1.b = t2.b union select a from +t1 where t1.b = t2.b) Z from t2 ; +a b Z +1 1 1 +2 1 0 +NULL 1 NULL +NULL 0 0 +select a,b, a in (select a from t1 where t1.b = t2.b) Z from t2 ; +a b Z +1 1 1 +2 1 0 +NULL 1 NULL +NULL 0 0 +drop table t1, t2; diff --git a/mysql-test/t/subselect3.test b/mysql-test/t/subselect3.test index f7fbafdd17f..d10e8d1e469 100644 --- a/mysql-test/t/subselect3.test +++ b/mysql-test/t/subselect3.test @@ -34,18 +34,22 @@ insert into t2 values # true, false, null, false, null select a, oref, a in (select max(ie) - from t1 where oref=t2.oref group by grp) from t2; + from t1 where oref=t2.oref group by grp) Z from t2; # This must have a trigcond explain extended select a, oref, a in (select max(ie) - from t1 where oref=t2.oref group by grp) from t2; + from t1 where oref=t2.oref group by grp) Z from t2; # This must not have a trigcond: explain extended select a, oref from t2 where a in (select max(ie) from t1 where oref=t2.oref group by grp); +select a, oref, a in ( + select max(ie) from t1 where oref=t2.oref group by grp union + select max(ie) from t1 where oref=t2.oref group by grp + ) Z from t2; # Non-correlated subquery, 2 NULL evaluations create table t3 (a int); @@ -135,3 +139,66 @@ from t3; drop table t1, t2, t3; + +# +# BUG#24085 +# + +# case 1: NULL IN (SELECT not_null_val FROM ...) w/o HAVING/GROUP-BY/etc +create table t1 (a int NOT NULL, b int NOT NULL, key(a)); +insert into t1 values + (0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); + +create table t2 like t1; +insert into t2 select * from t1; +update t2 set b=1; + +create table t3 (a int, oref int); +insert into t3 values (1, 1), (NULL,1), (NULL,0); +select a, oref, + t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; + +--echo This must show a trig_cond: +explain extended +select a, oref, + t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z +from t3; +drop table t1,t2,t3; + + +# case 2: NULL IN (SELECT not_null_val FROM) where SELECT has GROUP BY +create table t1 (oref int, grp int); +insert into t1 (oref, grp) values + (1, 1), + (1, 1); +# Ok, for +# select count(*) from t1 group by grp having grp=$PARAM$ +# we'll have: +# 1 -> (2) +# 2 -> () - nothing +create table t2 (oref int, a int); +insert into t2 values + (1, NULL), + (2, NULL); + +select a, oref, + a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2; + +--echo This must show a trig_cond: +explain extended +select a, oref, + a in (select count(*) from t1 group by grp having grp=t2.oref) Z from t2; + +drop table t1, t2; + +create table t1 (a int, b int, primary key (a)); +insert into t1 values (1,1), (3,1),(100,1); +create table t2 (a int, b int); +insert into t2 values (1,1),(2,1),(NULL,1),(NULL,0); + +select a,b, a in (select a from t1 where t1.b = t2.b union select a from +t1 where t1.b = t2.b) Z from t2 ; +select a,b, a in (select a from t1 where t1.b = t2.b) Z from t2 ; +drop table t1, t2; + diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d5d26b7b741..e475634a005 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -922,7 +922,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (!substitution) { - //first call for this unit + /* We're invoked for the 1st (or the only) SELECT in the subquery UNION */ SELECT_LEX_UNIT *unit= select_lex->master_unit(); substitution= optimizer; @@ -972,7 +972,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, ref_pointer_array, (char *)"", this->full_name())); - if (!abort_on_null && ((Item*)select_lex->item_list.head())->maybe_null) + if (!abort_on_null && left_expr->maybe_null) { /* We can encounter "NULL IN (SELECT ...)". Wrap the added condition @@ -1013,9 +1013,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, item= func->create(expr, item); if (!abort_on_null && orig_item->maybe_null) { - having= - new Item_func_trig_cond(new Item_is_not_null_test(this, having), - &enable_pushed_conds); + having= new Item_is_not_null_test(this, having); + if (left_expr->maybe_null) + { + if (!(having= new Item_func_trig_cond(having, + &enable_pushed_conds))) + DBUG_RETURN(RES_ERROR); + } /* Item_is_not_null_test can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last @@ -1032,16 +1036,19 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->having_fix_field= 0; if (tmp) DBUG_RETURN(RES_ERROR); - /* - NOTE: It is important that we add this "IS NULL" here, even when - orig_item can't be NULL. This is needed so that this predicate is - only used by ref[_or_null] analyzer (and, e.g. is not used by const - propagation). - */ item= new Item_cond_or(item, new Item_func_isnull(orig_item)); - item= new Item_func_trig_cond(item, &enable_pushed_conds); } + /* + If we may encounter NULL IN (SELECT ...) and care between NULL and + FALSE, wrap it in a trigger. + */ + if (!abort_on_null && left_expr->maybe_null) + { + if (!(item= new Item_func_trig_cond(item, &enable_pushed_conds))) + DBUG_RETURN(RES_ERROR); + } + item->name= (char *)in_additional_cond; /* AND can't be changed during fix_fields() @@ -1073,9 +1080,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->ref_pointer_array, (char *)"", (char *)"")); - new_having= new Item_func_trig_cond(new_having, &enable_pushed_conds); + if (!abort_on_null && left_expr->maybe_null) + { + if (!(new_having= new Item_func_trig_cond(new_having, + &enable_pushed_conds))) + DBUG_RETURN(RES_ERROR); + } select_lex->having= join->having= new_having; - select_lex->having_fix_field= 1; /* we do not check join->having->fixed, because comparison function From c3f46e1f26ba092a700d3ccdee75ee8060d43ef4 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Fri, 12 Jan 2007 23:22:41 +0300 Subject: [PATCH 25/73] BUG#24127: (a,b) IN (SELECT c,d ...) can produce wrong results if a and/or b are NULLs: - Make the code produce correct result: use an array of triggers to turn on/off equalities for each compared column. Also turn on/off optimizations based on those equalities. - Make EXPLAIN output show "Full scan on NULL key" for tables for which we switch between ref/unique_subquery/index_subquery and ALL access. - index_subquery engine now has HAVING clause when it is needed, and it is displayed in EXPLAIN EXTENDED - Fix incorrect presense of "Using index" for index/unique-based subqueries (BUG#22930) // bk trigger note: this commit refers to BUG#24127 --- mysql-test/r/ndb_subquery.result | 6 +- mysql-test/r/subselect.result | 28 +-- mysql-test/r/subselect2.result | 10 +- mysql-test/r/subselect3.result | 408 ++++++++++++++++++++++++++++++- mysql-test/t/subselect3.test | 291 +++++++++++++++++++++- sql/item_cmpfunc.cc | 34 ++- sql/item_cmpfunc.h | 1 + sql/item_subselect.cc | 267 +++++++++++--------- sql/item_subselect.h | 92 +++++-- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 3 + sql/sql_lex.h | 2 +- sql/sql_select.cc | 190 ++++++++------ sql/sql_select.h | 42 +++- 14 files changed, 1129 insertions(+), 247 deletions(-) diff --git a/mysql-test/r/ndb_subquery.result b/mysql-test/r/ndb_subquery.result index b19571b05c1..a67efb144b2 100644 --- a/mysql-test/r/ndb_subquery.result +++ b/mysql-test/r/ndb_subquery.result @@ -9,7 +9,7 @@ insert into t2 values (1,1,1),(2,2,2),(3,3,3), (4,4,4), (5,5,5); explain select * from t2 where p NOT IN (select p from t1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where -2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 select * from t2 where p NOT IN (select p from t1) order by p; p u o 4 4 4 @@ -17,7 +17,7 @@ p u o explain select * from t2 where p NOT IN (select u from t1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where -2 DEPENDENT SUBQUERY t1 unique_subquery u u 4 func 1 Using index +2 DEPENDENT SUBQUERY t1 unique_subquery u u 4 func 1 select * from t2 where p NOT IN (select u from t1) order by p; p u o 4 4 4 @@ -25,7 +25,7 @@ p u o explain select * from t2 where p NOT IN (select o from t1); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where -2 DEPENDENT SUBQUERY t1 index_subquery o o 4 func 1 Using index +2 DEPENDENT SUBQUERY t1 index_subquery o o 4 func 1 select * from t2 where p NOT IN (select o from t1) order by p; p u o 4 4 4 diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index c87de4acf40..64dfb912db9 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -892,7 +892,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL PRIMARY 4 NULL 4 Using index 2 DEPENDENT SUBQUERY t2 index_subquery a a 5 func 2 Using index Warnings: -Note 1003 select `test`.`t1`.`a` AS `a`,(`test`.`t1`.`a`,(((`test`.`t1`.`a`) in t2 on a checking NULL))) AS `t1.a in (select t2.a from t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`a` AS `a`,(`test`.`t1`.`a`,(((`test`.`t1`.`a`) in t2 on a checking NULL having (`test`.`t2`.`a`)))) AS `t1.a in (select t2.a from t2)` from `test`.`t1` CREATE TABLE t3 (a int(11) default '0'); INSERT INTO t3 VALUES (1),(2),(3); SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1; @@ -1305,7 +1305,7 @@ a explain extended select * from t2 where t2.a in (select a from t1 where t1.b <> 30); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 index NULL PRIMARY 4 NULL 4 Using where; Using index -2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using index; Using where +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using where Warnings: Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY where (`test`.`t1`.`b` <> 30)))) select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a); @@ -1462,27 +1462,27 @@ a3 1 explain extended select s1, s1 NOT IN (SELECT s1 FROM t2) from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index -2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index +2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL)))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond((`test`.`t2`.`s1`)))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1` explain extended select s1, s1 = ANY (SELECT s1 FROM t2) from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index -2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index +2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,(`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,(`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond((`test`.`t2`.`s1`))))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1` explain extended select s1, s1 <> ALL (SELECT s1 FROM t2) from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index -2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index +2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL)))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond((`test`.`t2`.`s1`)))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1` explain extended select s1, s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2') from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index -2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Using where +2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Using where; Full scan on NULL key Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2'))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2') having trigcond((`test`.`t2`.`s1`)))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` drop table t1,t2; create table t2 (a int, b int); create table t3 (a int); @@ -2816,19 +2816,19 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where Warnings: -Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'0') and trigcond(((((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and (((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))))) having trigcond(((`test`.`t2`.`one`) and (`test`.`t2`.`two`))))) AS `test` from `test`.`t1` +Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'0') and trigcond((((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond((((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)))) having (trigcond((`test`.`t2`.`one`)) and trigcond((`test`.`t2`.`two`))))) AS `test` from `test`.`t1` explain extended SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N'); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 Using where 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where Warnings: -Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` where ((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'N') and trigcond((((`test`.`t1`.`one`) = `test`.`t2`.`one`) and ((`test`.`t1`.`two`) = `test`.`t2`.`two`)))))) +Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two` from `test`.`t1` where ((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where ((`test`.`t2`.`flag` = _latin1'N') and ((`test`.`t1`.`one`) = `test`.`t2`.`one`) and ((`test`.`t1`.`two`) = `test`.`t2`.`two`)))) explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 8 2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where; Using temporary; Using filesort Warnings: -Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where (`test`.`t2`.`flag` = _latin1'0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having trigcond(((((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`)) and (((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)) and (`test`.`t2`.`one`) and (`test`.`t2`.`two`))))) AS `test` from `test`.`t1` +Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,((`test`.`t1`.`one`,`test`.`t1`.`two`),(select `test`.`t2`.`one` AS `one`,`test`.`t2`.`two` AS `two` from `test`.`t2` where (`test`.`t2`.`flag` = _latin1'0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having (trigcond((((`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond((((`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))) and trigcond((`test`.`t2`.`one`)) and trigcond((`test`.`t2`.`two`))))) AS `test` from `test`.`t1` DROP TABLE t1,t2; CREATE TABLE t1 (a char(5), b char(5)); INSERT INTO t1 VALUES (NULL,'aaa'), ('aaa','aaa'); @@ -3009,7 +3009,7 @@ INSERT INTO t2 VALUES (1),(2),(3); EXPLAIN SELECT a, a IN (SELECT a FROM t1) FROM t2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 -2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index +2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index; Full scan on NULL key SELECT a, a IN (SELECT a FROM t1) FROM t2; a a IN (SELECT a FROM t1) 1 1 diff --git a/mysql-test/r/subselect2.result b/mysql-test/r/subselect2.result index 75aa339fb29..27ec0ccfc0f 100644 --- a/mysql-test/r/subselect2.result +++ b/mysql-test/r/subselect2.result @@ -126,11 +126,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL DDOCTYPEID_IDX NULL NULL NULL 9 Using where 1 PRIMARY t1 eq_ref PRIMARY PRIMARY 34 test.t2.DOCID 1 1 PRIMARY t4 eq_ref PRIMARY PRIMARY 34 test.t2.DOCTYPEID 1 -2 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where -3 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where -4 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where -5 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using index; Using where -6 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 func 1 Using index; Using where +2 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where +3 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where +4 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where +5 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX PRIMARY 34 func 1 Using where +6 DEPENDENT SUBQUERY t3 unique_subquery PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 func 1 Using where drop table t1, t2, t3, t4; CREATE TABLE t1 (a int(10) , PRIMARY KEY (a)) Engine=InnoDB; INSERT INTO t1 VALUES (1),(2); diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index 10c7e4bc695..07ff17c9df9 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -89,10 +89,10 @@ explain extended select oref, a, a in (select a from t1 where oref=t2.oref) Z from t2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 4 -2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using index; Using where +2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using where; Full scan on NULL key Warnings: Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 -Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,(`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on a checking NULL where (`test`.`t1`.`oref` = `test`.`t2`.`oref`)))) AS `Z` from `test`.`t2` +Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,(`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on a checking NULL where (`test`.`t1`.`oref` = `test`.`t2`.`oref`) having trigcond((`test`.`t1`.`a`))))) AS `Z` from `test`.`t2` flush status; select oref, a from t2 where a in (select a from t1 where oref=t2.oref); oref a @@ -153,7 +153,7 @@ t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z from t3; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 3 -2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 4 Using where +2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 4 Using where; Full scan on NULL key 2 DEPENDENT SUBQUERY t2 ref a a 5 test.t1.b 1 Using where Warnings: Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1 @@ -181,7 +181,7 @@ t3.a in (select t1.a from t1, t2 where t1.b=t2.a and t2.b=t3.oref) Z from t3; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t3 ALL NULL NULL NULL NULL 3 -2 DEPENDENT SUBQUERY t1 ref a a 4 func 2 Using where +2 DEPENDENT SUBQUERY t1 ref a a 4 func 2 Using where; Full scan on NULL key 2 DEPENDENT SUBQUERY t2 ref a a 4 test.t1.b 1 Using where Warnings: Note 1276 Field or reference 't3.oref' of SELECT #2 was resolved in SELECT #1 @@ -229,3 +229,403 @@ a b Z NULL 1 NULL NULL 0 0 drop table t1, t2; +create table t3 (a int); +insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t2 (a int, b int, oref int); +insert into t2 values (NULL,1, 100), (NULL,2, 100); +create table t1 (a int, b int, c int, key(a,b)); +insert into t1 select 2*A, 2*A, 100 from t3; +explain extended select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 +2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 2 Using where; Full scan on NULL key +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`oref` AS `oref`,((`test`.`t2`.`a`,`test`.`t2`.`b`),(((`test`.`t2`.`a`) in t1 on a checking NULL where ((`test`.`t1`.`c` = `test`.`t2`.`oref`) and trigcond((((`test`.`t2`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`))) and trigcond((((`test`.`t2`.`b`) = `test`.`t1`.`b`) or isnull(`test`.`t1`.`b`)))) having (trigcond((`test`.`t1`.`a`)) and trigcond((`test`.`t1`.`b`)))))) AS `Z` from `test`.`t2` +select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2; +a b oref Z +NULL 1 100 0 +NULL 2 100 NULL +create table t4 (x int); +insert into t4 select A.a + 10*B.a from t1 A, t1 B; +explain extended +select a,b, oref, +(a,b) in (select a,b from t1,t4 where c=t2.oref) Z +from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 +2 DEPENDENT SUBQUERY t1 ref_or_null a a 5 func 2 Using where; Full scan on NULL key +2 DEPENDENT SUBQUERY t4 ALL NULL NULL NULL NULL 100 Using where +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`oref` AS `oref`,((`test`.`t2`.`a`,`test`.`t2`.`b`),(select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t4` where ((`test`.`t1`.`c` = `test`.`t2`.`oref`) and trigcond((((`test`.`t2`.`a`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`))) and trigcond((((`test`.`t2`.`b`) = `test`.`t1`.`b`) or isnull(`test`.`t1`.`b`)))) having (trigcond((`test`.`t1`.`a`)) and trigcond((`test`.`t1`.`b`))))) AS `Z` from `test`.`t2` +select a,b, oref, +(a,b) in (select a,b from t1,t4 where c=t2.oref) Z +from t2; +a b oref Z +NULL 1 100 0 +NULL 2 100 NULL +drop table t1,t2,t3,t4; +create table t1 (oref char(4), grp int, ie1 int, ie2 int); +insert into t1 (oref, grp, ie1, ie2) values +('aa', 10, 2, 1), +('aa', 10, 1, 1), +('aa', 20, 2, 1), +('bb', 10, 3, 1), +('cc', 10, 4, 2), +('cc', 20, 3, 2), +('ee', 10, 2, 1), +('ee', 10, 1, 2), +('ff', 20, 2, 2), +('ff', 20, 1, 2); +create table t2 (oref char(4), a int, b int); +insert into t2 values +('ee', NULL, 1), +('bb', 2, 1), +('ff', 2, 2), +('cc', 3, NULL), +('bb', NULL, NULL), +('aa', 1, 1), +('dd', 1, NULL); +alter table t1 add index idx(ie1,ie2); +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=3 and b is null ; +oref a b Z +cc 3 NULL NULL +insert into t2 values ('new1', 10,10); +insert into t1 values ('new1', 1234, 10, NULL); +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10; +oref a b Z +new1 10 10 NULL +explain extended +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 8 Using where +2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,((`test`.`t2`.`a`,`test`.`t2`.`b`),(((`test`.`t2`.`a`) in t1 on idx checking NULL where ((`test`.`t1`.`oref` = `test`.`t2`.`oref`) and trigcond((((`test`.`t2`.`a`) = `test`.`t1`.`ie1`) or isnull(`test`.`t1`.`ie1`))) and trigcond((((`test`.`t2`.`b`) = `test`.`t1`.`ie2`) or isnull(`test`.`t1`.`ie2`)))) having (trigcond((`test`.`t1`.`ie1`)) and trigcond((`test`.`t1`.`ie2`)))))) AS `Z` from `test`.`t2` where ((`test`.`t2`.`b` = 10) and (`test`.`t2`.`a` = 10)) +drop table t1, t2; +create table t1 (oref char(4), grp int, ie int); +insert into t1 (oref, grp, ie) values +('aa', 10, 2), +('aa', 10, 1), +('aa', 20, NULL), +('bb', 10, 3), +('cc', 10, 4), +('cc', 20, NULL), +('ee', 10, NULL), +('ee', 10, NULL), +('ff', 20, 2), +('ff', 20, 1); +create table t2 (oref char(4), a int); +insert into t2 values +('ee', NULL), +('bb', 2), +('ff', 2), +('cc', 3), +('aa', 1), +('dd', NULL), +('bb', NULL); +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; +oref a Z +ee NULL NULL +bb 2 0 +ff 2 1 +cc 3 NULL +aa 1 1 +dd NULL 0 +bb NULL NULL +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); +oref a +ff 2 +aa 1 +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); +oref a +bb 2 +dd NULL +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2; +oref a Z +ee NULL NULL +bb 2 0 +ff 2 0 +cc 3 NULL +aa 1 1 +dd NULL 0 +bb NULL NULL +select oref, a from t2 where +a in (select min(ie) from t1 where oref=t2.oref group by grp); +oref a +aa 1 +select oref, a from t2 where +a not in (select min(ie) from t1 where oref=t2.oref group by grp); +oref a +bb 2 +ff 2 +dd NULL +update t1 set ie=3 where oref='ff' and ie=1; +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by +grp) Z from t2; +oref a Z +ee NULL NULL +bb 2 0 +ff 2 1 +cc 3 NULL +aa 1 1 +dd NULL 0 +bb NULL NULL +select oref, a from t2 where a in (select min(ie) from t1 where +oref=t2.oref group by grp); +oref a +ff 2 +aa 1 +select oref, a from t2 where a not in (select min(ie) from t1 where +oref=t2.oref group by grp); +oref a +bb 2 +dd NULL +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by +grp having min(ie) > 1) Z from t2; +oref a Z +ee NULL 0 +bb 2 0 +ff 2 1 +cc 3 0 +aa 1 0 +dd NULL 0 +bb NULL NULL +select oref, a from t2 where a in (select min(ie) from t1 where +oref=t2.oref group by grp having min(ie) > 1); +oref a +ff 2 +select oref, a from t2 where a not in (select min(ie) from t1 where +oref=t2.oref group by grp having min(ie) > 1); +oref a +ee NULL +bb 2 +cc 3 +aa 1 +dd NULL +alter table t1 add index idx(ie); +explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 +2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; +oref a Z +ee NULL NULL +bb 2 0 +ff 2 1 +cc 3 NULL +aa 1 1 +dd NULL 0 +bb NULL NULL +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); +oref a +ff 2 +aa 1 +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); +oref a +bb 2 +dd NULL +alter table t1 drop index idx; +alter table t1 add index idx(oref,ie); +explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 +2 DEPENDENT SUBQUERY t1 ref_or_null idx idx 10 test.t2.oref,func 4 Using where; Using index; Full scan on NULL key +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; +oref a Z +ee NULL NULL +bb 2 0 +ff 2 1 +cc 3 NULL +aa 1 1 +dd NULL 0 +bb NULL NULL +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); +oref a +ff 2 +aa 1 +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); +oref a +bb 2 +dd NULL +explain +select oref, a, +a in (select min(ie) from t1 where oref=t2.oref +group by grp having min(ie) > 1) Z +from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 +2 DEPENDENT SUBQUERY t1 ref idx idx 5 test.t2.oref 2 Using where; Using temporary; Using filesort +select oref, a, +a in (select min(ie) from t1 where oref=t2.oref +group by grp having min(ie) > 1) Z +from t2; +oref a Z +ee NULL 0 +bb 2 0 +ff 2 1 +cc 3 0 +aa 1 0 +dd NULL 0 +bb NULL NULL +select oref, a from t2 where a in (select min(ie) from t1 where oref=t2.oref +group by grp having min(ie) > 1); +oref a +ff 2 +select oref, a from t2 where a not in (select min(ie) from t1 where oref=t2.oref +group by grp having min(ie) > 1); +oref a +ee NULL +bb 2 +cc 3 +aa 1 +dd NULL +drop table t1,t2; +create table t1 (oref char(4), grp int, ie1 int, ie2 int); +insert into t1 (oref, grp, ie1, ie2) values +('aa', 10, 2, 1), +('aa', 10, 1, 1), +('aa', 20, 2, 1), +('bb', 10, 3, 1), +('cc', 10, 4, 2), +('cc', 20, 3, 2), +('ee', 10, 2, 1), +('ee', 10, 1, 2), +('ff', 20, 2, 2), +('ff', 20, 1, 2); +create table t2 (oref char(4), a int, b int); +insert into t2 values +('ee', NULL, 1), +('bb', 2, 1), +('ff', 2, 2), +('cc', 3, NULL), +('bb', NULL, NULL), +('aa', 1, 1), +('dd', 1, NULL); +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; +oref a b Z +ee NULL 1 NULL +bb 2 1 0 +ff 2 2 1 +cc 3 NULL NULL +bb NULL NULL NULL +aa 1 1 1 +dd 1 NULL 0 +select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref); +oref a b +ff 2 2 +aa 1 1 +select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref); +oref a b +bb 2 1 +dd 1 NULL +select oref, a, b, +(a,b) in (select min(ie1),max(ie2) from t1 +where oref=t2.oref group by grp) Z +from t2; +oref a b Z +ee NULL 1 0 +bb 2 1 0 +ff 2 2 0 +cc 3 NULL NULL +bb NULL NULL NULL +aa 1 1 1 +dd 1 NULL 0 +select oref, a, b from t2 where +(a,b) in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp); +oref a b +aa 1 1 +select oref, a, b from t2 where +(a,b) not in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp); +oref a b +ee NULL 1 +bb 2 1 +ff 2 2 +dd 1 NULL +alter table t1 add index idx(ie1,ie2); +explain select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 +2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; +oref a b Z +ee NULL 1 NULL +bb 2 1 0 +ff 2 2 1 +cc 3 NULL NULL +bb NULL NULL NULL +aa 1 1 1 +dd 1 NULL 0 +select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref); +oref a b +ff 2 2 +aa 1 1 +select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref); +oref a b +bb 2 1 +dd 1 NULL +explain extended +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 +2 DEPENDENT SUBQUERY t1 index_subquery idx idx 5 func 4 Using where; Full scan on NULL key +Warnings: +Note 1276 Field or reference 't2.oref' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t2`.`oref` AS `oref`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,((`test`.`t2`.`a`,`test`.`t2`.`b`),(((`test`.`t2`.`a`) in t1 on idx checking NULL where ((`test`.`t1`.`oref` = `test`.`t2`.`oref`) and trigcond((((`test`.`t2`.`a`) = `test`.`t1`.`ie1`) or isnull(`test`.`t1`.`ie1`))) and trigcond((((`test`.`t2`.`b`) = `test`.`t1`.`ie2`) or isnull(`test`.`t1`.`ie2`)))) having (trigcond((`test`.`t1`.`ie1`)) and trigcond((`test`.`t1`.`ie2`)))))) AS `Z` from `test`.`t2` +drop table t1,t2; +create table t1 (oref char(4), grp int, ie int primary key); +insert into t1 (oref, grp, ie) values +('aa', 10, 2), +('aa', 10, 1), +('bb', 10, 3), +('cc', 10, 4), +('cc', 20, 5), +('cc', 10, 6); +create table t2 (oref char(4), a int); +insert into t2 values +('ee', NULL), +('bb', 2), +('cc', 5), +('cc', 2), +('cc', NULL), +('aa', 1), +('bb', NULL); +explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using where; Full scan on NULL key +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; +oref a Z +ee NULL 0 +bb 2 0 +cc 5 1 +cc 2 0 +cc NULL NULL +aa 1 1 +bb NULL NULL +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); +oref a +cc 5 +aa 1 +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); +oref a +ee NULL +bb 2 +cc 2 +explain +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 7 +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using where; Using temporary; Using filesort +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2; +oref a Z +ee NULL 0 +bb 2 0 +cc 5 1 +cc 2 0 +cc NULL NULL +aa 1 1 +bb NULL NULL +drop table t1,t2; diff --git a/mysql-test/t/subselect3.test b/mysql-test/t/subselect3.test index d10e8d1e469..7910a88c5d6 100644 --- a/mysql-test/t/subselect3.test +++ b/mysql-test/t/subselect3.test @@ -16,13 +16,14 @@ insert into t1 (oref, grp, ie) values (3, 1, 4), (3, 2, NULL); -# Ok, for +# Ok, for # select max(ie) from t1 where oref=PARAM group by grp # we'll have: -# 1 -> (1, NULL) matching + NULL -# 2 -> (3) non-matching -# 3 -> (3, NULL) non-matching + NULL -# 4 -> () nothing. +# PARAM subquery result +# 1 -> {(1), (NULL)} matching + NULL +# 2 -> {(3)} non-matching +# 3 -> {(3), (NULL)} non-matching + NULL +# 4 -> {} empty set create table t2 (oref int, a int); insert into t2 values @@ -141,7 +142,7 @@ drop table t1, t2, t3; # -# BUG#24085 +# BUG#24085: Wrong query result for "NULL IN (SELECT ... UNION SELECT ...)" # # case 1: NULL IN (SELECT not_null_val FROM ...) w/o HAVING/GROUP-BY/etc @@ -172,11 +173,13 @@ create table t1 (oref int, grp int); insert into t1 (oref, grp) values (1, 1), (1, 1); -# Ok, for -# select count(*) from t1 group by grp having grp=$PARAM$ + +# Ok, for +# select count(*) from t1 group by grp having grp=PARAM # we'll have: -# 1 -> (2) -# 2 -> () - nothing +# PARAM subuqery result +# 1 -> {(2)} +# 2 -> {} - empty set create table t2 (oref int, a int); insert into t2 values (1, NULL), @@ -202,3 +205,271 @@ t1 where t1.b = t2.b) Z from t2 ; select a,b, a in (select a from t1 where t1.b = t2.b) Z from t2 ; drop table t1, t2; + +# +# BUG#24127: Incorrect results of row-based subqueries with NULLs on the left side. +# +create table t3 (a int); +insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t2 (a int, b int, oref int); +insert into t2 values (NULL,1, 100), (NULL,2, 100); + +create table t1 (a int, b int, c int, key(a,b)); +insert into t1 select 2*A, 2*A, 100 from t3; + +# First test index subquery engine +explain extended select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2; +select a,b, oref, (a,b) in (select a,b from t1 where c=t2.oref) Z from t2; + +# Then check that we do turn off 'ref' scans in the subquery +create table t4 (x int); +insert into t4 select A.a + 10*B.a from t1 A, t1 B; +explain extended + select a,b, oref, + (a,b) in (select a,b from t1,t4 where c=t2.oref) Z + from t2; +select a,b, oref, + (a,b) in (select a,b from t1,t4 where c=t2.oref) Z +from t2; + +drop table t1,t2,t3,t4; + +# More tests for tricky multi-column cases, where some of pushed-down +# equalities are used for index lookups and some arent. +create table t1 (oref char(4), grp int, ie1 int, ie2 int); +insert into t1 (oref, grp, ie1, ie2) values + ('aa', 10, 2, 1), + ('aa', 10, 1, 1), + ('aa', 20, 2, 1), + ('bb', 10, 3, 1), + ('cc', 10, 4, 2), + ('cc', 20, 3, 2), + + ('ee', 10, 2, 1), + ('ee', 10, 1, 2), + + ('ff', 20, 2, 2), + ('ff', 20, 1, 2); +create table t2 (oref char(4), a int, b int); +insert into t2 values + ('ee', NULL, 1), + ('bb', 2, 1), + ('ff', 2, 2), + ('cc', 3, NULL), + ('bb', NULL, NULL), + ('aa', 1, 1), + ('dd', 1, NULL); +alter table t1 add index idx(ie1,ie2); + +--cc 3 NULL NULL +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=3 and b is null ; +insert into t2 values ('new1', 10,10); +insert into t1 values ('new1', 1234, 10, NULL); +-- new1, 10, 10, NULL, +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10; +explain extended +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2 where a=10 and b=10; +drop table t1, t2; + +# Now test different column types: +create table t1 (oref char(4), grp int, ie int); +insert into t1 (oref, grp, ie) values + ('aa', 10, 2), + ('aa', 10, 1), + ('aa', 20, NULL), + + ('bb', 10, 3), + + ('cc', 10, 4), + ('cc', 20, NULL), + + ('ee', 10, NULL), + ('ee', 10, NULL), + + ('ff', 20, 2), + ('ff', 20, 1); + +create table t2 (oref char(4), a int); +insert into t2 values + ('ee', NULL), + ('bb', 2), + ('ff', 2), + ('cc', 3), + ('aa', 1), + ('dd', NULL), + ('bb', NULL); + +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; + +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); + +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); + + +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2; + +select oref, a from t2 where + a in (select min(ie) from t1 where oref=t2.oref group by grp); + +select oref, a from t2 where + a not in (select min(ie) from t1 where oref=t2.oref group by grp); + +# +update t1 set ie=3 where oref='ff' and ie=1; + +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by +grp) Z from t2; + + +select oref, a from t2 where a in (select min(ie) from t1 where +oref=t2.oref group by grp); + +select oref, a from t2 where a not in (select min(ie) from t1 where +oref=t2.oref group by grp); + +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by +grp having min(ie) > 1) Z from t2; + +select oref, a from t2 where a in (select min(ie) from t1 where +oref=t2.oref group by grp having min(ie) > 1); + +select oref, a from t2 where a not in (select min(ie) from t1 where +oref=t2.oref group by grp having min(ie) > 1); + +# +alter table t1 add index idx(ie); + +explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; + +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; + +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); + +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); + + +alter table t1 drop index idx; +alter table t1 add index idx(oref,ie); + +explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; + +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; + +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); + +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); + +explain +select oref, a, + a in (select min(ie) from t1 where oref=t2.oref + group by grp having min(ie) > 1) Z +from t2; + +select oref, a, + a in (select min(ie) from t1 where oref=t2.oref + group by grp having min(ie) > 1) Z +from t2; + +select oref, a from t2 where a in (select min(ie) from t1 where oref=t2.oref + group by grp having min(ie) > 1); + +select oref, a from t2 where a not in (select min(ie) from t1 where oref=t2.oref + group by grp having min(ie) > 1); + +drop table t1,t2; + +create table t1 (oref char(4), grp int, ie1 int, ie2 int); +insert into t1 (oref, grp, ie1, ie2) values + ('aa', 10, 2, 1), + ('aa', 10, 1, 1), + ('aa', 20, 2, 1), + + ('bb', 10, 3, 1), + + ('cc', 10, 4, 2), + ('cc', 20, 3, 2), + + ('ee', 10, 2, 1), + ('ee', 10, 1, 2), + + ('ff', 20, 2, 2), + ('ff', 20, 1, 2); + +create table t2 (oref char(4), a int, b int); +insert into t2 values + ('ee', NULL, 1), + ('bb', 2, 1), + ('ff', 2, 2), + ('cc', 3, NULL), + ('bb', NULL, NULL), + ('aa', 1, 1), + ('dd', 1, NULL); + +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; + +select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref); + +select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref); + +select oref, a, b, + (a,b) in (select min(ie1),max(ie2) from t1 + where oref=t2.oref group by grp) Z +from t2; + +select oref, a, b from t2 where + (a,b) in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp); + +select oref, a, b from t2 where + (a,b) not in (select min(ie1), max(ie2) from t1 where oref=t2.oref group by grp); + +alter table t1 add index idx(ie1,ie2); + +explain select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; + +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; + +select oref, a, b from t2 where (a,b) in (select ie1,ie2 from t1 where oref=t2.oref); + +select oref, a, b from t2 where (a,b) not in (select ie1,ie2 from t1 where oref=t2.oref); + +explain extended +select oref, a, b, (a,b) in (select ie1,ie2 from t1 where oref=t2.oref) Z from t2; + +drop table t1,t2; + +create table t1 (oref char(4), grp int, ie int primary key); +insert into t1 (oref, grp, ie) values + ('aa', 10, 2), + ('aa', 10, 1), + + ('bb', 10, 3), + + ('cc', 10, 4), + ('cc', 20, 5), + ('cc', 10, 6); + +create table t2 (oref char(4), a int); +insert into t2 values + ('ee', NULL), + ('bb', 2), + ('cc', 5), + ('cc', 2), + ('cc', NULL), + ('aa', 1), + ('bb', NULL); + +explain select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; + +select oref, a, a in (select ie from t1 where oref=t2.oref) Z from t2; + +select oref, a from t2 where a in (select ie from t1 where oref=t2.oref); + +select oref, a from t2 where a not in (select ie from t1 where oref=t2.oref); + +explain +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2; + +select oref, a, a in (select min(ie) from t1 where oref=t2.oref group by grp) Z from t2; + +drop table t1,t2; + diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 936ae04e93d..ed7ee4b96f4 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -813,11 +813,35 @@ longlong Item_in_optimizer::val_int() We disable the predicates we've pushed down into subselect, run the subselect and see if it has produced any rows. */ - ((Item_in_subselect*)args[1])->enable_pushed_conds= FALSE; - longlong tmp= args[1]->val_bool_result(); - result_for_null_param= null_value= - !((Item_in_subselect*)args[1])->engine->no_rows(); - ((Item_in_subselect*)args[1])->enable_pushed_conds= TRUE; + Item_in_subselect *item_subs=(Item_in_subselect*)args[1]; + if (cache->cols() == 1) + { + item_subs->set_cond_guard_var(0, FALSE); + longlong tmp= args[1]->val_bool_result(); + result_for_null_param= null_value= !item_subs->engine->no_rows(); + item_subs->set_cond_guard_var(0, TRUE); + } + else + { + uint i; + uint ncols= cache->cols(); + /* + Turn off the predicates that are based on column compares for + which the left part is currently NULL + */ + for (i= 0; i < ncols; i++) + { + if (cache->el(i)->null_value) + item_subs->set_cond_guard_var(i, FALSE); + } + + longlong tmp= args[1]->val_bool_result(); + result_for_null_param= null_value= !item_subs->engine->no_rows(); + + /* Turn all predicates back on */ + for (i= 0; i < ncols; i++) + item_subs->set_cond_guard_var(i, TRUE); + } } } return 0; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 7bdc90adcee..af98e51c55d 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -311,6 +311,7 @@ public: enum Functype functype() const { return TRIG_COND_FUNC; }; const char *func_name() const { return "trigcond"; }; bool const_item() const { return FALSE; } + bool *get_trig_var() { return trig_var; } }; class Item_func_not_all :public Item_func_not diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index e475634a005..7e6a5614c27 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -191,16 +191,16 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) return res; } -bool Item_subselect::exec(bool full_scan) +bool Item_subselect::exec() { int res; - res= engine->exec(full_scan); + res= engine->exec(); if (engine_changed) { engine_changed= 0; - return exec(full_scan); + return exec(); } return (res); } @@ -448,13 +448,13 @@ bool Item_singlerow_subselect::null_inside() void Item_singlerow_subselect::bring_value() { - exec(FALSE); + exec(); } double Item_singlerow_subselect::val_real() { DBUG_ASSERT(fixed == 1); - if (!exec(FALSE) && !value->null_value) + if (!exec() && !value->null_value) { null_value= 0; return value->val_real(); @@ -469,7 +469,7 @@ double Item_singlerow_subselect::val_real() longlong Item_singlerow_subselect::val_int() { DBUG_ASSERT(fixed == 1); - if (!exec(FALSE) && !value->null_value) + if (!exec() && !value->null_value) { null_value= 0; return value->val_int(); @@ -483,7 +483,7 @@ longlong Item_singlerow_subselect::val_int() String *Item_singlerow_subselect::val_str(String *str) { - if (!exec(FALSE) && !value->null_value) + if (!exec() && !value->null_value) { null_value= 0; return value->val_str(str); @@ -498,7 +498,7 @@ String *Item_singlerow_subselect::val_str(String *str) my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) { - if (!exec(FALSE) && !value->null_value) + if (!exec() && !value->null_value) { null_value= 0; return value->val_decimal(decimal_value); @@ -513,7 +513,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) bool Item_singlerow_subselect::val_bool() { - if (!exec(FALSE) && !value->null_value) + if (!exec() && !value->null_value) { null_value= 0; return value->val_bool(); @@ -565,7 +565,7 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): Item_exists_subselect(), optimizer(0), transformed(0), - enable_pushed_conds(TRUE), upper_item(0) + pushed_cond_guards(NULL), upper_item(0) { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; @@ -610,7 +610,7 @@ void Item_exists_subselect::fix_length_and_dec() double Item_exists_subselect::val_real() { DBUG_ASSERT(fixed == 1); - if (exec(FALSE)) + if (exec()) { reset(); return 0; @@ -621,7 +621,7 @@ double Item_exists_subselect::val_real() longlong Item_exists_subselect::val_int() { DBUG_ASSERT(fixed == 1); - if (exec(FALSE)) + if (exec()) { reset(); return 0; @@ -632,7 +632,7 @@ longlong Item_exists_subselect::val_int() String *Item_exists_subselect::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (exec(FALSE)) + if (exec()) { reset(); return 0; @@ -645,7 +645,7 @@ String *Item_exists_subselect::val_str(String *str) my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); - if (exec(FALSE)) + if (exec()) { reset(); return 0; @@ -658,7 +658,7 @@ my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) bool Item_exists_subselect::val_bool() { DBUG_ASSERT(fixed == 1); - if (exec(FALSE)) + if (exec()) { reset(); return 0; @@ -676,7 +676,7 @@ double Item_in_subselect::val_real() DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec(!enable_pushed_conds)) + if (exec()) { reset(); null_value= 1; @@ -697,7 +697,7 @@ longlong Item_in_subselect::val_int() DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec(!enable_pushed_conds)) + if (exec()) { reset(); null_value= 1; @@ -718,7 +718,7 @@ String *Item_in_subselect::val_str(String *str) DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec(!enable_pushed_conds)) + if (exec()) { reset(); null_value= 1; @@ -738,7 +738,7 @@ bool Item_in_subselect::val_bool() { DBUG_ASSERT(fixed == 1); null_value= 0; - if (exec(!enable_pushed_conds)) + if (exec()) { reset(); null_value= 1; @@ -758,7 +758,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value) DBUG_ASSERT(0); null_value= 0; DBUG_ASSERT(fixed == 1); - if (exec(!enable_pushed_conds)) + if (exec()) { reset(); null_value= 1; @@ -948,19 +948,14 @@ Item_in_subselect::single_value_transformer(JOIN *join, unit->uncacheable|= UNCACHEABLE_DEPENDENT; } + if (!abort_on_null && left_expr->maybe_null) + { + if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool)))) + DBUG_RETURN(RES_ERROR); + pushed_cond_guards[0]= TRUE; + } select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; - /* - Add the left part of a subselect to a WHERE or HAVING clause of - the right part, e.g. - - SELECT 1 IN (SELECT a FROM t1) => - - SELECT Item_in_optimizer(1, SELECT a FROM t1 WHERE a=1) - - HAVING is used only if the right part contains a SUM function, a GROUP - BY or a HAVING clause. - */ if (join->having || select_lex->with_sum_func || select_lex->group_list.elements) { @@ -976,9 +971,9 @@ Item_in_subselect::single_value_transformer(JOIN *join, { /* We can encounter "NULL IN (SELECT ...)". Wrap the added condition - within a trigger. + within a trig_cond. */ - item= new Item_func_trig_cond(item, &enable_pushed_conds); + item= new Item_func_trig_cond(item, get_cond_guard(0)); } /* @@ -987,6 +982,8 @@ Item_in_subselect::single_value_transformer(JOIN *join, argument (reference) to fix_fields() */ select_lex->having= join->having= and_items(join->having, item); + if (join->having == item) + item->name= (char*)in_having_cond; select_lex->having_fix_field= 1; /* we do not check join->having->fixed, because Item_and (from and_items) @@ -1017,7 +1014,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (left_expr->maybe_null) { if (!(having= new Item_func_trig_cond(having, - &enable_pushed_conds))) + get_cond_guard(0)))) DBUG_RETURN(RES_ERROR); } /* @@ -1025,6 +1022,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ + having->name= (char*)in_having_cond; select_lex->having= join->having= having; select_lex->having_fix_field= 1; /* @@ -1040,16 +1038,21 @@ Item_in_subselect::single_value_transformer(JOIN *join, new Item_func_isnull(orig_item)); } /* - If we may encounter NULL IN (SELECT ...) and care between NULL and - FALSE, wrap it in a trigger. + If we may encounter NULL IN (SELECT ...) and care whether subquery + result is NULL or FALSE, wrap condition in a trig_cond. */ if (!abort_on_null && left_expr->maybe_null) { - if (!(item= new Item_func_trig_cond(item, &enable_pushed_conds))) + if (!(item= new Item_func_trig_cond(item, get_cond_guard(0)))) DBUG_RETURN(RES_ERROR); } - + /* + TODO: figure out why the following is done here in + single_value_transformer but there is no corresponding action in + row_value_transformer? + */ item->name= (char *)in_additional_cond; + /* AND can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last @@ -1083,11 +1086,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (!abort_on_null && left_expr->maybe_null) { if (!(new_having= new Item_func_trig_cond(new_having, - &enable_pushed_conds))) + get_cond_guard(0)))) DBUG_RETURN(RES_ERROR); } + new_having->name= (char*)in_having_cond; select_lex->having= join->having= new_having; select_lex->having_fix_field= 1; + /* we do not check join->having->fixed, because comparison function (from func->create) can't be fixed after creation @@ -1157,6 +1162,15 @@ Item_in_subselect::row_value_transformer(JOIN *join) thd->lex->current_select= current; unit->uncacheable|= UNCACHEABLE_DEPENDENT; + + if (!abort_on_null && left_expr->maybe_null) + { + if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool) * + left_expr->cols()))) + DBUG_RETURN(RES_ERROR); + for (uint i= 0; i < cols_num; i++) + pushed_cond_guards[i]= TRUE; + } } select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; @@ -1173,6 +1187,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) is_not_null_test(v3)) where is_not_null_test used to register nulls in case if we have not found matching to return correct NULL value + TODO: say here explicitly if the order of AND parts matters or not. */ Item *item_having_part2= 0; for (uint i= 0; i < cols_num; i++) @@ -1201,21 +1216,28 @@ Item_in_subselect::row_value_transformer(JOIN *join) (char *)"", (char *)"") ); - having_item= - and_items(having_item, - new Item_cond_or(item_eq, item_isnull)); - item_having_part2= - and_items(item_having_part2, - new - Item_is_not_null_test(this, - new - Item_ref(&select_lex->context, - select_lex-> - ref_pointer_array + i, - (char *)"", - (char *)"") - ) - ); + Item *col_item= new Item_cond_or(item_eq, item_isnull); + if (!abort_on_null && left_expr->el(i)->maybe_null) + { + if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + } + having_item= and_items(having_item, col_item); + + Item *item_nnull_test= + new Item_is_not_null_test(this, + new Item_ref(&select_lex->context, + select_lex-> + ref_pointer_array + i, + (char *)"", + (char *)"")); + if (!abort_on_null && left_expr->el(i)->maybe_null) + { + if (!(item_nnull_test= + new Item_func_trig_cond(item_nnull_test, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + } + item_having_part2= and_items(item_having_part2, item_nnull_test); item_having_part2->top_level_item(); } having_item= and_items(having_item, item_having_part2); @@ -1264,18 +1286,15 @@ Item_in_subselect::row_value_transformer(JOIN *join) ); if (!abort_on_null) { - having_item= - and_items(having_item, - new - Item_is_not_null_test(this, - new - Item_ref(&select_lex->context, - select_lex-> - ref_pointer_array + i, - (char *)"", - (char *)"") - ) - ); + Item *having_col_item= + new Item_is_not_null_test(this, + new + Item_ref(&select_lex->context, + select_lex->ref_pointer_array + i, + (char *)"", + (char *)"")); + + item_isnull= new Item_func_isnull(new Item_direct_ref(&select_lex->context, @@ -1284,14 +1303,23 @@ Item_in_subselect::row_value_transformer(JOIN *join) (char *)"", (char *)"") ); - item= new Item_cond_or(item, item_isnull); + /* + TODO: why we create the above for cases where the right part + cant be NULL? + */ + if (left_expr->el(i)->maybe_null) + { + if (!(item= new Item_func_trig_cond(item, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + if (!(having_col_item= + new Item_func_trig_cond(having_col_item, get_cond_guard(i)))) + DBUG_RETURN(RES_ERROR); + } + having_item= and_items(having_item, having_col_item); } - where_item= and_items(where_item, item); } - if (where_item) - where_item= new Item_func_trig_cond(where_item, &enable_pushed_conds); /* AND can't be changed during fix_fields() we can assign select_lex->where here, and pass 0 as last @@ -1305,9 +1333,9 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (having_item) { bool res; - having_item= new Item_func_trig_cond(having_item, &enable_pushed_conds); - select_lex->having= join->having= and_items(join->having, having_item); + if (having_item == select_lex->having) + having_item->name= (char*)in_having_cond; select_lex->having->top_level_item(); /* AND can't be changed during fix_fields() @@ -1693,7 +1721,7 @@ int init_read_record_seq(JOIN_TAB *tab); int join_read_always_key_or_null(JOIN_TAB *tab); int join_read_next_same_or_null(READ_RECORD *info); -int subselect_single_select_engine::exec(bool full_scan) +int subselect_single_select_engine::exec() { DBUG_ENTER("subselect_single_select_engine::exec"); char const *save_where= thd->where; @@ -1731,9 +1759,13 @@ int subselect_single_select_engine::exec(bool full_scan) if (!executed) { item->reset_value_registration(); - if (full_scan) + bool have_changed_access= FALSE; + JOIN_TAB *changed_tabs[MAX_TABLES]; + JOIN_TAB **last_changed_tab= changed_tabs; + if (item->have_guarded_conds()) { /* + For at least one of the pushed predicates the following is true: We should not apply optimizations based on the condition that was pushed down into the subquery. Those optimizations are ref[_or_null] acceses. Change them to be full table scans. @@ -1741,32 +1773,36 @@ int subselect_single_select_engine::exec(bool full_scan) for (uint i=join->const_tables ; i < join->tables ; i++) { JOIN_TAB *tab=join->join_tab+i; - if (tab->keyuse && tab->keyuse->outer_ref) + if (tab && tab->keyuse) { - tab->read_first_record= init_read_record_seq; - tab->read_record.record= tab->table->record[0]; - tab->read_record.thd= join->thd; - tab->read_record.ref_length= tab->table->file->ref_length; + for (uint i= 0; i < tab->ref.key_parts; i++) + { + bool *cond_guard= tab->ref.cond_guards[i]; + if (cond_guard && !*cond_guard) + { + /* Change the access method to full table scan */ + tab->read_first_record= init_read_record_seq; + tab->read_record.record= tab->table->record[0]; + tab->read_record.thd= join->thd; + tab->read_record.ref_length= tab->table->file->ref_length; + *(last_changed_tab++)= tab; + break; + } + } } } } join->exec(); - if (full_scan) + /* Enable the optimizations back */ + for (JOIN_TAB **ptab= changed_tabs; ptab != last_changed_tab; ptab++) { - /* Enable the optimizations back */ - for (uint i=join->const_tables ; i < join->tables ; i++) - { - JOIN_TAB *tab=join->join_tab+i; - if (tab->keyuse && tab->keyuse->outer_ref) - { - tab->read_record.record= 0; - tab->read_record.ref_length= 0; - tab->read_first_record= join_read_always_key_or_null; - tab->read_record.read_record= join_read_next_same_or_null; - } - } + JOIN_TAB *tab= *ptab; + tab->read_record.record= 0; + tab->read_record.ref_length= 0; + tab->read_first_record= join_read_always_key_or_null; + tab->read_record.read_record= join_read_next_same_or_null; } executed= 1; thd->where= save_where; @@ -1778,13 +1814,9 @@ int subselect_single_select_engine::exec(bool full_scan) DBUG_RETURN(0); } -int subselect_union_engine::exec(bool full_scan) +int subselect_union_engine::exec() { char const *save_where= thd->where; - /* - Ignore the full_scan parameter: the pushed down predicates are only used - for filtering, and the caller has disabled them if necessary. - */ int res= unit->exec(); thd->where= save_where; return res; @@ -1792,7 +1824,7 @@ int subselect_union_engine::exec(bool full_scan) /* - Search for at least on row satisfying select condition + Search for at least one row satisfying select condition SYNOPSIS subselect_uniquesubquery_engine::scan_table() @@ -1801,8 +1833,8 @@ int subselect_union_engine::exec(bool full_scan) Scan the table using sequential access until we find at least one row satisfying select condition. - The result of this function (info about whether a row was found) is - stored in this->empty_result_set. + The caller must set this->empty_result_set=FALSE before calling this + function. This function will set it to TRUE if it finds a matching row. RETURN FALSE - OK @@ -1814,7 +1846,6 @@ int subselect_uniquesubquery_engine::scan_table() int error; TABLE *table= tab->table; DBUG_ENTER("subselect_uniquesubquery_engine::scan_table"); - empty_result_set= TRUE; if (table->file->inited) table->file->ha_index_end(); @@ -1907,10 +1938,13 @@ bool subselect_uniquesubquery_engine::copy_ref_key() - FALSE otherwise. In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE) - the caller doesn't distinguish between NULL and FALSE result and we just + the caller doesn't distinguish between NULL and FALSE result and we just return FALSE. - Otherwise we make a full table scan to see if there is at least one matching row. - + Otherwise we make a full table scan to see if there is at least one + matching row. + + The result of this function (info about whether a row was found) is + stored in this->empty_result_set. NOTE RETURN @@ -1918,11 +1952,12 @@ bool subselect_uniquesubquery_engine::copy_ref_key() TRUE - an error occured while scanning */ -int subselect_uniquesubquery_engine::exec(bool full_scan) +int subselect_uniquesubquery_engine::exec() { DBUG_ENTER("subselect_uniquesubquery_engine::exec"); int error; TABLE *table= tab->table; + empty_result_set= TRUE; /* TODO: change to use of 'full_scan' here? */ if (copy_ref_key()) @@ -1943,9 +1978,13 @@ int subselect_uniquesubquery_engine::exec(bool full_scan) { error= 0; table->null_row= 0; - ((Item_in_subselect *) item)->value= (!table->status && - (!cond || cond->val_int()) ? 1 : - 0); + if (!table->status && (!cond || cond->val_int())) + { + ((Item_in_subselect *) item)->value= 1; + empty_result_set= FALSE; + } + else + ((Item_in_subselect *) item)->value= 0; } DBUG_RETURN(error != 0); @@ -2011,7 +2050,7 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine() 1 */ -int subselect_indexsubquery_engine::exec(bool full_scan) +int subselect_indexsubquery_engine::exec() { DBUG_ENTER("subselect_indexsubquery_engine::exec"); int error; @@ -2052,8 +2091,9 @@ int subselect_indexsubquery_engine::exec(bool full_scan) table->null_row= 0; if (!table->status) { - if (!cond || cond->val_int()) + if ((!cond || cond->val_int()) && (!having || having->val_int())) { + empty_result_set= FALSE; if (null_finding) ((Item_in_subselect *) item)->was_null= 1; else @@ -2196,11 +2236,16 @@ void subselect_indexsubquery_engine::print(String *str) str->append(key_info->name); if (check_null) str->append(STRING_WITH_LEN(" checking NULL")); - if (cond) + if (cond) { str->append(STRING_WITH_LEN(" where ")); cond->print(str); } + if (having) + { + str->append(STRING_WITH_LEN(" having ")); + having->print(str); + } str->append(')'); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 69ac78d859b..25a0326f8b6 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -94,7 +94,7 @@ public: return null_value; } bool fix_fields(THD *thd, Item **ref); - virtual bool exec(bool full_scan); + virtual bool exec(); virtual void fix_length_and_dec(); table_map used_tables() const; table_map not_null_tables() const { return 0; } @@ -104,6 +104,7 @@ public: Item *get_tmp_table_item(THD *thd); void update_used_tables(); void print(String *str); + virtual bool have_guarded_conds() { return FALSE; } bool change_engine(subselect_engine *eng) { old_engine= engine; @@ -249,13 +250,21 @@ protected: bool transformed; public: /* Used to trigger on/off conditions that were pushed down to subselect */ - bool enable_pushed_conds; + bool *pushed_cond_guards; + + bool *get_cond_guard(int i) + { + return pushed_cond_guards ? pushed_cond_guards + i : NULL; + } + void set_cond_guard_var(int i, bool v) { pushed_cond_guards[i]= v; } + bool have_guarded_conds() { return test(pushed_cond_guards); } + Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect() :Item_exists_subselect(), optimizer(0), abort_on_null(0), transformed(0), - enable_pushed_conds(TRUE), upper_item(0) + pushed_cond_guards(NULL), upper_item(0) {} subs_type substype() { return IN_SUBS; } @@ -340,23 +349,22 @@ public: SYNOPSIS exec() - full_scan TRUE - Pushed-down predicates are disabled, the engine - must disable made based on those predicates. - FALSE - Pushed-down predicates are in effect. + DESCRIPTION Execute the engine. The result of execution is subquery value that is either captured by previously set up select_result-based 'sink' or stored somewhere by the exec() method itself. - A required side effect: if full_scan==TRUE, subselect_engine->no_rows() - should return correct result. + A required side effect: If at least one pushed-down predicate is + disabled, subselect_engine->no_rows() must return correct result after + the exec() call. RETURN 0 - OK - 1 - Either an execution error, or the engine was be "changed", and + 1 - Either an execution error, or the engine was "changed", and the caller should call exec() again for the new engine. */ - virtual int exec(bool full_scan)= 0; + virtual int exec()= 0; virtual uint cols()= 0; /* return number of columns in select */ virtual uint8 uncacheable()= 0; /* query is uncacheable */ enum Item_result type() { return res_type; } @@ -391,7 +399,7 @@ public: void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); - int exec(bool full_scan); + int exec(); uint cols(); uint8 uncacheable(); void exclude(); @@ -415,7 +423,7 @@ public: void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); - int exec(bool full_scan); + int exec(); uint cols(); uint8 uncacheable(); void exclude(); @@ -429,11 +437,30 @@ public: struct st_join_table; + + +/* + A subquery execution engine that evaluates the subquery by doing one index + lookup in a unique index. + + This engine is used to resolve subqueries in forms + + outer_expr IN (SELECT tbl.unique_key FROM tbl WHERE subq_where) + + or, tuple-based: + + (oe1, .. oeN) IN (SELECT uniq_key_part1, ... uniq_key_partK + FROM tbl WHERE subqwhere) + + i.e. the subquery is a single table SELECT without GROUP BY, aggregate + functions, etc. +*/ + class subselect_uniquesubquery_engine: public subselect_engine { protected: st_join_table *tab; - Item *cond; + Item *cond; /* The WHERE condition of subselect */ /* TRUE<=> last execution produced empty set. Valid only when left expression is NULL. @@ -453,7 +480,7 @@ public: void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); - int exec(bool full_scan); + int exec(); uint cols() { return 1; } uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; } void exclude(); @@ -471,16 +498,47 @@ class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine { /* FALSE for 'ref', TRUE for 'ref-or-null'. */ bool check_null; + /* + The "having" clause. This clause (further reffered to as "artificial + having") was inserted by subquery transformation code. It contains + Item(s) that have a side-effect: they record whether the subquery has + produced a row with NULL certain components. We need to use it for cases + like + (oe1, oe2) IN (SELECT t.key, t.no_key FROM t1) + where we do index lookup on t.key=oe1 but need also to check if there + was a row such that t.no_key IS NULL. + + NOTE: This is currently here and not in the uniquesubquery_engine. Ideally + it should have been in uniquesubquery_engine in order to allow execution of + subqueries like + + (oe1, oe2) IN (SELECT primary_key, non_key_maybe_null_field FROM tbl) + + We could use uniquesubquery_engine for the first component and let + Item_is_not_null_test( non_key_maybe_null_field) to handle the second. + + However, subqueries like the above are currently not handled by index + lookup-based subquery engines, the engine applicability check misses + them: it doesn't switch the engine for case of artificial having and + [eq_]ref access (only for artifical having + ref_or_null or no having). + The above example subquery is handled as a full-blown SELECT with eq_ref + access to one table. + + Due to this limitation, the "artificial having" currently needs to be + checked by only in indexsubquery_engine. + */ + Item *having; public: // constructor can assign THD because it will be called after JOIN::prepare subselect_indexsubquery_engine(THD *thd, st_join_table *tab_arg, Item_subselect *subs, Item *where, - bool chk_null) + Item *having_arg, bool chk_null) :subselect_uniquesubquery_engine(thd, tab_arg, subs, where), - check_null(chk_null) + check_null(chk_null), + having(having_arg) {} - int exec(bool full_scan); + int exec(); void print (String *str); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 534d42e1c17..c4612433f3d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1197,7 +1197,7 @@ extern const char *command_name[]; extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword; extern const char **errmesg; /* Error messages */ extern const char *myisam_recover_options_str; -extern const char *in_left_expr_name, *in_additional_cond; +extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond; extern const char * const triggers_file_ext; extern const char * const trigname_file_ext; extern Eq_creator eq_creator; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 095b5095c36..6614a07ef3d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -449,10 +449,13 @@ char *mysqld_unix_port, *opt_mysql_tmpdir; const char **errmesg; /* Error messages */ const char *myisam_recover_options_str="OFF"; const char *myisam_stats_method_str="nulls_unequal"; + /* name of reference on left espression in rewritten IN subquery */ const char *in_left_expr_name= ""; /* name of additional condition */ const char *in_additional_cond= ""; +const char *in_having_cond= ""; + my_decimal decimal_zero; /* classes for comparation parsing/processing */ Eq_creator eq_creator; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index db119d527d9..54fdd1431e2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -469,7 +469,7 @@ public: void set_thd(THD *thd_arg) { thd= thd_arg; } friend void lex_start(THD *thd, uchar *buf, uint length); - friend int subselect_union_engine::exec(bool); + friend int subselect_union_engine::exec(); List *get_unit_column_types(); }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index abb949ae473..b6664abb299 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -513,72 +513,88 @@ err: /* - test if it is known for optimisation IN subquery + Remove the predicates pushed down into the subquery SYNOPSIS - JOIN::test_in_subselect() - where - pointer for variable in which conditions should be - stored if subquery is known + JOIN::remove_subq_pushed_predicates() + where IN Must be NULL + OUT The remaining WHERE condition, or NULL - RETURN - 1 - known - 0 - unknown + DESCRIPTION + Given that this join will be executed using (unique|index)_subquery, + without "checking NULL", remove the predicates that were pushed down + into the subquery. + + We can remove the equalities that will be guaranteed to be true by the + fact that subquery engine will be using index lookup. + + If the subquery compares scalar values, we can remove the condition that + was wrapped into trig_cond (it will be checked when needed by the subquery + engine) + + If the subquery compares row values, we need to keep the wrapped + equalities in the WHERE clause: when the left (outer) tuple has both NULL + and non-NULL values, we'll do a full table scan and will rely on the + equalities corresponding to non-NULL parts of left tuple to filter out + non-matching records. */ -bool JOIN::test_in_subselect(Item **where) +void JOIN::remove_subq_pushed_predicates(Item **where) { if (conds->type() == Item::FUNC_ITEM && ((Item_func *)this->conds)->functype() == Item_func::EQ_FUNC && ((Item_func *)conds)->arguments()[0]->type() == Item::REF_ITEM && ((Item_func *)conds)->arguments()[1]->type() == Item::FIELD_ITEM) { - join_tab->info= "Using index"; *where= 0; - return 1; + return; } if (conds->type() == Item::COND_ITEM && ((class Item_func *)this->conds)->functype() == Item_func::COND_AND_FUNC) { - if ((*where= remove_additional_cond(conds))) - join_tab->info= "Using index; Using where"; - else - join_tab->info= "Using index"; - return 1; + *where= remove_additional_cond(conds); } - return 0; } /* - Check if the passed HAVING clause is a clause added by subquery optimizer + Index lookup-based subquery: save some flags for EXPLAIN output SYNOPSIS - is_having_subq_predicates() - having Having clause + save_index_subquery_explain_info() + join_tab Subquery's join tab (there is only one as index lookup is + only used for subqueries that are single-table SELECTs) + where Subquery's WHERE clause - RETURN - TRUE The passed HAVING clause was added by the subquery optimizer - FALSE Otherwise + DESCRIPTION + For index lookup-based subquery (i.e. one executed with + subselect_uniquesubquery_engine or subselect_indexsubquery_engine), + check its EXPLAIN output row should contain + "Using index" (TAB_INFO_FULL_SCAN_ON_NULL) + "Using Where" (TAB_INFO_USING_WHERE) + "Full scan on NULL key" (TAB_INFO_FULL_SCAN_ON_NULL) + and set appropriate flags in join_tab->packed_info. */ -bool is_having_subq_predicates(Item *having) +static void save_index_subquery_explain_info(JOIN_TAB *join_tab, Item* where) { - if (having->type() == Item::FUNC_ITEM) + join_tab->packed_info= TAB_INFO_HAVE_VALUE; + if (join_tab->table->used_keys.is_set(join_tab->ref.key)) + join_tab->packed_info |= TAB_INFO_USING_INDEX; + if (where) + join_tab->packed_info |= TAB_INFO_USING_WHERE; + for (uint i = 0; i < join_tab->ref.key_parts; i++) { - if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC) - return TRUE; - if (((Item_func *) having)->functype() == Item_func::TRIG_COND_FUNC) + if (join_tab->ref.cond_guards[i]) { - having= ((Item_func*)having)->arguments()[0]; - if (((Item_func *) having)->functype() == Item_func::ISNOTNULLTEST_FUNC) - return TRUE; + join_tab->packed_info |= TAB_INFO_FULL_SCAN_ON_NULL; + break; } - return TRUE; } - return FALSE; } + /* global select optimisation. return 0 - success @@ -1017,51 +1033,47 @@ JOIN::optimize() if (join_tab[0].type == JT_EQ_REF && join_tab[0].ref.items[0]->name == in_left_expr_name) { - if (test_in_subselect(&where)) - { - join_tab[0].type= JT_UNIQUE_SUBQUERY; - error= 0; - DBUG_RETURN(unit->item-> - change_engine(new - subselect_uniquesubquery_engine(thd, - join_tab, - unit->item, - where))); - } + remove_subq_pushed_predicates(&where); + save_index_subquery_explain_info(join_tab, where); + join_tab[0].type= JT_UNIQUE_SUBQUERY; + error= 0; + DBUG_RETURN(unit->item-> + change_engine(new + subselect_uniquesubquery_engine(thd, + join_tab, + unit->item, + where))); } else if (join_tab[0].type == JT_REF && join_tab[0].ref.items[0]->name == in_left_expr_name) { - if (test_in_subselect(&where)) - { - join_tab[0].type= JT_INDEX_SUBQUERY; - error= 0; - DBUG_RETURN(unit->item-> - change_engine(new - subselect_indexsubquery_engine(thd, - join_tab, - unit->item, - where, - 0))); - } + remove_subq_pushed_predicates(&where); + save_index_subquery_explain_info(join_tab, where); + join_tab[0].type= JT_INDEX_SUBQUERY; + error= 0; + DBUG_RETURN(unit->item-> + change_engine(new + subselect_indexsubquery_engine(thd, + join_tab, + unit->item, + where, + NULL, + 0))); } } else if (join_tab[0].type == JT_REF_OR_NULL && join_tab[0].ref.items[0]->name == in_left_expr_name && - is_having_subq_predicates(having)) + having->name == in_having_cond) { join_tab[0].type= JT_INDEX_SUBQUERY; error= 0; - - if ((conds= remove_additional_cond(conds))) - join_tab->info= "Using index; Using where"; - else - join_tab->info= "Using index"; - + conds= remove_additional_cond(conds); + save_index_subquery_explain_info(join_tab, conds); DBUG_RETURN(unit->item-> change_engine(new subselect_indexsubquery_engine(thd, join_tab, unit->item, conds, + having, 1))); } @@ -2557,9 +2569,7 @@ typedef struct key_field_t { // Used when finding key fields when val IS NULL. */ bool null_rejecting; - - /* TRUE<=> This ref access is an outer subquery reference access */ - bool outer_ref; + bool *cond_guard; /* See KEYUSE::cond_guard */ } KEY_FIELD; /* Values in optimize */ @@ -2858,7 +2868,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, cond->functype() == Item_func::MULT_EQUAL_FUNC) && ((*value)->type() == Item::FIELD_ITEM) && ((Item_field*)*value)->field->maybe_null()); - (*key_fields)->outer_ref= FALSE; + (*key_fields)->cond_guard= NULL; (*key_fields)++; } @@ -2955,8 +2965,9 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, } /* - Subquery optimization: check if the encountered condition is one - added by condition push down into subquery. + Subquery optimization: Conditions that are pushed down into subqueries + are wrapped into Item_func_trig_cond. We process the wrapped condition + but need to set cond_guard for KEYUSE elements generated from it. */ { if (cond->type() == Item::FUNC_ITEM && @@ -2973,7 +2984,7 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, sargables); // Indicate that this ref access candidate is for subquery lookup: for (; save != *key_fields; save++) - save->outer_ref= TRUE; + save->cond_guard= ((Item_func_trig_cond*)cond)->get_trig_var(); } return; } @@ -3153,7 +3164,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) keyuse.used_tables=key_field->val->used_tables(); keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; keyuse.null_rejecting= key_field->null_rejecting; - keyuse.outer_ref= key_field->outer_ref; + keyuse.cond_guard= key_field->cond_guard; VOID(insert_dynamic(keyuse_array,(gptr) &keyuse)); } } @@ -4992,7 +5003,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) || !(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) * (keyparts+1)))) || - !(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts))) + !(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts)) || + !(j->ref.cond_guards= (bool**) thd->alloc(sizeof(uint*)*keyparts))) { DBUG_RETURN(TRUE); } @@ -5007,6 +5019,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, if (ftkey) { j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); + /* Predicates pushed down into subquery can't be used FT access */ + j->ref.cond_guards[0]= NULL; if (keyuse->used_tables) DBUG_RETURN(TRUE); // not supported yet. SerG @@ -5023,6 +5037,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, uint maybe_null= test(keyinfo->key_part[i].null_bit); j->ref.items[i]=keyuse->val; // Save for cond removal + j->ref.cond_guards[i]= keyuse->cond_guard; if (keyuse->null_rejecting) j->ref.null_rejecting |= 1 << i; keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables; @@ -7653,7 +7668,7 @@ change_cond_ref_to_const(THD *thd, I_List *save_list, SYNOPSIS remove_additional_cond() - conds - condition for processing + conds Condition for processing RETURN VALUES new conditions @@ -10911,7 +10926,9 @@ int rr_sequential(READ_RECORD *info); int init_read_record_seq(JOIN_TAB *tab) { tab->read_record.read_record= rr_sequential; - return tab->read_record.file->ha_rnd_init(1); + if (tab->read_record.file->ha_rnd_init(1)) + return 1; + return (*tab->read_record.read_record)(&tab->read_record); } static int @@ -14819,6 +14836,24 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (tab->info) item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs)); + else if (tab->packed_info & TAB_INFO_HAVE_VALUE) + { + if (tab->packed_info & TAB_INFO_USING_INDEX) + extra.append(STRING_WITH_LEN("; Using index")); + if (tab->packed_info & TAB_INFO_USING_WHERE) + extra.append(STRING_WITH_LEN("; Using where")); + if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL) + extra.append(STRING_WITH_LEN("; Full scan on NULL key")); + /* Skip initial "; "*/ + const char *str= extra.ptr(); + uint32 len= extra.length(); + if (len) + { + str += 2; + len -= 2; + } + item_list.push_back(new Item_string(str, len, cs)); + } else { if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || @@ -14877,6 +14912,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } if (distinct & test_all_bits(used_tables,thd->used_tables)) extra.append(STRING_WITH_LEN("; Distinct")); + + for (uint part= 0; part < tab->ref.key_parts; part++) + { + if (tab->ref.cond_guards[part]) + { + extra.append(STRING_WITH_LEN("; Full scan on NULL key")); + break; + } + } /* Skip initial "; "*/ const char *str= extra.ptr(); diff --git a/sql/sql_select.h b/sql/sql_select.h index ccdd66d5b95..a17d7fcb362 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -35,8 +35,17 @@ typedef struct keyuse_t { satisfied if val has NULL 'value'. */ bool null_rejecting; - /* TRUE<=> This ref access is an outer subquery reference access */ - bool outer_ref; + /* + !NULL - This KEYUSE was created from an equality that was wrapped into + an Item_func_trig_cond. This means the equality (and validity of + this KEYUSE element) can be turned on and off. The on/off state + is indicted by the pointed value: + *cond_guard == TRUE <=> equality condition is on + *cond_guard == FALSE <=> equality condition is off + + NULL - Otherwise (the source equality can't be turned off) + */ + bool *cond_guard; } KEYUSE; class store_key; @@ -51,6 +60,18 @@ typedef struct st_table_ref byte *key_buff2; // key_buff+key_length store_key **key_copy; // Item **items; // val()'s for each keypart + /* + Array of pointers to trigger variables. Some/all of the pointers may be + NULL. The ref access can be used iff + + for each used key part i, (!cond_guards[i] || *cond_guards[i]) + + This array is used by subquery code. The subquery code may inject + triggered conditions, i.e. conditions that can be 'switched off'. A ref + access created from such condition is not valid when at least one of the + underlying conditions is switched off (see subquery code for more details) + */ + bool **cond_guards; /* (null_rejecting & (1< Date: Fri, 12 Jan 2007 13:43:25 -0800 Subject: [PATCH 26/73] Fixed bug #25398: crash in a trigger when using trigger fields in a select list. The objects of the Item_trigger_field class inherited the implementations of the methods copy_or_same, get_tmp_table_item and get_tmp_table_field from the class Item_field while they rather should have used the default implementations defined for the base class Item. It could cause catastrophic problems for triggers that used SELECTs with select list containing trigger fields such as NEW. under DISTINCT. --- mysql-test/r/trigger.result | 32 ++++++++++++++++++++++++++ mysql-test/t/trigger.test | 46 +++++++++++++++++++++++++++++++++++++ sql/item.h | 3 +++ 3 files changed, 81 insertions(+) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 9f34f60eb1a..3d40a2d05df 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1278,4 +1278,36 @@ a b 2 b 3 c drop table t1; +CREATE TABLE t1 ( +id int NOT NULL DEFAULT '0', +a varchar(10) NOT NULL, +b varchar(10), +c varchar(10), +d timestamp NOT NULL, +PRIMARY KEY (id, a) +); +CREATE TABLE t2 ( +fubar_id int unsigned NOT NULL DEFAULT '0', +last_change_time datetime NOT NULL DEFAULT '0000-00-00 00:00:00', +PRIMARY KEY (fubar_id) +); +CREATE TRIGGER fubar_change +AFTER UPDATE ON t1 +FOR EACH ROW +BEGIN +INSERT INTO t2 (fubar_id, last_change_time) +SELECT DISTINCT NEW.id AS fubar_id, NOW() AS last_change_time +FROM t1 WHERE (id = NEW.id) AND (OLD.c != NEW.c) +ON DUPLICATE KEY UPDATE +last_change_time = +IF((fubar_id = NEW.id)AND(OLD.c != NEW.c),NOW(),last_change_time); +END +| +INSERT INTO t1 (id,a, b,c,d) VALUES +(1,'a','b','c',now()),(2,'a','b','c',now()); +UPDATE t1 SET c='Bang!' WHERE id=1; +SELECT fubar_id FROM t2; +fubar_id +1 +DROP TABLE t1,t2; End of 5.0 tests diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index ae01a4b2c3d..ea569f4ce09 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -1548,4 +1548,50 @@ select * from t1; drop table t1; +# +# Bug#25398: crash when a trigger contains a SELECT with +# trigger fields in the select list under DISTINCT +# + +CREATE TABLE t1 ( + id int NOT NULL DEFAULT '0', + a varchar(10) NOT NULL, + b varchar(10), + c varchar(10), + d timestamp NOT NULL, + PRIMARY KEY (id, a) +); + +CREATE TABLE t2 ( + fubar_id int unsigned NOT NULL DEFAULT '0', + last_change_time datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (fubar_id) +); + +DELIMITER |; + +CREATE TRIGGER fubar_change + AFTER UPDATE ON t1 + FOR EACH ROW + BEGIN + INSERT INTO t2 (fubar_id, last_change_time) + SELECT DISTINCT NEW.id AS fubar_id, NOW() AS last_change_time + FROM t1 WHERE (id = NEW.id) AND (OLD.c != NEW.c) + ON DUPLICATE KEY UPDATE + last_change_time = + IF((fubar_id = NEW.id)AND(OLD.c != NEW.c),NOW(),last_change_time); + END +| + +DELIMITER ;| + +INSERT INTO t1 (id,a, b,c,d) VALUES + (1,'a','b','c',now()),(2,'a','b','c',now()); + +UPDATE t1 SET c='Bang!' WHERE id=1; + +SELECT fubar_id FROM t2; + +DROP TABLE t1,t2; + --echo End of 5.0 tests diff --git a/sql/item.h b/sql/item.h index 6065e385a6c..62d6127f10b 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2269,6 +2269,9 @@ public: bool fix_fields(THD *, Item **); void print(String *str); table_map used_tables() const { return (table_map)0L; } + Field *get_tmp_table_field() { return 0; } + Item *copy_or_same(THD *thd) { return this; } + Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); } void cleanup(); private: From 34eea49eb7efa21ea2d4b8daffa370b5bf701f6c Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Sat, 13 Jan 2007 10:49:26 -0800 Subject: [PATCH 27/73] Fixed bug #24776: an assertion abort in handler::ha_index_init for queries using 'range checked for each record'. The problem was fixed in 5.0 by the patch for bug 12291. This patch down-ported the corresponding code from 5.0 into QUICK_SELECT::init() and added a new test case. --- mysql-test/r/range.result | 43 +++++++++++++++++++++++++++++++++++++++ mysql-test/t/range.test | 43 +++++++++++++++++++++++++++++++++++++++ sql/opt_range.h | 2 ++ 3 files changed, 88 insertions(+) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index f25d94f8066..2125f237d0e 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -658,4 +658,47 @@ select a from t1 where a > 'x'; a xx drop table t1; +CREATE TABLE t1 ( +OXID varchar(32) COLLATE latin1_german2_ci NOT NULL DEFAULT '', +OXPARENTID varchar(32) COLLATE latin1_german2_ci NOT NULL DEFAULT 'oxrootid', +OXLEFT int NOT NULL DEFAULT '0', +OXRIGHT int NOT NULL DEFAULT '0', +OXROOTID varchar(32) COLLATE latin1_german2_ci NOT NULL DEFAULT '', +PRIMARY KEY (OXID), +KEY OXNID (OXID), +KEY OXLEFT (OXLEFT), +KEY OXRIGHT (OXRIGHT), +KEY OXROOTID (OXROOTID) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german2_ci; +INSERT INTO t1 VALUES +('d8c4177d09f8b11f5.52725521','oxrootid',1,40,'d8c4177d09f8b11f5.52725521'), +('d8c4177d151affab2.81582770','d8c4177d09f8b11f5.52725521',2,3, +'d8c4177d09f8b11f5.52725521'), +('d8c4177d206a333d2.74422679','d8c4177d09f8b11f5.52725521',4,5, +'d8c4177d09f8b11f5.52725521'), +('d8c4177d225791924.30714720','d8c4177d09f8b11f5.52725521',6,7, +'d8c4177d09f8b11f5.52725521'), +('d8c4177d2380fc201.39666693','d8c4177d09f8b11f5.52725521',8,9, +'d8c4177d09f8b11f5.52725521'), +('d8c4177d24ccef970.14957924','d8c4177d09f8b11f5.52725521',10,11, +'d8c4177d09f8b11f5.52725521'); +EXPLAIN +SELECT s.oxid FROM t1 v, t1 s +WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND +v.oxrootid ='d8c4177d09f8b11f5.52725521' AND +s.oxleft > v.oxleft AND s.oxleft < v.oxright; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE v ref OXLEFT,OXRIGHT,OXROOTID OXROOTID 32 const 5 Using where +1 SIMPLE s ALL OXLEFT NULL NULL NULL 5 Range checked for each record (index map: 0x4) +SELECT s.oxid FROM t1 v, t1 s +WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND +v.oxrootid ='d8c4177d09f8b11f5.52725521' AND +s.oxleft > v.oxleft AND s.oxleft < v.oxright; +oxid +d8c4177d151affab2.81582770 +d8c4177d206a333d2.74422679 +d8c4177d225791924.30714720 +d8c4177d2380fc201.39666693 +d8c4177d24ccef970.14957924 +DROP TABLE t1; End of 4.1 tests diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 245178d7d4a..16cbcd754f6 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -520,4 +520,47 @@ explain select a from t1 where a > 'x'; select a from t1 where a > 'x'; drop table t1; +# +# Bug #24776: assertion abort for 'range checked for each record' +# + +CREATE TABLE t1 ( + OXID varchar(32) COLLATE latin1_german2_ci NOT NULL DEFAULT '', + OXPARENTID varchar(32) COLLATE latin1_german2_ci NOT NULL DEFAULT 'oxrootid', + OXLEFT int NOT NULL DEFAULT '0', + OXRIGHT int NOT NULL DEFAULT '0', + OXROOTID varchar(32) COLLATE latin1_german2_ci NOT NULL DEFAULT '', + PRIMARY KEY (OXID), + KEY OXNID (OXID), + KEY OXLEFT (OXLEFT), + KEY OXRIGHT (OXRIGHT), + KEY OXROOTID (OXROOTID) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german2_ci; + +INSERT INTO t1 VALUES +('d8c4177d09f8b11f5.52725521','oxrootid',1,40,'d8c4177d09f8b11f5.52725521'), +('d8c4177d151affab2.81582770','d8c4177d09f8b11f5.52725521',2,3, + 'd8c4177d09f8b11f5.52725521'), +('d8c4177d206a333d2.74422679','d8c4177d09f8b11f5.52725521',4,5, + 'd8c4177d09f8b11f5.52725521'), +('d8c4177d225791924.30714720','d8c4177d09f8b11f5.52725521',6,7, + 'd8c4177d09f8b11f5.52725521'), +('d8c4177d2380fc201.39666693','d8c4177d09f8b11f5.52725521',8,9, + 'd8c4177d09f8b11f5.52725521'), +('d8c4177d24ccef970.14957924','d8c4177d09f8b11f5.52725521',10,11, + 'd8c4177d09f8b11f5.52725521'); + +EXPLAIN +SELECT s.oxid FROM t1 v, t1 s + WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND + v.oxrootid ='d8c4177d09f8b11f5.52725521' AND + s.oxleft > v.oxleft AND s.oxleft < v.oxright; + +SELECT s.oxid FROM t1 v, t1 s + WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND + v.oxrootid ='d8c4177d09f8b11f5.52725521' AND + s.oxleft > v.oxleft AND s.oxleft < v.oxright; + +DROP TABLE t1; + --echo End of 4.1 tests diff --git a/sql/opt_range.h b/sql/opt_range.h index 4d425604921..4b20d1fe0fe 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -90,6 +90,8 @@ public: int init() { key_part_info= head->key_info[index].key_part; + if (file->inited != handler::NONE) + file->ha_index_or_rnd_end(); return error=file->ha_index_init(index); } virtual int get_next(); From 134e94931793d7b5ff891d0e684d6b126bf2a39d Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 15 Jan 2007 19:15:52 +0200 Subject: [PATCH 28/73] BUG#20420: optimizer reports wrong keys on left join with IN The optimizer needs to evaluate whether predicates are better evaluated using an index. IN is one such predicate. To qualify an IN predicate must involve a field of the index on the left and constant arguments on the right. However whether an expression is a constant can be determined only by knowing the preceding tables in the join order. Assuming that only IN predicates with expressions on the right that are constant for the whole query qualify limits the scope of possible optimizations of the IN predicate (more specifically it doesn't allow the "Range checked for each record" optimization for such an IN predicate. Fixed by not pre-determining the optimizability of the IN predicate in the case when all right IN operands are not SQL constant expressions --- mysql-test/r/func_in.result | 43 +++++++++++++++++++++++++++++++++++++ mysql-test/t/func_in.test | 36 +++++++++++++++++++++++++++++++ sql/item_cmpfunc.h | 6 +++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index a172d04d880..98e39917f04 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -351,4 +351,47 @@ some_id 1 2 drop table t1; +CREATE TABLE t1 (a int, b int, PRIMARY KEY (a)); +INSERT INTO t1 VALUES (1,1),(2,1),(3,1),(4,1),(5,1),(6,1); +CREATE TABLE t2 (a int, b int, PRIMARY KEY (a)); +INSERT INTO t2 VALUES (3,2),(4,2); +CREATE TABLE t3 (a int PRIMARY KEY); +INSERT INTO t3 VALUES (1),(2),(3),(4); +CREATE TABLE t4 (a int PRIMARY KEY); +INSERT INTO t4 VALUES (1),(2); +EXPLAIN SELECT STRAIGHT_JOIN * FROM t3 +JOIN t1 ON t3.a=t1.a +JOIN t2 ON t3.a=t2.a +JOIN t4 WHERE t4.a IN (t1.b, t2.b); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t3 index PRIMARY PRIMARY 4 NULL 4 Using index +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 +1 SIMPLE t4 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) +SELECT STRAIGHT_JOIN * FROM t3 +JOIN t1 ON t3.a=t1.a +JOIN t2 ON t3.a=t2.a +JOIN t4 WHERE t4.a IN (t1.b, t2.b); +a a b a b a +3 3 1 3 2 1 +3 3 1 3 2 2 +4 4 1 4 2 1 +4 4 1 4 2 2 +EXPLAIN SELECT STRAIGHT_JOIN +(SELECT SUM(t4.a) FROM t4 WHERE t4.a IN (t1.b, t2.b)) +FROM t3, t1, t2 +WHERE t3.a=t1.a AND t3.a=t2.a; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 index PRIMARY PRIMARY 4 NULL 4 Using index +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 +1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 +2 DEPENDENT SUBQUERY t4 index NULL PRIMARY 4 NULL 2 Using where; Using index +SELECT STRAIGHT_JOIN +(SELECT SUM(t4.a) FROM t4 WHERE t4.a IN (t1.b, t2.b)) +FROM t3, t1, t2 +WHERE t3.a=t1.a AND t3.a=t2.a; +(SELECT SUM(t4.a) FROM t4 WHERE t4.a IN (t1.b, t2.b)) +3 +3 +DROP TABLE t1,t2,t3,t4; End of 5.0 tests diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index d48606ac6e6..8c89aa1e729 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -254,5 +254,41 @@ select some_id from t1 where some_id not in(-4,-1,-4); select some_id from t1 where some_id not in(-4,-1,3423534,2342342); drop table t1; +# +# BUG#20420: optimizer reports wrong keys on left join with IN +# +CREATE TABLE t1 (a int, b int, PRIMARY KEY (a)); +INSERT INTO t1 VALUES (1,1),(2,1),(3,1),(4,1),(5,1),(6,1); + +CREATE TABLE t2 (a int, b int, PRIMARY KEY (a)); +INSERT INTO t2 VALUES (3,2),(4,2); + +CREATE TABLE t3 (a int PRIMARY KEY); +INSERT INTO t3 VALUES (1),(2),(3),(4); + +CREATE TABLE t4 (a int PRIMARY KEY); +INSERT INTO t4 VALUES (1),(2); + +EXPLAIN SELECT STRAIGHT_JOIN * FROM t3 + JOIN t1 ON t3.a=t1.a + JOIN t2 ON t3.a=t2.a + JOIN t4 WHERE t4.a IN (t1.b, t2.b); + +SELECT STRAIGHT_JOIN * FROM t3 + JOIN t1 ON t3.a=t1.a + JOIN t2 ON t3.a=t2.a + JOIN t4 WHERE t4.a IN (t1.b, t2.b); + +EXPLAIN SELECT STRAIGHT_JOIN + (SELECT SUM(t4.a) FROM t4 WHERE t4.a IN (t1.b, t2.b)) + FROM t3, t1, t2 + WHERE t3.a=t1.a AND t3.a=t2.a; + +SELECT STRAIGHT_JOIN + (SELECT SUM(t4.a) FROM t4 WHERE t4.a IN (t1.b, t2.b)) + FROM t3, t1, t2 + WHERE t3.a=t1.a AND t3.a=t2.a; + +DROP TABLE t1,t2,t3,t4; --echo End of 5.0 tests diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 454aed01aff..5149ab4cfbe 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -965,6 +965,10 @@ class Item_func_in :public Item_func_opt_neg { public: Item_result cmp_type; + /* + an array of values when the right hand arguments of IN + are all SQL constant and there are no nulls + */ in_vector *array; cmp_item *in_item; bool have_null; @@ -990,7 +994,7 @@ public: DBUG_VOID_RETURN; } optimize_type select_optimize() const - { return array ? OPTIMIZE_KEY : OPTIMIZE_NONE; } + { return OPTIMIZE_KEY; } void print(String *str); enum Functype functype() const { return IN_FUNC; } const char *func_name() const { return " IN "; } From 53a4949d3bb899390845621fee94eb273e139904 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Mon, 15 Jan 2007 22:40:39 +0300 Subject: [PATCH 29/73] sql_select.cc: Fix for crashes on 64bit platforms after fixing bug#23417. --- sql/sql_select.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index fb4e2c3ab30..93b29c05fd5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13416,10 +13416,12 @@ count_field_types(TMP_TABLE_PARAM *param, List &fields, param->quick_group=1; while ((field=li++)) { - Item::Type type=field->real_item()->type(); - if (type == Item::FIELD_ITEM) + Item::Type type=field->type(); + Item::Type real_type= field->real_item()->type(); + if (type == Item::FIELD_ITEM || (real_type == Item::FIELD_ITEM && + (type != Item::REF_ITEM || !((Item_ref *) field)->depended_from))) param->field_count++; - else if (type == Item::SUM_FUNC_ITEM) + else if (real_type == Item::SUM_FUNC_ITEM) { if (! field->const_item()) { From f3b3f1ef73d32d75926266989702dcacdc5a8f73 Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Mon, 15 Jan 2007 14:01:36 -0800 Subject: [PATCH 30/73] Adjusted results after merge 4.1 -> 5.0 for the patch fixing bug 24776. --- mysql-test/r/range.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 6037824ea16..f84bb2788e1 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -704,7 +704,7 @@ WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND v.oxrootid ='d8c4177d09f8b11f5.52725521' AND s.oxleft > v.oxleft AND s.oxleft < v.oxright; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE v ref OXLEFT,OXRIGHT,OXROOTID OXROOTID 32 const 5 Using where +1 SIMPLE v ref OXLEFT,OXRIGHT,OXROOTID OXROOTID 34 const 5 Using where 1 SIMPLE s ALL OXLEFT NULL NULL NULL 5 Range checked for each record (index map: 0x4) SELECT s.oxid FROM t1 v, t1 s WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND From 25c53269ab1802041413758b6488ebd3967f7dd4 Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Tue, 16 Jan 2007 12:46:48 +0100 Subject: [PATCH 31/73] configure.in : Fix a dependency problem for "extra/" which shows up only in RPM builds. --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 493fe8f174f..e3bb541c9ec 100644 --- a/configure.in +++ b/configure.in @@ -2516,9 +2516,9 @@ linked_client_targets="linked_libmysql_sources" if test "$THREAD_SAFE_CLIENT" = "no" then - sql_client_dirs="strings regex mysys extra libmysql client" + sql_client_dirs="strings regex mysys dbug extra libmysql client" else - sql_client_dirs="strings regex mysys extra libmysql libmysql_r client" + sql_client_dirs="strings regex mysys dbug extra libmysql libmysql_r client" linked_client_targets="$linked_client_targets linked_libmysql_r_sources" AC_CONFIG_FILES(libmysql_r/Makefile) AC_DEFINE([THREAD_SAFE_CLIENT], [1], [Should be client be thread safe]) From c606e30f3555164de4b8bec3de14685efb604c7b Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/june.mysql.com" <> Date: Tue, 16 Jan 2007 18:05:37 +0400 Subject: [PATCH 32/73] BUG#24855 - Crash mysqld 4.1.21 with corrupted tables Accessing fixed record format table with crashed key definition results in server/myisamchk segmentation fault. This is fixed by refusing to open such tables. Affects MyISAM only. No test case, since it requires crashed table. --- myisam/mi_open.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 4efe6f42d5e..047686278b4 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -315,7 +315,13 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) for (j=0 ; j < share->keyinfo[i].keysegs; j++,pos++) { disk_pos=mi_keyseg_read(disk_pos, pos); - + if (pos->flag & HA_BLOB_PART && + ! (share->options & (HA_OPTION_COMPRESS_RECORD | + HA_OPTION_PACK_RECORD))) + { + my_errno= HA_ERR_CRASHED; + goto err; + } if (pos->type == HA_KEYTYPE_TEXT || pos->type == HA_KEYTYPE_VARTEXT) { if (!pos->language) From 88bc918be6404e35ba4283861f6409fb801b3a4e Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Tue, 16 Jan 2007 20:01:17 +0100 Subject: [PATCH 33/73] myisam/mi_packrec.c Fix a compile error: Macro "DBUG_ENTER" must not follow an executable statement. --- myisam/mi_packrec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index 37614cc1e1f..d45d6f8b16f 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -592,8 +592,8 @@ static uint copy_decode_table(uint16 *to_pos, uint offset, uint16 *decode_table) { uint prev_offset; - prev_offset= offset; DBUG_ENTER("copy_decode_table"); + prev_offset= offset; /* Descent on the left side. */ if (!(*decode_table & IS_CHAR)) From 1d6fe0ffaa37fc115f6c0ac28d5ed497879eed72 Mon Sep 17 00:00:00 2001 From: "tulin/mysqldev@mysql.com/production.mysql.com" <> Date: Wed, 17 Jan 2007 10:06:50 +0100 Subject: [PATCH 34/73] ha_ndbcluster.cc: Bug #25668 ndb: mysqld may core if cluster disconnected -- pTrans may be NULL, remove not needed usage of handler::ndb_err --- sql/ha_ndbcluster.cc | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 0f580c833a9..c40404f0219 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -6109,23 +6109,7 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb, DBUG_RETURN(0); retry: - if(report_error) - { - if (file) - { - reterr= file->ndb_err(pTrans); - } - else - { - const NdbError& tmp= error; - ERR_PRINT(tmp); - reterr= ndb_to_mysql_error(&tmp); - } - } - else - reterr= error.code; - if (pTrans) { ndb->closeTransaction(pTrans); pTrans= NULL; @@ -6135,6 +6119,15 @@ retry: my_sleep(retry_sleep); continue; } + if(report_error) + { + const NdbError& tmp= error; + ERR_PRINT(tmp); + reterr= ndb_to_mysql_error(&tmp); + } + else + reterr= error.code; + break; } while(1); DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr, From 2fed34d7eab8b25df278880d9fb02d5084da22b8 Mon Sep 17 00:00:00 2001 From: "tulin/mysqldev@mysql.com/production.mysql.com" <> Date: Wed, 17 Jan 2007 10:41:52 +0100 Subject: [PATCH 35/73] ha_ndbcluster.cc: Bug #25668 - corrected patch after test failures --- sql/ha_ndbcluster.cc | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index c40404f0219..f0ae4bae3a2 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -6109,7 +6109,23 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb, DBUG_RETURN(0); retry: + if(report_error) + { + if (file && pTrans) + { + reterr= file->ndb_err(pTrans); + } + else + { + const NdbError& tmp= error; + ERR_PRINT(tmp); + reterr= ndb_to_mysql_error(&tmp); + } + } + else + reterr= error.code; + if (pTrans) { ndb->closeTransaction(pTrans); pTrans= NULL; @@ -6119,15 +6135,6 @@ retry: my_sleep(retry_sleep); continue; } - if(report_error) - { - const NdbError& tmp= error; - ERR_PRINT(tmp); - reterr= ndb_to_mysql_error(&tmp); - } - else - reterr= error.code; - break; } while(1); DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr, From 55ed38e533e825894561d370f81ee2ab7f999b8f Mon Sep 17 00:00:00 2001 From: "msvensson@pilot.mysql.com" <> Date: Wed, 17 Jan 2007 11:27:33 +0100 Subject: [PATCH 36/73] Cset exclude: msvensson@neptunus.(none)|ChangeSet|20060825084614|09755 --- mysql-test/r/myisam.result | 4 ++-- mysql-test/t/myisam.test | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 73cee4835bf..a95d57b4513 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -927,12 +927,12 @@ show create table t1; Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 show create table t1; Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 create table t1 (a int) engine=myisam select 42 a; select * from t1; a diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 7868e7bad4e..22f5bf18c0d 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -861,20 +861,14 @@ connect (session2,localhost,root,,); connection session1; disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 9 a; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/tmp" select 9 a; enable_query_log; -# If running test suite with a non standard tmp dir, the "show create table" -# will print "DATA_DIRECTORY=". Use replace_result to mask it out ---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR show create table t1; connection session2; disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 99 a; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/tmp" select 99 a; enable_query_log; -# If running test suite with a non standard tmp dir, the "show create table" -# will print "DATA_DIRECTORY=". Use replace_result to mask it out ---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR show create table t1; connection default; From 248c76823010738d9320d32c4eee1a5a92c1cf40 Mon Sep 17 00:00:00 2001 From: "msvensson@pilot.mysql.com" <> Date: Wed, 17 Jan 2007 12:12:24 +0100 Subject: [PATCH 37/73] Cset exclude: msvensson@pilot.mysql.com|ChangeSet|20070117102733|42618 --- mysql-test/r/myisam.result | 4 ++-- mysql-test/t/myisam.test | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index a95d57b4513..73cee4835bf 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -927,12 +927,12 @@ show create table t1; Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' show create table t1; Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' create table t1 (a int) engine=myisam select 42 a; select * from t1; a diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 22f5bf18c0d..7868e7bad4e 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -861,14 +861,20 @@ connect (session2,localhost,root,,); connection session1; disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/tmp" select 9 a; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 9 a; enable_query_log; +# If running test suite with a non standard tmp dir, the "show create table" +# will print "DATA_DIRECTORY=". Use replace_result to mask it out +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR show create table t1; connection session2; disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/tmp" select 99 a; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 99 a; enable_query_log; +# If running test suite with a non standard tmp dir, the "show create table" +# will print "DATA_DIRECTORY=". Use replace_result to mask it out +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR show create table t1; connection default; From 543bc0165f039f5b148c750483615705680ca5a3 Mon Sep 17 00:00:00 2001 From: "msvensson@pilot.mysql.com" <> Date: Wed, 17 Jan 2007 12:22:00 +0100 Subject: [PATCH 38/73] Bug#21122 SHOW CREATE TABLE: directory output only sometimes Bug #25000 myisam.test fails on 'pb-valgrind-*' Valgrind - Move tests that need symlink to symlink.test --- mysql-test/r/myisam.result | 21 --------------------- mysql-test/r/symlink.result | 21 +++++++++++++++++++++ mysql-test/t/myisam.test | 35 ----------------------------------- mysql-test/t/symlink.test | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 56 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 73cee4835bf..62d3e679eb2 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -923,27 +923,6 @@ SET @@myisam_repair_threads=1; SHOW VARIABLES LIKE 'myisam_repair%'; Variable_name Value myisam_repair_threads 1 -show create table t1; -Table Create Table -t1 CREATE TEMPORARY TABLE `t1` ( - `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' -show create table t1; -Table Create Table -t1 CREATE TEMPORARY TABLE `t1` ( - `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' -create table t1 (a int) engine=myisam select 42 a; -select * from t1; -a -9 -select * from t1; -a -99 -select * from t1; -a -42 -drop table t1; CREATE TABLE t1(a VARCHAR(16)); INSERT INTO t1 VALUES('aaaaaaaa'),(NULL); UPDATE t1 AS ta1, t1 AS ta2 SET ta1.a='aaaaaaaaaaaaaaaa'; diff --git a/mysql-test/r/symlink.result b/mysql-test/r/symlink.result index f6779689133..b104ce50a56 100644 --- a/mysql-test/r/symlink.result +++ b/mysql-test/r/symlink.result @@ -102,3 +102,24 @@ t1 CREATE TABLE `t1` ( `i` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; +show create table t1; +Table Create Table +t1 CREATE TEMPORARY TABLE `t1` ( + `a` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' +show create table t1; +Table Create Table +t1 CREATE TEMPORARY TABLE `t1` ( + `a` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' +create table t1 (a int) engine=myisam select 42 a; +select * from t1; +a +9 +select * from t1; +a +99 +select * from t1; +a +42 +drop table t1; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 7868e7bad4e..01e7a08ae53 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -854,41 +854,6 @@ DROP TABLE t1; # SET @@myisam_repair_threads=1; SHOW VARIABLES LIKE 'myisam_repair%'; -# Bug#8706 - temporary table with data directory option fails -# -connect (session1,localhost,root,,); -connect (session2,localhost,root,,); - -connection session1; -disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 9 a; -enable_query_log; -# If running test suite with a non standard tmp dir, the "show create table" -# will print "DATA_DIRECTORY=". Use replace_result to mask it out ---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR -show create table t1; - -connection session2; -disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 99 a; -enable_query_log; -# If running test suite with a non standard tmp dir, the "show create table" -# will print "DATA_DIRECTORY=". Use replace_result to mask it out ---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR -show create table t1; - -connection default; -create table t1 (a int) engine=myisam select 42 a; - -connection session1; -select * from t1; -disconnect session1; -connection session2; -select * from t1; -disconnect session2; -connection default; -select * from t1; -drop table t1; # # BUG#21310 - Trees in SQL causing a "crashed" table with MyISAM storage diff --git a/mysql-test/t/symlink.test b/mysql-test/t/symlink.test index c0c1db3d553..201a2866c4f 100644 --- a/mysql-test/t/symlink.test +++ b/mysql-test/t/symlink.test @@ -133,4 +133,41 @@ enable_query_log; show create table t1; drop table t1; +# +# Bug#8706 - temporary table with data directory option fails +# +connect (session1,localhost,root,,); +connect (session2,localhost,root,,); + +connection session1; +disable_query_log; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 9 a; +enable_query_log; +# If running test suite with a non standard tmp dir, the "show create table" +# will print "DATA_DIRECTORY=". Use replace_result to mask it out +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +show create table t1; + +connection session2; +disable_query_log; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 99 a; +enable_query_log; +# If running test suite with a non standard tmp dir, the "show create table" +# will print "DATA_DIRECTORY=". Use replace_result to mask it out +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +show create table t1; + +connection default; +create table t1 (a int) engine=myisam select 42 a; + +connection session1; +select * from t1; +disconnect session1; +connection session2; +select * from t1; +disconnect session2; +connection default; +select * from t1; +drop table t1; + # End of 4.1 tests From 3af9db79759b3d0d41cf7c8e709c1c2b7239d2e2 Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Wed, 17 Jan 2007 21:15:13 +0100 Subject: [PATCH 39/73] ndb - bug#25686 add support for doing mlockall before mallc instead of after (recommit in 5.0) --- ndb/include/portlib/NdbMem.h | 2 +- ndb/src/common/portlib/NdbMem.c | 10 +++++++++- ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp | 19 +++++++++++++++++-- ndb/src/kernel/vm/Configuration.cpp | 2 +- ndb/src/kernel/vm/Configuration.hpp | 2 +- ndb/src/mgmsrv/ConfigInfo.cpp | 8 ++++---- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/ndb/include/portlib/NdbMem.h b/ndb/include/portlib/NdbMem.h index 0f2de80200e..2afb1845112 100644 --- a/ndb/include/portlib/NdbMem.h +++ b/ndb/include/portlib/NdbMem.h @@ -66,7 +66,7 @@ void NdbMem_Free(void* ptr); * NdbMem_MemLockAll * Locks virtual memory in main memory */ -int NdbMem_MemLockAll(void); +int NdbMem_MemLockAll(int); /** * NdbMem_MemUnlockAll diff --git a/ndb/src/common/portlib/NdbMem.c b/ndb/src/common/portlib/NdbMem.c index f964f4d9937..0d2021aaf0a 100644 --- a/ndb/src/common/portlib/NdbMem.c +++ b/ndb/src/common/portlib/NdbMem.c @@ -57,7 +57,15 @@ void NdbMem_Free(void* ptr) } -int NdbMem_MemLockAll(){ +int NdbMem_MemLockAll(int i){ + if (i == 1) + { +#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && defined (MCL_FUTURE) + return mlockall(MCL_CURRENT | MCL_FUTURE); +#else + return -1; +#endif + } #if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) return mlockall(MCL_CURRENT); #else diff --git a/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp b/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp index cd0e471a676..5642a11db81 100644 --- a/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp +++ b/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp @@ -342,9 +342,9 @@ void Cmvmi::execSTTOR(Signal* signal) if (theStartPhase == 1){ jam(); - if(theConfig.lockPagesInMainMemory()) + if(theConfig.lockPagesInMainMemory() == 1) { - int res = NdbMem_MemLockAll(); + int res = NdbMem_MemLockAll(0); if(res != 0){ g_eventLogger.warning("Failed to memlock pages"); warningEvent("Failed to memlock pages"); @@ -788,6 +788,21 @@ Cmvmi::execSTART_ORD(Signal* signal) { if(globalData.theStartLevel == NodeState::SL_CMVMI){ jam(); + + if(theConfig.lockPagesInMainMemory() == 2) + { + int res = NdbMem_MemLockAll(1); + if(res != 0) + { + g_eventLogger.warning("Failed to memlock pages"); + warningEvent("Failed to memlock pages"); + } + else + { + g_eventLogger.info("Locked future allocations"); + } + } + globalData.theStartLevel = NodeState::SL_STARTING; globalData.theRestartFlag = system_started; /** diff --git a/ndb/src/kernel/vm/Configuration.cpp b/ndb/src/kernel/vm/Configuration.cpp index 49f16dae3dd..cbdd9494fd8 100644 --- a/ndb/src/kernel/vm/Configuration.cpp +++ b/ndb/src/kernel/vm/Configuration.cpp @@ -477,7 +477,7 @@ Configuration::setupConfiguration(){ DBUG_VOID_RETURN; } -bool +Uint32 Configuration::lockPagesInMainMemory() const { return _lockPagesInMainMemory; } diff --git a/ndb/src/kernel/vm/Configuration.hpp b/ndb/src/kernel/vm/Configuration.hpp index 6315209ddbb..13b31ad3538 100644 --- a/ndb/src/kernel/vm/Configuration.hpp +++ b/ndb/src/kernel/vm/Configuration.hpp @@ -37,7 +37,7 @@ public: void setupConfiguration(); void closeConfiguration(bool end_session= true); - bool lockPagesInMainMemory() const; + Uint32 lockPagesInMainMemory() const; int timeBetweenWatchDogCheck() const ; void timeBetweenWatchDogCheck(int value); diff --git a/ndb/src/mgmsrv/ConfigInfo.cpp b/ndb/src/mgmsrv/ConfigInfo.cpp index ab4f2b413b3..7f89f5c5c49 100644 --- a/ndb/src/mgmsrv/ConfigInfo.cpp +++ b/ndb/src/mgmsrv/ConfigInfo.cpp @@ -564,10 +564,10 @@ const ConfigInfo::ParamInfo ConfigInfo::m_ParamInfo[] = { "If set to yes, then NDB Cluster data will not be swapped out to disk", ConfigInfo::CI_USED, true, - ConfigInfo::CI_BOOL, - "false", - "false", - "true" }, + ConfigInfo::CI_INT, + "0", + "1", + "2" }, { CFG_DB_WATCHDOG_INTERVAL, From c1927e9a70f38a92a3164f71f4f7ff4449befcea Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Wed, 17 Jan 2007 20:13:45 -0800 Subject: [PATCH 40/73] Fixed bug #25580: incorrect stored representations of views in cases when they contain the '!' operator. Added an implementation for the method Item_func_not::print. The method encloses any NOT expression into extra parentheses to avoid incorrect stored representations of views that use the '!' operators. Without this change when a view was created that contained the expression !0*5 its stored representation contained not this expression but rather the expression not(0)*5 . The operator '!' is of a higher precedence than '*', while NOT is of a lower precedence than '*'. That's why the expression !0*5 is interpreted as not(0)*5, while the expression not(0)*5 is interpreted as not((0)*5) unless sql_mode is set to HIGH_NOT_PRECEDENCE. Now we translate !0*5 into (not(0))*5. --- mysql-test/r/sp-code.result | 2 +- mysql-test/r/subselect.result | 10 +++++----- mysql-test/r/view.result | 11 +++++++++++ mysql-test/t/view.test | 11 +++++++++++ sql/item_cmpfunc.cc | 16 ++++++++++++++++ sql/item_cmpfunc.h | 1 + 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result index 4ae38861d29..8fea85b7cd7 100644 --- a/mysql-test/r/sp-code.result +++ b/mysql-test/r/sp-code.result @@ -187,7 +187,7 @@ Pos Instruction 32 set v_dig@4 (v_dig@4 + 1) 33 stmt 4 "update sudoku_work set dig = v_dig wh..." 34 set v_tcounter@6 (v_tcounter@6 + 1) -35 jump_if_not 37(37) not(`test`.`sudoku_digit_ok`(v_row@7,v_col@8,v_dig@4)) +35 jump_if_not 37(37) (not(`test`.`sudoku_digit_ok`(v_row@7,v_col@8,v_dig@4))) 36 jump 15 37 set v_i@3 (v_i@3 + 1) 38 jump 15 diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 1bf6d6c7716..7d72a9cb038 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1464,7 +1464,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL)))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,(not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1` explain extended select s1, s1 = ANY (SELECT s1 FROM t2) from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index @@ -1476,13 +1476,13 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL)))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,(not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1` explain extended select s1, s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2') from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Using where Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2'))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,(not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2')))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` drop table t1,t2; create table t2 (a int, b int); create table t3 (a int); @@ -1737,14 +1737,14 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 12 Using where 2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using index; Using where Warnings: -Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where not((`test`.`t1`.`id`,(((`test`.`t1`.`id`) in t1 on PRIMARY where (`test`.`t1`.`id` < 8))))) +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where (not((`test`.`t1`.`id`,(((`test`.`t1`.`id`) in t1 on PRIMARY where (`test`.`t1`.`id` < 8)))))) explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY tt ALL NULL NULL NULL NULL 12 Using where 2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 Using where; Using index Warnings: Note 1276 Field or reference 'tt.id' of SELECT #2 was resolved in SELECT #1 -Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and (`test`.`t1`.`id` = `test`.`tt`.`id`)) having (`test`.`t1`.`id` is not null))) +Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where (not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and (`test`.`t1`.`id` = `test`.`tt`.`id`)) having (`test`.`t1`.`id` is not null)))) insert into t1 (id, text) values (1000, 'text1000'), (1001, 'text1001'); create table t2 (id int not null, text varchar(20) not null default '', primary key (id)); insert into t2 (id, text) values (1, 'text1'), (2, 'text2'), (3, 'text3'), (4, 'text4'), (5, 'text5'), (6, 'text6'), (7, 'text7'), (8, 'text8'), (9, 'text9'), (10, 'text10'), (11, 'text1'), (12, 'text2'), (13, 'text3'), (14, 'text4'), (15, 'text5'), (16, 'text6'), (17, 'text7'), (18, 'text8'), (19, 'text9'), (20, 'text10'),(21, 'text1'), (22, 'text2'), (23, 'text3'), (24, 'text4'), (25, 'text5'), (26, 'text6'), (27, 'text7'), (28, 'text8'), (29, 'text9'), (30, 'text10'), (31, 'text1'), (32, 'text2'), (33, 'text3'), (34, 'text4'), (35, 'text5'), (36, 'text6'), (37, 'text7'), (38, 'text8'), (39, 'text9'), (40, 'text10'), (41, 'text1'), (42, 'text2'), (43, 'text3'), (44, 'text4'), (45, 'text5'), (46, 'text6'), (47, 'text7'), (48, 'text8'), (49, 'text9'), (50, 'text10'); diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 4d076db5c22..7f3106e5972 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3014,4 +3014,15 @@ i j 6 3 DROP VIEW v1, v2; DROP TABLE t1; +CREATE VIEW v AS SELECT !0 * 5 AS x FROM DUAL; +SHOW CREATE VIEW v; +View Create View +v CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` AS select ((not(0)) * 5) AS `x` +SELECT !0 * 5 AS x FROM DUAL; +x +5 +SELECT * FROM v; +x +5 +DROP VIEW v; End of 5.0 tests. diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 8473458ae15..123759ec82e 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2959,5 +2959,16 @@ SELECT * FROM t1; DROP VIEW v1, v2; DROP TABLE t1; +# +# Bug #25580: !0 as an operand in a select expression of a view +# + +CREATE VIEW v AS SELECT !0 * 5 AS x FROM DUAL; +SHOW CREATE VIEW v; + +SELECT !0 * 5 AS x FROM DUAL; +SELECT * FROM v; + +DROP VIEW v; --echo End of 5.0 tests. diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 936ae04e93d..846aa0c9150 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -146,6 +146,22 @@ longlong Item_func_not::val_int() return ((!null_value && value == 0) ? 1 : 0); } +/* + We put any NOT expression into parenthesis to avoid + possible problems with internal view representations where + any '!' is converted to NOT. It may cause a problem if + '!' is used in an expression together with other operators + whose precedence is lower than the precedence of '!' yet + higher than the precedence of NOT. +*/ + +void Item_func_not::print(String *str) +{ + str->append('('); + Item_func::print(str); + str->append(')'); +} + /* special NOT for ALL subquery */ diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1d85d08c434..54457d37d18 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -269,6 +269,7 @@ public: enum Functype functype() const { return NOT_FUNC; } const char *func_name() const { return "not"; } Item *neg_transformer(THD *thd); + void print(String *str); }; class Item_maxmin_subselect; From 6e771358cee7d56a1b89b4b959112864719e1174 Mon Sep 17 00:00:00 2001 From: "kroki/tomash@moonlight.home" <> Date: Thu, 18 Jan 2007 12:48:17 +0300 Subject: [PATCH 41/73] Bug#24404: strange bug with view+permission+prepared statement. The problem was that if a prepared statement accessed a view, the access to the tables listed in the query after that view was done in the security context of the view. The bug was in the assigning of the security context to the tables belonging to a view: we traversed the list of all query tables instead. It didn't show up in the normal (non-prepared) statements because of the different order of the steps of checking privileges and descending into a view for normal and prepared statements. The solution is to traverse the list and stop once the last table belonging to the view was processed. --- mysql-test/r/view_grant.result | 43 ++++++++++++++++++++ mysql-test/t/view_grant.test | 72 +++++++++++++++++++++++++++++++++- sql/sql_view.cc | 10 +++-- 3 files changed, 121 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 35e7afc0a7b..bdb362a5112 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -712,3 +712,46 @@ DROP FUNCTION f1; DROP VIEW v2; DROP VIEW v1; DROP USER mysqltest_u1@localhost; +DROP DATABASE IF EXISTS mysqltest_db1; +DROP DATABASE IF EXISTS mysqltest_db2; +DROP USER mysqltest_u1; +DROP USER mysqltest_u2; +CREATE USER mysqltest_u1@localhost; +CREATE USER mysqltest_u2@localhost; +CREATE DATABASE mysqltest_db1; +CREATE DATABASE mysqltest_db2; +GRANT ALL ON mysqltest_db1.* TO mysqltest_u1@localhost WITH GRANT OPTION; +GRANT ALL ON mysqltest_db2.* TO mysqltest_u2@localhost; +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1); +CREATE VIEW v1 AS SELECT i FROM t1 WHERE 1 IN (SELECT * FROM t1); +CREATE TABLE t2 (s CHAR(7)); +INSERT INTO t2 VALUES ('public'); +GRANT SELECT ON v1 TO mysqltest_u2@localhost; +GRANT SELECT ON t2 TO mysqltest_u2@localhost; +SELECT * FROM mysqltest_db1.v1, mysqltest_db1.t2; +i s +1 public +PREPARE stmt1 FROM "SELECT * FROM mysqltest_db1.t2"; +EXECUTE stmt1; +s +public +PREPARE stmt2 FROM "SELECT * FROM mysqltest_db1.v1, mysqltest_db1.t2"; +EXECUTE stmt2; +i s +1 public +REVOKE SELECT ON t2 FROM mysqltest_u2@localhost; +UPDATE t2 SET s = 'private' WHERE s = 'public'; +SELECT * FROM mysqltest_db1.v1, mysqltest_db1.t2; +ERROR 42000: SELECT command denied to user 'mysqltest_u2'@'localhost' for table 't2' +EXECUTE stmt1; +ERROR 42000: SELECT command denied to user 'mysqltest_u2'@'localhost' for table 't2' +EXECUTE stmt2; +ERROR 42000: SELECT command denied to user 'mysqltest_u2'@'localhost' for table 't2' +REVOKE ALL ON mysqltest_db1.* FROM mysqltest_u1@localhost; +REVOKE ALL ON mysqltest_db2.* FROM mysqltest_u2@localhost; +DROP DATABASE mysqltest_db1; +DROP DATABASE mysqltest_db2; +DROP USER mysqltest_u1@localhost; +DROP USER mysqltest_u2@localhost; +End of 5.0 tests. diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index 4a3a29e3afe..ea6dbeb0a4b 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -927,4 +927,74 @@ DROP VIEW v2; DROP VIEW v1; DROP USER mysqltest_u1@localhost; -# End of 5.0 tests. + +# +# BUG#24404: strange bug with view+permission+prepared statement +# +--disable_warnings +DROP DATABASE IF EXISTS mysqltest_db1; +DROP DATABASE IF EXISTS mysqltest_db2; +--enable_warnings +--error 0,ER_CANNOT_USER +DROP USER mysqltest_u1; +--error 0,ER_CANNOT_USER +DROP USER mysqltest_u2; + +CREATE USER mysqltest_u1@localhost; +CREATE USER mysqltest_u2@localhost; + +CREATE DATABASE mysqltest_db1; +CREATE DATABASE mysqltest_db2; + +GRANT ALL ON mysqltest_db1.* TO mysqltest_u1@localhost WITH GRANT OPTION; +GRANT ALL ON mysqltest_db2.* TO mysqltest_u2@localhost; + +connect (conn1, localhost, mysqltest_u1, , mysqltest_db1); + +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1); + +# Use view with subquery for better coverage. +CREATE VIEW v1 AS SELECT i FROM t1 WHERE 1 IN (SELECT * FROM t1); + +CREATE TABLE t2 (s CHAR(7)); +INSERT INTO t2 VALUES ('public'); + +GRANT SELECT ON v1 TO mysqltest_u2@localhost; +GRANT SELECT ON t2 TO mysqltest_u2@localhost; + +connect (conn2, localhost, mysqltest_u2, , mysqltest_db2); + +SELECT * FROM mysqltest_db1.v1, mysqltest_db1.t2; +PREPARE stmt1 FROM "SELECT * FROM mysqltest_db1.t2"; +EXECUTE stmt1; +PREPARE stmt2 FROM "SELECT * FROM mysqltest_db1.v1, mysqltest_db1.t2"; +EXECUTE stmt2; + +connection conn1; +# Make table 't2' private. +REVOKE SELECT ON t2 FROM mysqltest_u2@localhost; +UPDATE t2 SET s = 'private' WHERE s = 'public'; + +connection conn2; +--error ER_TABLEACCESS_DENIED_ERROR +SELECT * FROM mysqltest_db1.v1, mysqltest_db1.t2; +--error ER_TABLEACCESS_DENIED_ERROR +EXECUTE stmt1; +# Original bug was here: the statement didn't fail. +--error ER_TABLEACCESS_DENIED_ERROR +EXECUTE stmt2; + +# Cleanup. +disconnect conn2; +disconnect conn1; +connection default; +REVOKE ALL ON mysqltest_db1.* FROM mysqltest_u1@localhost; +REVOKE ALL ON mysqltest_db2.* FROM mysqltest_u2@localhost; +DROP DATABASE mysqltest_db1; +DROP DATABASE mysqltest_db2; +DROP USER mysqltest_u1@localhost; +DROP USER mysqltest_u2@localhost; + + +--echo End of 5.0 tests. diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c0cdaf59712..8cb7ea9cd81 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1136,13 +1136,17 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, /* Prepare a security context to check underlying objects of the view */ - Security_context *save_security_ctx= thd->security_ctx; if (!(table->view_sctx= (Security_context *) thd->stmt_arena->alloc(sizeof(Security_context)))) goto err; /* Assign the context to the tables referenced in the view */ - for (tbl= view_tables; tbl; tbl= tbl->next_global) - tbl->security_ctx= table->view_sctx; + if (view_tables) + { + DBUG_ASSERT(view_tables_tail); + for (tbl= view_tables; tbl != view_tables_tail->next_global; + tbl= tbl->next_global) + tbl->security_ctx= table->view_sctx; + } /* assign security context to SELECT name resolution contexts of view */ for(SELECT_LEX *sl= lex->all_selects_list; sl; From 20d94f1105e02dc61e93af8ffe6e744b9c7803d8 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Thu, 18 Jan 2007 17:33:38 +0200 Subject: [PATCH 42/73] Bug #25382: Passing NULL to an UDF called from stored procedures crashes server Check for null value is reliable only after calling some of the val_xxx() methods. If the val_xxx() method is not called the null_value flag will be set only for certain types of NULL values (like SQL constant NULLs for example). This caused a crash while trying to dereference a NULL pointer that is returned by val_str() for NULL values. Fixed by swapping the order of val_xxx() and null_value check. --- mysql-test/r/udf.result | 34 +++++++++++++++++++++++++++++ mysql-test/t/udf.test | 47 +++++++++++++++++++++++++++++++++++++++++ sql/item_func.cc | 9 +++++--- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 64b7111bbc8..d5f59247084 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -240,3 +240,37 @@ drop table bug18761; select is_const((1,2,3)); ERROR 21000: Operand should contain 1 column(s) drop function if exists is_const; +CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; +CREATE FUNCTION myfunc_double RETURNS REAL SONAME "UDF_EXAMPLE_LIB"; +CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; +create function f1(p1 varchar(255)) +returns varchar(255) +begin +return metaphon(p1); +end// +create function f2(p1 varchar(255)) +returns double +begin +return myfunc_double(p1); +end// +create function f3(p1 varchar(255)) +returns double +begin +return myfunc_int(p1); +end// +select f3(NULL); +f3(NULL) +0 +select f2(NULL); +f2(NULL) +NULL +select f1(NULL); +f1(NULL) +NULL +drop function f1; +drop function f2; +drop function f3; +drop function metaphon; +drop function myfunc_double; +drop function myfunc_int; +End of 5.0 tests. diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 65cbc7ae3ae..0b582dc61b6 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -242,3 +242,50 @@ drop table bug18761; select is_const((1,2,3)); drop function if exists is_const; + +# +# Bug #25382: Passing NULL to an UDF called from stored procedures +# crashes server +# +--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB +eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_LIB"; + +--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB +eval CREATE FUNCTION myfunc_double RETURNS REAL SONAME "$UDF_EXAMPLE_LIB"; + +--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB +eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_LIB"; + +delimiter //; +create function f1(p1 varchar(255)) +returns varchar(255) +begin + return metaphon(p1); +end// + +create function f2(p1 varchar(255)) +returns double +begin + return myfunc_double(p1); +end// + +create function f3(p1 varchar(255)) +returns double +begin + return myfunc_int(p1); +end// + +delimiter ;// + +select f3(NULL); +select f2(NULL); +select f1(NULL); + +drop function f1; +drop function f2; +drop function f3; +drop function metaphon; +drop function myfunc_double; +drop function myfunc_int; + +--echo End of 5.0 tests. diff --git a/sql/item_func.cc b/sql/item_func.cc index 4138573785e..02aad2cfdc7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2729,25 +2729,28 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, if (arguments[i]->const_item()) { - if (arguments[i]->null_value) - continue; - switch (arguments[i]->result_type()) { case STRING_RESULT: case DECIMAL_RESULT: { String *res= arguments[i]->val_str(&buffers[i]); + if (arguments[i]->null_value) + continue; f_args.args[i]= (char*) res->ptr(); break; } case INT_RESULT: *((longlong*) to)= arguments[i]->val_int(); + if (arguments[i]->null_value) + continue; f_args.args[i]= to; to+= ALIGN_SIZE(sizeof(longlong)); break; case REAL_RESULT: *((double*) to)= arguments[i]->val_real(); + if (arguments[i]->null_value) + continue; f_args.args[i]= to; to+= ALIGN_SIZE(sizeof(double)); break; From 436e1f591a37826bfd3e9ced6e393d885d997e7c Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Thu, 18 Jan 2007 16:53:49 -0700 Subject: [PATCH 43/73] Bug#24562 (ALTER TABLE ... ORDER BY ... with complex expression asserts) WL#3681 (ALTER TABLE ORDER BY) Before this fix, the ALTER TABLE statement implemented an ORDER BY option with the following characteristics : 1) The order by clause accepts a list of criteria, with optional ASC or DESC keywords 2) Each criteria can be a general expression, involving operators, native functions, stored functions, user defined functions, subselects ... With this fix : 1) has been left unchanged, since it's a de-facto existing feature, that was already present in the code base and partially covered in the test suite. Code coverage for ASC and DESC was missing and has been improved. 2) has been changed to limit the kind of criteria that are permissible: now only a column name is valid. --- mysql-test/r/alter_table.result | 113 ++++++++++++++++++++++++++++++++ mysql-test/t/alter_table.test | 66 +++++++++++++++++++ sql/sql_yacc.yy | 25 ++++++- 3 files changed, 203 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 60830cd9c45..da5cf688325 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -690,3 +690,116 @@ SHOW INDEX FROM bug24219_2; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment bug24219_2 1 a 1 a A NULL NULL NULL YES BTREE disabled DROP TABLE bug24219_2; +drop table if exists table_24562; +create table table_24562( +section int, +subsection int, +title varchar(50)); +insert into table_24562 values +(1, 0, "Introduction"), +(1, 1, "Authors"), +(1, 2, "Acknowledgements"), +(2, 0, "Basics"), +(2, 1, "Syntax"), +(2, 2, "Client"), +(2, 3, "Server"), +(3, 0, "Intermediate"), +(3, 1, "Complex queries"), +(3, 2, "Stored Procedures"), +(3, 3, "Stored Functions"), +(4, 0, "Advanced"), +(4, 1, "Replication"), +(4, 2, "Load balancing"), +(4, 3, "High availability"), +(5, 0, "Conclusion"); +select * from table_24562; +section subsection title +1 0 Introduction +1 1 Authors +1 2 Acknowledgements +2 0 Basics +2 1 Syntax +2 2 Client +2 3 Server +3 0 Intermediate +3 1 Complex queries +3 2 Stored Procedures +3 3 Stored Functions +4 0 Advanced +4 1 Replication +4 2 Load balancing +4 3 High availability +5 0 Conclusion +alter table table_24562 add column reviewer varchar(20), +order by title; +select * from table_24562; +section subsection title reviewer +1 2 Acknowledgements NULL +4 0 Advanced NULL +1 1 Authors NULL +2 0 Basics NULL +2 2 Client NULL +3 1 Complex queries NULL +5 0 Conclusion NULL +4 3 High availability NULL +3 0 Intermediate NULL +1 0 Introduction NULL +4 2 Load balancing NULL +4 1 Replication NULL +2 3 Server NULL +3 3 Stored Functions NULL +3 2 Stored Procedures NULL +2 1 Syntax NULL +update table_24562 set reviewer="Me" where section=2; +update table_24562 set reviewer="You" where section=3; +alter table table_24562 +order by section ASC, subsection DESC; +select * from table_24562; +section subsection title reviewer +1 2 Acknowledgements NULL +1 1 Authors NULL +1 0 Introduction NULL +2 3 Server Me +2 2 Client Me +2 1 Syntax Me +2 0 Basics Me +3 3 Stored Functions You +3 2 Stored Procedures You +3 1 Complex queries You +3 0 Intermediate You +4 3 High availability NULL +4 2 Load balancing NULL +4 1 Replication NULL +4 0 Advanced NULL +5 0 Conclusion NULL +alter table table_24562 +order by table_24562.subsection ASC, table_24562.section DESC; +select * from table_24562; +section subsection title reviewer +5 0 Conclusion NULL +4 0 Advanced NULL +3 0 Intermediate You +2 0 Basics Me +1 0 Introduction NULL +4 1 Replication NULL +3 1 Complex queries You +2 1 Syntax Me +1 1 Authors NULL +4 2 Load balancing NULL +3 2 Stored Procedures You +2 2 Client Me +1 2 Acknowledgements NULL +4 3 High availability NULL +3 3 Stored Functions You +2 3 Server Me +alter table table_24562 order by 12; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '12' at line 1 +alter table table_24562 order by (section + 12); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(section + 12)' at line 1 +alter table table_24562 order by length(title); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'length(title)' at line 1 +alter table table_24562 order by (select 12 from dual); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(select 12 from dual)' at line 1 +alter table table_24562 order by no_such_col; +ERROR 42S22: Unknown column 'no_such_col' in 'order clause' +drop table table_24562; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 3e1e7952af1..52a569dfb57 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -518,4 +518,70 @@ SHOW INDEX FROM bug24219_2; DROP TABLE bug24219_2; +# +# Bug#24562 (ALTER TABLE ... ORDER BY ... with complex expression asserts) +# + +--disable_warnings +drop table if exists table_24562; +--enable_warnings + +create table table_24562( + section int, + subsection int, + title varchar(50)); + +insert into table_24562 values +(1, 0, "Introduction"), +(1, 1, "Authors"), +(1, 2, "Acknowledgements"), +(2, 0, "Basics"), +(2, 1, "Syntax"), +(2, 2, "Client"), +(2, 3, "Server"), +(3, 0, "Intermediate"), +(3, 1, "Complex queries"), +(3, 2, "Stored Procedures"), +(3, 3, "Stored Functions"), +(4, 0, "Advanced"), +(4, 1, "Replication"), +(4, 2, "Load balancing"), +(4, 3, "High availability"), +(5, 0, "Conclusion"); + +select * from table_24562; + +alter table table_24562 add column reviewer varchar(20), +order by title; + +select * from table_24562; + +update table_24562 set reviewer="Me" where section=2; +update table_24562 set reviewer="You" where section=3; + +alter table table_24562 +order by section ASC, subsection DESC; + +select * from table_24562; + +alter table table_24562 +order by table_24562.subsection ASC, table_24562.section DESC; + +select * from table_24562; + +--error 1064 +alter table table_24562 order by 12; +--error 1064 +alter table table_24562 order by (section + 12); +--error 1064 +alter table table_24562 order by length(title); +--error 1064 +alter table table_24562 order by (select 12 from dual); + +--error 1054 +alter table table_24562 order by no_such_col; + +drop table table_24562; + # End of 4.1 tests + diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 30283a47b21..ee57a4d611c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2021,7 +2021,7 @@ alter_list_item: lex->alter_info.is_simple= 0; lex->alter_info.flags|= ALTER_OPTIONS; } - | order_clause + | alter_order_clause { LEX *lex=Lex; lex->alter_info.is_simple= 0; @@ -3780,6 +3780,29 @@ olap_opt: } ; +/* + Order by statement in ALTER TABLE +*/ + +alter_order_clause: + ORDER_SYM BY alter_order_list + ; + +alter_order_list: + alter_order_list ',' alter_order_item + | alter_order_item + ; + +alter_order_item: + simple_ident order_dir + { + THD *thd= YYTHD; + bool ascending= ($2 == 1) ? true : false; + if (add_order_to_list(thd, $1, ascending)) + YYABORT; + } + ; + /* Order by statement in select */ From 4064c89d2400a910e24813ce633f5c9522d1a321 Mon Sep 17 00:00:00 2001 From: "malff/marcsql@weblab.(none)" <> Date: Thu, 18 Jan 2007 18:37:52 -0700 Subject: [PATCH 44/73] Manual merge --- mysql-test/r/alter_table.result | 113 ++++++++++++++++++++++++++++++++ mysql-test/t/alter_table.test | 10 +-- sql/sql_yacc.yy | 25 ++++++- 3 files changed, 142 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 80ad50b886f..d8de2655c6c 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -703,6 +703,119 @@ SHOW INDEX FROM bug24219_2; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment bug24219_2 1 a 1 a A NULL NULL NULL YES BTREE disabled DROP TABLE bug24219_2; +drop table if exists table_24562; +create table table_24562( +section int, +subsection int, +title varchar(50)); +insert into table_24562 values +(1, 0, "Introduction"), +(1, 1, "Authors"), +(1, 2, "Acknowledgements"), +(2, 0, "Basics"), +(2, 1, "Syntax"), +(2, 2, "Client"), +(2, 3, "Server"), +(3, 0, "Intermediate"), +(3, 1, "Complex queries"), +(3, 2, "Stored Procedures"), +(3, 3, "Stored Functions"), +(4, 0, "Advanced"), +(4, 1, "Replication"), +(4, 2, "Load balancing"), +(4, 3, "High availability"), +(5, 0, "Conclusion"); +select * from table_24562; +section subsection title +1 0 Introduction +1 1 Authors +1 2 Acknowledgements +2 0 Basics +2 1 Syntax +2 2 Client +2 3 Server +3 0 Intermediate +3 1 Complex queries +3 2 Stored Procedures +3 3 Stored Functions +4 0 Advanced +4 1 Replication +4 2 Load balancing +4 3 High availability +5 0 Conclusion +alter table table_24562 add column reviewer varchar(20), +order by title; +select * from table_24562; +section subsection title reviewer +1 2 Acknowledgements NULL +4 0 Advanced NULL +1 1 Authors NULL +2 0 Basics NULL +2 2 Client NULL +3 1 Complex queries NULL +5 0 Conclusion NULL +4 3 High availability NULL +3 0 Intermediate NULL +1 0 Introduction NULL +4 2 Load balancing NULL +4 1 Replication NULL +2 3 Server NULL +3 3 Stored Functions NULL +3 2 Stored Procedures NULL +2 1 Syntax NULL +update table_24562 set reviewer="Me" where section=2; +update table_24562 set reviewer="You" where section=3; +alter table table_24562 +order by section ASC, subsection DESC; +select * from table_24562; +section subsection title reviewer +1 2 Acknowledgements NULL +1 1 Authors NULL +1 0 Introduction NULL +2 3 Server Me +2 2 Client Me +2 1 Syntax Me +2 0 Basics Me +3 3 Stored Functions You +3 2 Stored Procedures You +3 1 Complex queries You +3 0 Intermediate You +4 3 High availability NULL +4 2 Load balancing NULL +4 1 Replication NULL +4 0 Advanced NULL +5 0 Conclusion NULL +alter table table_24562 +order by table_24562.subsection ASC, table_24562.section DESC; +select * from table_24562; +section subsection title reviewer +5 0 Conclusion NULL +4 0 Advanced NULL +3 0 Intermediate You +2 0 Basics Me +1 0 Introduction NULL +4 1 Replication NULL +3 1 Complex queries You +2 1 Syntax Me +1 1 Authors NULL +4 2 Load balancing NULL +3 2 Stored Procedures You +2 2 Client Me +1 2 Acknowledgements NULL +4 3 High availability NULL +3 3 Stored Functions You +2 3 Server Me +alter table table_24562 order by 12; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '12' at line 1 +alter table table_24562 order by (section + 12); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(section + 12)' at line 1 +alter table table_24562 order by length(title); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'length(title)' at line 1 +alter table table_24562 order by (select 12 from dual); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(select 12 from dual)' at line 1 +alter table table_24562 order by no_such_col; +ERROR 42S22: Unknown column 'no_such_col' in 'order clause' +drop table table_24562; create table t1 (mycol int(10) not null); alter table t1 alter column mycol set default 0; desc t1; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 68f984c65e1..01f55931ca4 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -579,16 +579,16 @@ order by table_24562.subsection ASC, table_24562.section DESC; select * from table_24562; ---error 1064 +--error ER_PARSE_ERROR alter table table_24562 order by 12; ---error 1064 +--error ER_PARSE_ERROR alter table table_24562 order by (section + 12); ---error 1064 +--error ER_PARSE_ERROR alter table table_24562 order by length(title); ---error 1064 +--error ER_PARSE_ERROR alter table table_24562 order by (select 12 from dual); ---error 1054 +--error ER_BAD_FIELD_ERROR alter table table_24562 order by no_such_col; drop table table_24562; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 92640ea58d6..0f29c3e1028 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3722,7 +3722,7 @@ alter_list_item: { Lex->alter_info.flags|= ALTER_FORCE; } - | order_clause + | alter_order_clause { LEX *lex=Lex; lex->alter_info.flags|= ALTER_ORDER; @@ -5942,6 +5942,29 @@ olap_opt: } ; +/* + Order by statement in ALTER TABLE +*/ + +alter_order_clause: + ORDER_SYM BY alter_order_list + ; + +alter_order_list: + alter_order_list ',' alter_order_item + | alter_order_item + ; + +alter_order_item: + simple_ident_nospvar order_dir + { + THD *thd= YYTHD; + bool ascending= ($2 == 1) ? true : false; + if (add_order_to_list(thd, $1, ascending)) + YYABORT; + } + ; + /* Order by statement in select */ From 8aea461461efb510695b1f587800181d54b46aaf Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Fri, 19 Jan 2007 04:36:33 +0100 Subject: [PATCH 45/73] ndb - bug#25711 fix cpu peak in big clusters during unpack of config --- ndb/src/common/util/ConfigValues.cpp | 208 ++++++++++++++++----------- 1 file changed, 122 insertions(+), 86 deletions(-) diff --git a/ndb/src/common/util/ConfigValues.cpp b/ndb/src/common/util/ConfigValues.cpp index ae4fbfd2f71..49fd6dd9a28 100644 --- a/ndb/src/common/util/ConfigValues.cpp +++ b/ndb/src/common/util/ConfigValues.cpp @@ -34,7 +34,7 @@ static const char Magic[] = { 'N', 'D', 'B', 'C', 'O', 'N', 'F', 'V' }; //#define DEBUG_CV #ifdef DEBUG_CV -#define DEBUG +#define DEBUG if(getenv("CV_DEBUG")) #else #define DEBUG if(0) #endif @@ -202,62 +202,60 @@ ConfigValues::Iterator::set(Uint32 key, const char * value){ static bool findKey(const Uint32 * values, Uint32 sz, Uint32 key, Uint32 * _pos){ - Uint32 pos = hash(key, sz); - Uint32 count = 0; - while((values[pos] & KP_MASK) != key && count < sz){ - pos = nextHash(key, sz, pos, ++count); + Uint32 lo = 0; + Uint32 hi = sz; + Uint32 pos = (hi + lo) >> 1; + + DEBUG printf("findKey(H'%.8x %d)", key, sz); + + if (sz == 0) + { + DEBUG ndbout_c(" -> false, 0"); + * _pos = 0; + return false; } - if((values[pos] & KP_MASK)== key){ - *_pos = pos; - return true; - } + Uint32 val = 0; + Uint32 oldpos = pos + 1; + while (pos != oldpos) + { + DEBUG printf(" [ %d %d %d ] ", lo, pos, hi); + assert(pos < hi); + assert(pos >= lo); + val = values[2*pos] & KP_MASK; + if (key > val) + { + lo = pos; + } + else if (key < val) + { + hi = pos; + } + else + { + * _pos = 2*pos; + DEBUG ndbout_c(" -> true, %d", pos); + return true; + } + oldpos = pos; + pos = (hi + lo) >> 1; + } + + DEBUG printf(" pos: %d (key %.8x val: %.8x values[pos]: %x) key>val: %d ", + pos, key, val, values[2*pos] & KP_MASK, + key > val); + + pos += (key > val) ? 1 : 0; + + * _pos = 2*pos; + DEBUG ndbout_c(" -> false, %d", pos); return false; } -static -Uint32 -hash(Uint32 key, Uint32 size){ - Uint32 tmp = (key >> 16) ^ (key & 0xFFFF); - return (((tmp << 16) | tmp) % size) << 1; -} - -static -Uint32 -nextHash(Uint32 key, Uint32 size, Uint32 pos, Uint32 count){ - Uint32 p = (pos >> 1); - if((key % size) != 0) - p += key; - else - p += 1; - return (p % size) << 1; -} - -static -Uint32 -directory(Uint32 sz){ - const Uint32 _input = sz; - if((sz & 1) == 0) - sz ++; - - bool prime = false; - while(!prime){ - prime = true; - for(Uint32 n = 3; n*n <= sz; n += 2){ - if((sz % n) == 0){ - prime = false; - sz += 2; - break; - } - } - } - DEBUG printf("directory %d -> %d\n", _input, sz); - return sz; -} ConfigValuesFactory::ConfigValuesFactory(Uint32 keys, Uint32 data){ m_sectionCounter = (1 << KP_SECTION_SHIFT); - m_freeKeys = directory(keys); + m_freeKeys = keys; m_freeData = (data + 7) & ~7; m_currentSection = 0; m_cfg = create(m_freeKeys, m_freeData); @@ -316,11 +314,14 @@ ConfigValuesFactory::expand(Uint32 fk, Uint32 fs){ return ; } + DEBUG printf("[ fk fd ] : [ %d %d ]", m_freeKeys, m_freeData); + m_freeKeys = (m_freeKeys >= fk ? m_cfg->m_size : fk + m_cfg->m_size); m_freeData = (m_freeData >= fs ? m_cfg->m_dataSize : fs + m_cfg->m_dataSize); - m_freeKeys = directory(m_freeKeys); m_freeData = (m_freeData + 7) & ~7; - + + DEBUG ndbout_c(" [ %d %d ]", m_freeKeys, m_freeData); + ConfigValues * m_tmp = m_cfg; m_cfg = create(m_freeKeys, m_freeData); put(* m_tmp); @@ -336,7 +337,6 @@ ConfigValuesFactory::shrink(){ m_freeKeys = m_cfg->m_size - m_freeKeys; m_freeData = m_cfg->m_dataSize - m_freeData; - m_freeKeys = directory(m_freeKeys); m_freeData = (m_freeData + 7) & ~7; ConfigValues * m_tmp = m_cfg; @@ -415,52 +415,58 @@ ConfigValuesFactory::put(const ConfigValues::Entry & entry){ } const Uint32 tmp = entry.m_key | m_currentSection; - const Uint32 sz = m_cfg->m_size; - Uint32 pos = hash(tmp, sz); - Uint32 count = 0; - Uint32 val = m_cfg->m_values[pos]; + const Uint32 sz = m_cfg->m_size - m_freeKeys; - while((val & KP_MASK) != tmp && val != CFV_KEY_FREE && count < sz){ - pos = nextHash(tmp, sz, pos, ++count); - val = m_cfg->m_values[pos]; - } - - if((val & KP_MASK) == tmp){ + Uint32 pos; + if (findKey(m_cfg->m_values, sz, tmp, &pos)) + { DEBUG ndbout_c("key %x already found at pos: %d", tmp, pos); return false; } - if(count >= sz){ - pos = hash(tmp, sz); - count = 0; - Uint32 val = m_cfg->m_values[pos]; - - printf("key: %d, (key %% size): %d\n", entry.m_key, (entry.m_key % sz)); - printf("pos: %d", pos); - while((val & KP_MASK) != tmp && val != CFV_KEY_FREE && count < sz){ - pos = nextHash(tmp, sz, pos, ++count); - val = m_cfg->m_values[pos]; - printf(" %d", pos); + DEBUG { + printf("H'before "); + Uint32 prev = 0; + for (Uint32 i = 0; im_values[2*i] & KP_MASK; + ndbout_c("%.8x", val); + assert(val >= prev); + prev = val; } - printf("\n"); - - abort(); - printf("Full\n"); - return false; + } + + if (pos != 2*sz) + { + DEBUG ndbout_c("pos: %d sz: %d", pos, sz); + memmove(m_cfg->m_values + pos + 2, m_cfg->m_values + pos, + 4 * (2*sz - pos)); } - assert(pos < (sz << 1)); Uint32 key = tmp; key |= (entry.m_type << KP_TYPE_SHIFT); m_cfg->m_values[pos] = key; + + DEBUG { + printf("H'after "); + Uint32 prev = 0; + for (Uint32 i = 0; i<=sz; i++) + { + Uint32 val = m_cfg->m_values[2*i] & KP_MASK; + ndbout_c("%.8x", val); + assert(val >= prev); + prev = val; + } + } + switch(entry.m_type){ case ConfigValues::IntType: case ConfigValues::SectionType: m_cfg->m_values[pos+1] = entry.m_int; m_freeKeys--; DEBUG printf("Putting at: %d(%d) (loop = %d) key: %d value: %d\n", - pos, sz, count, + pos, sz, 0, (key >> KP_KEYVAL_SHIFT) & KP_KEYVAL_MASK, entry.m_int); return true; @@ -472,7 +478,7 @@ ConfigValuesFactory::put(const ConfigValues::Entry & entry){ m_freeKeys--; m_freeData -= sizeof(char *); DEBUG printf("Putting at: %d(%d) (loop = %d) key: %d value(%d): %s\n", - pos, sz, count, + pos, sz, 0, (key >> KP_KEYVAL_SHIFT) & KP_KEYVAL_MASK, index, entry.m_string); @@ -485,7 +491,7 @@ ConfigValuesFactory::put(const ConfigValues::Entry & entry){ m_freeKeys--; m_freeData -= 8; DEBUG printf("Putting at: %d(%d) (loop = %d) key: %d value64(%d): %lld\n", - pos, sz, count, + pos, sz, 0, (key >> KP_KEYVAL_SHIFT) & KP_KEYVAL_MASK, index, entry.m_int64); @@ -648,7 +654,9 @@ ConfigValuesFactory::unpack(const void * _src, Uint32 len){ } const char * src = (const char *)_src; - + const char * end = src + len - 4; + src += sizeof(Magic); + { Uint32 len32 = (len >> 2); const Uint32 * tmp = (const Uint32*)_src; @@ -663,9 +671,37 @@ ConfigValuesFactory::unpack(const void * _src, Uint32 len){ } } - const char * end = src + len - 4; - src += sizeof(Magic); - + const char * save = src; + + { + Uint32 keys = 0; + Uint32 data = 0; + while(end - src > 4){ + Uint32 tmp = ntohl(* (const Uint32 *)src); src += 4; + keys++; + switch(::getTypeOf(tmp)){ + case ConfigValues::IntType: + case ConfigValues::SectionType: + src += 4; + break; + case ConfigValues::Int64Type: + src += 8; + data += 8; + break; + case ConfigValues::StringType:{ + Uint32 s_len = ntohl(* (const Uint32 *)src); + src += 4 + mod4(s_len); + data += sizeof(char*); + break; + } + default: + break; + } + } + expand(keys, data); + } + + src = save; ConfigValues::Entry entry; while(end - src > 4){ Uint32 tmp = ntohl(* (const Uint32 *)src); src += 4; From 3c814f224356e361a4a1c576949adcea72256ac8 Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Fri, 19 Jan 2007 00:17:28 -0800 Subject: [PATCH 46/73] Fixed bug #25219: crash for a query that contains an EXIST subquery with UNION over correlated and uncorrelated SELECTS. In such subqueries each uncorrelated SELECT should be considered as uncacheable. Otherwise join_free is called for it and in many cases it causes some problems. --- mysql-test/r/subselect.result | 33 +++++++++++++++++++++++++++++++++ mysql-test/t/subselect.test | 34 ++++++++++++++++++++++++++++++++++ sql/mysql_priv.h | 5 ++++- sql/sql_lex.cc | 12 ++++++++++-- 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 1bf6d6c7716..5cf3cd110c0 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3605,3 +3605,36 @@ FROM t1) t; COUNT(*) 3000 DROP TABLE t1,t2; +CREATE TABLE t1 (id char(4) PRIMARY KEY, c int); +CREATE TABLE t2 (c int); +INSERT INTO t1 VALUES ('aa', 1); +INSERT INTO t2 VALUES (1); +SELECT * FROM t1 +WHERE EXISTS (SELECT c FROM t2 WHERE c=1 +UNION +SELECT c from t2 WHERE c=t1.c); +id c +aa 1 +INSERT INTO t1 VALUES ('bb', 2), ('cc', 3), ('dd',1); +SELECT * FROM t1 +WHERE EXISTS (SELECT c FROM t2 WHERE c=1 +UNION +SELECT c from t2 WHERE c=t1.c); +id c +aa 1 +bb 2 +cc 3 +dd 1 +INSERT INTO t2 VALUES (2); +CREATE TABLE t3 (c int); +INSERT INTO t3 VALUES (1); +SELECT * FROM t1 +WHERE EXISTS (SELECT t2.c FROM t2 JOIN t3 ON t2.c=t3.c WHERE t2.c=1 +UNION +SELECT c from t2 WHERE c=t1.c); +id c +aa 1 +bb 2 +cc 3 +dd 1 +DROP TABLE t1,t2,t3; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 0b658f746a4..c6dd34b5172 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2508,3 +2508,37 @@ SELECT SQL_NO_CACHE COUNT(*) FROM t1) t; DROP TABLE t1,t2; + +# +# Bug #25219: EXIST subquery with UNION over a mix of +# correlated and uncorrelated selects +# + +CREATE TABLE t1 (id char(4) PRIMARY KEY, c int); +CREATE TABLE t2 (c int); + +INSERT INTO t1 VALUES ('aa', 1); +INSERT INTO t2 VALUES (1); + +SELECT * FROM t1 + WHERE EXISTS (SELECT c FROM t2 WHERE c=1 + UNION + SELECT c from t2 WHERE c=t1.c); + +INSERT INTO t1 VALUES ('bb', 2), ('cc', 3), ('dd',1); + +SELECT * FROM t1 + WHERE EXISTS (SELECT c FROM t2 WHERE c=1 + UNION + SELECT c from t2 WHERE c=t1.c); + +INSERT INTO t2 VALUES (2); +CREATE TABLE t3 (c int); +INSERT INTO t3 VALUES (1); + +SELECT * FROM t1 + WHERE EXISTS (SELECT t2.c FROM t2 JOIN t3 ON t2.c=t3.c WHERE t2.c=1 + UNION + SELECT c from t2 WHERE c=t1.c); + +DROP TABLE t1,t2,t3; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index caf3e6479f9..3033c7f360c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -410,7 +410,10 @@ MY_LOCALE *my_locale_by_name(const char *name); #define UNCACHEABLE_EXPLAIN 8 /* Don't evaluate subqueries in prepare even if they're not correlated */ #define UNCACHEABLE_PREPARE 16 -/* Used to chack GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ +/* For uncorrelated SELECT in an UNION with some correlated SELECTs */ +#define UNCACHEABLE_UNITED 32 + +/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ #define UNDEF_POS (-1) #ifdef EXTRA_DEBUG /* diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3ae95ef9036..87793849094 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1371,9 +1371,17 @@ void st_select_lex::mark_as_dependent(SELECT_LEX *last) if (!(s->uncacheable & UNCACHEABLE_DEPENDENT)) { // Select is dependent of outer select - s->uncacheable|= UNCACHEABLE_DEPENDENT; + s->uncacheable= (s->uncacheable & ~UNCACHEABLE_UNITED) | + UNCACHEABLE_DEPENDENT; SELECT_LEX_UNIT *munit= s->master_unit(); - munit->uncacheable|= UNCACHEABLE_DEPENDENT; + munit->uncacheable= (munit->uncacheable & ~UNCACHEABLE_UNITED) | + UNCACHEABLE_DEPENDENT; + for (SELECT_LEX *sl= munit->first_select(); sl ; sl= sl->next_select()) + { + if (sl != s && + !(sl->uncacheable & (UNCACHEABLE_DEPENDENT | UNCACHEABLE_UNITED))) + sl->uncacheable|= UNCACHEABLE_UNITED; + } } is_correlated= TRUE; this->master_unit()->item->is_correlated= TRUE; From 9d4f594ac9829619636a177da6a2e7d0960783c9 Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Fri, 19 Jan 2007 11:58:29 +0100 Subject: [PATCH 47/73] cmd-line-utils/readline/undo.c : Replace an "uint" cast by the expanded "unsigned int" (compile problem on QNX). --- cmd-line-utils/readline/undo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-line-utils/readline/undo.c b/cmd-line-utils/readline/undo.c index b4b5a6511ba..4d256f492b8 100644 --- a/cmd-line-utils/readline/undo.c +++ b/cmd-line-utils/readline/undo.c @@ -175,7 +175,7 @@ _rl_fix_last_undo_of_type (type, start, end) for (rl = rl_undo_list; rl; rl = rl->next) { - if (rl->what == (uint) type) + if (rl->what == (unsigned int) type) { rl->start = start; rl->end = end; From 009324b12b0b2092b902d056cb8a2a4f77e9212b Mon Sep 17 00:00:00 2001 From: "msvensson@pilot.mysql.com" <> Date: Fri, 19 Jan 2007 12:03:03 +0100 Subject: [PATCH 48/73] Use $MYSQLTESTVARDIR --- mysql-test/r/symlink.result | 4 ++-- mysql-test/t/symlink.test | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/symlink.result b/mysql-test/r/symlink.result index c64d34aa80c..891efb7a0cf 100644 --- a/mysql-test/r/symlink.result +++ b/mysql-test/r/symlink.result @@ -115,12 +115,12 @@ show create table t1; Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQLTEST_VARDIR/log/' show create table t1; Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( `a` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQLTEST_VARDIR/log/' create table t1 (a int) engine=myisam select 42 a; select * from t1; a diff --git a/mysql-test/t/symlink.test b/mysql-test/t/symlink.test index 94890eab93d..9a498eaadc9 100644 --- a/mysql-test/t/symlink.test +++ b/mysql-test/t/symlink.test @@ -147,20 +147,20 @@ connect (session2,localhost,root,,); connection session1; disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 9 a; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQLTEST_VARDIR/log" select 9 a; enable_query_log; # If running test suite with a non standard tmp dir, the "show create table" # will print "DATA_DIRECTORY=". Use replace_result to mask it out ---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR show create table t1; connection session2; disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 99 a; +eval create temporary table t1 (a int) engine=myisam data directory="$MYSQLTEST_VARDIR/log" select 99 a; enable_query_log; # If running test suite with a non standard tmp dir, the "show create table" # will print "DATA_DIRECTORY=". Use replace_result to mask it out ---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR show create table t1; connection default; From 8b77bd979de54f1b3212fcdb9d282a9c0b62a9e5 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Fri, 19 Jan 2007 13:06:22 +0100 Subject: [PATCH 49/73] After merge fix --- mysql-test/r/myisam.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 5629f0bc22a..76a5ce16293 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -1640,5 +1640,5 @@ DROP TABLE t1; CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=5100100100; SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 MyISAM 9 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4294967295 avg_row_length=70100 +t1 MyISAM 10 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4294967295 avg_row_length=70100 DROP TABLE t1; From d7d5db64ec83dc83728803bc9756dc53668da3c0 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 19 Jan 2007 18:34:09 +0300 Subject: [PATCH 50/73] Bug#25172: Not checked buffer size leads to a server crash. After fix for bug#21798 JOIN stores the pointer to the buffer for sorting fields. It is used while sorting for grouping and for ordering. If ORDER BY clause has more elements then the GROUP BY clause then a memory overrun occurs. Now the length of the ORDER BY list is always passed to the make_unireg_sortorder() function and it allocates buffer big enough to be used for bigger list. --- mysql-test/r/select.result | 8 ++++++++ mysql-test/t/select.test | 11 +++++++++++ sql/sql_delete.cc | 2 +- sql/sql_select.cc | 12 ++++++++---- sql/sql_table.cc | 2 +- sql/sql_update.cc | 2 +- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 44063c1e890..d890510ee67 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3611,3 +3611,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 range si,ai si 5 NULL 2 Using where 1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where DROP TABLE t1,t2,t3; +CREATE TABLE t1 ( f1 int primary key, f2 int, f3 int, f4 int, f5 int, f6 int, checked_out int); +CREATE TABLE t2 ( f11 int PRIMARY KEY ); +INSERT INTO t1 VALUES (1,1,1,0,0,0,0),(2,1,1,3,8,1,0),(3,1,1,4,12,1,0); +INSERT INTO t2 VALUES (62); +SELECT * FROM t1 LEFT JOIN t2 ON f11 = t1.checked_out GROUP BY f1 ORDER BY f2, f3, f4, f5 LIMIT 0, 1; +f1 f2 f3 f4 f5 f6 checked_out f11 +1 1 1 0 0 0 0 NULL +DROP TABLE t1, t2; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 0c82cef867f..5288151f330 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -3092,3 +3092,14 @@ SELECT t3.a FROM t1,t2,t3 t3.c IN ('bb','ee'); DROP TABLE t1,t2,t3; + +# +# Bug#25172: Not checked buffer size leads to a server crash +# +CREATE TABLE t1 ( f1 int primary key, f2 int, f3 int, f4 int, f5 int, f6 int, checked_out int); +CREATE TABLE t2 ( f11 int PRIMARY KEY ); +INSERT INTO t1 VALUES (1,1,1,0,0,0,0),(2,1,1,3,8,1,0),(3,1,1,4,12,1,0); +INSERT INTO t2 VALUES (62); +SELECT * FROM t1 LEFT JOIN t2 ON f11 = t1.checked_out GROUP BY f1 ORDER BY f2, f3, f4, f5 LIMIT 0, 1; +DROP TABLE t1, t2; + diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7752ac80adb..e8291edda5d 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -142,7 +142,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (order && order->elements) { - uint length; + uint length= 0; SORT_FIELD *sortorder; TABLE_LIST tables; List fields; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 93b29c05fd5..b03c4556279 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12262,7 +12262,7 @@ static int create_sort_index(THD *thd, JOIN *join, ORDER *order, ha_rows filesort_limit, ha_rows select_limit) { - uint length; + uint length= 0; ha_rows examined_rows; TABLE *table; SQL_SELECT *select; @@ -12283,8 +12283,10 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, !(join->select_options & SELECT_BIG_RESULT)) && test_if_skip_sort_order(tab,order,select_limit,0)) DBUG_RETURN(0); + for (ORDER *ord= join->order; ord; ord= ord->next) + length++; if (!(join->sortorder= - make_unireg_sortorder(order,&length,join->sortorder))) + make_unireg_sortorder(order, &length, join->sortorder))) goto err; /* purecov: inspected */ table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), @@ -12690,8 +12692,10 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length, for (ORDER *tmp = order; tmp; tmp=tmp->next) count++; if (!sortorder) - sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1)); - pos=sort=sortorder; + sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD) * + (max(count, *length) + 1)); + pos= sort= sortorder; + if (!pos) return 0; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b3bd3182a59..fd52b9becf6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3883,7 +3883,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, Copy_field *copy,*copy_end; ulong found_count,delete_count; THD *thd= current_thd; - uint length; + uint length= 0; SORT_FIELD *sortorder; READ_RECORD info; TABLE_LIST tables; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index abffd704188..76d4847f923 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -304,7 +304,7 @@ int mysql_update(THD *thd, Doing an ORDER BY; Let filesort find and sort the rows we are going to update */ - uint length; + uint length= 0; SORT_FIELD *sortorder; ha_rows examined_rows; From 7b1a94ef78956cdcda7994faafafa5f349f6f123 Mon Sep 17 00:00:00 2001 From: "dlenev@mockturtle.local" <> Date: Fri, 19 Jan 2007 23:15:59 +0300 Subject: [PATCH 51/73] Fix for bug #25044 "ALTER TABLE ... ENABLE KEYS acquires global 'opening tables' lock." Execution of ALTER TABLE ... ENABLE KEYS on a table (which can take rather long time) prevented concurrent execution of all statements using tables. The problem was caused by the fact that we were holding LOCK_open mutex during whole duration of this statement and particularly during call to handler::enable_indexes(). This behavior was introduced as part of the fix for bug 14262 "SP: DROP PROCEDURE|VIEW (maybe more) write to binlog too late (race cond)" The patch simply restores old behavior. Note that we can safely do this as this operation takes exclusive lock (similar to name-lock) which blocks both DML and DDL on the table being altered. It also introduces mysql-test/include/wait_show_pattern.inc helper script which is used to make test-case for this bug robust enough. --- mysql-test/include/wait_show_pattern.inc | 51 +++++++++++++++++++ mysql-test/include/wait_slave_status.inc | 45 +++-------------- mysql-test/r/alter_table-big.result | 18 +++++++ mysql-test/t/alter_table-big.test | 62 ++++++++++++++++++++++++ sql/sql_table.cc | 25 +++++++++- 5 files changed, 162 insertions(+), 39 deletions(-) create mode 100644 mysql-test/include/wait_show_pattern.inc create mode 100644 mysql-test/r/alter_table-big.result create mode 100644 mysql-test/t/alter_table-big.test diff --git a/mysql-test/include/wait_show_pattern.inc b/mysql-test/include/wait_show_pattern.inc new file mode 100644 index 00000000000..c9f84ce7f08 --- /dev/null +++ b/mysql-test/include/wait_show_pattern.inc @@ -0,0 +1,51 @@ +# include/wait_show_pattern.inc +# +# SUMMARY +# +# Waits until output produced by SHOW statement which particular type is +# specified as parameter matches certain pattern or maximum time reached. +# +# NOTES +# +# Only the first row produced by the parameter statement is checked. +# +# USAGE +# +# let $show_type= ; +# let $show_pattern= 'Pattern to be used for LIKE matching'; +# --source wait_show_pattern.inc +# +# EXAMPLES +# +# alter_table-big.test, wait_slave_status.inc +# +# SEE ALSO +# +# wait_slave_status.inc, wait_condition.inc (>=5.1) +# +############################################################################### + +--disable_query_log + +# We accept to wait maximum 30 seconds (0.2 sec/loop). +let $wait_counter= 150; +while ($wait_counter) +{ + let $result= `SHOW $show_type`; + let $success= `SELECT '$result' LIKE $show_pattern`; + if ($success) + { + let $wait_counter= 0; + } + if (!$success) + { + real_sleep 0.2; + dec $wait_counter; + } +} +if (!$success) +{ + echo Timeout in wait_show_pattern.inc \$show_type= $show_type \$show_pattern= $show_pattern (\$result= '$result'); +} + +--enable_query_log diff --git a/mysql-test/include/wait_slave_status.inc b/mysql-test/include/wait_slave_status.inc index 7d3636e673c..d8d048527cf 100644 --- a/mysql-test/include/wait_slave_status.inc +++ b/mysql-test/include/wait_slave_status.inc @@ -104,50 +104,21 @@ eval SELECT "let \$result_pattern= $result_pattern ;" AS ""; SELECT '--source include/wait_slave_status.inc' AS ""; -# We accept to wait maximum 30 seconds (0.2 sec/loop). -let $max_wait= 150; -while ($max_wait) -{ - let $my_val= `SHOW SLAVE STATUS`; - # Now we have the first record of the SHOW result set as one fat string - # within the variable $my_val. - - eval SET @my_val = '$my_val'; - # DEBUG eval SELECT @my_val AS "response to SHOW SLAVE STATUS"; - - eval SELECT @my_val LIKE $result_pattern INTO @success; - # @success is '1' if we have a match - # '0' if we have no match - # DEBUG SELECT @success; - - let $success= `SELECT @success`; - let $no_success= `SELECT @success = 0`; - if ($success) - { - # We reached the expected result and want to jump out of the loop - # without unneeded sleeps. - # Attention: Do not set $max_wait to 0, because "while" with negative value - # does not work. - let $max_wait= 1; - } - if ($no_success) - { - # We did not reach the expected result and will have to sleep again - # or jump out of the loop, when max_wait is exhausted. - real_sleep 0.2; - } - dec $max_wait; -} +let $show_type= SLAVE STATUS; +let $show_pattern= $result_pattern; --enable_query_log -if ($no_success) + +--source include/wait_show_pattern.inc + +if (!$success) { let $message= ! Attention: Timeout in wait_slave_status.inc. | Possible reasons with decreasing probability: - | - The LIKE pattern ($result_pattern) is wrong, because the + | - The LIKE pattern is wrong, because the | testcase was altered or the layout of the | SHOW SLAVE STATUS result set changed. | - There is a new bug within the replication. - | - We met an extreme testing environment and $max_wait is + | - We met an extreme testing environment and timeout is | too small.; --source include/show_msg80.inc --echo DEBUG INFO START (wait_slave_status.inc): diff --git a/mysql-test/r/alter_table-big.result b/mysql-test/r/alter_table-big.result new file mode 100644 index 00000000000..873978c60de --- /dev/null +++ b/mysql-test/r/alter_table-big.result @@ -0,0 +1,18 @@ +drop table if exists t1, t2; +create table t1 (n1 int, n2 int, n3 int, +key (n1, n2, n3), +key (n2, n3, n1), +key (n3, n1, n2)); +create table t2 (i int); +alter table t1 disable keys; +reset master; +alter table t1 enable keys;; +insert into t2 values (1); +insert into t1 values (1, 1, 1); +show binlog events in 'master-bin.000001' from 98; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; insert into t2 values (1) +master-bin.000001 # Query 1 # use `test`; alter table t1 enable keys +master-bin.000001 # Query 1 # use `test`; insert into t1 values (1, 1, 1) +drop tables t1, t2; +End of 5.0 tests diff --git a/mysql-test/t/alter_table-big.test b/mysql-test/t/alter_table-big.test new file mode 100644 index 00000000000..9a773f48a9c --- /dev/null +++ b/mysql-test/t/alter_table-big.test @@ -0,0 +1,62 @@ +# In order to be more or less robust test for bug#25044 has to take +# significant time (e.g. about 9 seconds on my (Dmitri's) computer) +# so we probably want execute it only in --big-test mode. +# Also in 5.1 this test will require statement-based binlog. +--source include/big_test.inc + + +# +# Test for bug #25044 "ALTER TABLE ... ENABLE KEYS acquires global +# 'opening tables' lock". +# +# ALTER TABLE ... ENABLE KEYS should not acquire LOCK_open mutex for +# the whole its duration as it prevents other queries from execution. +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +connect (addconroot, localhost, root,,); +connection default; +create table t1 (n1 int, n2 int, n3 int, + key (n1, n2, n3), + key (n2, n3, n1), + key (n3, n1, n2)); +create table t2 (i int); + +# Populating 't1' table with keys disabled, so ALTER TABLE .. ENABLE KEYS +# will run for some time +alter table t1 disable keys; +--disable_query_log +insert into t1 values (RAND()*1000,RAND()*1000,RAND()*1000); +let $1=19; +while ($1) +{ + eval insert into t1 select RAND()*1000,RAND()*1000,RAND()*1000 from t1; + dec $1; +} +--enable_query_log + +# Later we use binlog to check the order in which statements are +# executed so let us reset it first. +reset master; +--send alter table t1 enable keys; +connection addconroot; +let $show_type= PROCESSLIST; +let $show_pattern= '%Repair by sorting%alter table t1 enable keys%'; +--source include/wait_show_pattern.inc +# This statement should not be blocked by in-flight ALTER and therefore +# should be executed and written to binlog before ALTER TABLE ... ENABLE KEYS +# finishes. +insert into t2 values (1); +# And this should wait until the end of ALTER TABLE ... ENABLE KEYS. +insert into t1 values (1, 1, 1); +connection default; +--reap +# Check that statements were executed/binlogged in correct order. +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 98; + +# Clean up +drop tables t1, t2; + + +--echo End of 5.0 tests diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b3bd3182a59..88c9be9ff59 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3146,19 +3146,30 @@ view_err: if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) && !table->s->tmp_table) // no need to touch frm { - VOID(pthread_mutex_lock(&LOCK_open)); - switch (alter_info->keys_onoff) { case LEAVE_AS_IS: error= 0; break; case ENABLE: + /* + wait_while_table_is_used() ensures that table being altered is + opened only by this thread and that TABLE::TABLE_SHARE::version + of TABLE object corresponding to this table is 0. + The latter guarantees that no DML statement will open this table + until ALTER TABLE finishes (i.e. until close_thread_tables()) + while the fact that the table is still open gives us protection + from concurrent DDL statements. + */ + 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->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); /* COND_refresh will be signaled in close_thread_tables() */ break; case DISABLE: + 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->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); /* COND_refresh will be signaled in close_thread_tables() */ break; @@ -3171,6 +3182,16 @@ view_err: error= 0; } + VOID(pthread_mutex_lock(&LOCK_open)); + /* + Unlike to the above case close_cached_table() below will remove ALL + instances of TABLE from table cache (it will also remove table lock + held by this thread). So to make actual table renaming and writing + to binlog atomic we have to put them into the same critical section + protected by LOCK_open mutex. This also removes gap for races between + access() and mysql_rename_table() calls. + */ + if (!error && (new_name != table_name || new_db != db)) { thd->proc_info="rename"; From e921ac7afcb98fbedf34890fc17a56bfc70a8b2b Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Mon, 22 Jan 2007 15:14:38 +0300 Subject: [PATCH 52/73] Bug#25123: ON DUPLICATE KEY clause allows fields not from the insert table. When inserting into a join-based view the update fields from the ON DUPLICATE KEY UPDATE wasn't checked to be from the table being inserted into and were silently ignored. The new check_view_single_update() function is added to check that insert/update fields are being from the same single table of the view. --- mysql-test/r/insert.result | 21 +++++++++ mysql-test/t/insert.test | 18 ++++++++ sql/sql_insert.cc | 93 ++++++++++++++++++++++++++++++-------- 3 files changed, 112 insertions(+), 20 deletions(-) diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result index 78ef6fbccba..7900e0b7695 100644 --- a/mysql-test/r/insert.result +++ b/mysql-test/r/insert.result @@ -325,3 +325,24 @@ select row_count(); row_count() 1 drop table t1; +create table t1 (f1 int unique, f2 int); +create table t2 (f3 int, f4 int); +create view v1 as select * from t1, t2 where f1= f3; +insert into t1 values (1,11), (2,22); +insert into t2 values (1,12), (2,24); +insert into v1 (f1) values (3) on duplicate key update f3= f3 + 10; +ERROR HY000: Can not modify more than one base table through a join view 'test.v1' +insert into v1 (f1) values (3) on duplicate key update f1= f3 + 10; +select * from t1; +f1 f2 +1 11 +2 22 +3 NULL +insert into v1 (f1) values (3) on duplicate key update f1= f3 + 10; +select * from t1; +f1 f2 +1 11 +2 22 +12 NULL +drop view v1; +drop table t1,t2; diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test index 029c4d19e63..0a8e184ea5c 100644 --- a/mysql-test/t/insert.test +++ b/mysql-test/t/insert.test @@ -198,3 +198,21 @@ select row_count(); insert into t1 values (5, 5) on duplicate key update data= data + 10; select row_count(); drop table t1; + +# +# Bug#25123: ON DUPLICATE KEY clause allows fields not from the insert table +# +create table t1 (f1 int unique, f2 int); +create table t2 (f3 int, f4 int); +create view v1 as select * from t1, t2 where f1= f3; +insert into t1 values (1,11), (2,22); +insert into t2 values (1,12), (2,24); +--error 1393 +insert into v1 (f1) values (3) on duplicate key update f3= f3 + 10; +insert into v1 (f1) values (3) on duplicate key update f1= f3 + 10; +select * from t1; +insert into v1 (f1) values (3) on duplicate key update f1= f3 + 10; +select * from t1; +drop view v1; +drop table t1,t2; + diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index be6707c80a9..0e81730e772 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -80,6 +80,65 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); #define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0)) #endif +/* + Check that insert/update fields are from the same single table of a view. + + SYNOPSIS + check_view_single_update() + fields The insert/update fields to be checked. + view The view for insert. + map [in/out] The insert table map. + + DESCRIPTION + This function is called in 2 cases: + 1. to check insert fields. In this case *map will be set to 0. + Insert fields are checked to be all from the same single underlying + table of the given view. Otherwise the error is thrown. Found table + map is returned in the map parameter. + 2. to check update fields of the ON DUPLICATE KEY UPDATE clause. + In this case *map contains table_map found on the previous call of + the function to check insert fields. Update fields are checked to be + from the same table as the insert fields. + + RETURN + 0 OK + 1 Error +*/ + +bool check_view_single_update(List &fields, TABLE_LIST *view, + table_map *map) +{ + /* it is join view => we need to find the table for update */ + List_iterator_fast it(fields); + Item *item; + TABLE_LIST *tbl= 0; // reset for call to check_single_table() + table_map tables= 0; + + while ((item= it++)) + tables|= item->used_tables(); + + /* Check found map against provided map */ + if (*map) + { + if (tables != *map) + goto error; + return FALSE; + } + + if (view->check_single_table(&tbl, tables, view) || tbl == 0) + goto error; + + view->table= tbl->table; + *map= tables; + + return FALSE; + +error: + my_error(ER_VIEW_MULTIUPDATE, MYF(0), + view->view_db.str, view->view_name.str); + return TRUE; +} + /* Check if insert fields are correct. @@ -104,7 +163,7 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); static int check_insert_fields(THD *thd, TABLE_LIST *table_list, List &fields, List &values, - bool check_unique) + bool check_unique, table_map *map) { TABLE *table= table_list->table; @@ -177,21 +236,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (table_list->effective_algorithm == VIEW_ALGORITHM_MERGE) { - /* it is join view => we need to find table for update */ - List_iterator_fast it(fields); - Item *item; - TABLE_LIST *tbl= 0; // reset for call to check_single_table() - table_map map= 0; - - while ((item= it++)) - map|= item->used_tables(); - if (table_list->check_single_table(&tbl, map, table_list) || tbl == 0) - { - my_error(ER_VIEW_MULTIUPDATE, MYF(0), - table_list->view_db.str, table_list->view_name.str); + if (check_view_single_update(fields, table_list, map)) return -1; - } - table_list->table= table= tbl->table; + table= table_list->table; } if (check_unique && thd->dupp_field) @@ -241,7 +288,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, */ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, - List &update_fields) + List &update_fields, table_map *map) { TABLE *table= insert_table_list->table; query_id_t timestamp_query_id; @@ -264,6 +311,10 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, if (setup_fields(thd, 0, update_fields, 1, 0, 0)) return -1; + if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && + check_view_single_update(update_fields, insert_table_list, map)) + return -1; + if (table->timestamp_field) { /* Don't set timestamp column if this is modified. */ @@ -910,6 +961,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, Name_resolution_context_state ctx_state; bool insert_into_view= (table_list->view != 0); bool res= 0; + table_map map= 0; DBUG_ENTER("mysql_prepare_insert"); DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d", (ulong)table_list, (ulong)table, @@ -959,12 +1011,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, /* Prepare the fields in the statement. */ if (values && !(res= check_insert_fields(thd, context->table_list, fields, *values, - !insert_into_view) || + !insert_into_view, &map) || setup_fields(thd, 0, *values, 0, 0, 0)) && duplic == DUP_UPDATE) { select_lex->no_wrap_view_item= TRUE; - res= check_update_fields(thd, context->table_list, update_fields); + res= check_update_fields(thd, context->table_list, update_fields, &map); select_lex->no_wrap_view_item= FALSE; /* When we are not using GROUP BY we can refer to other tables in the @@ -2286,6 +2338,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) { LEX *lex= thd->lex; int res; + table_map map= 0; SELECT_LEX *lex_current_select_save= lex->current_select; DBUG_ENTER("select_insert::prepare"); @@ -2297,7 +2350,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) */ lex->current_select= &lex->select_lex; res= check_insert_fields(thd, table_list, *fields, values, - !insert_into_view) || + !insert_into_view, &map) || setup_fields(thd, 0, values, 0, 0, 0); if (info.handle_duplicates == DUP_UPDATE) @@ -2315,7 +2368,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) lex->select_lex.no_wrap_view_item= TRUE; res= res || check_update_fields(thd, context->table_list, - *info.update_fields); + *info.update_fields, &map); lex->select_lex.no_wrap_view_item= FALSE; /* When we are not using GROUP BY we can refer to other tables in the From afaebb6d62ab55626222dd75552af3af5ef32e48 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/june.mysql.com" <> Date: Mon, 22 Jan 2007 16:34:58 +0400 Subject: [PATCH 53/73] BUG#24401 - MySQL server crashes if you try to retrieve data from corrupted table Accessing a table with corrupted column definition results in server crash. This is fixed by refusing to open such tables. Affects MyISAM only. No test case, since it requires crashed table. --- myisam/mi_open.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 047686278b4..b007eb63e63 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -435,6 +435,13 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) offset+=share->rec[i].length; } share->rec[i].type=(int) FIELD_LAST; /* End marker */ + if (offset > share->base.reclength) + { + /* purecov: begin inspected */ + my_errno= HA_ERR_CRASHED; + goto err; + /* purecov: end */ + } if (! lock_error) { From c1243725503d82d9b3b33f9ee3616c4fc5bd6376 Mon Sep 17 00:00:00 2001 From: "df@kahlann.erinye.com" <> Date: Mon, 22 Jan 2007 16:32:57 +0100 Subject: [PATCH 54/73] after-merge fix --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 8cb33f9aaac..436030383e1 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1046,7 +1046,7 @@ sub command_line_setup () { # On some operating systems, there is a limit to the length of a # UNIX domain socket's path far below PATH_MAX, so try to avoid long # socket path names. - $sockdir = tempdir(CLEANUP => 1) if ( length($sockdir) > 80 ); + $sockdir = tempdir(CLEANUP => 0) if ( length($sockdir) > 80 ); # Put this into a hash, will be a C struct From ddffdd1c7428be7a769d14214d7a5121cecbe57b Mon Sep 17 00:00:00 2001 From: "anozdrin/alik@alik." <> Date: Mon, 22 Jan 2007 20:00:27 +0300 Subject: [PATCH 55/73] Enabling im_daemon_life_cycle.imtest in team tree. Do not propagate this change into main trees. --- mysql-test/t/disabled.def | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 3213bd4eb5b..53b73363c22 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -10,7 +10,6 @@ # ############################################################################## -im_daemon_life_cycle : Bug#24415 see note: [19 Dec 23:17] Trudy Pelzer ndb_load : Bug#17233 user_limits : Bug#23921 random failure of user_limits.test flush2 : Bug#24805 Pushbuild can't handle test with --disable-log-bin From 9605aee4fe4468d4df5c4c526e0dab27c157a715 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.mysql.com" <> Date: Tue, 23 Jan 2007 00:34:00 +0700 Subject: [PATCH 56/73] bug#25746 ndb: 4209 error with 2 VARCHAR primary keys - make sure keys are copied correctly when varchar has 2 length bytes - test case --- mysql-test/r/ndb_basic.result | 16 ++++++++++++++++ mysql-test/t/ndb_basic.test | 19 +++++++++++++++++++ sql/ha_ndbcluster.cc | 26 ++++++++++++++++---------- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/ndb_basic.result b/mysql-test/r/ndb_basic.result index d2111db24fe..fab10867acd 100644 --- a/mysql-test/r/ndb_basic.result +++ b/mysql-test/r/ndb_basic.result @@ -749,3 +749,19 @@ f1 f2 f3 222222 bbbbbb 2 drop table t1; Illegal ndb error code: 1186 +CREATE TABLE t1 ( +a VARBINARY(40) NOT NULL, +b VARCHAR (256) CHARACTER SET UTF8 NOT NULL, +c VARCHAR(256) CHARACTER SET UTF8 NOT NULL, +PRIMARY KEY (b,c)) ENGINE=ndbcluster; +INSERT INTO t1 VALUES +("a","ab","abc"),("b","abc","abcd"),("c","abc","ab"),("d","ab","ab"),("e","abc","abc"); +SELECT * FROM t1 ORDER BY a; +a b c +a ab abc +b abc abcd +c abc ab +d ab ab +e abc abc +DROP TABLE t1; +End of 5.0 tests diff --git a/mysql-test/t/ndb_basic.test b/mysql-test/t/ndb_basic.test index 6c1a4e44f4b..a1ceddcd183 100644 --- a/mysql-test/t/ndb_basic.test +++ b/mysql-test/t/ndb_basic.test @@ -710,3 +710,22 @@ drop table t1; --error 1 --exec $MY_PERROR --ndb 1186 2>&1 +# +# Bug #25746 - VARCHAR UTF8 PK issue +# - prior to bugfix 4209, illegal length parameter would be +# returned in SELECT * + +CREATE TABLE t1 ( +a VARBINARY(40) NOT NULL, +b VARCHAR (256) CHARACTER SET UTF8 NOT NULL, +c VARCHAR(256) CHARACTER SET UTF8 NOT NULL, +PRIMARY KEY (b,c)) ENGINE=ndbcluster; +INSERT INTO t1 VALUES +("a","ab","abc"),("b","abc","abcd"),("c","abc","ab"),("d","ab","ab"),("e","abc","abc"); +SELECT * FROM t1 ORDER BY a; +DROP TABLE t1; + +# End of 5.0 tests +--echo End of 5.0 tests + + diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index f9984b27077..6cff3637cf3 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3144,20 +3144,26 @@ void ha_ndbcluster::position(const byte *record) size_t len = key_part->length; const byte * ptr = record + key_part->offset; Field *field = key_part->field; - if ((field->type() == MYSQL_TYPE_VARCHAR) && - ((Field_varstring*)field)->length_bytes == 1) + if (unlikely(field->type() == MYSQL_TYPE_VARCHAR)) { - /** - * Keys always use 2 bytes length - */ - buff[0] = ptr[0]; - buff[1] = 0; - memcpy(buff+2, ptr + 1, len); - len += 2; + if (((Field_varstring*)field)->length_bytes == 1) + { + /** + * Keys always use 2 bytes length + */ + buff[0] = ptr[0]; + buff[1] = 0; + memcpy(buff+2, ptr + 1, len); + } + else + { + memcpy(buff, ptr, len + 2); + } + len += 2; } else { - memcpy(buff, ptr, len); + memcpy(buff, ptr, len); } buff += len; } From 4e0760067a0dc994f3d3d4e2c6c6bd3e72a62324 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Mon, 22 Jan 2007 22:08:51 +0300 Subject: [PATCH 57/73] sql_prepare.cc: Post fix for bug#25123. --- sql/sql_prepare.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index e9f33a172c4..90442ef4c68 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1676,7 +1676,7 @@ static bool check_prepared_statement(Prepared_statement *stmt, case SQLCOM_INSERT: res= mysql_test_insert(stmt, tables, lex->field_list, lex->many_values, - select_lex->item_list, lex->value_list, + lex->update_list, lex->value_list, lex->duplicates); break; From 884713fb08418ae819ba39a5429a3dd18586c423 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Mon, 22 Jan 2007 23:04:14 +0300 Subject: [PATCH 58/73] sql_select.cc: Code cleanup after fix for bug#23417. --- sql/sql_select.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b03c4556279..7bced1719eb 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8766,8 +8766,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, if (type != Item::FIELD_ITEM && item->real_item()->type() == Item::FIELD_ITEM && - (item->type() != Item::REF_ITEM || - !((Item_ref *) item)->depended_from)) + !((Item_ref *) item)->depended_from) { orig_item= item; item= item->real_item(); @@ -13423,7 +13422,7 @@ count_field_types(TMP_TABLE_PARAM *param, List &fields, Item::Type type=field->type(); Item::Type real_type= field->real_item()->type(); if (type == Item::FIELD_ITEM || (real_type == Item::FIELD_ITEM && - (type != Item::REF_ITEM || !((Item_ref *) field)->depended_from))) + !((Item_ref *) field)->depended_from)) param->field_count++; else if (real_type == Item::SUM_FUNC_ITEM) { From 9659c11b6eee9e3ddf7d18b40d9afbc00e310bf2 Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Mon, 22 Jan 2007 14:23:53 -0800 Subject: [PATCH 59/73] Fixed bug #25637: LEFT JOIN with BOOLEAN FULLTEXT loses left table matches. The bug is actually a duplicate of the bug 14708. Down-ported the fix for 14708 from 5.0. Merged the test case for bug 14708 from 5.0. --- mysql-test/r/fulltext_left_join.result | 40 +++++++++++++++++++++++++ mysql-test/t/fulltext_left_join.test | 41 ++++++++++++++++++++++++++ sql/item_func.cc | 2 +- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/fulltext_left_join.result b/mysql-test/r/fulltext_left_join.result index f3dad290525..fdf11c14cc4 100644 --- a/mysql-test/r/fulltext_left_join.result +++ b/mysql-test/r/fulltext_left_join.result @@ -50,3 +50,43 @@ venue_id venue_text dt name entity_id 1 a1 2003-05-23 19:30:00 aberdeen town hall 1 NULL a2 2003-05-23 19:30:00 NULL NULL drop table t1,t2; +create table t1 (id int not null primary key, d char(200) not null, e char(200)); +insert into t1 values (1, 'aword', null), (2, 'aword', 'bword'), (3, 'bword', null), (4, 'bword', 'aword'), (5, 'aword and bword', null); +select * from t1 where match(d, e) against ('+aword +bword' in boolean mode); +id d e +2 aword bword +4 bword aword +5 aword and bword NULL +create table t2 (m_id int not null, f char(200), key (m_id)); +insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); +select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); +id d e m_id f +1 aword NULL 1 bword +2 aword bword NULL NULL +3 bword NULL 3 aword +4 bword aword NULL NULL +5 aword and bword NULL 5 +drop table t1,t2; +CREATE TABLE t1 ( +id int(10) NOT NULL auto_increment, +link int(10) default NULL, +name mediumtext default NULL, +PRIMARY KEY (id), +FULLTEXT (name) +); +INSERT INTO t1 VALUES (1, 1, 'string'); +INSERT INTO t1 VALUES (2, 0, 'string'); +CREATE TABLE t2 ( +id int(10) NOT NULL auto_increment, +name mediumtext default NULL, +PRIMARY KEY (id), +FULLTEXT (name) +); +INSERT INTO t2 VALUES (1, 'string'); +SELECT t1.*, MATCH(t1.name) AGAINST('string') AS relevance +FROM t1 LEFT JOIN t2 ON t1.link = t2.id +WHERE MATCH(t1.name, t2.name) AGAINST('string' IN BOOLEAN MODE); +id link name relevance +1 1 string 0 +2 0 string 0 +DROP TABLE t1,t2; diff --git a/mysql-test/t/fulltext_left_join.test b/mysql-test/t/fulltext_left_join.test index 3bb1f0b7309..516c006b109 100644 --- a/mysql-test/t/fulltext_left_join.test +++ b/mysql-test/t/fulltext_left_join.test @@ -45,4 +45,45 @@ select * from t1 left join t2 on (venue_id = entity_id and match(name) against(' select * from t1 left join t2 on (venue_id = entity_id and match(name) against('aberdeen')) where dt = '2003-05-23 19:30:00'; drop table t1,t2; +# +# BUG#14708 +# Inconsistent treatment of NULLs in LEFT JOINed FULLTEXT matching without index +# + +create table t1 (id int not null primary key, d char(200) not null, e char(200)); +insert into t1 values (1, 'aword', null), (2, 'aword', 'bword'), (3, 'bword', null), (4, 'bword', 'aword'), (5, 'aword and bword', null); +select * from t1 where match(d, e) against ('+aword +bword' in boolean mode); +create table t2 (m_id int not null, f char(200), key (m_id)); +insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); +select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); +drop table t1,t2; + +# +# BUG#25637: LEFT JOIN with BOOLEAN FULLTEXT loses left table matches +# (this is actually the same bug as bug #14708) +# + +CREATE TABLE t1 ( + id int(10) NOT NULL auto_increment, + link int(10) default NULL, + name mediumtext default NULL, + PRIMARY KEY (id), + FULLTEXT (name) +); +INSERT INTO t1 VALUES (1, 1, 'string'); +INSERT INTO t1 VALUES (2, 0, 'string'); +CREATE TABLE t2 ( + id int(10) NOT NULL auto_increment, + name mediumtext default NULL, + PRIMARY KEY (id), + FULLTEXT (name) +); +INSERT INTO t2 VALUES (1, 'string'); + +SELECT t1.*, MATCH(t1.name) AGAINST('string') AS relevance + FROM t1 LEFT JOIN t2 ON t1.link = t2.id + WHERE MATCH(t1.name, t2.name) AGAINST('string' IN BOOLEAN MODE); + +DROP TABLE t1,t2; + # End of 4.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 635c6b4c6d6..461998477b5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3247,7 +3247,7 @@ double Item_func_match::val() if (ft_handler == NULL) DBUG_RETURN(-1.0); - if (table->null_row) /* NULL row from an outer join */ + if (key != NO_SUCH_KEY && table->null_row) /* NULL row from an outer join */ return 0.0; if (join_key) From 12911bb5c0754cdef812e1642e907f5966b64158 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.mysql.com" <> Date: Tue, 23 Jan 2007 07:12:03 +0700 Subject: [PATCH 60/73] bug#25746 ndb: 4209 error with 2 VARCHAR primary keys - post review changes --- sql/ha_ndbcluster.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 6cff3637cf3..3c3f6f4e06b 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3144,7 +3144,7 @@ void ha_ndbcluster::position(const byte *record) size_t len = key_part->length; const byte * ptr = record + key_part->offset; Field *field = key_part->field; - if (unlikely(field->type() == MYSQL_TYPE_VARCHAR)) + if (field->type() == MYSQL_TYPE_VARCHAR) { if (((Field_varstring*)field)->length_bytes == 1) { From 6825593df8767f4e7648d08cfb7b40047c8241da Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.mysql.com" <> Date: Tue, 23 Jan 2007 11:44:42 +0700 Subject: [PATCH 61/73] ndb - bug#22013 Fix bug in event handling wrt early node shutdown --- ndb/src/mgmsrv/MgmtSrvr.cpp | 48 ++++++++++++++++++++++++--------- ndb/src/ndbapi/ClusterMgr.cpp | 9 ++++--- ndb/src/ndbapi/ClusterMgr.hpp | 4 +-- ndb/src/ndbapi/SignalSender.cpp | 9 +++++++ 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/ndb/src/mgmsrv/MgmtSrvr.cpp b/ndb/src/mgmsrv/MgmtSrvr.cpp index ebef5510b55..0ee59f70885 100644 --- a/ndb/src/mgmsrv/MgmtSrvr.cpp +++ b/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -137,8 +137,11 @@ MgmtSrvr::logLevelThreadRun() m_started_nodes.erase(0, false); m_started_nodes.unlock(); - setEventReportingLevelImpl(node, req); - + if (setEventReportingLevelImpl(node, req)) + { + ndbout_c("setEventReportingLevelImpl(%d): failed", node); + } + SetLogLevelOrd ord; ord = m_nodeLogLevel[node]; setNodeLogLevelImpl(node, ord); @@ -155,10 +158,16 @@ MgmtSrvr::logLevelThreadRun() m_log_level_requests.erase(0, false); m_log_level_requests.unlock(); - if(req.blockRef == 0){ + if(req.blockRef == 0) + { req.blockRef = _ownReference; - setEventReportingLevelImpl(0, req); - } else { + if (setEventReportingLevelImpl(0, req)) + { + ndbout_c("setEventReportingLevelImpl: failed 2!"); + } + } + else + { SetLogLevelOrd ord; ord = req; setNodeLogLevelImpl(req.blockRef, ord); @@ -1376,9 +1385,6 @@ int MgmtSrvr::restartDB(bool nostart, bool initialStart, NodeId nodeId = 0; NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + waitTime; - ndbout_c(" %d", nodes.get(1)); - ndbout_c(" %d", nodes.get(2)); - while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) { if (!nodes.get(nodeId)) continue; @@ -1584,6 +1590,11 @@ MgmtSrvr::setEventReportingLevelImpl(int nodeId, } } + if (nodes.isclear()) + { + return SEND_OR_RECEIVE_FAILED; + } + int error = 0; while (!nodes.isclear()) { @@ -1600,16 +1611,24 @@ MgmtSrvr::setEventReportingLevelImpl(int nodeId, error = 1; break; } + // Since sending okToSend(true), + // there is no guarantee that NF_COMPLETEREP will come + // i.e listen also to NODE_FAILREP + case GSN_NODE_FAILREP: { + const NodeFailRep * const rep = + CAST_CONSTPTR(NodeFailRep, signal->getDataPtr()); + NdbNodeBitmask mask; + mask.assign(NdbNodeBitmask::Size, rep->theNodes); + nodes.bitANDC(mask); + break; + } + case GSN_NF_COMPLETEREP:{ const NFCompleteRep * const rep = CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); nodes.clear(rep->failedNodeId); break; } - case GSN_NODE_FAILREP:{ - // ignore, NF_COMPLETEREP will arrive later - break; - } default: report_unknown_signal(signal); return SEND_OR_RECEIVE_FAILED; @@ -1909,7 +1928,10 @@ MgmtSrvr::handleStatus(NodeId nodeId, bool alive, bool nfComplete) theData[1] = nodeId; if (alive) { - m_started_nodes.push_back(nodeId); + if (nodeTypes[nodeId] == NODE_TYPE_DB) + { + m_started_nodes.push_back(nodeId); + } rep->setEventType(NDB_LE_Connected); } else { rep->setEventType(NDB_LE_Disconnected); diff --git a/ndb/src/ndbapi/ClusterMgr.cpp b/ndb/src/ndbapi/ClusterMgr.cpp index 0aab294cd3a..2ff27ca893e 100644 --- a/ndb/src/ndbapi/ClusterMgr.cpp +++ b/ndb/src/ndbapi/ClusterMgr.cpp @@ -507,6 +507,7 @@ ClusterMgr::reportConnected(NodeId nodeId){ theNode.m_info.m_version = 0; theNode.compatible = true; theNode.nfCompleteRep = true; + theNode.m_state.startLevel = NodeState::SL_NOTHING; theFacade.ReportNodeAlive(nodeId); } @@ -518,14 +519,13 @@ ClusterMgr::reportDisconnected(NodeId nodeId){ noOfConnectedNodes--; theNodes[nodeId].connected = false; - theNodes[nodeId].m_state.m_connected_nodes.clear(); - reportNodeFailed(nodeId); + reportNodeFailed(nodeId, true); } void -ClusterMgr::reportNodeFailed(NodeId nodeId){ +ClusterMgr::reportNodeFailed(NodeId nodeId, bool disconnect){ Node & theNode = theNodes[nodeId]; @@ -536,10 +536,11 @@ ClusterMgr::reportNodeFailed(NodeId nodeId){ { theFacade.doDisconnect(nodeId); } + const bool report = (theNode.m_state.startLevel != NodeState::SL_NOTHING); theNode.m_state.startLevel = NodeState::SL_NOTHING; - if(report) + if(disconnect || report) { theFacade.ReportNodeDead(nodeId); } diff --git a/ndb/src/ndbapi/ClusterMgr.hpp b/ndb/src/ndbapi/ClusterMgr.hpp index 92fe1423f8f..32234a0b2f4 100644 --- a/ndb/src/ndbapi/ClusterMgr.hpp +++ b/ndb/src/ndbapi/ClusterMgr.hpp @@ -97,8 +97,8 @@ private: NdbMutex* clusterMgrThreadMutex; void showState(NodeId nodeId); - void reportNodeFailed(NodeId nodeId); - + void reportNodeFailed(NodeId nodeId, bool disconnect = false); + /** * Signals received */ diff --git a/ndb/src/ndbapi/SignalSender.cpp b/ndb/src/ndbapi/SignalSender.cpp index 1ed42c9c610..804ea92877d 100644 --- a/ndb/src/ndbapi/SignalSender.cpp +++ b/ndb/src/ndbapi/SignalSender.cpp @@ -19,6 +19,14 @@ #include #include +static +void +require(bool x) +{ + if (!x) + abort(); +} + SimpleSignal::SimpleSignal(bool dealloc){ memset(this, 0, sizeof(* this)); deallocSections = dealloc; @@ -145,6 +153,7 @@ SignalSender::waitFor(Uint32 timeOutMillis, T & t) { SimpleSignal * s = t.check(m_jobBuffer); if(s != 0){ + m_usedBuffer.push_back(s); return s; } From 4984af30ecbe62ab5d4bbb60a1820bad689a5372 Mon Sep 17 00:00:00 2001 From: "stewart@willster.(none)" <> Date: Tue, 23 Jan 2007 17:07:09 +1100 Subject: [PATCH 62/73] Bug #25487 deleting ndb_cluster_connection object takes long time aim is to: a) if set_connect_timeout called, timeout connect attempt (for retry on next call) after timeout period b) preserve existing blocking behaviour otherwise (for, e.g. mgmapi) Related to customer issue with long time deleting ndb_cluster_connection object. believe we're hanging on the connect(2) call until timeout (when we then realise we should exit the thread). --- ndb/include/mgmapi/mgmapi.h | 10 ++++ ndb/include/util/SocketClient.hpp | 4 ++ ndb/src/common/transporter/Transporter.cpp | 8 ++- ndb/src/common/util/SocketClient.cpp | 60 ++++++++++++++++++++-- ndb/src/mgmapi/mgmapi.cpp | 13 +++++ 5 files changed, 89 insertions(+), 6 deletions(-) diff --git a/ndb/include/mgmapi/mgmapi.h b/ndb/include/mgmapi/mgmapi.h index 2010aa8cc33..2423048f98f 100644 --- a/ndb/include/mgmapi/mgmapi.h +++ b/ndb/include/mgmapi/mgmapi.h @@ -540,6 +540,16 @@ extern "C" { */ const char *ndb_mgm_get_connectstring(NdbMgmHandle handle, char *buf, int buf_sz); + /** + * Sets the number of seconds to wait for connect(2) during ndb_mgm_connect + * Default is no timeout + * + * @param handle NdbMgmHandle + * @param seconds number of seconds + * @return non-zero on success + */ + int ndb_mgm_set_connect_timeout(NdbMgmHandle handle, unsigned int seconds); + /** * Connects to a management server. Connectstring is set by * ndb_mgm_set_connectstring(). diff --git a/ndb/include/util/SocketClient.hpp b/ndb/include/util/SocketClient.hpp index e1f1752e9a8..bb8d9b9ac41 100644 --- a/ndb/include/util/SocketClient.hpp +++ b/ndb/include/util/SocketClient.hpp @@ -23,6 +23,7 @@ class SocketClient { NDB_SOCKET_TYPE m_sockfd; struct sockaddr_in m_servaddr; + unsigned int m_connect_timeout_sec; unsigned short m_port; char *m_server_name; SocketAuthenticator *m_auth; @@ -34,6 +35,9 @@ public: m_port = port; m_servaddr.sin_port = htons(m_port); }; + void set_connect_timeout(unsigned int s) { + m_connect_timeout_sec= s; + } unsigned short get_port() { return m_port; }; char *get_server_name() { return m_server_name; }; int bind(const char* toaddress, unsigned short toport); diff --git a/ndb/src/common/transporter/Transporter.cpp b/ndb/src/common/transporter/Transporter.cpp index 339533c8d27..d1e88e303e7 100644 --- a/ndb/src/common/transporter/Transporter.cpp +++ b/ndb/src/common/transporter/Transporter.cpp @@ -79,9 +79,13 @@ Transporter::Transporter(TransporterRegistry &t_reg, if (isServer) m_socket_client= 0; else + { m_socket_client= new SocketClient(remoteHostName, s_port, new SocketAuthSimple("ndbd", "ndbd passwd")); + + m_socket_client->set_connect_timeout(m_timeOutMillis/1000); + } DBUG_VOID_RETURN; } @@ -140,9 +144,9 @@ Transporter::connect_client() { } sockfd= m_socket_client->connect(); } - + return connect_client(sockfd); -} +} bool Transporter::connect_client(NDB_SOCKET_TYPE sockfd) { diff --git a/ndb/src/common/util/SocketClient.cpp b/ndb/src/common/util/SocketClient.cpp index bb059585863..ec35fd3eb90 100644 --- a/ndb/src/common/util/SocketClient.cpp +++ b/ndb/src/common/util/SocketClient.cpp @@ -26,6 +26,7 @@ SocketClient::SocketClient(const char *server_name, unsigned short port, SocketA m_port= port; m_server_name= server_name ? strdup(server_name) : 0; m_sockfd= NDB_INVALID_SOCKET; + m_connect_timeout_sec= 0; } SocketClient::~SocketClient() @@ -58,7 +59,7 @@ SocketClient::init() if (m_sockfd == NDB_INVALID_SOCKET) { return false; } - + DBUG_PRINT("info",("NDB_SOCKET: %d", m_sockfd)); return true; @@ -104,6 +105,13 @@ SocketClient::bind(const char* bindaddress, unsigned short localport) NDB_SOCKET_TYPE SocketClient::connect(const char *toaddress, unsigned short toport) { + fd_set rset, wset; + struct timeval tval; + int r; + bool use_timeout; + socklen_t len; + int flags; + if (m_sockfd == NDB_INVALID_SOCKET) { if (!init()) { @@ -127,14 +135,58 @@ SocketClient::connect(const char *toaddress, unsigned short toport) if (Ndb_getInAddr(&m_servaddr.sin_addr, m_server_name)) return NDB_INVALID_SOCKET; } - - const int r = ::connect(m_sockfd, (struct sockaddr*) &m_servaddr, sizeof(m_servaddr)); - if (r == -1) { + + flags= fcntl(m_sockfd, F_GETFL, 0); + fcntl(m_sockfd, F_SETFL, flags | O_NONBLOCK); + + r= ::connect(m_sockfd, (struct sockaddr*) &m_servaddr, sizeof(m_servaddr)); + + if (r == 0) + goto done; // connected immediately. + + if (r < 0 && (errno != EINPROGRESS)) { NDB_CLOSE_SOCKET(m_sockfd); m_sockfd= NDB_INVALID_SOCKET; return NDB_INVALID_SOCKET; } + FD_ZERO(&rset); + FD_SET(m_sockfd, &rset); + wset= rset; + tval.tv_sec= m_connect_timeout_sec; + tval.tv_usec= 0; + use_timeout= m_connect_timeout_sec; + + if ((r= select(m_sockfd+1, &rset, &wset, NULL, + use_timeout? &tval : NULL)) == 0) + { + NDB_CLOSE_SOCKET(m_sockfd); + m_sockfd= NDB_INVALID_SOCKET; + return NDB_INVALID_SOCKET; + } + + if (FD_ISSET(m_sockfd, &rset) || FD_ISSET(m_sockfd, &wset)) + { + len= sizeof(r); + if (getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, &r, &len) < 0) + { + // Solaris got an error... different than others + NDB_CLOSE_SOCKET(m_sockfd); + m_sockfd= NDB_INVALID_SOCKET; + return NDB_INVALID_SOCKET; + } + } + else + { + // select error, probably m_sockfd not set. + NDB_CLOSE_SOCKET(m_sockfd); + m_sockfd= NDB_INVALID_SOCKET; + return NDB_INVALID_SOCKET; + } + +done: + fcntl(m_sockfd, F_SETFL, flags); + if (m_auth) { if (!m_auth->client_authenticate(m_sockfd)) { diff --git a/ndb/src/mgmapi/mgmapi.cpp b/ndb/src/mgmapi/mgmapi.cpp index 2f49efd9f58..fa7aed8b182 100644 --- a/ndb/src/mgmapi/mgmapi.cpp +++ b/ndb/src/mgmapi/mgmapi.cpp @@ -93,6 +93,7 @@ struct ndb_mgm_handle { char last_error_desc[NDB_MGM_MAX_ERR_DESC_SIZE]; int read_timeout; int write_timeout; + unsigned int connect_timeout; NDB_SOCKET_TYPE socket; @@ -159,6 +160,7 @@ ndb_mgm_create_handle() h->socket = NDB_INVALID_SOCKET; h->read_timeout = 50000; h->write_timeout = 100; + h->connect_timeout = 0; h->cfg_i = -1; h->errstream = stdout; h->m_name = 0; @@ -426,6 +428,16 @@ int ndb_mgm_is_connected(NdbMgmHandle handle) return handle->connected; } +extern "C" +int ndb_mgm_set_connect_timeout(NdbMgmHandle handle, unsigned int seconds) +{ + if(!handle) + return -1; + + handle->connect_timeout= seconds; + return 0; +} + /** * Connect to a management server */ @@ -456,6 +468,7 @@ ndb_mgm_connect(NdbMgmHandle handle, int no_retries, Uint32 i; int binderror = 0; SocketClient s(0, 0); + s.set_connect_timeout(handle->connect_timeout); if (!s.init()) { fprintf(handle->errstream, From 5b886fa35848d1dc875d0d34d9a286a6514f0c9e Mon Sep 17 00:00:00 2001 From: "stewart@willster.(none)" <> Date: Tue, 23 Jan 2007 17:19:27 +1100 Subject: [PATCH 63/73] round up Transporter connect timeout --- ndb/src/common/transporter/Transporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndb/src/common/transporter/Transporter.cpp b/ndb/src/common/transporter/Transporter.cpp index d1e88e303e7..20b6be8ce26 100644 --- a/ndb/src/common/transporter/Transporter.cpp +++ b/ndb/src/common/transporter/Transporter.cpp @@ -84,7 +84,7 @@ Transporter::Transporter(TransporterRegistry &t_reg, new SocketAuthSimple("ndbd", "ndbd passwd")); - m_socket_client->set_connect_timeout(m_timeOutMillis/1000); + m_socket_client->set_connect_timeout((m_timeOutMillis+999)/1000); } DBUG_VOID_RETURN; } From 56e2d4ffcb3615dc28eb632952101bb15f79844b Mon Sep 17 00:00:00 2001 From: "pekka@clam.ndb.mysql.com/clam.(none)" <> Date: Tue, 23 Jan 2007 12:58:10 +0100 Subject: [PATCH 64/73] ndb - bug#25562 use byte-size max_data_length() when setting blob part size --- sql/ha_ndbcluster.cc | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 3c3f6f4e06b..af097366159 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -4185,19 +4185,29 @@ static int create_ndb_column(NDBCOL &col, col.setType(NDBCOL::Text); col.setCharset(cs); } - // Use "<=" even if "<" is the exact condition - if (field->max_length() <= (1 << 8)) - goto mysql_type_tiny_blob; - else if (field->max_length() <= (1 << 16)) { - col.setInlineSize(256); - col.setPartSize(2000); - col.setStripeSize(16); + Field_blob *field_blob= (Field_blob *)field; + /* + * max_data_length is 2^8-1, 2^16-1, 2^24-1 for tiny, blob, medium. + * Tinyblob gets no blob parts. The other cases are just a crude + * way to control part size and striping. + * + * In mysql blob(256) is promoted to blob(65535) so it does not + * in fact fit "inline" in NDB. + */ + if (field_blob->max_data_length() < (1 << 8)) + goto mysql_type_tiny_blob; + else if (field_blob->max_data_length() < (1 << 16)) + { + col.setInlineSize(256); + col.setPartSize(2000); + col.setStripeSize(16); + } + else if (field_blob->max_data_length() < (1 << 24)) + goto mysql_type_medium_blob; + else + goto mysql_type_long_blob; } - else if (field->max_length() <= (1 << 24)) - goto mysql_type_medium_blob; - else - goto mysql_type_long_blob; break; mysql_type_medium_blob: case MYSQL_TYPE_MEDIUM_BLOB: From 2b63f106014a825b816c4506baf4c2aac1e9e99e Mon Sep 17 00:00:00 2001 From: "dlenev@mockturtle.local" <> Date: Tue, 23 Jan 2007 15:03:48 +0300 Subject: [PATCH 65/73] Proposed fix for bug#24491 "using alias from source table in insert ... on duplicate key". INSERT ... SELECT ... ON DUPLICATE KEY UPDATE which was used in stored routine or as prepared statement and which in its ON DUPLICATE KEY clause erroneously tried to assign value to a column mentioned only in its SELECT part was properly emitting error on the first execution but succeeded on the second and following executions. Code which is responsible for name resolution of fields mentioned in UPDATE clause (e.g. see select_insert::prepare()) modifies table list and Name_resolution_context used in this process. It uses Name_resolution_context_state::save_state/restore_state() to revert these modifications. Unfortunately those two methods failed to revert properly modifications to TABLE_LIST::next_name_resolution_table and this broke name resolution process for successive executions. This patch fixes Name_resolution_context_state::save_state/restore_state() in such way that it properly handles TABLE_LIST::next_name_resolution_table. --- mysql-test/r/ps.result | 16 ++++++++++++++++ mysql-test/r/sp-error.result | 19 +++++++++++++++++++ mysql-test/t/ps.test | 30 ++++++++++++++++++++++++++++++ mysql-test/t/sp-error.test | 32 ++++++++++++++++++++++++++++++++ sql/item.h | 9 ++------- 5 files changed, 99 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index eb07bb8e672..279e89150da 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1496,4 +1496,20 @@ Variable_name Value Slow_queries 1 deallocate prepare no_index; deallocate prepare sq; +drop tables if exists t1; +create table t1 (id int primary key auto_increment, value varchar(10)); +insert into t1 (id, value) values (1, 'FIRST'), (2, 'SECOND'), (3, 'THIRD'); +prepare stmt from "insert into t1 (id, value) select * from (select 4 as i, 'FOURTH' as v) as y on duplicate key update v = 'DUP'"; +execute stmt; +ERROR 42S22: Unknown column 'v' in 'field list' +execute stmt; +ERROR 42S22: Unknown column 'v' in 'field list' +deallocate prepare stmt; +prepare stmt from "insert into t1 (id, value) select * from (select 4 as id, 'FOURTH' as value) as y on duplicate key update y.value = 'DUP'"; +execute stmt; +ERROR 42S22: Unknown column 'y.value' in 'field list' +execute stmt; +ERROR 42S22: Unknown column 'y.value' in 'field list' +deallocate prepare stmt; +drop tables t1; End of 5.0 tests. diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 63fd1bfff6d..e7e387f4348 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1250,3 +1250,22 @@ ERROR HY000: View's SELECT contains a variable or parameter PREPARE stmt FROM "CREATE VIEW v AS SELECT ?"; ERROR HY000: View's SELECT contains a variable or parameter DROP TABLE t1; +drop tables if exists t1; +drop procedure if exists bug24491; +create table t1 (id int primary key auto_increment, value varchar(10)); +insert into t1 (id, value) values (1, 'FIRST'), (2, 'SECOND'), (3, 'THIRD'); +create procedure bug24491() +insert into t1 (id, value) select * from (select 4 as i, 'FOURTH' as v) as y on duplicate key update v = 'DUP'; +call bug24491(); +ERROR 42S22: Unknown column 'v' in 'field list' +call bug24491(); +ERROR 42S22: Unknown column 'v' in 'field list' +drop procedure bug24491; +create procedure bug24491() +insert into t1 (id, value) select * from (select 4 as id, 'FOURTH' as value) as y on duplicate key update y.value = 'DUP'; +call bug24491(); +ERROR 42S22: Unknown column 'y.value' in 'field list' +call bug24491(); +ERROR 42S22: Unknown column 'y.value' in 'field list' +drop procedure bug24491; +drop tables t1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 1c8d7b419a3..2059f2fb080 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1540,4 +1540,34 @@ execute sq; deallocate prepare no_index; deallocate prepare sq; + +# +# BUG#24491 "using alias from source table in insert ... on duplicate key" +# +--disable_warnings +drop tables if exists t1; +--enable_warnings +create table t1 (id int primary key auto_increment, value varchar(10)); +insert into t1 (id, value) values (1, 'FIRST'), (2, 'SECOND'), (3, 'THIRD'); +# Let us prepare INSERT ... SELECT ... ON DUPLICATE KEY UPDATE statement +# which in its ON DUPLICATE KEY clause erroneously tries to assign value +# to a column which is mentioned only in SELECT part. +prepare stmt from "insert into t1 (id, value) select * from (select 4 as i, 'FOURTH' as v) as y on duplicate key update v = 'DUP'"; +# Both first and second attempts to execute it should fail +--error ER_BAD_FIELD_ERROR +execute stmt; +--error ER_BAD_FIELD_ERROR +execute stmt; +deallocate prepare stmt; +# And now the same test for more complex case which is more close +# to the one that was reported originally. +prepare stmt from "insert into t1 (id, value) select * from (select 4 as id, 'FOURTH' as value) as y on duplicate key update y.value = 'DUP'"; +--error ER_BAD_FIELD_ERROR +execute stmt; +--error ER_BAD_FIELD_ERROR +execute stmt; +deallocate prepare stmt; +drop tables t1; + + --echo End of 5.0 tests. diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 77bd5259eb5..e77e3df8301 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1808,6 +1808,38 @@ PREPARE stmt FROM "CREATE VIEW v AS SELECT ?"; DROP TABLE t1; +# +# BUG#24491 "using alias from source table in insert ... on duplicate key" +# +--disable_warnings +drop tables if exists t1; +drop procedure if exists bug24491; +--enable_warnings +create table t1 (id int primary key auto_increment, value varchar(10)); +insert into t1 (id, value) values (1, 'FIRST'), (2, 'SECOND'), (3, 'THIRD'); +# Let us create routine with INSERT ... SELECT ... ON DUPLICATE KEY UPDATE +# statement which in its ON DUPLICATE KEY clause erroneously tries to assign +# value to a column which is mentioned only in SELECT part. +create procedure bug24491() + insert into t1 (id, value) select * from (select 4 as i, 'FOURTH' as v) as y on duplicate key update v = 'DUP'; +# Both first and second calls to it should fail +--error ER_BAD_FIELD_ERROR +call bug24491(); +--error ER_BAD_FIELD_ERROR +call bug24491(); +drop procedure bug24491; +# And now the same test for more complex case which is more close +# to the one that was reported originally. +create procedure bug24491() + insert into t1 (id, value) select * from (select 4 as id, 'FOURTH' as value) as y on duplicate key update y.value = 'DUP'; +--error ER_BAD_FIELD_ERROR +call bug24491(); +--error ER_BAD_FIELD_ERROR +call bug24491(); +drop procedure bug24491; +drop tables t1; + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/item.h b/sql/item.h index 63d89113ec1..7c6aa39cadf 100644 --- a/sql/item.h +++ b/sql/item.h @@ -337,23 +337,18 @@ public: { save_table_list= context->table_list; save_first_name_resolution_table= context->first_name_resolution_table; - save_next_name_resolution_table= (context->first_name_resolution_table) ? - context->first_name_resolution_table-> - next_name_resolution_table : - NULL; save_resolve_in_select_list= context->resolve_in_select_list; save_next_local= table_list->next_local; + save_next_name_resolution_table= table_list->next_name_resolution_table; } /* Restore a name resolution context from saved state. */ void restore_state(Name_resolution_context *context, TABLE_LIST *table_list) { table_list->next_local= save_next_local; + table_list->next_name_resolution_table= save_next_name_resolution_table; context->table_list= save_table_list; context->first_name_resolution_table= save_first_name_resolution_table; - if (context->first_name_resolution_table) - context->first_name_resolution_table-> - next_name_resolution_table= save_next_name_resolution_table; context->resolve_in_select_list= save_resolve_in_select_list; } }; From 20279c1a7b5073bc12a14e2b1b3291d9840a27ef Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Tue, 23 Jan 2007 10:11:53 -0800 Subject: [PATCH 66/73] Post-merge fix. --- mysql-test/t/fulltext_left_join.test | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/mysql-test/t/fulltext_left_join.test b/mysql-test/t/fulltext_left_join.test index f16556b19ed..5942ce119ee 100644 --- a/mysql-test/t/fulltext_left_join.test +++ b/mysql-test/t/fulltext_left_join.test @@ -58,19 +58,6 @@ insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); drop table t1,t2; -# -# BUG#14708 -# Inconsistent treatment of NULLs in LEFT JOINed FULLTEXT matching without index -# - -create table t1 (id int not null primary key, d char(200) not null, e char(200)); -insert into t1 values (1, 'aword', null), (2, 'aword', 'bword'), (3, 'bword', null), (4, 'bword', 'aword'), (5, 'aword and bword', null); -select * from t1 where match(d, e) against ('+aword +bword' in boolean mode); -create table t2 (m_id int not null, f char(200), key (m_id)); -insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); -select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); -drop table t1,t2; - # # BUG#25637: LEFT JOIN with BOOLEAN FULLTEXT loses left table matches # (this is actually the same bug as bug #14708) From 4889ea232130b9671333f06dd0a5b948c3ee5c49 Mon Sep 17 00:00:00 2001 From: "stewart@willster.(none)" <> Date: Wed, 24 Jan 2007 16:12:39 +1100 Subject: [PATCH 67/73] fix hpux build problem and some ndb connect errors being missed --- ndb/src/common/util/SocketClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndb/src/common/util/SocketClient.cpp b/ndb/src/common/util/SocketClient.cpp index ec35fd3eb90..c2825901929 100644 --- a/ndb/src/common/util/SocketClient.cpp +++ b/ndb/src/common/util/SocketClient.cpp @@ -109,7 +109,7 @@ SocketClient::connect(const char *toaddress, unsigned short toport) struct timeval tval; int r; bool use_timeout; - socklen_t len; + SOCKOPT_OPTLEN_TYPE len; int flags; if (m_sockfd == NDB_INVALID_SOCKET) @@ -168,7 +168,7 @@ SocketClient::connect(const char *toaddress, unsigned short toport) if (FD_ISSET(m_sockfd, &rset) || FD_ISSET(m_sockfd, &wset)) { len= sizeof(r); - if (getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, &r, &len) < 0) + if (getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, &r, &len) < 0 || r) { // Solaris got an error... different than others NDB_CLOSE_SOCKET(m_sockfd); From 0e5aa80e8a677d91460f02abbacf3b19b9db97ac Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/june.mysql.com" <> Date: Wed, 24 Jan 2007 11:53:09 +0400 Subject: [PATCH 68/73] After merge fix. --- mysql-test/r/myisam.result | 13 ------------- mysql-test/t/myisam.test | 34 ---------------------------------- 2 files changed, 47 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 76a5ce16293..52dae8abd02 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -1608,19 +1608,6 @@ create table t3 (c1 int) engine=myisam pack_keys=default; create table t4 (c1 int) engine=myisam pack_keys=2; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '2' at line 1 drop table t1, t2, t3; -show create table t1; -show create table t1; -create table t1 (a int) engine=myisam select 42 a; -select * from t1; -a -9 -select * from t1; -a -99 -select * from t1; -a -42 -drop table t1; CREATE TABLE t1(a VARCHAR(16)); INSERT INTO t1 VALUES('aaaaaaaa'),(NULL); UPDATE t1 AS ta1, t1 AS ta2 SET ta1.a='aaaaaaaaaaaaaaaa'; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 040ab3fce09..3189a59333b 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -969,40 +969,6 @@ create table t3 (c1 int) engine=myisam pack_keys=default; --error 1064 create table t4 (c1 int) engine=myisam pack_keys=2; drop table t1, t2, t3; -# -# Bug#8706 - temporary table with data directory option fails -# -connect (session1,localhost,root,,); -connect (session2,localhost,root,,); - -connection session1; -disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQLTEST_VARDIR/tmp" select 9 a; -enable_query_log; -disable_result_log; -show create table t1; -enable_result_log; - -connection session2; -disable_query_log; -eval create temporary table t1 (a int) engine=myisam data directory="$MYSQLTEST_VARDIR/tmp" select 99 a; -enable_query_log; -disable_result_log; -show create table t1; -enable_result_log; - -connection default; -create table t1 (a int) engine=myisam select 42 a; - -connection session1; -select * from t1; -disconnect session1; -connection session2; -select * from t1; -disconnect session2; -connection default; -select * from t1; -drop table t1; # # BUG#21310 - Trees in SQL causing a "crashed" table with MyISAM storage From 928d672c46f48444268bad63ba1377c8e2a3bc0d Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 24 Jan 2007 13:17:01 +0100 Subject: [PATCH 69/73] Bug#24607 - MyISAM pointer size determined incorrectly Fixed test. On 32-bit machines which compile without -DBIG_TABLES, MAX_ROWS is truncated to a 32-bit value. Using a value below 4G is portable. --- mysql-test/r/myisam.result | 4 ++-- mysql-test/t/myisam.test | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 48d9905311d..41d2d48f82e 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -939,8 +939,8 @@ a 2 3 DROP TABLE t1; -CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=5100100100; +CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=4100100100; SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 MyISAM 9 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4294967295 avg_row_length=70100 +t1 MyISAM 9 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4100100100 avg_row_length=70100 DROP TABLE t1; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 0aeb49b0e99..0abe0a25f44 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -877,7 +877,7 @@ DROP TABLE t1; # # Bug#24607 - MyISAM pointer size determined incorrectly # -CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=5100100100; +CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=4100100100; --replace_column 5 X 6 X 7 X 9 X 10 X 11 X 12 X 13 X 14 X 16 X SHOW TABLE STATUS LIKE 't1'; DROP TABLE t1; From 23debf86623420841e8e481896351a4f8e48a4cc Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 24 Jan 2007 13:29:46 +0100 Subject: [PATCH 70/73] Bug#24607 - MyISAM pointer size determined incorrectly After merge fix --- mysql-test/r/myisam.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 6ffbabe3c61..373049bf051 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -1640,5 +1640,5 @@ DROP TABLE t1; CREATE TABLE t1 (c1 TEXT) AVG_ROW_LENGTH=70100 MAX_ROWS=4100100100; SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 MyISAM 9 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4100100100 avg_row_length=70100 +t1 MyISAM 10 Dynamic X X X 72057594037927935 X X X X X X latin1_swedish_ci X max_rows=4100100100 avg_row_length=70100 DROP TABLE t1; From 37110aad7ff5520c65ccdd0ce9e5233a0277b3e2 Mon Sep 17 00:00:00 2001 From: "dlenev@mockturtle.local" <> Date: Wed, 24 Jan 2007 22:11:27 +0300 Subject: [PATCH 71/73] Disabling back im_daemon_life_cycle.test, which was temporarily enabled in the team tree for additional investigation. --- mysql-test/t/disabled.def | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 53b73363c22..3213bd4eb5b 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -10,6 +10,7 @@ # ############################################################################## +im_daemon_life_cycle : Bug#24415 see note: [19 Dec 23:17] Trudy Pelzer ndb_load : Bug#17233 user_limits : Bug#23921 random failure of user_limits.test flush2 : Bug#24805 Pushbuild can't handle test with --disable-log-bin From cced62550877735ef14a471196dada7227424e9e Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 24 Jan 2007 22:23:50 +0300 Subject: [PATCH 72/73] BUG#24127: merge fixes: If subselect is a union, dont allocate several identical pushed_cond_guards arrays. Allocate only one always. --- mysql-test/r/subselect.result | 6 +++--- mysql-test/t/subselect3.test | 1 - sql/item_subselect.cc | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 35523d74acc..57b2c0cd0f2 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1464,7 +1464,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond((`test`.`t2`.`s1`)))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,(not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond((`test`.`t2`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1` explain extended select s1, s1 = ANY (SELECT s1 FROM t2) from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index @@ -1476,13 +1476,13 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Full scan on NULL key Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond((`test`.`t2`.`s1`)))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,(not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL having trigcond((`test`.`t2`.`s1`))))))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1` explain extended select s1, s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2') from t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL s1 6 NULL 3 Using index 2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index; Using where; Full scan on NULL key Warnings: -Note 1003 select `test`.`t1`.`s1` AS `s1`,not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2') having trigcond((`test`.`t2`.`s1`)))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` +Note 1003 select `test`.`t1`.`s1` AS `s1`,(not((`test`.`t1`.`s1`,(((`test`.`t1`.`s1`) in t2 on s1 checking NULL where (`test`.`t2`.`s1` < _latin1'a2') having trigcond((`test`.`t2`.`s1`))))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1` drop table t1,t2; create table t2 (a int, b int); create table t3 (a int); diff --git a/mysql-test/t/subselect3.test b/mysql-test/t/subselect3.test index 7910a88c5d6..23d78721dbe 100644 --- a/mysql-test/t/subselect3.test +++ b/mysql-test/t/subselect3.test @@ -46,7 +46,6 @@ select a, oref, a in (select max(ie) explain extended select a, oref from t2 where a in (select max(ie) from t1 where oref=t2.oref group by grp); - select a, oref, a in ( select max(ie) from t1 where oref=t2.oref group by grp union select max(ie) from t1 where oref=t2.oref group by grp diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 7e6a5614c27..e19815960a6 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -948,7 +948,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, unit->uncacheable|= UNCACHEABLE_DEPENDENT; } - if (!abort_on_null && left_expr->maybe_null) + if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards) { if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool)))) DBUG_RETURN(RES_ERROR); @@ -1163,7 +1163,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) thd->lex->current_select= current; unit->uncacheable|= UNCACHEABLE_DEPENDENT; - if (!abort_on_null && left_expr->maybe_null) + if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards) { if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool) * left_expr->cols()))) From 39e6de4359e3d138597a544ac7aa1cddde6359f5 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Thu, 25 Jan 2007 02:27:28 +0300 Subject: [PATCH 73/73] BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...): Fixed typo problem that surfaced after the merge --- sql/sql_select.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 19034c44ba0..dbfdb783c9a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2973,14 +2973,14 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, if (cond->type() == Item::FUNC_ITEM && ((Item_func*)cond)->functype() == Item_func::TRIG_COND_FUNC) { - cond= ((Item_func*)cond)->arguments()[0]; + Item *cond_arg= ((Item_func*)cond)->arguments()[0]; if (!join->group_list && !join->order && join->unit->item && join->unit->item->substype() == Item_subselect::IN_SUBS && !join->unit->first_select()->next_select()) { KEY_FIELD *save= *key_fields; - add_key_fields(join, key_fields, and_level, cond, usable_tables, + add_key_fields(join, key_fields, and_level, cond_arg, usable_tables, sargables); // Indicate that this ref access candidate is for subquery lookup: for (; save != *key_fields; save++)