1
0
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:
Igor Babaev
2023-03-22 21:59:18 -07:00
parent 011261f4e9
commit f33fc2fae5
18 changed files with 565 additions and 59 deletions

View File

@ -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
}