MDEV-33971 NAME_CONST in WHERE clause replaced by inner item

Improve performance of queries like
  SELECT * FROM t1 WHERE field = NAME_CONST('a', 4);
by, in this example, replacing the WHERE clause with field = 4
in the case of ref access.

The rewrite is done during fix_fields and we disambiguate this
case from other cases of NAME_CONST by inspecting where we are
in parsing.  We rely on THD::where to accomplish this.  To
improve performance there, we change the type of THD::where to
be an enumeration, so we can avoid string comparisons during
Item_name_const::fix_fields.  Consequently, this patch also
changes all usages of THD::where to conform likewise.
This commit is contained in:
Dave Gosselin 2024-04-26 12:13:31 -04:00 committed by Dave Gosselin
parent a0a7b1c128
commit 02e38e2ece
20 changed files with 310 additions and 75 deletions

View File

@ -126,7 +126,7 @@ group by
a.text, b.id, b.betreff a.text, b.id, b.betreff
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Table 'b' from one of the SELECTs cannot be used in ORDER clause ERROR 42000: Table 'b' from one of the SELECTs cannot be used in order clause
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -142,7 +142,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Table 'b' from one of the SELECTs cannot be used in ORDER clause ERROR 42000: Table 'b' from one of the SELECTs cannot be used in order clause
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join

View File

@ -0,0 +1,87 @@
create table t1 (a int, b int);
insert into t1 values (1,1),(2,2);
explain format=json
select * from t1 where a=name_const('varname',1);
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 2,
"filtered": 100,
"attached_condition": "t1.a = 1"
}
}
}
explain format=json
select * from t1 left join t1 as t2 on t1.a=name_const('varname',1) and t1.b=t2.b;
EXPLAIN
{
"query_block": {
"select_id": 1,
"const_condition": "1",
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 2,
"filtered": 100
},
"block-nl-join": {
"table": {
"table_name": "t2",
"access_type": "ALL",
"rows": 2,
"filtered": 100
},
"buffer_type": "flat",
"buffer_size": "141",
"join_type": "BNL",
"attached_condition": "trigcond(t2.b = t1.b and trigcond(t1.a = 1))"
}
}
}
create table t2 (
a varchar(100) collate utf8_unicode_ci,
b int
);
insert into t2 values ('foo', 1),('bar', 1);
create procedure p1(var1 varchar(10))
update t2 set b=b+1 where a=var1;
call p1('foo');
call p1('foo');
call p1('foo');
select * from t2;
a b
foo 4
bar 1
create table t3 (
a varchar(100) collate utf8_unicode_ci,
b int
);
insert into t3 values ('foo', 1),('bar', 1);
select * from t3;
a b
foo 1
bar 1
explain format=json
update t3 set b=b+1 where a= NAME_CONST('var1',_latin1'foo' COLLATE 'latin1_swedish_ci');
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"update": 1,
"table_name": "t3",
"access_type": "ALL",
"rows": 2,
"attached_condition": "t3.a = convert(_latin1'foo' collate latin1_swedish_ci using utf8mb3)"
}
}
}
select * from t3 where a= NAME_CONST('var1',_latin1'foo' COLLATE 'latin1_swedish_ci');
a b
foo 1
drop procedure p1;
drop table t1, t2, t3;

View File

@ -0,0 +1,38 @@
#
# MDEV-33971 Using NAME_CONST() changes the plan
#
create table t1 (a int, b int);
insert into t1 values (1,1),(2,2);
explain format=json
select * from t1 where a=name_const('varname',1);
explain format=json
select * from t1 left join t1 as t2 on t1.a=name_const('varname',1) and t1.b=t2.b;
create table t2 (
a varchar(100) collate utf8_unicode_ci,
b int
);
insert into t2 values ('foo', 1),('bar', 1);
create procedure p1(var1 varchar(10))
update t2 set b=b+1 where a=var1;
call p1('foo');
call p1('foo');
call p1('foo');
select * from t2;
create table t3 (
a varchar(100) collate utf8_unicode_ci,
b int
);
insert into t3 values ('foo', 1),('bar', 1);
select * from t3;
explain format=json
update t3 set b=b+1 where a= NAME_CONST('var1',_latin1'foo' COLLATE 'latin1_swedish_ci');
select * from t3 where a= NAME_CONST('var1',_latin1'foo' COLLATE 'latin1_swedish_ci');
drop procedure p1;
drop table t1, t2, t3;

View File

