merge 10.5 to 10.6

This commit is contained in:
Vladislav Vaintroub 2021-07-16 22:12:09 +02:00
commit e7f4daf88c
35 changed files with 442 additions and 49 deletions

View file

@ -1,4 +1,5 @@
-- require include/have_pool_of_threads.require
--source include/not_aix.inc
--require include/have_pool_of_threads.require
disable_query_log;
show variables like 'thread_handling';
enable_query_log;

View file

@ -1 +1,2 @@
# run with and without threadpool
--source include/not_aix.inc

View file

@ -496,3 +496,22 @@ natural right outer join t3;
drop table t1,t2,t3;
set optimizer_prune_level=@mdev4270_opl;
set optimizer_search_depth=@mdev4270_osd;
#
# Bug #20939184:INNODB: UNLOCK ROW COULD NOT FIND A 2 MODE LOCK ON THE
# RECORD
#
CREATE TABLE t1 (c1 INT, c2 INT, c3 INT, PRIMARY KEY (c1,c2) ) engine=innodb;
CREATE TABLE t2 (c1 INT, c2 INT, c3 INT, PRIMARY KEY (c1), KEY (c2)) engine=innodb;
INSERT INTO t1 VALUES (1,2,3),(2,3,4),(3,4,5);
INSERT INTO t2 SELECT * FROM t1;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT * FROM t1 LEFT JOIN t2 ON t1.c2=t2.c2 AND t2.c1=1 FOR UPDATE;
c1 c2 c3 c1 c2 c3
1 2 3 1 2 3
2 3 4 NULL NULL NULL
3 4 5 NULL NULL NULL
UPDATE t1 LEFT JOIN t2 ON t1.c1 = t2.c2 AND t2.c1 = 3 SET t1.c3 = RAND()*10;
COMMIT;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DROP TABLE t1,t2;

View file

@ -374,3 +374,20 @@ drop table t1,t2,t3;
set optimizer_prune_level=@mdev4270_opl;
set optimizer_search_depth=@mdev4270_osd;
--echo #
--echo # Bug #20939184:INNODB: UNLOCK ROW COULD NOT FIND A 2 MODE LOCK ON THE
--echo # RECORD
--echo #
CREATE TABLE t1 (c1 INT, c2 INT, c3 INT, PRIMARY KEY (c1,c2) ) engine=innodb;
CREATE TABLE t2 (c1 INT, c2 INT, c3 INT, PRIMARY KEY (c1), KEY (c2)) engine=innodb;
INSERT INTO t1 VALUES (1,2,3),(2,3,4),(3,4,5);
INSERT INTO t2 SELECT * FROM t1;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
#unlocks rows in table t2 where c1 = 1
SELECT * FROM t1 LEFT JOIN t2 ON t1.c2=t2.c2 AND t2.c1=1 FOR UPDATE;
UPDATE t1 LEFT JOIN t2 ON t1.c1 = t2.c2 AND t2.c1 = 3 SET t1.c3 = RAND()*10;
COMMIT;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DROP TABLE t1,t2;

View file

@ -4,6 +4,7 @@
source include/not_embedded.inc;
source include/not_windows.inc;
source include/not_aix.inc;
# All these tests refer to configuration files that do not exist

View file

