Skip to content

Commit 21e7933

Browse files
committed
MDEV-22779: Crash: Prepared Statement with a '?' parameter inside a re-used CTE
When a prepared statement parameter '?' is used in a CTE that is used multiple times, the following happens: - The CTE definition is re-parsed multiple times. - There are multiple Item_param objects referring to the same "?" in the original query. - Prepared_statement::param has a pointer to the first of them, the others are "clones". - When prepared statement parameter gets the value, it should be passed over to clones with param->sync_clones() call. This call is made in insert_params(), etc. It was not made in insert_params_with_log(). This would cause Item_param to not have any value which would confuse the query optimizer. Added the missing call.
1 parent 2cd6afb commit 21e7933

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

sql/sql_prepare.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,8 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
903903

904904
if (param->convert_str_value(thd))
905905
DBUG_RETURN(1); /* out of memory */
906+
907+
param->sync_clones();
906908
}
907909
if (acc.finalize())
908910
DBUG_RETURN(1);

sql/sql_select.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5997,6 +5997,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
59975997
uint n_tables= my_count_bits(map);
59985998
if (n_tables == 1) // Only one table
59995999
{
6000+
DBUG_ASSERT(!(map & PSEUDO_TABLE_BITS)); // Must be a real table
60006001
Table_map_iterator it(map);
60016002
int tablenr= it.next_bit();
60026003
DBUG_ASSERT(tablenr != Table_map_iterator::BITMAP_END);

tests/mysql_client_test.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19843,6 +19843,57 @@ static void test_bulk_replace()
1984319843
}
1984419844
#endif
1984519845

19846+
19847+
static void test_ps_params_in_ctes()
19848+
{
19849+
int rc;
19850+
const char *query;
19851+
MYSQL_BIND ps_params[1];
19852+
int int_data[1];
19853+
MYSQL_STMT *stmt;
19854+
19855+
rc= mysql_query(mysql, "create table t1(a int, b int, key(a))");
19856+
myquery(rc);
19857+
19858+
rc= mysql_query(mysql, "insert into t1 (a) values "
19859+
"(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)");
19860+
myquery(rc);
19861+
19862+
query=
19863+
"explain "
19864+
"with T as "
19865+
"( "
19866+
" select * from t1 where t1.a=? limit 2 "
19867+
") "
19868+
"select * from T as TA, T as TB;";
19869+
19870+
stmt= mysql_stmt_init(mysql);
19871+
check_stmt(stmt);
19872+
19873+
rc= mysql_stmt_prepare(stmt, query, (uint) strlen(query));
19874+
check_execute(stmt, rc);
19875+
19876+
int_data[0]=2;
19877+
19878+
ps_params[0].buffer_type= MYSQL_TYPE_LONG;
19879+
ps_params[0].buffer= (char *) &int_data[0];
19880+
ps_params[0].length= 0;
19881+
ps_params[0].is_null= 0;
19882+
19883+
rc= mysql_stmt_bind_param(stmt, ps_params);
19884+
check_execute(stmt, rc);
19885+
19886+
rc= mysql_stmt_execute(stmt);
19887+
check_execute(stmt, rc);
19888+
19889+
rc= mysql_stmt_store_result(stmt);
19890+
check_execute(stmt, rc);
19891+
19892+
rc= mysql_query(mysql, "drop table t1");
19893+
myquery(rc);
19894+
}
19895+
19896+
1984619897
static struct my_tests_st my_tests[]= {
1984719898
{ "disable_query_logs", disable_query_logs },
1984819899
{ "test_view_sp_list_fields", test_view_sp_list_fields },
@@ -20127,6 +20178,7 @@ static struct my_tests_st my_tests[]= {
2012720178
{ "test_bulk_delete", test_bulk_delete },
2012820179
{ "test_bulk_replace", test_bulk_replace },
2012920180
#endif
20181+
{ "test_ps_params_in_ctes", test_ps_params_in_ctes },
2013020182
{ 0, 0 }
2013120183
};
2013220184

0 commit comments

Comments
 (0)