summaryrefslogtreecommitdiff
path: root/sql/sql_base.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r--sql/sql_base.cc135
1 files changed, 116 insertions, 19 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 0b993748fc3..7ecbce4154a 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -489,12 +489,20 @@ static TABLE_SHARE
"no such table" errors.
@todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
*/
- if (thd->is_error() && table_list->belong_to_view)
+ if (thd->is_error())
{
- TABLE_LIST *view= table_list->belong_to_view;
- thd->clear_error();
- my_error(ER_VIEW_INVALID, MYF(0),
- view->view_db.str, view->view_name.str);
+ if (table_list->parent_l)
+ {
+ thd->clear_error();
+ my_error(ER_WRONG_MRG_TABLE, MYF(0));
+ }
+ else if (table_list->belong_to_view)
+ {
+ TABLE_LIST *view= table_list->belong_to_view;
+ thd->clear_error();
+ my_error(ER_VIEW_INVALID, MYF(0),
+ view->view_db.str, view->view_name.str);
+ }
}
DBUG_RETURN(0);
}
@@ -3731,6 +3739,20 @@ void assign_new_table_id(TABLE_SHARE *share)
DBUG_VOID_RETURN;
}
+#ifndef DBUG_OFF
+/* Cause a spurious statement reprepare for debug purposes. */
+static bool inject_reprepare(THD *thd)
+{
+ if (thd->m_reprepare_observer && thd->stmt_arena->is_reprepared == FALSE)
+ {
+ thd->m_reprepare_observer->report_error(thd);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif
+
/**
Compare metadata versions of an element obtained from the table
definition cache and its corresponding node in the parse tree.
@@ -3784,13 +3806,7 @@ check_and_update_table_version(THD *thd,
tables->set_table_ref_id(table_share);
}
- DBUG_EXECUTE_IF("reprepare_each_statement",
- if (thd->m_reprepare_observer &&
- thd->stmt_arena->is_reprepared == FALSE)
- {
- thd->m_reprepare_observer->report_error(thd);
- return TRUE;
- });
+ DBUG_EXECUTE_IF("reprepare_each_statement", return inject_reprepare(thd););
return FALSE;
}
@@ -4365,6 +4381,38 @@ bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
/*
+ Return a appropriate read lock type given a table object.
+
+ @param thd Thread context
+ @param table TABLE object for table to be locked
+
+ @remark Due to a statement-based replication limitation, statements such as
+ INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
+ to grab a TL_READ_NO_INSERT lock on the source table in order to
+ prevent the replication of a concurrent statement that modifies the
+ source table. If such a statement gets applied on the slave before
+ the INSERT .. SELECT statement finishes, data on the master could
+ differ from data on the slave and end-up with a discrepancy between
+ the binary log and table state. Furthermore, this does not apply to
+ I_S and log tables as it's always unsafe to replicate such tables
+ under statement-based replication as the table on the slave might
+ contain other data (ie: general_log is enabled on the slave). The
+ statement will be marked as unsafe for SBR in decide_logging_format().
+*/
+
+thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
+{
+ bool log_on= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG);
+ ulong binlog_format= thd->variables.binlog_format;
+ if ((log_on == FALSE) || (binlog_format == BINLOG_FORMAT_ROW) ||
+ (table->s->table_category == TABLE_CATEGORY_PERFORMANCE))
+ return TL_READ;
+ else
+ return TL_READ_NO_INSERT;
+}
+
+
+/*
Open all tables in list
SYNOPSIS
@@ -4638,6 +4686,9 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{
if (tables->lock_type == TL_WRITE_DEFAULT)
tables->table->reginfo.lock_type= thd->update_lock_default;
+ else if (tables->lock_type == TL_READ_DEFAULT)
+ tables->table->reginfo.lock_type=
+ read_lock_type_for_table(thd, tables->table);
else if (tables->table->s->tmp_table == NO_TMP_TABLE)
tables->table->reginfo.lock_type= tables->lock_type;
}
@@ -5045,7 +5096,11 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables)
void* prev_ht= NULL;
for (TABLE_LIST *table= tables; table; table= table->next_global)
{
- if (!table->placeholder() && table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ if (table->placeholder())
+ continue;
+ if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE)
+ thd->lex->set_stmt_unsafe();
+ if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
{
ulonglong const flags= table->table->file->ha_table_flags();
DBUG_PRINT("info", ("table: %s; ha_table_flags: %s%s",
@@ -5709,8 +5764,21 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
{
/* This is a base table. */
DBUG_ASSERT(nj_col->view_field == NULL);
- DBUG_ASSERT(nj_col->table_ref->table == nj_col->table_field->table);
- found_field= nj_col->table_field;
+ /*
+ This fix_fields is not necessary (initially this item is fixed by
+ the Item_field constructor; after reopen_tables the Item_func_eq
+ calls fix_fields on that item), it's just a check during table
+ reopening for columns that was dropped by the concurrent connection.
+ */
+ if (!nj_col->table_field->fixed &&
+ nj_col->table_field->fix_fields(thd, (Item **)&nj_col->table_field))
+ {
+ DBUG_PRINT("info", ("column '%s' was dropped by the concurrent connection",
+ nj_col->table_field->name));
+ DBUG_RETURN(NULL);
+ }
+ DBUG_ASSERT(nj_col->table_ref->table == nj_col->table_field->field->table);
+ found_field= nj_col->table_field->field;
update_field_dependencies(thd, found_field, nj_col->table_ref->table);
}
@@ -6635,7 +6703,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
const char *field_name_1;
/* true if field_name_1 is a member of using_fields */
bool is_using_column_1;
- if (!(nj_col_1= it_1.get_or_create_column_ref(leaf_1)))
+ if (!(nj_col_1= it_1.get_or_create_column_ref(thd, leaf_1)))
goto err;
field_name_1= nj_col_1->name();
is_using_column_1= using_fields &&
@@ -6656,7 +6724,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
{
Natural_join_column *cur_nj_col_2;
const char *cur_field_name_2;
- if (!(cur_nj_col_2= it_2.get_or_create_column_ref(leaf_2)))
+ if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, leaf_2)))
goto err;
cur_field_name_2= cur_nj_col_2->name();
DBUG_PRINT ("info", ("cur_field_name_2=%s.%s",
@@ -7146,15 +7214,24 @@ static bool setup_natural_join_row_types(THD *thd,
TABLE_LIST *left_neighbor;
/* Table reference to the right of the current. */
TABLE_LIST *right_neighbor= NULL;
+ bool save_first_natural_join_processing=
+ context->select_lex->first_natural_join_processing;
+
+ context->select_lex->first_natural_join_processing= FALSE;
/* Note that tables in the list are in reversed order */
for (left_neighbor= table_ref_it++; left_neighbor ; )
{
table_ref= left_neighbor;
left_neighbor= table_ref_it++;
- /* For stored procedures do not redo work if already done. */
- if (context->select_lex->first_execution)
+ /*
+ Do not redo work if already done:
+ 1) for stored procedures,
+ 2) for multitable update after lock failure and table reopening.
+ */
+ if (save_first_natural_join_processing)
{
+ context->select_lex->first_natural_join_processing= FALSE;
if (store_top_level_join_columns(thd, table_ref,
left_neighbor, right_neighbor))
return TRUE;
@@ -7299,6 +7376,22 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
if (ref_pointer_array)
bzero(ref_pointer_array, sizeof(Item *) * fields.elements);
+ /*
+ We call set_entry() there (before fix_fields() of the whole list of field
+ items) because:
+ 1) the list of field items has same order as in the query, and the
+ Item_func_get_user_var item may go before the Item_func_set_user_var:
+ SELECT @a, @a := 10 FROM t;
+ 2) The entry->update_query_id value controls constantness of
+ Item_func_get_user_var items, so in presence of Item_func_set_user_var
+ items we have to refresh their entries before fixing of
+ Item_func_get_user_var items.
+ */
+ List_iterator<Item_func_set_user_var> li(thd->lex->set_var_list);
+ Item_func_set_user_var *var;
+ while ((var= li++))
+ var->set_entry(thd, FALSE);
+
Item **ref= ref_pointer_array;
thd->lex->current_select->cur_pos_in_select_list= 0;
while ((item= it++))
@@ -7680,6 +7773,10 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
if (!(item= field_iterator.create_item(thd)))
DBUG_RETURN(TRUE);
+ DBUG_ASSERT(item->fixed);
+ /* cache the table for the Item_fields inserted by expanding stars */
+ if (item->type() == Item::FIELD_ITEM && tables->cacheable_table)
+ ((Item_field *)item)->cached_table= tables;
if (!found)
{