Merge branch '10.6' into 10.11

This commit is contained in:
Oleksandr Byelkin 2024-08-20 10:00:39 +02:00
commit 70afc62750
17 changed files with 594 additions and 20 deletions

View file

@ -2000,7 +2000,7 @@ struct my_option xb_server_options[] =
0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
{"check-privileges", OPT_XTRA_CHECK_PRIVILEGES, "Check database user "
"privileges fro the backup user",
"privileges for the backup user",
&opt_check_privileges, &opt_check_privileges,
0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },

View file

@ -0,0 +1,4 @@
if (`select version() like '%valgrind%' || version() like '%asan%'`)
{
skip Does not run with binaries built with valgrind or asan;
}

View file

@ -27,4 +27,20 @@ a
DROP TABLE t1;
DROP VIEW v1, v2;
#
# MDEV-34771: Types mismatch when cloning items causes debug assertion
#
CREATE VIEW t AS SELECT 1 AS a;
SELECT * FROM t WHERE a=b'';
a
drop view t;
#
# MDEV-34776: Assertion failure in Item_string::do_build_clone
#
CREATE VIEW v AS SELECT version() AS f;
SELECT * FROM v WHERE f = '10.5.20';
f
drop view v;
#
# End of 10.5 tests
#

View file

@ -30,4 +30,22 @@ SELECT * FROM v2 WHERE a='' AND CASE '' WHEN '' THEN '' ELSE a END='';
DROP TABLE t1;
DROP VIEW v1, v2;
--echo #
--echo # MDEV-34771: Types mismatch when cloning items causes debug assertion
--echo #
CREATE VIEW t AS SELECT 1 AS a;
SELECT * FROM t WHERE a=b'';
drop view t;
--echo #
--echo # MDEV-34776: Assertion failure in Item_string::do_build_clone
--echo #
CREATE VIEW v AS SELECT version() AS f;
SELECT * FROM v WHERE f = '10.5.20';
drop view v;
--echo #
--echo # End of 10.5 tests
--echo #

View file

@ -1,5 +1,5 @@
--source include/not_msan.inc
--source include/not_valgrind.inc
--source include/not_valgrind_build.inc
--echo # MDEV-20699 do not cache SP in SHOW CREATE
--echo # Warmup round, this might allocate some memory for session variable

View file

@ -5942,7 +5942,9 @@ WHERE SLEEP(0.1) OR c < 'p' OR b = ( SELECT MIN(b) FROM t2 );
--enable_ps2_protocol
--echo # The following shows that t2 was indeed scanned with a full scan.
--sorted_result
show table_statistics;
--sorted_result
show index_statistics;
set global userstat=@tmp_mdev410;

View file

@ -4925,6 +4925,9 @@ public:
Item_string_sys(THD *thd, const char *str):
Item_string(thd, str, (uint) strlen(str), system_charset_info)
{ }
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_string_sys>(thd, this); }
Item *do_build_clone(THD *thd) const override { return get_copy(thd); }
};
@ -4939,6 +4942,9 @@ public:
Item_string(thd, str, (uint) strlen(str), &my_charset_latin1,
DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII)
{ }
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_string_ascii>(thd, this); }
Item *do_build_clone(THD *thd) const override { return get_copy(thd); }
};
@ -4975,6 +4981,9 @@ public:
// require fix_fields() to be re-run for every statement.
return mark_unsupported_function(func_name.str, arg, VCOL_TIME_FUNC);
}
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_static_string_func>(thd, this); }
Item *do_build_clone(THD *thd) const override { return get_copy(thd); }
};
@ -4992,6 +5001,9 @@ public:
{
return mark_unsupported_function("safe_string", arg, VCOL_IMPOSSIBLE);
}
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_partition_func_safe_string>(thd, this); }
Item *do_build_clone(THD *thd) const override { return get_copy(thd); }
};
@ -5153,6 +5165,9 @@ class Item_bin_string: public Item_hex_hybrid
public:
Item_bin_string(THD *thd, const char *str, size_t str_length);
void print(String *str, enum_query_type query_type) override;
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_bin_string>(thd, this); }
Item *do_build_clone(THD *thd) const override { return get_copy(thd); }
};

View file