@ -0,0 +1,76 @@
diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result
index 7b0ce27ead3..38176dcaa86 100644
--- a/mysql-test/main/mysqld--help.result
+++ b/mysql-test/main/mysqld--help.result
@@ -1301,8 +1301,6 @@ The following specify which files/extra groups are read (specified before remain
WHERE clause, or a LIMIT clause, or else they will
aborted. Prevents the common mistake of accidentally
deleting or updating every row in a table.
- --stack-trace Print a symbolic stack trace on failure
- (Defaults to on; use --skip-stack-trace to disable.)
--standard-compliant-cte
Allow only CTEs compliant to SQL standard
(Defaults to on; use --skip-standard-compliant-cte to disable.)
@@ -1367,39 +1365,6 @@ The following specify which files/extra groups are read (specified before remain
--thread-cache-size=#
How many threads we should keep in a cache for reuse.
These are freed after 5 minutes of idle time
- --thread-pool-dedicated-listener
- If set to 1,listener thread will not pick up queries
- --thread-pool-exact-stats
- If set to 1, provides better statistics in
- information_schema threadpool tables
- --thread-pool-idle-timeout=#
- Timeout in seconds for an idle thread in the thread
- pool.Worker thread will be shut down after timeout
- --thread-pool-max-threads=#
- Maximum allowed number of worker threads in the thread
- pool
- --thread-pool-oversubscribe=#
- How many additional active worker threads in a group are
- allowed.
- --thread-pool-prio-kickup-timer=#
- The number of milliseconds before a dequeued low-priority
- statement is moved to the high-priority queue
- --thread-pool-priority=name
- Threadpool priority. High priority connections usually
- start executing earlier than low priority.If priority set
- to 'auto', the the actual priority(low or high) is
- determined based on whether or not connection is inside
- transaction.
- --thread-pool-size=#
- Number of thread groups in the pool. This parameter is
- roughly equivalent to maximum number of concurrently
- executing threads (threads in a waiting state do not
- count as executing).
- --thread-pool-stall-limit=#
- Maximum query execution time in milliseconds,before an
- executing non-yielding thread is considered stalled.If a
- worker thread is stalled, additional worker thread may be
- created to handle remaining clients.
--thread-stack=# The stack size for each thread
--time-format=name The TIME format (ignored)
--tls-version=name TLS protocol version for secure connections.. Any
@@ -1788,7 +1753,6 @@ slow-query-log FALSE
sort-buffer-size 2097152
sql-mode STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
sql-safe-updates FALSE
-stack-trace TRUE
standard-compliant-cte TRUE
stored-program-cache 256
strict-password-validation TRUE
@@ -1807,14 +1771,6 @@ tcp-keepalive-probes 0
tcp-keepalive-time 0
tcp-nodelay TRUE
thread-cache-size 151
-thread-pool-dedicated-listener FALSE
-thread-pool-exact-stats FALSE
-thread-pool-idle-timeout 60
-thread-pool-max-threads 65536
-thread-pool-oversubscribe 3
-thread-pool-prio-kickup-timer 1000
-thread-pool-priority auto
-thread-pool-stall-limit 500
thread-stack 299008
time-format %H:%i:%s
tmp-disk-table-size 18446744073709551615

View file

@ -1 +1 @@
--skip-stack-trace --skip-core-file
--loose-skip-stack-trace --skip-core-file

View file

@ -1,6 +1,7 @@
# uint global
--source include/not_windows.inc
--source include/not_embedded.inc
--source include/not_aix.inc
SET @start_global_value = @@global.thread_pool_idle_timeout;
#

View file

@ -1,5 +1,6 @@
--source include/not_windows.inc
--source include/not_embedded.inc
--source include/not_aix.inc
SET @global=@@global.thread_pool_idle_timeout;

View file

@ -1,5 +1,6 @@
# uint global
--source include/not_embedded.inc
--source include/not_aix.inc
SET @start_global_value = @@global.thread_pool_max_threads;
#

View file

@ -1,5 +1,6 @@
# uint global
--source include/not_embedded.inc
--source include/not_aix.inc
SET @global=@@global.thread_pool_max_threads;

View file

@ -1,6 +1,7 @@
# uint global
--source include/not_embedded.inc
--source include/windows.inc
--source include/not_aix.inc
SET @start_global_value = @@global.thread_pool_min_threads;
#

View file

@ -1,6 +1,7 @@
# uint global
--source include/not_windows.inc
--source include/not_embedded.inc
--source include/not_aix.inc
SET @start_global_value = @@global.thread_pool_oversubscribe;
#

View file

@ -1,5 +1,6 @@
--source include/not_windows.inc
--source include/not_embedded.inc
--source include/not_aix.inc
SET @global=@@global.thread_pool_oversubscribe;

View file

@ -1,5 +1,6 @@
# uint global
--source include/not_embedded.inc
--source include/not_aix.inc
SET @start_global_value = @@global.thread_pool_stall_limit;
#

View file

@ -1,5 +1,6 @@
# uint global
--source include/not_embedded.inc
--source include/not_aix.inc
SET @global=@@global.thread_pool_stall_limit;

View file

@ -179,14 +179,26 @@ my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags)
DBUG_RETURN(0);
}
/**
Allocate and assign new net buffer
@note In case of error the old buffer left
@retval TRUE error
@retval FALSE success
*/
my_bool net_allocate_new_packet(NET *net, void *thd, uint my_flags)
{
uchar *tmp;
DBUG_ENTER("net_allocate_new_packet");
if (!(net->buff=(uchar*) my_malloc(key_memory_NET_buff,
(size_t) net->max_packet +
NET_HEADER_SIZE + COMP_HEADER_SIZE + 1,
MYF(MY_WME | my_flags))))
if (!(tmp= (uchar*) my_malloc(key_memory_NET_buff,
(size_t) net->max_packet +
NET_HEADER_SIZE + COMP_HEADER_SIZE + 1,
MYF(MY_WME | my_flags))))
DBUG_RETURN(1);
net->buff= tmp;
net->buff_end=net->buff+net->max_packet;
net->write_pos=net->read_pos = net->buff;
DBUG_RETURN(0);

View file

@ -597,6 +597,7 @@ void Protocol::end_statement()
thd->get_stmt_da()->get_sqlstate());
break;
case Diagnostics_area::DA_EOF:
case Diagnostics_area::DA_EOF_BULK:
error= send_eof(thd->server_status,
thd->get_stmt_da()->statement_warn_count());
break;