@ -80,7 +80,7 @@ a b
2 b 2 b
1 a 1 a
(select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by t1.b; (select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by t1.b;
ERROR 42000: Table 't1' from one of the SELECTs cannot be used in ORDER clause ERROR 42000: Table 't1' from one of the SELECTs cannot be used in order clause
explain extended (select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by b desc; explain extended (select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by b desc;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00
@ -493,7 +493,7 @@ drop temporary table t1;
create table t1 select a from t1 union select a from t2; create table t1 select a from t1 union select a from t2;
ERROR 42S01: Table 't1' already exists ERROR 42S01: Table 't1' already exists
select a from t1 union select a from t2 order by t2.a; select a from t1 union select a from t2 order by t2.a;
ERROR 42000: Table 't2' from one of the SELECTs cannot be used in ORDER clause ERROR 42000: Table 't2' from one of the SELECTs cannot be used in order clause
drop table t1,t2; drop table t1,t2;
select length(version()) > 1 as `*` UNION select 2; select length(version()) > 1 as `*` UNION select 2;
* *

View File

@ -129,7 +129,7 @@ group by
a.text, b.id, b.betreff a.text, b.id, b.betreff
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Table 'b' from one of the SELECTs cannot be used in ORDER clause ERROR 42000: Table 'b' from one of the SELECTs cannot be used in order clause
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join
@ -145,7 +145,7 @@ where
match(c.beitrag) against ('+abc' in boolean mode) match(c.beitrag) against ('+abc' in boolean mode)
order by order by
match(b.betreff) against ('+abc' in boolean mode) desc; match(b.betreff) against ('+abc' in boolean mode) desc;
ERROR 42000: Table 'b' from one of the SELECTs cannot be used in ORDER clause ERROR 42000: Table 'b' from one of the SELECTs cannot be used in order clause
select a.text, b.id, b.betreff select a.text, b.id, b.betreff
from from
t2 a inner join t3 b on a.id = b.forum inner join t2 a inner join t3 b on a.id = b.forum inner join

View File

@ -2184,6 +2184,36 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref)
my_error(ER_RESERVED_SYNTAX, MYF(0), "NAME_CONST"); my_error(ER_RESERVED_SYNTAX, MYF(0), "NAME_CONST");
return TRUE; return TRUE;
} }
/*
If we have either of the following:
... WHERE foo=NAME_CONST(...)
... JOIN ... ON foo=NAME_CONST(...)
then we have an opportunity to unwrap the NAME_CONST and
use the enclosed value directly, replacing NAME_CONST in
the parse tree with the value it encloses.
*/
if ((thd->where == THD_WHERE::WHERE_CLAUSE ||
thd->where == THD_WHERE::ON_CLAUSE) &&
(value_item->type() == FUNC_ITEM ||
value_item->type() == CONST_ITEM))
{
thd->change_item_tree(ref, value_item);
/*
We're replacing NAME_CONST('name', value_item) with value_item.
Only a few constants and functions are possible as value_item, see
Create_func_name_const::create_2_arg.
Set the value_item's coercibility to be the same as NAME_CONST(...)
would have (see how it's set a few lines below).
*/
if (value_item->collation.derivation != DERIVATION_NUMERIC)
value_item->collation.set(value_item->collation.collation,
DERIVATION_IMPLICIT);
return FALSE;
}
// else, could not unwrap, fall back to default handling below.
if (value_item->collation.derivation == DERIVATION_NUMERIC) if (value_item->collation.derivation == DERIVATION_NUMERIC)
collation= DTCollation_numeric(); collation= DTCollation_numeric();
else else
@ -5528,7 +5558,7 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
is ambiguous. is ambiguous.
*/ */
my_error(ER_NON_UNIQ_ERROR, MYF(0), my_error(ER_NON_UNIQ_ERROR, MYF(0),
find_item->full_name(), current_thd->where); find_item->full_name(), thd_where(current_thd));
return NULL; return NULL;
} }
} }
@ -5613,7 +5643,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NON_UNIQ_ERROR, ER_NON_UNIQ_ERROR,
ER_THD(thd,ER_NON_UNIQ_ERROR), ref->full_name(), ER_THD(thd,ER_NON_UNIQ_ERROR), ref->full_name(),
thd->where); thd_where(thd));
} }
} }
@ -5947,7 +5977,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
if (upward_lookup) if (upward_lookup)
{ {
// We can't say exactly what absent table or field // We can't say exactly what absent table or field
my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd_where(thd));
} }
else else
{ {
@ -6174,7 +6204,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
{ {
/* The column to which we link isn't valid. */ /* The column to which we link isn't valid. */
my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name.str, my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name.str,
thd->where); thd_where(thd));
return(1); return(1);
} }
@ -6219,7 +6249,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if (unlikely(!select)) if (unlikely(!select))
{ {
my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd_where(thd));
goto error; goto error;
} }
if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
@ -8163,7 +8193,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
if (unlikely(!outer_context)) if (unlikely(!outer_context))
{ {
/* The current reference cannot be resolved in this query. */ /* The current reference cannot be resolved in this query. */
my_error(ER_BAD_FIELD_ERROR,MYF(0), full_name(), thd->where); my_error(ER_BAD_FIELD_ERROR,MYF(0), full_name(), thd_where(thd));
goto error; goto error;
} }
@ -8314,7 +8344,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
{ {
/* The item was not a table field and not a reference */ /* The item was not a table field and not a reference */
my_error(ER_BAD_FIELD_ERROR, MYF(0), my_error(ER_BAD_FIELD_ERROR, MYF(0),
this->full_name(), thd->where); this->full_name(), thd_where(thd));
goto error; goto error;
} }
/* Should be checked in resolve_ref_in_select_and_group(). */ /* Should be checked in resolve_ref_in_select_and_group(). */

View File

@ -241,7 +241,7 @@ Item_subselect::select_transformer(JOIN *join)
bool Item_subselect::fix_fields(THD *thd_param, Item **ref) bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
{ {
char const *save_where= thd_param->where; THD_WHERE save_where= thd_param->where;
uint8 uncacheable; uint8 uncacheable;
bool res; bool res;
@ -320,7 +320,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
if (have_to_be_excluded) if (have_to_be_excluded)
engine->exclude(); engine->exclude();
substitution= 0; substitution= 0;
thd->where= "checking transformed subquery"; thd->where= THD_WHERE::CHECKING_TRANSFORMED_SUBQUERY;
res= (*ref)->fix_fields_if_needed(thd, ref); res= (*ref)->fix_fields_if_needed(thd, ref);
goto end; goto end;
@ -3403,13 +3403,13 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
{ {
Query_arena *arena= 0, backup; Query_arena *arena= 0, backup;
SELECT_LEX *current= thd->lex->current_select; SELECT_LEX *current= thd->lex->current_select;
const char *save_where= thd->where; THD_WHERE save_where= thd->where;
bool trans_res= true; bool trans_res= true;
bool result; bool result;
DBUG_ENTER("Item_in_subselect::select_in_like_transformer"); DBUG_ENTER("Item_in_subselect::select_in_like_transformer");
DBUG_ASSERT(thd == join->thd); DBUG_ASSERT(thd == join->thd);
thd->where= "IN/ALL/ANY subquery"; thd->where= THD_WHERE::IN_ALL_ANY_SUBQUERY;
/* /*
In some optimisation cases we will not need this Item_in_optimizer In some optimisation cases we will not need this Item_in_optimizer
@ -3496,7 +3496,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
{ {
uint outer_cols_num; uint outer_cols_num;
List<Item> *inner_cols; List<Item> *inner_cols;
char const *save_where= thd_arg->where; THD_WHERE save_where= thd_arg->where;
DBUG_ENTER("Item_in_subselect::fix_fields"); DBUG_ENTER("Item_in_subselect::fix_fields");
thd= thd_arg; thd= thd_arg;
@ -3505,7 +3505,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
if (test_strategy(SUBS_SEMI_JOIN)) if (test_strategy(SUBS_SEMI_JOIN))
DBUG_RETURN( !( (*ref)= new (thd->mem_root) Item_int(thd, 1)) ); DBUG_RETURN( !( (*ref)= new (thd->mem_root) Item_int(thd, 1)) );
thd->where= "IN/ALL/ANY subquery"; thd->where= THD_WHERE::IN_ALL_ANY_SUBQUERY;
/* /*
Check if the outer and inner IN operands match in those cases when we 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 will not perform IN=>EXISTS transformation. Currently this is when we
@ -4016,7 +4016,7 @@ int join_read_next_same_or_null(READ_RECORD *info);
int subselect_single_select_engine::exec() int subselect_single_select_engine::exec()
{ {
char const *save_where= thd->where; THD_WHERE save_where= thd->where;
SELECT_LEX *save_select= thd->lex->current_select; SELECT_LEX *save_select= thd->lex->current_select;
thd->lex->current_select= select_lex; thd->lex->current_select= select_lex;
DBUG_ENTER("subselect_single_select_engine::exec"); DBUG_ENTER("subselect_single_select_engine::exec");
@ -4137,7 +4137,7 @@ int subselect_single_select_engine::exec()
int subselect_union_engine::exec() int subselect_union_engine::exec()
{ {
char const *save_where= thd->where; THD_WHERE save_where= thd->where;
int res= unit->exec(); int res= unit->exec();
thd->where= save_where; thd->where= save_where;
return res; return res;

View File

@ -1146,7 +1146,7 @@ bool push_table_function_arg_context(LEX *lex, MEM_ROOT *alloc)
bool Table_function_json_table::setup(THD *thd, TABLE_LIST *sql_table, bool Table_function_json_table::setup(THD *thd, TABLE_LIST *sql_table,
SELECT_LEX *s_lex) SELECT_LEX *s_lex)
{ {
thd->where= "JSON_TABLE argument"; thd->where= THD_WHERE::JSON_TABLE_ARGUMENT;
if (!m_context_setup_done) if (!m_context_setup_done)
{ {

View File

@ -638,8 +638,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
{ {
SELECT_LEX *current= thd->lex->current_select; SELECT_LEX *current= thd->lex->current_select;
thd->lex->current_select= current->return_after_parsing(); thd->lex->current_select= current->return_after_parsing();
char const *save_where= thd->where; THD_WHERE save_where= thd->where;
thd->where= "IN/ALL/ANY subquery"; thd->where= THD_WHERE::IN_ALL_ANY_SUBQUERY;
Item **left= in_subs->left_exp_ptr(); Item **left= in_subs->left_exp_ptr();
bool failure= (*left)->fix_fields_if_needed(thd, left); bool failure= (*left)->fix_fields_if_needed(thd, left);

View File

@ -1824,7 +1824,7 @@ bool partition_info::add_column_list_value(THD *thd, Item *item)
part_column_list_val *col_val; part_column_list_val *col_val;
Name_resolution_context *context= &thd->lex->current_select->context; Name_resolution_context *context= &thd->lex->current_select->context;
TABLE_LIST *save_list= context->table_list; TABLE_LIST *save_list= context->table_list;
const char *save_where= thd->where; THD_WHERE save_where= thd->where;
DBUG_ENTER("partition_info::add_column_list_value"); DBUG_ENTER("partition_info::add_column_list_value");
if (part_type == LIST_PARTITION && if (part_type == LIST_PARTITION &&
@ -1838,9 +1838,9 @@ bool partition_info::add_column_list_value(THD *thd, Item *item)
context->table_list= 0; context->table_list= 0;
if (column_list) if (column_list)
thd->where= "field list"; thd->where= THD_WHERE::FIELD_LIST;
else else
thd->where= "partition function"; thd->where= THD_WHERE::PARTITION_FUNCTION;
if (item->walk(&Item::check_partition_func_processor, 0, NULL)) if (item->walk(&Item::check_partition_func_processor, 0, NULL))
{ {

View File

@ -5957,7 +5957,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, si
{ {
if (nj_col) if (nj_col)
{ {
my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where); my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd_where(thd));
DBUG_RETURN(NULL); DBUG_RETURN(NULL);
} }
nj_col= curr_nj_col; nj_col= curr_nj_col;
@ -6604,7 +6604,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
item->cached_field_index= NO_CACHED_FIELD_INDEX; item->cached_field_index= NO_CACHED_FIELD_INDEX;
} }
DBUG_ASSERT(thd->where); DBUG_ASSERT(thd->where != THD_WHERE::NOWHERE);
/* /*
If we found a fully qualified field we return it directly as it can't If we found a fully qualified field we return it directly as it can't
have duplicates. have duplicates.
@ -6617,7 +6617,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
if (report_error == REPORT_ALL_ERRORS || if (report_error == REPORT_ALL_ERRORS ||
report_error == IGNORE_EXCEPT_NON_UNIQUE) report_error == IGNORE_EXCEPT_NON_UNIQUE)
my_error(ER_NON_UNIQ_ERROR, MYF(0), my_error(ER_NON_UNIQ_ERROR, MYF(0),
table_name ? item->full_name() : name, thd->where); table_name ? item->full_name() : name, thd_where(thd));
return (Field*) 0; return (Field*) 0;
} }
found= cur_field; found= cur_field;
@ -6645,13 +6645,13 @@ find_field_in_tables(THD *thd, Item_ident *item,
strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS); strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS);
table_name=buff; table_name=buff;
} }
my_error(ER_UNKNOWN_TABLE, MYF(0), table_name, thd->where); my_error(ER_UNKNOWN_TABLE, MYF(0), table_name, thd_where(thd));
} }
else else
{ {
if (report_error == REPORT_ALL_ERRORS || if (report_error == REPORT_ALL_ERRORS ||
report_error == REPORT_EXCEPT_NON_UNIQUE) report_error == REPORT_EXCEPT_NON_UNIQUE)
my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(), thd->where); my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(), thd_where(thd));
else else
found= not_found_field; found= not_found_field;
} }
@ -6784,7 +6784,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
*/ */
if (report_error != IGNORE_ERRORS) if (report_error != IGNORE_ERRORS)
my_error(ER_NON_UNIQ_ERROR, MYF(0), my_error(ER_NON_UNIQ_ERROR, MYF(0),
find->full_name(), current_thd->where); find->full_name(), thd_where(current_thd));
return (Item**) 0; return (Item**) 0;
} }
found_unaliased= li.ref(); found_unaliased= li.ref();
@ -6815,7 +6815,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
continue; // Same field twice continue; // Same field twice
if (report_error != IGNORE_ERRORS) if (report_error != IGNORE_ERRORS)
my_error(ER_NON_UNIQ_ERROR, MYF(0), my_error(ER_NON_UNIQ_ERROR, MYF(0),
find->full_name(), current_thd->where); find->full_name(), thd_where(current_thd));
return (Item**) 0; return (Item**) 0;
} }
found= li.ref(); found= li.ref();
@ -6870,7 +6870,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
{ {
if (report_error != IGNORE_ERRORS) if (report_error != IGNORE_ERRORS)
my_error(ER_NON_UNIQ_ERROR, MYF(0), my_error(ER_NON_UNIQ_ERROR, MYF(0),
find->full_name(), current_thd->where); find->full_name(), thd_where(current_thd));
return (Item **) 0; return (Item **) 0;
} }
if (found_unaliased) if (found_unaliased)
@ -6887,7 +6887,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
{ {
if (report_error == REPORT_ALL_ERRORS) if (report_error == REPORT_ALL_ERRORS)
my_error(ER_BAD_FIELD_ERROR, MYF(0), my_error(ER_BAD_FIELD_ERROR, MYF(0),
find->full_name(), current_thd->where); find->full_name(), thd_where(current_thd));
return (Item **) 0; return (Item **) 0;
} }
else else
@ -7093,7 +7093,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common)); DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common));
if (cur_nj_col_2->is_common || found) if (cur_nj_col_2->is_common || found)
{ {
my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1->str, thd->where); my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1->str, thd_where(thd));
goto err; goto err;
} }
if ((!using_fields && !field_2_invisible) || is_using_column_1) if ((!using_fields && !field_2_invisible) || is_using_column_1)
@ -7307,7 +7307,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join,
if (!(common_field= it++)) if (!(common_field= it++))
{ {
my_error(ER_BAD_FIELD_ERROR, MYF(0), using_field_name_ptr, my_error(ER_BAD_FIELD_ERROR, MYF(0), using_field_name_ptr,
current_thd->where); thd_where(current_thd));
goto err; goto err;
} }
if (!my_strcasecmp(system_charset_info, if (!my_strcasecmp(system_charset_info,
@ -7561,7 +7561,7 @@ static bool setup_natural_join_row_types(THD *thd,
Name_resolution_context *context) Name_resolution_context *context)
{ {
DBUG_ENTER("setup_natural_join_row_types"); DBUG_ENTER("setup_natural_join_row_types");
thd->where= "from clause"; thd->where= THD_WHERE::FROM_CLAUSE;
if (from_clause->elements == 0) if (from_clause->elements == 0)
DBUG_RETURN(false); /* We come here in the case of UNIONs. */ DBUG_RETURN(false); /* We come here in the case of UNIONs. */
@ -7732,7 +7732,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
thd->lex->current_select->nest_level); thd->lex->current_select->nest_level);
if (allow_sum_func) if (allow_sum_func)
thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level); thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level);
thd->where= THD::DEFAULT_WHERE; thd->where= THD_WHERE::DEFAULT_WHERE;
save_is_item_list_lookup= thd->lex->current_select->is_item_list_lookup; save_is_item_list_lookup= thd->lex->current_select->is_item_list_lookup;
thd->lex->current_select->is_item_list_lookup= 0; thd->lex->current_select->is_item_list_lookup= 0;
@ -8491,7 +8491,7 @@ bool setup_on_expr(THD *thd, TABLE_LIST *table, bool is_update)
embedded= embedding; embedded= embedding;
if (embedded->on_expr) if (embedded->on_expr)
{ {
thd->where="on clause"; thd->where= THD_WHERE::ON_CLAUSE;
embedded->on_expr->mark_as_condition_AND_part(embedded); embedded->on_expr->mark_as_condition_AND_part(embedded);
if (embedded->on_expr->fix_fields_if_needed_for_bool(thd, if (embedded->on_expr->fix_fields_if_needed_for_bool(thd,
&embedded->on_expr)) &embedded->on_expr))
@ -8592,7 +8592,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
if (*conds) if (*conds)
{ {
thd->where="where clause"; thd->where= THD_WHERE::WHERE_CLAUSE;
DBUG_EXECUTE("where", DBUG_EXECUTE("where",
print_where(*conds, print_where(*conds,
"WHERE in setup_conds", "WHERE in setup_conds",

View File

@ -83,8 +83,6 @@
char internal_table_name[2]= "*"; char internal_table_name[2]= "*";
char empty_c_string[1]= {0}; /* used for not defined db */ char empty_c_string[1]= {0}; /* used for not defined db */
const char * const THD::DEFAULT_WHERE= "field list";
/**************************************************************************** /****************************************************************************
** User variables ** User variables
****************************************************************************/ ****************************************************************************/
@ -629,6 +627,64 @@ extern "C" void thd_kill_timeout(THD* thd)
thd->awake(KILL_TIMEOUT); thd->awake(KILL_TIMEOUT);
} }
const char *thd_where(THD *thd)
{
switch(thd->where) {
case THD_WHERE::CHECKING_TRANSFORMED_SUBQUERY:
return "checking transformed subquery";
break;
case THD_WHERE::IN_ALL_ANY_SUBQUERY:
return "IN/ALL/ANY subquery";
break;
case THD_WHERE::JSON_TABLE_ARGUMENT:
return "JSON_TABLE argument";
break;
case THD_WHERE::DEFAULT_WHERE: // same as FIELD_LIST
case THD_WHERE::FIELD_LIST:
return "field list";
break;
case THD_WHERE::PARTITION_FUNCTION:
return "partition function";
break;
case THD_WHERE::FROM_CLAUSE:
return "from clause";
break;
case THD_WHERE::ON_CLAUSE:
return "on clause";
break;
case THD_WHERE::WHERE_CLAUSE:
return "where clause";
break;
case THD_WHERE::CONVERT_CHARSET_CONST:
return "convert character set partition constant";
break;
case THD_WHERE::FOR_SYSTEM_TIME:
return "FOR SYSTEM_TIME";
break;
case THD_WHERE::ORDER_CLAUSE:
return "order clause";
break;
case THD_WHERE::HAVING_CLAUSE:
return "having clause";
break;
case THD_WHERE::GROUP_STATEMENT:
return "group statement";
break;
case THD_WHERE::PROCEDURE_LIST:
return "procedure list";
break;
case THD_WHERE::CHECK_OPTION:
return "check option";
break;
case THD_WHERE::USE_WHERE_STRING:
return thd->where_str;
default:
break; // "fall-through" to default return below
};
DBUG_ASSERT(false);
return "UNKNOWN";
}
THD::THD(my_thread_id id, bool is_wsrep_applier) THD::THD(my_thread_id id, bool is_wsrep_applier)
:Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION, :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
/* statement id */ 0), /* statement id */ 0),
@ -838,7 +894,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
/* Variables with default values */ /* Variables with default values */
proc_info="login"; proc_info="login";
where= THD::DEFAULT_WHERE; where= THD_WHERE::DEFAULT_WHERE;
slave_net = 0; slave_net = 0;
m_command=COM_CONNECT; m_command=COM_CONNECT;
*scramble= '\0'; *scramble= '\0';
@ -2328,7 +2384,7 @@ void THD::cleanup_after_query()
/* Free Items that were created during this execution */ /* Free Items that were created during this execution */
free_items(); free_items();
/* Reset where. */ /* Reset where. */
where= THD::DEFAULT_WHERE; where= THD_WHERE::DEFAULT_WHERE;
/* reset table map for multi-table update */ /* reset table map for multi-table update */
table_map_for_update= 0; table_map_for_update= 0;
m_binlog_invoker= INVOKER_NONE; m_binlog_invoker= INVOKER_NONE;

View File

@ -2668,6 +2668,33 @@ struct thd_async_state
}; };
enum class THD_WHERE
{
NOWHERE = 0,
CHECKING_TRANSFORMED_SUBQUERY,
IN_ALL_ANY_SUBQUERY,
JSON_TABLE_ARGUMENT,
FIELD_LIST,
PARTITION_FUNCTION,
FROM_CLAUSE,
DEFAULT_WHERE,
ON_CLAUSE,
WHERE_CLAUSE,
CONVERT_CHARSET_CONST,
FOR_SYSTEM_TIME,
ORDER_CLAUSE,
HAVING_CLAUSE,
GROUP_STATEMENT,
PROCEDURE_LIST,
CHECK_OPTION,
USE_WHERE_STRING, // ugh, a compromise for vcol...
};
class THD;
const char *thd_where(THD *thd);
/** /**
@class THD @class THD
For each client connection we create a separate thread with THD serving as For each client connection we create a separate thread with THD serving as
@ -2720,13 +2747,6 @@ public:
MDL_request *backup_commit_lock; MDL_request *backup_commit_lock;
void reset_for_next_command(bool do_clear_errors= 1); void reset_for_next_command(bool do_clear_errors= 1);
/*
Constant for THD::where initialization in the beginning of every query.
It's needed because we do not save/restore THD::where normally during
primary (non subselect) query execution.
*/
static const char * const DEFAULT_WHERE;
#ifdef EMBEDDED_LIBRARY #ifdef EMBEDDED_LIBRARY
struct st_mysql *mysql; struct st_mysql *mysql;
@ -2882,12 +2902,15 @@ public:
const char *get_proc_info() const const char *get_proc_info() const
{ return proc_info; } { return proc_info; }
// Used by thd_where() when where==USE_WHERE_STRING
const char *where_str;
/* /*
Used in error messages to tell user in what part of MySQL we found an Used in error messages to tell user in what part of MySQL we found an
error. E. g. when where= "having clause", if fix_fields() fails, user error. E. g. when where= "having clause", if fix_fields() fails, user
will know that the error was in having clause. will know that the error was in having clause.
*/ */
const char *where; THD_WHERE where;
/* Needed by MariaDB semi sync replication */ /* Needed by MariaDB semi sync replication */
Trans_binlog_info *semisync_info; Trans_binlog_info *semisync_info;

View File

@ -431,7 +431,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->on_expr= expr; derived->on_expr= expr;
derived->prep_on_expr= expr->copy_andor_structure(thd); derived->prep_on_expr= expr->copy_andor_structure(thd);
} }
thd->where= "on clause"; thd->where= THD_WHERE::ON_CLAUSE;
if (derived->on_expr && if (derived->on_expr &&
derived->on_expr->fix_fields_if_needed_for_bool(thd, &derived->on_expr)) derived->on_expr->fix_fields_if_needed_for_bool(thd, &derived->on_expr))
{ {

View File

@ -8147,7 +8147,7 @@ bool LEX::check_expr_allows_fields_or_error(THD *thd, const char *name) const
{ {
if (select_stack_top > 0) if (select_stack_top > 0)
return false; // OK, fields are allowed return false; // OK, fields are allowed
my_error(ER_BAD_FIELD_ERROR, MYF(0), name, thd->where); my_error(ER_BAD_FIELD_ERROR, MYF(0), name, thd_where(thd));
return true; // Error, fields are not allowed return true; // Error, fields are not allowed
} }
@ -8170,7 +8170,7 @@ Item *LEX::create_item_ident_nospvar(THD *thd,
if (unlikely(current_select->no_table_names_allowed)) if (unlikely(current_select->no_table_names_allowed))
{ {
my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), a->str, thd->where); my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), a->str, thd_where(thd));
return NULL; return NULL;
} }
@ -8385,7 +8385,7 @@ Item *LEX::create_item_ident(THD *thd,
if (current_select->no_table_names_allowed) if (current_select->no_table_names_allowed)
{ {
my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), b->str, thd->where); my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), b->str, thd_where(thd));
return NULL; return NULL;
} }