@ -9206,10 +9206,22 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
if (!result && triggers)
{
void *save_bulk_param= thd->bulk_param;
/*
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.
*/
thd->bulk_param= nullptr;
if (triggers->process_triggers(thd, event, TRG_ACTION_BEFORE,
TRUE) ||
not_null_fields_have_null_values(table))
{
thd->bulk_param= save_bulk_param;
return TRUE;
}
thd->bulk_param= save_bulk_param;
/*
Re-calculate virtual fields to cater for cases when base columns are

View file

@ -830,13 +830,17 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete_history);
if (delete_record)
{
void *save_bulk_param= thd->bulk_param;
thd->bulk_param= nullptr;
if (!delete_history && table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE))
{
error= 1;
thd->bulk_param= save_bulk_param;
break;
}
thd->bulk_param= save_bulk_param;
// no LIMIT / OFFSET
if (returning && result->send_data(returning->item_list) < 0)
@ -867,13 +871,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (likely(!error))
{
deleted++;
thd->bulk_param= nullptr;
if (!delete_history && table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE))
{
error= 1;
thd->bulk_param= save_bulk_param;
break;
}
thd->bulk_param= save_bulk_param;
if (!--limit && using_limit)
{
error= -1;

View file

@ -309,14 +309,27 @@ Diagnostics_area::reset_diagnostics_area()
m_message[0]= '\0';
Sql_state_errno::clear();
Sql_user_condition_identity::clear();
m_affected_rows= 0;
m_last_insert_id= 0;
m_statement_warn_count= 0;
if (!is_bulk_op())
{
m_affected_rows= 0;
m_statement_warn_count= 0;
}
#endif
get_warning_info()->clear_error_condition();
set_is_sent(false);
/** Tiny reset in debug mode to see garbage right away */
m_status= DA_EMPTY;
if (!is_bulk_op())
/*
For BULK DML operations (e.g. UPDATE) the data member m_status
has the value DA_OK_BULK. Keep this value in order to handle
m_affected_rows, m_statement_warn_count in correct way. Else,
the number of rows and the number of warnings affected by
the last statement executed as part of a trigger fired by the dml
(e.g. UPDATE statement fires a trigger on AFTER UPDATE) would counts
rows modified by trigger's statement.
*/
m_status= DA_EMPTY;
DBUG_VOID_RETURN;
}

View file

@ -1078,6 +1078,11 @@ public:
return m_affected_rows;
}
void set_message(const char *msg)
{
strmake_buf(m_message, msg);
}
ulonglong last_insert_id() const
{
DBUG_ASSERT(m_status == DA_OK || m_status == DA_OK_BULK);

View file

@ -1053,19 +1053,11 @@ 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++;
@ -1079,7 +1071,6 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
error=1;
break;
}
thd->bulk_param= save_bulk_param;
}
else
{
@ -1372,7 +1363,18 @@ values_loop_end:
*/
if (returning)
result->send_eof();
else
else if (!(thd->in_sub_stmt & SUB_STMT_TRIGGER))
/*
Set the status and the number of affected rows in Diagnostics_area
only in case the INSERT statement is not processed as part of a trigger
invoked by some other DML statement. Else we would result in incorrect
number of affected rows for bulk DML operations, e.g. the UPDATE
statement (called via PS protocol). It would happen since the data
member Diagnostics_area::m_affected_rows modified twice per DML
statement - first time at the end of handling the INSERT statement
invoking by a trigger fired on handling the original DML statement,
and the second time at the end of handling the original DML statement.
*/
my_ok(thd, info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
info.touched : info.updated), id);
@ -1394,7 +1396,18 @@ values_loop_end:
(long) thd->get_stmt_da()->current_statement_warn_count());
if (returning)
result->send_eof();
else
else if (!(thd->in_sub_stmt & SUB_STMT_TRIGGER))
/*
Set the status and the number of affected rows in Diagnostics_area
only in case the INSERT statement is not processed as part of a trigger
invoked by some other DML statement. Else we would result in incorrect
number of affected rows for bulk DML operations, e.g. the UPDATE
statement (called via PS protocol). It would happen since the data
member Diagnostics_area::m_affected_rows modified twice per DML
statement - first time at the end of handling the INSERT statement
invoking by a trigger fired on handling the original DML statement,
and the second time at the end of handling the original DML statement.
*/
::my_ok(thd, info.copied + info.deleted + updated, id, buff);
}
thd->abort_on_warning= 0;

View file

@ -1165,13 +1165,19 @@ error:
rows_inserted++;
}
void *save_bulk_param= thd->bulk_param;
thd->bulk_param= nullptr;
if (table->triggers &&
unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
TRG_ACTION_AFTER, TRUE)))
{
error= 1;
thd->bulk_param= save_bulk_param;
break;
}
thd->bulk_param= save_bulk_param;
if (!--limit && using_limit)
{
@ -1361,6 +1367,20 @@ update_end:
(ulong) thd->get_stmt_da()->current_statement_warn_count());
my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
id, buff);
if (thd->get_stmt_da()->is_bulk_op())
{
/*
Update the diagnostics message sent to a client with number of actual
rows update by the statement. For bulk UPDATE operation it should be
done after returning from my_ok() since the final number of updated
rows be knows on finishing the entire bulk update statement.
*/
my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO),
(ulong) thd->get_stmt_da()->affected_rows(),
(ulong) thd->get_stmt_da()->affected_rows(),
(ulong) thd->get_stmt_da()->current_statement_warn_count());
thd->get_stmt_da()->set_message(buff);
}
DBUG_PRINT("info",("%ld records updated", (long) updated));
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */

View file

@ -807,7 +807,7 @@ bool buf_LRU_free_page(buf_page_t *bpage, bool zip)
break;
case 1:
mysql_mutex_lock(&buf_pool.flush_list_mutex);
if (const lsn_t om = bpage->oldest_modification()) {
if (ut_d(const lsn_t om =) bpage->oldest_modification()) {
ut_ad(om == 1);
buf_pool.delete_from_flush_list(bpage);
}

View file

@ -421,8 +421,7 @@ inline void page_rec_set_n_owned(buf_block_t *block, rec_t *rec, ulint n_owned,
ut_ad(block->page.frame == page_align(rec));
ut_ad(comp == (page_is_comp(block->page.frame) != 0));
if (page_zip_des_t *page_zip= compressed
? buf_block_get_page_zip(block) : nullptr)
if (compressed && is_buf_block_get_page_zip(block))
{
ut_ad(comp);
rec_set_bit_field_1(rec, n_owned, REC_NEW_N_OWNED,

View file

@ -1822,7 +1822,7 @@ trx_print_low(
/*!< in: mem_heap_get_size(trx->lock.lock_heap) */
{
if (const trx_id_t id = trx->id) {
fprintf(f, "TRANSACTION " TRX_ID_FMT, trx->id);
fprintf(f, "TRANSACTION " TRX_ID_FMT, id);
} else {
fprintf(f, "TRANSACTION (%p)", trx);
}

View file

@ -22062,6 +22062,452 @@ static void test_mdev_30159()
myquery(rc);
}
#ifndef EMBEDDED_LIBRARY
/**
Test case for bulk UPDATE against a table with an active AFTER UPDATE
trigger.
*/
static void test_mdev_34718_au()
{
int rc;
MYSQL_STMT *stmt_update;
MYSQL_BIND bind[2];
unsigned int vals[]= { 1, 2, 3};
unsigned int new_vals[]= { 5, 6, 7};
unsigned int vals_array_len= 3;
my_ulonglong row_count;
MYSQL_RES *result;
MYSQL_ROW row;
const char *update_stmt= "UPDATE t1 SET a = ? WHERE a = ?";
const char *update_stmt_state_info;
myheader("test_mdev_34718_au");
/* Set up test's environment */
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, "INSERT INTO t1 VALUES (1), (2), (3)");
myquery(rc);
rc= mysql_query(mysql, "CREATE TRIGGER t1_au AFTER UPDATE ON t1 "
"FOR EACH ROW BEGIN INSERT INTO t2 (a) VALUES (NEW.a); END;");
stmt_update= mysql_stmt_init(mysql);
check_stmt(stmt_update);
rc= mysql_stmt_prepare(stmt_update, update_stmt, strlen(update_stmt));
check_execute(stmt_update, rc);
memset(&bind[0], 0, sizeof(MYSQL_BIND));
memset(&bind[1], 0, sizeof(MYSQL_BIND));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= new_vals;
bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= vals;
/*
Every input positional parameter is bound with array of 3 elements
containing actual values for positional parameters
*/
rc= mysql_stmt_attr_set(stmt_update, STMT_ATTR_ARRAY_SIZE, &vals_array_len);
check_execute(stmt_update, rc);
rc= mysql_stmt_bind_param(stmt_update, bind);
check_execute(stmt_update, rc);
/*
Execution of this prepared statement replaces the table rows (1), (2), (3)
with values (5), (6), (7)
*/
rc= mysql_stmt_execute(stmt_update);
check_execute(stmt_update, rc);
/*
Check that the BULK UPDATE statement affects exactly 3 rows
*/
row_count = mysql_stmt_affected_rows(stmt_update);
DIE_UNLESS(row_count == 3);
update_stmt_state_info= mysql_info(mysql);
/*
Check that information about executed operation is matched with
the expected result
*/
DIE_UNLESS(!strcmp("Rows matched: 3 Changed: 3 Warnings: 0",
update_stmt_state_info));
/*
* Check that the AFTER UPDATE trigger of the table t1 does work correctly
* and inserts the rows (5), (6), (7) 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]) == 5);
row = mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t1") == 0 && atoi(row[1]) == 6);
row = mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t1") == 0 && atoi(row[1]) == 7);
row = mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 5);
row = mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 6);
row = mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 7);
row= mysql_fetch_row(result);
DIE_UNLESS(row == NULL);
mysql_free_result(result);
mysql_stmt_close(stmt_update);
/* Clean up */
rc= mysql_query(mysql, "DROP TABLE t1, t2");
myquery(rc);
}
/**
Test case for bulk UPDATE against a table with an active BEFORE UPDATE
trigger.
*/
static void test_mdev_34718_bu()
{
int rc;
MYSQL_STMT *stmt_update;
MYSQL_BIND bind[2];
unsigned int vals[]= { 1, 2, 3};
unsigned int new_vals[]= { 5, 6, 7};
unsigned int vals_array_len= 3;
my_ulonglong row_count;
MYSQL_RES *result;
MYSQL_ROW row;
const char *update_stmt= "UPDATE t1 SET a = ? WHERE a = ?";
const char *update_stmt_state_info;
myheader("test_mdev_34718_bu");
/* Set up test's environment */
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, "INSERT INTO t1 VALUES (1), (2), (3)");
myquery(rc);
rc= mysql_query(mysql, "CREATE TRIGGER t1_au BEFORE UPDATE ON t1 "
"FOR EACH ROW BEGIN INSERT INTO t2 (a) VALUES (NEW.a); END;");
/* Initialize the prepared statement and set it up for bulk operations */
stmt_update= mysql_stmt_init(mysql);
check_stmt(stmt_update);
rc= mysql_stmt_prepare(stmt_update, update_stmt, strlen(update_stmt));
check_execute(stmt_update, rc);
memset(&bind[0], 0, sizeof(MYSQL_BIND));
memset(&bind[1], 0, sizeof(MYSQL_BIND));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= new_vals;
bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= vals;
/*
Every input positional parameter is bound with array of 3 elements
containing actual values for positional parameters
*/
rc= mysql_stmt_attr_set(stmt_update, STMT_ATTR_ARRAY_SIZE, &vals_array_len);
check_execute(stmt_update, rc);
rc= mysql_stmt_bind_param(stmt_update, bind);
check_execute(stmt_update, rc);
/*
Execution of this prepared statement replaces the table rows (1), (2), (3)
with values (5), (6), (7)
*/
rc= mysql_stmt_execute(stmt_update);
check_execute(stmt_update, rc);
/*
Check that the BULK UPDATE statement affects exactly 3 rows
*/
row_count= mysql_stmt_affected_rows(stmt_update);
DIE_UNLESS(row_count == 3);
update_stmt_state_info= mysql_info(mysql);
/*
Check that information about executed operation is matched with
the expected result
*/
DIE_UNLESS(!strcmp("Rows matched: 3 Changed: 3 Warnings: 0",
update_stmt_state_info));
/*
* Check that the BEFORE UPDATE trigger of the table t1 does work correctly
* and inserts the rows (5), (6), (7) 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]) == 5);
row= mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t1") == 0 && atoi(row[1]) == 6);
row= mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t1") == 0 && atoi(row[1]) == 7);
row= mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 5);
row= mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 6);
row= mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "t2") == 0 && atoi(row[1]) == 7);
row= mysql_fetch_row(result);
DIE_UNLESS(row == NULL);
mysql_free_result(result);
mysql_stmt_close(stmt_update);
/* Clean up */
rc= mysql_query(mysql, "DROP TABLE t1, t2");
myquery(rc);
}
/**
Test case for bulk DELETE against a table with an active BEFORE DELETE
trigger.
*/
static void test_mdev_34718_bd()
{
int rc;
MYSQL_STMT *stmt_delete;
MYSQL_BIND bind[1];
unsigned int vals[]= { 1, 2, 3};
unsigned int vals_array_len= 3;
my_ulonglong row_count;
MYSQL_RES *result;
MYSQL_ROW row;
const char *delete_stmt= "DELETE FROM t1 WHERE a = ?";
myheader("test_mdev_34718_bd");
/* Set up test's environment */
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, "INSERT INTO t1 VALUES (1), (2), (3)");
myquery(rc);
rc= mysql_query(mysql, "CREATE TRIGGER t1_bd BEFORE DELETE ON t1 "
"FOR EACH ROW BEGIN INSERT INTO t2 (a) VALUES (OLD.a); END;");
/* Initialize the prepared statement and set it up for bulk operations */
stmt_delete= mysql_stmt_init(mysql);
check_stmt(stmt_delete);
rc= mysql_stmt_prepare(stmt_delete, delete_stmt, strlen(delete_stmt));
check_execute(stmt_delete, rc);
memset(&bind[0], 0, sizeof(MYSQL_BIND));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= vals;
/*
Input positional parameter is bound with array of 3 elements
containing actual values for the positional parameter
*/
rc= mysql_stmt_attr_set(stmt_delete, STMT_ATTR_ARRAY_SIZE, &vals_array_len);
check_execute(stmt_delete, rc);
rc= mysql_stmt_bind_param(stmt_delete, bind);
check_execute(stmt_delete, rc);
/*
Execution of this prepared statement deletes the rows (1), (2), (3)
from the table t1 and inserts the rows (1), (2), (3) into the table t2
in result of firing the BEFORE DELETE trigger
*/
rc= mysql_stmt_execute(stmt_delete);
check_execute(stmt_delete, rc);
/*
Check that the BULK DELETE statement affects exactly 3 rows
*/
row_count= mysql_stmt_affected_rows(stmt_delete);
DIE_UNLESS(row_count == 3);
/*
* Check that the BEFORE DELETE trigger of the table t1 does work correctly
* and inserts 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], "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_delete);
/* Clean up */
rc= mysql_query(mysql, "DROP TABLE t1, t2");
myquery(rc);
}
/**
Test case for bulk DELETE against a table with an active AFTER DELETE
trigger.
*/
static void test_mdev_34718_ad()
{
int rc;
MYSQL_STMT *stmt_delete;
MYSQL_BIND bind[1];
unsigned int vals[]= { 1, 2, 3};
unsigned int vals_array_len= 3;
my_ulonglong row_count;
MYSQL_RES *result;
MYSQL_ROW row;
const char *delete_stmt= "DELETE FROM t1 WHERE a = ?";
myheader("test_mdev_34718_bd");
/* Set up test's environment */
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, "INSERT INTO t1 VALUES (1), (2), (3)");
myquery(rc);
rc= mysql_query(mysql, "CREATE TRIGGER t1_bd AFTER DELETE ON t1 "
"FOR EACH ROW BEGIN INSERT INTO t2 (a) VALUES (OLD.a); END;");
/* Initialize the prepared statement and set it up for bulk operations */
stmt_delete= mysql_stmt_init(mysql);
check_stmt(stmt_delete);
rc= mysql_stmt_prepare(stmt_delete, delete_stmt, strlen(delete_stmt));
check_execute(stmt_delete, rc);
memset(&bind[0], 0, sizeof(MYSQL_BIND));
bind[0].buffer_type= MYSQL_TYPE_LONG;
bind[0].buffer= vals;
/*
Input positional parameter is bound with array of 3 elements
containing actual values for the positional parameter
*/
rc= mysql_stmt_attr_set(stmt_delete, STMT_ATTR_ARRAY_SIZE, &vals_array_len);
check_execute(stmt_delete, rc);
rc= mysql_stmt_bind_param(stmt_delete, bind);
check_execute(stmt_delete, rc);
/*
Execution of this prepared statement deletes the rows (1), (2), (3)
from the table t1 and inserts the rows (1), (2), (3) into the table t2
in result of firing the BEFORE DELETE trigger
*/
rc= mysql_stmt_execute(stmt_delete);
check_execute(stmt_delete, rc);
/*
Check that the BULK DELETE statement affects exactly 3 rows
*/
row_count= mysql_stmt_affected_rows(stmt_delete);
DIE_UNLESS(row_count == 3);
/*
* Check that the AFTER DELETE trigger of the table t1 does work correctly
* and inserts 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], "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_delete);
/* Clean up */
rc= mysql_query(mysql, "DROP TABLE t1, t2");
myquery(rc);
}
#endif // EMBEDDED_LIBRARY
/*
Check that server_status returned after connecting to server
is consistent with the value of autocommit variable.
@ -22630,6 +23076,10 @@ static struct my_tests_st my_tests[]= {
{ "test_cache_metadata", test_cache_metadata},
#ifndef EMBEDDED_LIBRARY
{ "test_mdev_24411", test_mdev_24411},
{ "test_mdev_34718_bu", test_mdev_34718_bu },
{ "test_mdev_34718_au", test_mdev_34718_au },
{ "test_mdev_34718_bd", test_mdev_34718_bd },
{ "test_mdev_34718_ad", test_mdev_34718_ad },
#endif
{ "test_mdev_10075", test_mdev_10075},
{ 0, 0 }