View file

@ -709,7 +709,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
!table->prepare_triggers_for_delete_stmt_or_event())
will_batch= !table->file->start_bulk_delete();
if (returning)
/*
thd->get_stmt_da()->is_set() means first iteration of prepared statement
with array binding operation execution (non optimized so it is not
INSERT)
*/
if (returning && !thd->get_stmt_da()->is_set())
{
if (result->send_result_set_metadata(returning->item_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))

View file

@ -371,7 +371,7 @@ Diagnostics_area::set_eof_status(THD *thd)
{
DBUG_ENTER("set_eof_status");
/* Only allowed to report eof if has not yet reported an error */
DBUG_ASSERT(! is_set());
DBUG_ASSERT(!is_set() || (m_status == DA_EOF_BULK && is_bulk_op()));
/*
In production, refuse to overwrite an error or a custom response
with an EOF packet.
@ -384,11 +384,23 @@ Diagnostics_area::set_eof_status(THD *thd)
number of warnings, since they are not available to the client
anyway.
*/
m_statement_warn_count= (thd->spcont ?
0 :
current_statement_warn_count());
if (m_status == DA_EOF_BULK)
{
if (!thd->spcont)
m_statement_warn_count+= current_statement_warn_count();
}
else
{
if (thd->spcont)
{
m_statement_warn_count= 0;
m_affected_rows= 0;
}
else
m_statement_warn_count= current_statement_warn_count();
m_status= (is_bulk_op() ? DA_EOF_BULK : DA_EOF);
}
m_status= DA_EOF;
DBUG_VOID_RETURN;
}

View file

@ -970,6 +970,8 @@ public:
DA_EOF,
/** Set whenever one calls my_ok() in PS bulk mode. */
DA_OK_BULK,
/** Set whenever one calls my_eof() in PS bulk mode. */
DA_EOF_BULK,
/** Set whenever one calls my_error() or my_message(). */
DA_ERROR,
/** Set in case of a custom response, such as one from COM_STMT_PREPARE. */
@ -1029,8 +1031,11 @@ public:
enum_diagnostics_status status() const { return m_status; }
const char *message() const
{ DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK ||
m_status == DA_OK_BULK); return m_message; }
{
DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK ||
m_status == DA_OK_BULK || m_status == DA_EOF_BULK);
return m_message;
}
uint sql_errno() const
@ -1057,7 +1062,7 @@ public:
uint statement_warn_count() const
{
DBUG_ASSERT(m_status == DA_OK || m_status == DA_OK_BULK ||
m_status == DA_EOF);
m_status == DA_EOF ||m_status == DA_EOF_BULK );
return m_statement_warn_count;
}

