From 6c2330407f99b0bab1db68ec38789fc10a3518ec Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Sep 2004 13:39:15 +0300 Subject: check that table used in multi-update is unique added (BUG#5455) mysql-test/r/multi_update.result: multi* unique updating table check mysql-test/t/multi_update.test: multi* unique updating table check sql/sql_lex.cc: new method to check table only in subqueries sql/sql_lex.h: new method to check table only in subqueries sql/sql_parse.cc: used new method to check table only in subqueries sql/sql_update.cc: check that table is unique added --- sql/sql_update.cc | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'sql/sql_update.cc') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index db4edff4fa1..b6cd0d967e9 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -490,9 +490,8 @@ int mysql_multi_update(THD *thd, table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege); } - if (thd->lex->derived_tables) + /* Assign table map values to check updatability of derived tables */ { - // Assign table map values to check updatability of derived tables uint tablenr=0; for (TABLE_LIST *table_list= update_list; table_list; @@ -501,11 +500,12 @@ int mysql_multi_update(THD *thd, table_list->table->map= (table_map) 1 << tablenr; } } + if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0)) DBUG_RETURN(-1); - if (thd->lex->derived_tables) + + /* Find tables used in items */ { - // Find tables used in items List_iterator_fast it(*fields); Item *item; while ((item= it++)) @@ -527,7 +527,23 @@ int mysql_multi_update(THD *thd, if (table->timestamp_field && table->timestamp_field->query_id == thd->query_id) table->timestamp_on_update_now= 0; - + + /* if table will be updated then check that it is unique */ + if (table->map & item_tables) + { + /* + Multi-update can't be constructed over-union => we always have + single SELECT on top and have to check underlaying SELECTs of it + */ + if (select_lex->check_updateable_in_subqueries(tl->db, + tl->real_name)) + { + my_error(ER_UPDATE_TABLE_USED, MYF(0), + tl->real_name); + DBUG_RETURN(-1); + } + } + if (tl->derived) derived_tables|= table->map; } -- cgit v1.2.1 From e84eb55a0778ca62490c79136b9a09379b02773a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 17 Sep 2004 12:07:59 +0100 Subject: Bug#5553 - Multi table UPDATE IGNORE fails on dup key We don't want the update to abort when IGNORE is specified mysql-test/r/update.result: Bug#5553 - UPDATE IGNORE fails on dup key New test mysql-test/t/update.test: Bug#5553 - UPDATE IGNORE fails on dup key New test BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted --- sql/sql_update.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sql/sql_update.cc') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 02d2fe2c442..d51c81ee127 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -792,9 +792,13 @@ bool multi_update::send_data(List ¬_used_values) if ((error=table->file->update_row(table->record[1], table->record[0]))) { - table->file->print_error(error,MYF(0)); updated--; - DBUG_RETURN(1); + if (handle_duplicates != DUP_IGNORE || + error != HA_ERR_FOUND_DUPP_KEY) + { + table->file->print_error(error,MYF(0)); + DBUG_RETURN(1); + } } } } -- cgit v1.2.1 From 7b511544614ab2ab3906c685f7ea7730f1f46605 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 1 Oct 2004 18:54:06 +0400 Subject: Support for TIMESTAMP columns holding NULL values. Unlike all other column types TIMESTAMP is NOT NULL by default, so in order to have TIMESTAMP column holding NULL valaues you have to specify NULL as one of its attributes (this needed for backward compatibility). Main changes: Replaced TABLE::timestamp_default_now/on_update_now members with TABLE::timestamp_auto_set_type flag which is used everywhere for determining if we should auto-set value of TIMESTAMP field during this operation or not. We are also use Field_timestamp::set_time() instead of handler::update_timestamp() in handlers. mysql-test/r/type_timestamp.result: Added test for TIMESTAMP columns which are able to store NULL values. mysql-test/t/type_timestamp.test: Added test for TIMESTAMP columns which are able to store NULL values. sql/field.cc: Added support for TIMESTAMP fields holding NULL values. We don't need Field_timestamp::set_timestamp_offsets() anymore. Instead we need Field_timestamp::get_auto_set_type() function which will convert TIMESTAMP auto-set type stored in Field in unireg_check to value from timestamp_auto_set_type_enum. (We can't replace this function with additional Field_timestamp member and some code in constructor because then we will have troubles with Field::new_field() method). We should also set field to not null in Field_timestamp::set_time() now. sql/field.h: Added support for TIMESTAMP fields holding NULL values. We don't need Field_timestamp::set_timestamp_offsets() anymore. Instead we need Field_timestamp::get_auto_set_type() function, which will convert TIMESTAMP auto-set type stored in Field in unireg_check to value from timestamp_auto_set_type_enum. We also have to support NULL values in Field_timestamp::get_timestamp() function. sql/field_conv.cc: Added comment clarifying behavior in case of TIMESTAMP fields which are able to store NULL values. sql/ha_berkeley.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/ha_heap.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/ha_innodb.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/ha_isam.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/ha_isammrg.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/ha_myisam.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/ha_myisammrg.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/ha_ndbcluster.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now for determining if we should auto-set value of TIMESTAMP field during this operation. We are also use Field_timestamp::set_time() instead of handler::update_timestamp(). sql/handler.cc: handler::update_timestamp() is no longer needed since now we use Field_timestamp::set_time() instead. (we can't use handler::update_timestamp() anyway since field position only is not enough for TIMESTAMP fields which are able to store NULLs) sql/handler.h: handler::update_timestamp() is no longer needed since now we use Field_timestamp::set_time() instead. sql/item_timefunc.cc: Since now TIMESTAMP fields can hold NULL values we should take this into account. sql/sql_base.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now. (Here we use Field_timestamp::get_auto_set_type() to setup its value before further statement execution). sql/sql_insert.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now. sql/sql_load.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now. sql/sql_parse.cc: Added support for TIMESTAMP fields holding NULL values. We should distinguish NULL default values and non-specified default values for such fields (because latter could mean DEFAULT NOW() ON UPDATE NOW() in some cases). sql/sql_show.cc: Added support for TIMESTAMP fields holding NULL values. Unlike all other fields these are NOT NULL by default so we have to specify NULL attribute explicitly for them. sql/sql_table.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now. sql/sql_update.cc: Now we use TABLE::timestamp_field_type instead of TABLE::timestamp_default_now/on_update_now. sql/sql_yacc.yy: Added support for TIMESTAMP fields holding NULL values. Unlike all other fields these are NOT NULL by default (so we have to set NOT_NULL_FLAG properly for them). sql/table.h: Added timestamp_auto_set_type enum which values are used for indicating during which operations we should automatically set TIMESTAPM field value to current timestamp. TABLE: Replaced timestamp_default_now/on_update_now members with timestamp_auto_set_type flag (Now when TIMESTAMP field are able to store NULL values, single position of field in record is not enough for updating this field anyway). --- sql/sql_update.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sql/sql_update.cc') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b6cd0d967e9..c6fb3d6e415 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -116,7 +116,7 @@ int mysql_update(THD *thd, { // Don't set timestamp column if this is modified if (table->timestamp_field->query_id == thd->query_id) - table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; else table->timestamp_field->query_id=timestamp_query_id; } @@ -526,7 +526,7 @@ int mysql_multi_update(THD *thd, // Only set timestamp column if this is not modified if (table->timestamp_field && table->timestamp_field->query_id == thd->query_id) - table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; /* if table will be updated then check that it is unique */ if (table->map & item_tables) -- cgit v1.2.1 From a49f5cae9ad6f4de7f5c2d9f8bbdbca270376af6 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 3 Oct 2004 00:20:47 +0100 Subject: Bug#4118: multi-table UPDATE takes WRITE lock on read table Ensures that WRITE lock is not obtained on all tables referenced. mysql-test/r/lock_multi.result: Bug#4118 New test for multi-update locking mysql-test/r/multi_update.result: Bug#4118 Fix test mysql-test/t/lock_multi.test: Bug#4118 New test for multi-update locking mysql-test/t/multi_update.test: Bug#4118 Fix test sql/sql_parse.cc: Bug#4118 Split multi-update to its own case statement in sql_parse.cc sql/sql_update.cc: Bug#4118 Overview of locking checking: 1. Open and acquire READ lock 2. Check to see which tables need WRITE lock 3. Unlock tables and relock sql/sql_yacc.yy: Bug#4118 Split multi-update to its own case statement in sql_parse.cc --- sql/sql_update.cc | 104 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 14 deletions(-) (limited to 'sql/sql_update.cc') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index d51c81ee127..a17742df03b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -401,25 +401,101 @@ int mysql_multi_update(THD *thd, int res; multi_update *result; TABLE_LIST *tl; + const bool locked= !(thd->locked_tables); DBUG_ENTER("mysql_multi_update"); - if ((res=open_and_lock_tables(thd,table_list))) - DBUG_RETURN(res); + for (;;) + { + table_map update_map= 0; + int tnr= 0; + + if ((res= open_tables(thd, table_list))) + DBUG_RETURN(res); - thd->select_limit=HA_POS_ERROR; + /* + Only need to call lock_tables if (thd->locked_tables == NULL) + */ + if (locked && ((res= lock_tables(thd, table_list)))) + DBUG_RETURN(res); - /* - Ensure that we have update privilege for all tables and columns in the - SET part - */ - for (tl= table_list ; tl ; tl=tl->next) - { - TABLE *table= tl->table; - table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege); - } + thd->select_limit=HA_POS_ERROR; - if (setup_fields(thd, table_list, *fields, 1, 0, 0)) - DBUG_RETURN(-1); + /* + Ensure that we have update privilege for all tables and columns in the + SET part + While we are here, initialize the table->map field. + */ + for (tl= table_list ; tl ; tl=tl->next) + { + TABLE *table= tl->table; + table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege); + table->map= (table_map) 1 << (tnr++); + } + + if (!setup_fields(thd, table_list, *fields, 1, 0, 0)) + { + List_iterator_fast field_it(*fields); + Item_field *item; + + while ((item= (Item_field *) field_it++)) + update_map|= item->used_tables(); + + DBUG_PRINT("info",("update_map=0x%08x", update_map)); + } + else + DBUG_RETURN(-1); + + /* + Unlock the tables in preparation for relocking + */ + if (locked) + { + pthread_mutex_lock(&LOCK_open); + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + pthread_mutex_unlock(&LOCK_open); + } + + /* + Set the table locking strategy according to the update map + */ + for (tl= table_list ; tl ; tl=tl->next) + { + TABLE *table= tl->table; + if (update_map & table->map) + { + DBUG_PRINT("info",("setting table `%s` for update", tl->alias)); + tl->lock_type= thd->lex.lock_option; + tl->updating= 1; + } + else + { + DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias)); + tl->lock_type= TL_READ; + tl->updating= 0; + } + if (locked) + tl->table->reginfo.lock_type= tl->lock_type; + } + + /* + Relock the tables + */ + if (!(res=lock_tables(thd,table_list))) + break; + + if (!locked) + DBUG_RETURN(res); + + List_iterator_fast field_it(*fields); + Item_field *item; + + while ((item= (Item_field *) field_it++)) + /* item->cleanup(); XXX Use this instead in MySQL 4.1+ */ + item->field= item->result_field= 0; + + close_thread_tables(thd); + } /* Count tables and setup timestamp handling -- cgit v1.2.1 From 0d76cb7ea4a20570342d51136a6da598fb553800 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 6 Oct 2004 01:24:21 +0300 Subject: Reverted patch for new usage of open_count as it caused more problems than it solved Cleaned up patch for checking locks for multi-table updates myisam/mi_close.c: Reverted patch for new usage of open_counts myisam/mi_locking.c: Reverted patch for new usage of open_counts sql/ha_myisam.cc: Reverted patch for new usage of open_counts sql/handler.cc: Removed compiler warning sql/sql_acl.cc: Removed compiler warning sql/sql_table.cc: No need to unlock after failed call to external_lock() sql/sql_update.cc: Cleaned up (and made it more secure) patch for checking locks for multi-table updates --- sql/sql_update.cc | 110 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 43 deletions(-) (limited to 'sql/sql_update.cc') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index a17742df03b..cdcc90e8651 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -386,6 +386,24 @@ err: Update multiple tables from join ***************************************************************************/ +/* + Get table map for list of Item_field +*/ + +static table_map get_table_map(List *items) +{ + List_iterator_fast item_it(*items); + Item_field *item; + table_map map= 0; + + while ((item= (Item_field *) item_it++)) + map|= item->used_tables(); + DBUG_PRINT("info",("table_map: 0x%08x", map)); + return map; +} + + + /* Setup multi-update handling and call SELECT to do the join */ @@ -401,59 +419,45 @@ int mysql_multi_update(THD *thd, int res; multi_update *result; TABLE_LIST *tl; - const bool locked= !(thd->locked_tables); + const bool using_lock_tables= thd->locked_tables != 0; DBUG_ENTER("mysql_multi_update"); + thd->select_limit= HA_POS_ERROR; + for (;;) { - table_map update_map= 0; - int tnr= 0; + table_map update_map; + int tnr; if ((res= open_tables(thd, table_list))) DBUG_RETURN(res); - /* - Only need to call lock_tables if (thd->locked_tables == NULL) - */ - if (locked && ((res= lock_tables(thd, table_list)))) + /* Only need to call lock_tables if we are not using LOCK TABLES */ + if (!using_lock_tables && ((res= lock_tables(thd, table_list)))) DBUG_RETURN(res); - thd->select_limit=HA_POS_ERROR; - /* Ensure that we have update privilege for all tables and columns in the SET part While we are here, initialize the table->map field. */ - for (tl= table_list ; tl ; tl=tl->next) + for (tl= table_list,tnr=0 ; tl ; tl=tl->next) { TABLE *table= tl->table; table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege); table->map= (table_map) 1 << (tnr++); } - if (!setup_fields(thd, table_list, *fields, 1, 0, 0)) - { - List_iterator_fast field_it(*fields); - Item_field *item; - - while ((item= (Item_field *) field_it++)) - update_map|= item->used_tables(); - - DBUG_PRINT("info",("update_map=0x%08x", update_map)); - } - else + if (setup_fields(thd, table_list, *fields, 1, 0, 0)) DBUG_RETURN(-1); - /* - Unlock the tables in preparation for relocking - */ - if (locked) + update_map= get_table_map(fields); + + /* Unlock the tables in preparation for relocking */ + if (!using_lock_tables) { - pthread_mutex_lock(&LOCK_open); mysql_unlock_tables(thd, thd->lock); thd->lock= 0; - pthread_mutex_unlock(&LOCK_open); } /* @@ -474,26 +478,48 @@ int mysql_multi_update(THD *thd, tl->lock_type= TL_READ; tl->updating= 0; } - if (locked) + if (!using_lock_tables) tl->table->reginfo.lock_type= tl->lock_type; } + /* Relock the tables with the correct modes */ + res= lock_tables(thd,table_list); + if (using_lock_tables) + { + if (res) + DBUG_RETURN(res); + break; // Don't have to do setup_field() + } + /* - Relock the tables + We must setup fields again as the file may have been reopened + during lock_tables */ - if (!(res=lock_tables(thd,table_list))) - break; - - if (!locked) - DBUG_RETURN(res); - List_iterator_fast field_it(*fields); - Item_field *item; + { + List_iterator_fast field_it(*fields); + Item_field *item; - while ((item= (Item_field *) field_it++)) - /* item->cleanup(); XXX Use this instead in MySQL 4.1+ */ - item->field= item->result_field= 0; + while ((item= (Item_field *) field_it++)) +#if MYSQL_VERSION < 40100 + item->field= item->result_field= 0; +#else + item->cleanup(); +#endif + } + if (setup_fields(thd, table_list, *fields, 1, 0, 0)) + DBUG_RETURN(-1); + /* + If lock succeded and the table map didn't change since the above lock + we can continue. + */ + if (!res && update_map == get_table_map(fields)) + break; + /* + There was some very unexpected changes in the table definition between + open tables and lock tables. Close tables and try again. + */ close_thread_tables(thd); } @@ -548,7 +574,7 @@ int multi_update::prepare(List ¬_used_values) { TABLE_LIST *table_ref; SQL_LIST update; - table_map tables_to_update= 0; + table_map tables_to_update; Item_field *item; List_iterator_fast field_it(*fields); List_iterator_fast value_it(*values); @@ -559,8 +585,7 @@ int multi_update::prepare(List ¬_used_values) thd->cuted_fields=0L; thd->proc_info="updating main table"; - while ((item= (Item_field *) field_it++)) - tables_to_update|= item->used_tables(); + tables_to_update= get_table_map(fields); if (!tables_to_update) { @@ -624,7 +649,6 @@ int multi_update::prepare(List ¬_used_values) /* Split fields into fields_for_table[] and values_by_table[] */ - field_it.rewind(); while ((item= (Item_field *) field_it++)) { Item *value= value_it++; -- cgit v1.2.1 From 96e7be58c86335d68c5c79b750244e2762d6e319 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 7 Oct 2004 10:50:13 +0300 Subject: After merge fixes Some bigger code changes was necessary becasue of the multi-table-update and the new HANDLER code include/hash.h: Added back function that's was used in 4.0 mysql-test/r/delete.result: Update results after merge mysql-test/r/flush_table.result: Update results after merge mysql-test/r/func_str.result: Update results after merge mysql-test/r/handler.result: Update results after merge Change is big becasue in MySQL 4.1 you are not allowed to qualify the handler alias with a databasename mysql-test/r/multi_update.result: More startup cleanups mysql-test/r/rename.result: More startup-cleanups mysql-test/r/select.result: More startup cleanups mysql-test/r/show_check.result: More startup-cleanups mysql-test/t/ctype_latin1_de.test: Cleanup mysql-test/t/derived.test: Portability fix mysql-test/t/handler.test: Update results after merge Change is big becasue in MySQL 4.1 you are not allowed to qualify the handler alias with a databasename mysql-test/t/multi_update.test: More startup cleanups mysql-test/t/range.test: More comments mysql-test/t/rename.test: More startup cleanups mysql-test/t/select.test: More startup cleanups mysql-test/t/show_check.test: More startup cleanups mysql-test/t/type_timestamp.test: Add back test deleted during merge sql/item_cmpfunc.cc: After merge fixes sql/item_func.cc: Remove compiler warning sql/mysql_priv.h: After merge fixes sql/mysqld.cc: After merge fixes sql/sql_acl.cc: More debugging sql/sql_base.cc: After merge fixes (This fix was needed bacause of multi-table-update reopens tables) sql/sql_handler.cc: After merge fixes sql/sql_lex.h: After merge fixes sql/sql_select.cc: After merge fixes sql/sql_show.cc: After merge fixes sql/sql_table.cc: After merge fixes Simple cleanup of mysql_discard_or_import_tablespace sql/sql_update.cc: After merge fixes Rework mysql_multi_update to take into account derived tables. sql/sql_yacc.yy: After merge fixes --- sql/sql_update.cc | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) (limited to 'sql/sql_update.cc') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 25d94d6d039..d3597f274dc 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -303,6 +303,7 @@ int mysql_update(THD *thd, else if (handle_duplicates != DUP_IGNORE || error != HA_ERR_FOUND_DUPP_KEY) { + thd->fatal_error(); // Force error message table->file->print_error(error,MYF(0)); error= 1; break; @@ -484,6 +485,8 @@ int mysql_multi_update(THD *thd, TABLE_LIST *tl; TABLE_LIST *update_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first; List total_list; + const bool using_lock_tables= thd->locked_tables != 0; + bool initialized_dervied= 0; DBUG_ENTER("mysql_multi_update"); select_lex->select_limit= HA_POS_ERROR; @@ -495,15 +498,24 @@ int mysql_multi_update(THD *thd, for (;;) { table_map update_tables, derived_tables=0; - uint tnr, counter; + uint tnr, table_count; - if ((res=open_tables(thd,table_list, &counter))) + if ((res=open_tables(thd, table_list, &table_count))) DBUG_RETURN(res); /* Only need to call lock_tables if we are not using LOCK TABLES */ - if (!using_lock_tables && ((res= lock_tables(thd, table_list)))) + if (!using_lock_tables && + ((res= lock_tables(thd, table_list, table_count)))) DBUG_RETURN(res); + if (!initialized_dervied) + { + initialized_dervied= 1; + relink_tables_for_derived(thd); + if ((res= mysql_handle_derived(thd->lex))) + DBUG_RETURN(res); + } + /* Ensure that we have update privilege for all tables and columns in the SET part @@ -558,7 +570,7 @@ int mysql_multi_update(THD *thd, DBUG_RETURN(-1); } DBUG_PRINT("info",("setting table `%s` for update", tl->alias)); - tl->lock_type= thd->lex.lock_option; + tl->lock_type= thd->lex->multi_lock_option; tl->updating= 1; } else @@ -569,6 +581,8 @@ int mysql_multi_update(THD *thd, } if (tl->derived) derived_tables|= table->map; + else if (!using_lock_tables) + tl->table->reginfo.lock_type= tl->lock_type; } if (thd->lex->derived_tables && (update_tables & derived_tables)) @@ -586,7 +600,7 @@ int mysql_multi_update(THD *thd, } /* Relock the tables with the correct modes */ - res= lock_tables(thd,table_list); + res= lock_tables(thd, table_list, table_count); if (using_lock_tables) { if (res) @@ -608,7 +622,7 @@ int mysql_multi_update(THD *thd, item->cleanup(); } } - if (setup_fields(thd, table_list, *fields, 1, 0, 0)) + if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0)) DBUG_RETURN(-1); /* If lock succeded and the table map didn't change since the above lock @@ -624,9 +638,7 @@ int mysql_multi_update(THD *thd, close_thread_tables(thd); } - /* - Setup timestamp handling - */ + /* Setup timestamp handling */ for (tl= update_list; tl; tl= tl->next) { TABLE *table= tl->table; @@ -634,6 +646,9 @@ int mysql_multi_update(THD *thd, if (table->timestamp_field && table->timestamp_field->query_id == thd->query_id) table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + /* We only need SELECT privilege for columns in the values list */ + table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); } if (!(result=new multi_update(thd, update_list, fields, values, @@ -994,6 +1009,7 @@ bool multi_update::send_data(List ¬_used_values) if (handle_duplicates != DUP_IGNORE || error != HA_ERR_FOUND_DUPP_KEY) { + thd->fatal_error(); // Force error message table->file->print_error(error,MYF(0)); DBUG_RETURN(1); } @@ -1149,7 +1165,10 @@ int multi_update::do_updates(bool from_send_error) err: if (!from_send_error) + { + thd->fatal_error(); table->file->print_error(local_error,MYF(0)); + } (void) table->file->ha_rnd_end(); (void) tmp_table->file->ha_rnd_end(); -- cgit v1.2.1