mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
Merge branch '10.6' into 10.11
This commit is contained in:
commit
70afc62750
17 changed files with 594 additions and 20 deletions
|
@ -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 },
|
||||
|
||||
|
|
4
mysql-test/include/not_valgrind_build.inc
Normal file
4
mysql-test/include/not_valgrind_build.inc
Normal file
|
@ -0,0 +1,4 @@
|
|||
if (`select version() like '%valgrind%' || version() like '%asan%'`)
|
||||
{
|
||||
skip Does not run with binaries built with valgrind or asan;
|
||||
}
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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 #
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
15
sql/item.h
15
sql/item.h
|
@ -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); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
Loading…
Add table
Reference in a new issue