mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-29988: Major performance regression with 10.6.11
The idea is to put Item_direct_ref_to_item as a transparent and permanent wrapper before a string which require conversion. So that Item_direct_ref_to_item would be the only place where the pointer to the string item is stored, this pointer can be changed and restored during PS execution as needed. And if any permanent (subquery) optimization would need a pointer to the item, it'll use a pointer to the Item_direct_ref_to_item - which is a permanent item and won't go away.
This commit is contained in:
committed by
Sergei Golubchik
parent
a9b31b0814
commit
37a316c01d
@ -5700,3 +5700,18 @@ EXECUTE stmt USING 'd';
|
|||||||
EXECUTE stmt USING 'd';
|
EXECUTE stmt USING 'd';
|
||||||
300
|
300
|
||||||
DROP TABLE t1, t2, t3;
|
DROP TABLE t1, t2, t3;
|
||||||
|
set @@max_session_mem_used=default;
|
||||||
|
create table t (a varchar(10)) character set utf8;
|
||||||
|
insert into t values ('');
|
||||||
|
prepare stmt from "select 1 from t where a = ?";
|
||||||
|
set @@max_session_mem_used=(select memory_used*2 from information_schema.processlist where id=connection_id());
|
||||||
|
deallocate prepare stmt;
|
||||||
|
drop table t;
|
||||||
|
set @@max_session_mem_used=default;
|
||||||
|
create table t (a varchar(10)) character set utf8;
|
||||||
|
insert into t values ('');
|
||||||
|
prepare stmt from "select 1 from t where a = 'a'";
|
||||||
|
set @@max_session_mem_used=(select memory_used*2 from information_schema.processlist where id=connection_id());
|
||||||
|
deallocate prepare stmt;
|
||||||
|
drop table t;
|
||||||
|
set @@max_session_mem_used=default;
|
||||||
|
@ -5116,3 +5116,38 @@ EXECUTE stmt USING 'b';
|
|||||||
EXECUTE stmt USING 'd';
|
EXECUTE stmt USING 'd';
|
||||||
EXECUTE stmt USING 'd';
|
EXECUTE stmt USING 'd';
|
||||||
DROP TABLE t1, t2, t3;
|
DROP TABLE t1, t2, t3;
|
||||||
|
|
||||||
|
set @@max_session_mem_used=default;
|
||||||
|
create table t (a varchar(10)) character set utf8;
|
||||||
|
insert into t values ('');
|
||||||
|
prepare stmt from "select 1 from t where a = ?";
|
||||||
|
set @@max_session_mem_used=(select memory_used*2 from information_schema.processlist where id=connection_id());
|
||||||
|
let $run= 1000;
|
||||||
|
disable_result_log;
|
||||||
|
disable_query_log;
|
||||||
|
while ($run) {
|
||||||
|
execute stmt using repeat('x',10000);
|
||||||
|
dec $run;
|
||||||
|
}
|
||||||
|
enable_result_log;
|
||||||
|
enable_query_log;
|
||||||
|
deallocate prepare stmt;
|
||||||
|
drop table t;
|
||||||
|
set @@max_session_mem_used=default;
|
||||||
|
|
||||||
|
create table t (a varchar(10)) character set utf8;
|
||||||
|
insert into t values ('');
|
||||||
|
prepare stmt from "select 1 from t where a = 'a'";
|
||||||
|
set @@max_session_mem_used=(select memory_used*2 from information_schema.processlist where id=connection_id());
|
||||||
|
let $run= 1000;
|
||||||
|
disable_result_log;
|
||||||
|
disable_query_log;
|
||||||
|
while ($run) {
|
||||||
|
execute stmt;
|
||||||
|
dec $run;
|
||||||
|
}
|
||||||
|
enable_result_log;
|
||||||
|
enable_query_log;
|
||||||
|
deallocate prepare stmt;
|
||||||
|
drop table t;
|
||||||
|
set @@max_session_mem_used=default;
|
||||||
|
80
sql/item.cc
80
sql/item.cc
@ -41,6 +41,7 @@
|
|||||||
// find_item_in_list,
|
// find_item_in_list,
|
||||||
// RESOLVED_AGAINST_ALIAS, ...
|
// RESOLVED_AGAINST_ALIAS, ...
|
||||||
#include "sql_expression_cache.h"
|
#include "sql_expression_cache.h"
|
||||||
|
#include "sql_lex.h" // empty_clex_str
|
||||||
|
|
||||||
const String my_null_string("NULL", 4, default_charset_info);
|
const String my_null_string("NULL", 4, default_charset_info);
|
||||||
const String my_default_string("DEFAULT", 7, default_charset_info);
|
const String my_default_string("DEFAULT", 7, default_charset_info);
|
||||||
@ -1401,13 +1402,11 @@ Item *Item_cache::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
|
|||||||
Item *conv= example->safe_charset_converter(thd, tocs);
|
Item *conv= example->safe_charset_converter(thd, tocs);
|
||||||
if (conv == example)
|
if (conv == example)
|
||||||
return this;
|
return this;
|
||||||
Item_cache *cache;
|
if (!conv || conv->fix_fields(thd, (Item **) NULL))
|
||||||
if (!conv || conv->fix_fields(thd, (Item **) NULL) ||
|
|
||||||
unlikely(!(cache= new (thd->mem_root) Item_cache_str(thd, conv))))
|
|
||||||
return NULL; // Safe conversion is not possible, or OEM
|
return NULL; // Safe conversion is not possible, or OEM
|
||||||
cache->setup(thd, conv);
|
setup(thd, conv);
|
||||||
cache->fixed= false; // Make Item::fix_fields() happy
|
thd->change_item_tree(&example, conv);
|
||||||
return cache;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2782,7 +2781,6 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll,
|
|||||||
safe_args[1]= args[item_sep];
|
safe_args[1]= args[item_sep];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool res= FALSE;
|
|
||||||
uint i;
|
uint i;
|
||||||
|
|
||||||
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
|
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
|
||||||
@ -2802,19 +2800,31 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll,
|
|||||||
args[item_sep]= safe_args[1];
|
args[item_sep]= safe_args[1];
|
||||||
}
|
}
|
||||||
my_coll_agg_error(args, nargs, fname, item_sep);
|
my_coll_agg_error(args, nargs, fname, item_sep);
|
||||||
res= TRUE;
|
return TRUE;
|
||||||
break; // we cannot return here, we need to restore "arena".
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thd->change_item_tree(arg, conv);
|
|
||||||
|
|
||||||
if (conv->fix_fields(thd, arg))
|
if (conv->fix_fields(thd, arg))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
Query_arena *arena, backup;
|
||||||
|
arena= thd->activate_stmt_arena_if_needed(&backup);
|
||||||
|
if (arena)
|
||||||
{
|
{
|
||||||
res= TRUE;
|
Item_direct_ref_to_item *ref=
|
||||||
break; // we cannot return here, we need to restore "arena".
|
new (thd->mem_root) Item_direct_ref_to_item(thd, *arg);
|
||||||
|
if ((ref == NULL) || ref->fix_fields(thd, (Item **)&ref))
|
||||||
|
{
|
||||||
|
thd->restore_active_arena(arena, &backup);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
*arg= ref;
|
||||||
|
thd->restore_active_arena(arena, &backup);
|
||||||
|
ref->change_item(thd, conv);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
thd->change_item_tree(arg, conv);
|
||||||
}
|
}
|
||||||
return res;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -10952,3 +10962,45 @@ void Item::register_in(THD *thd)
|
|||||||
next= thd->free_list;
|
next= thd->free_list;
|
||||||
thd->free_list= this;
|
thd->free_list= this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Item_direct_ref_to_item::Item_direct_ref_to_item(THD *thd, Item *item)
|
||||||
|
: Item_direct_ref(thd, NULL, NULL, "", &empty_clex_str, FALSE)
|
||||||
|
{
|
||||||
|
m_item= item;
|
||||||
|
ref= (Item**)&m_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Item_direct_ref_to_item::fix_fields(THD *thd, Item **)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(m_item != NULL);
|
||||||
|
if (m_item->fix_fields_if_needed_for_scalar(thd, ref))
|
||||||
|
return TRUE;
|
||||||
|
set_properties();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item_direct_ref_to_item::print(String *str, enum_query_type query_type)
|
||||||
|
{
|
||||||
|
m_item->print(str, query_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Item *Item_direct_ref_to_item::safe_charset_converter(THD *thd,
|
||||||
|
CHARSET_INFO *tocs)
|
||||||
|
{
|
||||||
|
Item *conv= m_item->safe_charset_converter(thd, tocs);
|
||||||
|
if (conv != m_item)
|
||||||
|
{
|
||||||
|
if (conv== NULL || conv->fix_fields(thd, &conv))
|
||||||
|
return NULL;
|
||||||
|
change_item(thd, conv);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item_direct_ref_to_item::change_item(THD *thd, Item *i)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(i->fixed);
|
||||||
|
thd->change_item_tree(ref, i);
|
||||||
|
set_properties();
|
||||||
|
}
|
||||||
|
93
sql/item.h
93
sql/item.h
@ -6890,4 +6890,97 @@ inline void Virtual_column_info::print(String* str)
|
|||||||
expr->print_for_table_def(str);
|
expr->print_for_table_def(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Item_direct_ref_to_item : public Item_direct_ref
|
||||||
|
{
|
||||||
|
Item *m_item;
|
||||||
|
public:
|
||||||
|
Item_direct_ref_to_item(THD *thd, Item *item);
|
||||||
|
|
||||||
|
void change_item(THD *thd, Item *);
|
||||||
|
|
||||||
|
bool fix_fields(THD *thd, Item **it);
|
||||||
|
|
||||||
|
void print(String *str, enum_query_type query_type);
|
||||||
|
|
||||||
|
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
|
||||||
|
Item *get_tmp_table_item(THD *thd)
|
||||||
|
{ return m_item->get_tmp_table_item(thd); }
|
||||||
|
Item *get_copy(THD *thd)
|
||||||
|
{ return m_item->get_copy(thd); }
|
||||||
|
COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
|
||||||
|
bool link_item_fields,
|
||||||
|
COND_EQUAL **cond_equal_ref)
|
||||||
|
{
|
||||||
|
return m_item->build_equal_items(thd, inherited, link_item_fields,
|
||||||
|
cond_equal_ref);
|
||||||
|
}
|
||||||
|
const char *full_name() const { return m_item->full_name(); }
|
||||||
|
void make_send_field(THD *thd, Send_field *field)
|
||||||
|
{ m_item->make_send_field(thd, field); }
|
||||||
|
bool eq(const Item *item, bool binary_cmp) const
|
||||||
|
{
|
||||||
|
Item *it= ((Item *) item)->real_item();
|
||||||
|
return m_item->eq(it, binary_cmp);
|
||||||
|
}
|
||||||
|
void fix_after_pullout(st_select_lex *new_parent, Item **refptr, bool merge)
|
||||||
|
{ m_item->fix_after_pullout(new_parent, &m_item, merge); }
|
||||||
|
void save_val(Field *to)
|
||||||
|
{ return m_item->save_val(to); }
|
||||||
|
void save_result(Field *to)
|
||||||
|
{ return m_item->save_result(to); }
|
||||||
|
int save_in_field(Field *to, bool no_conversions)
|
||||||
|
{ return m_item->save_in_field(to, no_conversions); }
|
||||||
|
const Type_handler *type_handler() const { return m_item->type_handler(); }
|
||||||
|
table_map used_tables() const { return m_item->used_tables(); }
|
||||||
|
void update_used_tables()
|
||||||
|
{ m_item->update_used_tables(); }
|
||||||
|
bool const_item() const { return m_item->const_item(); }
|
||||||
|
table_map not_null_tables() const { return m_item->not_null_tables(); }
|
||||||
|
bool walk(Item_processor processor, bool walk_subquery, void *arg)
|
||||||
|
{
|
||||||
|
return m_item->walk(processor, walk_subquery, arg) ||
|
||||||
|
(this->*processor)(arg);
|
||||||
|
}
|
||||||
|
bool enumerate_field_refs_processor(void *arg)
|
||||||
|
{ return m_item->enumerate_field_refs_processor(arg); }
|
||||||
|
Item_field *field_for_view_update()
|
||||||
|
{ return m_item->field_for_view_update(); }
|
||||||
|
|
||||||
|
/* Row emulation: forwarding of ROW-related calls to orig_item */
|
||||||
|
uint cols() const
|
||||||
|
{ return m_item->cols(); }
|
||||||
|
Item* element_index(uint i)
|
||||||
|
{ return this; }
|
||||||
|
Item** addr(uint i)
|
||||||
|
{ return &m_item; }
|
||||||
|
bool check_cols(uint c)
|
||||||
|
{ return Item::check_cols(c); }
|
||||||
|
bool null_inside()
|
||||||
|
{ return m_item->null_inside(); }
|
||||||
|
void bring_value()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Item_equal *get_item_equal() { return m_item->get_item_equal(); }
|
||||||
|
void set_item_equal(Item_equal *item_eq) { m_item->set_item_equal(item_eq); }
|
||||||
|
Item_equal *find_item_equal(COND_EQUAL *cond_equal)
|
||||||
|
{ return m_item->find_item_equal(cond_equal); }
|
||||||
|
Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
|
||||||
|
{ return m_item->propagate_equal_fields(thd, ctx, cond); }
|
||||||
|
Item *replace_equal_field(THD *thd, uchar *arg)
|
||||||
|
{ return m_item->replace_equal_field(thd, arg); }
|
||||||
|
|
||||||
|
bool excl_dep_on_table(table_map tab_map)
|
||||||
|
{ return m_item->excl_dep_on_table(tab_map); }
|
||||||
|
bool excl_dep_on_grouping_fields(st_select_lex *sel)
|
||||||
|
{ return m_item->excl_dep_on_grouping_fields(sel); }
|
||||||
|
bool is_expensive() { return m_item->is_expensive(); }
|
||||||
|
Item* build_clone(THD *thd) { return get_copy(thd); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
This processor states that this is safe for virtual columns
|
||||||
|
(because this Item transparency)
|
||||||
|
*/
|
||||||
|
bool check_vcol_func_processor(void *arg) { return FALSE;}
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* SQL_ITEM_INCLUDED */
|
#endif /* SQL_ITEM_INCLUDED */
|
||||||
|
@ -539,7 +539,7 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd,
|
|||||||
|
|
||||||
if (is_list_of_rows)
|
if (is_list_of_rows)
|
||||||
{
|
{
|
||||||
Item_row *row_list= (Item_row *)(args[i]->build_clone(thd));
|
Item_row *row_list= (Item_row *)(args[i]);
|
||||||
|
|
||||||
if (!row_list)
|
if (!row_list)
|
||||||
return true;
|
return true;
|
||||||
@ -564,8 +564,7 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd,
|
|||||||
sprintf(col_name, "_col_%i", 1);
|
sprintf(col_name, "_col_%i", 1);
|
||||||
args[i]->set_name(thd, col_name, strlen(col_name), thd->charset());
|
args[i]->set_name(thd, col_name, strlen(col_name), thd->charset());
|
||||||
}
|
}
|
||||||
Item *arg_clone= args[i]->build_clone(thd);
|
if (tvc_value->push_back(args[i]))
|
||||||
if (!arg_clone || tvc_value->push_back(arg_clone))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3010,7 +3010,7 @@ class Vcol_expr_context
|
|||||||
bool inited;
|
bool inited;
|
||||||
THD *thd;
|
THD *thd;
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
Query_arena backup_arena;
|
Query_arena backup_arena, *stmt_arena;
|
||||||
table_map old_map;
|
table_map old_map;
|
||||||
Security_context *save_security_ctx;
|
Security_context *save_security_ctx;
|
||||||
sql_mode_t save_sql_mode;
|
sql_mode_t save_sql_mode;
|
||||||
@ -3020,6 +3020,7 @@ public:
|
|||||||
inited(false),
|
inited(false),
|
||||||
thd(_thd),
|
thd(_thd),
|
||||||
table(_table),
|
table(_table),
|
||||||
|
stmt_arena(thd->stmt_arena),
|
||||||
old_map(table->map),
|
old_map(table->map),
|
||||||
save_security_ctx(thd->security_ctx),
|
save_security_ctx(thd->security_ctx),
|
||||||
save_sql_mode(thd->variables.sql_mode) {}
|
save_sql_mode(thd->variables.sql_mode) {}
|
||||||
@ -3040,6 +3041,7 @@ bool Vcol_expr_context::init()
|
|||||||
thd->security_ctx= tl->security_ctx;
|
thd->security_ctx= tl->security_ctx;
|
||||||
|
|
||||||
thd->set_n_backup_active_arena(table->expr_arena, &backup_arena);
|
thd->set_n_backup_active_arena(table->expr_arena, &backup_arena);
|
||||||
|
thd->stmt_arena= thd;
|
||||||
|
|
||||||
inited= true;
|
inited= true;
|
||||||
return false;
|
return false;
|
||||||
@ -3053,6 +3055,7 @@ Vcol_expr_context::~Vcol_expr_context()
|
|||||||
thd->security_ctx= save_security_ctx;
|
thd->security_ctx= save_security_ctx;
|
||||||
thd->restore_active_arena(table->expr_arena, &backup_arena);
|
thd->restore_active_arena(table->expr_arena, &backup_arena);
|
||||||
thd->variables.sql_mode= save_sql_mode;
|
thd->variables.sql_mode= save_sql_mode;
|
||||||
|
thd->stmt_arena= stmt_arena;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user