View file

@ -710,6 +710,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
Name_resolution_context *context;
Name_resolution_context_state ctx_state;
SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0;
unsigned char *readbuff= NULL;
#ifndef EMBEDDED_LIBRARY
char *query= thd->query();
@ -786,7 +787,25 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list,
/* Prepares LEX::returing_list if it is not empty */
if (returning)
{
result->prepare(returning->item_list, NULL);
if (thd->is_bulk_op())
{
/*
It is RETURNING which needs network buffer to write result set and
it is array binfing which need network buffer to read parameters.
So we allocate yet another network buffer.
The old buffer will be freed at the end of operation.
*/
DBUG_ASSERT(thd->protocol == &thd->protocol_binary);
readbuff= thd->net.buff; // old buffer
if (net_allocate_new_packet(&thd->net, thd, MYF(MY_THREAD_SPECIFIC)))
{
readbuff= NULL; // failure, net_allocate_new_packet keeps old buffer
goto abort;
}
}
}
context= &thd->lex->first_select_lex()->context;
/*
@ -1322,7 +1341,8 @@ values_loop_end:
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
}
if (readbuff)
my_free(readbuff);
DBUG_RETURN(FALSE);
abort:
@ -1336,6 +1356,8 @@ abort:
if (!joins_freed)
free_underlaid_joins(thd, thd->lex->first_select_lex());
thd->abort_on_warning= 0;
if (readbuff)
my_free(readbuff);
DBUG_RETURN(retval);
}

View file

@ -903,6 +903,9 @@ static bool insert_bulk_params(Prepared_statement *stmt,
case STMT_INDICATOR_IGNORE:
param->set_ignore();
break;
default:
DBUG_ASSERT(0);
DBUG_RETURN(1);
}
}
else
@ -4680,6 +4683,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
uchar *packet_end_arg)
{
Reprepare_observer reprepare_observer;
unsigned char *readbuff= NULL;
bool error= 0;
packet= packet_arg;
packet_end= packet_end_arg;
@ -4693,24 +4697,37 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
if (state == Query_arena::STMT_ERROR)
{
my_message(last_errno, last_error, MYF(0));
thd->set_bulk_execution(0);
return TRUE;
goto err;
}
/* Check for non zero parameter count*/
if (param_count == 0)
{
DBUG_PRINT("error", ("Statement with no parameters for bulk execution."));
my_error(ER_UNSUPPORTED_PS, MYF(0));
thd->set_bulk_execution(0);
return TRUE;
goto err;
}
if (!(sql_command_flags[lex->sql_command] & CF_PS_ARRAY_BINDING_SAFE))
{
DBUG_PRINT("error", ("Command is not supported in bulk execution."));
my_error(ER_UNSUPPORTED_PS, MYF(0));
thd->set_bulk_execution(0);
return TRUE;
goto err;
}
/*
Here second buffer for not optimized commands,
optimized commands do it inside thier internal loop.
*/
if (!(sql_command_flags[lex->sql_command] & CF_PS_ARRAY_BINDING_OPTIMIZED) &&
this->lex->has_returning())
{
// Above check can be true for SELECT in future
DBUG_ASSERT(lex->sql_command != SQLCOM_SELECT);
readbuff= thd->net.buff; // old buffer
if (net_allocate_new_packet(&thd->net, thd, MYF(MY_THREAD_SPECIFIC)))
{
readbuff= NULL; // failure, net_allocate_new_packet keeps old buffer
goto err;
}
}
#ifndef EMBEDDED_LIBRARY
@ -4722,9 +4739,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
{
my_error(ER_WRONG_ARGUMENTS, MYF(0),
"mysqld_stmt_bulk_execute");
reset_stmt_params(this);
thd->set_bulk_execution(0);
return true;
goto err;
}
read_types= FALSE;
@ -4741,8 +4756,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query,
{
if (set_bulk_parameters(TRUE))
{
thd->set_bulk_execution(0);
return true;
goto err;
}
}
@ -4806,8 +4820,16 @@ reexecute:
}
reset_stmt_params(this);
thd->set_bulk_execution(0);
if (readbuff)
my_free(readbuff);
return error;
err:
reset_stmt_params(this);
thd->set_bulk_execution(0);
if (readbuff)
my_free(readbuff);
return true;
}

