mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
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
This commit is contained in:
@@ -448,6 +448,24 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
|
||||
Update multiple tables from join
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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++)
|
||||
for (tl= update_list, tnr=0 ; tl ; tl=tl->next)
|
||||
{
|
||||
table_list->table->map= (table_map) 1 << tablenr;
|
||||
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++);
|
||||
}
|
||||
}
|
||||
|
||||
if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
|
||||
DBUG_RETURN(-1);
|
||||
if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* Find tables used in items */
|
||||
{
|
||||
List_iterator_fast<Item> it(*fields);
|
||||
Item *item;
|
||||
while ((item= it++))
|
||||
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)
|
||||
{
|
||||
item_tables|= item->used_tables();
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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()
|
||||
}
|
||||
|
||||
/*
|
||||
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> ¬_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> ¬_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> ¬_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> ¬_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);
|
||||
|
||||
Reference in New Issue
Block a user