[Commits] 4d22371: MDEV-15035 Wrong results when calling a stored procedure
by IgorBabaev 21 Apr '18
by IgorBabaev 21 Apr '18
21 Apr '18
revision-id: 4d223712c3f49e9b9f5dfb9d65b72bbd19132ad3 (mariadb-5.5.59-59-g4d22371)
parent(s): 5e61e1716e763315009318081fba5994b8910242
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-04-20 23:11:11 -0700
message:
MDEV-15035 Wrong results when calling a stored procedure
multiple times with different arguments.
If the ON expression of an outer join is an OR formula with one
of the disjunct being a constant formula then the expression
cannot be null-rejected if the constant formula is true. Otherwise
it can be null-rejected and if so the outer join can be converted
into inner join. This optimization was added in the patch for
mdev-4817. Yet the code had a defect: if the query was used in
a stored procedure with parameters and the constant item contained
some of them then the value of this constant item depended on the
values of the parameters. With some parameters it may be true,
for others not. The validity of conversion to inner join is checked
only once and it happens only for the first call of procedure.
So if the parameters in the first call allowed the conversion it
was done and next calls used the transformed query though there
could be calls whose parameters made the conversion invalid.
Fixed by cheking whether the constant disjunct in the ON expression
originally contained an SP parameter. If so the expression is not
considered as null-rejected. For this check a new item's attribute
was intruduced: Item::with_param. It is calculated for each item
by fix fields() functions.
---
mysql-test/r/sp-innodb.result | 34 ++++++++++++++++++++++++++++++++++
mysql-test/t/sp-innodb.test | 42 ++++++++++++++++++++++++++++++++++++++++++
sql/item.cc | 7 +++++++
sql/item.h | 1 +
sql/item_cmpfunc.cc | 12 +++++++++---
sql/item_func.cc | 1 +
sql/item_func.h | 7 +++++++
sql/item_row.cc | 1 +
sql/item_sum.cc | 3 +++
9 files changed, 105 insertions(+), 3 deletions(-)
diff --git a/mysql-test/r/sp-innodb.result b/mysql-test/r/sp-innodb.result
index b5fe920..9daf2c4 100644
--- a/mysql-test/r/sp-innodb.result
+++ b/mysql-test/r/sp-innodb.result
@@ -138,3 +138,37 @@ SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
#
# BUG 16041903: End of test case
#
+#
+# MDEV-15035: SP using query with outer join and a parameter
+# in ON expression
+#
+CREATE TABLE t1 (
+id int NOT NULL,
+PRIMARY KEY (id)
+) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1), (2);
+CREATE TABLE t2 (
+id int NOT NULL,
+id_foo int NOT NULL,
+PRIMARY KEY (id)
+) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1, 1);
+DROP PROCEDURE IF EXISTS test_proc;
+CREATE PROCEDURE test_proc(IN param int)
+LANGUAGE SQL
+READS SQL DATA
+BEGIN
+SELECT DISTINCT f.id
+FROM t1 f
+LEFT OUTER JOIN t2 b ON b.id_foo = f.id
+WHERE (param <> 0 OR b.id IS NOT NULL);
+END|
+CALL test_proc(0);
+id
+1
+CALL test_proc(1);
+id
+1
+2
+DROP PROCEDURE IF EXISTS test_proc;
+DROP TABLE t1, t2;
diff --git a/mysql-test/t/sp-innodb.test b/mysql-test/t/sp-innodb.test
index 2371516..e44a853 100644
--- a/mysql-test/t/sp-innodb.test
+++ b/mysql-test/t/sp-innodb.test
@@ -158,5 +158,47 @@ SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
--echo # BUG 16041903: End of test case
--echo #
+--echo #
+--echo # MDEV-15035: SP using query with outer join and a parameter
+--echo # in ON expression
+--echo #
+
+CREATE TABLE t1 (
+ id int NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES (1), (2);
+
+CREATE TABLE t2 (
+ id int NOT NULL,
+ id_foo int NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
+
+INSERT INTO t2 VALUES (1, 1);
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS test_proc;
+--enable_warnings
+
+DELIMITER |;
+CREATE PROCEDURE test_proc(IN param int)
+LANGUAGE SQL
+READS SQL DATA
+BEGIN
+ SELECT DISTINCT f.id
+ FROM t1 f
+ LEFT OUTER JOIN t2 b ON b.id_foo = f.id
+ WHERE (param <> 0 OR b.id IS NOT NULL);
+END|
+DELIMITER ;|
+
+CALL test_proc(0);
+CALL test_proc(1);
+
+DROP PROCEDURE IF EXISTS test_proc;
+DROP TABLE t1, t2;
+
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
diff --git a/sql/item.cc b/sql/item.cc
index 08a0061..c5c6df0 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -504,6 +504,7 @@ Item::Item():
in_rollup= 0;
decimals= 0; max_length= 0;
with_subselect= 0;
+ with_param= 0;
cmp_context= IMPOSSIBLE_RESULT;
/* Initially this item is not attached to any JOIN_TAB. */
join_tab_idx= MAX_TABLES;
@@ -550,6 +551,7 @@ Item::Item(THD *thd, Item *item):
null_value(item->null_value),
unsigned_flag(item->unsigned_flag),
with_sum_func(item->with_sum_func),
+ with_param(item->with_param),
with_field(item->with_field),
fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name),
@@ -1486,6 +1488,9 @@ bool Item_sp_variable::fix_fields(THD *thd, Item **)
max_length= it->max_length;
decimals= it->decimals;
unsigned_flag= it->unsigned_flag;
+ with_param= 1;
+ if (thd->lex->current_select->master_unit()->item)
+ thd->lex->current_select->master_unit()->item->with_param= 1;
fixed= 1;
collation.set(it->collation.collation, it->collation.derivation);
@@ -7234,6 +7239,7 @@ void Item_ref::set_properties()
split_sum_func() doesn't try to change the reference.
*/
with_sum_func= (*ref)->with_sum_func;
+ with_param= (*ref)->with_param;
with_field= (*ref)->with_field;
unsigned_flag= (*ref)->unsigned_flag;
fixed= 1;
@@ -7681,6 +7687,7 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
decimals= orig_item->decimals;
collation.set(orig_item->collation);
with_sum_func= orig_item->with_sum_func;
+ with_param= orig_item->with_param;
with_field= orig_item->with_field;
unsigned_flag= orig_item->unsigned_flag;
name= item_arg->name;
diff --git a/sql/item.h b/sql/item.h
index 830f8bf..d756cf8 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -644,6 +644,7 @@ class Item {
bool null_value; /* if item is null */
bool unsigned_flag;
bool with_sum_func; /* True if item contains a sum func */
+ bool with_param; /* True if contains an SP parameter */
/**
True if any item except Item_sum_func contains a field. Set during parsing.
*/
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 39f497e..db811a7 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1546,6 +1546,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
}
eval_not_null_tables(NULL);
with_sum_func= args[0]->with_sum_func;
+ with_param= args[0]->with_param || args[1]->with_param;
with_field= args[0]->with_field;
if ((const_item_cache= args[0]->const_item()))
{
@@ -1587,6 +1588,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
with_subselect= 1;
with_sum_func= with_sum_func || args[1]->with_sum_func;
with_field= with_field || args[1]->with_field;
+ with_param= args[0]->with_param || args[1]->with_param;
used_tables_cache|= args[1]->used_tables();
const_item_cache&= args[1]->const_item();
fixed= 1;
@@ -2108,6 +2110,7 @@ void Item_func_interval::fix_length_and_dec()
used_tables_cache|= row->used_tables();
not_null_tables_cache= row->not_null_tables();
with_sum_func= with_sum_func || row->with_sum_func;
+ with_param= with_param || row->with_param;
with_field= with_field || row->with_field;
const_item_cache&= row->const_item();
}
@@ -4427,6 +4430,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
}
with_sum_func= with_sum_func || item->with_sum_func;
+ with_param= with_param || item->with_param;
with_field= with_field || item->with_field;
with_subselect|= item->has_subquery();
if (item->maybe_null)
@@ -4449,14 +4453,15 @@ Item_cond::eval_not_null_tables(uchar *opt_arg)
while ((item=li++))
{
table_map tmp_table_map;
- if (item->const_item())
+ if (item->const_item() && !item->with_param)
{
if (!item->is_expensive() && !cond_has_datetime_is_null(item) &&
item->val_int() == 0)
{
/*
- This is "... OR false_cond OR ..."
- In this case, false_cond has no effect on cond_or->not_null_tables()
+ This is "... OR false_cond/null_cond OR ..."
+ In this case, false_cond/null_cond has no effect on
+ cond_or->not_null_tables()
*/
}
else
@@ -5118,6 +5123,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
args[1]->fix_fields(thd, args + 1)) || args[1]->check_cols(1))
return TRUE; /* purecov: inspected */
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
+ with_param=args[0]->with_param || args[1]->with_param;
with_field= args[0]->with_field || args[1]->with_field;
with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
max_length= 1;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 9e4edfc..8b3c72dd3 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -222,6 +222,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
maybe_null=1;
with_sum_func= with_sum_func || item->with_sum_func;
+ with_param= with_param || item->with_param;
with_field= with_field || item->with_field;
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
diff --git a/sql/item_func.h b/sql/item_func.h
index 5781822..3a609fc 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -83,6 +83,7 @@ class Item_func :public Item_result_field
args= tmp_arg;
args[0]= a;
with_sum_func= a->with_sum_func;
+ with_param= a->with_param;
with_field= a->with_field;
}
Item_func(Item *a,Item *b):
@@ -91,6 +92,7 @@ class Item_func :public Item_result_field
args= tmp_arg;
args[0]= a; args[1]= b;
with_sum_func= a->with_sum_func || b->with_sum_func;
+ with_param= a->with_param || b->with_param;
with_field= a->with_field || b->with_field;
}
Item_func(Item *a,Item *b,Item *c):
@@ -102,6 +104,7 @@ class Item_func :public Item_result_field
arg_count= 3;
args[0]= a; args[1]= b; args[2]= c;
with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func;
+ with_param= a->with_param || b->with_param || c->with_param;
with_field= a->with_field || b->with_field || c->with_field;
}
}
@@ -115,6 +118,8 @@ class Item_func :public Item_result_field
args[0]= a; args[1]= b; args[2]= c; args[3]= d;
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func;
+ with_param= a->with_param || b->with_param ||
+ c->with_param || d->with_param;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field;
}
@@ -128,6 +133,8 @@ class Item_func :public Item_result_field
args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e;
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func || e->with_sum_func ;
+ with_param= a->with_param || b->with_param ||
+ c->with_param || d->with_param || e->with_param;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field || e->with_field;
}
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 9e81c05..9fe34dd 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -125,6 +125,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
with_sum_func= with_sum_func || item->with_sum_func;
with_field= with_field || item->with_field;
with_subselect|= item->with_subselect;
+ with_param|= item->with_param;
}
fixed= 1;
return FALSE;
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 709c2b6..16334cd 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1164,6 +1164,7 @@ Item_sum_num::fix_fields(THD *thd, Item **ref)
return TRUE;
set_if_bigger(decimals, args[i]->decimals);
with_subselect|= args[i]->with_subselect;
+ with_param|= args[i]->with_param;
}
result_field=0;
max_length=float_length(decimals);
@@ -1195,6 +1196,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
return TRUE;
decimals=item->decimals;
with_subselect= args[0]->with_subselect;
+ with_param= args[0]->with_param;
switch (hybrid_type= item->result_type()) {
case INT_RESULT:
@@ -3430,6 +3432,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
args[i]->check_cols(1))
return TRUE;
with_subselect|= args[i]->with_subselect;
+ with_param|= args[i]->with_param;
}
/* skip charset aggregation for order columns */
1
0
[Commits] fcebc77: MDEV-15035 Wrong results when calling a stored procedure
by IgorBabaev 21 Apr '18
by IgorBabaev 21 Apr '18
21 Apr '18
revision-id: fcebc778dbf865c8dab5f0f0800e4eab5018d76f (mariadb-5.5.59-59-gfcebc77)
parent(s): 5e61e1716e763315009318081fba5994b8910242
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-04-20 23:03:40 -0700
message:
MDEV-15035 Wrong results when calling a stored procedure
multiple times with different arguments.
If the ON expression of an outer join is an OR formula with one
of the disjunct being a constant formula then the expression
cannot be null-rejected if the constant formula is true. Otherwise
it can be null-rejected and if so the outer join can be converted
into inner join. This optimization was added in the patch for
mdev-4817. Yet the code had a defect: if the query was used in
a stored procedure with parameters and the constant item contained
some of them then the value of this constant item depended on the
values of the parameters. With some parameters it may be true,
for others not. The validity of conversion to inner join is checked
only once and it happens only for the first call of procedure.
So if the parameters in the first call allowed the conversion it
was done and next calls used the transformed query though there
could be calls whose parameters made the conversion invalid.
Fixed by cheking whether the constant disjunct in the ON expression
originally contained an SP parameter. If so the expression is not
considered as null-rejected. For this check a new item's attribute
was intruduced: Item::with_param. It is calculated for each item
by fix fields() functions.
---
mysql-test/r/sp-innodb.result | 34 ++++++++++++++++++++++++++++++++++
mysql-test/t/sp-innodb.test | 42 ++++++++++++++++++++++++++++++++++++++++++
sql/item.cc | 7 +++++++
sql/item.h | 1 +
sql/item_cmpfunc.cc | 12 +++++++++---
sql/item_func.cc | 1 +
sql/item_func.h | 7 +++++++
sql/item_sum.cc | 3 +++
8 files changed, 104 insertions(+), 3 deletions(-)
diff --git a/mysql-test/r/sp-innodb.result b/mysql-test/r/sp-innodb.result
index b5fe920..9daf2c4 100644
--- a/mysql-test/r/sp-innodb.result
+++ b/mysql-test/r/sp-innodb.result
@@ -138,3 +138,37 @@ SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
#
# BUG 16041903: End of test case
#
+#
+# MDEV-15035: SP using query with outer join and a parameter
+# in ON expression
+#
+CREATE TABLE t1 (
+id int NOT NULL,
+PRIMARY KEY (id)
+) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1), (2);
+CREATE TABLE t2 (
+id int NOT NULL,
+id_foo int NOT NULL,
+PRIMARY KEY (id)
+) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1, 1);
+DROP PROCEDURE IF EXISTS test_proc;
+CREATE PROCEDURE test_proc(IN param int)
+LANGUAGE SQL
+READS SQL DATA
+BEGIN
+SELECT DISTINCT f.id
+FROM t1 f
+LEFT OUTER JOIN t2 b ON b.id_foo = f.id
+WHERE (param <> 0 OR b.id IS NOT NULL);
+END|
+CALL test_proc(0);
+id
+1
+CALL test_proc(1);
+id
+1
+2
+DROP PROCEDURE IF EXISTS test_proc;
+DROP TABLE t1, t2;
diff --git a/mysql-test/t/sp-innodb.test b/mysql-test/t/sp-innodb.test
index 2371516..e44a853 100644
--- a/mysql-test/t/sp-innodb.test
+++ b/mysql-test/t/sp-innodb.test
@@ -158,5 +158,47 @@ SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
--echo # BUG 16041903: End of test case
--echo #
+--echo #
+--echo # MDEV-15035: SP using query with outer join and a parameter
+--echo # in ON expression
+--echo #
+
+CREATE TABLE t1 (
+ id int NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES (1), (2);
+
+CREATE TABLE t2 (
+ id int NOT NULL,
+ id_foo int NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
+
+INSERT INTO t2 VALUES (1, 1);
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS test_proc;
+--enable_warnings
+
+DELIMITER |;
+CREATE PROCEDURE test_proc(IN param int)
+LANGUAGE SQL
+READS SQL DATA
+BEGIN
+ SELECT DISTINCT f.id
+ FROM t1 f
+ LEFT OUTER JOIN t2 b ON b.id_foo = f.id
+ WHERE (param <> 0 OR b.id IS NOT NULL);
+END|
+DELIMITER ;|
+
+CALL test_proc(0);
+CALL test_proc(1);
+
+DROP PROCEDURE IF EXISTS test_proc;
+DROP TABLE t1, t2;
+
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
diff --git a/sql/item.cc b/sql/item.cc
index 08a0061..c5c6df0 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -504,6 +504,7 @@ Item::Item():
in_rollup= 0;
decimals= 0; max_length= 0;
with_subselect= 0;
+ with_param= 0;
cmp_context= IMPOSSIBLE_RESULT;
/* Initially this item is not attached to any JOIN_TAB. */
join_tab_idx= MAX_TABLES;
@@ -550,6 +551,7 @@ Item::Item(THD *thd, Item *item):
null_value(item->null_value),
unsigned_flag(item->unsigned_flag),
with_sum_func(item->with_sum_func),
+ with_param(item->with_param),
with_field(item->with_field),
fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name),
@@ -1486,6 +1488,9 @@ bool Item_sp_variable::fix_fields(THD *thd, Item **)
max_length= it->max_length;
decimals= it->decimals;
unsigned_flag= it->unsigned_flag;
+ with_param= 1;
+ if (thd->lex->current_select->master_unit()->item)
+ thd->lex->current_select->master_unit()->item->with_param= 1;
fixed= 1;
collation.set(it->collation.collation, it->collation.derivation);
@@ -7234,6 +7239,7 @@ void Item_ref::set_properties()
split_sum_func() doesn't try to change the reference.
*/
with_sum_func= (*ref)->with_sum_func;
+ with_param= (*ref)->with_param;
with_field= (*ref)->with_field;
unsigned_flag= (*ref)->unsigned_flag;
fixed= 1;
@@ -7681,6 +7687,7 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
decimals= orig_item->decimals;
collation.set(orig_item->collation);
with_sum_func= orig_item->with_sum_func;
+ with_param= orig_item->with_param;
with_field= orig_item->with_field;
unsigned_flag= orig_item->unsigned_flag;
name= item_arg->name;
diff --git a/sql/item.h b/sql/item.h
index 830f8bf..d756cf8 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -644,6 +644,7 @@ class Item {
bool null_value; /* if item is null */
bool unsigned_flag;
bool with_sum_func; /* True if item contains a sum func */
+ bool with_param; /* True if contains an SP parameter */
/**
True if any item except Item_sum_func contains a field. Set during parsing.
*/
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 39f497e..db811a7 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1546,6 +1546,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
}
eval_not_null_tables(NULL);
with_sum_func= args[0]->with_sum_func;
+ with_param= args[0]->with_param || args[1]->with_param;
with_field= args[0]->with_field;
if ((const_item_cache= args[0]->const_item()))
{
@@ -1587,6 +1588,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
with_subselect= 1;
with_sum_func= with_sum_func || args[1]->with_sum_func;
with_field= with_field || args[1]->with_field;
+ with_param= args[0]->with_param || args[1]->with_param;
used_tables_cache|= args[1]->used_tables();
const_item_cache&= args[1]->const_item();
fixed= 1;
@@ -2108,6 +2110,7 @@ void Item_func_interval::fix_length_and_dec()
used_tables_cache|= row->used_tables();
not_null_tables_cache= row->not_null_tables();
with_sum_func= with_sum_func || row->with_sum_func;
+ with_param= with_param || row->with_param;
with_field= with_field || row->with_field;
const_item_cache&= row->const_item();
}
@@ -4427,6 +4430,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
}
with_sum_func= with_sum_func || item->with_sum_func;
+ with_param= with_param || item->with_param;
with_field= with_field || item->with_field;
with_subselect|= item->has_subquery();
if (item->maybe_null)
@@ -4449,14 +4453,15 @@ Item_cond::eval_not_null_tables(uchar *opt_arg)
while ((item=li++))
{
table_map tmp_table_map;
- if (item->const_item())
+ if (item->const_item() && !item->with_param)
{
if (!item->is_expensive() && !cond_has_datetime_is_null(item) &&
item->val_int() == 0)
{
/*
- This is "... OR false_cond OR ..."
- In this case, false_cond has no effect on cond_or->not_null_tables()
+ This is "... OR false_cond/null_cond OR ..."
+ In this case, false_cond/null_cond has no effect on
+ cond_or->not_null_tables()
*/
}
else
@@ -5118,6 +5123,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
args[1]->fix_fields(thd, args + 1)) || args[1]->check_cols(1))
return TRUE; /* purecov: inspected */
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
+ with_param=args[0]->with_param || args[1]->with_param;
with_field= args[0]->with_field || args[1]->with_field;
with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
max_length= 1;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 9e4edfc..8b3c72dd3 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -222,6 +222,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
maybe_null=1;
with_sum_func= with_sum_func || item->with_sum_func;
+ with_param= with_param || item->with_param;
with_field= with_field || item->with_field;
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
diff --git a/sql/item_func.h b/sql/item_func.h
index 5781822..3a609fc 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -83,6 +83,7 @@ class Item_func :public Item_result_field
args= tmp_arg;
args[0]= a;
with_sum_func= a->with_sum_func;
+ with_param= a->with_param;
with_field= a->with_field;
}
Item_func(Item *a,Item *b):
@@ -91,6 +92,7 @@ class Item_func :public Item_result_field
args= tmp_arg;
args[0]= a; args[1]= b;
with_sum_func= a->with_sum_func || b->with_sum_func;
+ with_param= a->with_param || b->with_param;
with_field= a->with_field || b->with_field;
}
Item_func(Item *a,Item *b,Item *c):
@@ -102,6 +104,7 @@ class Item_func :public Item_result_field
arg_count= 3;
args[0]= a; args[1]= b; args[2]= c;
with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func;
+ with_param= a->with_param || b->with_param || c->with_param;
with_field= a->with_field || b->with_field || c->with_field;
}
}
@@ -115,6 +118,8 @@ class Item_func :public Item_result_field
args[0]= a; args[1]= b; args[2]= c; args[3]= d;
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func;
+ with_param= a->with_param || b->with_param ||
+ c->with_param || d->with_param;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field;
}
@@ -128,6 +133,8 @@ class Item_func :public Item_result_field
args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e;
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func || e->with_sum_func ;
+ with_param= a->with_param || b->with_param ||
+ c->with_param || d->with_param || e->with_param;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field || e->with_field;
}
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 709c2b6..16334cd 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1164,6 +1164,7 @@ Item_sum_num::fix_fields(THD *thd, Item **ref)
return TRUE;
set_if_bigger(decimals, args[i]->decimals);
with_subselect|= args[i]->with_subselect;
+ with_param|= args[i]->with_param;
}
result_field=0;
max_length=float_length(decimals);
@@ -1195,6 +1196,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
return TRUE;
decimals=item->decimals;
with_subselect= args[0]->with_subselect;
+ with_param= args[0]->with_param;
switch (hybrid_type= item->result_type()) {
case INT_RESULT:
@@ -3430,6 +3432,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
args[i]->check_cols(1))
return TRUE;
with_subselect|= args[i]->with_subselect;
+ with_param|= args[i]->with_param;
}
/* skip charset aggregation for order columns */
1
0
[Commits] e86fbf3: MDEV-15035 Wrong results when calling a stored procedure
by IgorBabaev 21 Apr '18
by IgorBabaev 21 Apr '18
21 Apr '18
revision-id: e86fbf3d9c679d19ab824e150fe2ccdea89e0021 (mariadb-5.5.59-59-ge86fbf3)
parent(s): 5e61e1716e763315009318081fba5994b8910242
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-04-20 22:57:06 -0700
message:
MDEV-15035 Wrong results when calling a stored procedure
multiple times with different arguments.
If the ON expression of an outer join is an OR formula with one
of the disjunct being a constant formula then the expression
cannot be null-rejected if the constant formula is true. Otherwise
it can be null-rejected and if so the outer join can be converted
into inner join. This optimization was added in the patch for
mdev-4817. Yet the code had a defect: if the query was used in
a stored procedure with parameters and the constant item contained
some of them then the value of this constant item depended on the
values of the parameters. With some parameters it may be true,
for others not. The validity of conversion to inner join is checked
only once and it happens only for the first call of procedure.
So if the parameters in the first call allowed the conversion it
was done and next calls used the transformed query though there
could be calls whose parameters made the conversion invalid.
Fixed by cheking whether the constant disjunct in the ON expression
originally contained an SP parameter. If so the expression is not
considered as null-rejected. For this check a new item's attribute
was intruduced: Item::with_param. It is calculated for each item
by fix fields() functions.
---
mysql-test/r/sp-innodb.result | 34 ++++++++++++++++++++++++++++++++++
mysql-test/t/sp-innodb.test | 42 ++++++++++++++++++++++++++++++++++++++++++
sql/item.cc | 7 +++++++
sql/item.h | 1 +
sql/item_cmpfunc.cc | 12 +++++++++---
sql/item_func.cc | 1 +
sql/item_func.h | 7 +++++++
sql/item_sum.cc | 3 +++
8 files changed, 104 insertions(+), 3 deletions(-)
diff --git a/mysql-test/r/sp-innodb.result b/mysql-test/r/sp-innodb.result
index b5fe920..2ca0a04 100644
--- a/mysql-test/r/sp-innodb.result
+++ b/mysql-test/r/sp-innodb.result
@@ -138,3 +138,37 @@ SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
#
# BUG 16041903: End of test case
#
+#
+# MDEV-15035: SP using query with outer join and a parameter
+# in WHERE clause
+#
+CREATE TABLE t1 (
+id int NOT NULL,
+PRIMARY KEY (id)
+) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1), (2);
+CREATE TABLE t2 (
+id int NOT NULL,
+id_foo int NOT NULL,
+PRIMARY KEY (id)
+) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1, 1);
+DROP PROCEDURE IF EXISTS test_proc;
+CREATE PROCEDURE test_proc(IN param int)
+LANGUAGE SQL
+READS SQL DATA
+BEGIN
+SELECT DISTINCT f.id
+FROM t1 f
+LEFT OUTER JOIN t2 b ON b.id_foo = f.id
+WHERE (param <> 0 OR b.id IS NOT NULL);
+END|
+CALL test_proc(0);
+id
+1
+CALL test_proc(1);
+id
+1
+2
+DROP PROCEDURE IF EXISTS test_proc;
+DROP TABLE t1, t2;
diff --git a/mysql-test/t/sp-innodb.test b/mysql-test/t/sp-innodb.test
index 2371516..7a2929e 100644
--- a/mysql-test/t/sp-innodb.test
+++ b/mysql-test/t/sp-innodb.test
@@ -158,5 +158,47 @@ SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
--echo # BUG 16041903: End of test case
--echo #
+--echo #
+--echo # MDEV-15035: SP using query with outer join and a parameter
+--echo # in WHERE clause
+--echo #
+
+CREATE TABLE t1 (
+ id int NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES (1), (2);
+
+CREATE TABLE t2 (
+ id int NOT NULL,
+ id_foo int NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
+
+INSERT INTO t2 VALUES (1, 1);
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS test_proc;
+--enable_warnings
+
+DELIMITER |;
+CREATE PROCEDURE test_proc(IN param int)
+LANGUAGE SQL
+READS SQL DATA
+BEGIN
+ SELECT DISTINCT f.id
+ FROM t1 f
+ LEFT OUTER JOIN t2 b ON b.id_foo = f.id
+ WHERE (param <> 0 OR b.id IS NOT NULL);
+END|
+DELIMITER ;|
+
+CALL test_proc(0);
+CALL test_proc(1);
+
+DROP PROCEDURE IF EXISTS test_proc;
+DROP TABLE t1, t2;
+
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
diff --git a/sql/item.cc b/sql/item.cc
index 08a0061..c5c6df0 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -504,6 +504,7 @@ Item::Item():
in_rollup= 0;
decimals= 0; max_length= 0;
with_subselect= 0;
+ with_param= 0;
cmp_context= IMPOSSIBLE_RESULT;
/* Initially this item is not attached to any JOIN_TAB. */
join_tab_idx= MAX_TABLES;
@@ -550,6 +551,7 @@ Item::Item(THD *thd, Item *item):
null_value(item->null_value),
unsigned_flag(item->unsigned_flag),
with_sum_func(item->with_sum_func),
+ with_param(item->with_param),
with_field(item->with_field),
fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name),
@@ -1486,6 +1488,9 @@ bool Item_sp_variable::fix_fields(THD *thd, Item **)
max_length= it->max_length;
decimals= it->decimals;
unsigned_flag= it->unsigned_flag;
+ with_param= 1;
+ if (thd->lex->current_select->master_unit()->item)
+ thd->lex->current_select->master_unit()->item->with_param= 1;
fixed= 1;
collation.set(it->collation.collation, it->collation.derivation);
@@ -7234,6 +7239,7 @@ void Item_ref::set_properties()
split_sum_func() doesn't try to change the reference.
*/
with_sum_func= (*ref)->with_sum_func;
+ with_param= (*ref)->with_param;
with_field= (*ref)->with_field;
unsigned_flag= (*ref)->unsigned_flag;
fixed= 1;
@@ -7681,6 +7687,7 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
decimals= orig_item->decimals;
collation.set(orig_item->collation);
with_sum_func= orig_item->with_sum_func;
+ with_param= orig_item->with_param;
with_field= orig_item->with_field;
unsigned_flag= orig_item->unsigned_flag;
name= item_arg->name;
diff --git a/sql/item.h b/sql/item.h
index 830f8bf..d756cf8 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -644,6 +644,7 @@ class Item {
bool null_value; /* if item is null */
bool unsigned_flag;
bool with_sum_func; /* True if item contains a sum func */
+ bool with_param; /* True if contains an SP parameter */
/**
True if any item except Item_sum_func contains a field. Set during parsing.
*/
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 39f497e..db811a7 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1546,6 +1546,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
}
eval_not_null_tables(NULL);
with_sum_func= args[0]->with_sum_func;
+ with_param= args[0]->with_param || args[1]->with_param;
with_field= args[0]->with_field;
if ((const_item_cache= args[0]->const_item()))
{
@@ -1587,6 +1588,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
with_subselect= 1;
with_sum_func= with_sum_func || args[1]->with_sum_func;
with_field= with_field || args[1]->with_field;
+ with_param= args[0]->with_param || args[1]->with_param;
used_tables_cache|= args[1]->used_tables();
const_item_cache&= args[1]->const_item();
fixed= 1;
@@ -2108,6 +2110,7 @@ void Item_func_interval::fix_length_and_dec()
used_tables_cache|= row->used_tables();
not_null_tables_cache= row->not_null_tables();
with_sum_func= with_sum_func || row->with_sum_func;
+ with_param= with_param || row->with_param;
with_field= with_field || row->with_field;
const_item_cache&= row->const_item();
}
@@ -4427,6 +4430,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
}
with_sum_func= with_sum_func || item->with_sum_func;
+ with_param= with_param || item->with_param;
with_field= with_field || item->with_field;
with_subselect|= item->has_subquery();
if (item->maybe_null)
@@ -4449,14 +4453,15 @@ Item_cond::eval_not_null_tables(uchar *opt_arg)
while ((item=li++))
{
table_map tmp_table_map;
- if (item->const_item())
+ if (item->const_item() && !item->with_param)
{
if (!item->is_expensive() && !cond_has_datetime_is_null(item) &&
item->val_int() == 0)
{
/*
- This is "... OR false_cond OR ..."
- In this case, false_cond has no effect on cond_or->not_null_tables()
+ This is "... OR false_cond/null_cond OR ..."
+ In this case, false_cond/null_cond has no effect on
+ cond_or->not_null_tables()
*/
}
else
@@ -5118,6 +5123,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
args[1]->fix_fields(thd, args + 1)) || args[1]->check_cols(1))
return TRUE; /* purecov: inspected */
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
+ with_param=args[0]->with_param || args[1]->with_param;
with_field= args[0]->with_field || args[1]->with_field;
with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
max_length= 1;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 9e4edfc..8b3c72dd3 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -222,6 +222,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
maybe_null=1;
with_sum_func= with_sum_func || item->with_sum_func;
+ with_param= with_param || item->with_param;
with_field= with_field || item->with_field;
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
diff --git a/sql/item_func.h b/sql/item_func.h
index 5781822..3a609fc 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -83,6 +83,7 @@ class Item_func :public Item_result_field
args= tmp_arg;
args[0]= a;
with_sum_func= a->with_sum_func;
+ with_param= a->with_param;
with_field= a->with_field;
}
Item_func(Item *a,Item *b):
@@ -91,6 +92,7 @@ class Item_func :public Item_result_field
args= tmp_arg;
args[0]= a; args[1]= b;
with_sum_func= a->with_sum_func || b->with_sum_func;
+ with_param= a->with_param || b->with_param;
with_field= a->with_field || b->with_field;
}
Item_func(Item *a,Item *b,Item *c):
@@ -102,6 +104,7 @@ class Item_func :public Item_result_field
arg_count= 3;
args[0]= a; args[1]= b; args[2]= c;
with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func;
+ with_param= a->with_param || b->with_param || c->with_param;
with_field= a->with_field || b->with_field || c->with_field;
}
}
@@ -115,6 +118,8 @@ class Item_func :public Item_result_field
args[0]= a; args[1]= b; args[2]= c; args[3]= d;
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func;
+ with_param= a->with_param || b->with_param ||
+ c->with_param || d->with_param;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field;
}
@@ -128,6 +133,8 @@ class Item_func :public Item_result_field
args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e;
with_sum_func= a->with_sum_func || b->with_sum_func ||
c->with_sum_func || d->with_sum_func || e->with_sum_func ;
+ with_param= a->with_param || b->with_param ||
+ c->with_param || d->with_param || e->with_param;
with_field= a->with_field || b->with_field ||
c->with_field || d->with_field || e->with_field;
}
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 709c2b6..16334cd 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1164,6 +1164,7 @@ Item_sum_num::fix_fields(THD *thd, Item **ref)
return TRUE;
set_if_bigger(decimals, args[i]->decimals);
with_subselect|= args[i]->with_subselect;
+ with_param|= args[i]->with_param;
}
result_field=0;
max_length=float_length(decimals);
@@ -1195,6 +1196,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
return TRUE;
decimals=item->decimals;
with_subselect= args[0]->with_subselect;
+ with_param= args[0]->with_param;
switch (hybrid_type= item->result_type()) {
case INT_RESULT:
@@ -3430,6 +3432,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
args[i]->check_cols(1))
return TRUE;
with_subselect|= args[i]->with_subselect;
+ with_param|= args[i]->with_param;
}
/* skip charset aggregation for order columns */
1
0
[Commits] b9e28d4227e: MDEV-15777:Support Early NULLs filtering-like restrictions in the range optimizer
by varunraiko1803ï¼ gmail.com 20 Apr '18
by varunraiko1803ï¼ gmail.com 20 Apr '18
20 Apr '18
revision-id: b9e28d4227ea4b4d9cd147f87354afaf44e14746 (mariadb-10.3.0-765-gb9e28d4227e)
parent(s): 91245909a2f0c89444ecb5af587284f53b7196ee
author: Varun Gupta
committer: Varun Gupta
timestamp: 2018-04-20 22:58:45 +0530
message:
MDEV-15777:Support Early NULLs filtering-like restrictions in the range optimizer
Introduced function make_null_rejecting_conds() that would calcualte the null rejecting conds for the keys of a table and
then we can perform range analysis on these conditions.
TABLE strucuture introduces a null_rejecting_cond field that would hold these null rejecting conditions for a table.
---
mysql-test/main/mdev15777.result | 65 ++++++++++++++++++++
mysql-test/main/mdev15777.test | 34 +++++++++++
sql/opt_range.cc | 124 ++++++++++++++++++++++++++++++++++++++-
sql/opt_range.h | 2 +
sql/sql_select.cc | 22 ++++++-
sql/table.cc | 1 +
sql/table.h | 6 ++
7 files changed, 250 insertions(+), 4 deletions(-)
diff --git a/mysql-test/main/mdev15777.result b/mysql-test/main/mdev15777.result
new file mode 100644
index 00000000000..87167c16f6d
--- /dev/null
+++ b/mysql-test/main/mdev15777.result
@@ -0,0 +1,65 @@
+create table ten(a int);
+insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table one_k(a int);
+insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C;
+create table one_m(a int);
+insert into one_m select A.a + B.a* 1000 from one_k A, one_k B;
+delete from one_m where a=0 limit 1;
+create table t1 (
+id int(10) unsigned NOT NULL AUTO_INCREMENT,
+filler varchar(100),
+subset_id int(11) DEFAULT NULL,
+PRIMARY KEY (id),
+KEY t1_subset_id (subset_id)
+);
+create table t1_subsets (
+id int(10) unsigned NOT NULL AUTO_INCREMENT,
+filler1 varchar(100),
+filler2 varchar(100),
+filler3 varchar(100),
+PRIMARY KEY (id)
+);
+insert into t1 select a,a, NULL from one_m where a < 50*1000;
+insert into t1_subsets select a,a,a,a from one_m where a < 500*1000 limit 499000;
+analyze format=json
+SELECT * FROM t1 WHERE t1.subset_id IN (SELECT t1_subsets.id FROM t1_subsets);
+ANALYZE
+{
+ "query_block": {
+ "select_id": 1,
+ "r_loops": 1,
+ "r_total_time_ms": 0.0444,
+ "table": {
+ "table_name": "t1",
+ "access_type": "range",
+ "possible_keys": ["t1_subset_id"],
+ "key": "t1_subset_id",
+ "key_length": "5",
+ "used_key_parts": ["subset_id"],
+ "r_loops": 1,
+ "rows": 3,
+ "r_rows": 0,
+ "r_total_time_ms": 0.0146,
+ "filtered": 100,
+ "r_filtered": 100,
+ "index_condition": "t1.subset_id is not null"
+ },
+ "table": {
+ "table_name": "t1_subsets",
+ "access_type": "eq_ref",
+ "possible_keys": ["PRIMARY"],
+ "key": "PRIMARY",
+ "key_length": "4",
+ "used_key_parts": ["id"],
+ "ref": ["test.t1.subset_id"],
+ "r_loops": 0,
+ "rows": 1,
+ "r_rows": null,
+ "filtered": 100,
+ "r_filtered": null,
+ "attached_condition": "t1.subset_id = t1_subsets.`id`",
+ "using_index": true
+ }
+ }
+}
+drop table t1,t1_subsets,ten,one_k,one_m;
diff --git a/mysql-test/main/mdev15777.test b/mysql-test/main/mdev15777.test
new file mode 100644
index 00000000000..ad5777aeecf
--- /dev/null
+++ b/mysql-test/main/mdev15777.test
@@ -0,0 +1,34 @@
+create table ten(a int);
+insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+
+create table one_k(a int);
+insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C;
+
+create table one_m(a int);
+insert into one_m select A.a + B.a* 1000 from one_k A, one_k B;
+delete from one_m where a=0 limit 1;
+
+create table t1 (
+ id int(10) unsigned NOT NULL AUTO_INCREMENT,
+ filler varchar(100),
+ subset_id int(11) DEFAULT NULL,
+ PRIMARY KEY (id),
+ KEY t1_subset_id (subset_id)
+);
+
+create table t1_subsets (
+ id int(10) unsigned NOT NULL AUTO_INCREMENT,
+ filler1 varchar(100),
+ filler2 varchar(100),
+ filler3 varchar(100),
+ PRIMARY KEY (id)
+);
+
+insert into t1 select a,a, NULL from one_m where a < 50*1000;
+insert into t1_subsets select a,a,a,a from one_m where a < 500*1000 limit 499000;
+
+analyze format=json
+SELECT * FROM t1 WHERE t1.subset_id IN (SELECT t1_subsets.id FROM t1_subsets);
+drop table t1,t1_subsets,ten,one_k,one_m;
+
+
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 38dbed92a22..9343f97382d 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -2399,6 +2399,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
uint idx;
double scan_time;
+ Item *null_rejecting_conds= NULL;
DBUG_ENTER("SQL_SELECT::test_quick_select");
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
(ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables,
@@ -2422,6 +2423,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
read_time= (double) records + scan_time + 1; // Force to use index
possible_keys.clear_all();
+ null_rejecting_conds= head->null_rejecting_conds;
DBUG_PRINT("info",("Time to scan table: %g", read_time));
@@ -2430,7 +2432,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
uchar buff[STACK_BUFF_ALLOC];
MEM_ROOT alloc;
- SEL_TREE *tree= NULL;
+ SEL_TREE *tree= NULL, *not_null_cond_tree= NULL;
KEY_PART *key_parts;
KEY *key_info;
PARAM param;
@@ -2539,6 +2541,12 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
TRP_GROUP_MIN_MAX *group_trp;
double best_read_time= read_time;
+ if (null_rejecting_conds)
+ {
+ not_null_cond_tree= null_rejecting_conds->get_mm_tree(¶m,
+ &null_rejecting_conds);
+ }
+
if (cond)
{
if ((tree= cond->get_mm_tree(¶m, &cond)))
@@ -2557,6 +2565,13 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
tree= NULL;
}
}
+ if (not_null_cond_tree)
+ {
+ if (!tree)
+ tree= not_null_cond_tree;
+ else
+ tree= tree_and(¶m, tree, not_null_cond_tree);
+ }
/*
Try to construct a QUICK_GROUP_MIN_MAX_SELECT.
@@ -14647,6 +14662,113 @@ void QUICK_GROUP_MIN_MAX_SELECT::add_keys_and_lengths(String *key_names,
add_key_and_length(key_names, used_lengths, &first);
}
+inline void add_cond(THD *thd, Item **e1, Item *e2)
+{
+ if (*e1)
+ {
+ if (!e2)
+ return;
+ Item *res;
+ if ((res= new (thd->mem_root) Item_cond_and(thd, *e1, e2)))
+ {
+ res->fix_fields(thd, 0);
+ res->update_used_tables();
+ *e1= res;
+ }
+ }
+ else
+ *e1= e2;
+}
+
+/*
+ Create null rejecting conditions for a table, for all the equalites
+ present in the WHERE clause of a query.
+
+ SYNOPSIS
+ make_null_rejecting_conds()
+ @param TABLE - Keys of this table will participate in null
+ rejecting conditions
+ @param keyuse_array - array that has all the equalites of the
+ WHERE clasuse
+
+ DESCRIPTION
+ This function creates null rejecting conditions for a table. These
+ conditions are created to do range analysis on them , the conditions
+ are of the form tbl.key.keypart IS NOT NULL.
+
+ IMPLEMENTATION
+ Lookup in the keyuse array to check if it has equalites that belong
+ to the given table. If yes then find out if the conditions are null
+ rejecting and accordingly create all the condition for the keys of a
+ given table and AND them.
+
+
+ RETURN
+ NOT NULL - Found null rejecting conditions for the given table
+ NULL - No null rejecting conditions for the given table
+*/
+
+void make_null_rejecting_conds(THD *thd, TABLE *table,
+ DYNAMIC_ARRAY *keyuse_array, key_map *const_keys)
+{
+ KEY *keyinfo;
+ Item *cond= NULL;
+ KEYUSE* keyuse;
+
+ /*
+ The null rejecting conds added will be on the keypart of a key, so for
+ that we need the table to atleast have a key.
+ */
+ if (!table->s->keys)
+ return ;
+ if (table->null_rejecting_conds)
+ return;
+
+ for(uint i=0; i < keyuse_array->elements; i++)
+ {
+ keyuse= (KEYUSE*)dynamic_array_ptr(keyuse_array, i);
+ if (keyuse->table == table)
+ {
+ /*
+ No null rejecting conds for a hash key
+ */
+ if (keyuse->key == MAX_KEY)
+ continue;
+ keyinfo= keyuse->table->key_info+keyuse->key;
+ Field *field= keyinfo->key_part[keyuse->keypart].field;
+
+ /*
+ No need to add null-rejecting condition if we have a
+ keyuse element as
+ - table.key.keypart= const
+ - (table.key.keypart= tbl.otherfield or table.key.keypart IS NULL)
+ - table.key.keypart IS NOT NULLABLE
+ */
+
+ if (keyuse->val->const_item()
+ || !(keyuse->null_rejecting && field->maybe_null())
+ || keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL)
+ continue;
+
+ Item_field *field_item= new (thd->mem_root)Item_field(thd, field);
+ Item* not_null_item= new (thd->mem_root)Item_func_isnotnull(thd,
+ field_item);
+
+ /*
+ adding the key to const keys as we have the condition
+ as key.keypart IS NOT NULL
+ */
+
+ const_keys->set_bit(keyuse->key);
+ not_null_item->fix_fields(thd, 0);
+ not_null_item->update_used_tables();
+ add_cond(thd, &cond, not_null_item);
+ }
+ }
+ table->null_rejecting_conds= cond;
+ return;
+}
+
#ifndef DBUG_OFF
diff --git a/sql/opt_range.h b/sql/opt_range.h
index bd85a12d4a1..894d46a892c 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -1728,6 +1728,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond);
bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond);
#endif
void store_key_image_to_rec(Field *field, uchar *ptr, uint len);
+void make_null_rejecting_conds(THD *thd, TABLE *table,
+ DYNAMIC_ARRAY *keyuse_array, key_map *const_keys);
extern String null_string;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 796ea569e64..daef79e2f68 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -4783,6 +4783,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
add_group_and_distinct_keys(join, s);
s->table->cond_selectivity= 1.0;
+
+ make_null_rejecting_conds(join->thd, s->table,
+ keyuse_array, &s->const_keys);
/*
Perform range analysis if there are keys it could use (1).
@@ -4812,6 +4815,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
1, &error);
if (!select)
goto error;
+
records= get_quick_record_count(join->thd, select, s->table,
&s->const_keys, join->row_limit);
/* Range analyzer could modify the condition. */
@@ -5330,15 +5334,24 @@ add_key_field(JOIN *join,
If the condition has form "tbl.keypart = othertbl.field" and
othertbl.field can be NULL, there will be no matches if othertbl.field
has NULL value.
+
+ The field KEY_FIELD::null_rejecting is set to TRUE if we have both
+ the left and right hand side of the equality are NULLABLE
+
We use null_rejecting in add_not_null_conds() to add
'othertbl.field IS NOT NULL' to tab->select_cond.
+
+ We use null_rejecting in make_null_rejecting_conds() to add
+ tbl.keypart IS NOT NULL so we can do range analysis on this condition
+
*/
{
Item *real= (*value)->real_item();
if (((cond->functype() == Item_func::EQ_FUNC) ||
(cond->functype() == Item_func::MULT_EQUAL_FUNC)) &&
- (real->type() == Item::FIELD_ITEM) &&
+ (((real->type() == Item::FIELD_ITEM) &&
((Item_field*)real)->field->maybe_null())
+ ||(field->maybe_null())))
(*key_fields)->null_rejecting= true;
else
(*key_fields)->null_rejecting= false;
@@ -9794,7 +9807,10 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
uint maybe_null= MY_TEST(keyinfo->key_part[i].null_bit);
j->ref.items[i]=keyuse->val; // Save for cond removal
j->ref.cond_guards[i]= keyuse->cond_guard;
- if (keyuse->null_rejecting)
+ Item *real= (keyuse->val)->real_item();
+ if (keyuse->null_rejecting &&
+ (real->type() == Item::FIELD_ITEM) &&
+ ((Item_field*)real)->field->maybe_null())
j->ref.null_rejecting|= (key_part_map)1 << i;
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
/*
@@ -18516,7 +18532,7 @@ free_tmp_table(THD *thd, TABLE *entry)
DBUG_ASSERT(entry->pos_in_table_list->table == entry);
entry->pos_in_table_list->table= NULL;
}
-
+ entry->null_rejecting_conds= NULL;
free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
thd_proc_info(thd, save_proc_info);
diff --git a/sql/table.cc b/sql/table.cc
index 577ed20a87e..c4e7c3aba09 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4593,6 +4593,7 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
created= TRUE;
cond_selectivity= 1.0;
cond_selectivity_sampling_explain= NULL;
+ null_rejecting_conds= NULL;
#ifdef HAVE_REPLICATION
/* used in RBR Triggers */
master_had_triggers= 0;
diff --git a/sql/table.h b/sql/table.h
index 32e99db880f..9496aef046d 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1354,6 +1354,12 @@ struct TABLE
SplM_opt_info *spl_opt_info;
key_map keys_usable_for_splitting;
+ /*
+ Null rejecting conds added for all tables so we can do range analysis
+ on these conditions
+ */
+ Item* null_rejecting_conds;
+
void init(THD *thd, TABLE_LIST *tl);
bool fill_item_list(List<Item> *item_list) const;
void reset_item_list(List<Item> *item_list, uint skip) const;
1
0
[Commits] 331034cb6e1: MDEV-11975: SQLCOM_PREPARE of EXPLAIN & ANALYZE statement do not return correct metadata info
by Oleksandr Byelkin 20 Apr '18
by Oleksandr Byelkin 20 Apr '18
20 Apr '18
revision-id: 331034cb6e1552e1ab7c12b6f7341f557f76b807 (mariadb-10.3.6-16-g331034cb6e1)
parent(s): cff60be7fe159fdcb2517ce8441610ad512aa7d0
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2018-04-20 14:40:14 +0200
message:
MDEV-11975: SQLCOM_PREPARE of EXPLAIN & ANALYZE statement do not return correct metadata info
Added metadate info after prepare EXPLAIN/ANALYZE.
---
mysql-test/main/mysql_client_test.result | 122 ++++++++++++
mysql-test/main/mysql_client_test.test | 7 +
sql/sql_class.cc | 30 ++-
sql/sql_class.h | 2 +
sql/sql_prepare.cc | 24 ++-
tests/mysql_client_test.c | 323 +++++++++++++++++++++++++++++++
6 files changed, 498 insertions(+), 10 deletions(-)
diff --git a/mysql-test/main/mysql_client_test.result b/mysql-test/main/mysql_client_test.result
index 83ef8d442b3..6f65979517b 100644
--- a/mysql-test/main/mysql_client_test.result
+++ b/mysql-test/main/mysql_client_test.result
@@ -122,5 +122,127 @@ EOF
mysql_stmt_next_result(): 0; field_count: 0
# ------------------------------------
+
+# cat MYSQL_TMP_DIR/test_explain_meta.out.log
+# ------------------------------------
+SELECT number of fields: 1
+EXPALIN number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+EXPALIN INSERT number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON INSERT number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE INSERT number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON INSERT number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+EXPALIN UPDATE number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON UPDATE number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE UPDATE number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON UPDATE number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+EXPALIN DELETE number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON DELETE number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE DELETE number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON DELETE number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+# ------------------------------------
+
SET @@global.general_log= @old_general_log;
SET @@global.slow_query_log= @old_slow_query_log;
diff --git a/mysql-test/main/mysql_client_test.test b/mysql-test/main/mysql_client_test.test
index 260473aa0d0..2c6febd8ffa 100644
--- a/mysql-test/main/mysql_client_test.test
+++ b/mysql-test/main/mysql_client_test.test
@@ -29,5 +29,12 @@ echo ok;
--echo # ------------------------------------
--echo
+--echo
+--echo # cat MYSQL_TMP_DIR/test_explain_meta.out.log
+--echo # ------------------------------------
+--cat_file $MYSQL_TMP_DIR/test_explain_meta.out.log
+--echo # ------------------------------------
+--echo
+
SET @@global.general_log= @old_general_log;
SET @@global.slow_query_log= @old_slow_query_log;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 96485ed15ec..0ec95e11829 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2659,18 +2659,32 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length)
}
-int THD::send_explain_fields(select_result *result, uint8 explain_flags, bool is_analyze)
+void THD::prepare_explain_fields(select_result *result,
+ List<Item> *field_list,
+ uint8 explain_flags,
+ bool is_analyze)
{
- List<Item> field_list;
+ ;
if (lex->explain_json)
- make_explain_json_field_list(field_list, is_analyze);
+ make_explain_json_field_list(*field_list, is_analyze);
else
- make_explain_field_list(field_list, explain_flags, is_analyze);
+ make_explain_field_list(*field_list, explain_flags, is_analyze);
+
+ result->prepare(*field_list, NULL);
+}
- result->prepare(field_list, NULL);
- return (result->send_result_set_metadata(field_list,
- Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF));
+
+int THD::send_explain_fields(select_result *result,
+ uint8 explain_flags,
+ bool is_analyze)
+{
+ List<Item> field_list;
+ int rc;
+ prepare_explain_fields(result, &field_list, explain_flags, is_analyze);
+ rc= result->send_result_set_metadata(field_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF);
+ return(rc);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index a7c33cbc504..67bcb639954 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3738,6 +3738,8 @@ class THD :public Statement,
void add_changed_table(TABLE *table);
void add_changed_table(const char *key, size_t key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, size_t key_length);
+ void prepare_explain_fields(select_result *result, List<Item> *field_list,
+ uint8 explain_flags, bool is_analyze);
int send_explain_fields(select_result *result, uint8 explain_flags,
bool is_analyze);
void make_explain_field_list(List<Item> &field_list, uint8 explain_flags,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 24f3cc66c6b..e37c2006cc6 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2500,8 +2500,28 @@ static bool check_prepared_statement(Prepared_statement *stmt)
break;
}
if (res == 0)
- DBUG_RETURN(stmt->is_sql_prepare() ?
- FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));
+ {
+ if (!stmt->is_sql_prepare())
+ {
+ if (lex->describe || lex->analyze_stmt)
+ {
+ if (!lex->result &&
+ !(lex->result= new (stmt->mem_root) select_send(thd)))
+ DBUG_RETURN(TRUE);
+ List<Item> field_list;
+ thd->prepare_explain_fields(lex->result, &field_list,
+ lex->describe, lex->analyze_stmt);
+ res= send_prep_stmt(stmt, lex->result->field_count(field_list)) ||
+ lex->result->send_result_set_metadata(field_list,
+ Protocol::SEND_EOF);
+ }
+ else
+ res= send_prep_stmt(stmt, 0);
+ if (!res)
+ thd->protocol->flush();
+ }
+ DBUG_RETURN(FALSE);
+ }
error:
DBUG_RETURN(TRUE);
}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index e90453411cb..ae4b2ead99d 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -1056,6 +1056,7 @@ static void test_wl4435_2()
rc= mysql_query(mysql, "DROP PROCEDURE p1");
myquery(rc);
}
+ mct_close_log();
}
@@ -20238,6 +20239,327 @@ static void test_proxy_header()
#endif
+
+static void print_metadata(MYSQL_RES *rs_metadata, int num_fields)
+{
+ MYSQL_FIELD *fields= mysql_fetch_fields(rs_metadata);
+
+ for (int i = 0; i < num_fields; ++i)
+ {
+ mct_log(" - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
+ "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
+ "type: %d; decimals: %d\n",
+ (int) i,
+ (const char *) fields[i].name,
+ (const char *) fields[i].org_name,
+ (const char *) fields[i].table,
+ (const char *) fields[i].org_table,
+ (const char *) fields[i].db,
+ (const char *) fields[i].catalog,
+ (int) fields[i].length,
+ (int) fields[i].max_length,
+ (int) fields[i].type,
+ (int) fields[i].decimals);
+
+ }
+}
+
+static void test_explain_meta()
+{
+ MYSQL_STMT *stmt;
+ int num_fields;
+ char query[MAX_TEST_QUERY_LENGTH];
+ MYSQL_RES *rs_metadata;
+ int rc;
+
+ myheader("test_explain_meta");
+ mct_start_logging("test_explain_meta");
+
+ strmov(query, "SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("SELECT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int)");
+ myquery(rc);
+
+ strmov(query, "EXPLAIN INSERT INTO t1 values (1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "EXPLAIN UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "EXPLAIN DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ mct_close_log();
+}
+
static struct my_tests_st my_tests[]= {
{ "disable_query_logs", disable_query_logs },
{ "test_view_sp_list_fields", test_view_sp_list_fields },
@@ -20524,6 +20846,7 @@ static struct my_tests_st my_tests[]= {
#ifndef EMBEDDED_LIBRARY
{ "test_proxy_header", test_proxy_header},
#endif
+ { "test_explain_meta", test_explain_meta },
{ 0, 0 }
};
1
0
[Commits] 331034cb6e1: MDEV-11975: SQLCOM_PREPARE of EXPLAIN & ANALYZE statement do not return correct metadata info
by Oleksandr Byelkin 20 Apr '18
by Oleksandr Byelkin 20 Apr '18
20 Apr '18
revision-id: 331034cb6e1552e1ab7c12b6f7341f557f76b807 (mariadb-10.3.6-16-g331034cb6e1)
parent(s): cff60be7fe159fdcb2517ce8441610ad512aa7d0
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2018-04-20 14:40:14 +0200
message:
MDEV-11975: SQLCOM_PREPARE of EXPLAIN & ANALYZE statement do not return correct metadata info
Added metadate info after prepare EXPLAIN/ANALYZE.
---
mysql-test/main/mysql_client_test.result | 122 ++++++++++++
mysql-test/main/mysql_client_test.test | 7 +
sql/sql_class.cc | 30 ++-
sql/sql_class.h | 2 +
sql/sql_prepare.cc | 24 ++-
tests/mysql_client_test.c | 323 +++++++++++++++++++++++++++++++
6 files changed, 498 insertions(+), 10 deletions(-)
diff --git a/mysql-test/main/mysql_client_test.result b/mysql-test/main/mysql_client_test.result
index 83ef8d442b3..6f65979517b 100644
--- a/mysql-test/main/mysql_client_test.result
+++ b/mysql-test/main/mysql_client_test.result
@@ -122,5 +122,127 @@ EOF
mysql_stmt_next_result(): 0; field_count: 0
# ------------------------------------
+
+# cat MYSQL_TMP_DIR/test_explain_meta.out.log
+# ------------------------------------
+SELECT number of fields: 1
+EXPALIN number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+EXPALIN INSERT number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON INSERT number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE INSERT number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON INSERT number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+EXPALIN UPDATE number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON UPDATE number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE UPDATE number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON UPDATE number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+EXPALIN DELETE number of fields: 10
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+EXPALIN JSON DELETE number of fields: 1
+ - 0: name: 'EXPLAIN'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+ANALYZE DELETE number of fields: 13
+ - 0: name: 'id'/''; table: ''/''; db: ''; catalog: 'def'; length: 3; max_length: 0; type: 8; decimals: 0
+ - 1: name: 'select_type'/''; table: ''/''; db: ''; catalog: 'def'; length: 57; max_length: 0; type: 253; decimals: 39
+ - 2: name: 'table'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 3: name: 'type'/''; table: ''/''; db: ''; catalog: 'def'; length: 30; max_length: 0; type: 253; decimals: 39
+ - 4: name: 'possible_keys'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 5: name: 'key'/''; table: ''/''; db: ''; catalog: 'def'; length: 192; max_length: 0; type: 253; decimals: 39
+ - 6: name: 'key_len'/''; table: ''/''; db: ''; catalog: 'def'; length: 12288; max_length: 0; type: 253; decimals: 39
+ - 7: name: 'ref'/''; table: ''/''; db: ''; catalog: 'def'; length: 6144; max_length: 0; type: 253; decimals: 39
+ - 8: name: 'rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 10; max_length: 0; type: 8; decimals: 0
+ - 9: name: 'r_rows'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 10
+ - 10: name: 'filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 11: name: 'r_filtered'/''; table: ''/''; db: ''; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2
+ - 12: name: 'Extra'/''; table: ''/''; db: ''; catalog: 'def'; length: 765; max_length: 0; type: 253; decimals: 39
+ANALYZE JSON DELETE number of fields: 1
+ - 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
+# ------------------------------------
+
SET @@global.general_log= @old_general_log;
SET @@global.slow_query_log= @old_slow_query_log;
diff --git a/mysql-test/main/mysql_client_test.test b/mysql-test/main/mysql_client_test.test
index 260473aa0d0..2c6febd8ffa 100644
--- a/mysql-test/main/mysql_client_test.test
+++ b/mysql-test/main/mysql_client_test.test
@@ -29,5 +29,12 @@ echo ok;
--echo # ------------------------------------
--echo
+--echo
+--echo # cat MYSQL_TMP_DIR/test_explain_meta.out.log
+--echo # ------------------------------------
+--cat_file $MYSQL_TMP_DIR/test_explain_meta.out.log
+--echo # ------------------------------------
+--echo
+
SET @@global.general_log= @old_general_log;
SET @@global.slow_query_log= @old_slow_query_log;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 96485ed15ec..0ec95e11829 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2659,18 +2659,32 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length)
}
-int THD::send_explain_fields(select_result *result, uint8 explain_flags, bool is_analyze)
+void THD::prepare_explain_fields(select_result *result,
+ List<Item> *field_list,
+ uint8 explain_flags,
+ bool is_analyze)
{
- List<Item> field_list;
+ ;
if (lex->explain_json)
- make_explain_json_field_list(field_list, is_analyze);
+ make_explain_json_field_list(*field_list, is_analyze);
else
- make_explain_field_list(field_list, explain_flags, is_analyze);
+ make_explain_field_list(*field_list, explain_flags, is_analyze);
+
+ result->prepare(*field_list, NULL);
+}
- result->prepare(field_list, NULL);
- return (result->send_result_set_metadata(field_list,
- Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF));
+
+int THD::send_explain_fields(select_result *result,
+ uint8 explain_flags,
+ bool is_analyze)
+{
+ List<Item> field_list;
+ int rc;
+ prepare_explain_fields(result, &field_list, explain_flags, is_analyze);
+ rc= result->send_result_set_metadata(field_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF);
+ return(rc);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index a7c33cbc504..67bcb639954 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3738,6 +3738,8 @@ class THD :public Statement,
void add_changed_table(TABLE *table);
void add_changed_table(const char *key, size_t key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, size_t key_length);
+ void prepare_explain_fields(select_result *result, List<Item> *field_list,
+ uint8 explain_flags, bool is_analyze);
int send_explain_fields(select_result *result, uint8 explain_flags,
bool is_analyze);
void make_explain_field_list(List<Item> &field_list, uint8 explain_flags,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 24f3cc66c6b..e37c2006cc6 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2500,8 +2500,28 @@ static bool check_prepared_statement(Prepared_statement *stmt)
break;
}
if (res == 0)
- DBUG_RETURN(stmt->is_sql_prepare() ?
- FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));
+ {
+ if (!stmt->is_sql_prepare())
+ {
+ if (lex->describe || lex->analyze_stmt)
+ {
+ if (!lex->result &&
+ !(lex->result= new (stmt->mem_root) select_send(thd)))
+ DBUG_RETURN(TRUE);
+ List<Item> field_list;
+ thd->prepare_explain_fields(lex->result, &field_list,
+ lex->describe, lex->analyze_stmt);
+ res= send_prep_stmt(stmt, lex->result->field_count(field_list)) ||
+ lex->result->send_result_set_metadata(field_list,
+ Protocol::SEND_EOF);
+ }
+ else
+ res= send_prep_stmt(stmt, 0);
+ if (!res)
+ thd->protocol->flush();
+ }
+ DBUG_RETURN(FALSE);
+ }
error:
DBUG_RETURN(TRUE);
}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index e90453411cb..ae4b2ead99d 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -1056,6 +1056,7 @@ static void test_wl4435_2()
rc= mysql_query(mysql, "DROP PROCEDURE p1");
myquery(rc);
}
+ mct_close_log();
}
@@ -20238,6 +20239,327 @@ static void test_proxy_header()
#endif
+
+static void print_metadata(MYSQL_RES *rs_metadata, int num_fields)
+{
+ MYSQL_FIELD *fields= mysql_fetch_fields(rs_metadata);
+
+ for (int i = 0; i < num_fields; ++i)
+ {
+ mct_log(" - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
+ "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
+ "type: %d; decimals: %d\n",
+ (int) i,
+ (const char *) fields[i].name,
+ (const char *) fields[i].org_name,
+ (const char *) fields[i].table,
+ (const char *) fields[i].org_table,
+ (const char *) fields[i].db,
+ (const char *) fields[i].catalog,
+ (int) fields[i].length,
+ (int) fields[i].max_length,
+ (int) fields[i].type,
+ (int) fields[i].decimals);
+
+ }
+}
+
+static void test_explain_meta()
+{
+ MYSQL_STMT *stmt;
+ int num_fields;
+ char query[MAX_TEST_QUERY_LENGTH];
+ MYSQL_RES *rs_metadata;
+ int rc;
+
+ myheader("test_explain_meta");
+ mct_start_logging("test_explain_meta");
+
+ strmov(query, "SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("SELECT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int)");
+ myquery(rc);
+
+ strmov(query, "EXPLAIN INSERT INTO t1 values (1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "EXPLAIN UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "EXPLAIN DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ mct_close_log();
+}
+
static struct my_tests_st my_tests[]= {
{ "disable_query_logs", disable_query_logs },
{ "test_view_sp_list_fields", test_view_sp_list_fields },
@@ -20524,6 +20846,7 @@ static struct my_tests_st my_tests[]= {
#ifndef EMBEDDED_LIBRARY
{ "test_proxy_header", test_proxy_header},
#endif
+ { "test_explain_meta", test_explain_meta },
{ 0, 0 }
};
1
0
revision-id: b810b210d28329354365391135a3c5c465ad5f8b (mariadb-10.3.6-17-gb810b210d28)
parent(s): 346c8ab9533a3de6a4cb348428402ffee2aa8da2
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2018-04-20 14:39:57 +0200
message:
postreview fix
---
mysql-test/main/mysql_client_test.result | 2 +-
mysql-test/main/mysql_client_test.test | 4 +++-
sql/sql_class.cc | 36 ++++++++++++++++++--------------
sql/sql_class.h | 5 +++--
sql/sql_explain.cc | 3 +--
sql/sql_parse.cc | 5 ++---
sql/sql_prepare.cc | 12 +++++++----
sql/sql_prepare.h | 2 --
tests/mysql_client_test.c | 2 ++
9 files changed, 40 insertions(+), 31 deletions(-)
diff --git a/mysql-test/main/mysql_client_test.result b/mysql-test/main/mysql_client_test.result
index 20385acfa00..6f65979517b 100644
--- a/mysql-test/main/mysql_client_test.result
+++ b/mysql-test/main/mysql_client_test.result
@@ -122,6 +122,7 @@ EOF
mysql_stmt_next_result(): 0; field_count: 0
# ------------------------------------
+
# cat MYSQL_TMP_DIR/test_explain_meta.out.log
# ------------------------------------
SELECT number of fields: 1
@@ -243,6 +244,5 @@ ANALYZE JSON DELETE number of fields: 1
- 0: name: 'ANALYZE'/''; table: ''/''; db: ''; catalog: 'def'; length: 234; max_length: 0; type: 253; decimals: 39
# ------------------------------------
-
SET @@global.general_log= @old_general_log;
SET @@global.slow_query_log= @old_slow_query_log;
diff --git a/mysql-test/main/mysql_client_test.test b/mysql-test/main/mysql_client_test.test
index 79ad9f84477..2c6febd8ffa 100644
--- a/mysql-test/main/mysql_client_test.test
+++ b/mysql-test/main/mysql_client_test.test
@@ -27,12 +27,14 @@ echo ok;
--echo # ------------------------------------
--cat_file $MYSQL_TMP_DIR/test_wl4435.out.log
--echo # ------------------------------------
+--echo
+
--echo
--echo # cat MYSQL_TMP_DIR/test_explain_meta.out.log
--echo # ------------------------------------
--cat_file $MYSQL_TMP_DIR/test_explain_meta.out.log
--echo # ------------------------------------
--echo
---echo
+
SET @@global.general_log= @old_general_log;
SET @@global.slow_query_log= @old_slow_query_log;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 349ec576264..0ec95e11829 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -69,7 +69,6 @@
#include "wsrep_thd.h"
#include "sql_connect.h"
#include "my_atomic.h"
-#include "sql_prepare.h"
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
@@ -2660,26 +2659,31 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length)
}
+void THD::prepare_explain_fields(select_result *result,
+ List<Item> *field_list,
+ uint8 explain_flags,
+ bool is_analyze)
+{
+ ;
+ if (lex->explain_json)
+ make_explain_json_field_list(*field_list, is_analyze);
+ else
+ make_explain_field_list(*field_list, explain_flags, is_analyze);
+
+ result->prepare(*field_list, NULL);
+}
+
+
int THD::send_explain_fields(select_result *result,
uint8 explain_flags,
- bool is_analyze,
- Prepared_statement *stmt)
+ bool is_analyze)
{
List<Item> field_list;
int rc;
- if (lex->explain_json)
- make_explain_json_field_list(field_list, is_analyze);
- else
- make_explain_field_list(field_list, explain_flags, is_analyze);
-
- result->prepare(field_list, NULL);
- if (stmt)
- rc= send_prep_stmt(stmt, result->field_count(field_list)) ||
- result->send_result_set_metadata(field_list, Protocol::SEND_EOF);
- else
- rc= result->send_result_set_metadata(field_list,
- Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF);
+ prepare_explain_fields(result, &field_list, explain_flags, is_analyze);
+ rc= result->send_result_set_metadata(field_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF);
return(rc);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 706b72d9d06..67bcb639954 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2174,7 +2174,6 @@ struct QUERY_START_TIME_INFO
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
-class Prepared_statement;
/**
@class THD
For each client connection we create a separate thread with THD serving as
@@ -3739,8 +3738,10 @@ class THD :public Statement,
void add_changed_table(TABLE *table);
void add_changed_table(const char *key, size_t key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, size_t key_length);
+ void prepare_explain_fields(select_result *result, List<Item> *field_list,
+ uint8 explain_flags, bool is_analyze);
int send_explain_fields(select_result *result, uint8 explain_flags,
- bool is_analyze, Prepared_statement *stmt);
+ bool is_analyze);
void make_explain_field_list(List<Item> &field_list, uint8 explain_flags,
bool is_analyze);
void make_explain_json_field_list(List<Item> &field_list, bool is_analyze);
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 96374fc1a33..5d977c6d5c2 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -164,8 +164,7 @@ int Explain_query::send_explain(THD *thd)
LEX *lex= thd->lex;
if (!(result= new (thd->mem_root) select_send(thd)) ||
- thd->send_explain_fields(result, lex->describe, lex->analyze_stmt,
- NULL))
+ thd->send_explain_fields(result, lex->describe, lex->analyze_stmt))
return 1;
int res= 0;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index fa8279d9d6f..2593bac7462 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -6469,9 +6469,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
*/
if (!(result= new (thd->mem_root) select_send(thd)))
return 1; /* purecov: inspected */
- thd->send_explain_fields(result, lex->describe, lex->analyze_stmt,
- NULL);
-
+ thd->send_explain_fields(result, lex->describe, lex->analyze_stmt);
+
/*
This will call optimize() for all parts of query. The query plan is
printed out below.
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index b9016e72410..e37c2006cc6 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -356,7 +356,7 @@ find_prepared_statement(THD *thd, ulong id)
*/
#ifndef EMBEDDED_LIBRARY
-bool send_prep_stmt(Prepared_statement *stmt, uint columns)
+static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
{
NET *net= &stmt->thd->net;
uchar buff[12];
@@ -394,7 +394,7 @@ bool send_prep_stmt(Prepared_statement *stmt, uint columns)
DBUG_RETURN(error);
}
#else
-bool send_prep_stmt(Prepared_statement *stmt,
+static bool send_prep_stmt(Prepared_statement *stmt,
uint columns __attribute__((unused)))
{
THD *thd= stmt->thd;
@@ -2508,8 +2508,12 @@ static bool check_prepared_statement(Prepared_statement *stmt)
if (!lex->result &&
!(lex->result= new (stmt->mem_root) select_send(thd)))
DBUG_RETURN(TRUE);
- res= thd->send_explain_fields(lex->result, lex->describe,
- lex->analyze_stmt, stmt);
+ List<Item> field_list;
+ thd->prepare_explain_fields(lex->result, &field_list,
+ lex->describe, lex->analyze_stmt);
+ res= send_prep_stmt(stmt, lex->result->field_count(field_list)) ||
+ lex->result->send_result_set_metadata(field_list,
+ Protocol::SEND_EOF);
}
else
res= send_prep_stmt(stmt, 0);
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h
index ce6d9df22d0..ca040da341f 100644
--- a/sql/sql_prepare.h
+++ b/sql/sql_prepare.h
@@ -83,8 +83,6 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
void mysqld_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
void reinit_stmt_before_use(THD *thd, LEX *lex);
-class Prepared_statement;
-bool send_prep_stmt(Prepared_statement *stmt, uint columns);
my_bool bulk_parameters_iterations(THD *thd);
my_bool bulk_parameters_set(THD *thd);
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index b2177c660de..ae4b2ead99d 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -20236,8 +20236,10 @@ static void test_proxy_header()
test_proxy_header_localhost();
test_proxy_header_ignore();
}
+
#endif
+
static void print_metadata(MYSQL_RES *rs_metadata, int num_fields)
{
MYSQL_FIELD *fields= mysql_fetch_fields(rs_metadata);
1
0
[Commits] c15b617: MDEV-15902 Assertion `n < m_size' failed, sql_array.h:64:
by IgorBabaev 20 Apr '18
by IgorBabaev 20 Apr '18
20 Apr '18
revision-id: c15b6170bbd59d6763ebba085e34c1ce864427f0 (mariadb-10.3.6-16-gc15b617)
parent(s): cff60be7fe159fdcb2517ce8441610ad512aa7d0
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-04-19 18:40:31 -0700
message:
MDEV-15902 Assertion `n < m_size' failed, sql_array.h:64:
Element_type& Bounds_checked_array<Element_type>::operator[]
(size_t) [with Element_type = Item*; size_t = long unsigned int]
In sql_yacc.yy the semantic actions for the MEDIAN window function
lacked a call of st_select_lex::prepare_add_window_spec().
This function saves the head of the thd->lex->order_list into
lex->save_order_list in order this head to be restored in
st_select_lex::add_window_spec after the specification of the
window function has been parsed.
Without a call of prepare_add_window_spec() when add_window_spec()
was called the head of an empty list was copied into
thd->lex->order_list (instead of assumed saved head this list).
This made the list thd->lex->order_list invalid and potentially
could cause many different problems.
Corrected the result set in the test case for MDEV-15899 that
used the MEDIAN window function and could not be correct
without this fix.
---
mysql-test/main/derived_cond_pushdown.result | 44 ++++++++++++++--------------
mysql-test/main/win_percentile.result | 15 ++++++++++
mysql-test/main/win_percentile.test | 15 ++++++++++
sql/sql_yacc.yy | 3 +-
4 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 1b7aeda..82f621c 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -15130,31 +15130,31 @@ cte as (select median(f2) over (partition by f1) as k1 from t1 order by f1),
cte1 as (select median(f4) over (partition by f1) as k2 from t1)
select k1,k2 from cte1, cte;
k1 k2
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
+1.0000000000 9.0000000000
+1.0000000000 9.0000000000
1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
+1.0000000000 0.0000000000
+1.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
explain with
cte as (select median(f2) over (partition by f1) as k1 from t1 order by f1),
cte1 as (select median(f4) over (partition by f1) as k2 from t1)
@@ -15162,6 +15162,6 @@ select k1,k2 from cte1, cte;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 5
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join)
-3 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
-2 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary
+3 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary
+2 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
drop table t1;
diff --git a/mysql-test/main/win_percentile.result b/mysql-test/main/win_percentile.result
index c51e2e6..d1b205e 100644
--- a/mysql-test/main/win_percentile.result
+++ b/mysql-test/main/win_percentile.result
@@ -324,3 +324,18 @@ median(score) over (partition by name) c
4.0000000000 4.0000000000
4.0000000000 4.0000000000
drop table t1;
+#
+# MDEV-13352: MEDIAN window function over a table with virtual column
+# in select with CTE and ORDER BY
+#
+CREATE TABLE t1 (f1 int ,f2 int ,f3 int, f4 int, v1 int AS (-f4) virtual);
+INSERT INTO t1(f1,f2,f3,f4) VALUES
+(1,10,100,10), (7,11,112,15), (3,14,121,12);
+WITH CTE AS (SELECT MIN(f3) OVER () FROM t1)
+SELECT MEDIAN(f3) OVER () FROM t1
+ORDER BY f1, f2, f3, f4, v1;
+MEDIAN(f3) OVER ()
+112.0000000000
+112.0000000000
+112.0000000000
+DROP TABLE t1;
diff --git a/mysql-test/main/win_percentile.test b/mysql-test/main/win_percentile.test
index 468d8cf..233b21d 100644
--- a/mysql-test/main/win_percentile.test
+++ b/mysql-test/main/win_percentile.test
@@ -102,3 +102,18 @@ select median(score) over (partition by name), percentile_cont(0.8) within grou
select median(score) over (partition by name), percentile_cont(0.9) within group(order by score) over (partition by name) as c from t1;
select median(score) over (partition by name), percentile_cont(1) within group(order by score) over (partition by name) as c from t1;
drop table t1;
+
+--echo #
+--echo # MDEV-13352: MEDIAN window function over a table with virtual column
+--echo # in select with CTE and ORDER BY
+--echo #
+
+CREATE TABLE t1 (f1 int ,f2 int ,f3 int, f4 int, v1 int AS (-f4) virtual);
+INSERT INTO t1(f1,f2,f3,f4) VALUES
+ (1,10,100,10), (7,11,112,15), (3,14,121,12);
+
+WITH CTE AS (SELECT MIN(f3) OVER () FROM t1)
+SELECT MEDIAN(f3) OVER () FROM t1
+ORDER BY f1, f2, f3, f4, v1;
+
+DROP TABLE t1;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d69156c..93704cd 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -11257,10 +11257,11 @@ percentile_function:
{
Item *args= new (thd->mem_root) Item_decimal(thd, "0.5", 3,
thd->charset());
- if (($$ == NULL) || (thd->is_error()))
+ if ((args == NULL) || (thd->is_error()))
{
MYSQL_YYABORT;
}
+ Select->prepare_add_window_spec(thd);
if (add_order_to_list(thd, $3,FALSE)) MYSQL_YYABORT;
$$= new (thd->mem_root) Item_sum_percentile_cont(thd, args);
1
0
[Commits] 6373c85: MDEV-15902 Assertion `n < m_size' failed, sql_array.h:64:
by IgorBabaev 20 Apr '18
by IgorBabaev 20 Apr '18
20 Apr '18
revision-id: 6373c8599004e15a6e23816302de9ca310787111 (mariadb-10.3.6-16-g6373c85)
parent(s): cff60be7fe159fdcb2517ce8441610ad512aa7d0
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-04-19 18:35:03 -0700
message:
MDEV-15902 Assertion `n < m_size' failed, sql_array.h:64:
Element_type& Bounds_checked_array<Element_type>::operator[]
(size_t) [with Element_type = Item*; size_t = long unsigned int]
In sql_yacc.yy the semantic actions for the MEDIAN window function
lacked a call of st_select_lex::prepare_add_window_spec().
This function saves the head of the thd->lex->order_list into
lex->save_order_list in order this head to be restored in
st_select_lex::add_window_spec after the specification of the
window function has been parsed.
Without a call of prepare_add_window_spec() when add_window_spec()
was called the head of an empty list was copied into
thd->lex->order_list (instead of assumed saved head this list).
This made the list thd->lex->order_list invalid and potentially
could cause many different problems.
---
mysql-test/main/derived_cond_pushdown.result | 44 ++++++++++++++--------------
mysql-test/main/win_percentile.result | 15 ++++++++++
mysql-test/main/win_percentile.test | 15 ++++++++++
sql/sql_yacc.yy | 3 +-
4 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 1b7aeda..82f621c 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -15130,31 +15130,31 @@ cte as (select median(f2) over (partition by f1) as k1 from t1 order by f1),
cte1 as (select median(f4) over (partition by f1) as k2 from t1)
select k1,k2 from cte1, cte;
k1 k2
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
+1.0000000000 9.0000000000
+1.0000000000 9.0000000000
1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-1.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
-0.0000000000 8.0000000000
+1.0000000000 0.0000000000
+1.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
+0.0000000000 9.0000000000
0.0000000000 8.0000000000
+0.0000000000 0.0000000000
+0.0000000000 9.0000000000
explain with
cte as (select median(f2) over (partition by f1) as k1 from t1 order by f1),
cte1 as (select median(f4) over (partition by f1) as k2 from t1)
@@ -15162,6 +15162,6 @@ select k1,k2 from cte1, cte;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 5
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join)
-3 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
-2 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary
+3 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary
+2 DERIVED t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
drop table t1;
diff --git a/mysql-test/main/win_percentile.result b/mysql-test/main/win_percentile.result
index c51e2e6..d1b205e 100644
--- a/mysql-test/main/win_percentile.result
+++ b/mysql-test/main/win_percentile.result
@@ -324,3 +324,18 @@ median(score) over (partition by name) c
4.0000000000 4.0000000000
4.0000000000 4.0000000000
drop table t1;
+#
+# MDEV-13352: MEDIAN window function over a table with virtual column
+# in select with CTE and ORDER BY
+#
+CREATE TABLE t1 (f1 int ,f2 int ,f3 int, f4 int, v1 int AS (-f4) virtual);
+INSERT INTO t1(f1,f2,f3,f4) VALUES
+(1,10,100,10), (7,11,112,15), (3,14,121,12);
+WITH CTE AS (SELECT MIN(f3) OVER () FROM t1)
+SELECT MEDIAN(f3) OVER () FROM t1
+ORDER BY f1, f2, f3, f4, v1;
+MEDIAN(f3) OVER ()
+112.0000000000
+112.0000000000
+112.0000000000
+DROP TABLE t1;
diff --git a/mysql-test/main/win_percentile.test b/mysql-test/main/win_percentile.test
index 468d8cf..233b21d 100644
--- a/mysql-test/main/win_percentile.test
+++ b/mysql-test/main/win_percentile.test
@@ -102,3 +102,18 @@ select median(score) over (partition by name), percentile_cont(0.8) within grou
select median(score) over (partition by name), percentile_cont(0.9) within group(order by score) over (partition by name) as c from t1;
select median(score) over (partition by name), percentile_cont(1) within group(order by score) over (partition by name) as c from t1;
drop table t1;
+
+--echo #
+--echo # MDEV-13352: MEDIAN window function over a table with virtual column
+--echo # in select with CTE and ORDER BY
+--echo #
+
+CREATE TABLE t1 (f1 int ,f2 int ,f3 int, f4 int, v1 int AS (-f4) virtual);
+INSERT INTO t1(f1,f2,f3,f4) VALUES
+ (1,10,100,10), (7,11,112,15), (3,14,121,12);
+
+WITH CTE AS (SELECT MIN(f3) OVER () FROM t1)
+SELECT MEDIAN(f3) OVER () FROM t1
+ORDER BY f1, f2, f3, f4, v1;
+
+DROP TABLE t1;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d69156c..93704cd 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -11257,10 +11257,11 @@ percentile_function:
{
Item *args= new (thd->mem_root) Item_decimal(thd, "0.5", 3,
thd->charset());
- if (($$ == NULL) || (thd->is_error()))
+ if ((args == NULL) || (thd->is_error()))
{
MYSQL_YYABORT;
}
+ Select->prepare_add_window_spec(thd);
if (add_order_to_list(thd, $3,FALSE)) MYSQL_YYABORT;
$$= new (thd->mem_root) Item_sum_percentile_cont(thd, args);
1
0
[Commits] 67260fd: MDEV-15130 Assertion `table->s->null_bytes == 0' failed in ...
by sachin 19 Apr '18
by sachin 19 Apr '18
19 Apr '18
revision-id: 67260fd307423b04119bcb4ef1e0bc62fdff4969 (mariadb-10.3.6-22-g67260fd)
parent(s): cd8b8169b6f39450f10f1ba8a16ae68486bc0975
author: Sachin Setiya
committer: Sachin Setiya
timestamp: 2018-04-19 22:57:30 +0530
message:
MDEV-15130 Assertion `table->s->null_bytes == 0' failed in ...
table_setup_timers::read_row_values under 'test_completely_invisible'
Also solves:-
MDEV-15131 Assertion `false' failed in table_setup_actors::read_row_values
upon querying perfschema under 'test_pseudo_invisible'
MDEV-15137 Assertion `f->real_type() == MYSQL_TYPE_VARCHAR' failed in
PFS_engine_table::set_field_varchar_utf8 upon querying perfschema under
'test_completely_invisible'
Performance schema table are created on the fly , when there is query on
perf schema tables, and test_pseudo_invisible debug_dbug flag is on, then
when query calls mysql_prepare_create_table one invisible field is added ,
this crashes the server.
---
mysql-test/main/invisible_field.result | 14 ++++++++++++++
mysql-test/main/invisible_field.test | 21 +++++++++++++++++++++
storage/perfschema/ha_perfschema.cc | 10 ++++++++++
3 files changed, 45 insertions(+)
diff --git a/mysql-test/main/invisible_field.result b/mysql-test/main/invisible_field.result
index 5cea77f..8395789 100644
--- a/mysql-test/main/invisible_field.result
+++ b/mysql-test/main/invisible_field.result
@@ -556,3 +556,17 @@ INSERT INTO t1 (c,t) VALUES ('foo','2000-01-01 00:00:00');
CREATE TRIGGER tr BEFORE INSERT ON t1 FOR EACH ROW SET @a= 1;
INSERT INTO t1 SELECT * FROM t1;
DROP TABLE t1;
+#MDEV-15130
+SET debug_dbug= "+d,test_completely_invisible";
+UPDATE performance_schema.setup_timers SET timer_name = 'CYCLE' WHERE name = 'wait';
+ERROR HY000: Internal error: Don't query performance_schema whentest_completely_invisible is on
+#MDEV-15131
+SET debug_dbug="+d,test_pseudo_invisible";
+SELECT * FROM performance_schema.setup_actors;
+ERROR HY000: Internal error: Don't query performance_schema whentest_pseudo_invisible is on
+#MDEV-15137
+CREATE TABLE t1 (i INT);
+SET debug_dbug= "+d,test_completely_invisible";
+SELECT * FROM performance_schema.events_stages_summary_by_account_by_event_name STRAIGHT_JOIN t1 ON ( i = SUM_TIMER_WAIT );
+ERROR HY000: Internal error: Don't query performance_schema whentest_pseudo_invisible is on
+DROP TABLE t1;
diff --git a/mysql-test/main/invisible_field.test b/mysql-test/main/invisible_field.test
index cfe89d7..53336b8 100644
--- a/mysql-test/main/invisible_field.test
+++ b/mysql-test/main/invisible_field.test
@@ -1,3 +1,5 @@
+--source include/have_perfschema.inc
+
FLUSH STATUS;
create table t1(abc int primary key, xyz int invisible);
SHOW STATUS LIKE 'Feature_invisible_columns';
@@ -246,3 +248,22 @@ CREATE TRIGGER tr BEFORE INSERT ON t1 FOR EACH ROW SET @a= 1;
INSERT INTO t1 SELECT * FROM t1;
# Cleanup
DROP TABLE t1;
+
+--echo #MDEV-15130
+SET debug_dbug= "+d,test_completely_invisible";
+--error ER_INTERNAL_ERROR
+UPDATE performance_schema.setup_timers SET timer_name = 'CYCLE' WHERE name = 'wait';
+
+--echo #MDEV-15131
+SET debug_dbug="+d,test_pseudo_invisible";
+--error ER_INTERNAL_ERROR
+SELECT * FROM performance_schema.setup_actors;
+
+--echo #MDEV-15137
+CREATE TABLE t1 (i INT);
+SET debug_dbug= "+d,test_completely_invisible";
+--error ER_INTERNAL_ERROR
+SELECT * FROM performance_schema.events_stages_summary_by_account_by_event_name STRAIGHT_JOIN t1 ON ( i = SUM_TIMER_WAIT );
+
+# Cleanup
+DROP TABLE t1;
diff --git a/storage/perfschema/ha_perfschema.cc b/storage/perfschema/ha_perfschema.cc
index e8e9581..d5846f2 100644
--- a/storage/perfschema/ha_perfschema.cc
+++ b/storage/perfschema/ha_perfschema.cc
@@ -64,6 +64,16 @@ static int pfs_discover_table(handlerton *hton, THD *thd, TABLE_SHARE *share)
{
const PFS_engine_table_share *pfs_share;
+ DBUG_EXECUTE_IF("test_pseudo_invisible", {
+ my_error(ER_INTERNAL_ERROR, MYF(0), "Don't query performance_schema when"
+ "test_pseudo_invisible is on");
+ return 1;
+ });
+ DBUG_EXECUTE_IF("test_completely_invisible", {
+ my_error(ER_INTERNAL_ERROR, MYF(0), "Don't query performance_schema when"
+ "test_completely_invisible is on");
+ return 1;
+ });
if ((pfs_share= find_table_share(share->db.str, share->table_name.str)))
return share->init_from_sql_statement_string(thd, false,
pfs_share->sql.str,
1
0