View file

@ -190,6 +190,7 @@ static int join_read_system(JOIN_TAB *tab);
static int join_read_const(JOIN_TAB *tab);
static int join_read_key(JOIN_TAB *tab);
static void join_read_key_unlock_row(st_join_table *tab);
static void join_const_unlock_row(JOIN_TAB *tab);
static int join_read_always_key(JOIN_TAB *tab);
static int join_read_last_key(JOIN_TAB *tab);
static int join_no_more_records(READ_RECORD *info);
@ -11189,8 +11190,12 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
else
j->type=JT_EQ_REF;
j->read_record.unlock_row= (j->type == JT_EQ_REF)?
join_read_key_unlock_row : rr_unlock_row;
if (j->type == JT_EQ_REF)
j->read_record.unlock_row= join_read_key_unlock_row;
else if (j->type == JT_CONST)
j->read_record.unlock_row= join_const_unlock_row;
else
j->read_record.unlock_row= rr_unlock_row;
DBUG_RETURN(0);
}
@ -13455,6 +13460,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
/* Only happens with outer joins */
tab->read_first_record= tab->type == JT_SYSTEM ? join_read_system
: join_read_const;
tab->read_record.unlock_row= join_const_unlock_row;
if (!(table->covering_keys.is_set(tab->ref.key) && !table->no_keyread) &&
(!jcl || jcl > 4) && !tab->ref.is_access_triggered())
push_index_cond(tab, tab->ref.key);
@ -21726,6 +21732,19 @@ join_read_key_unlock_row(st_join_table *tab)
tab->ref.use_count--;
}
/**
Rows from const tables are read once but potentially used
multiple times during execution of a query.
Ensure such rows are never unlocked during query execution.
*/
void
join_const_unlock_row(JOIN_TAB *tab)
{
DBUG_ASSERT(tab->type == JT_CONST);
}
/*
ref access method implementation: "read_first" function
@ -24178,8 +24197,12 @@ check_reverse_order:
else if (select && select->quick)
select->quick->need_sorted_output();
tab->read_record.unlock_row= (tab->type == JT_EQ_REF) ?
join_read_key_unlock_row : rr_unlock_row;
if (tab->type == JT_EQ_REF)
tab->read_record.unlock_row= join_read_key_unlock_row;
else if (tab->type == JT_CONST)
tab->read_record.unlock_row= join_const_unlock_row;
else
tab->read_record.unlock_row= rr_unlock_row;
} // QEP has been modified

View file

@ -674,8 +674,7 @@ void buf_dblwr_t::flush_buffered_writes_completed(const IORequest &request)
static_cast<const byte*>(frame)));
ut_ad(lsn);
ut_ad(lsn >= bpage->oldest_modification());
if (lsn > log_sys.get_flushed_lsn())
log_write_up_to(lsn, true);
log_write_up_to(lsn, true);
e.request.node->space->io(e.request, bpage->physical_offset(), e_size,
frame, bpage);
}

View file

@ -15432,7 +15432,7 @@ ha_innobase::extra(
case HA_EXTRA_END_ALTER_COPY:
m_prebuilt->table->skip_alter_undo = 0;
if (!m_prebuilt->table->is_temporary()) {
log_write_up_to(LSN_MAX, true);
log_buffer_flush_to_disk();
}
break;
default:/* Do nothing */

View file

@ -705,15 +705,6 @@ public:
/** Shut down the redo log subsystem. */
void close();
/** Initiate a write of the log buffer to the file if needed.
@param flush whether to initiate a durable write */
inline void initiate_write(bool flush)
{
const lsn_t lsn= get_lsn();
if (!flush || get_flushed_lsn() < lsn)
log_write_up_to(lsn, flush);
}
};
/** Redo log system */

View file

