mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-41238 Bound parameters prevent condition pushdown into a grouped view, while CAST enables it
Item_param::deep_copy() returned NULL, and every rewrite that copies a condition gives up on what it cannot copy. A conjunct holding a prepared statement parameter was therefore left out of: - pushdown into derived tables and views, including UNION branches and conditions on a derived table column whose expression holds a parameter; - pushdown from HAVING to WHERE, for the part extracted from an OR; - pushdown into a materialized IN subquery; - split materialization, which then found no split key; - the UPPER(col) IN (...) rewrite, which has skipped such conjuncts since MDEV-40006. When the derived table groups by an expression with a parameter, the condition was pushed into its HAVING instead of its WHERE. CAST(? AS ...) worked around this only by accident. Before the pushdown, constant propagation can replace a constant expression on one side of = with a literal. A bare parameter already counts as a basic constant, so propagation leaves it as it is. A client whose charset differs from the column's got the pushdown by a similar accident: the charset conversion replaces the parameter with a literal. Item_param::shallow_copy() now copies a parameter that holds a value, NULL included, and deep_copy() calls it, as in other leaf items. Every such copy is made while optimizing during EXECUTE, and the next EXECUTE makes a new one, so a copy always carries the value bound for the EXECUTE that made it. A copy that outlived its EXECUTE would keep an old value and point into a string buffer that the next binding may free, so shallow_copy() asserts that it does not allocate on the statement's arena. The copy is not added to m_clones. m_clones keeps the parameter's copies in CTE definitions updated with each new value for the whole life of the statement, while this copy lives for one execution. An unbound parameter, DEFAULT and IGNORE still have no copy. mysqltest gets a PS_bind_long command. It binds like PS_bind, but sends every value with mysql_stmt_send_long_data() before each PS_execute, so a test can reach a parameter in LONG_DATA_VALUE state.
This commit is contained in:
@@ -342,6 +342,8 @@ struct st_connection
|
||||
size_t name_len;
|
||||
MYSQL_STMT* stmt;
|
||||
MYSQL_BIND *ps_params;
|
||||
/* PS_bind_long: send ps_params as long data before each PS_execute */
|
||||
my_bool ps_long_data;
|
||||
/* Set after send to disallow other queries before reap */
|
||||
my_bool pending;
|
||||
|
||||
@@ -421,6 +423,7 @@ enum enum_commands {
|
||||
Q_OPTIMIZER_TRACE,
|
||||
Q_PS_PREPARE,
|
||||
Q_PS_BIND,
|
||||
Q_PS_BIND_LONG,
|
||||
Q_PS_EXECUTE,
|
||||
Q_PS_CLOSE,
|
||||
Q_UNKNOWN, /* Unknown command. */
|
||||
@@ -546,6 +549,7 @@ const char *command_names[]=
|
||||
"optimizer_trace",
|
||||
"PS_prepare",
|
||||
"PS_bind",
|
||||
"PS_bind_long",
|
||||
"PS_execute",
|
||||
"PS_close",
|
||||
0
|
||||
@@ -11236,7 +11240,8 @@ static void handle_no_active_connection(struct st_command *command,
|
||||
void run_prepare_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings);
|
||||
void run_bind_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings);
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings,
|
||||
my_bool long_data);
|
||||
void run_execute_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
size_t query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings);
|
||||
void run_close_stmt(struct st_connection *cn, struct st_command *command, const char *query,
|
||||
@@ -11280,7 +11285,9 @@ void run_query_normal(struct st_connection *cn, struct st_command *command,
|
||||
goto end;
|
||||
break;
|
||||
case Q_PS_BIND:
|
||||
run_bind_stmt(cn, command, query, query_len, ds, ds_warnings);
|
||||
case Q_PS_BIND_LONG:
|
||||
run_bind_stmt(cn, command, query, query_len, ds, ds_warnings,
|
||||
command->type == Q_PS_BIND_LONG);
|
||||
flags &= ~QUERY_SEND_FLAG;
|
||||
goto end;
|
||||
break;
|
||||
@@ -12085,15 +12092,15 @@ void run_prepare_stmt(struct st_connection *cn, struct st_command *command,
|
||||
query - query string to execute
|
||||
query_len - length query string to execute
|
||||
ds - output buffer where to store result form query
|
||||
long_data - send the values as long data before each execution
|
||||
|
||||
RETURN VALUE
|
||||
error - function will not return
|
||||
*/
|
||||
|
||||
void run_bind_stmt(struct st_connection *cn, struct st_command *command,
|
||||
const char *query, size_t query_len, DYNAMIC_STRING *ds,
|
||||
DYNAMIC_STRING *ds_warnings
|
||||
)
|
||||
const char *query, size_t query_len, DYNAMIC_STRING *ds,
|
||||
DYNAMIC_STRING *ds_warnings, my_bool long_data)
|
||||
{
|
||||
MYSQL_STMT *stmt= cn->stmt;
|
||||
DBUG_ENTER("run_bind_stmt");
|
||||
@@ -12116,6 +12123,8 @@ void run_bind_stmt(struct st_connection *cn, struct st_command *command,
|
||||
stmt->param_count,
|
||||
MYF(MY_WME|MY_FAE));
|
||||
bzero((char *) ps_params, sizeof(MYSQL_BIND) * stmt->param_count);
|
||||
/* Long data can only be a string, so every parameter is bound as one */
|
||||
cn->ps_long_data= long_data;
|
||||
|
||||
int i=0;
|
||||
char *c;
|
||||
@@ -12126,7 +12135,7 @@ void run_bind_stmt(struct st_connection *cn, struct st_command *command,
|
||||
while (p != nullptr)
|
||||
{
|
||||
(void)strtol(p, &c, 10);
|
||||
if (!*c)
|
||||
if (!long_data && !*c)
|
||||
{
|
||||
ps_params[i].buffer_type= MYSQL_TYPE_LONG;
|
||||
l= (long*)my_malloc(PSI_NOT_INSTRUMENTED, sizeof(long),
|
||||
@@ -12138,7 +12147,7 @@ void run_bind_stmt(struct st_connection *cn, struct st_command *command,
|
||||
else
|
||||
{
|
||||
(void)strtod(p, &c);
|
||||
if (!*c)
|
||||
if (!long_data && !*c)
|
||||
{
|
||||
ps_params[i].buffer_type= MYSQL_TYPE_DECIMAL;
|
||||
d= (double*)my_malloc(PSI_NOT_INSTRUMENTED, sizeof(double),
|
||||
@@ -12215,6 +12224,16 @@ void run_execute_stmt(struct st_connection *cn, struct st_command *command,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cn->ps_params && cn->ps_long_data)
|
||||
{
|
||||
for (ulong i= 0; i < stmt->param_count; i++)
|
||||
if (mysql_stmt_send_long_data(stmt, (uint) i,
|
||||
(const char *) cn->ps_params[i].buffer,
|
||||
cn->ps_params[i].buffer_length))
|
||||
die("mysql_stmt_send_long_data() failed': %d %s",
|
||||
mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
|
||||
}
|
||||
|
||||
/*
|
||||
Execute the query
|
||||
*/
|
||||
@@ -13482,6 +13501,7 @@ int main(int argc, char **argv)
|
||||
case Q_REAP:
|
||||
case Q_PS_PREPARE:
|
||||
case Q_PS_BIND:
|
||||
case Q_PS_BIND_LONG:
|
||||
case Q_PS_EXECUTE:
|
||||
case Q_PS_CLOSE:
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4572,3 +4572,306 @@ select * from (select id from t1 union select id from t2) dt1,
|
||||
drop table t1,t2,t3;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41238 Bound parameters prevent condition pushdown into a grouped
|
||||
--echo # view, while CAST enables it
|
||||
--echo #
|
||||
|
||||
create table t0 (x varchar(32) not null, y varchar(32) not null,
|
||||
key ix_xy (x, y));
|
||||
insert into t0 select concat('x', seq mod 10), concat('y', seq mod 5)
|
||||
from seq_1_to_200;
|
||||
create view v1 as select x, y, count(*) as total from t0 group by x, y;
|
||||
|
||||
create table t1 (i int not null, s varchar(32) not null,
|
||||
dc decimal(10,2) not null, d date not null,
|
||||
dtm datetime not null, b int not null);
|
||||
insert into t1 select seq mod 10, concat('s', seq mod 10), (seq mod 10) + 0.5,
|
||||
date'2020-01-01' + interval (seq mod 10) day,
|
||||
timestamp'2020-01-01 00:00:00' + interval (seq mod 10) day,
|
||||
seq
|
||||
from seq_1_to_100;
|
||||
create view vg as select i, s, dc, d, dtm, count(*) as cnt, max(b) as mb
|
||||
from t1 group by i, s, dc, d, dtm;
|
||||
create view vu as select i, s, b from t1 where b <= 50
|
||||
union all
|
||||
select i, s, b from t1 where b > 50;
|
||||
create table t2 (i int not null, b int not null);
|
||||
insert into t2 select seq mod 10, seq from seq_1_to_50;
|
||||
|
||||
--echo # Both conjuncts are pushed into the view, with each execution's values
|
||||
prepare s from 'select * from v1 where x = ? and y = ?';
|
||||
execute s using 'x7', 'y2';
|
||||
execute s using 'x3', 'y3';
|
||||
prepare s from 'explain format=json select * from v1 where x = ? and y = ?';
|
||||
--echo # Check that x = 'x7' and y = 'y2' is pushed down into the materialized view v1
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 'x7', 'y2';
|
||||
|
||||
--echo # One parameter and one literal
|
||||
prepare s from 'select * from v1 where x = ? and y = ''y2''';
|
||||
execute s using 'x7';
|
||||
execute s using 'x2';
|
||||
prepare s from 'explain format=json select * from v1 where x = ? and y = ''y2''';
|
||||
--echo # Check that x = 'x7' and y = 'y2' is pushed down into the materialized view v1
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 'x7';
|
||||
|
||||
--echo # The same with the value sent with mysql_stmt_send_long_data()
|
||||
# The PS_ commands are sent as SQL when these protocols are on
|
||||
--disable_cursor_protocol
|
||||
--disable_ps_protocol
|
||||
--PS_prepare select * from v1 where x = ? and y = 'y2'
|
||||
--PS_bind_long x7
|
||||
--PS_execute
|
||||
--PS_bind_long x2
|
||||
--PS_execute
|
||||
--PS_close
|
||||
--PS_prepare explain format=json select * from v1 where x = ? and y = 'y2'
|
||||
--PS_bind_long x7
|
||||
--echo # Check that x = 'x7' and y = 'y2' is pushed down into the materialized view v1
|
||||
--source include/explain-no-costs.inc
|
||||
--PS_execute
|
||||
--PS_close
|
||||
--enable_ps_protocol
|
||||
--enable_cursor_protocol
|
||||
|
||||
--echo # A parameter inside an OR, a comparison and an IN list
|
||||
prepare s from 'select i, cnt from vg where (i = ? or i = 8) and s <> ''zz''';
|
||||
execute s using 7;
|
||||
execute s using 1;
|
||||
prepare s from
|
||||
'explain format=json select i, cnt from vg where (i = ? or i = 8) and s <> ''zz''';
|
||||
--echo # Check that (i = 7 or i = 8) and s <> 'zz' is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7;
|
||||
prepare s from 'select i, cnt from vg where i > ?';
|
||||
execute s using 7;
|
||||
execute s using 8;
|
||||
prepare s from 'explain format=json select i, cnt from vg where i > ?';
|
||||
--echo # Check that i > 7 is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7;
|
||||
prepare s from 'select i, cnt from vg where s in (?, ?)';
|
||||
execute s using 's7', 's8';
|
||||
execute s using 's1', 's2';
|
||||
prepare s from 'explain format=json select i, cnt from vg where s in (?, ?)';
|
||||
--echo # Check that s in ('s7','s8') is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 's7', 's8';
|
||||
|
||||
--echo # Equalities on columns of each type, and NULL
|
||||
prepare s from 'select i, cnt from vg where i = ?';
|
||||
execute s using 7;
|
||||
execute s using NULL;
|
||||
prepare s from 'explain format=json select i, cnt from vg where i = ?';
|
||||
--echo # Check that i = 7 is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7;
|
||||
--echo # Check that i = NULL is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using NULL;
|
||||
prepare s from 'select i, cnt from vg where dc = ?';
|
||||
execute s using 7.5;
|
||||
execute s using 2.50;
|
||||
prepare s from 'explain format=json select i, cnt from vg where dc = ?';
|
||||
--echo # Check that dc = 7.5 is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7.5;
|
||||
prepare s from 'select i, cnt from vg where d = ?';
|
||||
execute s using date'2020-01-08';
|
||||
execute s using date'2020-01-03';
|
||||
prepare s from 'explain format=json select i, cnt from vg where d = ?';
|
||||
--echo # Check that d = DATE'2020-01-08' is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using date'2020-01-08';
|
||||
prepare s from 'select i, cnt from vg where dtm = ?';
|
||||
execute s using timestamp'2020-01-08 00:00:00';
|
||||
execute s using date'2020-01-03';
|
||||
prepare s from 'explain format=json select i, cnt from vg where dtm = ?';
|
||||
--echo # Check that dtm = TIMESTAMP'2020-01-08 00:00:00' is pushed down into vg
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using timestamp'2020-01-08 00:00:00';
|
||||
|
||||
--echo # A UNION ALL view, and a parameter in one branch's select list
|
||||
prepare s from 'select * from vu where i = ? and b > ?';
|
||||
execute s using 7, 40;
|
||||
execute s using 3, 80;
|
||||
prepare s from 'explain format=json select * from vu where i = ? and b > ?';
|
||||
--echo # Check that i = 7 and b > 40 is pushed down into both branches of vu
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7, 40;
|
||||
prepare s from
|
||||
'select * from (select i, b as bb from t1 where b <= 50
|
||||
union all
|
||||
select i, b + ? from t1 where b > 50) dt
|
||||
where dt.bb > 95';
|
||||
execute s using 0;
|
||||
execute s using 10;
|
||||
prepare s from
|
||||
'explain format=json
|
||||
select * from (select i, b as bb from t1 where b <= 50
|
||||
union all
|
||||
select i, b + ? from t1 where b > 50) dt
|
||||
where dt.bb > 95';
|
||||
--echo # Check that bb > 95 is pushed down into both branches, as b + 0 > 95 into the second
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 0;
|
||||
|
||||
--echo # A derived table that is not grouped, and a parameter in its select list
|
||||
set optimizer_switch='derived_merge=off';
|
||||
prepare s from 'select * from (select i, s, b from t1) dt where dt.i = ? and dt.b > ?';
|
||||
execute s using 7, 90;
|
||||
execute s using 3, 50;
|
||||
prepare s from
|
||||
'explain format=json
|
||||
select * from (select i, s, b from t1) dt where dt.i = ? and dt.b > ?';
|
||||
--echo # Check that i = 7 and b > 90 is pushed down into dt
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7, 90;
|
||||
prepare s from 'select * from (select i, b + ? as bb from t1) dt where dt.bb > 99';
|
||||
execute s using 0;
|
||||
execute s using 5;
|
||||
prepare s from
|
||||
'explain format=json
|
||||
select * from (select i, b + ? as bb from t1) dt where dt.bb > 99';
|
||||
--echo # Check that bb > 99 is pushed down into dt as b + 0 > 99
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 0;
|
||||
set optimizer_switch=default;
|
||||
|
||||
--echo # A parameter in the GROUP BY expression: pushed into WHERE, not HAVING
|
||||
prepare s from
|
||||
'select * from (select i + ? as ii, count(*) as c from t1 group by ii) dt
|
||||
where dt.ii = 5';
|
||||
execute s using 0;
|
||||
execute s using 2;
|
||||
prepare s from
|
||||
'explain format=json
|
||||
select * from (select i + ? as ii, count(*) as c from t1 group by ii) dt
|
||||
where dt.ii = 5';
|
||||
--echo # Check that ii = 5 is pushed down into the WHERE of dt as i + 0 = 5
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 0;
|
||||
|
||||
--echo # HAVING to WHERE: the part of an OR that is extracted
|
||||
prepare s from
|
||||
'select i, max(b) from t1 group by i
|
||||
having (i = ? and max(b) > 0) or (i = 8 and max(b) > 0)';
|
||||
execute s using 7;
|
||||
execute s using 1;
|
||||
prepare s from
|
||||
'explain format=json select i, max(b) from t1 group by i
|
||||
having (i = ? and max(b) > 0) or (i = 8 and max(b) > 0)';
|
||||
--echo # Check that i = 7 or i = 8 is moved from HAVING to WHERE
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7;
|
||||
prepare s from
|
||||
'select i, max(b) from t1 group by i
|
||||
having count(*) > 0 and ((i > ? and max(b) > 0) or (i < 3 and max(b) > 0))';
|
||||
execute s using 7;
|
||||
execute s using 8;
|
||||
prepare s from
|
||||
'explain format=json select i, max(b) from t1 group by i
|
||||
having count(*) > 0 and ((i > ? and max(b) > 0) or (i < 3 and max(b) > 0))';
|
||||
--echo # Check that i > 7 or i < 3 is moved from HAVING to WHERE
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7;
|
||||
|
||||
--echo # Pushdown into a materialized IN subquery
|
||||
prepare s from
|
||||
'select * from t1
|
||||
where (t1.i, t1.b) in (select t2.i, max(t2.b) from t2 group by t2.i)
|
||||
and t1.i = ? and t1.b < ?';
|
||||
execute s using 7, 50;
|
||||
execute s using 3, 50;
|
||||
prepare s from
|
||||
'explain format=json select * from t1
|
||||
where (t1.i, t1.b) in (select t2.i, max(t2.b) from t2 group by t2.i)
|
||||
and t1.i = ? and t1.b < ?';
|
||||
--echo # Check that i = 7 is pushed down into the materialized subquery as t2.i = 7
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7, 50;
|
||||
prepare s from
|
||||
'select * from t1
|
||||
where t1.i in (select t2.i + cast(? as signed) as x from t2 having x >= 0)
|
||||
and t1.i > 7';
|
||||
execute s using 0;
|
||||
execute s using 1;
|
||||
prepare s from
|
||||
'explain format=json select * from t1
|
||||
where t1.i in (select t2.i + cast(? as signed) as x from t2 having x >= 0)
|
||||
and t1.i > 7';
|
||||
--echo # Check that i > 7 is pushed down into the materialized subquery as
|
||||
--echo # t2.i + cast(0 as signed) > 7
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 0;
|
||||
|
||||
--echo # A CTE referenced twice: the second reference has clones of the
|
||||
--echo # parameters, and both references get the copies
|
||||
prepare s from
|
||||
'with c as (select i, max(b) as mb from t1 group by i
|
||||
having (i = ? and max(b) > 0) or (i = 8 and max(b) > 0))
|
||||
select * from c c1 join c c2 on c1.i = c2.i order by c1.i';
|
||||
execute s using 7;
|
||||
execute s using 1;
|
||||
prepare s from
|
||||
'explain format=json
|
||||
with c as (select i, max(b) as mb from t1 group by i
|
||||
having (i = ? and max(b) > 0) or (i = 8 and max(b) > 0))
|
||||
select * from c c1 join c c2 on c1.i = c2.i';
|
||||
--echo # Check that i = 7 or i = 8 is moved to WHERE in both references to c
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 7;
|
||||
prepare s from
|
||||
'with c as (select i + ? as ii, count(*) as cnt from t1 group by ii)
|
||||
select * from c c1 join c c2 on c1.ii = c2.ii where c1.ii = 5';
|
||||
execute s using 0;
|
||||
execute s using 2;
|
||||
prepare s from
|
||||
'explain format=json
|
||||
with c as (select i + ? as ii, count(*) as cnt from t1 group by ii)
|
||||
select * from c c1 join c c2 on c1.ii = c2.ii where c1.ii = 5';
|
||||
--echo # Check that ii = 5 is pushed down into both references to c as i + 0 = 5
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 0;
|
||||
|
||||
drop view v1, vg, vu;
|
||||
drop table t0, t1, t2;
|
||||
|
||||
--echo # Split materialization on an equality with a parameter
|
||||
create table t3 (a int not null, b int not null, key ix_a (a));
|
||||
insert into t3 select seq mod 2000, seq from seq_1_to_20000;
|
||||
create table t4 (k int not null);
|
||||
insert into t4 values (10), (20), (30), (40), (50);
|
||||
analyze table t3, t4 persistent for all;
|
||||
prepare s from
|
||||
'select * from t4, (select a, count(*) as c, max(b) as mb from t3 group by a) dt
|
||||
where dt.a = t4.k + ?';
|
||||
execute s using 0;
|
||||
execute s using 1;
|
||||
prepare s from
|
||||
'explain format=json
|
||||
select * from t4, (select a, count(*) as c, max(b) as mb from t3 group by a) dt
|
||||
where dt.a = t4.k + ?';
|
||||
--echo # Check that dt is split: it is read by ref on key0, and t3 by ref on ix_a
|
||||
--source include/explain-no-costs.inc
|
||||
execute s using 0;
|
||||
drop table t3, t4;
|
||||
|
||||
--echo # The casefold rewrite of MDEV-40006 applies to a parameter list
|
||||
set names utf8mb4;
|
||||
create table t1 (id int, cs varchar(10), key(cs)) collate=utf8mb4_general_ci;
|
||||
insert into t1 (id, cs) values (1, 'AA1AA'),(2, 'BB2BB'),(3, 'CC3CC');
|
||||
prepare s from 'select id, cs from t1 where upper(cs) in (?, ?)';
|
||||
execute s using 'AA1AA', 'BB2BB';
|
||||
execute s using 'aa1aa', 'CC3CC';
|
||||
prepare s from 'explain select id, cs from t1 where upper(cs) in (?, ?)';
|
||||
--echo # Check that the plan reads t1 by range on cs
|
||||
execute s using 'AA1AA', 'BB2BB';
|
||||
deallocate prepare s;
|
||||
drop table t1;
|
||||
set names default;
|
||||
|
||||
--echo # End of 11.4 tests
|
||||
|
||||
@@ -1044,3 +1044,34 @@ SELECT 1 /* doesn't throw error */;
|
||||
#
|
||||
# caught error and exited my_copy()
|
||||
# End of 10.11 tests
|
||||
#
|
||||
# PS_bind_long binds like PS_bind, but sends every value as a string
|
||||
# with mysql_stmt_send_long_data() before each PS_execute
|
||||
#
|
||||
PS_prepare select concat('[', ?, ']') as a, concat('[', ?, ']') as b;
|
||||
# PS_bind: 010 is bound as an integer, no long data is sent
|
||||
PS_bind 010 abc;
|
||||
PS_execute;
|
||||
a b
|
||||
[10] [abc]
|
||||
# long data packets: 0
|
||||
PS_close;
|
||||
PS_prepare select concat('[', ?, ']') as a, concat('[', ?, ']') as b;
|
||||
# PS_bind_long: 010 stays a string, both values are sent as long data
|
||||
# for each of the two executions
|
||||
PS_bind_long 010 abc;
|
||||
PS_execute;
|
||||
a b
|
||||
[010] [abc]
|
||||
PS_execute;
|
||||
a b
|
||||
[010] [abc]
|
||||
# long data packets: 4
|
||||
# A later PS_bind sends no long data
|
||||
PS_bind 7 xyz;
|
||||
PS_execute;
|
||||
a b
|
||||
[7] [xyz]
|
||||
# long data packets: 0
|
||||
PS_close;
|
||||
# End of 11.4 tests
|
||||
|
||||
@@ -3084,6 +3084,47 @@ SELECT 1 /* doesn't throw error */;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
--echo #
|
||||
--echo # PS_bind_long binds like PS_bind, but sends every value as a string
|
||||
--echo # with mysql_stmt_send_long_data() before each PS_execute
|
||||
--echo #
|
||||
|
||||
# The PS_ commands are sent as SQL when these protocols are on
|
||||
--disable_cursor_protocol
|
||||
--disable_ps_protocol
|
||||
--PS_prepare select concat('[', ?, ']') as a, concat('[', ?, ']') as b
|
||||
--echo # PS_bind: 010 is bound as an integer, no long data is sent
|
||||
--let $c0= query_get_value(show session status like 'Com_stmt_send_long_data', Value, 1)
|
||||
--PS_bind 010 abc
|
||||
--PS_execute
|
||||
--let $c1= query_get_value(show session status like 'Com_stmt_send_long_data', Value, 1)
|
||||
--let $d= $($c1 - $c0)
|
||||
--echo # long data packets: $d
|
||||
--PS_close
|
||||
|
||||
# Prepare again: the server cannot take long data for a parameter that the
|
||||
# previous execution bound as an integer
|
||||
--PS_prepare select concat('[', ?, ']') as a, concat('[', ?, ']') as b
|
||||
--echo # PS_bind_long: 010 stays a string, both values are sent as long data
|
||||
--echo # for each of the two executions
|
||||
--PS_bind_long 010 abc
|
||||
--PS_execute
|
||||
--PS_execute
|
||||
--let $c2= query_get_value(show session status like 'Com_stmt_send_long_data', Value, 1)
|
||||
--let $d= $($c2 - $c1)
|
||||
--echo # long data packets: $d
|
||||
--echo # A later PS_bind sends no long data
|
||||
--PS_bind 7 xyz
|
||||
--PS_execute
|
||||
--let $c3= query_get_value(show session status like 'Com_stmt_send_long_data', Value, 1)
|
||||
--let $d= $($c3 - $c2)
|
||||
--echo # long data packets: $d
|
||||
--PS_close
|
||||
--enable_ps_protocol
|
||||
--enable_cursor_protocol
|
||||
|
||||
--echo # End of 11.4 tests
|
||||
|
||||
# Wait till we reached the initial number of concurrent sessions
|
||||
--source include/wait_until_count_sessions.inc
|
||||
|
||||
|
||||
45
sql/item.cc
45
sql/item.cc
@@ -4187,6 +4187,51 @@ Item_param::Item_param(THD *thd, const LEX_CSTRING *name_arg,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Binding sets only the statement's parameters and their m_clones, so the copy
|
||||
keeps its value. Its strings point into the original's buffer without owning
|
||||
it, which is safe because copies are dropped before the next EXECUTE.
|
||||
*/
|
||||
Item_param::Item_param(THD *thd, const Item_param &item):
|
||||
Item_basic_value(item),
|
||||
Settable_routine_parameter(item),
|
||||
Rewritable_query_parameter(item),
|
||||
Type_handler_hybrid_field_type(item),
|
||||
state(item.state),
|
||||
m_empty_string_is_null(item.m_empty_string_is_null),
|
||||
value(item.value),
|
||||
indicator(item.indicator),
|
||||
m_out_param_info(item.m_out_param_info),
|
||||
m_is_settable_routine_parameter(item.m_is_settable_routine_parameter),
|
||||
m_clones(thd->mem_root),
|
||||
m_associated_field(item.m_associated_field),
|
||||
m_default_field(item.m_default_field)
|
||||
{}
|
||||
|
||||
|
||||
Item *Item_param::shallow_copy(THD *thd) const
|
||||
{
|
||||
switch (state) {
|
||||
case NULL_VALUE:
|
||||
case SHORT_DATA_VALUE:
|
||||
case LONG_DATA_VALUE:
|
||||
{
|
||||
/* We can clone only if item has the single query lifetime */
|
||||
DBUG_ASSERT(thd->mem_root != thd->stmt_arena->mem_root);
|
||||
Item *copy= new (thd->mem_root) Item_param(thd, *this);
|
||||
if (likely(copy))
|
||||
copy->register_in(thd);
|
||||
return copy;
|
||||
}
|
||||
case NO_VALUE:
|
||||
case DEFAULT_VALUE:
|
||||
case IGNORE_VALUE:
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/* Add reference to Item_param used in a copy of CTE to its master as a clone */
|
||||
|
||||
bool Item_param::add_as_clone(THD *thd)
|
||||
|
||||
@@ -4297,6 +4297,7 @@ public:
|
||||
|
||||
Item_param(THD *thd, const LEX_CSTRING *name_arg,
|
||||
uint pos_in_query_arg, uint len_in_query_arg);
|
||||
Item_param(THD *thd, const Item_param &item);
|
||||
|
||||
void cleanup() override
|
||||
{
|
||||
@@ -4572,8 +4573,9 @@ private:
|
||||
Field *m_default_field;
|
||||
|
||||
protected:
|
||||
Item *shallow_copy(THD *thd) const override { return nullptr; }
|
||||
Item *deep_copy(THD *thd) const override { return nullptr; }
|
||||
Item *shallow_copy(THD *thd) const override;
|
||||
Item *deep_copy(THD *thd) const override
|
||||
{ return shallow_copy_with_checks(thd); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user