summaryrefslogtreecommitdiff
path: root/sql/sql_update.cc
diff options
context:
space:
mode:
authorunknown <monty@mysql.com>2004-10-06 19:14:33 +0300
committerunknown <monty@mysql.com>2004-10-06 19:14:33 +0300
commitbbab9ec678f9e8a0309f0b018cf6d22cd93acf84 (patch)
tree4e2cfa6a6a8032773454e22aa802b2798b2935b8 /sql/sql_update.cc
parent7d583c5834f420406c9abe8bb9c44518e49e74c3 (diff)
parent95e1c07483005b784aaefa35b5a8597ffb1d3932 (diff)
downloadmariadb-git-bbab9ec678f9e8a0309f0b018cf6d22cd93acf84.tar.gz
Merge with 4.0 for 4.1 release
Noteworthy: - New HANDLER code - New multi-update-grant-check code - Table lock code in ha_innodb.cc was not applied BitKeeper/etc/logging_ok: auto-union BitKeeper/deleted/.del-ctype-latin1_de.c~c5d8f9208bceb98e: Auto merged Build-tools/mysql-copyright-2: Auto merged acinclude.m4: Auto merged client/mysqladmin.c: Auto merged client/mysqldump.c: Auto merged include/config-win.h: Auto merged include/my_global.h: Auto merged include/myisam.h: Auto merged innobase/btr/btr0btr.c: Auto merged innobase/buf/buf0buf.c: Auto merged ltmain.sh: Auto merged innobase/dict/dict0dict.c: Auto merged innobase/fsp/fsp0fsp.c: Auto merged innobase/include/dict0dict.h: Auto merged innobase/include/row0mysql.h: Auto merged innobase/log/log0log.c: Auto merged innobase/log/log0recv.c: Auto merged innobase/pars/pars0opt.c: Auto merged innobase/row/row0row.c: Auto merged innobase/sync/sync0arr.c: Auto merged innobase/ut/ut0dbg.c: Auto merged myisam/mi_check.c: Auto merged myisam/mi_close.c: Auto merged myisam/mi_create.c: Auto merged myisam/mi_locking.c: Auto merged myisam/myisampack.c: Auto merged mysql-test/r/delete.result: Auto merged mysql-test/r/func_if.result: Auto merged Build-tools/mysql-copyright: Merge with 4.0 (too most of the code from 4.0) Makefile.am: merge client/mysql.cc: Used 4.1 code configure.in: merge innobase/os/os0file.c: merge innobase/row/row0mysql.c: merge mysql-test/r/ctype_latin1_de.result: merge mysql-test/r/flush_table.result: merge mysql-test/r/func_str.result: merge mysql-test/r/handler.result: merge mysql-test/r/multi_update.result: merge mysql-test/r/type_timestamp.result: Removed testing of 'new' mode, as this is only relevant for 4.0 mysql-test/r/update.result: merge mysql-test/t/delete.test: merge mysql-test/t/flush_table.test: merge mysql-test/t/func_str.test: merge mysql-test/t/handler.test: merge mysql-test/t/multi_update.test: merge mysql-test/t/type_timestamp.test: Removed testing of 'new' mode, as this is only relevant for 4.0 mysql-test/t/update.test: merge mysys/errors.c: merge mysys/my_fstream.c: merge mysys/my_pread.c: merge mysys/my_write.c: merge mysys/mysys_priv.h: merge scripts/mysqlhotcopy.sh: merge sql/field.cc: Keep code from 4.1 sql/field.h: Keep code from 4.1 sql/ha_innodb.cc: Don't merge lock code from 4.0; Heikki will look at this sql/ha_myisam.cc: merge sql/handler.cc: merge sql/item_cmpfunc.cc: merge sql/item_cmpfunc.h: merge sql/item_strfunc.cc: merge sql/mysql_priv.h: merge sql/mysqld.cc: merge sql/protocol.cc: merge sql/records.cc: merge sql/repl_failsafe.cc: merge mysql-test/r/lock_multi.result: merge mysql-test/t/ctype_latin1_de.test: merge mysql-test/t/func_if.test: merge mysql-test/t/lock_multi.test: merge sql/repl_failsafe.h: merge Remove unnessessary header protection sql/slave.h: merge sql/sql_acl.cc: merge sql/sql_base.cc: merge sql/sql_cache.cc: auto merge sql/sql_class.cc: merge sql/sql_class.h: merge sql/sql_delete.cc: merge sql/sql_handler.cc: Get new HANDLER code into 4.1 sql/sql_parse.cc: Keep old file sql/sql_repl.cc: merge sql/sql_repl.h: merge sql/sql_show.cc: merge sql/sql_table.cc: merge sql/sql_union.cc: Applied the examine_rows bug fix from 4.0 by hand sql/sql_update.cc: New multi-update-grant-check code from 4.0 sql/sql_yacc.yy: New multi-update-grant-check code from 4.0 sql/stacktrace.c: merge sql/table.h: merge
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r--sql/sql_update.cc227
1 files changed, 151 insertions, 76 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index c6fb3d6e415..25d94d6d039 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -449,6 +449,24 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
***************************************************************************/
/*
+ Get table map for list of Item_field
+*/
+
+static table_map get_table_map(List<Item> *items)
+{
+ List_iterator_fast<Item> 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
*/
@@ -465,107 +483,163 @@ int mysql_multi_update(THD *thd,
multi_update *result;
TABLE_LIST *tl;
TABLE_LIST *update_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
- table_map item_tables= 0, derived_tables= 0;
+ List<Item> total_list;
DBUG_ENTER("mysql_multi_update");
- if ((res=open_and_lock_tables(thd,table_list)))
- DBUG_RETURN(res);
-
select_lex->select_limit= HA_POS_ERROR;
/*
- Ensure that we have update privilege for all tables and columns in the
- SET part
+ The following loop is here to to ensure that we only lock tables
+ that we are going to update with a write lock
*/
- for (tl= update_list; tl; tl= tl->next)
+ for (;;)
{
- TABLE *table= tl->table;
+ table_map update_tables, derived_tables=0;
+ uint tnr, counter;
+
+ if ((res=open_tables(thd,table_list, &counter)))
+ 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))))
+ DBUG_RETURN(res);
+
/*
- Update of derived tables is checked later
- We don't check privileges here, becasue then we would get error
- "UPDATE command denided .. for column N" instead of
- "Target table ... is not updatable"
+ Ensure that we have update privilege for all tables and columns in the
+ SET part
+ While we are here, initialize the table->map field to check which
+ tables are updated and updatability of derived tables
*/
- if (!tl->derived)
- table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege);
- }
+ for (tl= update_list, tnr=0 ; tl ; tl=tl->next)
+ {
+ TABLE *table= tl->table;
+ /*
+ Update of derived tables is checked later
+ We don't check privileges here, becasue then we would get error
+ "UPDATE command denided .. for column N" instead of
+ "Target table ... is not updatable"
+ */
+ if (!tl->derived)
+ table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege);
+ table->map= (table_map) 1 << (tnr++);
+ }
- /* Assign table map values to check updatability of derived tables */
- {
- uint tablenr=0;
- for (TABLE_LIST *table_list= update_list;
- table_list;
- table_list= table_list->next, tablenr++)
+ if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
+ DBUG_RETURN(-1);
+
+ update_tables= get_table_map(fields);
+
+ /* Unlock the tables in preparation for relocking */
+ if (!using_lock_tables)
+ {
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ }
+
+ /*
+ Count tables and setup timestamp handling
+ Set also the table locking strategy according to the update map
+ */
+ for (tl= update_list; tl; tl= tl->next)
{
- table_list->table->map= (table_map) 1 << tablenr;
+ TABLE *table= tl->table;
+ /* if table will be updated then check that it is unique */
+ if (table->map & update_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);
+ }
+ 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 (tl->derived)
+ derived_tables|= table->map;
}
- }
- if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
- DBUG_RETURN(-1);
+ if (thd->lex->derived_tables && (update_tables & derived_tables))
+ {
+ // find derived table which cause error
+ for (tl= update_list; tl; tl= tl->next)
+ {
+ if (tl->derived && (update_tables & tl->table->map))
+ {
+ my_printf_error(ER_NON_UPDATABLE_TABLE, ER(ER_NON_UPDATABLE_TABLE),
+ MYF(0), tl->alias, "UPDATE");
+ DBUG_RETURN(-1);
+ }
+ }
+ }
- /* Find tables used in items */
- {
- List_iterator_fast<Item> it(*fields);
- Item *item;
- while ((item= it++))
+ /* Relock the tables with the correct modes */
+ res= lock_tables(thd,table_list);
+ if (using_lock_tables)
{
- item_tables|= item->used_tables();
+ if (res)
+ DBUG_RETURN(res);
+ break; // Don't have to do setup_field()
}
+
+ /*
+ We must setup fields again as the file may have been reopened
+ during lock_tables
+ */
+ {
+ List_iterator_fast<Item> field_it(*fields);
+ Item_field *item;
+
+ while ((item= (Item_field *) field_it++))
+ {
+ item->field->query_id= 0;
+ item->cleanup();
+ }
+ }
+ 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_tables == 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);
}
/*
- Count tables and setup timestamp handling
+ Setup timestamp handling
*/
for (tl= update_list; tl; tl= tl->next)
{
TABLE *table= tl->table;
-
- /* We only need SELECT privilege for columns in the values list */
- table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
- // Only set timestamp column if this is not modified
+ /* Only set timestamp column if this is not modified */
if (table->timestamp_field &&
table->timestamp_field->query_id == thd->query_id)
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
- /* 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;
- }
- if (thd->lex->derived_tables && (item_tables & derived_tables))
- {
- // find derived table which cause error
- for (tl= update_list; tl; tl= tl->next)
- {
- if (tl->derived && (item_tables & tl->table->map))
- {
- my_printf_error(ER_NON_UPDATABLE_TABLE, ER(ER_NON_UPDATABLE_TABLE),
- MYF(0), tl->alias, "UPDATE");
- DBUG_RETURN(-1);
- }
- }
}
if (!(result=new multi_update(thd, update_list, fields, values,
handle_duplicates)))
DBUG_RETURN(-1);
- List<Item> total_list;
res= mysql_select(thd, &select_lex->ref_pointer_array,
select_lex->get_table_list(), select_lex->with_wild,
total_list,
@@ -597,7 +671,7 @@ int multi_update::prepare(List<Item> &not_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<Item> field_it(*fields);
List_iterator_fast<Item> value_it(*values);
@@ -608,8 +682,7 @@ int multi_update::prepare(List<Item> &not_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)
{
@@ -672,7 +745,6 @@ int multi_update::prepare(List<Item> &not_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++;
@@ -918,9 +990,13 @@ bool multi_update::send_data(List<Item> &not_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);
+ }
}
}
}
@@ -986,7 +1062,6 @@ int multi_update::do_updates(bool from_send_error)
TABLE *table, *tmp_table;
DBUG_ENTER("do_updates");
-
do_update= 0; // Don't retry this function
if (!found)
DBUG_RETURN(0);