summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2014-08-20 20:57:32 +0200
committerSergei Golubchik <serg@mariadb.org>2014-10-10 22:27:39 +0200
commit30ea6ddda00e75c023c270e9e068591e27354834 (patch)
tree3642decbc0cce8c186e0aac749bda5a29a3eca90
parent013f0f6ceca51514a9cbfb599c397e8a4d72934c (diff)
downloadmariadb-git-30ea6ddda00e75c023c270e9e068591e27354834.tar.gz
MDEV-6603 SBR failure upon executing a prepared statement with input placeholder under anonymous block
Normally, Prepared_statement object rewrites the query on execution to replace ?-placeholders with values. The rewritten query may be written to logs (including binlog) or stored in the query cache. But for compound statements, the whole block is prepared and executed, while contained statements are logged individually. So it doesn't make sense to rewrite the original statement block. Instead, we need to rewrite every contained statement. SP is already doing it to replace SP variables with values. Let it rewrite PS parameters too in the same loop.
-rw-r--r--mysql-test/r/compound.result44
-rw-r--r--mysql-test/t/compound.test35
-rw-r--r--sql/sp_head.cc26
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_yacc.yy4
5 files changed, 97 insertions, 14 deletions
diff --git a/mysql-test/r/compound.result b/mysql-test/r/compound.result
index 84c3cdbfc04..3ccb51cc8b0 100644
--- a/mysql-test/r/compound.result
+++ b/mysql-test/r/compound.result
@@ -15,6 +15,7 @@ PREPARE stmt FROM "BEGIN NOT ATOMIC
INSERT INTO t1 VALUES (?);
END";
SET @val = 6|
+reset master|
EXECUTE stmt USING @val|
SELECT * FROM t1|
a
@@ -24,6 +25,17 @@ a
4
5
6
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (4)
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (5)
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (6)
+master-bin.000001 # Query # # COMMIT
PREPARE stmt FROM "BEGIN NOT ATOMIC
DECLARE v_res INT;
SELECT COUNT(*) INTO v_res FROM t1;
@@ -119,3 +131,35 @@ executing: drop table t1
executing: drop table t2
executing: drop table t3
executing: drop table t4
+create table t1 (x int)|
+create function fn(a int) returns int
+begin
+insert t1 values (a+7);
+return a+8;
+end|
+reset master|
+/**/ if fn(9) > 5 then
+select 1;
+end if|
+1
+1
+prepare stmt from "if fn(?) > 6 then
+ begin
+ declare a int;
+ set a=?*2;
+ insert t1 values(a+?);
+ end;
+end if"|
+set @a=1, @b=2, @c=3|
+execute stmt using @a, @b, @c|
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; SELECT "test"."fn"(9)
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; SELECT "test"."fn"(1)
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; insert t1 values( NAME_CONST('a',4)+3)
+master-bin.000001 # Query # # COMMIT
+drop function fn|
+drop table t1|
diff --git a/mysql-test/t/compound.test b/mysql-test/t/compound.test
index 7140fe9ad3f..a2f12d7f3bb 100644
--- a/mysql-test/t/compound.test
+++ b/mysql-test/t/compound.test
@@ -1,6 +1,7 @@
#
# MDEV-5317 Compound statement / anonymous blocks
#
+source include/have_log_bin.inc;
delimiter |;
CREATE TABLE t1 (a INT PRIMARY KEY)|
@@ -18,9 +19,15 @@ PREPARE stmt FROM "BEGIN NOT ATOMIC
INSERT INTO t1 VALUES (?);
END";
SET @val = 6|
+reset master|
EXECUTE stmt USING @val|
SELECT * FROM t1|
+# see how ?-placeholder was replaced with the value
+delimiter ;|
+source include/show_binlog_events.inc;
+delimiter |;
+
PREPARE stmt FROM "BEGIN NOT ATOMIC
DECLARE v_res INT;
SELECT COUNT(*) INTO v_res FROM t1;
@@ -113,3 +120,31 @@ do
execute dt;
end while|
--horizontal_results
+
+# see how ?-placeholder and SP variables are replaced with values
+create table t1 (x int)|
+create function fn(a int) returns int
+begin
+ insert t1 values (a+7);
+ return a+8;
+end|
+reset master|
+/**/ if fn(9) > 5 then
+ select 1;
+end if|
+prepare stmt from "if fn(?) > 6 then
+ begin
+ declare a int;
+ set a=?*2;
+ insert t1 values(a+?);
+ end;
+end if"|
+set @a=1, @b=2, @c=3|
+execute stmt using @a, @b, @c|
+delimiter ;|
+source include/show_binlog_events.inc;
+delimiter |;
+
+drop function fn|
+drop table t1|
+
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 35abe4e51b5..f8320e830a5 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -890,7 +890,8 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
}
-int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
+int cmp_rqp_locations(Rewritable_query_parameter * const *a,
+ Rewritable_query_parameter * const *b)
{
return (int)((*a)->pos_in_query - (*b)->pos_in_query);
}
@@ -996,30 +997,29 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
{
DBUG_ENTER("subst_spvars");
- Dynamic_array<Item_splocal*> sp_vars_uses;
+ Dynamic_array<Rewritable_query_parameter*> rewritables;
char *pbuf;
StringBuffer<512> qbuf;
Copy_query_with_rewrite acc(thd, query_str->str, query_str->length, &qbuf);
- /* Find all instances of Item_splocal used in this statement */
+ /* Find rewritable Items used in this statement */
for (Item *item= instr->free_list; item; item= item->next)
{
- Item_splocal *item_spl= item->get_item_splocal();
- if (item_spl && item_spl->pos_in_query)
- sp_vars_uses.append(item_spl);
+ Rewritable_query_parameter *rqp= item->get_rewritable_query_parameter();
+ if (rqp && rqp->pos_in_query)
+ rewritables.append(rqp);
}
- if (!sp_vars_uses.elements())
+ if (!rewritables.elements())
DBUG_RETURN(FALSE);
- /* Sort SP var refs by their occurences in the query */
- sp_vars_uses.sort(cmp_splocal_locations);
+ rewritables.sort(cmp_rqp_locations);
- thd->query_name_consts= sp_vars_uses.elements();
+ thd->query_name_consts= rewritables.elements();
- for (Item_splocal **splocal= sp_vars_uses.front();
- splocal <= sp_vars_uses.back(); splocal++)
+ for (Rewritable_query_parameter **rqp= rewritables.front();
+ rqp <= rewritables.back(); rqp++)
{
- if (acc.append(*splocal))
+ if (acc.append(*rqp))
DBUG_RETURN(TRUE);
}
if (acc.finalize())
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index c602ec7441a..b1765cdda04 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -3235,6 +3235,8 @@ void Prepared_statement::setup_set_params()
replace_params_with_values|= opt_log || thd->variables.sql_log_slow;
// query cache
replace_params_with_values|= query_cache_is_cacheable_query(lex);
+ // but never for compound statements
+ replace_params_with_values&= lex->sql_command != SQLCOM_COMPOUND;
if (replace_params_with_values)
{
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 828e9e3a367..a7c6a7e04ec 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13333,7 +13333,9 @@ param_marker:
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
MYSQL_YYABORT;
}
- item= new (thd->mem_root) Item_param((uint) (lip->get_tok_start() - thd->query()));
+ const char *query_start= lex->sphead ? lex->sphead->m_tmp_query
+ : thd->query();
+ item= new (thd->mem_root) Item_param(lip->get_tok_start() - query_start);
if (!($$= item) || lex->param_list.push_back(item))
{
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));