From a319220e6214390730030e1063fca7675e5e98a4 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 6 Feb 2022 23:00:34 +0100 Subject: update test result --- mysql-test/suite/galera_sr/r/MDEV-27615.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/suite/galera_sr/r/MDEV-27615.result b/mysql-test/suite/galera_sr/r/MDEV-27615.result index a3475811285..e3bfd0ed539 100644 --- a/mysql-test/suite/galera_sr/r/MDEV-27615.result +++ b/mysql-test/suite/galera_sr/r/MDEV-27615.result @@ -15,7 +15,7 @@ SET DEBUG_SYNC='now WAIT_FOR before_fragment'; SET GLOBAL wsrep_cluster_address = ''; SET DEBUG_SYNC = 'now SIGNAL continue'; connection node_2; -ERROR HY000: Lost connection to MySQL server during query +ERROR HY000: Lost connection to server during query connection node_2a; SELECT * FROM mysql.wsrep_streaming_log; node_uuid trx_id seqno flags frag -- cgit v1.2.1 From 881918bf775d86d589cec6b213ce2efb8bf76563 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 7 Feb 2022 00:23:14 +0100 Subject: MDEV-27754 : Assertion with innodb_flush_method=O_DSYNC If innodb_flush_method=O_DSYNC, log_sys.flushed_to_disk_lsn is changed without 'flush_lock' protection inside log_write(). This leads to a race condition, if there are 2 threads running in parallel, doing log_write_up_to() with different values for 'flush_to_disk' In this case, log_write() and log_write_flush_to_disk_low() can execute at the same time, and both would change flushed_lsn. The fix is to remove special treatment of durable writes from log_write(). There is no apparent reason for this special treatment, log_write_flush_to_disk_low() is already optimized for durable writes. Nor there is an apparent reason to call log_flush_notify() more often in for O_DSYNC. --- storage/innobase/log/log0log.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc index 914337d2a54..cee2fa978ab 100644 --- a/storage/innobase/log/log0log.cc +++ b/storage/innobase/log/log0log.cc @@ -639,8 +639,7 @@ loop: } } -/** Flush the recently written changes to the log file. -and invoke mysql_mutex_lock(&log_sys.mutex). */ +/** Flush the recently written changes to the log file.*/ static void log_write_flush_to_disk_low(lsn_t lsn) { if (!log_sys.log.writes_are_durable()) @@ -780,10 +779,6 @@ static void log_write(bool rotate_key) start_offset - area_start); srv_stats.log_padded.add(pad_size); log_sys.write_lsn = write_lsn; - if (log_sys.log.writes_are_durable()) { - log_sys.set_flushed_lsn(write_lsn); - log_flush_notify(write_lsn); - } return; } -- cgit v1.2.1 From df02de68f3b42741289d85296255a1a169358a12 Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 17 Aug 2020 19:01:43 +0300 Subject: Fixed my_addr_resolve (cherry picked from 10.6) When a server is compiled with -fPIE, my_addr_resolve needs to subtract the info.dli_fbase from symbol addresses in memory for addr2line to recognize them. When a server is compiled without -fPIE, my_addr_resolve should not do it. Unfortunately not all compilers define __PIE__ when -fPIE was used (e.g. older gcc doesn't), so we have to resort to run-time detection. --- mysys/my_addr_resolve.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index 0603a50fac7..86670cf1db2 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -170,6 +170,7 @@ static pid_t pid; static char addr2line_binary[1024]; static char output[1024]; static struct pollfd poll_fds; +static void *addr_offset; int start_addr2line_fork(const char *binary_path) { @@ -297,7 +298,6 @@ static int addr_resolve(void *ptr, my_addr_loc *loc) int my_addr_resolve(void *ptr, my_addr_loc *loc) { Dl_info info; - int error; if (!dladdr(ptr, &info)) return 1; @@ -307,7 +307,7 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) /* We use dli_fname in case the path is longer than the length of our static string. We don't want to allocate anything - dynamicaly here as we are in a "crashed" state. + dynamically here as we are in a "crashed" state. */ if (start_addr2line_fork(info.dli_fname)) { @@ -318,10 +318,22 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) } /* Save result for future comparisons. */ strnmov(addr2line_binary, info.dli_fname, sizeof(addr2line_binary)); + + /* + Check if we should use info.dli_fbase as an offset or not + for the base program. This is depending on if the compilation is + done with PIE or not. + */ + addr_offset= info.dli_fbase; +#ifndef __PIE__ + if (strcmp(info.dli_fname, my_progname) == 0 && + addr_resolve((void*) my_addr_resolve, loc) == 0 && + strcmp(loc->func, "my_addr_resolve") == 0) + addr_offset= 0; +#endif } - if (!(error= addr_resolve((void*) (ptr - info.dli_fbase), loc))) - return 0; - return error; + + return addr_resolve((void*) (ptr - addr_offset), loc); } -- cgit v1.2.1 From 88fb89acb7ad6a7300b01d02d71d2cee00449ec4 Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 2 Feb 2022 16:41:13 +0200 Subject: Fixes some compiler issues on AIX ( --- include/my_alarm.h | 2 +- mysys/my_addr_resolve.c | 2 +- mysys/my_gethwaddr.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/my_alarm.h b/include/my_alarm.h index 14a81c1ef17..df5cb7f51de 100644 --- a/include/my_alarm.h +++ b/include/my_alarm.h @@ -31,7 +31,7 @@ extern ulong my_time_to_wait_for_lock; #include #ifdef HAVE_SIGHANDLER_T #define sig_return sighandler_t -#elif defined(SOLARIS) || defined(__sun) || defined(__APPLE__) || defined(__FreeBSD__) +#elif defined(SOLARIS) || defined(__sun) || defined(__APPLE__) || defined(__FreeBSD__) || defined(_AIX) typedef void (*sig_return)(int); /* Returns type from signal */ #else typedef void (*sig_return)(void); /* Returns type from signal */ diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index 86670cf1db2..444a47bb7c5 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -324,7 +324,7 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) for the base program. This is depending on if the compilation is done with PIE or not. */ - addr_offset= info.dli_fbase; + addr_offset= (void*) info.dli_fbase; #ifndef __PIE__ if (strcmp(info.dli_fname, my_progname) == 0 && addr_resolve((void*) my_addr_resolve, loc) == 0 && diff --git a/mysys/my_gethwaddr.c b/mysys/my_gethwaddr.c index 70e1d549e15..46b62a0166d 100644 --- a/mysys/my_gethwaddr.c +++ b/mysys/my_gethwaddr.c @@ -23,6 +23,7 @@ #ifndef MAIN +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) || defined(__sun) || defined(_WIN32) static my_bool memcpy_and_test(uchar *to, uchar *from, uint len) { uint i, res= 1; @@ -32,6 +33,7 @@ static my_bool memcpy_and_test(uchar *to, uchar *from, uint len) res= 0; return res; } +#endif #if defined(__APPLE__) || defined(__FreeBSD__) #include @@ -195,4 +197,3 @@ int main(int argc __attribute__((unused)),char **argv) return 0; } #endif - -- cgit v1.2.1 From 0ec27d7b1f7b5f1f49a3099faa554994bc58603a Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 2 Feb 2022 14:26:38 +0200 Subject: Don't run innodb_defgragment under valgrind (too slow) --- mysql-test/suite/innodb/t/innodb_defragment.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_defragment.test b/mysql-test/suite/innodb/t/innodb_defragment.test index d9f5f56316e..51ef78377cb 100644 --- a/mysql-test/suite/innodb/t/innodb_defragment.test +++ b/mysql-test/suite/innodb/t/innodb_defragment.test @@ -1,6 +1,8 @@ --source include/have_innodb.inc --source include/big_test.inc --source include/not_embedded.inc +# Valgrind is to slow for this test +--source include/not_valgrind.inc set global innodb_defragment_stats_accuracy = 80; -- cgit v1.2.1 From a1c23807530c6ffd5d400f71d9226bca5b91fb62 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 6 Feb 2022 16:05:48 +0200 Subject: MENT-328 Retry BACKUP STAGE BLOCK DDL in case of deadlocks MENT-328 wrongly assumed that the backup failed because of warnings from mariabackup about not found files. This is normal (and the error message should be deleted). randgen failed because mariabackup didn't retry BACKUP STAGE BLOCK DDL if it failed with a deadlock. To simplify things, I implemented the retry loop in the server as this particular deadlock should be quickly resolved. --- mysql-test/main/backup_locks.result | 23 ++++++++++++++++++++++- mysql-test/main/backup_locks.test | 31 +++++++++++++++++++++++++++++-- sql/backup.cc | 37 ++++++++++++++++++++++++++++--------- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/mysql-test/main/backup_locks.result b/mysql-test/main/backup_locks.result index 1505c39f166..1e567c1a58d 100644 --- a/mysql-test/main/backup_locks.result +++ b/mysql-test/main/backup_locks.result @@ -39,6 +39,28 @@ MDL_INTENTION_EXCLUSIVE Schema metadata lock test select * from t1; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction backup unlock; +connection con1; +connection default; +# +# Check that BACKUP LOCK blocks some operations +# +create sequence seq1; +create sequence seq2; +backup lock seq1; +connection con1; +CREATE OR REPLACE SEQUENCE seq1 START -28; +ERROR HY000: Sequence 'test.seq1' values are conflicting +SET STATEMENT max_statement_time=10 FOR CREATE OR REPLACE SEQUENCE seq1 START 50; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +SET STATEMENT max_statement_time=10 FOR ALTER SEQUENCE IF EXISTS seq1 NOMAXVALUE; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +SET STATEMENT max_statement_time=10 FOR ALTER SEQUENCE IF EXISTS seq1 MAXVALUE 1000; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +SET STATEMENT max_statement_time=10 for rename table seq2 to seq3, seq3 to seq1; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +connection default; +backup unlock; +drop table seq1,seq2; # # BACKUP LOCK and BACKUP UNLOCK are not allowed in procedures. # @@ -141,7 +163,6 @@ ERROR HY000: Can't execute the given command because you have active locked tabl SET STATEMENT max_statement_time=180 FOR BACKUP LOCK test.u; # restart # -connection con1; connection default; disconnect con1; show tables; diff --git a/mysql-test/main/backup_locks.test b/mysql-test/main/backup_locks.test index d2f3d95d703..1271abfd993 100644 --- a/mysql-test/main/backup_locks.test +++ b/mysql-test/main/backup_locks.test @@ -43,10 +43,39 @@ SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.me --error ER_LOCK_DEADLOCK select * from t1; backup unlock; +connection con1; +--reap +connection default; + +--echo # +--echo # Check that BACKUP LOCK blocks some operations +--echo # + +# These test has to be done with timeouts as we want to ensure that the tables +# doesn't change + +create sequence seq1; +create sequence seq2; +backup lock seq1; +connection con1; +--error ER_SEQUENCE_INVALID_DATA +CREATE OR REPLACE SEQUENCE seq1 START -28; +--error ER_STATEMENT_TIMEOUT +SET STATEMENT max_statement_time=10 FOR CREATE OR REPLACE SEQUENCE seq1 START 50; +--error ER_STATEMENT_TIMEOUT +SET STATEMENT max_statement_time=10 FOR ALTER SEQUENCE IF EXISTS seq1 NOMAXVALUE; +--error ER_STATEMENT_TIMEOUT +SET STATEMENT max_statement_time=10 FOR ALTER SEQUENCE IF EXISTS seq1 MAXVALUE 1000; +--error ER_STATEMENT_TIMEOUT +SET STATEMENT max_statement_time=10 for rename table seq2 to seq3, seq3 to seq1; +connection default; +backup unlock; +drop table seq1,seq2; --echo # --echo # BACKUP LOCK and BACKUP UNLOCK are not allowed in procedures. --echo # + delimiter |; --error ER_SP_BADSTATEMENT CREATE PROCEDURE p_BACKUP_LOCK() @@ -162,8 +191,6 @@ SET STATEMENT max_statement_time=180 FOR BACKUP LOCK test.u; --echo # -connection con1; ---reap connection default; disconnect con1; show tables; diff --git a/sql/backup.cc b/sql/backup.cc index 539dc9c31f4..c856048b49a 100644 --- a/sql/backup.cc +++ b/sql/backup.cc @@ -233,8 +233,12 @@ static bool backup_flush(THD *thd) This will probably require a callback from the InnoDB code. */ +/* Retry to get inital lock for 0.1 + 0.5 + 2.25 + 11.25 + 56.25 = 70.35 sec */ +#define MAX_RETRY_COUNT 5 + static bool backup_block_ddl(THD *thd) { + uint sleep_time; DBUG_ENTER("backup_block_ddl"); kill_delayed_threads(); @@ -275,17 +279,32 @@ static bool backup_block_ddl(THD *thd) block new DDL's, in addition to all previous blocks We didn't do this lock above, as we wanted DDL's to be executed while we wait for non transactional tables (which may take a while). + + We do this lock in a loop as we can get a deadlock if there are multi-object + ddl statements like + RENAME TABLE t1 TO t2, t3 TO t3 + and the MDL happens in the middle of it. */ - if (thd->mdl_context.upgrade_shared_lock(backup_flush_ticket, - MDL_BACKUP_WAIT_DDL, - thd->variables.lock_wait_timeout)) + sleep_time= 100; // Start with 0.1 seconds + for (uint i= 0 ; i <= MAX_RETRY_COUNT ; i++) { - /* - Could be a timeout. Downgrade lock to what is was before this function - was called so that this function can be called again - */ - backup_flush_ticket->downgrade_lock(MDL_BACKUP_FLUSH); - DBUG_RETURN(1); + if (!thd->mdl_context.upgrade_shared_lock(backup_flush_ticket, + MDL_BACKUP_WAIT_DDL, + thd->variables.lock_wait_timeout)) + break; + if (thd->get_stmt_da()->sql_errno() != ER_LOCK_DEADLOCK || thd->killed || + i == MAX_RETRY_COUNT) + { + /* + Could be a timeout. Downgrade lock to what is was before this function + was called so that this function can be called again + */ + backup_flush_ticket->downgrade_lock(MDL_BACKUP_FLUSH); + DBUG_RETURN(1); + } + thd->clear_error(); // Forget the DEADLOCK error + my_sleep(sleep_time); + sleep_time*= 5; // Wait a bit longer next time } DBUG_RETURN(0); } -- cgit v1.2.1 From d314bd266491baf0954d13fa51dc22b730a6f4d1 Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 2 Feb 2022 14:09:21 +0200 Subject: MDEV-27442 Wrong result upon query with DISTINCT and EXISTS subquery The problem was that get_best_group_min_max() did not check if fields used by the "group_min_max optimization" where used in sub queries. Because of this, it did not detect that a key (b,a) was used in the WHERE clause for the statement: SELECT DISTINCT b FROM t1 WHERE EXISTS ( SELECT 1 FROM DUAL WHERE a > 1 ). Fixed by also traversing the sub queries when checking if a field is used. This disables group_min_max_optimization for the above query. Reviewer: Sergei Petrunia --- mysql-test/main/group_min_max.result | 16 ++++++++++++++++ mysql-test/main/group_min_max.test | 11 +++++++++++ sql/opt_range.cc | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/group_min_max.result b/mysql-test/main/group_min_max.result index a17f3f09c3b..f264f221ab5 100644 --- a/mysql-test/main/group_min_max.result +++ b/mysql-test/main/group_min_max.result @@ -4027,3 +4027,19 @@ drop table t1; # # End of 10.1 tests # +# +# MDEV-27442 Wrong result upon query with DISTINCT and EXISTS subquery +# +CREATE TABLE t1 (a int, b int, KEY b (b,a)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,100),(2,100),(2,101),(3,102); +# Must not use Using index for group-by +explain SELECT DISTINCT b FROM t1 WHERE EXISTS ( SELECT 1 FROM DUAL WHERE a > 1 ); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index NULL b 10 NULL 4 Using where; Using index +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used +SELECT DISTINCT b FROM t1 WHERE EXISTS ( SELECT 1 FROM DUAL WHERE a > 1 ); +b +100 +101 +102 +DROP TABLE t1; diff --git a/mysql-test/main/group_min_max.test b/mysql-test/main/group_min_max.test index 526552dda92..4622fe473a0 100644 --- a/mysql-test/main/group_min_max.test +++ b/mysql-test/main/group_min_max.test @@ -1689,3 +1689,14 @@ drop table t1; --echo # --echo # End of 10.1 tests --echo # + +--echo # +--echo # MDEV-27442 Wrong result upon query with DISTINCT and EXISTS subquery +--echo # + +CREATE TABLE t1 (a int, b int, KEY b (b,a)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,100),(2,100),(2,101),(3,102); +--echo # Must not use Using index for group-by +explain SELECT DISTINCT b FROM t1 WHERE EXISTS ( SELECT 1 FROM DUAL WHERE a > 1 ); +SELECT DISTINCT b FROM t1 WHERE EXISTS ( SELECT 1 FROM DUAL WHERE a > 1 ); +DROP TABLE t1; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index ae24a257aaa..e3d26896900 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -14000,7 +14000,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) key_part_range[1]= last_part; /* Check if cur_part is referenced in the WHERE clause. */ - if (join->conds->walk(&Item::find_item_in_field_list_processor, 0, + if (join->conds->walk(&Item::find_item_in_field_list_processor, true, key_part_range)) { cause= "keypart reference from where clause"; -- cgit v1.2.1 From 38058c04a4fc021f381f8000e40ed23bd4fb8d75 Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 2 Feb 2022 14:25:25 +0200 Subject: MDEV-26585 Wrong query results when `using index for group-by` The problem was that "group_min_max optimization" does not work if some aggregate functions, like COUNT(*), is used. The function get_best_group_min_max() is using the join->sum_funcs array to check which aggregate functions are used. The bug was that aggregates in HAVING where not yet added to join->sum_funcs at the time get_best_group_min_max() was called. Fixed by populate join->sum_funcs already in prepare, which means that all sum functions will be in join->sum_funcs in get_best_group_min_max(). A benefit of this approach is that we can remove several calls to make_sum_func_list() from the code and simplify the function. I removed some wrong setting of 'sort_and_group'. This variable is set when alloc_group_fields() is called, as part of allocating the cache needed by end_send_group() and does not need to be set by other functions. One problematic thing was that Spider is using *join->sum_funcs to detect at which stage the optimizer is and do internal calculations of aggregate functions. Updating join->sum_funcs early caused Spider to fail when trying to find min/max values in opt_sum_query(). Fixed by temporarily resetting sum_funcs during opt_sum_query(). Reviewer: Sergei Petrunia --- mysql-test/main/group_min_max.result | 21 +++++++++++ mysql-test/main/group_min_max.test | 22 ++++++++++- sql/sql_select.cc | 72 +++++++++++++++++++++--------------- sql/sql_select.h | 14 ++++++- 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/mysql-test/main/group_min_max.result b/mysql-test/main/group_min_max.result index f264f221ab5..4f7c49092d3 100644 --- a/mysql-test/main/group_min_max.result +++ b/mysql-test/main/group_min_max.result @@ -4043,3 +4043,24 @@ b 101 102 DROP TABLE t1; +# +# MDEV-26585 Wrong query results when `using index for group-by` +# +CREATE TABLE `t1` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`owner_id` int(11) DEFAULT NULL, +`foo` tinyint(1) DEFAULT 0, +`whatever` varchar(255) DEFAULT NULL, +PRIMARY KEY (`id`), +KEY `index_t1_on_owner_id_and_foo` (`owner_id`,`foo`) +) engine=InnoDB DEFAULT CHARSET=utf8; +INSERT INTO t1 (owner_id, foo, whatever) +VALUES (1, TRUE, "yello"), (1, FALSE, "yello"), (2, TRUE, "yello"), +(2, TRUE, "yello"), (2, FALSE, "yello"); +EXPLAIN SELECT DISTINCT owner_id FROM t1 WHERE foo = true GROUP BY owner_id HAVING (COUNT(*) = 1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL index_t1_on_owner_id_and_foo 7 NULL 5 Using where; Using index +SELECT DISTINCT owner_id FROM t1 WHERE foo = true GROUP BY owner_id HAVING (COUNT(*) = 1); +owner_id +1 +DROP TABLE t1; diff --git a/mysql-test/main/group_min_max.test b/mysql-test/main/group_min_max.test index 4622fe473a0..b1d912684c6 100644 --- a/mysql-test/main/group_min_max.test +++ b/mysql-test/main/group_min_max.test @@ -4,7 +4,7 @@ # --source include/default_optimizer_switch.inc - +--source include/have_innodb.inc # # TODO: # Add queries with: @@ -1700,3 +1700,23 @@ INSERT INTO t1 VALUES (0,100),(2,100),(2,101),(3,102); explain SELECT DISTINCT b FROM t1 WHERE EXISTS ( SELECT 1 FROM DUAL WHERE a > 1 ); SELECT DISTINCT b FROM t1 WHERE EXISTS ( SELECT 1 FROM DUAL WHERE a > 1 ); DROP TABLE t1; + +--echo # +--echo # MDEV-26585 Wrong query results when `using index for group-by` +--echo # + +CREATE TABLE `t1` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `owner_id` int(11) DEFAULT NULL, + `foo` tinyint(1) DEFAULT 0, + `whatever` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `index_t1_on_owner_id_and_foo` (`owner_id`,`foo`) +) engine=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO t1 (owner_id, foo, whatever) +VALUES (1, TRUE, "yello"), (1, FALSE, "yello"), (2, TRUE, "yello"), + (2, TRUE, "yello"), (2, FALSE, "yello"); +EXPLAIN SELECT DISTINCT owner_id FROM t1 WHERE foo = true GROUP BY owner_id HAVING (COUNT(*) = 1); +SELECT DISTINCT owner_id FROM t1 WHERE foo = true GROUP BY owner_id HAVING (COUNT(*) = 1); +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6fdde5f82dd..fdf58349fc2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1589,7 +1589,8 @@ bool JOIN::prepare_stage2() #endif if (select_lex->olap == ROLLUP_TYPE && rollup_init()) goto err; - if (alloc_func_list()) + if (alloc_func_list() || + make_sum_func_list(all_fields, fields_list, false)) goto err; res= FALSE; @@ -2204,7 +2205,21 @@ JOIN::optimize_inner() If all items were resolved by opt_sum_query, there is no need to open any tables. */ - if ((res=opt_sum_query(thd, select_lex->leaf_tables, all_fields, conds))) + + /* + The following resetting and restoring of sum_funcs is needed to + go around a bug in spider where it assumes that + make_sum_func_list() has not been called yet and do logical + choices based on this if special handling of min/max functions should + be done. We disable this special handling while we are trying to find + out if we can replace MIN/MAX values with constants. + */ + Item_sum **save_func_sums= sum_funcs, *tmp_sum_funcs= 0; + sum_funcs= &tmp_sum_funcs; + res= opt_sum_query(thd, select_lex->leaf_tables, all_fields, conds); + sum_funcs= save_func_sums; + + if (res) { DBUG_ASSERT(res >= 0); if (res == HA_ERR_KEY_NOT_FOUND) @@ -2776,13 +2791,15 @@ int JOIN::optimize_stage2() calc_group_buffer(this, group_list); } - if (test_if_subpart(group_list, order) || - (!group_list && tmp_table_param.sum_func_count)) - { + /* + We can ignore ORDER BY if it's a prefix of the GROUP BY list + (as MariaDB is by default sorting on GROUP BY) or + if there is no GROUP BY and aggregate functions are used + (as the result will only contain one row). + */ + if (order && (test_if_subpart(group_list, order) || + (!group_list && tmp_table_param.sum_func_count))) order=0; - if (is_indexed_agg_distinct(this, NULL)) - sort_and_group= 0; - } // Can't use sort on head table if using join buffering if (full_join || hash_join) @@ -2814,7 +2831,6 @@ int JOIN::optimize_stage2() if (select_lex->have_window_funcs()) simple_order= FALSE; - /* If the hint FORCE INDEX FOR ORDER BY/GROUP BY is used for the table whose columns are required to be returned in a sorted order, then @@ -3540,7 +3556,7 @@ bool JOIN::make_aggr_tables_info() // for the first table if (group_list || tmp_table_param.sum_func_count) { - if (make_sum_func_list(*curr_all_fields, *curr_fields_list, true, true)) + if (make_sum_func_list(*curr_all_fields, *curr_fields_list, true)) DBUG_RETURN(true); if (prepare_sum_aggregators(sum_funcs, !join_tab->is_using_agg_loose_index_scan())) @@ -3650,7 +3666,7 @@ bool JOIN::make_aggr_tables_info() last_tab->all_fields= &tmp_all_fields3; last_tab->fields= &tmp_fields_list3; } - if (make_sum_func_list(*curr_all_fields, *curr_fields_list, true, true)) + if (make_sum_func_list(*curr_all_fields, *curr_fields_list, true)) DBUG_RETURN(true); if (prepare_sum_aggregators(sum_funcs, !join_tab || @@ -3846,8 +3862,6 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List *table_fields, } else { - if (make_sum_func_list(all_fields, fields_list, false)) - goto err; if (prepare_sum_aggregators(sum_funcs, !join_tab->is_using_agg_loose_index_scan())) goto err; @@ -7089,8 +7103,7 @@ void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) Check for the presence of AGGFN(DISTINCT a) queries that may be subject to loose index scan. - - Check if the query is a subject to AGGFN(DISTINCT) using loose index scan + Check if the query is a subject to AGGFN(DISTINCT) using loose index scan (QUICK_GROUP_MIN_MAX_SELECT). Optionally (if out_args is supplied) will push the arguments of AGGFN(DISTINCT) to the list @@ -7123,14 +7136,11 @@ is_indexed_agg_distinct(JOIN *join, List *out_args) Item_sum **sum_item_ptr; bool result= false; - if (join->table_count != 1 || /* reference more than 1 table */ + if (join->table_count != 1 || /* reference more than 1 table */ join->select_distinct || /* or a DISTINCT */ join->select_lex->olap == ROLLUP_TYPE) /* Check (B3) for ROLLUP */ return false; - if (join->make_sum_func_list(join->all_fields, join->fields_list, true)) - return false; - Bitmap first_aggdistinct_fields; bool first_aggdistinct_fields_initialized= false; for (sum_item_ptr= join->sum_funcs; *sum_item_ptr; sum_item_ptr++) @@ -7232,16 +7242,23 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab) while ((item= select_items_it++)) item->walk(&Item::collect_item_field_processor, 0, &indexed_fields); } - else if (join->tmp_table_param.sum_func_count && - is_indexed_agg_distinct(join, &indexed_fields)) + else if (!join->tmp_table_param.sum_func_count || + !is_indexed_agg_distinct(join, &indexed_fields)) { - join->sort_and_group= 1; - } - else + /* + There where no GROUP BY fields and also either no aggregate + functions or not all aggregate functions where used with the + same DISTINCT (or MIN() / MAX() that works similarly). + Nothing to do there. + */ return; + } if (indexed_fields.elements == 0) + { + /* There where no index we could use to satisfy the GROUP BY */ return; + } /* Intersect the keys of all group fields. */ cur_item= indexed_fields_it++; @@ -25692,16 +25709,13 @@ bool JOIN::alloc_func_list() bool JOIN::make_sum_func_list(List &field_list, List &send_result_set_metadata, - bool before_group_by, bool recompute) + bool before_group_by) { List_iterator_fast it(field_list); Item_sum **func; Item *item; DBUG_ENTER("make_sum_func_list"); - if (*sum_funcs && !recompute) - DBUG_RETURN(FALSE); /* We have already initialized sum_funcs. */ - func= sum_funcs; while ((item=it++)) { @@ -25848,7 +25862,7 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, Change all funcs to be fields in tmp table. @param thd THD pointer - @param ref_pointer_array array of pointers to top elements of filed list + @param ref_pointer_array array of pointers to top elements of field list @param res_selected_fields new list of items of select item list @param res_all_fields new list of all items @param elements number of elements in select item list diff --git a/sql/sql_select.h b/sql/sql_select.h index 29e42ff8ef8..eea1ca9becd 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1196,7 +1196,17 @@ public: Indicates that grouping will be performed on the result set during query execution. This field belongs to query execution. - @see make_group_fields, alloc_group_fields, JOIN::exec + If 'sort_and_group' is set, then the optimizer is going to use on of + the following algorithms to resolve GROUP BY. + + - If one table, sort the table and then calculate groups on the fly. + - If more than one table, create a temporary table to hold the join, + sort it and then resolve group by on the fly. + + The 'on the fly' calculation is done in end_send_group() + + @see make_group_fields, alloc_group_fields, JOIN::exec, + setup_end_select_func */ bool sort_and_group; bool first_record,full_join, no_field_update; @@ -1654,7 +1664,7 @@ public: bool make_range_rowid_filters(); bool init_range_rowid_filters(); bool make_sum_func_list(List &all_fields, List &send_fields, - bool before_group_by, bool recompute= FALSE); + bool before_group_by); /// Initialzes a slice, see comments for ref_ptrs above. Ref_ptr_array ref_ptr_array_slice(size_t slice_num) -- cgit v1.2.1 From f7704d74cb99f3b5cdd106ef76bd9cd8cef6eb8a Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Tue, 8 Feb 2022 17:31:40 -0500 Subject: bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c986c33613e..9d94c079411 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=14 +MYSQL_VERSION_PATCH=15 SERVER_MATURITY=stable -- cgit v1.2.1 From fa73117bf8d3a271ca724fbec08efede9387d7d7 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Tue, 8 Feb 2022 17:51:29 -0500 Subject: bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index cbe56835de8..38d0bcd7a05 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=6 -MYSQL_VERSION_PATCH=6 +MYSQL_VERSION_PATCH=7 SERVER_MATURITY=stable -- cgit v1.2.1 From 5c46751f238ee8dcef1e718ac5f63952bff5d09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 9 Feb 2022 08:36:41 +0200 Subject: MDEV-27734 Set innodb_change_buffering=none by default The aim of the InnoDB change buffer is to avoid delays when a leaf page of a secondary index is not present in the buffer pool, and a record needs to be inserted, delete-marked, or purged. Instead of reading the page into the buffer pool for making such a modification, we may insert a record to the change buffer (a special index tree in the InnoDB system tablespace). The buffered changes are guaranteed to be merged if the index page actually needs to be read later. The change buffer could be useful when the database is stored on a rotational medium (hard disk) where random seeks are slower than sequential reads or writes. Obviously, the change buffer will cause write amplification, due to potentially large amount of metadata that is being written to the change buffer. We will have to write redo log records for modifying the change buffer tree as well as the user tablespace. Furthermore, in the user tablespace, we must maintain a change buffer bitmap page that uses 2 bits for estimating the amount of free space in pages, and 1 bit to specify whether buffered changes exist. This bitmap needs to be updated on every operation, which could reduce performance. Even if the change buffer were free of bugs such as MDEV-24449 (potentially causing the corruption of any page in the system tablespace) or MDEV-26977 (corruption of secondary indexes due to a currently unknown reason), it will make diagnosis of other data corruption harder. Because of all this, it is best to disable the change buffer by default. --- mysql-test/suite/innodb/r/ibuf_not_empty.result | 1 + .../suite/innodb/r/innodb-change-buffer-recovery.result | 1 + mysql-test/suite/innodb/t/ibuf_not_empty.test | 1 + .../suite/innodb/t/innodb-change-buffer-recovery.test | 1 + .../suite/sys_vars/r/innodb_change_buffering_basic.result | 14 +++++++------- mysql-test/suite/sys_vars/r/sysvars_innodb.result | 2 +- storage/innobase/handler/ha_innodb.cc | 2 +- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/innodb/r/ibuf_not_empty.result b/mysql-test/suite/innodb/r/ibuf_not_empty.result index d1b8203b063..f2da89990b0 100644 --- a/mysql-test/suite/innodb/r/ibuf_not_empty.result +++ b/mysql-test/suite/innodb/r/ibuf_not_empty.result @@ -5,6 +5,7 @@ c INT, INDEX(b)) ENGINE=InnoDB STATS_PERSISTENT=0; SET GLOBAL innodb_change_buffering_debug = 1; +SET GLOBAL innodb_change_buffering=all; INSERT INTO t1 SELECT 0,'x',1 FROM seq_1_to_1024; # restart: --innodb-force-recovery=6 --innodb-change-buffer-dump check table t1; diff --git a/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result b/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result index 678c8c67be5..f676d15b134 100644 --- a/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result +++ b/mysql-test/suite/innodb/r/innodb-change-buffer-recovery.result @@ -13,6 +13,7 @@ c INT, INDEX(b)) ENGINE=InnoDB STATS_PERSISTENT=0; SET GLOBAL innodb_change_buffering_debug = 1; +SET GLOBAL innodb_change_buffering = all; INSERT INTO t1 SELECT 0,'x',1 FROM seq_1_to_8192; BEGIN; SELECT b FROM t1 LIMIT 3; diff --git a/mysql-test/suite/innodb/t/ibuf_not_empty.test b/mysql-test/suite/innodb/t/ibuf_not_empty.test index 545a78c887e..96ceb81ac00 100644 --- a/mysql-test/suite/innodb/t/ibuf_not_empty.test +++ b/mysql-test/suite/innodb/t/ibuf_not_empty.test @@ -24,6 +24,7 @@ ENGINE=InnoDB STATS_PERSISTENT=0; # change buffering is possible, so that the change buffer will be used # whenever possible. SET GLOBAL innodb_change_buffering_debug = 1; +SET GLOBAL innodb_change_buffering=all; # Create enough rows for the table, so that the change buffer will be # used for modifying the secondary index page. There must be multiple diff --git a/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test b/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test index a12ca43cec1..129037e783b 100644 --- a/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test +++ b/mysql-test/suite/innodb/t/innodb-change-buffer-recovery.test @@ -33,6 +33,7 @@ ENGINE=InnoDB STATS_PERSISTENT=0; # change buffering is possible, so that the change buffer will be used # whenever possible. SET GLOBAL innodb_change_buffering_debug = 1; +SET GLOBAL innodb_change_buffering = all; let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err; # Create enough rows for the table, so that the change buffer will be diff --git a/mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result b/mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result index 92e22c6aa34..c11f4ee617c 100644 --- a/mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result +++ b/mysql-test/suite/sys_vars/r/innodb_change_buffering_basic.result @@ -1,28 +1,28 @@ SET @start_global_value = @@global.innodb_change_buffering; SELECT @start_global_value; @start_global_value -all +none Valid values are 'all', 'deletes', 'changes', 'inserts', 'none', 'purges' select @@global.innodb_change_buffering in ('all', 'deletes', 'changes', 'inserts', 'none', 'purges'); @@global.innodb_change_buffering in ('all', 'deletes', 'changes', 'inserts', 'none', 'purges') 1 select @@global.innodb_change_buffering; @@global.innodb_change_buffering -all +none select @@session.innodb_change_buffering; ERROR HY000: Variable 'innodb_change_buffering' is a GLOBAL variable show global variables like 'innodb_change_buffering'; Variable_name Value -innodb_change_buffering all +innodb_change_buffering none show session variables like 'innodb_change_buffering'; Variable_name Value -innodb_change_buffering all +innodb_change_buffering none select * from information_schema.global_variables where variable_name='innodb_change_buffering'; VARIABLE_NAME VARIABLE_VALUE -INNODB_CHANGE_BUFFERING all +INNODB_CHANGE_BUFFERING none select * from information_schema.session_variables where variable_name='innodb_change_buffering'; VARIABLE_NAME VARIABLE_VALUE -INNODB_CHANGE_BUFFERING all +INNODB_CHANGE_BUFFERING none set global innodb_change_buffering='none'; select @@global.innodb_change_buffering; @@global.innodb_change_buffering @@ -62,4 +62,4 @@ ERROR 42000: Variable 'innodb_change_buffering' can't be set to the value of 'so SET @@global.innodb_change_buffering = @start_global_value; SELECT @@global.innodb_change_buffering; @@global.innodb_change_buffering -all +none diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index ecd107f67c8..c333b56fd2e 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -311,7 +311,7 @@ READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME INNODB_CHANGE_BUFFERING SESSION_VALUE NULL -DEFAULT_VALUE all +DEFAULT_VALUE none VARIABLE_SCOPE GLOBAL VARIABLE_TYPE ENUM VARIABLE_COMMENT Buffer changes to secondary indexes. diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 646e2619348..2962e6a4a22 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -19623,7 +19623,7 @@ static MYSQL_SYSVAR_BOOL(numa_interleave, srv_numa_interleave, static MYSQL_SYSVAR_ENUM(change_buffering, innodb_change_buffering, PLUGIN_VAR_RQCMDARG, "Buffer changes to secondary indexes.", - NULL, NULL, IBUF_USE_ALL, &innodb_change_buffering_typelib); + NULL, NULL, IBUF_USE_NONE, &innodb_change_buffering_typelib); static MYSQL_SYSVAR_UINT(change_buffer_max_size, srv_change_buffer_max_size, -- cgit v1.2.1 From fd101daa84ec5f70f09c48e3fb7a4ce8f6d26edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 9 Feb 2022 15:10:10 +0200 Subject: MDEV-27716 mtr_t::commit() acquires log_sys.mutex when writing no log mtr_t::is_block_dirtied(), mtr_t::memo_push(): Never set m_made_dirty for pages of the temporary tablespace. Ever since commit 5eb539555b36a60944eefeb84d5d6d436ba61e63 we never add those pages to buf_pool.flush_list. mtr_t::commit(): Implement part of mtr_t::prepare_write() here, and avoid acquiring log_sys.mutex if no log is written. During IMPORT TABLESPACE fixup, we do not write log, but we must add pages to buf_pool.flush_list and for that, be prepared to acquire log_sys.flush_order_mutex. mtr_t::do_write(): Replaces mtr_t::prepare_write(). --- storage/innobase/include/mtr0mtr.h | 6 ++--- storage/innobase/include/mtr0mtr.inl | 9 +++---- storage/innobase/mtr/mtr0mtr.cc | 46 ++++++++++++++++++------------------ 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 3e72bf9acd7..3f9777ad225 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -623,9 +623,9 @@ private: @param type extended record subtype; @see mrec_ext_t */ inline void log_write_extended(const buf_block_t &block, byte type); - /** Prepare to write the mini-transaction log to the redo log buffer. - @return number of bytes to write in finish_write() */ - inline ulint prepare_write(); + /** Append the redo log records to the redo log buffer. + @return {start_lsn,flush_ahead} */ + std::pair do_write(); /** Append the redo log records to the redo log buffer. @param len number of bytes to write diff --git a/storage/innobase/include/mtr0mtr.inl b/storage/innobase/include/mtr0mtr.inl index 48bdb9bb6d1..bc2986503f9 100644 --- a/storage/innobase/include/mtr0mtr.inl +++ b/storage/innobase/include/mtr0mtr.inl @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2021, MariaDB Corporation. +Copyright (c) 2017, 2022, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -32,7 +32,8 @@ inline bool mtr_t::is_block_dirtied(const buf_block_t *block) { ut_ad(block->page.state() == BUF_BLOCK_FILE_PAGE); ut_ad(block->page.buf_fix_count()); - return block->page.oldest_modification() <= 1; + return block->page.oldest_modification() <= 1 && + block->page.id().space() < SRV_TMP_SPACE_ID; } /** @@ -51,8 +52,8 @@ mtr_t::memo_push(void* object, mtr_memo_type_t type) grab log_sys.flush_order_mutex at mtr_t::commit() so that we can insert the dirtied page into the flush list. */ - if ((type == MTR_MEMO_PAGE_X_FIX || type == MTR_MEMO_PAGE_SX_FIX) - && !m_made_dirty) { + if (!m_made_dirty + && (type == MTR_MEMO_PAGE_X_FIX || type == MTR_MEMO_PAGE_SX_FIX)) { m_made_dirty = is_block_dirtied( reinterpret_cast(object)); diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index a8dffa48c35..ca97a9e77e4 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -404,18 +404,27 @@ void mtr_t::commit() std::pair lsns; - if (const ulint len= prepare_write()) - lsns= finish_write(len); - else - lsns= { m_commit_lsn, PAGE_FLUSH_NO }; + if (UNIV_LIKELY(m_log_mode == MTR_LOG_ALL)) + { + lsns= do_write(); - if (m_made_dirty) - mysql_mutex_lock(&log_sys.flush_order_mutex); + if (m_made_dirty) + mysql_mutex_lock(&log_sys.flush_order_mutex); - /* It is now safe to release the log mutex because the - flush_order mutex will ensure that we are the first one - to insert into the flush list. */ - mysql_mutex_unlock(&log_sys.mutex); + /* It is now safe to release log_sys.mutex because the + buf_pool.flush_order_mutex will ensure that we are the first one + to insert into buf_pool.flush_list. */ + mysql_mutex_unlock(&log_sys.mutex); + } + else + { + ut_ad(m_log_mode == MTR_LOG_NO_REDO); + ut_ad(m_log.size() == 0); + m_commit_lsn= log_sys.get_lsn(); + lsns= { m_commit_lsn, PAGE_FLUSH_NO }; + if (UNIV_UNLIKELY(m_made_dirty)) /* This should be IMPORT TABLESPACE */ + mysql_mutex_lock(&log_sys.flush_order_mutex); + } if (m_freed_pages) { @@ -517,7 +526,7 @@ void mtr_t::commit_shrink(fil_space_t &space) log_write_and_flush_prepare(); - const lsn_t start_lsn= finish_write(prepare_write()).first; + const lsn_t start_lsn= do_write().first; mysql_mutex_lock(&log_sys.flush_order_mutex); /* Durably write the reduced FSP_SIZE before truncating the data file. */ @@ -924,19 +933,10 @@ struct mtr_write_log } }; -/** Prepare to write the mini-transaction log to the redo log buffer. -@return number of bytes to write in finish_write() */ -inline ulint mtr_t::prepare_write() +std::pair mtr_t::do_write() { ut_ad(!recv_no_log_write); - - if (UNIV_UNLIKELY(m_log_mode != MTR_LOG_ALL)) { - ut_ad(m_log_mode == MTR_LOG_NO_REDO); - ut_ad(m_log.size() == 0); - mysql_mutex_lock(&log_sys.mutex); - m_commit_lsn = log_sys.get_lsn(); - return 0; - } + ut_ad(m_log_mode == MTR_LOG_ALL); ulint len = m_log.size(); ut_ad(len > 0); @@ -968,7 +968,7 @@ inline ulint mtr_t::prepare_write() /* check and attempt a checkpoint if exceeding capacity */ log_margin_checkpoint_age(len); - return(len); + return finish_write(len); } /** Append the redo log records to the redo log buffer. -- cgit v1.2.1