@ -791,6 +791,7 @@ void log_write_up_to(lsn_t lsn, bool flush_to_disk, bool rotate_key,
{
ut_ad(!srv_read_only_mode);
ut_ad(!rotate_key || flush_to_disk);
ut_ad(lsn != LSN_MAX);
if (recv_no_ibuf_operations)
{

View file

@ -702,7 +702,7 @@ static lsn_t log_reserve_and_open(size_t len)
DEBUG_SYNC_C("log_buf_size_exceeded");
/* Not enough free space, do a write of the log buffer */
log_sys.initiate_write(false);
log_write_up_to(log_sys.get_lsn(), false);
srv_stats.log_waits.inc();

View file

@ -2672,7 +2672,7 @@ rollback:
ALTER TABLE...DISCARD TABLESPACE operation altogether. */
err= row_discard_tablespace(trx, table);
DBUG_EXECUTE_IF("ib_discard_before_commit_crash",
log_write_up_to(LSN_MAX, true); DBUG_SUICIDE(););
log_buffer_flush_to_disk(); DBUG_SUICIDE(););
/* FTS_ tables may be deleted */
std::vector<pfs_os_file_t> deleted;
trx->commit(deleted);

View file

@ -1477,7 +1477,7 @@ static void srv_sync_log_buffer_in_background()
srv_main_thread_op_info = "flushing log";
if (difftime(current_time, srv_last_log_flush_time)
>= srv_flush_log_at_timeout) {
log_sys.initiate_write(true);
log_buffer_flush_to_disk();
srv_last_log_flush_time = current_time;
srv_log_writes_and_flush++;
}

View file

@ -328,7 +328,7 @@ static dberr_t create_log_file(bool create_new_db, lsn_t lsn,
mysql_mutex_unlock(&log_sys.mutex);
log_make_checkpoint();
log_write_up_to(LSN_MAX, true);
log_buffer_flush_to_disk();
return DB_SUCCESS;
}

View file

@ -777,7 +777,7 @@ not_free:
DBUG_EXECUTE_IF("ib_undo_trunc",
ib::info() << "ib_undo_trunc";
log_write_up_to(LSN_MAX, true);
log_buffer_flush_to_disk();
DBUG_SUICIDE(););
for (auto& rseg : trx_sys.rseg_array) {

View file

@ -5466,6 +5466,7 @@ void pfs_end_statement_v1(PSI_statement_locker *locker, void *stmt_da)
switch(da->status())
{
case Diagnostics_area::DA_OK_BULK:
case Diagnostics_area::DA_EOF_BULK:
case Diagnostics_area::DA_EMPTY:
break;
case Diagnostics_area::DA_OK:
@ -5706,6 +5707,7 @@ void pfs_end_statement_v1(PSI_statement_locker *locker, void *stmt_da)
switch (da->status())
{
case Diagnostics_area::DA_OK_BULK:
case Diagnostics_area::DA_EOF_BULK:
case Diagnostics_area::DA_EMPTY:
break;
case Diagnostics_area::DA_OK:

View file

@ -20547,6 +20547,178 @@ static void test_bulk_replace()
rc= mysql_query(mysql, "DROP TABLE t1");
myquery(rc);
}
static void test_bulk_insert_returning()
{
int rc;
MYSQL_STMT *stmt;
MYSQL_BIND bind[2], res_bind[2];
MYSQL_ROW row;
MYSQL_RES *result;
int i,
id[]= {1, 2, 3, 4},
val[]= {1, 1, 1, 1},
count= sizeof(id)/sizeof(id[0]);
unsigned long length[2];
my_bool is_null[2];
my_bool error[2];
int32 res[2];
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
myquery(rc);
rc= mysql_query(mysql,
"CREATE TABLE t1 (id int not null primary key, active int)");
myquery(rc);
stmt= mysql_stmt_init(mysql);
rc= mysql_stmt_prepare(stmt,
"insert into t1 values (?, ?) returning id, active",
-1);
check_execute(stmt, rc);
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (void *)id;
bind[0].buffer_length = 0;
bind[1].buffer_type = MYSQL_TYPE_LONG;
bind[1].buffer = (void *)val;
bind[1].buffer_length = 0;
mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
rc= mysql_stmt_bind_param(stmt, bind);
check_execute(stmt, rc);
rc= mysql_stmt_execute(stmt);
myquery(rc);
memset(res_bind, 0, sizeof(res_bind));
for (i= 0; i < 2; i++)
{
res_bind[i].buffer_type= MYSQL_TYPE_LONG;
res_bind[i].buffer= (char *)&res[i];
res_bind[i].is_null= &is_null[i];
res_bind[i].length= &length[i];
res_bind[i].error= &error[i];
}
rc= mysql_stmt_bind_result(stmt, res_bind);
myquery(rc);
rc= mysql_stmt_store_result(stmt);
myquery(rc);
i= 0;
while (!mysql_stmt_fetch(stmt))
{
i++;
DIE_IF(is_null[0] || is_null[1]);
DIE_IF(res[0] != i);
DIE_IF(res[1] != 1);
}
DIE_IF(i != 4);
mysql_stmt_close(stmt);
rc= mysql_query(mysql, "SELECT id,active FROM t1");
myquery(rc);
result= mysql_store_result(mysql);
mytest(result);
i= 0;
while ((row= mysql_fetch_row(result)))
{
i++;
DIE_IF(atoi(row[0]) != i);
DIE_IF(atoi(row[1]) != 1);
}
DIE_IF(i != 4);
mysql_free_result(result);
rc= mysql_query(mysql, "DROP TABLE t1");
myquery(rc);
}
static void test_bulk_delete_returning()
{
int rc;
MYSQL_STMT *stmt;
MYSQL_BIND bind[2], res_bind[2];
MYSQL_ROW row;
MYSQL_RES *result;
int i,
id[]= {1, 2, 3, 4},
count= sizeof(id)/sizeof(id[0]);
unsigned long length[1];
my_bool is_null[1];
my_bool error[1];
int32 res[1];
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
myquery(rc);
rc= mysql_query(mysql, "CREATE TABLE t1 (id int not null primary key)");
myquery(rc);
rc= mysql_query(mysql, "insert into t1 values (1), (2), (3), (4)");
myquery(rc);
verify_affected_rows(4);
stmt= mysql_stmt_init(mysql);
rc= mysql_stmt_prepare(stmt, "DELETE FROM t1 WHERE id=? RETURNING id", -1);
check_execute(stmt, rc);
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (void *)id;
bind[0].buffer_length = 0;
mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
rc= mysql_stmt_bind_param(stmt, bind);
check_execute(stmt, rc);
rc= mysql_stmt_execute(stmt);
myquery(rc);
memset(res_bind, 0, sizeof(res_bind));
res_bind[0].buffer_type= MYSQL_TYPE_LONG;
res_bind[0].buffer= (char *)&res[0];
res_bind[0].is_null= &is_null[0];
res_bind[0].length= &length[0];
res_bind[0].error= &error[0];
rc= mysql_stmt_bind_result(stmt, res_bind);
myquery(rc);
rc= mysql_stmt_store_result(stmt);
myquery(rc);
i= 0;
while (!mysql_stmt_fetch(stmt))
{
i++;
DIE_IF(is_null[0]);
DIE_IF(res[0] != i);
}
DIE_IF(i != 4);
mysql_stmt_close(stmt);
rc= mysql_query(mysql, "SELECT id FROM t1");
myquery(rc);
result= mysql_store_result(mysql);
mytest(result);
i= 0;
while ((row= mysql_fetch_row(result)))
{
i++;
printf("\nResult (SHOULD NOT BE HERE!!!) %d %s \n", i, row[0]);
}
DIE_IF(i != 0 );
mysql_free_result(result);
rc= mysql_query(mysql, "DROP TABLE t1");
myquery(rc);
}
#endif
@ -21556,6 +21728,8 @@ static struct my_tests_st my_tests[]= {
{ "test_bulk_autoinc", test_bulk_autoinc},
{ "test_bulk_delete", test_bulk_delete },
{ "test_bulk_replace", test_bulk_replace },
{ "test_bulk_insert_returning", test_bulk_insert_returning },
{ "test_bulk_delete_returning", test_bulk_delete_returning },
#endif
{ "test_ps_params_in_ctes", test_ps_params_in_ctes },
{ "test_explain_meta", test_explain_meta },