From 72deed59880bd2a92e3210648de72d9e2e4d9afd Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 19 Mar 2018 08:41:33 +0100 Subject: [PATCH 01/15] Fix mtr to be able to wait for >1 exited mysqld If a mtr test case has started two mysqld processes (replication tests), then kills the first one and kills the second one before starting the first (so at some point there are two mysqlds down), then the ./mtr waiting process bricks and forgets to monitor the "expect" file of the first mysqld, so it never gets started again, even when its contents is changed to "restart". A victim of this deficiency is at least galera.galera_gcache_recover. The fix is to keep a list of all mysqlds we should wait to start, not just one (the last one killed). --- mysql-test/mysql-test-run.pl | 247 ++++++++++++++++++----------------- 1 file changed, 129 insertions(+), 118 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5e6eeb8ede2..5a6f0be5b75 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3961,32 +3961,32 @@ sub run_testcase ($$) { } my $test= $tinfo->{suite}->start_test($tinfo); - # Set only when we have to keep waiting after expectedly died server - my $keep_waiting_proc = 0; + # Set to a list of processes we have to keep waiting (expectedly died servers) + my %keep_waiting_proc = (); my $print_timeout= start_timer($print_freq * 60); while (1) { my $proc; - if ($keep_waiting_proc) + if (scalar(keys(%keep_waiting_proc)) > 0) { # Any other process exited? $proc = My::SafeProcess->check_any(); if ($proc) { mtr_verbose ("Found exited process $proc"); + $keep_waiting_proc{$proc} = 1; } else { - $proc = $keep_waiting_proc; # Also check if timer has expired, if so cancel waiting if ( has_expired($test_timeout) ) { - $keep_waiting_proc = 0; + %keep_waiting_proc = (); } } } - if (! $keep_waiting_proc) + if (scalar(keys(%keep_waiting_proc)) == 0) { if($test_timeout > $print_timeout) { @@ -4003,137 +4003,148 @@ sub run_testcase ($$) { else { $proc= My::SafeProcess->wait_any_timeout($test_timeout); + $keep_waiting_proc{$proc} = 1; } } - # Will be restored if we need to keep waiting - $keep_waiting_proc = 0; - - unless ( defined $proc ) + if (scalar(keys(%keep_waiting_proc)) == 0) { mtr_error("wait_any failed"); } - mtr_verbose("Got $proc"); + mtr_verbose("Got " . join(",", keys(%keep_waiting_proc))); mark_time_used('test'); - # ---------------------------------------------------- - # Was it the test program that exited - # ---------------------------------------------------- - if ($proc eq $test) - { - my $res= $test->exit_status(); - - if (($res == 0 or $res == 62) and $opt_warnings and check_warnings($tinfo) ) + my $expected_exit = 1; + foreach $proc (keys(%keep_waiting_proc)) { + # ---------------------------------------------------- + # Was it the test program that exited + # ---------------------------------------------------- + if ($proc eq $test) { - # If test case suceeded, but it has produced unexpected - # warnings, continue with $res == 1; - # but if the test was skipped, it should remain skipped - $res= 1 if $res == 0; - resfile_output($tinfo->{'warnings'}) if $opt_resfile; - } + my $res= $test->exit_status(); - if ( $res == 0 ) - { - my $check_res; - if ( $opt_check_testcases and - $check_res= check_testcase($tinfo, "after")) - { - if ($check_res == 1) { - # Test case had sideeffects, not fatal error, just continue - if ($opt_warnings) { - # Checking error logs for warnings, so need to stop server - # gracefully so that memory leaks etc. can be properly detected. - stop_servers(reverse all_servers()); - check_warnings_post_shutdown($server_socket); - # Even if we got warnings here, we should not fail this - # particular test, as the warnings may be caused by an earlier - # test. - } else { - # Not checking warnings, so can do a hard shutdown. - stop_all_servers($opt_shutdown_timeout); + if (($res == 0 or $res == 62) and $opt_warnings and check_warnings($tinfo) ) + { + # If test case suceeded, but it has produced unexpected + # warnings, continue with $res == 1; + # but if the test was skipped, it should remain skipped + $res= 1 if $res == 0; + resfile_output($tinfo->{'warnings'}) if $opt_resfile; + } + + if ( $res == 0 ) + { + my $check_res; + if ( $opt_check_testcases and + $check_res= check_testcase($tinfo, "after")) + { + if ($check_res == 1) { + # Test case had sideeffects, not fatal error, just continue + if ($opt_warnings) { + # Checking error logs for warnings, so need to stop server + # gracefully so that memory leaks etc. can be properly detected. + stop_servers(reverse all_servers()); + check_warnings_post_shutdown($server_socket); + # Even if we got warnings here, we should not fail this + # particular test, as the warnings may be caused by an earlier + # test. + } else { + # Not checking warnings, so can do a hard shutdown. + stop_all_servers($opt_shutdown_timeout); + } + mtr_report("Resuming tests...\n"); + resfile_output($tinfo->{'check'}) if $opt_resfile; } - mtr_report("Resuming tests...\n"); - resfile_output($tinfo->{'check'}) if $opt_resfile; - } - else { - # Test case check failed fatally, probably a server crashed - report_failure_and_restart($tinfo); - return 1; - } - } - mtr_report_test_passed($tinfo); - } - elsif ( $res == 62 ) - { - # Testcase itself tell us to skip this one - $tinfo->{skip_detected_by_test}= 1; - # Try to get reason from test log file - find_testcase_skipped_reason($tinfo); - mtr_report_test_skipped($tinfo); - # Restart if skipped due to missing perl, it may have had side effects - if ( $tinfo->{'comment'} =~ /^perl not found/ ) - { - stop_all_servers($opt_shutdown_timeout); - } - } - elsif ( $res == 65 ) - { - # Testprogram killed by signal - $tinfo->{comment}= - "testprogram crashed(returned code $res)"; - report_failure_and_restart($tinfo); - } - elsif ( $res == 1 ) - { - # Check if the test tool requests that - # an analyze script should be run - my $analyze= find_analyze_request(); - if ($analyze){ - run_on_all($tinfo, "analyze-$analyze"); - } + else { + # Test case check failed fatally, probably a server crashed + report_failure_and_restart($tinfo); + return 1; + } + } + mtr_report_test_passed($tinfo); + } + elsif ( $res == 62 ) + { + # Testcase itself tell us to skip this one + $tinfo->{skip_detected_by_test}= 1; + # Try to get reason from test log file + find_testcase_skipped_reason($tinfo); + mtr_report_test_skipped($tinfo); + # Restart if skipped due to missing perl, it may have had side effects + if ( $tinfo->{'comment'} =~ /^perl not found/ ) + { + stop_all_servers($opt_shutdown_timeout); + } + } + elsif ( $res == 65 ) + { + # Testprogram killed by signal + $tinfo->{comment}= + "testprogram crashed(returned code $res)"; + report_failure_and_restart($tinfo); + } + elsif ( $res == 1 ) + { + # Check if the test tool requests that + # an analyze script should be run + my $analyze= find_analyze_request(); + if ($analyze){ + run_on_all($tinfo, "analyze-$analyze"); + } - # Wait a bit and see if a server died, if so report that instead - mtr_milli_sleep(100); - my $srvproc= My::SafeProcess::check_any(); - if ($srvproc && grep($srvproc eq $_, started(all_servers()))) { - $proc= $srvproc; - goto SRVDIED; - } + # Wait a bit and see if a server died, if so report that instead + mtr_milli_sleep(100); + my $srvproc= My::SafeProcess::check_any(); + if ($srvproc && grep($srvproc eq $_, started(all_servers()))) { + $proc= $srvproc; + goto SRVDIED; + } + + # Test case failure reported by mysqltest + report_failure_and_restart($tinfo); + } + else + { + # mysqltest failed, probably crashed + $tinfo->{comment}= + "mysqltest failed with unexpected return code $res\n"; + report_failure_and_restart($tinfo); + } + + # Save info from this testcase run to mysqltest.log + if( -f $path_current_testlog) + { + if ($opt_resfile && $res && $res != 62) { + resfile_output_file($path_current_testlog); + } + mtr_appendfile_to_file($path_current_testlog, $path_testlog); + unlink($path_current_testlog); + } + + return ($res == 62) ? 0 : $res; - # Test case failure reported by mysqltest - report_failure_and_restart($tinfo); - } - else - { - # mysqltest failed, probably crashed - $tinfo->{comment}= - "mysqltest failed with unexpected return code $res\n"; - report_failure_and_restart($tinfo); } - # Save info from this testcase run to mysqltest.log - if( -f $path_current_testlog) + # ---------------------------------------------------- + # Check if it was an expected crash + # ---------------------------------------------------- + my $check_crash = check_expected_crash_and_restart($proc); + if ($check_crash == 0) # unexpected exit/crash of $proc { - if ($opt_resfile && $res && $res != 62) { - resfile_output_file($path_current_testlog); - } - mtr_appendfile_to_file($path_current_testlog, $path_testlog); - unlink($path_current_testlog); + $expected_exit = 0; + last; + } + elsif ($check_crash == 1) # $proc was started again by check_expected_crash_and_restart() + { + delete $keep_waiting_proc{$proc}; + } + elsif ($check_crash == 2) # we must keep waiting + { + # do nothing } - - return ($res == 62) ? 0 : $res; - } - # ---------------------------------------------------- - # Check if it was an expected crash - # ---------------------------------------------------- - my $check_crash = check_expected_crash_and_restart($proc); - if ($check_crash) - { - # Keep waiting if it returned 2, if 1 don't wait or stop waiting. - $keep_waiting_proc = 0 if $check_crash == 1; - $keep_waiting_proc = $proc if $check_crash == 2; + if ($expected_exit) { next; } From 12e2d03948b33acb372337ed591b22bee8e2b48d Mon Sep 17 00:00:00 2001 From: Jacob Mathew Date: Thu, 5 Apr 2018 14:23:31 -0700 Subject: [PATCH 02/15] MDEV-15692: install_spider.sql can fail with some collations The error occurs because of how the character set and collation are chosen for stored procedure parameters that have a character data type. If the character set and collation are not explicitly stated in the declaration, the server chooses the database character set and collation in effect at routine creation time. To fix the problem, I added explicit character set and collation attributes for the stored procedure parameters in the install_spider.sql script. Author: Jacob Mathew. Reviewer: Kentoku Shiba. Cherry-Picked: Commit ff0bf451dbf on bb-10.3-MDEV-15692 --- storage/spider/scripts/install_spider.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/storage/spider/scripts/install_spider.sql b/storage/spider/scripts/install_spider.sql index 328541a550b..dbafe95bdbf 100644 --- a/storage/spider/scripts/install_spider.sql +++ b/storage/spider/scripts/install_spider.sql @@ -132,7 +132,9 @@ drop procedure if exists mysql.spider_fix_one_table; drop procedure if exists mysql.spider_fix_system_tables; delimiter // create procedure mysql.spider_fix_one_table - (tab_name char(255), test_col_name char(255), _sql text) + (tab_name char(255) charset utf8 collate utf8_bin, + test_col_name char(255) charset utf8 collate utf8_bin, + _sql text charset utf8 collate utf8_bin) begin set @col_exists := 0; select 1 into @col_exists from INFORMATION_SCHEMA.COLUMNS From d13e3547e44e28521a383989620b9917375726c1 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 13 Apr 2018 01:56:01 +0300 Subject: [PATCH 03/15] MDEV-14460: Memory leak with only SELECT statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cherry-pick this fix from the upstream: commit 6ddedd8f1e0ddcbc24e8f9a005636c5463799ab7 Author: Sergei Petrunia Date: Tue Apr 10 11:43:01 2018 -0700 [mysql-5.6][PR] Issue #802: MyRocks: Statement rollback doesnt work correctly for nes… Summary: …ted statements Variant #1: When the statement fails, we should roll back to the latest savepoint taken at the top level. Closes https://github.com/facebook/mysql-5.6/pull/804 Differential Revision: D7509380 Pulled By: hermanlee fbshipit-source-id: 9a6f414 --- storage/rocksdb/ha_rocksdb.cc | 130 +++++++++++++++--- .../mysql-test/rocksdb/r/transaction.result | 24 ++++ .../mysql-test/rocksdb/t/transaction.test | 30 ++++ 3 files changed, 162 insertions(+), 22 deletions(-) diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index 7dbf7a994f2..18d5c6ee9a2 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -1772,6 +1772,17 @@ protected: bool m_is_delayed_snapshot = false; bool m_is_two_phase = false; +private: + /* Number of RockDB savepoints taken */ + int m_n_savepoints; + /* + Number of write operations this transaction had when we took the last + savepoint (the idea is not to take another savepoint if we haven't made + any changes) + */ + ulonglong m_writes_at_last_savepoint; + +protected: THD *m_thd = nullptr; rocksdb::ReadOptions m_read_opts; @@ -1799,6 +1810,14 @@ protected: virtual rocksdb::Iterator * get_iterator(const rocksdb::ReadOptions &options, rocksdb::ColumnFamilyHandle *column_family) = 0; + +protected: + /* + The following two are helper functions to be overloaded by child classes. + They should provide RocksDB's savepoint semantics. + */ + virtual void do_set_savepoint() = 0; + virtual void do_rollback_to_savepoint() = 0; public: const char *m_mysql_log_file_name; @@ -2173,6 +2192,50 @@ public: virtual bool is_tx_started() const = 0; virtual void start_tx() = 0; virtual void start_stmt() = 0; + + void set_initial_savepoint() { + /* + Set the initial savepoint. If the first statement in the transaction + fails, we need something to roll back to, without rolling back the + entire transaction. + */ + do_set_savepoint(); + m_n_savepoints= 1; + m_writes_at_last_savepoint= m_write_count; + } + + /* + Called when a "top-level" statement inside a transaction completes + successfully and its changes become part of the transaction's changes. + */ + void make_stmt_savepoint_permanent() { + + // Take another RocksDB savepoint only if we had changes since the last + // one. This is very important for long transactions doing lots of + // SELECTs. + if (m_writes_at_last_savepoint != m_write_count) + { + do_set_savepoint(); + m_writes_at_last_savepoint= m_write_count; + m_n_savepoints++; + } + } + + + /* + Rollback to the savepoint we've set before the last statement + */ + void rollback_to_stmt_savepoint() { + if (m_writes_at_last_savepoint != m_write_count) { + do_rollback_to_savepoint(); + if (!--m_n_savepoints) { + do_set_savepoint(); + m_n_savepoints= 1; + } + m_writes_at_last_savepoint= m_write_count; + } + } + virtual void rollback_stmt() = 0; void set_tx_failed(bool failed_arg) { m_is_tx_failed = failed_arg; } @@ -2462,9 +2525,20 @@ public: m_read_opts = rocksdb::ReadOptions(); + set_initial_savepoint(); + m_ddl_transaction = false; } + /* Implementations of do_*savepoint based on rocksdB::Transaction savepoints */ + void do_set_savepoint() override { + m_rocksdb_tx->SetSavePoint(); + } + + void do_rollback_to_savepoint() override { + m_rocksdb_tx->RollbackToSavePoint(); + } + /* Start a statement inside a multi-statement transaction. @@ -2477,7 +2551,6 @@ public: void start_stmt() override { // Set the snapshot to delayed acquisition (SetSnapshotOnNextOperation) acquire_snapshot(false); - m_rocksdb_tx->SetSavePoint(); } /* @@ -2488,7 +2561,7 @@ public: /* TODO: here we must release the locks taken since the start_stmt() call */ if (m_rocksdb_tx) { const rocksdb::Snapshot *const org_snapshot = m_rocksdb_tx->GetSnapshot(); - m_rocksdb_tx->RollbackToSavePoint(); + rollback_to_stmt_savepoint(); const rocksdb::Snapshot *const cur_snapshot = m_rocksdb_tx->GetSnapshot(); if (org_snapshot != cur_snapshot) { @@ -2565,6 +2638,16 @@ private: return res; } +protected: + /* Implementations of do_*savepoint based on rocksdB::WriteBatch savepoints */ + void do_set_savepoint() override { + m_batch->SetSavePoint(); + } + + void do_rollback_to_savepoint() override { + m_batch->RollbackToSavePoint(); + } + public: bool is_writebatch_trx() const override { return true; } @@ -2670,13 +2753,15 @@ public: write_opts.disableWAL = THDVAR(m_thd, write_disable_wal); write_opts.ignore_missing_column_families = THDVAR(m_thd, write_ignore_missing_column_families); + + set_initial_savepoint(); } - void start_stmt() override { m_batch->SetSavePoint(); } + void start_stmt() override {} void rollback_stmt() override { if (m_batch) - m_batch->RollbackToSavePoint(); + rollback_to_stmt_savepoint(); } explicit Rdb_writebatch_impl(THD *const thd) @@ -2922,6 +3007,8 @@ static int rocksdb_prepare(handlerton* hton, THD* thd, bool prepare_tx) DEBUG_SYNC(thd, "rocksdb.prepared"); } + else + tx->make_stmt_savepoint_permanent(); return HA_EXIT_SUCCESS; } @@ -3172,11 +3259,8 @@ static int rocksdb_commit(handlerton* hton, THD* thd, bool commit_tx) } else { /* We get here when committing a statement within a transaction. - - We don't need to do anything here. tx->start_stmt() will notify - Rdb_transaction_impl that another statement has started. */ - tx->set_tx_failed(false); + tx->make_stmt_savepoint_permanent(); } if (my_core::thd_tx_isolation(thd) <= ISO_READ_COMMITTED) { @@ -10063,22 +10147,24 @@ int ha_rocksdb::external_lock(THD *const thd, int lock_type) { } if (lock_type == F_UNLCK) { - Rdb_transaction *const tx = get_or_create_tx(thd); + Rdb_transaction *const tx = get_tx_from_thd(thd); - tx->io_perf_end_and_record(&m_io_perf); - tx->m_n_mysql_tables_in_use--; - if (tx->m_n_mysql_tables_in_use == 0 && - !my_core::thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { - /* - Do like InnoDB: when we get here, it's time to commit a - single-statement transaction. + if (tx) { + tx->io_perf_end_and_record(&m_io_perf); + tx->m_n_mysql_tables_in_use--; + if (tx->m_n_mysql_tables_in_use == 0 && + !my_core::thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { + /* + Do like InnoDB: when we get here, it's time to commit a + single-statement transaction. - If the statement involved multiple tables, this code will be executed - for each of them, but that's ok because non-first tx->commit() calls - will be no-ops. - */ - if (tx->commit_or_rollback()) { - res = HA_ERR_INTERNAL_ERROR; + If the statement involved multiple tables, this code will be executed + for each of them, but that's ok because non-first tx->commit() calls + will be no-ops. + */ + if (tx->commit_or_rollback()) { + res = HA_ERR_INTERNAL_ERROR; + } } } } else { diff --git a/storage/rocksdb/mysql-test/rocksdb/r/transaction.result b/storage/rocksdb/mysql-test/rocksdb/r/transaction.result index fe13c1633a8..006baaf1339 100644 --- a/storage/rocksdb/mysql-test/rocksdb/r/transaction.result +++ b/storage/rocksdb/mysql-test/rocksdb/r/transaction.result @@ -934,3 +934,27 @@ value 3 rollback; drop table t1; +# +# #802: MyRocks: Statement rollback doesnt work correctly for nested statements +# +create table t1 (a varchar(100)) engine=rocksdb; +create table t2(a int) engine=rocksdb; +insert into t2 values (1), (2); +create table t3(a varchar(100)) engine=rocksdb; +create function func() returns varchar(100) deterministic +begin +insert into t3 values ('func-called'); +set @a= (select a from t2); +return 'func-returned'; +end;// +begin; +insert into t1 values (func()); +ERROR 21000: Subquery returns more than 1 row +select * from t1; +a +# The following must not produce 'func-called': +select * from t3; +a +rollback; +drop function func; +drop table t1,t2,t3; diff --git a/storage/rocksdb/mysql-test/rocksdb/t/transaction.test b/storage/rocksdb/mysql-test/rocksdb/t/transaction.test index a76fa8f9871..3350db99dab 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/transaction.test +++ b/storage/rocksdb/mysql-test/rocksdb/t/transaction.test @@ -103,3 +103,33 @@ update t1 set id=115 where id=3; rollback; drop table t1; + +--echo # +--echo # #802: MyRocks: Statement rollback doesnt work correctly for nested statements +--echo # +create table t1 (a varchar(100)) engine=rocksdb; +create table t2(a int) engine=rocksdb; +insert into t2 values (1), (2); + +create table t3(a varchar(100)) engine=rocksdb; + +delimiter //; +create function func() returns varchar(100) deterministic +begin + insert into t3 values ('func-called'); + set @a= (select a from t2); + return 'func-returned'; +end;// +delimiter ;// + +begin; +--error ER_SUBQUERY_NO_1_ROW +insert into t1 values (func()); +select * from t1; +--echo # The following must not produce 'func-called': +select * from t3; + +rollback; +drop function func; +drop table t1,t2,t3; + From e341da4711d80a8150ac26b2fe1ed4b035c13754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 12 Apr 2018 16:02:25 +0300 Subject: [PATCH 04/15] MDEV-15580: Assertion `!lex->explain' failed in lex_start(THD*): Problem was that we did not delete explain information when Galera must replay a query. Could not find easily repeatable test case that would not cause other problems. --- sql/sql_parse.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5edf92f2a96..886a16f803b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7741,6 +7741,8 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, if (thd->wsrep_conflict_state == MUST_REPLAY) { + if (thd->lex->explain) + delete_explain_query(thd->lex); wsrep_replay_transaction(thd); } @@ -7793,8 +7795,12 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, } /* If retry is requested clean up explain structure */ - if (thd->wsrep_conflict_state == RETRY_AUTOCOMMIT && thd->lex->explain) + if ((thd->wsrep_conflict_state == RETRY_AUTOCOMMIT || + thd->wsrep_conflict_state == MUST_REPLAY ) + && thd->lex->explain) + { delete_explain_query(thd->lex); + } } while (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT); From f638c37abe1ab2e84746829a05144528b8953432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 13 Apr 2018 09:25:52 +0300 Subject: [PATCH 05/15] MDEV-12632: Source and destination overlap in memcpy, encryption.innodb-discard-import-change fails in buildbot with valgrind Use block->page.offset for checking page number. --- storage/innobase/row/row0import.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index dad16c1f54e..c699b2117be 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -3464,7 +3464,7 @@ page_corrupted: /* When tablespace is encrypted or compressed its first page (i.e. page 0) is not encrypted or compressed and there is no need to copy frame. */ - if (encrypted && i != 0) { + if (encrypted && block->page.id.page_no() != 0) { byte *local_frame = callback.get_frame(block); ut_ad((writeptr + (i * size)) != local_frame); memcpy((writeptr + (i * size)), local_frame, size); From 4ea636d5e7f3918847d58d7e608c8dd323408c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 13 Apr 2018 12:50:03 +0300 Subject: [PATCH 06/15] MDEV-15672: encryption.innodb_encryption_filekeys - typo in I_S column name: ENCRYPTION_SHCEME Fixed typo on select that is executed only when something unexpected happens. --- .../suite/encryption/t/innodb_encryption_filekeys.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test b/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test index a087635fc99..03447bbcfa6 100644 --- a/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test +++ b/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test @@ -52,7 +52,7 @@ while ($cnt) } if (!$success) { - SELECT NAME,ENCRYPTION_SHCEME,MIN_KEY_VERSION, ROTATING_OR_FLUSHING FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION; + SELECT NAME,ENCRYPTION_SCHEME,MIN_KEY_VERSION, ROTATING_OR_FLUSHING FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION; SHOW STATUS LIKE 'innodb_encryption%'; -- die Timeout waiting for encryption threads } @@ -82,7 +82,7 @@ while ($cnt) } if (!$success) { - SELECT NAME,ENCRYPTION_SHCEME,MIN_KEY_VERSION, ROTATING_OR_FLUSHING FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION; + SELECT NAME,ENCRYPTION_SCHEME,MIN_KEY_VERSION, ROTATING_OR_FLUSHING FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION; SHOW STATUS LIKE 'innodb_encryption%'; -- die Timeout waiting for encryption threads } @@ -111,7 +111,7 @@ while ($cnt) } if (!$success) { - SELECT NAME,ENCRYPTION_SHCEME,MIN_KEY_VERSION, ROTATING_OR_FLUSHING FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION; + SELECT NAME,ENCRYPTION_SCHEME,MIN_KEY_VERSION, ROTATING_OR_FLUSHING FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION; SHOW STATUS LIKE 'innodb_encryption%'; -- die Timeout waiting for encryption threads } From 043a9b4e1b9cc173016575cddb6a13acdfcc7ad1 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 14 Apr 2018 23:51:23 +0100 Subject: [PATCH 07/15] Windows, innodb : reduce noise from os_file_get_block_size() if volume can't be opened due to permissions, or IOCTL_STORAGE_QUERY_PROPERTY fails with not implemented, do not report it. Those errors happen, there is nothing user can do. This patch amends fix for MDEV-12948. --- storage/innobase/os/os0file.cc | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index 486e8a35c5e..89b02028900 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -865,8 +865,10 @@ os_file_get_block_size( 0, OPEN_EXISTING, 0, 0); if (volume_handle == INVALID_HANDLE_VALUE) { - os_file_handle_error_no_exit(volume, - "CreateFile()", FALSE); + if (GetLastError() != ERROR_ACCESS_DENIED) { + os_file_handle_error_no_exit(volume, + "CreateFile()", FALSE); + } goto end; } @@ -888,16 +890,7 @@ os_file_get_block_size( if (!result) { DWORD err = GetLastError(); - if (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED) { - // Don't report error, it is driver's fault, not ours or users. - // We handle this with fallback. Report wit info message, just once. - static bool write_info = true; - if (write_info) { - ib::info() << "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)" - << " unsupported on volume " << volume; - write_info = false; - } - } else { + if (err != ERROR_INVALID_FUNCTION || err != ERROR_NOT_SUPPORTED) { os_file_handle_error_no_exit(volume, "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)", FALSE); } From 47ea2227e5644df399c33f22db1c61787ff482c2 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Sat, 14 Apr 2018 23:59:59 +0100 Subject: [PATCH 08/15] fix typo, amend last commit --- storage/innobase/os/os0file.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index 89b02028900..0041c1861f8 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -890,7 +890,7 @@ os_file_get_block_size( if (!result) { DWORD err = GetLastError(); - if (err != ERROR_INVALID_FUNCTION || err != ERROR_NOT_SUPPORTED) { + if (err != ERROR_INVALID_FUNCTION && err != ERROR_NOT_SUPPORTED) { os_file_handle_error_no_exit(volume, "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)", FALSE); } From 87af52d7dd733e71fc7a9e39b882a4fd44f41fec Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 16 Apr 2018 13:21:13 +0200 Subject: [PATCH 09/15] MDEV-15866 Mysql CRASH : Json connect + MariaDB 10.3.4 revert incorrect change --- storage/connect/plugutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/connect/plugutil.cpp b/storage/connect/plugutil.cpp index ac102d03208..887527e38ab 100644 --- a/storage/connect/plugutil.cpp +++ b/storage/connect/plugutil.cpp @@ -559,7 +559,7 @@ void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size) if (trace(1)) htrc("PlugSubAlloc: %s\n", g->Message); - abort(); + throw 1234; } /* endif size OS32 code */ /*********************************************************************/ From 224f7af911487f25cc2f768b66a1cce1b3833fd0 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 16 Apr 2018 07:19:19 -0700 Subject: [PATCH 10/15] MDEV-15575 different results when using CTE and big_tables=1. This bug happened due to a defect of the implementation of the handler function ha_delete_all_rows() for the ARIA engine. The function maria_delete_all_rows() truncated the table, but it didn't touch the write cache, so the cache's write offset was not reset. In the scenario like in the function st_select_lex_unit::exec_recursive when first all records were deleted from the table and then several new records were added some metadata became inconsistent with the state of the cache. As a result the table scan function could not read records at the end of the table. The same defect could be found in the implementation of ha_delete_all_rows() for the MYISAM engine mi_delete_all_rows(). Additionally made late instantiation for the temporary table used to store rows that were used for each new iteration when executing a recursive CTE. --- mysql-test/r/cte_recursive.result | 17 +++++++++++++++++ mysql-test/t/cte_recursive.test | 12 ++++++++++++ sql/sql_union.cc | 31 ++++++++++++++++++------------- storage/maria/ma_delete_all.c | 3 +++ storage/myisam/mi_delete_all.c | 4 ++++ 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result index 4e9b4aee67b..55cd30ff236 100644 --- a/mysql-test/r/cte_recursive.result +++ b/mysql-test/r/cte_recursive.result @@ -3063,3 +3063,20 @@ SELECT * FROM cte; 5 2 3 +# +# MDEV-15575: using recursive cte with big_tables enabled +# +set big_tables=1; +with recursive qn as +(select 123 as a union all select 1+a from qn where a<130) +select * from qn; +a +123 +124 +125 +126 +127 +128 +129 +130 +set big_tables=default; diff --git a/mysql-test/t/cte_recursive.test b/mysql-test/t/cte_recursive.test index c3a5ed5cff0..373d6ca97cd 100644 --- a/mysql-test/t/cte_recursive.test +++ b/mysql-test/t/cte_recursive.test @@ -2097,3 +2097,15 @@ WITH RECURSIVE cte AS UNION SELECT @c:=@c+1 FROM cte WHERE @c<3) SELECT * FROM cte; + +--echo # +--echo # MDEV-15575: using recursive cte with big_tables enabled +--echo # + +set big_tables=1; + +with recursive qn as +(select 123 as a union all select 1+a from qn where a<130) +select * from qn; + +set big_tables=default; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 19c9330481f..3fe8a24f8bd 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -214,19 +214,13 @@ select_union_recursive::create_result_table(THD *thd_arg, if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, false, 1, options, HA_POS_ERROR, "", - !create_table, keep_row_order))) + true, keep_row_order))) return true; incr_table->keys_in_use_for_query.clear_all(); for (uint i=0; i < table->s->fields; i++) incr_table->field[i]->flags &= ~PART_KEY_FLAG; - if (create_table) - { - incr_table->file->extra(HA_EXTRA_WRITE_CACHE); - incr_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - } - TABLE *rec_table= 0; if (! (rec_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, false, 1, @@ -270,8 +264,11 @@ void select_union_recursive::cleanup() if (incr_table) { - incr_table->file->extra(HA_EXTRA_RESET_STATE); - incr_table->file->ha_delete_all_rows(); + if (incr_table->is_created()) + { + incr_table->file->extra(HA_EXTRA_RESET_STATE); + incr_table->file->ha_delete_all_rows(); + } free_tmp_table(thd, incr_table); } @@ -1228,16 +1225,24 @@ bool st_select_lex_unit::exec_recursive() if (!was_executed) save_union_explain(thd->lex->explain); - if ((saved_error= incr_table->file->ha_delete_all_rows())) - goto err; - if (with_element->level == 0) { + if (!incr_table->is_created() && + instantiate_tmp_table(incr_table, + tmp_table_param->keyinfo, + tmp_table_param->start_recinfo, + &tmp_table_param->recinfo, + 0)) + DBUG_RETURN(1); + incr_table->file->extra(HA_EXTRA_WRITE_CACHE); + incr_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); start= first_select(); if (with_element->with_anchor) end= with_element->first_recursive; } - + else if ((saved_error= incr_table->file->ha_delete_all_rows())) + goto err; + for (st_select_lex *sl= start ; sl != end; sl= sl->next_select()) { thd->lex->current_select= sl; diff --git a/storage/maria/ma_delete_all.c b/storage/maria/ma_delete_all.c index a14603b24a5..ee75218e2b6 100644 --- a/storage/maria/ma_delete_all.c +++ b/storage/maria/ma_delete_all.c @@ -135,6 +135,9 @@ int maria_delete_all_rows(MARIA_HA *info) goto err; } + if (info->opt_flag & WRITE_CACHE_USED) + reinit_io_cache(&info->rec_cache, WRITE_CACHE, 0, 1, 1); + _ma_writeinfo(info, WRITEINFO_UPDATE_KEYFILE); #ifdef HAVE_MMAP /* Map again */ diff --git a/storage/myisam/mi_delete_all.c b/storage/myisam/mi_delete_all.c index 37fdf2dcb04..c772e843ecf 100644 --- a/storage/myisam/mi_delete_all.c +++ b/storage/myisam/mi_delete_all.c @@ -62,6 +62,10 @@ int mi_delete_all_rows(MI_INFO *info) if (mysql_file_chsize(info->dfile, 0, 0, MYF(MY_WME)) || mysql_file_chsize(share->kfile, share->base.keystart, 0, MYF(MY_WME))) goto err; + + if (info->opt_flag & WRITE_CACHE_USED) + reinit_io_cache(&info->rec_cache, WRITE_CACHE, 0, 1, 1); + (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE); DBUG_RETURN(0); From 612850782d6d8bbe44d2b153a045b9a8afc624ef Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 16 Apr 2018 08:55:15 -0700 Subject: [PATCH 11/15] MDEV-15571 Wrong results with big_tables=1 and CTE The cause of this bug was the same as for the bug mdev-15575. Fixed by the patch for the latter. --- mysql-test/r/cte_recursive.result | 13 +++++++++++++ mysql-test/t/cte_recursive.test | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result index 55cd30ff236..01443bb6494 100644 --- a/mysql-test/r/cte_recursive.result +++ b/mysql-test/r/cte_recursive.result @@ -3080,3 +3080,16 @@ a 129 130 set big_tables=default; +# +# MDEV-1571: Setting user variable in recursive CTE +# +set big_tables=1; +with recursive qn as +( +select 1 as a from dual +union all +select a*2000 from qn where a<10000000000000000000 +) +select * from qn; +ERROR 22003: BIGINT value is out of range in '`qn`.`a` * 2000' +set big_tables=default; diff --git a/mysql-test/t/cte_recursive.test b/mysql-test/t/cte_recursive.test index 373d6ca97cd..008c8ea9296 100644 --- a/mysql-test/t/cte_recursive.test +++ b/mysql-test/t/cte_recursive.test @@ -2109,3 +2109,20 @@ with recursive qn as select * from qn; set big_tables=default; + +--echo # +--echo # MDEV-1571: Setting user variable in recursive CTE +--echo # + +set big_tables=1; + +--error ER_DATA_OUT_OF_RANGE +with recursive qn as +( + select 1 as a from dual + union all + select a*2000 from qn where a<10000000000000000000 +) +select * from qn; + +set big_tables=default; From e34d3184fd02967616bb83904aa3c21977ce6205 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 16 Apr 2018 10:31:30 -0700 Subject: [PATCH 12/15] MDEV-15556 MariaDB crash with big_tables=1 and CTE This bug manifested itself when the optimizer chose an execution plan with an access of the recursive CTE in a recursive query by key and ARIA/MYISAM temporary tables were used to store recursive tables. The problem appeared due to passing an incorrect parameter to the call of instantiate_tmp_table() in the function With_element::instantiate_tmp_tables(). --- mysql-test/r/cte_recursive.result | 88 ++++++++++++++++++++++++++++++- mysql-test/t/cte_recursive.test | 66 ++++++++++++++++++++++- sql/sql_cte.cc | 2 +- 3 files changed, 153 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result index 01443bb6494..e1a52be5a6b 100644 --- a/mysql-test/r/cte_recursive.result +++ b/mysql-test/r/cte_recursive.result @@ -3081,7 +3081,7 @@ a 130 set big_tables=default; # -# MDEV-1571: Setting user variable in recursive CTE +# MDEV-15571: using recursive cte with big_tables enabled # set big_tables=1; with recursive qn as @@ -3093,3 +3093,89 @@ select a*2000 from qn where a<10000000000000000000 select * from qn; ERROR 22003: BIGINT value is out of range in '`qn`.`a` * 2000' set big_tables=default; +# +# MDEV-15556: using recursive cte with big_tables enabled +# when recursive tables are accessed by key +# +SET big_tables=1; +CREATE TABLE t1 (id int, name char(10), leftpar int, rightpar int); +INSERT INTO t1 VALUES +(1, "A", 2, 3), (2, "LA", 4, 5), (4, "LLA", 6, 7), +(6, "LLLA", NULL, NULL), (7, "RLLA", NULL, NULL), (5, "RLA", 8, 9), +(8, "LRLA", NULL, NULL), (9, "RRLA", NULL, NULL), (3, "RA", 10, 11), +(10, "LRA", 12, 13), (11, "RRA", 14, 15), (15, "RRRA", NULL, NULL), +(16, "B", 17, 18), (17, "LB", NULL, NULL), (18, "RB", NULL, NULL); +CREATE TABLE t2 SELECT * FROM t1 ORDER BY rand(); +WITH RECURSIVE tree_of_a AS +(SELECT *, cast(id AS char(200)) AS path FROM t2 WHERE name="A" + UNION ALL +SELECT t2.*, concat(tree_of_a.path,",",t2.id) +FROM t2 JOIN tree_of_a ON t2.id=tree_of_a.leftpar +UNION ALL +SELECT t2.*, concat(tree_of_a.path,",",t2.id) +FROM t2 JOIN tree_of_a ON t2.id=tree_of_a.rightpar) +SELECT * FROM tree_of_a +ORDER BY path; +id name leftpar rightpar path +1 A 2 3 1 +2 LA 4 5 1,2 +4 LLA 6 7 1,2,4 +6 LLLA NULL NULL 1,2,4,6 +7 RLLA NULL NULL 1,2,4,7 +5 RLA 8 9 1,2,5 +8 LRLA NULL NULL 1,2,5,8 +9 RRLA NULL NULL 1,2,5,9 +3 RA 10 11 1,3 +10 LRA 12 13 1,3,10 +11 RRA 14 15 1,3,11 +15 RRRA NULL NULL 1,3,11,15 +EXPLAIN WITH RECURSIVE tree_of_a AS +(SELECT *, cast(id AS char(200)) AS path FROM t2 WHERE name="A" + UNION ALL +SELECT t2.*, concat(tree_of_a.path,",",t2.id) +FROM t2 JOIN tree_of_a ON t2.id=tree_of_a.leftpar +UNION ALL +SELECT t2.*, concat(tree_of_a.path,",",t2.id) +FROM t2 JOIN tree_of_a ON t2.id=tree_of_a.rightpar) +SELECT * FROM tree_of_a +ORDER BY path; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 15 Using filesort +2 DERIVED t2 ALL NULL NULL NULL NULL 15 Using where +3 RECURSIVE UNION t2 ALL NULL NULL NULL NULL 15 Using where +3 RECURSIVE UNION ref key0 key0 5 test.t2.id 2 +4 RECURSIVE UNION t2 ALL NULL NULL NULL NULL 15 Using where +4 RECURSIVE UNION ref key0 key0 5 test.t2.id 2 +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +DROP TABLE t1,t2; +SET big_tables=0; +# +# MDEV-15840: recursive tables are accessed by key +# (the same problem as for MDEV-15556) +# +CREATE TABLE t1 (p1 text,k2 int, p2 text, k1 int); +INSERT INTO t1 select seq, seq, seq, seq from seq_1_to_1000; +CREATE PROCEDURE getNums() +BEGIN +WITH RECURSIVE cte as +( +SELECT * FROM t1 +UNION +SELECT c.* FROM t1 c JOIN cte p ON c.p1 = p.p2 AND c.k2 = p.k1 +) +SELECT * FROM cte LIMIT 10; +END | +call getNums(); +p1 k2 p2 k1 +1 1 1 1 +2 2 2 2 +3 3 3 3 +4 4 4 4 +5 5 5 5 +6 6 6 6 +7 7 7 7 +8 8 8 8 +9 9 9 9 +10 10 10 10 +DROP PROCEDURE getNums; +DROP TABLE t1; diff --git a/mysql-test/t/cte_recursive.test b/mysql-test/t/cte_recursive.test index 008c8ea9296..e3032fce86a 100644 --- a/mysql-test/t/cte_recursive.test +++ b/mysql-test/t/cte_recursive.test @@ -2111,7 +2111,7 @@ select * from qn; set big_tables=default; --echo # ---echo # MDEV-1571: Setting user variable in recursive CTE +--echo # MDEV-15571: using recursive cte with big_tables enabled --echo # set big_tables=1; @@ -2126,3 +2126,67 @@ with recursive qn as select * from qn; set big_tables=default; + +--echo # +--echo # MDEV-15556: using recursive cte with big_tables enabled +--echo # when recursive tables are accessed by key +--echo # + +SET big_tables=1; + +CREATE TABLE t1 (id int, name char(10), leftpar int, rightpar int); +INSERT INTO t1 VALUES + (1, "A", 2, 3), (2, "LA", 4, 5), (4, "LLA", 6, 7), + (6, "LLLA", NULL, NULL), (7, "RLLA", NULL, NULL), (5, "RLA", 8, 9), + (8, "LRLA", NULL, NULL), (9, "RRLA", NULL, NULL), (3, "RA", 10, 11), + (10, "LRA", 12, 13), (11, "RRA", 14, 15), (15, "RRRA", NULL, NULL), + (16, "B", 17, 18), (17, "LB", NULL, NULL), (18, "RB", NULL, NULL); + +CREATE TABLE t2 SELECT * FROM t1 ORDER BY rand(); + +let $q= +WITH RECURSIVE tree_of_a AS + (SELECT *, cast(id AS char(200)) AS path FROM t2 WHERE name="A" + UNION ALL + SELECT t2.*, concat(tree_of_a.path,",",t2.id) + FROM t2 JOIN tree_of_a ON t2.id=tree_of_a.leftpar + UNION ALL + SELECT t2.*, concat(tree_of_a.path,",",t2.id) + FROM t2 JOIN tree_of_a ON t2.id=tree_of_a.rightpar) +SELECT * FROM tree_of_a +ORDER BY path; + +eval $q; +eval EXPLAIN $q; + +DROP TABLE t1,t2; + +SET big_tables=0; + +--echo # +--echo # MDEV-15840: recursive tables are accessed by key +--echo # (the same problem as for MDEV-15556) +--echo # + +--source include/have_sequence.inc + +CREATE TABLE t1 (p1 text,k2 int, p2 text, k1 int); +INSERT INTO t1 select seq, seq, seq, seq from seq_1_to_1000; + +DELIMITER |; +CREATE PROCEDURE getNums() +BEGIN +WITH RECURSIVE cte as +( + SELECT * FROM t1 + UNION + SELECT c.* FROM t1 c JOIN cte p ON c.p1 = p.p2 AND c.k2 = p.k1 +) +SELECT * FROM cte LIMIT 10; +END | + +DELIMITER ;| +call getNums(); + +DROP PROCEDURE getNums; +DROP TABLE t1; diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index dd46295d799..6bc833b46a7 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -1401,7 +1401,7 @@ bool With_element::instantiate_tmp_tables() { if (!rec_table->is_created() && instantiate_tmp_table(rec_table, - rec_result->tmp_table_param.keyinfo, + rec_table->s->key_info, rec_result->tmp_table_param.start_recinfo, &rec_result->tmp_table_param.recinfo, 0)) From 1d98333ad986d89215f903798a1787ab20308c6b Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 17 Apr 2018 10:35:55 -0700 Subject: [PATCH 13/15] MDEV-15894 Error, while using aggregated functions/window functions in anchor part Usage of aggregate/window functions in non-recursive parts of recursive CTEs is allowed. Error messages complaining about this were reported by mistake. --- mysql-test/r/cte_recursive.result | 19 +++++++++++++++++++ mysql-test/t/cte_recursive.test | 19 +++++++++++++++++++ sql/sql_cte.cc | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result index e1a52be5a6b..6b2db359294 100644 --- a/mysql-test/r/cte_recursive.result +++ b/mysql-test/r/cte_recursive.result @@ -3179,3 +3179,22 @@ p1 k2 p2 k1 10 10 10 10 DROP PROCEDURE getNums; DROP TABLE t1; +# +# MDEV-15894: aggregate/winfow functions in non-recorsive part +# +create table t1(b int); +insert into t1 values(10),(20),(10); +with recursive qn as +(select max(b) as a from t1 union +select a from qn) +select * from qn; +a +20 +with recursive qn as +(select rank() over (order by b) as a from t1 union +select a from qn) +select * from qn; +a +1 +3 +drop table t1; diff --git a/mysql-test/t/cte_recursive.test b/mysql-test/t/cte_recursive.test index e3032fce86a..50cb39a2b35 100644 --- a/mysql-test/t/cte_recursive.test +++ b/mysql-test/t/cte_recursive.test @@ -2190,3 +2190,22 @@ call getNums(); DROP PROCEDURE getNums; DROP TABLE t1; + +--echo # +--echo # MDEV-15894: aggregate/winfow functions in non-recorsive part +--echo # + +create table t1(b int); +insert into t1 values(10),(20),(10); + +with recursive qn as + (select max(b) as a from t1 union + select a from qn) +select * from qn; + +with recursive qn as + (select rank() over (order by b) as a from t1 union + select a from qn) +select * from qn; + +drop table t1; diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 6bc833b46a7..cd2516c2beb 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -1173,7 +1173,7 @@ bool st_select_lex::check_unrestricted_recursive(bool only_standard_compliant) /* Check conditions 3-4 for restricted specification*/ - if (with_sum_func || + if ((with_sum_func && !with_elem->is_anchor(this)) || (with_elem->contains_sq_with_recursive_reference())) with_elem->get_owner()->add_unrestricted( with_elem->get_mutually_recursive()); From 341edddc3d28d62eaf47039f34f72f32ba80743f Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 18 Apr 2018 12:39:39 +0530 Subject: [PATCH 14/15] MDEV-15826 Purge attempts to free BLOB page after BEGIN;INSERT;UPDATE;ROLLBACK - During rollback, redo segments priorities over no-redo rollback segments and it leads to failure of redo rollback segment undo logs truncation. --- mysql-test/suite/innodb/r/undo_log.result | 13 ++++++++++ mysql-test/suite/innodb/t/undo_log.test | 14 +++++++++++ storage/innobase/trx/trx0roll.cc | 29 +++++++++++++++-------- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/mysql-test/suite/innodb/r/undo_log.result b/mysql-test/suite/innodb/r/undo_log.result index a40c6b5b3bf..6fe0da3da47 100644 --- a/mysql-test/suite/innodb/r/undo_log.result +++ b/mysql-test/suite/innodb/r/undo_log.result @@ -140,3 +140,16 @@ CHECK TABLE test_tab; Table Op Msg_type Msg_text test.test_tab check status OK DROP TABLE test_tab; +SET @saved_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency; +SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; +CREATE TEMPORARY TABLE t2(i INT)ENGINE=InnoDB; +CREATE TABLE t1(i TEXT NOT NULL) ENGINE=INNODB; +BEGIN; +INSERT t1 SET i=REPEAT('1234567890',840); +UPDATE t1 SET i=''; +INSERT INTO t2 VALUES(2); +ROLLBACK; +InnoDB 0 transactions not purged +DROP TABLE t1; +DROP TABLE t2; +SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency; diff --git a/mysql-test/suite/innodb/t/undo_log.test b/mysql-test/suite/innodb/t/undo_log.test index c1a98793d91..1f4cf9702d9 100644 --- a/mysql-test/suite/innodb/t/undo_log.test +++ b/mysql-test/suite/innodb/t/undo_log.test @@ -137,3 +137,17 @@ ROLLBACK; SELECT COUNT(*) FROM test_tab; CHECK TABLE test_tab; DROP TABLE test_tab; + +SET @saved_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency; +SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; +CREATE TEMPORARY TABLE t2(i INT)ENGINE=InnoDB; +CREATE TABLE t1(i TEXT NOT NULL) ENGINE=INNODB; +BEGIN; +INSERT t1 SET i=REPEAT('1234567890',840); +UPDATE t1 SET i=''; +INSERT INTO t2 VALUES(2); +ROLLBACK; +--source include/wait_all_purged.inc +DROP TABLE t1; +DROP TABLE t2; +SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency; diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index d1f33c2efd6..82592fd3ae4 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -1003,7 +1003,7 @@ trx_roll_pop_top_rec_of_trx(trx_t* trx, roll_ptr_t* roll_ptr, mem_heap_t* heap) trx_roll_try_truncate(trx); } - trx_undo_t* undo; + trx_undo_t* undo = NULL; trx_undo_t* insert = trx->rsegs.m_redo.insert_undo; trx_undo_t* update = trx->rsegs.m_redo.update_undo; trx_undo_t* temp = trx->rsegs.m_noredo.undo; @@ -1017,17 +1017,26 @@ trx_roll_pop_top_rec_of_trx(trx_t* trx, roll_ptr_t* roll_ptr, mem_heap_t* heap) || update->top_undo_no != temp->top_undo_no); if (insert && !insert->empty && limit <= insert->top_undo_no) { - if (update && !update->empty - && update->top_undo_no > insert->top_undo_no) { + undo = insert; + } + + if (update && !update->empty && update->top_undo_no >= limit) { + if (!undo) { + undo = update; + } else if (undo->top_undo_no < update->top_undo_no) { undo = update; - } else { - undo = insert; } - } else if (update && !update->empty && limit <= update->top_undo_no) { - undo = update; - } else if (temp && !temp->empty && limit <= temp->top_undo_no) { - undo = temp; - } else { + } + + if (temp && !temp->empty && temp->top_undo_no >= limit) { + if (!undo) { + undo = temp; + } else if (undo->top_undo_no < temp->top_undo_no) { + undo = temp; + } + } + + if (undo == NULL) { trx_roll_try_truncate(trx); /* Mark any ROLLBACK TO SAVEPOINT completed, so that if the transaction object is committed and reused From 66c14d3a8d31e877ede75d23f96dc61a4aa12971 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 18 Apr 2018 13:52:30 +0530 Subject: [PATCH 15/15] MDEV-14377 innodb_zip.cmp_per_index failed in buildbot, result length mismatch - If select query chooses the index 'b' over clustered index then the issue can happen. Changed the test case to use primary index for the select query. --- .../suite/innodb_zip/r/cmp_per_index.result | 19 +++++++++++-------- .../suite/innodb_zip/t/cmp_per_index.test | 4 +++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/innodb_zip/r/cmp_per_index.result b/mysql-test/suite/innodb_zip/r/cmp_per_index.result index b380071c623..cdb81e7be8a 100644 --- a/mysql-test/suite/innodb_zip/r/cmp_per_index.result +++ b/mysql-test/suite/innodb_zip/r/cmp_per_index.result @@ -70,8 +70,17 @@ index_name PRIMARY compress_ops 65 compress_ops_ok 65 uncompress_ops 0 +SHOW CREATE TABLE t; +Table t +Create Table CREATE TABLE `t` ( + `a` int(11) NOT NULL, + `b` varchar(512) DEFAULT NULL, + `c` varchar(16) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b` (`b`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 KEY_BLOCK_SIZE=2 SET GLOBAL innodb_cmp_per_index_enabled=ON; -SELECT COUNT(*) FROM t; +SELECT COUNT(*) FROM t IGNORE INDEX(b); COUNT(*) 128 SELECT database_name, @@ -85,15 +94,9 @@ FROM information_schema.innodb_cmp_per_index ORDER BY 1, 2, 3; database_name test table_name t -index_name b -compress_ops 0 -compress_ops_ok 0 -uncompress_ops 6 -database_name test -table_name t index_name PRIMARY compress_ops 0 compress_ops_ok 0 -uncompress_ops 5 +uncompress_ops 4 DROP TABLE t; SET GLOBAL innodb_cmp_per_index_enabled=default; diff --git a/mysql-test/suite/innodb_zip/t/cmp_per_index.test b/mysql-test/suite/innodb_zip/t/cmp_per_index.test index 58b7855219b..8d66277a1bd 100644 --- a/mysql-test/suite/innodb_zip/t/cmp_per_index.test +++ b/mysql-test/suite/innodb_zip/t/cmp_per_index.test @@ -98,9 +98,11 @@ ORDER BY 1, 2, 3; -- source include/restart_mysqld.inc +SHOW CREATE TABLE t; + SET GLOBAL innodb_cmp_per_index_enabled=ON; -SELECT COUNT(*) FROM t; +SELECT COUNT(*) FROM t IGNORE INDEX(b); SELECT database_name,