mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
MDEV-24411: Trigger doesn't work correctly with bulk insert
Executing an INSERT statement in PS mode having positional parameter bound with an array could result in incorrect number of inserted rows in case there is a BEFORE INSERT trigger that executes yet another INSERT statement to put a copy of row being inserted into some table. The reason for incorrect number of inserted rows is that a data structure used for binding positional argument with its actual values is stored in THD (this is thd->bulk_param) and reused on processing every INSERT statement. It leads to consuming actual values bound with top-level INSERT statement by other INSERT statements used by triggers' body. To fix the issue, reset the thd->bulk_param temporary to the value nullptr before invoking triggers and restore its value on finishing its execution.
This commit is contained in:
@ -1021,10 +1021,19 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
|
||||
*/
|
||||
restore_record(table,s->default_values); // Get empty record
|
||||
table->reset_default_fields();
|
||||
/*
|
||||
Reset the sentinel thd->bulk_param in order not to consume the next
|
||||
values of a bound array in case one of statement executed by
|
||||
the trigger's body is INSERT statement.
|
||||
*/
|
||||
void *save_bulk_param= thd->bulk_param;
|
||||
thd->bulk_param= nullptr;
|
||||
|
||||
if (unlikely(fill_record_n_invoke_before_triggers(thd, table, fields,
|
||||
*values, 0,
|
||||
TRG_EVENT_INSERT)))
|
||||
{
|
||||
thd->bulk_param= save_bulk_param;
|
||||
if (values_list.elements != 1 && ! thd->is_error())
|
||||
{
|
||||
info.records++;
|
||||
@ -1038,6 +1047,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
|
||||
error=1;
|
||||
break;
|
||||
}
|
||||
thd->bulk_param= save_bulk_param;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1067,12 +1077,22 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
|
||||
}
|
||||
}
|
||||
table->reset_default_fields();
|
||||
|
||||
/*
|
||||
Reset the sentinel thd->bulk_param in order not to consume the next
|
||||
values of a bound array in case one of statement executed by
|
||||
the trigger's body is INSERT statement.
|
||||
*/
|
||||
void *save_bulk_param= thd->bulk_param;
|
||||
thd->bulk_param= nullptr;
|
||||
|
||||
if (unlikely(fill_record_n_invoke_before_triggers(thd, table,
|
||||
table->
|
||||
field_to_fill(),
|
||||
*values, 0,
|
||||
TRG_EVENT_INSERT)))
|
||||
{
|
||||
thd->bulk_param= save_bulk_param;
|
||||
if (values_list.elements != 1 && ! thd->is_error())
|
||||
{
|
||||
info.records++;
|
||||
@ -1081,6 +1101,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
|
||||
error=1;
|
||||
break;
|
||||
}
|
||||
thd->bulk_param= save_bulk_param;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -21870,6 +21870,103 @@ static void test_mdev19838()
|
||||
rc = mysql_query(mysql, "drop table mdev19838");
|
||||
myquery(rc);
|
||||
}
|
||||
|
||||
static void test_mdev_24411()
|
||||
{
|
||||
int rc;
|
||||
MYSQL_STMT *stmt;
|
||||
MYSQL_BIND bind;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
my_ulonglong row_count;
|
||||
unsigned int vals[] = { 1, 2, 3};
|
||||
unsigned int vals_array_len = 3;
|
||||
const char *insert_stmt= "INSERT INTO t1 VALUES (?)";
|
||||
|
||||
myheader("test_mdev_24411");
|
||||
|
||||
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
|
||||
myquery(rc);
|
||||
|
||||
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2");
|
||||
myquery(rc);
|
||||
|
||||
rc= mysql_query(mysql, "CREATE TABLE t1 (a INT)");
|
||||
myquery(rc);
|
||||
|
||||
rc= mysql_query(mysql, "CREATE TABLE t2 (a INT)");
|
||||
myquery(rc);
|
||||
|
||||
rc= mysql_query(mysql,
|
||||
"CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW "
|
||||
"BEGIN INSERT INTO t2 (a) VALUES (NEW.a); END;");
|
||||
myquery(rc);
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
check_stmt(stmt);
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, insert_stmt, strlen(insert_stmt));
|
||||
check_execute(stmt, rc);
|
||||
|
||||
memset(&bind, 0, sizeof(bind));
|
||||
bind.buffer_type= MYSQL_TYPE_LONG;
|
||||
bind.buffer= vals;
|
||||
|
||||
rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &vals_array_len);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
rc= mysql_stmt_bind_param(stmt, &bind);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
/*
|
||||
It's expected that the INSERT statement adds three rows into
|
||||
the table t1
|
||||
*/
|
||||
row_count = mysql_stmt_affected_rows(stmt);
|
||||
DIE_UNLESS(row_count == 3);
|
||||
|
||||
/*
|
||||
* Check that the BEFORE INSERT trigger of the table t1 does work correct
|
||||
* and inserted the rows (1), (2), (3) into the table t2.
|
||||
*/
|
||||
rc= mysql_query(mysql, "SELECT 't1' tname, a FROM t1 "
|
||||
"UNION SELECT 't2' tname, a FROM t2 ORDER BY tname,a");
|
||||
myquery(rc);
|
||||
|
||||
result= mysql_store_result(mysql);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
DIE_UNLESS(strcmp(row[0], "t1") == 0 && atoi(row[1]) == 1);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
DIE_UNLESS(strcmp(row[0], "t1") == 0 && atoi(row[1]) == 2);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
DIE_UNLESS(strcmp(row[0], "t1") == 0 && atoi(row[1]) == 3);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 1);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 2);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 3);
|
||||
|
||||
row= mysql_fetch_row(result);
|
||||
DIE_UNLESS(row == NULL);
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
mysql_stmt_close(stmt);
|
||||
|
||||
rc= mysql_query(mysql, "DROP TABLE t1, t2");
|
||||
myquery(rc);
|
||||
}
|
||||
|
||||
#endif // EMBEDDED_LIBRARY
|
||||
|
||||
|
||||
@ -22307,6 +22404,9 @@ static struct my_tests_st my_tests[]= {
|
||||
{ "test_mdev20261", test_mdev20261 },
|
||||
{ "test_mdev_30159", test_mdev_30159 },
|
||||
{ "test_connect_autocommit", test_connect_autocommit},
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
{ "test_mdev_24411", test_mdev_24411},
|
||||
#endif
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user