summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <dmitry.shulga@mariadb.com>2022-12-09 21:10:25 +0700
committerSergei Golubchik <serg@mariadb.org>2023-01-02 00:04:03 +0100
commit37a316c01d778a62a056d5d20110ef18bb55975e (patch)
tree2d788a20cd48420bbe917b9f7eb620fe7ab3aebd
parenta9b31b0814b02e65930209b1a90a8293b2ca6619 (diff)
downloadmariadb-git-37a316c01d778a62a056d5d20110ef18bb55975e.tar.gz
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.
-rw-r--r--mysql-test/main/ps.result15
-rw-r--r--mysql-test/main/ps.test35
-rw-r--r--sql/item.cc80
-rw-r--r--sql/item.h93
-rw-r--r--sql/sql_tvc.cc5
-rw-r--r--sql/table.cc5
6 files changed, 215 insertions, 18 deletions
diff --git a/mysql-test/main/ps.result b/mysql-test/main/ps.result
index 26c41526389..16700fd83d3 100644
--- a/mysql-test/main/ps.result
+++ b/mysql-test/main/ps.result
@@ -5700,3 +5700,18 @@ EXECUTE stmt USING 'd';
EXECUTE stmt USING 'd';
300
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;
diff --git a/mysql-test/main/ps.test b/mysql-test/main/ps.test
index 2ccfac3c119..87cae7211a4 100644
--- a/mysql-test/main/ps.test
+++ b/mysql-test/main/ps.test
@@ -5116,3 +5116,38 @@ EXECUTE stmt USING 'b';
EXECUTE stmt USING 'd';
EXECUTE stmt USING 'd';
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;
diff --git a/sql/item.cc b/sql/item.cc
index 757f09dec01..03e6ffb5715 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -41,6 +41,7 @@
// find_item_in_list,
// RESOLVED_AGAINST_ALIAS, ...
#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_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);
if (conv == example)
return this;
- Item_cache *cache;
- if (!conv || conv->fix_fields(thd, (Item **) NULL) ||
- unlikely(!(cache= new (thd->mem_root) Item_cache_str(thd, conv))))
+ if (!conv || conv->fix_fields(thd, (Item **) NULL))
return NULL; // Safe conversion is not possible, or OEM
- cache->setup(thd, conv);
- cache->fixed= false; // Make Item::fix_fields() happy
- return cache;
+ setup(thd, conv);
+ thd->change_item_tree(&example, conv);
+ return this;
}
@@ -2782,7 +2781,6 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll,
safe_args[1]= args[item_sep];
}
- bool res= FALSE;
uint i;
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];
}
my_coll_agg_error(args, nargs, fname, item_sep);
- res= TRUE;
- break; // we cannot return here, we need to restore "arena".
+ return TRUE;
}
- thd->change_item_tree(arg, conv);
-
if (conv->fix_fields(thd, arg))
+ return TRUE;
+
+ Query_arena *arena, backup;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+ if (arena)
{
- res= TRUE;
- break; // we cannot return here, we need to restore "arena".
+ Item_direct_ref_to_item *ref=
+ 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;
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();
+}
diff --git a/sql/item.h b/sql/item.h
index ec87a3fb812..e0bcaf41eac 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -6890,4 +6890,97 @@ inline void Virtual_column_info::print(String* 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 */
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 43c25db5097..b2e9df2d745 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -539,7 +539,7 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd,
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)
return true;
@@ -564,8 +564,7 @@ bool Item_func_in::create_value_list_for_tvc(THD *thd,
sprintf(col_name, "_col_%i", 1);
args[i]->set_name(thd, col_name, strlen(col_name), thd->charset());
}
- Item *arg_clone= args[i]->build_clone(thd);
- if (!arg_clone || tvc_value->push_back(arg_clone))
+ if (tvc_value->push_back(args[i]))
return true;
}
diff --git a/sql/table.cc b/sql/table.cc
index ec49241dd7a..a1ee2f3b8b3 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3010,7 +3010,7 @@ class Vcol_expr_context
bool inited;
THD *thd;
TABLE *table;
- Query_arena backup_arena;
+ Query_arena backup_arena, *stmt_arena;
table_map old_map;
Security_context *save_security_ctx;
sql_mode_t save_sql_mode;
@@ -3020,6 +3020,7 @@ public:
inited(false),
thd(_thd),
table(_table),
+ stmt_arena(thd->stmt_arena),
old_map(table->map),
save_security_ctx(thd->security_ctx),
save_sql_mode(thd->variables.sql_mode) {}
@@ -3040,6 +3041,7 @@ bool Vcol_expr_context::init()
thd->security_ctx= tl->security_ctx;
thd->set_n_backup_active_arena(table->expr_arena, &backup_arena);
+ thd->stmt_arena= thd;
inited= true;
return false;
@@ -3053,6 +3055,7 @@ Vcol_expr_context::~Vcol_expr_context()
thd->security_ctx= save_security_ctx;
thd->restore_active_arena(table->expr_arena, &backup_arena);
thd->variables.sql_mode= save_sql_mode;
+ thd->stmt_arena= stmt_arena;
}