View File

@ -142,11 +142,11 @@ Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs)
THD *thd= current_thd; THD *thd= current_thd;
Name_resolution_context *context= &thd->lex->current_select->context; Name_resolution_context *context= &thd->lex->current_select->context;
TABLE_LIST *save_list= context->table_list; TABLE_LIST *save_list= context->table_list;
const char *save_where= thd->where; THD_WHERE save_where= thd->where;
item= item->safe_charset_converter(thd, cs); item= item->safe_charset_converter(thd, cs);
context->table_list= NULL; context->table_list= NULL;
thd->where= "convert character set partition constant"; thd->where= THD_WHERE::CONVERT_CHARSET_CONST;
if (item && item->fix_fields_if_needed(thd, (Item**)NULL)) if (item && item->fix_fields_if_needed(thd, (Item**)NULL))
item= NULL; item= NULL;
thd->where= save_where; thd->where= save_where;
@ -841,7 +841,7 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
func_expr->walk(&Item::change_context_processor, 0, func_expr->walk(&Item::change_context_processor, 0,
&lex.first_select_lex()->context); &lex.first_select_lex()->context);
thd->where= "partition function"; thd->where= THD_WHERE::PARTITION_FUNCTION;
/* /*
In execution we must avoid the use of thd->change_item_tree since In execution we must avoid the use of thd->change_item_tree since
we might release memory before statement is completed. We do this we might release memory before statement is completed. We do this

View File

@ -1245,7 +1245,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY) if (vers_conditions.is_set() && vers_conditions.type != SYSTEM_TIME_HISTORY)
{ {
thd->where= "FOR SYSTEM_TIME"; thd->where= THD_WHERE::FOR_SYSTEM_TIME;
/* TODO: do resolve fix_length_and_dec(), fix_fields(). This requires /* TODO: do resolve fix_length_and_dec(), fix_fields(). This requires
storing vers_conditions as Item and make some magic related to storing vers_conditions as Item and make some magic related to
vers_system_time_t/VERS_TRX_ID at stage of fix_fields() vers_system_time_t/VERS_TRX_ID at stage of fix_fields()
@ -1530,7 +1530,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
{ {
nesting_map save_allow_sum_func= thd->lex->allow_sum_func; nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
thd->lex->allow_sum_func.set_bit(select_lex->nest_level); thd->lex->allow_sum_func.set_bit(select_lex->nest_level);
thd->where= "order clause"; thd->where= THD_WHERE::ORDER_CLAUSE;
for (ORDER *order= select_lex->order_list.first; order; order= order->next) for (ORDER *order= select_lex->order_list.first; order; order= order->next)
{ {
/* Don't add the order items to all fields. Just resolve them to ensure /* Don't add the order items to all fields. Just resolve them to ensure
@ -1546,7 +1546,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
if (having) if (having)
{ {
nesting_map save_allow_sum_func= thd->lex->allow_sum_func; nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
thd->where="having clause"; thd->where= THD_WHERE::HAVING_CLAUSE;
thd->lex->allow_sum_func.set_bit(select_lex_arg->nest_level); thd->lex->allow_sum_func.set_bit(select_lex_arg->nest_level);
select_lex->having_fix_field= 1; select_lex->having_fix_field= 1;
/* /*
@ -25986,7 +25986,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
if (!count || count > fields.elements) if (!count || count > fields.elements)
{ {
my_error(ER_BAD_FIELD_ERROR, MYF(0), my_error(ER_BAD_FIELD_ERROR, MYF(0),
order_item->full_name(), thd->where); order_item->full_name(), thd_where(thd));
return TRUE; return TRUE;
} }
thd->change_item_tree((Item **)&order->item, (Item *)&ref_pointer_array[count - 1]); thd->change_item_tree((Item **)&order->item, (Item *)&ref_pointer_array[count - 1]);
@ -26065,7 +26065,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
ER_NON_UNIQ_ERROR, ER_NON_UNIQ_ERROR,
ER_THD(thd, ER_NON_UNIQ_ERROR), ER_THD(thd, ER_NON_UNIQ_ERROR),
((Item_ident*) order_item)->field_name.str, ((Item_ident*) order_item)->field_name.str,
thd->where); thd_where(thd));
} }
} }
else if (from_window_spec) else if (from_window_spec)
@ -26135,7 +26135,7 @@ int setup_order(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
SELECT_LEX *select = thd->lex->current_select; SELECT_LEX *select = thd->lex->current_select;
enum_parsing_place context_analysis_place= enum_parsing_place context_analysis_place=
thd->lex->current_select->context_analysis_place; thd->lex->current_select->context_analysis_place;
thd->where="order clause"; thd->where= THD_WHERE::ORDER_CLAUSE;
const bool for_union= select->master_unit()->is_unit_op() && const bool for_union= select->master_unit()->is_unit_op() &&
select == select->master_unit()->fake_select_lex; select == select->master_unit()->fake_select_lex;
for (uint number = 1; order; order=order->next, number++) for (uint number = 1; order; order=order->next, number++)
@ -26214,7 +26214,7 @@ setup_group(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
uint org_fields=all_fields.elements; uint org_fields=all_fields.elements;
thd->where="group statement"; thd->where= THD_WHERE::GROUP_STATEMENT;
for (ord= order; ord; ord= ord->next) for (ord= order; ord; ord= ord->next)
{ {
if (find_order_in_list(thd, ref_pointer_array, tables, ord, fields, if (find_order_in_list(thd, ref_pointer_array, tables, ord, fields,
@ -26332,7 +26332,7 @@ setup_new_fields(THD *thd, List<Item> &fields,
new_field->item=item; /* Change to shared Item */ new_field->item=item; /* Change to shared Item */
else else
{ {
thd->where="procedure list"; thd->where= THD_WHERE::PROCEDURE_LIST;
if ((*new_field->item)->fix_fields(thd, new_field->item)) if ((*new_field->item)->fix_fields(thd, new_field->item))
DBUG_RETURN(1); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */
all_fields.push_front(*new_field->item, thd->mem_root); all_fields.push_front(*new_field->item, thd->mem_root);

View File

@ -312,7 +312,7 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)
*/ */
thd->where="order clause"; thd->where= THD_WHERE::ORDER_CLAUSE;
ORDER *order= sl->order_list.first; ORDER *order= sl->order_list.first;
for (; order; order=order->next) for (; order; order=order->next)
{ {
@ -327,7 +327,7 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl,
if (!count || count > first_elem->elements) if (!count || count > first_elem->elements)
{ {
my_error(ER_BAD_FIELD_ERROR, MYF(0), my_error(ER_BAD_FIELD_ERROR, MYF(0),
order_item->full_name(), thd->where); order_item->full_name(), thd_where(thd));
DBUG_RETURN(true); DBUG_RETURN(true);
} }
order->in_field_list= 1; order->in_field_list= 1;

View File

@ -12520,7 +12520,7 @@ opt_order_clause:
order_clause: order_clause:
ORDER_SYM BY ORDER_SYM BY
{ {
thd->where= "ORDER clause"; thd->where= THD_WHERE::ORDER_CLAUSE;
} }
order_list order_list
{ {

View File

@ -1212,7 +1212,8 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
expr_str.length(parse_vcol_keyword.length); expr_str.length(parse_vcol_keyword.length);
expr_str.append((char*)pos, expr_length); expr_str.append((char*)pos, expr_length);
thd->where= vcol_type_name(static_cast<enum_vcol_info_type>(type)); thd->where= THD_WHERE::USE_WHERE_STRING;
thd->where_str= vcol_type_name(static_cast<enum_vcol_info_type>(type));
switch (type) { switch (type) {
case VCOL_GENERATED_VIRTUAL: case VCOL_GENERATED_VIRTUAL:
@ -6282,8 +6283,8 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
if (check_option) if (check_option)
{ {
const char *save_where= thd->where; THD_WHERE save_where= thd->where;
thd->where= "check option"; thd->where= THD_WHERE::CHECK_OPTION;
if (check_option->fix_fields_if_needed_for_bool(thd, &check_option)) if (check_option->fix_fields_if_needed_for_bool(thd, &check_option))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
thd->where= save_where; thd->where= save_where;