diff --git a/mysql-test/include/have_pool_of_threads.inc b/mysql-test/include/have_pool_of_threads.inc index 432c6216804..62460127d88 100644 --- a/mysql-test/include/have_pool_of_threads.inc +++ b/mysql-test/include/have_pool_of_threads.inc @@ -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; diff --git a/mysql-test/include/maybe_pool_of_threads.inc b/mysql-test/include/maybe_pool_of_threads.inc index 51a179b4426..d25e3864333 100644 --- a/mysql-test/include/maybe_pool_of_threads.inc +++ b/mysql-test/include/maybe_pool_of_threads.inc @@ -1 +1,2 @@ # run with and without threadpool +--source include/not_aix.inc diff --git a/mysql-test/main/join_outer_innodb.result b/mysql-test/main/join_outer_innodb.result index 0b34a399d77..09a37a29702 100644 --- a/mysql-test/main/join_outer_innodb.result +++ b/mysql-test/main/join_outer_innodb.result @@ -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; diff --git a/mysql-test/main/join_outer_innodb.test b/mysql-test/main/join_outer_innodb.test index c26cd62fbc7..6b332f3d155 100644 --- a/mysql-test/main/join_outer_innodb.test +++ b/mysql-test/main/join_outer_innodb.test @@ -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; + diff --git a/mysql-test/main/mysqld--defaults-file.test b/mysql-test/main/mysqld--defaults-file.test index 4859cc17b42..9ca427568ef 100644 --- a/mysql-test/main/mysqld--defaults-file.test +++ b/mysql-test/main/mysqld--defaults-file.test @@ -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 diff --git a/mysql-test/main/mysqld--help,aix.rdiff b/mysql-test/main/mysqld--help,aix.rdiff index e69de29bb2d..972f8b1844b 100644 --- a/mysql-test/main/mysqld--help,aix.rdiff +++ b/mysql-test/main/mysqld--help,aix.rdiff @@ -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 diff --git a/mysql-test/suite/innodb/t/innodb_bug53756-master.opt b/mysql-test/suite/innodb/t/innodb_bug53756-master.opt index 425fda95086..590d44a6d12 100644 --- a/mysql-test/suite/innodb/t/innodb_bug53756-master.opt +++ b/mysql-test/suite/innodb/t/innodb_bug53756-master.opt @@ -1 +1 @@ ---skip-stack-trace --skip-core-file +--loose-skip-stack-trace --skip-core-file diff --git a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test index 4afcc0379f0..b06a5069514 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_basic.test @@ -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; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_grant.test b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_grant.test index 71b007bde23..8f5f2491e12 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_grant.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_idle_timeout_grant.test @@ -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; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test index 3d5cd5f5198..392dbd3a145 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_basic.test @@ -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; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_grant.test b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_grant.test index 29a9ac42ea0..299d5ff4f44 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_max_threads_grant.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_max_threads_grant.test @@ -1,5 +1,6 @@ # uint global --source include/not_embedded.inc +--source include/not_aix.inc SET @global=@@global.thread_pool_max_threads; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test index 131fbe98502..1447c83d39c 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_min_threads_basic.test @@ -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; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test index 74f0f5e6ea7..43817febafd 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_basic.test @@ -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; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_grant.test b/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_grant.test index cbffb94c297..f3a96c69e8a 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_grant.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_oversubscribe_grant.test @@ -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; diff --git a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test index 1ab27907535..2d1dc59476c 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_basic.test @@ -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; # diff --git a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_grant.test b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_grant.test index 8c0908fe2c0..ae0ed8a785b 100644 --- a/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_grant.test +++ b/mysql-test/suite/sys_vars/t/thread_pool_stall_limit_grant.test @@ -1,5 +1,6 @@ # uint global --source include/not_embedded.inc +--source include/not_aix.inc SET @global=@@global.thread_pool_stall_limit; diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 8b0f1eed46d..39b4290a161 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -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); diff --git a/sql/protocol.cc b/sql/protocol.cc index c0a5f2327a0..1aea0dae3de 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -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; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 34fa14d4324..0a2db2ea93a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -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)) diff --git a/sql/sql_error.cc b/sql/sql_error.cc index bb47502b3ce..cef9e6cec00 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -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; } diff --git a/sql/sql_error.h b/sql/sql_error.h index a3782048c79..6b0d4d7749c 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -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; } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 24ffad7368a..e258d46f78a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 2f5470db123..e7d02aaeb0d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -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; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5a02218bb4a..08de2565caf 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -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 diff --git a/storage/innobase/buf/buf0dblwr.cc b/storage/innobase/buf/buf0dblwr.cc index 44db9986df6..447fba38323 100644 --- a/storage/innobase/buf/buf0dblwr.cc +++ b/storage/innobase/buf/buf0dblwr.cc @@ -674,8 +674,7 @@ void buf_dblwr_t::flush_buffered_writes_completed(const IORequest &request) static_cast(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); } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 2a91106ba22..65dc7fcae1a 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -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 */ diff --git a/storage/innobase/include/log0log.h b/storage/innobase/include/log0log.h index 89cef8d69ed..0fe7b22fb04 100644 --- a/storage/innobase/include/log0log.h +++ b/storage/innobase/include/log0log.h @@ -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 */ diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc index 5e64007ef84..1c0a617191e 100644 --- a/storage/innobase/log/log0log.cc +++ b/storage/innobase/log/log0log.cc @@ -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) { diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index d7bb3ce886b..76a703b7496 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -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(); diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index bb85e82cbb8..b8f78775624 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -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 deleted; trx->commit(deleted); diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index ce020a6b88d..8692eaa0e89 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -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++; } diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index d0596cded6b..bac9a3b25b5 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -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; } diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 40fa8172e6f..609e058ca52 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -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) { diff --git a/storage/perfschema/pfs.cc b/storage/perfschema/pfs.cc index 1bb712f64cc..3e9198a6b6c 100644 --- a/storage/perfschema/pfs.cc +++ b/storage/perfschema/pfs.cc @@ -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: diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 77ebf0ae3b8..e6c5f4ab654 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -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 },