Loading mysql-test/r/myisam.result +16 −0 Original line number Diff line number Diff line Loading @@ -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; mysql-test/t/myisam.test +19 −0 Original line number Diff line number Diff line Loading @@ -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 sql/lock.cc +1 −1 Original line number Diff line number Diff line Loading @@ -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. */ Loading sql/sql_update.cc +59 −72 Original line number Diff line number Diff line Loading @@ -23,8 +23,6 @@ #include "mysql_priv.h" #include "sql_select.h" static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields); /* Return 0 if row hasn't changed */ static bool compare_record(TABLE *table, ulong query_id) Loading Loading @@ -846,27 +844,69 @@ int multi_update::prepare(List<Item> ¬_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]; DBUG_RETURN(thd->is_fatal_error != 0); } /* Mark all copies of tables that are updates to ensure that init_read_record() will not try to enable a cache on them Check if table is safe to update on fly The problem is that for queries like 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: UPDATE t1, t1 AS t2 SET t1.b=t2.c WHERE t1.a=t2.a; - We are doing a table scan and the data is in a separate file (MyISAM) or if we don't update a clustered key. 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) - 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 */ for (table_ref= all_tables; table_ref; table_ref=table_ref->next) static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, TABLE_LIST *all_tables, List<Item> *fields) { 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 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 } DBUG_RETURN(thd->is_fatal_error != 0); return 0; } Loading Loading @@ -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; Loading Loading @@ -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<Item> *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() { Loading Loading
mysql-test/r/myisam.result +16 −0 Original line number Diff line number Diff line Loading @@ -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;
mysql-test/t/myisam.test +19 −0 Original line number Diff line number Diff line Loading @@ -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
sql/lock.cc +1 −1 Original line number Diff line number Diff line Loading @@ -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. */ Loading
sql/sql_update.cc +59 −72 Original line number Diff line number Diff line Loading @@ -23,8 +23,6 @@ #include "mysql_priv.h" #include "sql_select.h" static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields); /* Return 0 if row hasn't changed */ static bool compare_record(TABLE *table, ulong query_id) Loading Loading @@ -846,27 +844,69 @@ int multi_update::prepare(List<Item> ¬_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]; DBUG_RETURN(thd->is_fatal_error != 0); } /* Mark all copies of tables that are updates to ensure that init_read_record() will not try to enable a cache on them Check if table is safe to update on fly The problem is that for queries like 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: UPDATE t1, t1 AS t2 SET t1.b=t2.c WHERE t1.a=t2.a; - We are doing a table scan and the data is in a separate file (MyISAM) or if we don't update a clustered key. 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) - 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 */ for (table_ref= all_tables; table_ref; table_ref=table_ref->next) static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, TABLE_LIST *all_tables, List<Item> *fields) { 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 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 } DBUG_RETURN(thd->is_fatal_error != 0); return 0; } Loading Loading @@ -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; Loading Loading @@ -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<Item> *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() { Loading