summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2015-10-09 17:12:26 +0200
committerSergei Golubchik <serg@mariadb.org>2015-10-09 17:12:26 +0200
commitcfeedbfd3e292f61c7da8f0a7f86307cbeeddb64 (patch)
tree3629d1de148b14915a35a21f809e1a2ea6a08619 /sql
parentbff1af983ad7b0bed6c3973e4d13297df5fe2791 (diff)
parent16c4b3c68b06653592a9500050ad977a38f4ebae (diff)
downloadmariadb-git-cfeedbfd3e292f61c7da8f0a7f86307cbeeddb64.tar.gz
Merge branch '5.5' into 10.0
Diffstat (limited to 'sql')
-rw-r--r--sql/field.h17
-rw-r--r--sql/field_conv.cc16
-rw-r--r--sql/item_cmpfunc.cc54
-rw-r--r--sql/item_func.cc53
-rw-r--r--sql/item_func.h39
-rw-r--r--sql/item_subselect.cc26
-rw-r--r--sql/item_subselect.h6
-rw-r--r--sql/log_event.cc15
-rw-r--r--sql/mysqld.cc18
-rw-r--r--sql/opt_range.cc17
-rw-r--r--sql/opt_subselect.cc49
-rw-r--r--sql/partition_info.cc16
-rw-r--r--sql/sql_acl.cc56
-rw-r--r--sql/sql_base.cc14
-rw-r--r--sql/sql_handler.cc37
-rw-r--r--sql/sql_handler.h5
-rw-r--r--sql/sql_insert.cc56
-rw-r--r--sql/sql_load.cc13
-rw-r--r--sql/sql_parse.cc29
-rw-r--r--sql/sql_select.cc5
-rw-r--r--sql/sql_show.cc15
-rw-r--r--sql/sql_yacc.yy12
22 files changed, 386 insertions, 182 deletions
diff --git a/sql/field.h b/sql/field.h
index 4e3a9f4c7b1..c64d6bc443c 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1,6 +1,6 @@
#ifndef FIELD_INCLUDED
#define FIELD_INCLUDED
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
@@ -977,6 +977,16 @@ public:
/* Hash value */
virtual void hash(ulong *nr, ulong *nr2);
+/**
+ Checks whether a string field is part of write_set.
+
+ @return
+ FALSE - If field is not char/varchar/....
+ - If field is char/varchar/.. and is not part of write set.
+ TRUE - If field is char/varchar/.. and is part of write set.
+*/
+ virtual bool is_updatable() const { return FALSE; }
+
/* Check whether the field can be used as a join attribute in hash join */
virtual bool hash_join_is_possible() { return TRUE; }
virtual bool eq_cmp_as_binary() { return TRUE; }
@@ -1174,6 +1184,11 @@ public:
int store_decimal(const my_decimal *d);
uint32 max_data_length() const;
+ bool is_updatable() const
+ {
+ DBUG_ASSERT(table && table->write_set);
+ return bitmap_is_set(table->write_set, field_index);
+ }
bool match_collation_to_optimize_range() const { return true; }
};
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index e31f7c5f005..79d579b6828 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -1,7 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2010, 2012, Monty Program Ab
-
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -905,15 +904,10 @@ int field_conv_incompatible(Field *to, Field *from)
{ // Be sure the value is stored
Field_blob *blob=(Field_blob*) to;
from->val_str(&blob->value);
- /*
- Copy value if copy_blobs is set, or source is not a string and
- we have a pointer to its internal string conversion buffer.
- */
- if (to->table->copy_blobs ||
- (!blob->value.is_alloced() &&
- from_real_type != MYSQL_TYPE_STRING &&
- from_real_type != MYSQL_TYPE_VARCHAR))
+
+ if (!blob->value.is_alloced() && from->is_updatable())
blob->value.copy();
+
return blob->store(blob->value.ptr(),blob->value.length(),from->charset());
}
if (from_real_type == MYSQL_TYPE_ENUM &&
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 90eef1ea55c..830a12ea48d 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1466,9 +1466,36 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg)
bool Item_in_optimizer::fix_left(THD *thd)
{
DBUG_ENTER("Item_in_optimizer::fix_left");
- if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) ||
- (!cache && !(cache= Item_cache::get_cache(args[0]))))
+ /*
+ Here we will store pointer on place of main storage of left expression.
+ For usual IN (ALL/ANY) it is subquery left_expr.
+ For other cases (MAX/MIN optimization, non-transformed EXISTS (10.0))
+ it is args[0].
+ */
+ Item **ref0= args;
+ if (args[1]->type() == Item::SUBSELECT_ITEM &&
+ ((Item_subselect *)args[1])->is_in_predicate())
+ {
+ /*
+ left_expr->fix_fields() may cause left_expr to be substituted for
+ another item. (e.g. an Item_field may be changed into Item_ref). This
+ transformation is undone at the end of statement execution (e.g. the
+ Item_ref is deleted). However, Item_in_optimizer::args[0] may keep
+ the pointer to the post-transformation item. Because of that, on the
+ next execution we need to copy args[1]->left_expr again.
+ */
+ ref0= &(((Item_in_subselect *)args[1])->left_expr);
+ args[0]= ((Item_in_subselect *)args[1])->left_expr;
+ }
+ if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) ||
+ (!cache && !(cache= Item_cache::get_cache(*ref0))))
DBUG_RETURN(1);
+ /*
+ During fix_field() expression could be substituted.
+ So we copy changes before use
+ */
+ if (args[0] != (*ref0))
+ args[0]= (*ref0);
DBUG_PRINT("info", ("actual fix fields"));
cache->setup(args[0]);
@@ -1531,6 +1558,16 @@ bool Item_in_optimizer::fix_left(THD *thd)
bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
+ Item_subselect *sub= 0;
+ uint col;
+
+ /*
+ MAX/MIN optimization can convert the subquery into
+ expr + Item_singlerow_subselect
+ */
+ if (args[1]->type() == Item::SUBSELECT_ITEM)
+ sub= (Item_subselect *)args[1];
+
if (fix_left(thd))
return TRUE;
if (args[0]->maybe_null)
@@ -1538,12 +1575,11 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
if (!args[1]->fixed && args[1]->fix_fields(thd, args+1))
return TRUE;
-
- Item_in_subselect * sub= (Item_in_subselect *)args[1];
if (!invisible_mode() &&
- args[0]->cols() != sub->engine->cols())
+ ((sub && ((col= args[0]->cols()) != sub->engine->cols())) ||
+ (!sub && (args[1]->cols() != (col= 1)))))
{
- my_error(ER_OPERAND_COLUMNS, MYF(0), args[0]->cols());
+ my_error(ER_OPERAND_COLUMNS, MYF(0), col);
return TRUE;
}
if (args[1]->maybe_null)
@@ -2756,7 +2792,8 @@ Item_func_if::str_op(String *str)
String *res=arg->val_str(str);
if (res)
res->set_charset(collation.collation);
- null_value=arg->null_value;
+ if ((null_value=arg->null_value))
+ res= NULL;
return res;
}
@@ -2767,7 +2804,8 @@ Item_func_if::decimal_op(my_decimal *decimal_value)
DBUG_ASSERT(fixed == 1);
Item *arg= args[0]->val_bool() ? args[1] : args[2];
my_decimal *value= arg->val_decimal(decimal_value);
- null_value= arg->null_value;
+ if ((null_value= arg->null_value))
+ value= NULL;
return value;
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index da688689148..6e21f357f41 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -885,7 +885,7 @@ String *Item_func_hybrid_result_type::val_str(String *str)
case DECIMAL_RESULT:
{
my_decimal decimal_value, *val;
- if (!(val= decimal_op(&decimal_value)))
+ if (!(val= decimal_op_with_null_check(&decimal_value)))
return 0; // null is set
my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val);
str->set_charset(collation.collation);
@@ -912,24 +912,22 @@ String *Item_func_hybrid_result_type::val_str(String *str)
if (is_temporal_type(field_type()))
{
MYSQL_TIME ltime;
- if (date_op(&ltime,
- field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) ||
- str->alloc(MAX_DATE_STRING_REP_LENGTH))
- {
- null_value= 1;
+ if (date_op_with_null_check(&ltime) ||
+ (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH)))
return (String *) 0;
- }
ltime.time_type= mysql_type_to_time_type(field_type());
str->length(my_TIME_to_str(&ltime, const_cast<char*>(str->ptr()), decimals));
str->set_charset(&my_charset_bin);
+ DBUG_ASSERT(!null_value);
return str;
}
- return str_op(&str_value);
+ return str_op_with_null_check(&str_value);
case TIME_RESULT:
case ROW_RESULT:
case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
}
+ DBUG_ASSERT(!null_value || (str == NULL));
return str;
}
@@ -942,7 +940,7 @@ double Item_func_hybrid_result_type::val_real()
{
my_decimal decimal_value, *val;
double result;
- if (!(val= decimal_op(&decimal_value)))
+ if (!(val= decimal_op_with_null_check(&decimal_value)))
return 0.0; // null is set
my_decimal2double(E_DEC_FATAL_ERROR, val, &result);
return result;
@@ -959,18 +957,14 @@ double Item_func_hybrid_result_type::val_real()
if (is_temporal_type(field_type()))
{
MYSQL_TIME ltime;
- if (date_op(&ltime,
- field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0 ))
- {
- null_value= 1;
+ if (date_op_with_null_check(&ltime))
return 0;
- }
ltime.time_type= mysql_type_to_time_type(field_type());
return TIME_to_double(&ltime);
}
char *end_not_used;
int err_not_used;
- String *res= str_op(&str_value);
+ String *res= str_op_with_null_check(&str_value);
return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
&end_not_used, &err_not_used) : 0.0);
}
@@ -990,7 +984,7 @@ longlong Item_func_hybrid_result_type::val_int()
case DECIMAL_RESULT:
{
my_decimal decimal_value, *val;
- if (!(val= decimal_op(&decimal_value)))
+ if (!(val= decimal_op_with_null_check(&decimal_value)))
return 0; // null is set
longlong result;
my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result);
@@ -1005,18 +999,14 @@ longlong Item_func_hybrid_result_type::val_int()
if (is_temporal_type(field_type()))
{
MYSQL_TIME ltime;
- if (date_op(&ltime,
- field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0))
- {
- null_value= 1;
+ if (date_op_with_null_check(&ltime))
return 0;
- }
ltime.time_type= mysql_type_to_time_type(field_type());
return TIME_to_ulonglong(&ltime);
}
int err_not_used;
String *res;
- if (!(res= str_op(&str_value)))
+ if (!(res= str_op_with_null_check(&str_value)))
return 0;
char *end= (char*) res->ptr() + res->length();
@@ -1038,17 +1028,21 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
DBUG_ASSERT(fixed == 1);
switch (cached_result_type) {
case DECIMAL_RESULT:
- val= decimal_op(decimal_value);
+ val= decimal_op_with_null_check(decimal_value);
break;
case INT_RESULT:
{
longlong result= int_op();
+ if (null_value)
+ return NULL;
int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value);
break;
}
case REAL_RESULT:
{
double result= (double)real_op();
+ if (null_value)
+ return NULL;
double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value);
break;
}
@@ -1057,19 +1051,20 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
if (is_temporal_type(field_type()))
{
MYSQL_TIME ltime;
- if (date_op(&ltime,
- field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0))
+ if (date_op_with_null_check(&ltime))
{
my_decimal_set_zero(decimal_value);
- null_value= 1;
return 0;
}
ltime.time_type= mysql_type_to_time_type(field_type());
return date2my_decimal(&ltime, decimal_value);
}
String *res;
- if (!(res= str_op(&str_value)))
+ if (!(res= str_op_with_null_check(&str_value)))
+ {
+ null_value= 1;
return NULL;
+ }
str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(),
res->length(), res->charset(), decimal_value);
@@ -1092,7 +1087,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
case DECIMAL_RESULT:
{
my_decimal value, *res;
- if (!(res= decimal_op(&value)) ||
+ if (!(res= decimal_op_with_null_check(&value)) ||
decimal_to_datetime_with_warn(res, ltime, fuzzydate,
field_name_or_null()))
goto err;
@@ -1122,7 +1117,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
return date_op(ltime, fuzzydate);
char buff[40];
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
- if (!(res= str_op(&tmp)) ||
+ if (!(res= str_op_with_null_check(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
ltime, fuzzydate))
goto err;
diff --git a/sql/item_func.h b/sql/item_func.h
index ce1f2fdd676..0b3454fa4b0 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -385,17 +385,17 @@ public:
void no_rows_in_result()
{
- bool_func_call_args info;
- info.original_func_item= this;
- info.bool_function= &Item::no_rows_in_result;
- walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info);
+ for (uint i= 0; i < arg_count; i++)
+ {
+ args[i]->no_rows_in_result();
+ }
}
void restore_to_before_no_rows_in_result()
{
- bool_func_call_args info;
- info.original_func_item= this;
- info.bool_function= &Item::restore_to_before_no_rows_in_result;
- walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info);
+ for (uint i= 0; i < arg_count; i++)
+ {
+ args[i]->no_rows_in_result();
+ }
}
};
@@ -419,6 +419,29 @@ public:
class Item_func_hybrid_result_type: public Item_func
{
+ /*
+ Helper methods to make sure that the result of
+ decimal_op(), str_op() and date_op() is properly synched with null_value.
+ */
+ bool date_op_with_null_check(MYSQL_TIME *ltime)
+ {
+ bool rc= date_op(ltime,
+ field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0);
+ DBUG_ASSERT(!rc ^ null_value);
+ return rc;
+ }
+ String *str_op_with_null_check(String *str)
+ {
+ String *res= str_op(str);
+ DBUG_ASSERT((res != NULL) ^ null_value);
+ return res;
+ }
+ my_decimal *decimal_op_with_null_check(my_decimal *decimal_buffer)
+ {
+ my_decimal *res= decimal_op(decimal_buffer);
+ DBUG_ASSERT((res != NULL) ^ null_value);
+ return res;
+ }
protected:
Item_result cached_result_type;
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index fe2352f3008..ce01fc7a3b8 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1378,7 +1378,7 @@ Item_in_subselect::Item_in_subselect(Item * left_exp,
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
DBUG_PRINT("info", ("in_strategy: %u", (uint)in_strategy));
- left_expr= left_exp;
+ left_expr_orig= left_expr= left_exp;
func= &eq_creator;
init(select_lex, new select_exists_subselect(this));
max_columns= UINT_MAX;
@@ -1401,7 +1401,7 @@ Item_allany_subselect::Item_allany_subselect(Item * left_exp,
:Item_in_subselect(), func_creator(fc), all(all_arg)
{
DBUG_ENTER("Item_allany_subselect::Item_allany_subselect");
- left_expr= left_exp;
+ left_expr_orig= left_expr= left_exp;
func= func_creator(all_arg);
init(select_lex, new select_exists_subselect(this));
max_columns= 1;
@@ -3032,15 +3032,13 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
arena= thd->activate_stmt_arena_if_needed(&backup);
if (!optimizer)
{
- result= (!(optimizer= new Item_in_optimizer(left_expr, this)));
+ result= (!(optimizer= new Item_in_optimizer(left_expr_orig, this)));
if (result)
goto out;
}
thd->lex->current_select= current->return_after_parsing();
result= optimizer->fix_left(thd);
- /* fix_fields can change reference to left_expr, we need reassign it */
- left_expr= optimizer->arguments()[0];
thd->lex->current_select= current;
if (changed)
@@ -3107,11 +3105,13 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
{
uint outer_cols_num;
List<Item> *inner_cols;
+ char const *save_where= thd->where;
DBUG_ENTER("Item_in_subselect::fix_fields");
if (test_strategy(SUBS_SEMI_JOIN))
DBUG_RETURN( !( (*ref)= new Item_int(1)) );
+ thd->where= "IN/ALL/ANY subquery";
/*
Check if the outer and inner IN operands match in those cases when we
will not perform IN=>EXISTS transformation. Currently this is when we
@@ -3142,7 +3142,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
if (outer_cols_num != inner_cols->elements)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), outer_cols_num);
- DBUG_RETURN(TRUE);
+ goto err;
}
if (outer_cols_num > 1)
{
@@ -3152,20 +3152,24 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
{
inner_col= inner_col_it++;
if (inner_col->check_cols(left_expr->element_index(i)->cols()))
- DBUG_RETURN(TRUE);
+ goto err;
}
}
}
- if (thd_arg->lex->is_view_context_analysis() &&
- left_expr && !left_expr->fixed &&
+ if (left_expr && !left_expr->fixed &&
left_expr->fix_fields(thd_arg, &left_expr))
- DBUG_RETURN(TRUE);
+ goto err;
else
if (Item_subselect::fix_fields(thd_arg, ref))
- DBUG_RETURN(TRUE);
+ goto err;
fixed= TRUE;
+ thd->where= save_where;
DBUG_RETURN(FALSE);
+
+err:
+ thd->where= save_where;
+ DBUG_RETURN(TRUE);
}
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 92b269d02f1..3c0b7bd6ade 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -482,6 +482,12 @@ protected:
Item **having_item);
public:
Item *left_expr;
+ /*
+ Important for PS/SP: left_expr_orig is the item that left_expr originally
+ pointed at. That item is allocated on the statement arena, while
+ left_expr could later be changed to something on the execution arena.
+ */
+ Item *left_expr_orig;
/* Priority of this predicate in the convert-to-semi-join-nest process. */
int sj_convert_priority;
/*
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 411229d319a..215bb6df0f8 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2224,20 +2224,13 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
uint precision= meta >> 8;
uint decimals= meta & 0xFF;
uint bin_size= my_decimal_get_binary_size(precision, decimals);
- uint length;
my_decimal dec;
binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) ptr, &dec,
precision, decimals);
- int i, end;
- char buff[512], *pos;
- pos= buff;
- pos+= sprintf(buff, "%s", dec.sign() ? "-" : "");
- end= ROUND_UP(dec.frac) + ROUND_UP(dec.intg)-1;
- for (i=0; i < end; i++)
- pos+= sprintf(pos, "%09d.", dec.buf[i]);
- pos+= sprintf(pos, "%09d", dec.buf[i]);
- length= (uint) (pos - buff);
- my_b_write(file, buff, length);
+ int length= DECIMAL_MAX_STR_LENGTH;
+ char buff[DECIMAL_MAX_STR_LENGTH + 1];
+ decimal2string(&dec, buff, &length, 0, 0, 0);
+ my_b_write(file, (uchar*)buff, length);
my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)",
precision, decimals);
return bin_size;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index f542977a28e..204bf75909f 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4344,14 +4344,24 @@ static int init_common_variables()
{
if (lower_case_table_names_used)
{
+#if MYSQL_VERSION_ID < 100100
if (global_system_variables.log_warnings)
sql_print_warning("You have forced lower_case_table_names to 0 through "
"a command-line option, even though your file system "
"'%s' is case insensitive. This means that you can "
- "corrupt a MyISAM table by accessing it with "
- "different cases. You should consider changing "
- "lower_case_table_names to 1 or 2",
- mysql_real_data_home);
+ "corrupt your tables if you access them using names "
+ "with different letter case. You should consider "
+ "changing lower_case_table_names to 1 or 2",
+ mysql_real_data_home);
+#else
+ sql_print_error("The server option 'lower_case_table_names' is "
+ "configured to use case sensitive table names but the "
+ "data directory resides on a case-insensitive file system. "
+ "Please use a case sensitive file system for your data "
+ "directory or switch to a case-insensitive table name "
+ "mode.");
+#endif
+ return 1;
}
else
{
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 3f12748c071..da18a30ac78 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -4634,10 +4634,19 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
key_tree->min_flag |
key_tree->max_flag,
&subpart_iter);
- DBUG_ASSERT(res); /* We can't get "no satisfying subpartitions" */
+ if (res == 0)
+ {
+ /*
+ The only case where we can get "no satisfying subpartitions"
+ returned from the above call is when an error has occurred.
+ */
+ DBUG_ASSERT(range_par->thd->is_error());
+ return 0;
+ }
+
if (res == -1)
goto pop_and_go_right; /* all subpartitions satisfy */
-
+
uint32 subpart_id;
bitmap_clear_all(&ppar->subparts_bitmap);
while ((subpart_id= subpart_iter.get_next(&subpart_iter)) !=
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 062b43291fb..3d470b6ff5c 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -617,6 +617,18 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
thd->stmt_arena->state != Query_arena::PREPARED)
*/
{
+ SELECT_LEX *current= thd->lex->current_select;
+ thd->lex->current_select= current->return_after_parsing();
+ char const *save_where= thd->where;
+ thd->where= "IN/ALL/ANY subquery";
+
+ bool failure= !in_subs->left_expr->fixed &&
+ in_subs->left_expr->fix_fields(thd, &in_subs->left_expr);
+ thd->lex->current_select= current;
+ thd->where= save_where;
+ if (failure)
+ DBUG_RETURN(-1); /* purecov: deadcode */
+
/*
Check if the left and right expressions have the same # of
columns, i.e. we don't have a case like
@@ -630,18 +642,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
my_error(ER_OPERAND_COLUMNS, MYF(0), in_subs->left_expr->cols());
DBUG_RETURN(-1);
}
-
- SELECT_LEX *current= thd->lex->current_select;
- thd->lex->current_select= current->return_after_parsing();
- char const *save_where= thd->where;
- thd->where= "IN/ALL/ANY subquery";
-
- bool failure= !in_subs->left_expr->fixed &&
- in_subs->left_expr->fix_fields(thd, &in_subs->left_expr);
- thd->lex->current_select= current;
- thd->where= save_where;
- if (failure)
- DBUG_RETURN(-1); /* purecov: deadcode */
}
DBUG_PRINT("info", ("Checking if subq can be converted to semi-join"));
@@ -704,6 +704,12 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
if (!optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) &&
!optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION))
my_error(ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES, MYF(0));
+ /*
+ Transform each subquery predicate according to its overloaded
+ transformer.
+ */
+ if (subselect->select_transformer(join))
+ DBUG_RETURN(-1);
/*
If the subquery predicate is IN/=ANY, analyse and set all possible
@@ -755,12 +761,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
allany_subs->add_strategy(strategy);
}
- /*
- Transform each subquery predicate according to its overloaded
- transformer.
- */
- if (subselect->select_transformer(join))
- DBUG_RETURN(-1);
}
}
DBUG_RETURN(0);
@@ -1593,8 +1593,19 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
if (subq_pred->left_expr->cols() == 1)
{
nested_join->sj_outer_expr_list.push_back(subq_pred->left_expr);
+ /*
+ Create Item_func_eq. Note that
+ 1. this is done on the statement, not execution, arena
+ 2. if it's a PS then this happens only once - on the first execution.
+ On following re-executions, the item will be fix_field-ed normally.
+ 3. Thus it should be created as if it was fix_field'ed, in particular
+ all pointers to items in the execution arena should be protected
+ with thd->change_item_tree
+ */
Item_func_eq *item_eq=
- new Item_func_eq(subq_pred->left_expr, subq_lex->ref_pointer_array[0]);
+ new Item_func_eq(subq_pred->left_expr_orig, subq_lex->ref_pointer_array[0]);
+ if (subq_pred->left_expr_orig != subq_pred->left_expr)
+ thd->change_item_tree(item_eq->arguments(), subq_pred->left_expr);
item_eq->in_equality_no= 0;
sj_nest->sj_on_expr= and_items(sj_nest->sj_on_expr, item_eq);
}
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 4a42d7965b8..1607b1937df 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1665,15 +1666,22 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
{
int err= 0;
+ /* Check for partition expression. */
if (!list_of_part_fields)
{
DBUG_ASSERT(part_expr);
err= part_expr->walk(&Item::check_partition_func_processor, 0,
NULL);
- if (!err && is_sub_partitioned() && !list_of_subpart_fields)
- err= subpart_expr->walk(&Item::check_partition_func_processor, 0,
- NULL);
}
+
+ /* Check for sub partition expression. */
+ if (!err && is_sub_partitioned() && !list_of_subpart_fields)
+ {
+ DBUG_ASSERT(subpart_expr);
+ err= subpart_expr->walk(&Item::check_partition_func_processor, 0,
+ NULL);
+ }
+
if (err)
{
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 9cc9efae6f8..7d0fefeabd4 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -6720,16 +6720,18 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
for (tl= tables; number-- ; tl= tl->next_global)
{
- sctx= MY_TEST(tl->security_ctx) ? tl->security_ctx : thd->security_ctx;
+ TABLE_LIST *const t_ref=
+ tl->correspondent_table ? tl->correspondent_table : tl;
+ sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
const ACL_internal_table_access *access=
- get_cached_table_access(&tl->grant.m_internal,
- tl->get_db_name(),
- tl->get_table_name());
+ get_cached_table_access(&t_ref->grant.m_internal,
+ t_ref->get_db_name(),
+ t_ref->get_table_name());
if (access)
{
- switch(access->check(orig_want_access, &tl->grant.privilege))
+ switch(access->check(orig_want_access, &t_ref->grant.privilege))
{
case ACL_INTERNAL_ACCESS_GRANTED:
/*
@@ -6753,26 +6755,26 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
if (!want_access)
continue; // ok
- if (!(~tl->grant.privilege & want_access) ||
- tl->is_anonymous_derived_table() || tl->schema_table)
+ if (!(~t_ref->grant.privilege & want_access) ||
+ t_ref->is_anonymous_derived_table() || t_ref->schema_table)
{
/*
- It is subquery in the FROM clause. VIEW set tl->derived after
+ It is subquery in the FROM clause. VIEW set t_ref->derived after
table opening, but this function always called before table opening.
*/
- if (!tl->referencing_view)
+ if (!t_ref->referencing_view)
{
/*
If it's a temporary table created for a subquery in the FROM
clause, or an INFORMATION_SCHEMA table, drop the request for
a privilege.
*/
- tl->grant.want_privilege= 0;
+ t_ref->grant.want_privilege= 0;
}
continue;
}
- if (is_temporary_table(tl))
+ if (is_temporary_table(t_ref))
{
/*
If this table list element corresponds to a pre-opened temporary
@@ -6780,8 +6782,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
Note that during creation of temporary table we still need to check
if user has CREATE_TMP_ACL.
*/
- tl->grant.privilege|= TMP_TABLE_ACLS;
- tl->grant.want_privilege= 0;
+ t_ref->grant.privilege|= TMP_TABLE_ACLS;
+ t_ref->grant.want_privilege= 0;
continue;
}
@@ -6792,20 +6794,20 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
}
grant_table= table_hash_search(sctx->host, sctx->ip,
- tl->get_db_name(),
+ t_ref->get_db_name(),
sctx->priv_user,
- tl->get_table_name(),
+ t_ref->get_table_name(),
FALSE);
if (sctx->priv_role[0])
- grant_table_role= table_hash_search("", NULL, tl->get_db_name(),
+ grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(),
sctx->priv_role,
- tl->get_table_name(),
+ t_ref->get_table_name(),
TRUE);
if (!grant_table && !grant_table_role)
{
- want_access&= ~tl->grant.privilege;
- goto err;
+ want_access&= ~t_ref->grant.privilege;
+ goto err; // No grants
}
/*
@@ -6815,19 +6817,19 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
if (any_combination_will_do)
continue;
- tl->grant.grant_table_user= grant_table; // Remember for column test
- tl->grant.grant_table_role= grant_table_role;
- tl->grant.version= grant_version;
- tl->grant.privilege|= grant_table ? grant_table->privs : 0;
- tl->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
- tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
+ t_ref->grant.grant_table_user= grant_table; // Remember for column test
+ t_ref->grant.grant_table_role= grant_table_role;
+ t_ref->grant.version= grant_version;
+ t_ref->grant.privilege|= grant_table ? grant_table->privs : 0;
+ t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
+ t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
- if (!(~tl->grant.privilege & want_access))
+ if (!(~t_ref->grant.privilege & want_access))
continue;
if ((want_access&= ~((grant_table ? grant_table->cols : 0) |
(grant_table_role ? grant_table_role->cols : 0) |
- tl->grant.privilege)))
+ t_ref->grant.privilege)))
{
goto err; // impossible
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 7e2fe7b1b63..8f222761c60 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1094,13 +1094,20 @@ bool close_temporary_tables(THD *thd)
DBUG_RETURN(FALSE);
DBUG_ASSERT(!thd->rgi_slave);
+ /*
+ Ensure we don't have open HANDLERs for tables we are about to close.
+ This is necessary when close_temporary_tables() is called as part
+ of execution of BINLOG statement (e.g. for format description event).
+ */
+ mysql_ha_rm_temporary_tables(thd);
if (!mysql_bin_log.is_open())
{
TABLE *tmp_next;
- for (table= thd->temporary_tables; table; table= tmp_next)
+ for (TABLE *t= thd->temporary_tables; t; t= tmp_next)
{
- tmp_next= table->next;
- close_temporary(table, 1, 1);
+ tmp_next= t->next;
+ mysql_lock_remove(thd, thd->lock, t);
+ close_temporary(t, 1, 1);
}
thd->temporary_tables= 0;
DBUG_RETURN(FALSE);
@@ -1196,6 +1203,7 @@ bool close_temporary_tables(THD *thd)
strlen(table->s->table_name.str));
s_query.append(',');
next= table->next;
+ mysql_lock_remove(thd, thd->lock, table);
close_temporary(table, 1, 1);
}
thd->clear_error();
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 110bca96530..c2d4c32ab71 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2001, 2013, Oracle and/or its affiliates.
- Copyright (c) 2011, 2013, Monty Program Ab.
+/* Copyright (c) 2001, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1190,3 +1190,36 @@ void mysql_ha_set_explicit_lock_duration(THD *thd)
DBUG_VOID_RETURN;
}
+
+/**
+ Remove temporary tables from the HANDLER's hash table. The reason
+ for having a separate function, rather than calling
+ mysql_ha_rm_tables() is that it is not always feasible (e.g. in
+ close_temporary_tables) to obtain a TABLE_LIST containing the
+ temporary tables.
+
+ @See close_temporary_tables
+ @param thd Thread identifier.
+*/
+void mysql_ha_rm_temporary_tables(THD *thd)
+{
+ DBUG_ENTER("mysql_ha_rm_temporary_tables");
+
+ TABLE_LIST *tmp_handler_tables= NULL;
+ for (uint i= 0; i < thd->handler_tables_hash.records; i++)
+ {
+ TABLE_LIST *handler_table= reinterpret_cast<TABLE_LIST*>
+ (my_hash_element(&thd->handler_tables_hash, i));
+
+ if (handler_table->table && handler_table->table->s->tmp_table)
+ {
+ handler_table->next_local= tmp_handler_tables;
+ tmp_handler_tables= handler_table;
+ }
+ }
+
+ if (tmp_handler_tables)
+ mysql_ha_rm_tables(thd, tmp_handler_tables);
+
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/sql_handler.h b/sql/sql_handler.h
index 133f553675e..7fe5ae5bba8 100644
--- a/sql/sql_handler.h
+++ b/sql/sql_handler.h
@@ -1,6 +1,8 @@
#ifndef SQL_HANDLER_INCLUDED
#define SQL_HANDLER_INCLUDED
-/* Copyright (C) 2010 Monty Program Ab
+/* Copyright (c) 2006, 2015, Oracle and/or its affiliates.
+ Copyright (C) 2010, 2015, MariaDB
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
@@ -73,6 +75,7 @@ void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
void mysql_ha_cleanup(THD *thd);
void mysql_ha_set_explicit_lock_duration(THD *thd);
+void mysql_ha_rm_temporary_tables(THD *thd);
SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
enum enum_ha_read_modes mode, char *keyname,
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 1ec33a0a0ac..522f55cd102 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2010, 2014, SkySQL Ab.
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -3852,7 +3852,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
/* Add selected items to field list */
List_iterator_fast<Item> it(*items);
Item *item;
- Field *tmp_field;
DBUG_ENTER("create_table_from_items");
tmp_table.alias= 0;
@@ -3867,24 +3866,49 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
while ((item=it++))
{
- Create_field *cr_field;
- Field *field, *def_field;
+ Field *tmp_table_field;
if (item->type() == Item::FUNC_ITEM)
{
if (item->result_type() != STRING_RESULT)
- field= item->tmp_table_field(&tmp_table);
+ tmp_table_field= item->tmp_table_field(&tmp_table);
else
- field= item->tmp_table_field_from_field_type(&tmp_table, 0);
+ tmp_table_field= item->tmp_table_field_from_field_type(&tmp_table,
+ false);
}
else
- field= create_tmp_field(thd, &tmp_table, item, item->type(),
- (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0,
- 0);
- if (!field ||
- !(cr_field=new Create_field(field,(item->type() == Item::FIELD_ITEM ?
- ((Item_field *)item)->field :
- (Field*) 0))))
- DBUG_RETURN(0);
+ {
+ Field *from_field, * default_field;
+ tmp_table_field= create_tmp_field(thd, &tmp_table, item, item->type(),
+ (Item ***) NULL, &from_field, &default_field,
+ 0, 0, 0, 0, 0);
+ }
+
+ if (!tmp_table_field)
+ DBUG_RETURN(NULL);
+
+ Field *table_field;
+
+ switch (item->type())
+ {
+ /*
+ We have to take into account both the real table's fields and
+ pseudo-fields used in trigger's body. These fields are used
+ to copy defaults values later inside constructor of
+ the class Create_field.
+ */
+ case Item::FIELD_ITEM:
+ case Item::TRIGGER_FIELD_ITEM:
+ table_field= ((Item_field *) item)->field;
+ break;
+ default:
+ table_field= NULL;
+ }
+
+ Create_field *cr_field= new Create_field(tmp_table_field, table_field);
+
+ if (!cr_field)
+ DBUG_RETURN(NULL);
+
if (item->maybe_null)
cr_field->flags &= ~NOT_NULL_FLAG;
alter_info->create_list.push_back(cr_field);
@@ -3974,7 +3998,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
{
if (!thd->is_error()) // CREATE ... IF NOT EXISTS
my_ok(thd); // succeed, but did nothing
- DBUG_RETURN(0);
+ DBUG_RETURN(NULL);
}
DEBUG_SYNC(thd,"create_table_select_before_lock");
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 503cc579dd7..08687b20b00 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2010, 2014, SkySQL Ab.
+ Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -2028,8 +2028,15 @@ int READ_INFO::read_xml()
break;
case '/': /* close tag */
- level--;
chr= my_tospace(GET);
+ /* Decrease the 'level' only when (i) It's not an */
+ /* (without space) empty tag i.e. <tag/> or, (ii) */
+ /* It is of format <row col="val" .../> */
+ if(chr != '>' || in_tag)
+ {
+ level--;
+ in_tag= false;
+ }
if(chr != '>') /* if this is an empty tag <tag /> */
tag.length(0); /* we should keep tag value */
while(chr != '>' && chr != my_b_EOF)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index b2636cfff95..a665f0314e8 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, SkySQL Ab.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -5825,9 +5825,12 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
for (; i < number && tables != first_not_own_table && tables;
tables= tables->next_global, i++)
{
+ TABLE_LIST *const table_ref= tables->correspondent_table ?
+ tables->correspondent_table : tables;
+
ulong want_access= requirements;
- if (tables->security_ctx)
- sctx= tables->security_ctx;
+ if (table_ref->security_ctx)
+ sctx= table_ref->security_ctx;
else
sctx= backup_ctx;
@@ -5835,26 +5838,26 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
Register access for view underlying table.
Remove SHOW_VIEW_ACL, because it will be checked during making view
*/
- tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
+ table_ref->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
- if (tables->schema_table_reformed)
+ if (table_ref->schema_table_reformed)
{
- if (check_show_access(thd, tables))
+ if (check_show_access(thd, table_ref))
goto deny;
continue;
}
- DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0,
- tables->view != 0));
+ DBUG_PRINT("info", ("derived: %d view: %d", table_ref->derived != 0,
+ table_ref->view != 0));
- if (tables->is_anonymous_derived_table())
+ if (table_ref->is_anonymous_derived_table())
continue;
thd->security_ctx= sctx;
- if (check_access(thd, want_access, tables->get_db_name(),
- &tables->grant.privilege,
- &tables->grant.m_internal,
+ if (check_access(thd, want_access, table_ref->get_db_name(),
+ &table_ref->grant.privilege,
+ &table_ref->grant.m_internal,
0, no_errors))
goto deny;
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 22c14733ffa..edfd9f5ebd3 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -15600,6 +15600,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
case Item::FIELD_ITEM:
case Item::DEFAULT_VALUE_ITEM:
case Item::INSERT_VALUE_ITEM:
+ case Item::TRIGGER_FIELD_ITEM:
{
Item_field *field= (Item_field*) item;
bool orig_modify= modify_item;
@@ -18051,6 +18052,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
if (return_tab < join->return_tab)
join->return_tab= return_tab;
+ /* check for errors evaluating the condition */
+ if (join->thd->is_error())
+ DBUG_RETURN(NESTED_LOOP_ERROR);
+
if (join->return_tab < join_tab)
DBUG_RETURN(NESTED_LOOP_OK);
/*
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 103d39e7fd0..853c2888a6a 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -8104,13 +8104,14 @@ bool get_schema_tables_result(JOIN *join,
TABLE_LIST *table_list= tab->table->pos_in_table_list;
if (table_list->schema_table && thd->fill_information_schema_tables())
{
-#if MYSQL_VERSION_ID > 100105
-#error I_S tables only need to be re-populated if make_cond_for_info_schema() will preserve outer fields
- bool is_subselect= (&lex->unit != lex->current_select->master_unit() &&
- lex->current_select->master_unit()->item);
-#else
-#define is_subselect false
-#endif
+ /*
+ I_S tables only need to be re-populated if make_cond_for_info_schema()
+ preserves outer fields
+ */
+ bool is_subselect= &lex->unit != lex->current_select->master_unit() &&
+ lex->current_select->master_unit()->item &&
+ tab->select_cond &&
+ tab->select_cond->used_tables() & OUTER_REF_TABLE_BIT;
/* A value of 0 indicates a dummy implementation */
if (table_list->schema_table->fill_table == 0)
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d980de7e1a5..bb8215aaaec 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -11507,8 +11507,20 @@ procedure_clause:
if (add_proc_to_list(lex->thd, item))
MYSQL_YYABORT;
Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+
+ /*
+ PROCEDURE CLAUSE cannot handle subquery as one of its parameter,
+ so set expr_allows_subselect as false to disallow any subqueries
+ further. Reset expr_allows_subselect back to true once the
+ parameters are reduced.
+ */
+ Lex->expr_allows_subselect= false;
}
'(' procedure_list ')'
+ {
+ /* Subqueries are allowed from now.*/
+ Lex->expr_allows_subselect= true;
+ }
;
procedure_list: