mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-30539 EXPLAIN EXTENDED: no message with queries for DML statements
EXPLAIN EXTENDED for an UPDATE/DELETE/INSERT/REPLACE statement did not produce the warning containing the text representation of the query obtained after the optimization phase. Such warning was produced for SELECT statements, but not for DML statements. The patch fixes this defect of EXPLAIN EXTENDED for DML statements.
This commit is contained in:
@ -305,6 +305,9 @@ static Item **get_sargable_cond(JOIN *join, TABLE *table);
|
||||
|
||||
bool is_eq_cond_injected_for_split_opt(Item_func_eq *eq_item);
|
||||
|
||||
void print_list_item(String *str, List_item *list,
|
||||
enum_query_type query_type);
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
|
||||
/*
|
||||
@ -27981,6 +27984,162 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
|
||||
}
|
||||
}
|
||||
|
||||
enum explainable_cmd_type
|
||||
{
|
||||
SELECT_CMD, INSERT_CMD, REPLACE_CMD, UPDATE_CMD, DELETE_CMD, NO_CMD
|
||||
};
|
||||
|
||||
static
|
||||
const char * const explainable_cmd_name []=
|
||||
{
|
||||
"select ",
|
||||
"insert ",
|
||||
"replace ",
|
||||
"update ",
|
||||
"delete ",
|
||||
};
|
||||
|
||||
static
|
||||
char const *get_explainable_cmd_name(enum explainable_cmd_type cmd)
|
||||
{
|
||||
return explainable_cmd_name[cmd];
|
||||
}
|
||||
|
||||
static
|
||||
enum explainable_cmd_type get_explainable_cmd_type(THD *thd)
|
||||
{
|
||||
switch (thd->lex->sql_command) {
|
||||
case SQLCOM_SELECT:
|
||||
return SELECT_CMD;
|
||||
case SQLCOM_INSERT:
|
||||
case SQLCOM_INSERT_SELECT:
|
||||
return INSERT_CMD;
|
||||
case SQLCOM_REPLACE:
|
||||
case SQLCOM_REPLACE_SELECT:
|
||||
return REPLACE_CMD;
|
||||
case SQLCOM_UPDATE:
|
||||
case SQLCOM_UPDATE_MULTI:
|
||||
return UPDATE_CMD;
|
||||
case SQLCOM_DELETE:
|
||||
case SQLCOM_DELETE_MULTI:
|
||||
return DELETE_CMD;
|
||||
default:
|
||||
return SELECT_CMD;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TABLE_LIST::print_leaf_tables(THD *thd, String *str,
|
||||
enum_query_type query_type)
|
||||
{
|
||||
if (merge_underlying_list)
|
||||
{
|
||||
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
|
||||
tbl->print_leaf_tables(thd, str, query_type);
|
||||
}
|
||||
else
|
||||
print(thd, 0, str, query_type);
|
||||
}
|
||||
|
||||
|
||||
void st_select_lex::print_item_list(THD *thd, String *str,
|
||||
enum_query_type query_type)
|
||||
{
|
||||
bool first= 1;
|
||||
/*
|
||||
outer_select() can not be used here because it is for name resolution
|
||||
and will return NULL at any end of name resolution chain (view/derived)
|
||||
*/
|
||||
bool top_level= (get_master()->get_master() == 0);
|
||||
List_iterator_fast<Item> it(item_list);
|
||||
Item *item;
|
||||
while ((item= it++))
|
||||
{
|
||||
if (first)
|
||||
first= 0;
|
||||
else
|
||||
str->append(',');
|
||||
|
||||
if ((is_subquery_function() && item->is_autogenerated_name) ||
|
||||
!item->name.str)
|
||||
{
|
||||
/*
|
||||
Do not print auto-generated aliases in subqueries. It has no purpose
|
||||
in a view definition or other contexts where the query is printed.
|
||||
*/
|
||||
item->print(str, query_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Do not print illegal names (if it is not top level SELECT).
|
||||
Top level view checked (and correct name are assigned),
|
||||
other cases of top level SELECT are not important, because
|
||||
it is not "table field".
|
||||
*/
|
||||
if (top_level ||
|
||||
!item->is_autogenerated_name ||
|
||||
!check_column_name(item->name.str))
|
||||
item->print_item_w_name(str, query_type);
|
||||
else
|
||||
item->print(str, query_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void st_select_lex::print_set_clause(THD *thd, String *str,
|
||||
enum_query_type query_type)
|
||||
{
|
||||
bool first= 1;
|
||||
/*
|
||||
outer_select() can not be used here because it is for name resolution
|
||||
and will return NULL at any end of name resolution chain (view/derived)
|
||||
*/
|
||||
List_iterator_fast<Item> it(item_list);
|
||||
List_iterator_fast<Item> vt(thd->lex->value_list);
|
||||
Item *item;
|
||||
Item *val;
|
||||
while ((item= it++, val= vt++ ))
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
str->append(STRING_WITH_LEN(" set "));
|
||||
first= 0;
|
||||
}
|
||||
else
|
||||
str->append(',');
|
||||
|
||||
item->print(str, query_type);
|
||||
str->append(STRING_WITH_LEN(" = "));
|
||||
val->print(str, query_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void st_select_lex::print_on_duplicate_key_clause(THD *thd, String *str,
|
||||
enum_query_type query_type)
|
||||
{
|
||||
bool first= 1;
|
||||
List_iterator_fast<Item> it(thd->lex->update_list);
|
||||
List_iterator_fast<Item> vt(thd->lex->value_list);
|
||||
Item *item;
|
||||
Item *val;
|
||||
while ((item= it++, val= vt++ ))
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
str->append(STRING_WITH_LEN(" on duplicate key update "));
|
||||
first= 0;
|
||||
}
|
||||
else
|
||||
str->append(',');
|
||||
|
||||
item->print(str, query_type);
|
||||
str->append(STRING_WITH_LEN(" = "));
|
||||
val->print(str, query_type);
|
||||
}
|
||||
}
|
||||
|
||||
void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
|
||||
{
|
||||
@ -27998,6 +28157,61 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
|
||||
return;
|
||||
}
|
||||
|
||||
bool top_level= (get_master()->get_master() == 0);
|
||||
enum explainable_cmd_type sel_type= SELECT_CMD;
|
||||
if (top_level)
|
||||
sel_type= get_explainable_cmd_type(thd);
|
||||
|
||||
if (sel_type == INSERT_CMD || sel_type == REPLACE_CMD)
|
||||
{
|
||||
str->append(get_explainable_cmd_name(sel_type));
|
||||
str->append(STRING_WITH_LEN("into "));
|
||||
TABLE_LIST *tbl= thd->lex->query_tables;
|
||||
while (tbl->merge_underlying_list)
|
||||
tbl= tbl->merge_underlying_list;
|
||||
tbl->print(thd, 0, str, query_type);
|
||||
if (thd->lex->field_list.elements)
|
||||
{
|
||||
str->append ('(');
|
||||
List_iterator_fast<Item> it(thd->lex->field_list);
|
||||
Item *item;
|
||||
bool first= true;
|
||||
while ((item= it++))
|
||||
{
|
||||
if (first)
|
||||
first= false;
|
||||
else
|
||||
str->append(',');
|
||||
str->append(item->name);
|
||||
}
|
||||
str->append(')');
|
||||
}
|
||||
|
||||
str->append(' ');
|
||||
|
||||
if (thd->lex->sql_command == SQLCOM_INSERT ||
|
||||
thd->lex->sql_command == SQLCOM_REPLACE)
|
||||
{
|
||||
str->append(STRING_WITH_LEN("values "));
|
||||
bool is_first_elem= true;
|
||||
List_iterator_fast<List_item> li(thd->lex->many_values);
|
||||
List_item *list;
|
||||
|
||||
while ((list= li++))
|
||||
{
|
||||
if (is_first_elem)
|
||||
is_first_elem= false;
|
||||
else
|
||||
str->append(',');
|
||||
|
||||
print_list_item(str, list, query_type);
|
||||
}
|
||||
if (thd->lex->update_list.elements)
|
||||
print_on_duplicate_key_clause(thd, str, query_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((query_type & QT_SHOW_SELECT_NUMBER) &&
|
||||
thd->lex->all_selects_list &&
|
||||
thd->lex->all_selects_list->link_next &&
|
||||
@ -28021,7 +28235,10 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
|
||||
str->append(" */ ");
|
||||
}
|
||||
|
||||
str->append(STRING_WITH_LEN("select "));
|
||||
if (sel_type == SELECT_CMD ||
|
||||
sel_type == INSERT_CMD ||
|
||||
sel_type == REPLACE_CMD)
|
||||
str->append(STRING_WITH_LEN("select "));
|
||||
|
||||
if (join && join->cleaned)
|
||||
{
|
||||
@ -28067,56 +28284,65 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
|
||||
}
|
||||
|
||||
//Item List
|
||||
bool first= 1;
|
||||
/*
|
||||
outer_select() can not be used here because it is for name resolution
|
||||
and will return NULL at any end of name resolution chain (view/derived)
|
||||
*/
|
||||
bool top_level= (get_master()->get_master() == 0);
|
||||
List_iterator_fast<Item> it(item_list);
|
||||
Item *item;
|
||||
while ((item= it++))
|
||||
{
|
||||
if (first)
|
||||
first= 0;
|
||||
else
|
||||
str->append(',');
|
||||
|
||||
if ((is_subquery_function() && item->is_autogenerated_name) ||
|
||||
!item->name.str)
|
||||
{
|
||||
/*
|
||||
Do not print auto-generated aliases in subqueries. It has no purpose
|
||||
in a view definition or other contexts where the query is printed.
|
||||
*/
|
||||
item->print(str, query_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Do not print illegal names (if it is not top level SELECT).
|
||||
Top level view checked (and correct name are assigned),
|
||||
other cases of top level SELECT are not important, because
|
||||
it is not "table field".
|
||||
*/
|
||||
if (top_level ||
|
||||
!item->is_autogenerated_name ||
|
||||
!check_column_name(item->name.str))
|
||||
item->print_item_w_name(str, query_type);
|
||||
else
|
||||
item->print(str, query_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (sel_type == SELECT_CMD ||
|
||||
sel_type == INSERT_CMD ||
|
||||
sel_type == REPLACE_CMD)
|
||||
print_item_list(thd, str, query_type);
|
||||
/*
|
||||
from clause
|
||||
TODO: support USING/FORCE/IGNORE index
|
||||
*/
|
||||
if (table_list.elements)
|
||||
{
|
||||
str->append(STRING_WITH_LEN(" from "));
|
||||
/* go through join tree */
|
||||
print_join(thd, join? join->eliminated_tables: 0, str, &top_join_list, query_type);
|
||||
if (sel_type == SELECT_CMD ||
|
||||
sel_type == INSERT_CMD ||
|
||||
sel_type == REPLACE_CMD)
|
||||
{
|
||||
str->append(STRING_WITH_LEN(" from "));
|
||||
/* go through join tree */
|
||||
print_join(thd, join? join->eliminated_tables: 0, str, &top_join_list,
|
||||
query_type);
|
||||
}
|
||||
if (sel_type == UPDATE_CMD || sel_type == DELETE_CMD)
|
||||
str->append(STRING_WITH_LEN(get_explainable_cmd_name(sel_type)));
|
||||
if (sel_type == DELETE_CMD)
|
||||
{
|
||||
str->append(STRING_WITH_LEN(" from "));
|
||||
bool first= true;
|
||||
for (TABLE_LIST *target_tbl= thd->lex->auxiliary_table_list.first;
|
||||
target_tbl;
|
||||
target_tbl= target_tbl->next_local)
|
||||
{
|
||||
if (first)
|
||||
first= false;
|
||||
else
|
||||
str->append(',');
|
||||
target_tbl->correspondent_table->print_leaf_tables(thd, str,
|
||||
query_type);
|
||||
}
|
||||
|
||||
if (!first)
|
||||
str->append(STRING_WITH_LEN(" using "));
|
||||
}
|
||||
if (sel_type == UPDATE_CMD || sel_type == DELETE_CMD)
|
||||
{
|
||||
if (join)
|
||||
print_join(thd, 0, str, &top_join_list, query_type);
|
||||
else
|
||||
{
|
||||
bool first= true;
|
||||
List_iterator_fast<TABLE_LIST> li(leaf_tables);
|
||||
TABLE_LIST *tbl;
|
||||
while ((tbl= li++))
|
||||
{
|
||||
if (first)
|
||||
first= false;
|
||||
else
|
||||
str->append(',');
|
||||
tbl->print(thd, 0, str, query_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (where)
|
||||
{
|
||||
@ -28127,10 +28353,15 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
|
||||
str->append(STRING_WITH_LEN(" from DUAL "));
|
||||
}
|
||||
|
||||
if (sel_type == UPDATE_CMD)
|
||||
print_set_clause(thd, str, query_type);
|
||||
|
||||
// Where
|
||||
Item *cur_where= where;
|
||||
if (join)
|
||||
cur_where= join->conds;
|
||||
else if (sel_type == UPDATE_CMD || sel_type == DELETE_CMD)
|
||||
cur_where= thd->lex->upd_del_where;
|
||||
if (cur_where || cond_value != Item::COND_UNDEF)
|
||||
{
|
||||
str->append(STRING_WITH_LEN(" where "));
|
||||
@ -28187,6 +28418,15 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
|
||||
else if (lock_type == TL_WRITE)
|
||||
str->append(" for update");
|
||||
|
||||
if ((sel_type == INSERT_CMD || sel_type == REPLACE_CMD) &&
|
||||
thd->lex->update_list.elements)
|
||||
print_on_duplicate_key_clause(thd, str, query_type);
|
||||
|
||||
// returning clause
|
||||
if (sel_type == DELETE_CMD && !item_list.elements)
|
||||
{
|
||||
print_item_list(thd, str, query_type);
|
||||
}
|
||||
// PROCEDURE unsupported here
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user