merge with 5.3

sql/sql_insert.cc:
  CREATE ... IF NOT EXISTS may do nothing, but
  it is still not a failure. don't forget to my_ok it.
  ******
  CREATE ... IF NOT EXISTS may do nothing, but
  it is still not a failure. don't forget to my_ok it.
sql/sql_table.cc:
  small cleanup
  ******
  small cleanup
This commit is contained in:
Sergei Golubchik 2011-10-19 21:45:18 +02:00
commit 76f0b94bb0
1705 changed files with 166479 additions and 39785 deletions

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
Copyright (c) 2011 Monty Program Ab
This program is free software; you can redistribute it and/or modify
@ -46,7 +46,7 @@
/**
True if the table's input and output record buffers are comparable using
compare_records(TABLE*).
compare_record(TABLE*).
*/
bool records_are_comparable(const TABLE *table) {
return ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) ||
@ -56,18 +56,13 @@ bool records_are_comparable(const TABLE *table) {
/**
Compares the input and outbut record buffers of the table to see if a row
has changed. The algorithm iterates over updated columns and if they are
nullable compares NULL bits in the buffer before comparing actual
data. Special care must be taken to compare only the relevant NULL bits and
mask out all others as they may be undefined. The storage engine will not
and should not touch them.
@param table The table to evaluate.
has changed.
@return true if row has changed.
@return false otherwise.
*/
bool compare_records(const TABLE *table)
bool compare_record(const TABLE *table)
{
DBUG_ASSERT(records_are_comparable(table));
@ -104,7 +99,6 @@ bool compare_records(const TABLE *table)
comparison done above.
*/
if (table->s->can_cmp_whole_record)
// Fixed-size record: do bitwise comparison of the records
return cmp_record(table,record[1]);
/* Compare null bits */
if (memcmp(table->null_flags,
@ -262,6 +256,7 @@ int mysql_update(THD *thd,
bool using_limit= limit != HA_POS_ERROR;
bool safe_update= test(thd->variables.option_bits & OPTION_SAFE_UPDATES);
bool used_key_is_modified= FALSE, transactional_table, will_batch;
bool can_compare_record;
int res;
int error, loc_error;
uint used_index, dup_key_found;
@ -285,7 +280,11 @@ int mysql_update(THD *thd,
if (open_tables(thd, &table_list, &table_count, 0))
DBUG_RETURN(1);
if (table_list->multitable_view)
//Prepare views so they are handled correctly.
if (mysql_handle_derived(thd->lex, DT_INIT))
DBUG_RETURN(1);
if (table_list->is_multitable())
{
DBUG_ASSERT(table_list->view != 0);
DBUG_PRINT("info", ("Switch to multi-update"));
@ -297,20 +296,19 @@ int mysql_update(THD *thd,
if (lock_tables(thd, table_list, table_count, 0))
DBUG_RETURN(1);
if (mysql_handle_derived(thd->lex, &mysql_derived_prepare))
if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(1);
if (thd->fill_derived_tables() &&
mysql_handle_derived(thd->lex, &mysql_derived_filling))
{
mysql_handle_derived(thd->lex, &mysql_derived_cleanup);
if (table_list->handle_derived(thd->lex, DT_PREPARE))
DBUG_RETURN(1);
}
mysql_handle_derived(thd->lex, &mysql_derived_cleanup);
thd_proc_info(thd, "init");
table= table_list->table;
if (!table_list->updatable)
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
DBUG_RETURN(1);
}
/* Calculate "table->covering_keys" based on the WHERE */
table->covering_keys= table->s->keys_in_use;
table->quick_keys.clear_all();
@ -329,13 +327,17 @@ int mysql_update(THD *thd,
table_list->grant.want_privilege= table->grant.want_privilege= want_privilege;
table_list->register_want_access(want_privilege);
#endif
/* 'Unfix' fields to allow correct marking by the setup_fields function. */
if (table_list->is_view())
unfix_fields(fields);
if (setup_fields_with_no_wrap(thd, 0, fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
if (table_list->view && check_fields(thd, fields))
{
DBUG_RETURN(1);
}
if (!table_list->updatable || check_key_in_view(thd, table_list))
if (check_key_in_view(thd, table_list))
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
DBUG_RETURN(1);
@ -365,6 +367,10 @@ int mysql_update(THD *thd,
DBUG_RETURN(1); /* purecov: inspected */
}
/* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */
if (select_lex->optimize_unflattened_subqueries())
DBUG_RETURN(TRUE);
if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
DBUG_RETURN(1);
@ -659,6 +665,13 @@ int mysql_update(THD *thd,
if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)
table->prepare_for_position();
/*
We can use compare_record() to optimize away updates if
the table handler is returning all columns OR if
if all updated columns are read
*/
can_compare_record= records_are_comparable(table);
while (!(error=info.read_record(&info)) && !thd->killed)
{
update_virtual_fields(thd, table);
@ -676,7 +689,7 @@ int mysql_update(THD *thd,
found++;
if (!records_are_comparable(table) || compare_records(table))
if (!can_compare_record || compare_record(table))
{
if ((res= table_list->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
@ -924,6 +937,11 @@ int mysql_update(THD *thd,
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
thd->abort_on_warning= 0;
if (thd->lex->current_select->first_cond_optimization)
{
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
}
*found_return= found;
*updated_return= updated;
DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
@ -973,8 +991,8 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
if (setup_tables_and_check_access(thd, &select_lex->context,
&select_lex->top_join_list,
table_list,
&select_lex->leaf_tables,
FALSE, UPDATE_ACL, SELECT_ACL) ||
select_lex->leaf_tables,
FALSE, UPDATE_ACL, SELECT_ACL, TRUE) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
select_lex->setup_ref_array(thd, order_num) ||
setup_order(thd, select_lex->ref_pointer_array,
@ -1010,8 +1028,8 @@ static table_map get_table_map(List<Item> *items)
Item_field *item;
table_map map= 0;
while ((item= (Item_field *) item_it++))
map|= item->used_tables();
while ((item= (Item_field *) item_it++))
map|= item->all_used_tables();
DBUG_PRINT("info", ("table_map: 0x%08lx", (long) map));
return map;
}
@ -1046,11 +1064,12 @@ static table_map get_table_map(List<Item> *items)
false otherwise.
*/
static
bool unsafe_key_update(TABLE_LIST *leaves, table_map tables_for_update)
bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
{
TABLE_LIST *tl= leaves;
List_iterator_fast<TABLE_LIST> it(leaves), it2(leaves);
TABLE_LIST *tl, *tl2;
for (tl= leaves; tl ; tl= tl->next_leaf)
while ((tl= it++))
{
if (tl->table->map & tables_for_update)
{
@ -1066,14 +1085,16 @@ bool unsafe_key_update(TABLE_LIST *leaves, table_map tables_for_update)
if (!table_partitioned && !primkey_clustered)
continue;
for (TABLE_LIST* tl2= tl->next_leaf; tl2 ; tl2= tl2->next_leaf)
it2.rewind();
while ((tl2= it2++))
{
/*
Look at "next" tables only since all previous tables have
already been checked
*/
TABLE *table2= tl2->table;
if (table2->map & tables_for_update && table1->s == table2->s)
if (tl2 != tl &&
table2->map & tables_for_update && table1->s == table2->s)
{
// A table is updated through two aliases
if (table_partitioned &&
@ -1135,7 +1156,7 @@ int mysql_multi_update_prepare(THD *thd)
{
LEX *lex= thd->lex;
TABLE_LIST *table_list= lex->query_tables;
TABLE_LIST *tl, *leaves;
TABLE_LIST *tl;
List<Item> *fields= &lex->select_lex.item_list;
table_map tables_for_update;
bool update_view= 0;
@ -1163,7 +1184,7 @@ int mysql_multi_update_prepare(THD *thd)
open_tables(thd, &table_list, &table_count,
(thd->stmt_arena->is_stmt_prepare() ?
MYSQL_OPEN_FORCE_SHARED_MDL : 0))) ||
mysql_handle_derived(lex, &mysql_derived_prepare))
mysql_handle_derived(lex, DT_INIT))
DBUG_RETURN(TRUE);
/*
setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables()
@ -1171,11 +1192,20 @@ int mysql_multi_update_prepare(THD *thd)
call in setup_tables()).
*/
//We need to merge for insert prior to prepare.
if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(TRUE);
if (mysql_handle_derived(lex, DT_PREPARE))
DBUG_RETURN(TRUE);
if (setup_tables_and_check_access(thd, &lex->select_lex.context,
&lex->select_lex.top_join_list,
table_list,
&lex->select_lex.leaf_tables, FALSE,
UPDATE_ACL, SELECT_ACL))
lex->select_lex.leaf_tables, FALSE,
UPDATE_ACL, SELECT_ACL, FALSE))
DBUG_RETURN(TRUE);
if (lex->select_lex.handle_derived(thd->lex, DT_MERGE))
DBUG_RETURN(TRUE);
if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0))
@ -1197,15 +1227,14 @@ int mysql_multi_update_prepare(THD *thd)
thd->table_map_for_update= tables_for_update= get_table_map(fields);
leaves= lex->select_lex.leaf_tables;
if (unsafe_key_update(leaves, tables_for_update))
if (unsafe_key_update(lex->select_lex.leaf_tables, tables_for_update))
DBUG_RETURN(true);
/*
Setup timestamp handling and locking mode
*/
for (tl= leaves; tl; tl= tl->next_leaf)
List_iterator<TABLE_LIST> ti(lex->select_lex.leaf_tables);
while ((tl= ti++))
{
TABLE *table= tl->table;
/* Only set timestamp column if this is not modified */
@ -1252,7 +1281,7 @@ int mysql_multi_update_prepare(THD *thd)
for (tl= table_list; tl; tl= tl->next_local)
{
/* Check access privileges for table */
if (!tl->derived)
if (!tl->is_derived())
{
uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL;
if (check_access(thd, want_privilege, tl->db,
@ -1267,7 +1296,7 @@ int mysql_multi_update_prepare(THD *thd)
/* check single table update for view compound from several tables */
for (tl= table_list; tl; tl= tl->next_local)
{
if (tl->effective_algorithm == VIEW_ALGORITHM_MERGE)
if (tl->is_merged_derived())
{
TABLE_LIST *for_update= 0;
if (tl->check_single_table(&for_update, tables_for_update, tl))
@ -1293,7 +1322,8 @@ int mysql_multi_update_prepare(THD *thd)
*/
lex->select_lex.exclude_from_table_unique_test= TRUE;
/* We only need SELECT privilege for columns in the values list */
for (tl= leaves; tl; tl= tl->next_leaf)
ti.rewind();
while ((tl= ti++))
{
TABLE *table= tl->table;
TABLE_LIST *tlist;
@ -1321,15 +1351,10 @@ int mysql_multi_update_prepare(THD *thd)
further check in multi_update::prepare whether to use record cache.
*/
lex->select_lex.exclude_from_table_unique_test= FALSE;
if (thd->fill_derived_tables() &&
mysql_handle_derived(lex, &mysql_derived_filling))
{
mysql_handle_derived(lex, &mysql_derived_cleanup);
DBUG_RETURN(TRUE);
}
mysql_handle_derived(lex, &mysql_derived_cleanup);
if (lex->select_lex.save_prep_leaf_tables(thd))
DBUG_RETURN(TRUE);
DBUG_RETURN (FALSE);
}
@ -1354,7 +1379,7 @@ bool mysql_multi_update(THD *thd,
DBUG_ENTER("mysql_multi_update");
if (!(*result= new multi_update(table_list,
thd->lex->select_lex.leaf_tables,
&thd->lex->select_lex.leaf_tables,
fields, values,
handle_duplicates, ignore)))
{
@ -1390,7 +1415,7 @@ bool mysql_multi_update(THD *thd,
multi_update::multi_update(TABLE_LIST *table_list,
TABLE_LIST *leaves_list,
List<TABLE_LIST> *leaves_list,
List<Item> *field_list, List<Item> *value_list,
enum enum_duplicates handle_duplicates_arg,
bool ignore_arg)
@ -1408,6 +1433,7 @@ multi_update::multi_update(TABLE_LIST *table_list,
int multi_update::prepare(List<Item> &not_used_values,
SELECT_LEX_UNIT *lex_unit)
{
TABLE_LIST *table_ref;
SQL_I_List<TABLE_LIST> update;
@ -1417,6 +1443,7 @@ int multi_update::prepare(List<Item> &not_used_values,
List_iterator_fast<Item> value_it(*values);
uint i, max_fields;
uint leaf_table_count= 0;
List_iterator<TABLE_LIST> ti(*leaves);
DBUG_ENTER("multi_update::prepare");
thd->count_cuted_fields= CHECK_FIELD_WARN;
@ -1436,7 +1463,7 @@ int multi_update::prepare(List<Item> &not_used_values,
TABLE::tmp_set by pointing TABLE::read_set to it and then restore it after
setup_fields().
*/
for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf)
while ((table_ref= ti++))
{
TABLE *table= table_ref->table;
if (tables_to_update & table->map)
@ -1454,7 +1481,8 @@ int multi_update::prepare(List<Item> &not_used_values,
int error= setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0);
for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf)
ti.rewind();
while ((table_ref= ti++))
{
TABLE *table= table_ref->table;
if (tables_to_update & table->map)
@ -1483,7 +1511,8 @@ int multi_update::prepare(List<Item> &not_used_values,
*/
update.empty();
for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf)
ti.rewind();
while ((table_ref= ti++))
{
/* TODO: add support of view of join support */
TABLE *table=table_ref->table;
@ -1709,9 +1738,9 @@ loop_end:
{
table_map unupdated_tables= table_ref->check_option->used_tables() &
~first_table_for_update->map;
for (TABLE_LIST *tbl_ref =leaves;
unupdated_tables && tbl_ref;
tbl_ref= tbl_ref->next_leaf)
List_iterator<TABLE_LIST> ti(*leaves);
TABLE_LIST *tbl_ref;
while ((tbl_ref= ti++) && unupdated_tables)
{
if (unupdated_tables & tbl_ref->table->map)
unupdated_tables&= ~tbl_ref->table->map;
@ -1737,7 +1766,8 @@ loop_end:
do
{
Field_string *field= new Field_string(tbl->file->ref_length, 0,
tbl->alias, &my_charset_bin);
tbl->alias.c_ptr(),
&my_charset_bin);
if (!field)
DBUG_RETURN(1);
field->init(tbl);
@ -1809,7 +1839,7 @@ multi_update::~multi_update()
}
bool multi_update::send_data(List<Item> &not_used_values)
int multi_update::send_data(List<Item> &not_used_values)
{
TABLE_LIST *cur_table;
DBUG_ENTER("multi_update::send_data");
@ -1835,6 +1865,14 @@ bool multi_update::send_data(List<Item> &not_used_values)
if (table == table_to_update)
{
/*
We can use compare_record() to optimize away updates if
the table handler is returning all columns OR if
if all updated columns are read
*/
bool can_compare_record;
can_compare_record= records_are_comparable(table);
table->status|= STATUS_UPDATED;
store_record(table,record[1]);
if (fill_record_n_invoke_before_triggers(thd, *fields_for_table[offset],
@ -1849,7 +1887,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
*/
table->auto_increment_field_not_null= FALSE;
found++;
if (!records_are_comparable(table) || compare_records(table))
if (!can_compare_record || compare_record(table))
{
int error;
if ((error= cur_table->view_check_option(thd, ignore)) !=
@ -1946,7 +1984,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
*values_for_table[offset], TRUE, FALSE);
/* Write row, ignoring duplicated updates to a row */
error= tmp_table->file->ha_write_row(tmp_table->record[0]);
error= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]);
if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE)
{
if (error &&
@ -2040,6 +2078,7 @@ int multi_update::do_updates()
DBUG_RETURN(0);
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
bool can_compare_record;
uint offset= cur_table->shared;
table = cur_table->table;
@ -2088,6 +2127,8 @@ int multi_update::do_updates()
goto err;
}
can_compare_record= records_are_comparable(table);
for (;;)
{
if (thd->killed && trans_safe)
@ -2135,7 +2176,7 @@ int multi_update::do_updates()
TRG_ACTION_BEFORE, TRUE))
goto err2;
if (!records_are_comparable(table) || compare_records(table))
if (!can_compare_record || compare_record(table))
{
int error;
if ((error= cur_table->view_check_option(thd, ignore)) !=