From 6e71dde8b8f70087f06beb7d6fa172a7580a80c2 Mon Sep 17 00:00:00 2001 From: Ian Gilfillan Date: Thu, 4 Apr 2019 15:33:20 +0200 Subject: [PATCH 01/25] MDEV-19169: Add --defaults-group-suffix option to mysql_install_db man page --- man/mysql_install_db.1 | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/man/mysql_install_db.1 b/man/mysql_install_db.1 index 9fb775f2099..636c59740a5 100644 --- a/man/mysql_install_db.1 +++ b/man/mysql_install_db.1 @@ -1,6 +1,6 @@ '\" t .\" -.TH "\FBMYSQL_INSTALL_DB\" "1" "14/12/2015" "MariaDB 10\&.1" "MariaDB Database System" +.TH "\FBMYSQL_INSTALL_DB\FR" "1" "4 April 2019" "MariaDB 10\&.1" "MariaDB Database System" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -198,6 +198,21 @@ Must be given as first option\&. .sp -1 .IP \(bu 2.3 .\} +.\" mysql_install_db: defaults-group-suffix option +.\" defaults-group-suffix option: mysql_install_db +\fB\-\-defaults\-group\-suffix=\fR\fB\fIname\fR\fR +.sp +In addition to the given groups, also read groups with this suffix\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} .\" mysql_install_db: force option .\" force option: mysql_install_db \fB\-\-force\fR @@ -354,7 +369,7 @@ For internal use\&. This option is used for creating Windows distributions\&. .SH "COPYRIGHT" .br .PP -Copyright 2007-2008 MySQL AB, 2008-2010 Sun Microsystems, Inc., 2010-2015 MariaDB Foundation +Copyright 2007-2008 MySQL AB, 2008-2010 Sun Microsystems, Inc., 2010-2019 MariaDB Foundation .PP This documentation is free software; you can redistribute it and/or modify it only under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. .PP From 37bf7b195c7a42e7c4bbc05c877147789f7cba29 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 4 Apr 2019 15:27:16 +0100 Subject: [PATCH 02/25] MDEV-17610 Unexpected connection abort after certain operations from within stored procedure Always set SERVER_MORE_RESULTS_EXIST when executing stored procedure. statements If statements produce a result, EOF packet needs this flag (SP ends with an OK packet). IF statetement does not produce a result, affected rows count are part of the final OK packet. --- mysql-test/r/sp.result | 16 ++++++++++++++++ mysql-test/t/sp.test | 9 +++++++++ sql/sp_head.cc | 1 + sql/sql_parse.cc | 15 +++++++-------- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index cd818ed22b8..e39754159fb 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -7635,6 +7635,22 @@ c1 c2 count(c3) 2012-03-01 02:00:00 3 1 DROP PROCEDURE p1; # End of 5.5 test +CREATE PROCEDURE sp() ALTER TABLE non_existing_table OPTIMIZE PARTITION p0; +CALL sp; +Table Op Msg_type Msg_text +test.non_existing_table optimize Error Table 'test.non_existing_table' doesn't exist +test.non_existing_table optimize status Operation failed +SELECT 1; +1 +1 +DROP PROCEDURE sp; +CREATE PROCEDURE sp() SHOW USER_STATISTICS; +CALL sp; +User Total_connections Concurrent_connections Connected_time Busy_time Cpu_time Bytes_received Bytes_sent Binlog_bytes_written Rows_read Rows_sent Rows_deleted Rows_inserted Rows_updated Select_commands Update_commands Other_commands Commit_transactions Rollback_transactions Denied_connections Lost_connections Access_denied Empty_queries Total_ssl_connections Max_statement_time_exceeded +SELECT 1; +1 +1 +DROP PROCEDURE sp; # # Bug#12663165 SP DEAD CODE REMOVAL DOESN'T UNDERSTAND CONTINUE HANDLERS # diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index a24da76602d..0e95fb5c271 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -9073,6 +9073,15 @@ DROP PROCEDURE p1; --echo # End of 5.5 test +#MDEV-17610 +CREATE PROCEDURE sp() ALTER TABLE non_existing_table OPTIMIZE PARTITION p0; +CALL sp; +SELECT 1; +DROP PROCEDURE sp; +CREATE PROCEDURE sp() SHOW USER_STATISTICS; +CALL sp; +SELECT 1; +DROP PROCEDURE sp; --echo # --echo # Bug#12663165 SP DEAD CODE REMOVAL DOESN'T UNDERSTAND CONTINUE HANDLERS diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 4e886e29a63..f26115efd8b 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -243,6 +243,7 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_SHOW_EXPLAIN: case SQLCOM_SHOW_FIELDS: case SQLCOM_SHOW_FUNC_CODE: + case SQLCOM_SHOW_GENERIC: case SQLCOM_SHOW_GRANTS: case SQLCOM_SHOW_ENGINE_STATUS: case SQLCOM_SHOW_ENGINE_LOGS: diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1f060305d4f..6649c60f827 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2442,15 +2442,14 @@ static bool do_execute_sp(THD *thd, sp_head *sp) my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); return 1; } - /* - If SERVER_MORE_RESULTS_EXISTS is not set, - then remember that it should be cleared - */ - bits_to_be_cleared= (~thd->server_status & - SERVER_MORE_RESULTS_EXISTS); - thd->server_status|= SERVER_MORE_RESULTS_EXISTS; } - + /* + If SERVER_MORE_RESULTS_EXISTS is not set, + then remember that it should be cleared + */ + bits_to_be_cleared= (~thd->server_status & + SERVER_MORE_RESULTS_EXISTS); + thd->server_status|= SERVER_MORE_RESULTS_EXISTS; ha_rows select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; From 370886a9e247d8151dfe340a5e7b8b6929ccfacb Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 4 Apr 2019 13:10:13 +0100 Subject: [PATCH 03/25] MDEV-17610 Unexpected connection abort after certain operations from within stored procedure Always set SERVER_MORE_RESULTS_EXIST when executing stored procedure statements If statements produce a result, EOF packet needs this flag (SP ends with an OK packet). IF statetement does not produce a result, affected rows count are part of the final OK packet. --- mysql-test/r/sp.result | 16 ++++++++++++++++ mysql-test/t/sp.test | 10 ++++++++++ sql/sql_parse.cc | 15 ++++++++------- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 52b52fbbd24..dcf13e8bc4c 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -8106,3 +8106,19 @@ DROP PROCEDURE sp; DROP VIEW v1; DROP TABLE t1, t2; # End of 5.5 test +CREATE PROCEDURE sp() ALTER TABLE non_existing_table OPTIMIZE PARTITION p0; +CALL sp; +Table Op Msg_type Msg_text +test.non_existing_table optimize Error Table 'test.non_existing_table' doesn't exist +test.non_existing_table optimize status Operation failed +SELECT 1; +1 +1 +DROP PROCEDURE sp; +CREATE PROCEDURE sp() SHOW USER_STATISTICS; +CALL sp; +User Total_connections Concurrent_connections Connected_time Busy_time Cpu_time Bytes_received Bytes_sent Binlog_bytes_written Rows_read Rows_sent Rows_deleted Rows_inserted Rows_updated Select_commands Update_commands Other_commands Commit_transactions Rollback_transactions Denied_connections Lost_connections Access_denied Empty_queries +SELECT 1; +1 +1 +DROP PROCEDURE sp; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index fb9da936fdb..58bffab462d 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -9416,3 +9416,13 @@ DROP VIEW v1; DROP TABLE t1, t2; --echo # End of 5.5 test + +#MDEV-17610 +CREATE PROCEDURE sp() ALTER TABLE non_existing_table OPTIMIZE PARTITION p0; +CALL sp; +SELECT 1; +DROP PROCEDURE sp; +CREATE PROCEDURE sp() SHOW USER_STATISTICS; +CALL sp; +SELECT 1; +DROP PROCEDURE sp; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 95243ead2fe..bb53c116b0c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4088,15 +4088,16 @@ create_sp_error: my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); goto error; } - /* - If SERVER_MORE_RESULTS_EXISTS is not set, - then remember that it should be cleared - */ - bits_to_be_cleared= (~thd->server_status & - SERVER_MORE_RESULTS_EXISTS); - thd->server_status|= SERVER_MORE_RESULTS_EXISTS; } + /* + If SERVER_MORE_RESULTS_EXISTS is not set, + then remember that it should be cleared + */ + bits_to_be_cleared= (~thd->server_status & + SERVER_MORE_RESULTS_EXISTS); + thd->server_status|= SERVER_MORE_RESULTS_EXISTS; + if (check_routine_access(thd, EXECUTE_ACL, sp->m_db.str, sp->m_name.str, TRUE, FALSE)) { From 812ac2bb857877efc6973e275353ecdfcd8aeb35 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 4 Apr 2019 22:19:56 +0300 Subject: [PATCH 04/25] MDEV-19172 Reorder fields in PFS_events and PFS_events_waits to speed up memcpy() before: (gdb) p sizeof(PFS_events_waits) $1 = 184 after: (gdb) p sizeof(PFS_events_waits) $1 = 160 no functional changes --- storage/perfschema/pfs_events.h | 12 +++++----- storage/perfschema/pfs_events_waits.h | 34 +++++++++++++-------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/storage/perfschema/pfs_events.h b/storage/perfschema/pfs_events.h index 97fb7e08d63..905d6f8590f 100644 --- a/storage/perfschema/pfs_events.h +++ b/storage/perfschema/pfs_events.h @@ -34,14 +34,8 @@ struct PFS_events ulonglong m_event_id; /** END_EVENT_ID. */ ulonglong m_end_event_id; - /** (EVENT_TYPE) */ - enum_event_type m_event_type; /** NESTING_EVENT_ID. */ ulonglong m_nesting_event_id; - /** NESTING_EVENT_TYPE */ - enum_event_type m_nesting_event_type; - /** Instrument metadata. */ - PFS_instr_class *m_class; /** Timer start. This member is populated only if m_class->m_timed is true. @@ -52,8 +46,14 @@ struct PFS_events This member is populated only if m_class->m_timed is true. */ ulonglong m_timer_end; + /** Instrument metadata. */ + PFS_instr_class *m_class; /** Location of the instrumentation in the source code (file name). */ const char *m_source_file; + /** (EVENT_TYPE) */ + enum_event_type m_event_type; + /** NESTING_EVENT_TYPE */ + enum_event_type m_nesting_event_type; /** Location of the instrumentation in the source code (line number). */ uint m_source_line; }; diff --git a/storage/perfschema/pfs_events_waits.h b/storage/perfschema/pfs_events_waits.h index a7f7a095b9f..52e2ef05ad3 100644 --- a/storage/perfschema/pfs_events_waits.h +++ b/storage/perfschema/pfs_events_waits.h @@ -54,6 +54,23 @@ enum events_waits_class /** A wait event record. */ struct PFS_events_waits : public PFS_events { + /** Executing thread. */ + PFS_thread *m_thread; + /** Table share, for table operations only. */ + PFS_table_share *m_weak_table_share; + /** File, for file operations only. */ + PFS_file *m_weak_file; + /** Address in memory of the object instance waited on. */ + const void *m_object_instance_addr; + /** Socket, for socket operations only. */ + PFS_socket *m_weak_socket; + /** + Number of bytes read/written. + This member is populated for file READ/WRITE operations only. + */ + size_t m_number_of_bytes; + /** Flags */ + ulong m_flags; /** The type of wait. Readers: @@ -66,34 +83,17 @@ struct PFS_events_waits : public PFS_events - TRUNCATE EVENTS_WAITS_HISTORY_LONG */ events_waits_class m_wait_class; - /** Executing thread. */ - PFS_thread *m_thread; /** Object type */ enum_object_type m_object_type; - /** Table share, for table operations only. */ - PFS_table_share *m_weak_table_share; - /** File, for file operations only. */ - PFS_file *m_weak_file; - /** Socket, for socket operations only. */ - PFS_socket *m_weak_socket; /** For weak pointers, target object version. */ uint32 m_weak_version; - /** Address in memory of the object instance waited on. */ - const void *m_object_instance_addr; /** Operation performed. */ enum_operation_type m_operation; - /** - Number of bytes read/written. - This member is populated for file READ/WRITE operations only. - */ - size_t m_number_of_bytes; /** Index used. This member is populated for TABLE IO operations only. */ uint m_index; - /** Flags */ - ulong m_flags; }; /** TIMED bit in the state flags bitfield. */ From 409dddf6958736d65ee77aa9b469f8b807da3bb1 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Thu, 11 Apr 2019 13:05:01 +0530 Subject: [PATCH 05/25] MDEV-18300: ASAN error in Field_blob::get_key_image upon UPDATE with subquery For single table updates and multi-table updates , engine independent statistics were not being read even if the statistics were collected. Fixed it, so when the optimizer_use_condition_selectivity > 2 then we would read the available statistics for update queries. --- mysql-test/r/update_innodb.result | 24 ++++++++++++++++++++++++ mysql-test/t/update_innodb.test | 29 +++++++++++++++++++++++++++++ sql/sql_statistics.cc | 1 + sql/sql_update.cc | 3 +++ 4 files changed, 57 insertions(+) diff --git a/mysql-test/r/update_innodb.result b/mysql-test/r/update_innodb.result index 0a85c6dab3e..695561122f0 100644 --- a/mysql-test/r/update_innodb.result +++ b/mysql-test/r/update_innodb.result @@ -65,3 +65,27 @@ SELECT * FROM t1; a_id b_id c_id 1 NULL NULL drop table t1,t2; +# +# MDEV-18300: ASAN error in Field_blob::get_key_image upon UPDATE with subquery +# +set @save_optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity; +set @save_use_stat_tables= @@use_stat_tables; +set use_stat_tables=preferably; +set optimizer_use_condition_selectivity=4; +CREATE TABLE t1 (a INT, b CHAR(8)) ENGINE=InnoDB; +insert into t1 values (1,'foo'),(2, 'abc'); +CREATE TABLE t2 (c CHAR(8), d BLOB) ENGINE=InnoDB; +insert into t2 values ('abc', 'foo'),('edf', 'food'); +ANALYZE TABLE t1,t2; +UPDATE t1 SET a = 1 WHERE b = ( SELECT c FROM t2 WHERE d = 'foo' ); +SELECT * FROM t1; +a b +1 foo +1 abc +DROP TABLE t1, t2; +create table t1 (a int not null, b int, c int) engine=InnoDB; +create table t2 (d int, e int) engine=InnoDB; +update t1, t2 set a=NULL, b=2, c=NULL where b=d and e=200; +drop table t1,t2; +set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +set @@use_stat_tables= @save_use_stat_tables; diff --git a/mysql-test/t/update_innodb.test b/mysql-test/t/update_innodb.test index acc8aceab00..a29dd071cf8 100644 --- a/mysql-test/t/update_innodb.test +++ b/mysql-test/t/update_innodb.test @@ -75,3 +75,32 @@ SELECT t2.b_id FROM t1,t2 WHERE t2.c_id = t1.c_id; UPDATE t1 SET b_id = (SELECT t2.b_id FROM t2 t2 WHERE t2.c_id = t1.c_id); SELECT * FROM t1; drop table t1,t2; + +--echo # +--echo # MDEV-18300: ASAN error in Field_blob::get_key_image upon UPDATE with subquery +--echo # + +set @save_optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity; +set @save_use_stat_tables= @@use_stat_tables; +set use_stat_tables=preferably; +set optimizer_use_condition_selectivity=4; + +CREATE TABLE t1 (a INT, b CHAR(8)) ENGINE=InnoDB; +insert into t1 values (1,'foo'),(2, 'abc'); +CREATE TABLE t2 (c CHAR(8), d BLOB) ENGINE=InnoDB; +insert into t2 values ('abc', 'foo'),('edf', 'food'); + +--disable_result_log +ANALYZE TABLE t1,t2; +--enable_result_log +UPDATE t1 SET a = 1 WHERE b = ( SELECT c FROM t2 WHERE d = 'foo' ); +SELECT * FROM t1; +DROP TABLE t1, t2; + +create table t1 (a int not null, b int, c int) engine=InnoDB; +create table t2 (d int, e int) engine=InnoDB; +update t1, t2 set a=NULL, b=2, c=NULL where b=d and e=200; +drop table t1,t2; + +set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +set @@use_stat_tables= @save_use_stat_tables; diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index f4dcafdb7c3..d3a2094e272 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -4071,6 +4071,7 @@ bool is_eits_usable(Field *field) partition list of a table. We assume the selecticivity for such columns would be handled during partition pruning. */ + DBUG_ASSERT(field->table->stats_is_read); Column_statistics* col_stats= field->read_stats; return col_stats && !col_stats->no_stat_values_provided() && //(1) field->type() != MYSQL_TYPE_GEOMETRY && //(2) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 960b5cbccc5..78aa059f64f 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -301,6 +301,8 @@ int mysql_update(THD *thd, if (lock_tables(thd, table_list, table_count, 0)) DBUG_RETURN(1); + (void) read_statistics_for_tables_if_needed(thd, table_list); + if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) DBUG_RETURN(1); if (table_list->handle_derived(thd->lex, DT_PREPARE)) @@ -1540,6 +1542,7 @@ int mysql_multi_update_prepare(THD *thd) { DBUG_RETURN(TRUE); } + (void) read_statistics_for_tables_if_needed(thd, table_list); /* @todo: downgrade the metadata locks here. */ /* From 0bb924e18c338fa2dc901041aad09998b399efc1 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 17 Apr 2019 20:21:11 +0400 Subject: [PATCH 06/25] MDEV-17830 Server crashes in Item_null_result::field_type upon SELECT with CHARSET(date) and ROLLUP --- mysql-test/r/olap.result | 14 ++++++++++++++ mysql-test/t/olap.test | 15 +++++++++++++++ sql/item.h | 10 ++++++++++ 3 files changed, 39 insertions(+) diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index 24140583d13..84c54c0c3c7 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -786,5 +786,19 @@ t COUNT(*) 12:12:13 1 DROP TABLE t1; # +# MDEV-17830 Server crashes in Item_null_result::field_type upon SELECT with CHARSET(date) and ROLLUP +# +# Note, returning "latin1" in the first row vs "binary" in the second row is wrong here. +# Both lines should return equal values. +# The point in this test is to make sure it does not crash. +# Bad result will be fixed in a later version. +CREATE TABLE t (d DATE) ENGINE=MyISAM; +INSERT INTO t VALUES ('2018-12-12'); +SELECT CHARSET(d) AS f FROM t GROUP BY d WITH ROLLUP; +f +latin1 +binary +DROP TABLE t; +# # End of 10.1 tests # diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test index 4a61cebdc0d..bb7806969d3 100644 --- a/mysql-test/t/olap.test +++ b/mysql-test/t/olap.test @@ -424,6 +424,21 @@ SELECT t, COUNT(*) FROM t1 GROUP BY t WITH ROLLUP HAVING t > '00:00:00'; DROP TABLE t1; +--echo # +--echo # MDEV-17830 Server crashes in Item_null_result::field_type upon SELECT with CHARSET(date) and ROLLUP +--echo # + +--echo # Note, returning "latin1" in the first row vs "binary" in the second row is wrong here. +--echo # Both lines should return equal values. +--echo # The point in this test is to make sure it does not crash. +--echo # Bad result will be fixed in a later version. + +CREATE TABLE t (d DATE) ENGINE=MyISAM; +INSERT INTO t VALUES ('2018-12-12'); +SELECT CHARSET(d) AS f FROM t GROUP BY d WITH ROLLUP; +DROP TABLE t; + + --echo # --echo # End of 10.1 tests --echo # diff --git a/sql/item.h b/sql/item.h index 75ebcdb624c..4b93d3f9164 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2618,6 +2618,10 @@ public: { return result_field->type(); } + CHARSET_INFO *charset_for_protocol(void) const + { + return collation.collation; + } #else const Type_handler *type_handler() const { @@ -3942,6 +3946,12 @@ public: void save_org_in_field(Field *field, fast_field_copier optimizer_data); fast_field_copier setup_fast_field_copier(Field *field) { return (*ref)->setup_fast_field_copier(field); } +#if MARIADB_VERSION_ID < 100300 + CHARSET_INFO *charset_for_protocol(void) const + { + return (*ref)->charset_for_protocol(); + } +#endif enum Item_result result_type () const { return (*ref)->result_type(); } enum_field_types field_type() const { return (*ref)->field_type(); } Field *get_tmp_table_field() From 323e6cd74ce76c7811835bed640a2934e1d77f1b Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 18 Apr 2019 08:34:08 +0400 Subject: [PATCH 07/25] MDEV-18092 Query with the table I_S.PARAMETERS stop working after a package is created This patch was originally made by Anel Husakovic. Skip `PACKAGE` and `PACKAGE BODY` records quickly. These stored objects do not have any parameters or return values (only procedures and functions have). So no needs to build a `CREATE` statement (in `Sp_handler::sp_load_for_information_schema()`) and parse it: this won't give us any data useful for `INFORMATION_SCHEMA.PARAMETERS`. --- .../r/information_schema_parameters.result | 38 +++++++++++++++++++ .../t/information_schema_parameters.test | 33 ++++++++++++++++ sql/sql_show.cc | 3 +- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/compat/oracle/r/information_schema_parameters.result b/mysql-test/suite/compat/oracle/r/information_schema_parameters.result index e1ed53c39de..f7e9bfcafb9 100644 --- a/mysql-test/suite/compat/oracle/r/information_schema_parameters.result +++ b/mysql-test/suite/compat/oracle/r/information_schema_parameters.result @@ -814,3 +814,41 @@ DTD_IDENTIFIER ROW ROUTINE_TYPE FUNCTION -------- -------- DROP FUNCTION f1; +# +# MDEV 18092 Query with the table I_S.PARAMETERS stop working +# after a package is created +# +SET sql_mode=ORACLE; +CREATE DATABASE db1_mdev18092; +USE db1_mdev18092; +CREATE PROCEDURE p1(a INT) +AS BEGIN +NULL; +END; +$$ +CREATE OR REPLACE PACKAGE employee_tools AS +FUNCTION getSalary(eid INT) RETURN DECIMAL(10,2); +PROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2)); +PROCEDURE raiseSalaryStd(eid INT); +PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2)); +END; +$$ +SELECT *, '---------------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_SCHEMA='db1_mdev18092'; +SPECIFIC_CATALOG def +SPECIFIC_SCHEMA db1_mdev18092 +SPECIFIC_NAME p1 +ORDINAL_POSITION 1 +PARAMETER_MODE IN +PARAMETER_NAME a +DATA_TYPE int +CHARACTER_MAXIMUM_LENGTH NULL +CHARACTER_OCTET_LENGTH NULL +NUMERIC_PRECISION 10 +NUMERIC_SCALE 0 +DATETIME_PRECISION NULL +CHARACTER_SET_NAME NULL +COLLATION_NAME NULL +DTD_IDENTIFIER int(11) +ROUTINE_TYPE PROCEDURE +--------------- --------------- +DROP DATABASE db1_mdev18092; diff --git a/mysql-test/suite/compat/oracle/t/information_schema_parameters.test b/mysql-test/suite/compat/oracle/t/information_schema_parameters.test index af241661939..c13a59103dd 100644 --- a/mysql-test/suite/compat/oracle/t/information_schema_parameters.test +++ b/mysql-test/suite/compat/oracle/t/information_schema_parameters.test @@ -92,3 +92,36 @@ SET sql_mode=ORACLE; SELECT *, '--------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_NAME='f1'; --horizontal_results DROP FUNCTION f1; + +--echo # +--echo # MDEV 18092 Query with the table I_S.PARAMETERS stop working +--echo # after a package is created +--echo # + +SET sql_mode=ORACLE; + +CREATE DATABASE db1_mdev18092; +USE db1_mdev18092; + +DELIMITER $$; + +CREATE PROCEDURE p1(a INT) +AS BEGIN + NULL; +END; +$$ + +CREATE OR REPLACE PACKAGE employee_tools AS + FUNCTION getSalary(eid INT) RETURN DECIMAL(10,2); + PROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2)); + PROCEDURE raiseSalaryStd(eid INT); + PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2)); +END; +$$ +DELIMITER ;$$ + +--vertical_results +SELECT *, '---------------' FROM INFORMATION_SCHEMA.PARAMETERS WHERE SPECIFIC_SCHEMA='db1_mdev18092'; +--horizontal_results + +DROP DATABASE db1_mdev18092; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d9985406323..3f40384ab72 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6268,7 +6268,8 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, sph= Sp_handler::handler_mysql_proc((stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]-> val_int()); - if (!sph) + if (!sph || sph->type() == TYPE_ENUM_PACKAGE || + sph->type() == TYPE_ENUM_PACKAGE_BODY) DBUG_RETURN(0); if (!full_access) From 056b6fe1d59b515a6380e50783b3c4ad0f93959f Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Thu, 18 Apr 2019 23:12:43 +0300 Subject: [PATCH 08/25] MDEV-17297: stats.records=0 for a table of Archive engine when it has rows, when we run ANALYZE command Archive storage engine assumed that any query that attempts to read from the table will call ha_archive::info() beforehand. ha_archive would flush un-written data in that call (this would make it visible for the reads). Break this assumption. Flush the data when the table is opened for reading. This way, one can do multiple write statements without causing a flush, but as soon as we might need the data, we flush it. --- mysql-test/suite/archive/archive_eits.result | 24 ++++++++++ mysql-test/suite/archive/archive_eits.test | 32 +++++++++++++ storage/archive/ha_archive.cc | 50 +++++++++++++------- storage/archive/ha_archive.h | 3 ++ 4 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 mysql-test/suite/archive/archive_eits.result create mode 100644 mysql-test/suite/archive/archive_eits.test diff --git a/mysql-test/suite/archive/archive_eits.result b/mysql-test/suite/archive/archive_eits.result new file mode 100644 index 00000000000..e077c2e4954 --- /dev/null +++ b/mysql-test/suite/archive/archive_eits.result @@ -0,0 +1,24 @@ +drop table if exists t1; +# +# MDEV-17297: stats.records=0 for a table of Archive engine when it has rows, when we run ANALYZE command +# +CREATE TABLE t1 (fid INTEGER PRIMARY KEY AUTO_INCREMENT, g POINT)engine=archive; +INSERT INTO t1 VALUES +(101, PointFromText('POINT(10 10)')), +(102, PointFromText('POINT(20 10)')), +(103, PointFromText('POINT(20 20)')), +(104, PointFromWKB(AsWKB(PointFromText('POINT(10 20)')))); +set @tmp1= @@optimizer_use_condition_selectivity; +set @tmp2= @@use_stat_tables; +set optimizer_use_condition_selectivity=4; +set use_stat_tables=PREFERABLY; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze note The storage engine for the table doesn't support analyze +select * from mysql.table_stats where table_name='t1' and db_name=database(); +db_name table_name cardinality +test t1 4 +drop table t1; +set optimizer_use_condition_selectivity=@tmp1; +set use_stat_tables=@tmp2; diff --git a/mysql-test/suite/archive/archive_eits.test b/mysql-test/suite/archive/archive_eits.test new file mode 100644 index 00000000000..04c4ccdb709 --- /dev/null +++ b/mysql-test/suite/archive/archive_eits.test @@ -0,0 +1,32 @@ +-- source include/have_archive.inc + +--disable_warnings +drop table if exists t1; +--enable_warnings + +--echo # +--echo # MDEV-17297: stats.records=0 for a table of Archive engine when it has rows, when we run ANALYZE command +--echo # + +CREATE TABLE t1 (fid INTEGER PRIMARY KEY AUTO_INCREMENT, g POINT)engine=archive; +INSERT INTO t1 VALUES +(101, PointFromText('POINT(10 10)')), +(102, PointFromText('POINT(20 10)')), +(103, PointFromText('POINT(20 20)')), +(104, PointFromWKB(AsWKB(PointFromText('POINT(10 20)')))); + +set @tmp1= @@optimizer_use_condition_selectivity; +set @tmp2= @@use_stat_tables; + +set optimizer_use_condition_selectivity=4; +set use_stat_tables=PREFERABLY; +ANALYZE TABLE t1; + +select * from mysql.table_stats where table_name='t1' and db_name=database(); + +drop table t1; + +set optimizer_use_condition_selectivity=@tmp1; +set use_stat_tables=@tmp2; + + diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc index d70757e8142..e985f75d646 100644 --- a/storage/archive/ha_archive.cc +++ b/storage/archive/ha_archive.cc @@ -1650,7 +1650,6 @@ void ha_archive::update_create_info(HA_CREATE_INFO *create_info) DBUG_VOID_RETURN; } - /* Hints for optimizer, see ha_tina for more information */ @@ -1658,22 +1657,7 @@ int ha_archive::info(uint flag) { DBUG_ENTER("ha_archive::info"); - mysql_mutex_lock(&share->mutex); - if (share->dirty) - { - DBUG_PRINT("ha_archive", ("archive flushing out rows for scan")); - DBUG_ASSERT(share->archive_write_open); - azflush(&(share->archive_write), Z_SYNC_FLUSH); - share->dirty= FALSE; - } - - /* - This should be an accurate number now, though bulk and delayed inserts can - cause the number to be inaccurate. - */ - stats.records= share->rows_recorded; - mysql_mutex_unlock(&share->mutex); - + flush_and_clear_pending_writes(); stats.deleted= 0; DBUG_PRINT("ha_archive", ("Stats rows is %d\n", (int)stats.records)); @@ -1716,6 +1700,38 @@ int ha_archive::info(uint flag) } +int ha_archive::external_lock(THD *thd, int lock_type) +{ + if (lock_type == F_RDLCK) + { + // We are going to read from the table. Flush any pending writes that we + // may have + flush_and_clear_pending_writes(); + } + return 0; +} + + +void ha_archive::flush_and_clear_pending_writes() +{ + mysql_mutex_lock(&share->mutex); + if (share->dirty) + { + DBUG_PRINT("ha_archive", ("archive flushing out rows for scan")); + DBUG_ASSERT(share->archive_write_open); + azflush(&(share->archive_write), Z_SYNC_FLUSH); + share->dirty= FALSE; + } + + /* + This should be an accurate number now, though bulk and delayed inserts can + cause the number to be inaccurate. + */ + stats.records= share->rows_recorded; + mysql_mutex_unlock(&share->mutex); +} + + /* This method tells us that a bulk insert operation is about to occur. We set a flag which will keep write_row from saying that its data is dirty. This in diff --git a/storage/archive/ha_archive.h b/storage/archive/ha_archive.h index 56ff566db8c..a74374a340f 100644 --- a/storage/archive/ha_archive.h +++ b/storage/archive/ha_archive.h @@ -169,5 +169,8 @@ public: int unpack_row(azio_stream *file_to_read, uchar *record); unsigned int pack_row(uchar *record, azio_stream *writer); bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes); + int external_lock(THD *thd, int lock_type); +private: + void flush_and_clear_pending_writes(); }; From d315b4ff3903e665a6b2ad196dbe0f7feb57eed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 19 Apr 2019 12:44:46 +0300 Subject: [PATCH 09/25] Remove IBUF_COUNT_DEBUG The compile-time option IBUF_COUNT_DEBUG has not been used for years. It would only work with up to 3 created .ibd files, with no buffered changes existing while InnoDB is started up. --- storage/innobase/buf/buf0buf.cc | 52 +----------- storage/innobase/buf/buf0flu.cc | 7 +- storage/innobase/buf/buf0lru.cc | 6 +- storage/innobase/ibuf/ibuf0ibuf.cc | 113 --------------------------- storage/innobase/include/ibuf0ibuf.h | 9 +-- storage/innobase/include/univ.i | 5 +- storage/innobase/srv/srv0start.cc | 4 - 7 files changed, 7 insertions(+), 189 deletions(-) diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index bc8347e4f31..6e4facd7a22 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -3854,10 +3854,6 @@ got_block: } } -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(page_id) == 0); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - return(bpage); } @@ -4631,15 +4627,9 @@ evict_from_pool: } } - if (!recv_no_ibuf_operations) { - if (access_time) { -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(page_id) == 0); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - } else { - ibuf_merge_or_delete_for_page( - block, page_id, &page_size, TRUE); - } + if (!access_time && !recv_no_ibuf_operations) { + ibuf_merge_or_delete_for_page( + block, page_id, &page_size, TRUE); } buf_pool_mutex_enter(buf_pool); @@ -4843,10 +4833,6 @@ evict_from_pool: buf_read_ahead_linear(page_id, page_size, ibuf_inside(mtr)); } -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(fix_block->page.id) == 0); -#endif - ut_ad(!rw_lock_own_flagged(hash_lock, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); @@ -4956,10 +4942,6 @@ buf_page_optimistic_get( ibuf_inside(mtr)); } -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(block->page.id) == 0); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - buf_pool = buf_pool_from_block(block); buf_pool->stat.n_page_gets++; @@ -5063,9 +5045,6 @@ buf_page_get_known_nowait( } #endif /* UNIV_DEBUG */ -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a((mode == BUF_KEEP_OLD) || ibuf_count_get(block->page.id) == 0); -#endif buf_pool->stat.n_page_gets++; return(TRUE); @@ -5152,10 +5131,6 @@ buf_page_try_get_func( buf_pool->stat.n_page_gets++; -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(block->page.id) == 0); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - return(block); } @@ -5554,11 +5529,6 @@ buf_page_create( if (block && buf_page_in_file(&block->page) && !buf_pool_watch_is_sentinel(buf_pool, &block->page)) { - -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(page_id) == 0); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - ut_d(block->page.file_page_was_freed = FALSE); /* Page can be found in buf_pool */ @@ -5663,9 +5633,6 @@ buf_page_create( #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(block->page.id) == 0); -#endif return(block); } @@ -6114,14 +6081,6 @@ database_corrupted: buf_pool_mutex_enter(buf_pool); mutex_enter(block_mutex); -#ifdef UNIV_IBUF_COUNT_DEBUG - if (io_type == BUF_IO_WRITE || uncompressed) { - /* For BUF_IO_READ of compressed-only blocks, the - buffered operations will be merged by buf_page_get_gen() - after the block has been uncompressed. */ - ut_a(ibuf_count_get(bpage->id) == 0); - } -#endif /* Because this thread which does the unlocking is not the same that did the locking, we use a pass value != 0 in unlock, which simply removes the newest lock debug record, without checking the thread @@ -6350,11 +6309,6 @@ buf_pool_validate_instance( buf_pool, block->page.id) == &block->page); -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(buf_page_get_io_fix(&block->page) - == BUF_IO_READ - || !ibuf_count_get(block->page.id)); -#endif switch (buf_page_get_io_fix(&block->page)) { case BUF_IO_NONE: break; diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index a84a79669a7..f6f566bb8c7 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2018, MariaDB Corporation. +Copyright (c) 2013, 2019, MariaDB Corporation. Copyright (c) 2013, 2014, Fusion-io This program is free software; you can redistribute it and/or modify it under @@ -1041,11 +1041,6 @@ buf_flush_write_block_low( ut_ad(!buf_page_get_mutex(bpage)->is_owned()); ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_WRITE); ut_ad(bpage->oldest_modification != 0); - -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(bpage->id) == 0); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - ut_ad(bpage->newest_modification != 0); /* Force the log to the disk before writing the modified block */ diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc index 9218ea1b141..bd6f45a29ba 100644 --- a/storage/innobase/buf/buf0lru.cc +++ b/storage/innobase/buf/buf0lru.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2018, MariaDB Corporation. +Copyright (c) 2017, 2019, 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 @@ -1596,10 +1596,6 @@ buf_LRU_free_page( goto func_exit; } -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(bpage->id) == 0); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - if (zip || !bpage->zip.data) { /* This would completely free the block. */ /* Do not completely free dirty blocks. */ diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index 8b162af6790..0914452c39f 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -195,35 +195,6 @@ uint ibuf_debug; /** The insert buffer control structure */ ibuf_t* ibuf = NULL; -#ifdef UNIV_IBUF_COUNT_DEBUG -/** Number of tablespaces in the ibuf_counts array */ -#define IBUF_COUNT_N_SPACES 4 -/** Number of pages within each tablespace in the ibuf_counts array */ -#define IBUF_COUNT_N_PAGES 130000 - -/** Buffered entry counts for file pages, used in debugging */ -static ulint ibuf_counts[IBUF_COUNT_N_SPACES][IBUF_COUNT_N_PAGES]; - -/** Checks that the indexes to ibuf_counts[][] are within limits. -@param[in] page_id page id */ -UNIV_INLINE -void -ibuf_count_check( - const page_id_t page_id) -{ - if (page_id.space() < IBUF_COUNT_N_SPACES - && page_id.page_no() < IBUF_COUNT_N_PAGES) { - return; - } - - ib::fatal() << "UNIV_IBUF_COUNT_DEBUG limits space_id and page_no" - " and breaks crash recovery. space_id=" << page_id.space() - << ", should be 0<=space_id<" << IBUF_COUNT_N_SPACES - << ". page_no=" << page_id.page_no() - << ", should be 0<=page_no<" << IBUF_COUNT_N_PAGES; -} -#endif - /** @name Offsets to the per-page bits in the insert buffer bitmap */ /* @{ */ #define IBUF_BITMAP_FREE 0 /*!< Bits indicating the @@ -414,35 +385,6 @@ ibuf_tree_root_get( return(root); } -#ifdef UNIV_IBUF_COUNT_DEBUG - -/** Gets the ibuf count for a given page. -@param[in] page_id page id -@return number of entries in the insert buffer currently buffered for -this page */ -ulint ibuf_count_get(const page_id_t page_id) -{ - ibuf_count_check(page_id); - - return(ibuf_counts[page_id.space()][page_id.page_no()]); -} - -/** Sets the ibuf count for a given page. -@param[in] page_id page id -@param[in] val value to set */ -static -void -ibuf_count_set( - const page_id_t page_id, - ulint val) -{ - ibuf_count_check(page_id); - ut_a(val < UNIV_PAGE_SIZE); - - ibuf_counts[page_id.space()][page_id.page_no()] = val; -} -#endif - /******************************************************************//** Closes insert buffer and frees the data structures. */ void @@ -736,10 +678,6 @@ ibuf_bitmap_page_set_bits( #endif ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr->is_named_space(page_id.space())); -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a((bit != IBUF_BITMAP_BUFFERED) || (val != FALSE) - || (0 == ibuf_count_get(page_id))); -#endif bit_offset = (page_id.page_no() % page_size.physical()) * IBUF_BITS_PER_PAGE + bit; @@ -3503,9 +3441,6 @@ fail_exit: which it cannot do until we have buffered the IBUF_OP_DELETE and done mtr_commit(&mtr) to release the latch. */ -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a((buffered == 0) || ibuf_count_get(page_id)); -#endif ibuf_mtr_start(&bitmap_mtr); bitmap_mtr.set_named_space(page_id.space()); @@ -3648,17 +3583,6 @@ fail_exit: } func_exit: -#ifdef UNIV_IBUF_COUNT_DEBUG - if (err == DB_SUCCESS) { - - ib::info() << "Incrementing ibuf count of page " << page_id - << " from " << ibuf_count_get(space, page_no) - << " by 1"; - - ibuf_count_set(page_id, ibuf_count_get(page_id) + 1); - } -#endif - ibuf_mtr_commit(&mtr); btr_pcur_close(&pcur); @@ -4356,14 +4280,6 @@ ibuf_delete_rec( ibuf->empty = true; } -#ifdef UNIV_IBUF_COUNT_DEBUG - ib::info() << "Decrementing ibuf count of space " << space - << " page " << page_no << " from " - << ibuf_count_get(page_id) << " by 1"; - - ibuf_count_set(page_id, ibuf_count_get(page_id) - 1); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - return(FALSE); } @@ -4399,10 +4315,6 @@ ibuf_delete_rec( false, mtr); ut_a(err == DB_SUCCESS); -#ifdef UNIV_IBUF_COUNT_DEBUG - ibuf_count_set(page_id, ibuf_count_get(page_id) - 1); -#endif /* UNIV_IBUF_COUNT_DEBUG */ - ibuf_size_update(root); mutex_exit(&ibuf_mutex); @@ -4783,10 +4695,6 @@ reset_bit: my_atomic_addlint(&ibuf->n_merges, 1); ibuf_add_ops(ibuf->n_merged_ops, mops); ibuf_add_ops(ibuf->n_discarded_ops, dops); - -#ifdef UNIV_IBUF_COUNT_DEBUG - ut_a(ibuf_count_get(page_id) == 0); -#endif } /*********************************************************************//** @@ -4905,11 +4813,6 @@ ibuf_print( /*=======*/ FILE* file) /*!< in: file where to print */ { -#ifdef UNIV_IBUF_COUNT_DEBUG - ulint i; - ulint j; -#endif - mutex_enter(&ibuf_mutex); fprintf(file, @@ -4926,22 +4829,6 @@ ibuf_print( fputs("discarded operations:\n ", file); ibuf_print_ops(ibuf->n_discarded_ops, file); -#ifdef UNIV_IBUF_COUNT_DEBUG - for (i = 0; i < IBUF_COUNT_N_SPACES; i++) { - for (j = 0; j < IBUF_COUNT_N_PAGES; j++) { - ulint count = ibuf_count_get(page_id_t(i, j, 0)); - - if (count > 0) { - fprintf(stderr, - "Ibuf count for page " - ULINTPF ":" ULINTPF "" - " is " ULINTPF "\n", - i, j, count); - } - } - } -#endif /* UNIV_IBUF_COUNT_DEBUG */ - mutex_exit(&ibuf_mutex); } diff --git a/storage/innobase/include/ibuf0ibuf.h b/storage/innobase/include/ibuf0ibuf.h index ef72081c7cd..f6cf5a06911 100644 --- a/storage/innobase/include/ibuf0ibuf.h +++ b/storage/innobase/include/ibuf0ibuf.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2017, MariaDB Corporation. +Copyright (c) 2016, 2019, 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 @@ -384,13 +384,6 @@ ibuf_parse_bitmap_init( buf_block_t* block, /*!< in: block or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ -#ifdef UNIV_IBUF_COUNT_DEBUG -/** Gets the ibuf count for a given page. -@param[in] page_id page id -@return number of entries in the insert buffer currently buffered for -this page */ -ulint ibuf_count_get(const page_id_t page_id); -#endif /******************************************************************//** Looks if the insert buffer is empty. @return true if empty */ diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 021cf2c34a7..3aa52aadae7 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2018, MariaDB Corporation. +Copyright (c) 2013, 2019, MariaDB Corporation. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by @@ -212,9 +212,6 @@ command. */ this will break redo log file compatibility, but it may be useful when debugging redo log application problems. */ #define UNIV_IBUF_DEBUG /* debug the insert buffer */ -#define UNIV_IBUF_COUNT_DEBUG /* debug the insert buffer; -this limits the database to IBUF_COUNT_N_SPACES and IBUF_COUNT_N_PAGES, -and the insert buffer must be empty when the database is started */ #define UNIV_PERF_DEBUG /* debug flag that enables light weight performance related stuff. */ diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 03e9691da63..d75310607b3 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -1482,10 +1482,6 @@ innobase_start_or_create_for_mysql() #ifdef UNIV_IBUF_DEBUG ib::info() << "!!!!!!!! UNIV_IBUF_DEBUG switched on !!!!!!!!!"; -# ifdef UNIV_IBUF_COUNT_DEBUG - ib::info() << "!!!!!!!! UNIV_IBUF_COUNT_DEBUG switched on !!!!!!!!!"; - ib::error() << "Crash recovery will fail with UNIV_IBUF_COUNT_DEBUG"; -# endif #endif #ifdef UNIV_LOG_LSN_DEBUG From 42c58b87da01fdf41ae5aa0d41af226d41096262 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Wed, 17 Apr 2019 15:32:30 +0300 Subject: [PATCH 10/25] MDEV-18096 The server would crash when has configs rpl_semi_sync_master_enabled = OFF rpl_semi_sync_master_wait_no_slave = OFF The patch fixes a fired assert in the semisync master module. The assert caught attempt to switch semisync off (per rpl_semi_sync_master_wait_no_slave = OFF) when it was not even initialized (per rpl_semi_sync_master_enabled = OFF). The switching-off execution branch is relocated under one that executes enable_master() first. A minor cleaup is done to remove the int return from two functions that did not return anything but an error which could not happen in the functions. --- .../rpl/r/rpl_semi_sync_wait_no_slave.result | 8 ++++++ .../t/rpl_semi_sync_wait_no_slave-master.opt | 1 + .../rpl/t/rpl_semi_sync_wait_no_slave.test | 14 ++++++++++ sql/semisync_master.cc | 27 +++++++++---------- sql/semisync_master.h | 7 ++--- 5 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_semi_sync_wait_no_slave.result create mode 100644 mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave-master.opt create mode 100644 mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave.test diff --git a/mysql-test/suite/rpl/r/rpl_semi_sync_wait_no_slave.result b/mysql-test/suite/rpl/r/rpl_semi_sync_wait_no_slave.result new file mode 100644 index 00000000000..4bf6af2714d --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_semi_sync_wait_no_slave.result @@ -0,0 +1,8 @@ +include/master-slave.inc +[connection master] +connection master; +CREATE TABLE t1 (a INT); +INSERT INTO t1 SET a=1; +DROP TABLE t1; +connection slave; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave-master.opt b/mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave-master.opt new file mode 100644 index 00000000000..d84ebab5d56 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave-master.opt @@ -0,0 +1 @@ +--rpl_semi_sync_master_enabled=0 --rpl_semi_sync_master_wait_no_slave=0 diff --git a/mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave.test b/mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave.test new file mode 100644 index 00000000000..fecd0e25cb2 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_semi_sync_wait_no_slave.test @@ -0,0 +1,14 @@ +# The test verifies master crash of MDEV-18096 when the server starts with +# rpl_semi_sync_master_enabled = OFF rpl_semi_sync_master_wait_no_slave = OFF + +--source include/master-slave.inc +--source include/have_binlog_format_mixed.inc + +--connection master +CREATE TABLE t1 (a INT); +INSERT INTO t1 SET a=1; +DROP TABLE t1; + +--sync_slave_with_master + +--source include/rpl_end.inc diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc index 8a82fd9085c..4e37e3af58d 100644 --- a/sql/semisync_master.cc +++ b/sql/semisync_master.cc @@ -228,7 +228,7 @@ bool Active_tranx::is_tranx_end_pos(const char *log_file_name, DBUG_RETURN(entry != NULL); } -int Active_tranx::clear_active_tranx_nodes(const char *log_file_name, +void Active_tranx::clear_active_tranx_nodes(const char *log_file_name, my_off_t log_file_pos) { Tranx_node *new_front; @@ -307,7 +307,7 @@ int Active_tranx::clear_active_tranx_nodes(const char *log_file_name, m_trx_front->log_name, (ulong)m_trx_front->log_pos)); } - DBUG_RETURN(0); + DBUG_VOID_RETURN; } @@ -371,20 +371,21 @@ int Repl_semi_sync_master::init_object() { result = enable_master(); if (!result) + { result= ack_receiver.start(); /* Start the ACK thread. */ + /* + If rpl_semi_sync_master_wait_no_slave is disabled, let's temporarily + switch off semisync to avoid hang if there's none active slave. + */ + if (!rpl_semi_sync_master_wait_no_slave) + switch_off(); + } } else { result = disable_master(); } - /* - If rpl_semi_sync_master_wait_no_slave is disabled, let's temporarily - switch off semisync to avoid hang if there's none active slave. - */ - if (!rpl_semi_sync_master_wait_no_slave) - switch_off(); - return result; } @@ -961,17 +962,15 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, * the current sending event catches up with last wait position. If it * does match, semi-sync will be switched on again. */ -int Repl_semi_sync_master::switch_off() +void Repl_semi_sync_master::switch_off() { - int result; - DBUG_ENTER("Repl_semi_sync_master::switch_off"); m_state = false; /* Clear the active transaction list. */ assert(m_active_tranxs != NULL); - result = m_active_tranxs->clear_active_tranx_nodes(NULL, 0); + m_active_tranxs->clear_active_tranx_nodes(NULL, 0); rpl_semi_sync_master_off_times++; m_wait_file_name_inited = false; @@ -979,7 +978,7 @@ int Repl_semi_sync_master::switch_off() sql_print_information("Semi-sync replication switched OFF."); cond_broadcast(); /* wake up all waiting threads */ - DBUG_RETURN(result); + DBUG_VOID_RETURN; } int Repl_semi_sync_master::try_switch_on(int server_id, diff --git a/sql/semisync_master.h b/sql/semisync_master.h index 3b05d9e0348..de5e3240802 100644 --- a/sql/semisync_master.h +++ b/sql/semisync_master.h @@ -343,11 +343,8 @@ public: * position. * If log_file_name is NULL, everything will be cleared: the sorted * list and the hash table will be reset to empty. - * - * Return: - * 0: success; non-zero: error */ - int clear_active_tranx_nodes(const char *log_file_name, + void clear_active_tranx_nodes(const char *log_file_name, my_off_t log_file_pos); /* Given a position, check to see whether the position is an active @@ -449,7 +446,7 @@ class Repl_semi_sync_master } /* Switch semi-sync off because of timeout in transaction waiting. */ - int switch_off(); + void switch_off(); /* Switch semi-sync on when slaves catch up. */ int try_switch_on(int server_id, From f4b27400185bab217da11f8781eebb09a8502304 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 19 Apr 2019 21:04:44 +0400 Subject: [PATCH 11/25] MDEV-17299 Assertion `maybe_null' failed in make_sortkey --- mysql-test/r/type_date.result | 25 +++++++++++++++++++++++++ mysql-test/t/type_date.test | 14 ++++++++++++++ sql/item_func.cc | 2 ++ 3 files changed, 41 insertions(+) diff --git a/mysql-test/r/type_date.result b/mysql-test/r/type_date.result index 661dcabbcfe..e2c3b7f3b28 100644 --- a/mysql-test/r/type_date.result +++ b/mysql-test/r/type_date.result @@ -854,5 +854,30 @@ d COUNT(*) NULL 2 DROP TABLE t1; # +# MDEV-17299 Assertion `maybe_null' failed in make_sortkey +# +CREATE TABLE t1 (pk int NOT NULL, d1 date, d2 date NOT NULL); +INSERT INTO t1 values (1,'2018-06-22','2018-06-22'),(2,'2018-07-11','2018-07-11'); +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT group_concat(d1/(CASE 'b' WHEN 'j' THEN 'c' END)) +FROM v1 GROUP BY greatest(pk, 0, d2); +group_concat(d1/(CASE 'b' WHEN 'j' THEN 'c' END)) +NULL +Warnings: +Warning 1292 Incorrect datetime value: '1' for column 'pk' at row 1 +Warning 1292 Incorrect datetime value: '2' for column 'pk' at row 1 +Warning 1292 Incorrect datetime value: '1' for column 'pk' at row 1 +Warning 1292 Incorrect datetime value: '1' for column 'pk' at row 1 +Warning 1292 Incorrect datetime value: '2' for column 'pk' at row 2 +CREATE TABLE t2 AS SELECT greatest(pk, 0, d2) AS c1 FROM t1 LIMIT 0; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `c1` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +DROP VIEW v1; +DROP TABLE t1; +# # End of 10.1 tests # diff --git a/mysql-test/t/type_date.test b/mysql-test/t/type_date.test index 8248386a93f..554542f9dd3 100644 --- a/mysql-test/t/type_date.test +++ b/mysql-test/t/type_date.test @@ -585,6 +585,20 @@ INSERT INTO t1 VALUES ('1985-05-13'),('1989-12-24'); SELECT d, COUNT(*) FROM t1 GROUP BY d WITH ROLLUP HAVING CASE d WHEN '2017-05-25' THEN 0 ELSE 1 END; DROP TABLE t1; +--echo # +--echo # MDEV-17299 Assertion `maybe_null' failed in make_sortkey +--echo # + +CREATE TABLE t1 (pk int NOT NULL, d1 date, d2 date NOT NULL); +INSERT INTO t1 values (1,'2018-06-22','2018-06-22'),(2,'2018-07-11','2018-07-11'); +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT group_concat(d1/(CASE 'b' WHEN 'j' THEN 'c' END)) + FROM v1 GROUP BY greatest(pk, 0, d2); +CREATE TABLE t2 AS SELECT greatest(pk, 0, d2) AS c1 FROM t1 LIMIT 0; +SHOW CREATE TABLE t2; +DROP TABLE t2; +DROP VIEW v1; +DROP TABLE t1; --echo # --echo # End of 10.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index e995903b940..a1a2c3f1d1c 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2794,6 +2794,8 @@ void Item_func_min_max::fix_length_and_dec() switch (tmp_cmp_type) { case TIME_RESULT: // At least one temporal argument was found. + if (temporal_type_count < arg_count) + maybe_null= true; // Non-temporal-to-temporal conversion can return NULL collation.set_numeric(); set_handler_by_field_type(temporal_field_type); if (is_temporal_type_with_time(temporal_field_type)) From f4019f5b3544a18f3ddf32df2c5214c3f8dabdce Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Sat, 20 Apr 2019 00:11:50 +0400 Subject: [PATCH 12/25] Backporting from 10.4 to 10.3: MDEV-17325 NULL-ability problems with LEAST() in combination with NO_ZERO_DATE and NO_ZERO_IN_DATE This also fixes: MDEV-17299 Assertion `maybe_null' failed in make_sortkey Note, during merge of the 10.1 version of MDEV-17299, please use the 10.3 version of the code (i.e. null merge the 10.1 version). --- mysql-test/main/func_hybrid_type.result | 114 ++++++++++++++++++++++++ mysql-test/main/func_hybrid_type.test | 64 ++++++++++++- mysql-test/main/type_date.result | 25 ++++++ mysql-test/main/type_date.test | 14 +++ sql/sql_type.cc | 91 +++++++++++++++++++ sql/sql_type.h | 2 + 6 files changed, 309 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/func_hybrid_type.result b/mysql-test/main/func_hybrid_type.result index 04c428e745d..109f7a35038 100644 --- a/mysql-test/main/func_hybrid_type.result +++ b/mysql-test/main/func_hybrid_type.result @@ -3771,5 +3771,119 @@ t2 CREATE TABLE `t2` ( DROP TABLE t1, t2; SET sql_mode=DEFAULT; # +# MDEV-17325 NULL-ability problems with LEAST() in combination with NO_ZERO_DATE and NO_ZERO_IN_DATE +# +SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE'; +SELECT +LEAST('0000-00-00',DATE'2001-01-01') AS s1, +LEAST('0001-00-01',DATE'2001-01-01') AS s2, +LEAST('0000-00-00',TIMESTAMP'2001-01-01 00:00:00') AS s3, +LEAST('0001-00-01',TIMESTAMP'2001-01-01 00:00:00') AS s4, +LEAST(0,DATE'2001-01-01') AS i1, +LEAST(20010001,DATE'2001-01-01') AS i2, +LEAST(0,TIMESTAMP'2001-01-01 00:00:00') AS i3, +LEAST(20010001,TIMESTAMP'2001-01-01 00:00:00') AS i4; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def s1 10 10 0 Y 128 0 63 +def s2 10 10 0 Y 128 0 63 +def s3 12 26 0 Y 128 6 63 +def s4 12 26 0 Y 128 6 63 +def i1 10 10 0 Y 128 0 63 +def i2 10 10 0 Y 128 0 63 +def i3 12 19 0 Y 128 0 63 +def i4 12 19 0 Y 128 0 63 +s1 s2 s3 s4 i1 i2 i3 i4 +NULL NULL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 1292 Incorrect datetime value: '0000-00-00' +Warning 1292 Incorrect datetime value: '0001-00-01' +Warning 1292 Incorrect datetime value: '0000-00-00 00:00:00' +Warning 1292 Incorrect datetime value: '0001-00-01 00:00:00' +Warning 1292 Incorrect datetime value: '0000-00-00' +Warning 1292 Incorrect datetime value: '2001-00-01' +Warning 1292 Incorrect datetime value: '0000-00-00 00:00:00' +Warning 1292 Incorrect datetime value: '2001-00-01 00:00:00' +SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE'; +CREATE TABLE t1 AS SELECT +LEAST('0000-00-00',DATE'2001-01-01') AS s1, +LEAST('0001-00-01',DATE'2001-01-01') AS s2, +LEAST('0000-00-00',TIMESTAMP'2001-01-01 00:00:00') AS s3, +LEAST('0001-00-01',TIMESTAMP'2001-01-01 00:00:00') AS s4, +LEAST(0,DATE'2001-01-01') AS i1, +LEAST(20010001,DATE'2001-01-01') AS i2, +LEAST(0,TIMESTAMP'2001-01-01 00:00:00') AS i3, +LEAST(20010001,TIMESTAMP'2001-01-01 00:00:00') AS i4; +Warnings: +Warning 1292 Incorrect datetime value: '0000-00-00' +Warning 1292 Incorrect datetime value: '0001-00-01' +Warning 1292 Incorrect datetime value: '0000-00-00 00:00:00' +Warning 1292 Incorrect datetime value: '0001-00-01 00:00:00' +Warning 1292 Incorrect datetime value: '0000-00-00' +Warning 1292 Incorrect datetime value: '2001-00-01' +Warning 1292 Incorrect datetime value: '0000-00-00 00:00:00' +Warning 1292 Incorrect datetime value: '2001-00-01 00:00:00' +SELECT * FROM t1; +s1 s2 s3 s4 i1 i2 i3 i4 +NULL NULL NULL NULL NULL NULL NULL NULL +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `s1` date DEFAULT NULL, + `s2` date DEFAULT NULL, + `s3` datetime(6) DEFAULT NULL, + `s4` datetime(6) DEFAULT NULL, + `i1` date DEFAULT NULL, + `i2` date DEFAULT NULL, + `i3` datetime DEFAULT NULL, + `i4` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30'); +CREATE TABLE t1 AS SELECT LEAST(CURRENT_DATE,CURRENT_TIME) AS c1; +SELECT * FROM t1; +c1 +2001-01-01 00:00:00 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` datetime NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SET old_mode=ZERO_DATE_TIME_CAST; +CREATE TABLE t1 AS SELECT LEAST(CURRENT_DATE,CURRENT_TIME) AS c1; +Warnings: +Warning 1292 Incorrect datetime value: '0000-00-00 10:20:30' +SELECT * FROM t1; +c1 +NULL +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SET old_mode=DEFAULT; +SET timestamp=DEFAULT; +SET sql_mode=DEFAULT; +SET sql_mode=''; +SELECT LEAST(999,TIME'10:20:30') AS c1; +c1 +NULL +Warnings: +Warning 1292 Incorrect datetime value: '999' +CREATE TABLE t1 AS SELECT LEAST(999,TIME'10:20:30') AS c1; +Warnings: +Warning 1292 Incorrect datetime value: '999' +SELECT * FROM t1; +c1 +NULL +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` time DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SET sql_mode=DEFAULT; +# # End of 10.3 tests # diff --git a/mysql-test/main/func_hybrid_type.test b/mysql-test/main/func_hybrid_type.test index f754d660140..223ae4b2166 100644 --- a/mysql-test/main/func_hybrid_type.test +++ b/mysql-test/main/func_hybrid_type.test @@ -628,6 +628,68 @@ SET sql_mode=DEFAULT; --echo # ---echo # End of 10.3 tests +--echo # MDEV-17325 NULL-ability problems with LEAST() in combination with NO_ZERO_DATE and NO_ZERO_IN_DATE --echo # +SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE'; + +--disable_ps_protocol +--enable_metadata +SELECT + LEAST('0000-00-00',DATE'2001-01-01') AS s1, + LEAST('0001-00-01',DATE'2001-01-01') AS s2, + LEAST('0000-00-00',TIMESTAMP'2001-01-01 00:00:00') AS s3, + LEAST('0001-00-01',TIMESTAMP'2001-01-01 00:00:00') AS s4, + LEAST(0,DATE'2001-01-01') AS i1, + LEAST(20010001,DATE'2001-01-01') AS i2, + LEAST(0,TIMESTAMP'2001-01-01 00:00:00') AS i3, + LEAST(20010001,TIMESTAMP'2001-01-01 00:00:00') AS i4; +--disable_metadata +--enable_ps_protocol + +SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE'; +CREATE TABLE t1 AS SELECT + LEAST('0000-00-00',DATE'2001-01-01') AS s1, + LEAST('0001-00-01',DATE'2001-01-01') AS s2, + LEAST('0000-00-00',TIMESTAMP'2001-01-01 00:00:00') AS s3, + LEAST('0001-00-01',TIMESTAMP'2001-01-01 00:00:00') AS s4, + LEAST(0,DATE'2001-01-01') AS i1, + LEAST(20010001,DATE'2001-01-01') AS i2, + LEAST(0,TIMESTAMP'2001-01-01 00:00:00') AS i3, + LEAST(20010001,TIMESTAMP'2001-01-01 00:00:00') AS i4; +SELECT * FROM t1; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30'); + +# A TIME always converts to a non-NULL DATETIME with the new CAST style +# Expect a NOT NULL column +CREATE TABLE t1 AS SELECT LEAST(CURRENT_DATE,CURRENT_TIME) AS c1; +SELECT * FROM t1; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +# A TIME can convert to a NULL DATETIME with old CAST style +# Expect a NULL-able column +SET old_mode=ZERO_DATE_TIME_CAST; +CREATE TABLE t1 AS SELECT LEAST(CURRENT_DATE,CURRENT_TIME) AS c1; +SELECT * FROM t1; +SHOW CREATE TABLE t1; +DROP TABLE t1; +SET old_mode=DEFAULT; +SET timestamp=DEFAULT; + +SET sql_mode=DEFAULT; + +SET sql_mode=''; +SELECT LEAST(999,TIME'10:20:30') AS c1; +CREATE TABLE t1 AS SELECT LEAST(999,TIME'10:20:30') AS c1; +SELECT * FROM t1; +SHOW CREATE TABLE t1; +DROP TABLE t1; +SET sql_mode=DEFAULT; + +--echo # +--echo # End of 10.3 tests +--echo # diff --git a/mysql-test/main/type_date.result b/mysql-test/main/type_date.result index e87cd836317..4b5a0ad63a0 100644 --- a/mysql-test/main/type_date.result +++ b/mysql-test/main/type_date.result @@ -863,6 +863,31 @@ d COUNT(*) NULL 2 DROP TABLE t1; # +# MDEV-17299 Assertion `maybe_null' failed in make_sortkey +# +CREATE TABLE t1 (pk int NOT NULL, d1 date, d2 date NOT NULL); +INSERT INTO t1 values (1,'2018-06-22','2018-06-22'),(2,'2018-07-11','2018-07-11'); +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT group_concat(d1/(CASE 'b' WHEN 'j' THEN 'c' END)) +FROM v1 GROUP BY greatest(pk, 0, d2); +group_concat(d1/(CASE 'b' WHEN 'j' THEN 'c' END)) +NULL +Warnings: +Warning 1292 Incorrect datetime value: '1' for column `test`.`t1`.`pk` at row 1 +Warning 1292 Incorrect datetime value: '2' for column `test`.`t1`.`pk` at row 1 +Warning 1292 Incorrect datetime value: '1' for column `test`.`t1`.`pk` at row 1 +Warning 1292 Incorrect datetime value: '1' for column `test`.`t1`.`pk` at row 1 +Warning 1292 Incorrect datetime value: '2' for column `test`.`t1`.`pk` at row 2 +CREATE TABLE t2 AS SELECT greatest(pk, 0, d2) AS c1 FROM t1 LIMIT 0; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `c1` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +DROP VIEW v1; +DROP TABLE t1; +# # End of 10.1 tests # # diff --git a/mysql-test/main/type_date.test b/mysql-test/main/type_date.test index 8d29a54a26c..befee57183d 100644 --- a/mysql-test/main/type_date.test +++ b/mysql-test/main/type_date.test @@ -591,6 +591,20 @@ INSERT INTO t1 VALUES ('1985-05-13'),('1989-12-24'); SELECT d, COUNT(*) FROM t1 GROUP BY d WITH ROLLUP HAVING CASE d WHEN '2017-05-25' THEN 0 ELSE 1 END; DROP TABLE t1; +--echo # +--echo # MDEV-17299 Assertion `maybe_null' failed in make_sortkey +--echo # + +CREATE TABLE t1 (pk int NOT NULL, d1 date, d2 date NOT NULL); +INSERT INTO t1 values (1,'2018-06-22','2018-06-22'),(2,'2018-07-11','2018-07-11'); +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT group_concat(d1/(CASE 'b' WHEN 'j' THEN 'c' END)) + FROM v1 GROUP BY greatest(pk, 0, d2); +CREATE TABLE t2 AS SELECT greatest(pk, 0, d2) AS c1 FROM t1 LIMIT 0; +SHOW CREATE TABLE t2; +DROP TABLE t2; +DROP VIEW v1; +DROP TABLE t1; --echo # --echo # End of 10.1 tests diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 22eebaf6a38..c2c853efa23 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -2933,6 +2933,97 @@ bool Type_handler:: } +bool Type_handler_temporal_result:: + Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const +{ + bool rc= Type_handler::Item_func_min_max_fix_attributes(thd, func, + items, nitems); + if (rc || func->maybe_null) + return rc; + /* + LEAST/GREATES(non-temporal, temporal) can return NULL. + CAST functions Item_{time|datetime|date}_typecast always set maybe_full + to true. Here we try to detect nullability more thoroughly. + Perhaps CAST functions should also reuse this idea eventually. + */ + const Type_handler *hf= func->type_handler(); + for (uint i= 0; i < nitems; i++) + { + /* + If items[i] does not need conversion to the current temporal data + type, then we trust items[i]->maybe_null, which was already ORred + to func->maybe_null in the argument loop in fix_fields(). + If items[i] requires conversion to the current temporal data type, + then conversion can fail and return NULL even for NOT NULL items. + */ + const Type_handler *ha= items[i]->type_handler(); + if (hf == ha) + continue; // No conversion. + if (ha->cmp_type() != TIME_RESULT) + { + func->maybe_null= true; // Conversion from non-temporal is not safe + break; + } + timestamp_type tf= hf->mysql_timestamp_type(); + timestamp_type ta= ha->mysql_timestamp_type(); + if (tf == ta || + (tf == MYSQL_TIMESTAMP_DATETIME && ta == MYSQL_TIMESTAMP_DATE)) + { + /* + If handlers have the same mysql_timestamp_type(), + then conversion is NULL safe. Conversion from DATE to DATETIME + is also safe. This branch includes data type pairs: + Function return type Argument type Comment + -------------------- ------------- ------------- + TIMESTAMP TIMESTAMP no conversion + TIMESTAMP DATETIME not possible + TIMESTAMP DATE not possible + DATETIME DATETIME no conversion + DATETIME TIMESTAMP safe conversion + DATETIME DATE safe conversion + DATE DATE no conversion + TIME TIME no conversion + + Note, a function cannot return TIMESTAMP if it has non-TIMESTAMP + arguments (it would return DATETIME in such case). + */ + DBUG_ASSERT(hf->field_type() != MYSQL_TYPE_TIMESTAMP || tf == ta); + continue; + } + /* + Here we have the following data type pairs that did not match + the condition above: + + Function return type Argument type Comment + -------------------- ------------- ------- + TIMESTAMP TIME Not possible + DATETIME TIME depends on OLD_MODE_ZERO_DATE_TIME_CAST + DATE TIMESTAMP Not possible + DATE DATETIME Not possible + DATE TIME Not possible + TIME TIMESTAMP Not possible + TIME DATETIME Not possible + TIME DATE Not possible + + Most pairs are not possible, because the function data type + would be DATETIME (according to LEAST/GREATEST aggregation rules). + Conversion to DATETIME from TIME is not safe when + OLD_MODE_ZERO_DATE_TIME_CAST is set: + - negative TIME values cannot be converted to not-NULL DATETIME values + - TIME values can produce DATETIME values that do not pass + NO_ZERO_DATE and NO_ZERO_IN_DATE tests. + */ + DBUG_ASSERT(hf->field_type() == MYSQL_TYPE_DATETIME); + if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) + continue; + func->maybe_null= true; + break; + } + return rc; +} + + bool Type_handler_real_result:: Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, Item **items, uint nitems) const diff --git a/sql/sql_type.h b/sql/sql_type.h index 1ddcef2da61..21e0eb56338 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -2138,6 +2138,8 @@ public: Item *source_expr, Item *source_const) const; bool subquery_type_allows_materialization(const Item *inner, const Item *outer) const; + bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const; bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; From 765ae6e82165d1bc4cf6cc9f0d556d66a5e172d1 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Sun, 21 Apr 2019 12:07:30 +0400 Subject: [PATCH 13/25] MDEV-19239 ERROR 1300 (HY000): Invalid utf8 character string in 10.3.13-MariaDB A sequence of e, e.g.: SELECT 123eXYzzz FROM t1; was not scanned correctly (where XY is a multi-byte character). The multi-byte head byte X was appended to 123e separately from the multi-byte tail byte Y, so a pointer to "Yzzz" was passed into scan_ident_start(), which failed on a bad multi-byte sequence. After this change, scan_ident_start() gets a pointer to "XYzzz", so it correctly sees the whole multi-byte character. --- mysql-test/main/ctype_utf8.result | 68 ++++++++++++++++++++++++++++ mysql-test/main/ctype_utf8.test | 75 +++++++++++++++++++++++++++++++ sql/sql_lex.cc | 20 ++++++++- 3 files changed, 162 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/ctype_utf8.result b/mysql-test/main/ctype_utf8.result index 9c2fcb84765..6a72f60a437 100644 --- a/mysql-test/main/ctype_utf8.result +++ b/mysql-test/main/ctype_utf8.result @@ -11300,5 +11300,73 @@ t1 CREATE TABLE `t1` ( DROP TABLE t1; SET sql_mode=DEFAULT; # +# MDEV-19239 ERROR 1300 (HY000): Invalid utf8 character string in 10.3.13-MariaDB +# +SET NAMES utf8; +SELECT +x.消息ID, +x.消息TITLE, +x.消息类型, +x.发送时间, +x.阅读时间,x.老师ID, +IF(x.四天内最近一次登录时间='2100-01-01 00:00:00','',x.四天内最近一次登录时间) 四天内最近一次登录时间 +FROM ( +SELECT +msg.*, +CASE +WHEN login.login_time BETWEEN msg.发送时间 AND DATE_ADD(msg.发送时间,INTERVAL 4 DAY) +THEN login.login_time +WHEN (login.login_time NOT BETWEEN msg.发送时间 AND DATE_ADD(msg.发送时间,INTERVAL 4 DAY)) AND login.login_time>0 +THEN '2100-01-01 00:00:00' ELSE '' + END 四天内最近一次登录时间 +FROM ( +SELECT +me.id 消息ID, +me.title 消息TITLE, +CASE +WHEN me.type=1 +THEN 'Interview Message' + WHEN me.type=2 +THEN 'Orientation Message' + WHEN me.type=3 +THEN 'Warning Message' + WHEN me.type=4 +THEN 'Fail Message' + WHEN me.type=5 +THEN 'FM Message' + WHEN me.type=6 +THEN 'Training Message' + WHEN me.type=7 +THEN 'TUrgent Message' + END 消息类型, +FROM_UNIXTIME(me.sending_time) 发送时间, +IF(tar.is_read=1,FROM_UNIXTIME(tar.read_time),'') 阅读时间, +tar.tid 老师ID +FROM ebk_message me +LEFT JOIN ebk_message_target tar +ON me.id=tar.msg_id +WHERE +FROM_UNIXTIME(me.sending_time,'%Y-%m-%d') BETWEEN 'start' AND 'end' AND me.status=1 AND tar.tid>0 +GROUP BY +tar.tid, +me.sending_time,me.id) msg +LEFT JOIN ( +SELECT tid,FROM_UNIXTIME(login_time) login_time +FROM ebk_teacher_login_log +WHERE FROM_UNIXTIME(login_time,'%Y-%m-%d') BETWEEN 'start' AND DATE_ADD('end',INTERVAL 4 DAY) +ORDER BY tid,FROM_UNIXTIME(login_time)) login +ON +msg.老师ID=login.tid +ORDER BY msg.消息ID,msg.发送时间,msg.老师ID,login_time) x +GROUP BY x.消息ID,x.发送时间,x.老师ID; +ERROR 42S02: Table 'test.ebk_message' doesn't exist +SET NAMES utf8; +CREATE TABLE t1 (x INT); +INSERT INTO t1 VALUES (1); +SELECT x AS 5天内最近一次登录时间 FROM t1; +5天内最近一次登录时间 +1 +DROP TABLE t1; +# # End of 10.3 tests # diff --git a/mysql-test/main/ctype_utf8.test b/mysql-test/main/ctype_utf8.test index 6f8657dacb5..ab26b69d765 100644 --- a/mysql-test/main/ctype_utf8.test +++ b/mysql-test/main/ctype_utf8.test @@ -2180,6 +2180,81 @@ DROP TABLE t1; SET sql_mode=DEFAULT; +--echo # +--echo # MDEV-19239 ERROR 1300 (HY000): Invalid utf8 character string in 10.3.13-MariaDB +--echo # + +# +# Test that the following query does not fail on "Invalid utf8 character string" +# + +SET NAMES utf8; +--error ER_NO_SUCH_TABLE +SELECT + x.消息ID, + x.消息TITLE, + x.消息类型, + x.发送时间, + x.阅读时间,x.老师ID, + IF(x.四天内最近一次登录时间='2100-01-01 00:00:00','',x.四天内最近一次登录时间) 四天内最近一次登录时间 +FROM ( + SELECT + msg.*, + CASE + WHEN login.login_time BETWEEN msg.发送时间 AND DATE_ADD(msg.发送时间,INTERVAL 4 DAY) + THEN login.login_time + WHEN (login.login_time NOT BETWEEN msg.发送时间 AND DATE_ADD(msg.发送时间,INTERVAL 4 DAY)) AND login.login_time>0 + THEN '2100-01-01 00:00:00' ELSE '' + END 四天内最近一次登录时间 + FROM ( + SELECT + me.id 消息ID, + me.title 消息TITLE, + CASE + WHEN me.type=1 + THEN 'Interview Message' + WHEN me.type=2 + THEN 'Orientation Message' + WHEN me.type=3 + THEN 'Warning Message' + WHEN me.type=4 + THEN 'Fail Message' + WHEN me.type=5 + THEN 'FM Message' + WHEN me.type=6 + THEN 'Training Message' + WHEN me.type=7 + THEN 'TUrgent Message' + END 消息类型, + FROM_UNIXTIME(me.sending_time) 发送时间, + IF(tar.is_read=1,FROM_UNIXTIME(tar.read_time),'') 阅读时间, + tar.tid 老师ID + FROM ebk_message me + LEFT JOIN ebk_message_target tar + ON me.id=tar.msg_id + WHERE + FROM_UNIXTIME(me.sending_time,'%Y-%m-%d') BETWEEN 'start' AND 'end' AND me.status=1 AND tar.tid>0 + GROUP BY + tar.tid, + me.sending_time,me.id) msg + LEFT JOIN ( + SELECT tid,FROM_UNIXTIME(login_time) login_time + FROM ebk_teacher_login_log + WHERE FROM_UNIXTIME(login_time,'%Y-%m-%d') BETWEEN 'start' AND DATE_ADD('end',INTERVAL 4 DAY) + ORDER BY tid,FROM_UNIXTIME(login_time)) login + ON + msg.老师ID=login.tid + ORDER BY msg.消息ID,msg.发送时间,msg.老师ID,login_time) x + GROUP BY x.消息ID,x.发送时间,x.老师ID; + + +SET NAMES utf8; +CREATE TABLE t1 (x INT); +INSERT INTO t1 VALUES (1); +SELECT x AS 5天内最近一次登录时间 FROM t1; +DROP TABLE t1; + + --echo # --echo # End of 10.3 tests --echo # diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index c52005e7683..b5ff060ecc6 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1587,9 +1587,27 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) return(FLOAT_NUM); } } + /* + We've found: + - A sequence of digits + - Followed by 'e' or 'E' + - Followed by some byte XX which is not a known mantissa start, + and it's known to be a valid identifier part. + XX can be either a 8bit identifier character, or a multi-byte head. + */ yyUnget(); + return scan_ident_start(thd, &yylval->ident_cli); } - // fall through + /* + We've found: + - A sequence of digits + - Followed by some character XX, which is neither 'e' nor 'E', + and it's known to be a valid identifier part. + XX can be a 8bit identifier character, or a multi-byte head. + */ + yyUnget(); + return scan_ident_start(thd, &yylval->ident_cli); + case MY_LEX_IDENT_START: // We come here after '.' return scan_ident_start(thd, &yylval->ident_cli); From 279b50b4eb69f882510f069e79715c38dc13355e Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 22 Apr 2019 14:01:58 +0400 Subject: [PATCH 14/25] MDEV-14041 Server crashes in String::length on queries with functions and ROLLUP --- mysql-test/r/olap.result | 40 ++++++++++++++++++++++++++++++++++++++++ mysql-test/t/olap.test | 22 ++++++++++++++++++++++ sql/item.h | 39 +++++++++++++++++++++++++++++++++++++++ sql/item_func.h | 11 +++++++---- 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index 84c54c0c3c7..df2d6416648 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -800,5 +800,45 @@ latin1 binary DROP TABLE t; # +# MDEV-14041 Server crashes in String::length on queries with functions and ROLLUP +# +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT GET_LOCK( 'foo', 0 ); +GET_LOCK( 'foo', 0 ) +1 +SELECT HEX( RELEASE_LOCK( 'foo' ) ) AS f FROM t1 GROUP BY f WITH ROLLUP; +f +NULL +1 +NULL +DROP TABLE t1; +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT i FROM t1 GROUP BY i WITH ROLLUP +UNION ALL +SELECT ELT( FOUND_ROWS(), 1 ) f FROM t1 GROUP BY f WITH ROLLUP; +i +1 +2 +NULL +NULL +NULL +DROP TABLE t1; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2); +SELECT a FROM t1 GROUP BY NULLIF( CONVERT('', DATE), '2015-10-15' ) WITH ROLLUP; +a +1 +1 +Warnings: +Warning 1292 Incorrect datetime value: '' +Warning 1292 Incorrect datetime value: '' +Warning 1292 Incorrect datetime value: '' +Warning 1292 Incorrect datetime value: '' +Warning 1292 Incorrect datetime value: '' +Warning 1292 Incorrect datetime value: '' +DROP TABLE t1; +# # End of 10.1 tests # diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test index bb7806969d3..8d1951573b3 100644 --- a/mysql-test/t/olap.test +++ b/mysql-test/t/olap.test @@ -439,6 +439,28 @@ SELECT CHARSET(d) AS f FROM t GROUP BY d WITH ROLLUP; DROP TABLE t; +--echo # +--echo # MDEV-14041 Server crashes in String::length on queries with functions and ROLLUP +--echo # + +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT GET_LOCK( 'foo', 0 ); +SELECT HEX( RELEASE_LOCK( 'foo' ) ) AS f FROM t1 GROUP BY f WITH ROLLUP; +DROP TABLE t1; + +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT i FROM t1 GROUP BY i WITH ROLLUP +UNION ALL +SELECT ELT( FOUND_ROWS(), 1 ) f FROM t1 GROUP BY f WITH ROLLUP; +DROP TABLE t1; + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2); +SELECT a FROM t1 GROUP BY NULLIF( CONVERT('', DATE), '2015-10-15' ) WITH ROLLUP; +DROP TABLE t1; + --echo # --echo # End of 10.1 tests --echo # diff --git a/sql/item.h b/sql/item.h index 4b93d3f9164..5c432887ed9 100644 --- a/sql/item.h +++ b/sql/item.h @@ -663,6 +663,45 @@ protected: SEL_TREE *get_mm_tree_for_const(RANGE_OPT_PARAM *param); Field *create_tmp_field(bool group, TABLE *table, uint convert_int_length); + /* Helper methods, to get an Item value from another Item */ + double val_real_from_item(Item *item) + { + DBUG_ASSERT(fixed == 1); + double value= item->val_real(); + null_value= item->null_value; + return value; + } + longlong val_int_from_item(Item *item) + { + DBUG_ASSERT(fixed == 1); + longlong value= item->val_int(); + null_value= item->null_value; + return value; + } + String *val_str_from_item(Item *item, String *str) + { + DBUG_ASSERT(fixed == 1); + String *res= item->val_str(str); + if (res) + res->set_charset(collation.collation); + if ((null_value= item->null_value)) + res= NULL; + return res; + } + my_decimal *val_decimal_from_item(Item *item, my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + my_decimal *value= item->val_decimal(decimal_value); + if ((null_value= item->null_value)) + value= NULL; + return value; + } + bool get_date_from_item(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) + { + bool rc= item->get_date(ltime, fuzzydate); + null_value= MY_TEST(rc || item->null_value); + return rc; + } /* This method is used if the item was not null but convertion to TIME/DATE/DATETIME failed. We return a zero date if allowed, diff --git a/sql/item_func.h b/sql/item_func.h index 9700429d543..36a2f94b31d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1120,10 +1120,13 @@ public: name= a->name; name_length= a->name_length; } - double val_real() { return args[0]->val_real(); } - longlong val_int() { return args[0]->val_int(); } - String *val_str(String *str) { return args[0]->val_str(str); } - my_decimal *val_decimal(my_decimal *dec) { return args[0]->val_decimal(dec); } + double val_real() { return val_real_from_item(args[0]); } + longlong val_int() { return val_int_from_item(args[0]); } + String *val_str(String *str) { return val_str_from_item(args[0], str); } + my_decimal *val_decimal(my_decimal *dec) + { return val_decimal_from_item(args[0], dec); } + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { return get_date_from_item(args[0], ltime, fuzzydate); } const char *func_name() const { return "rollup_const"; } bool const_item() const { return 0; } Item_result result_type() const { return args[0]->result_type(); } From 6c5e4c9bc0d9ac30f7ec7ee334630bacb58687ba Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 22 Apr 2019 15:05:11 +0400 Subject: [PATCH 15/25] Fixing -Werror=format-overflow errors (found by gcc-8.3.1) --- sql/sp_head.cc | 2 +- storage/maria/ma_test3.c | 2 +- storage/maria/maria_chk.c | 4 ++-- storage/myisam/mi_test3.c | 2 +- storage/myisam/myisamchk.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f26115efd8b..852ba453090 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2885,7 +2885,7 @@ sp_head::show_routine_code(THD *thd) const char *format= "Instruction at position %u has m_ip=%u"; char tmp[sizeof(format) + 2*SP_INSTR_UINT_MAXLEN + 1]; - sprintf(tmp, format, ip, i->m_ip); + my_snprintf(tmp, sizeof(tmp), format, ip, i->m_ip); /* Since this is for debugging purposes only, we don't bother to introduce a special error code for it. diff --git a/storage/maria/ma_test3.c b/storage/maria/ma_test3.c index f81d5363c6b..604c2b676a4 100644 --- a/storage/maria/ma_test3.c +++ b/storage/maria/ma_test3.c @@ -362,7 +362,7 @@ int test_write(MARIA_HA *file,int id,int lock_type) maria_extra(file,HA_EXTRA_WRITE_CACHE,0); } - sprintf((char*) record.id,"%7ld", (long) getpid()); + my_snprintf((char*) record.id, sizeof(record.id), "%7ld", (long) getpid()); strnmov((char*) record.text,"Testing...", sizeof(record.text)); tries=(uint) rnd(100)+10; diff --git a/storage/maria/maria_chk.c b/storage/maria/maria_chk.c index d03c50891df..76edacee964 100644 --- a/storage/maria/maria_chk.c +++ b/storage/maria/maria_chk.c @@ -1699,8 +1699,8 @@ static void descript(HA_CHECK *param, register MARIA_HA *info, char *name) null_bit[0]=null_pos[0]=0; if (keyseg->null_bit) { - sprintf(null_bit,"%d",keyseg->null_bit); - sprintf(null_pos,"%ld",(long) keyseg->null_pos+1); + my_snprintf(null_bit, sizeof(null_bit), "%d", keyseg->null_bit); + my_snprintf(null_pos, sizeof(null_pos), "%ld", (long) keyseg->null_pos+1); } printf("%-7ld%-5d%-9s%-10s%-30s\n", (long) keyseg->start+1,keyseg->length, diff --git a/storage/myisam/mi_test3.c b/storage/myisam/mi_test3.c index e05398f7c4a..86a639ad2b0 100644 --- a/storage/myisam/mi_test3.c +++ b/storage/myisam/mi_test3.c @@ -364,7 +364,7 @@ int test_write(MI_INFO *file,int id,int lock_type) mi_extra(file,HA_EXTRA_WRITE_CACHE,0); } - sprintf((char*) record.id,"%7ld",(long) getpid()); + my_snprintf((char*) record.id, sizeof(record.id), "%7ld", (long) getpid()); strnmov((char*) record.text,"Testing...", sizeof(record.text)); tries=(uint) rnd(100)+10; diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c index dfff5720847..74e29a7621f 100644 --- a/storage/myisam/myisamchk.c +++ b/storage/myisam/myisamchk.c @@ -1404,8 +1404,8 @@ static void descript(HA_CHECK *param, register MI_INFO *info, char * name) null_bit[0]=null_pos[0]=0; if (keyseg->null_bit) { - sprintf(null_bit,"%d",keyseg->null_bit); - sprintf(null_pos,"%ld",(long) keyseg->null_pos+1); + my_snprintf(null_bit, sizeof(null_bit), "%d", keyseg->null_bit); + my_snprintf(null_pos, sizeof(null_pos), "%ld", (long) keyseg->null_pos+1); } printf("%-7ld%-5d%-9s%-10s%-30s\n", (long) keyseg->start+1,keyseg->length, From a4f7d859322ab771289abf13f50752266af43187 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 22 Apr 2019 23:28:44 +0400 Subject: [PATCH 16/25] MDEV-18920 Prepared statements with st_convexhull hang and eat 100% cpu. In the case of error when object shapes are half-collected we need to set the NULL at the vertice's list. --- mysql-test/r/gis-precise.result | 4 ++++ mysql-test/t/gis-precise.test | 6 ++++++ sql/gcalc_slicescan.cc | 2 ++ 3 files changed, 12 insertions(+) diff --git a/mysql-test/r/gis-precise.result b/mysql-test/r/gis-precise.result index 89e5c237413..76f72bb8102 100644 --- a/mysql-test/r/gis-precise.result +++ b/mysql-test/r/gis-precise.result @@ -505,6 +505,10 @@ GEOMETRYFROMTEXT('POINT(4599 60359)'), ) as relate_res; relate_res 0 +prepare s from 'do st_convexhull(st_aswkb(multipoint(point(-11702,15179),point(-5031,27960),point(-30557,11158),point(-27804,30314))))'; +execute s; +execute s; +deallocate prepare s; DROP TABLE IF EXISTS p1; CREATE PROCEDURE p1(dist DOUBLE, geom TEXT) BEGIN diff --git a/mysql-test/t/gis-precise.test b/mysql-test/t/gis-precise.test index 7391b2114f3..7626da650b4 100644 --- a/mysql-test/t/gis-precise.test +++ b/mysql-test/t/gis-precise.test @@ -381,5 +381,11 @@ SELECT ST_RELATE( 'F*FFFF**F' ) as relate_res; +# MDEV-18920 Prepared statements with st_convexhull hang and eat 100% cpu. +prepare s from 'do st_convexhull(st_aswkb(multipoint(point(-11702,15179),point(-5031,27960),point(-30557,11158),point(-27804,30314))))'; +execute s; +execute s; +deallocate prepare s; + --source include/gis_debug.inc diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc index ab48542add6..644ab4b8710 100644 --- a/sql/gcalc_slicescan.cc +++ b/sql/gcalc_slicescan.cc @@ -982,6 +982,8 @@ void Gcalc_heap::reset() { if (m_n_points) { + if (m_hook) + *m_hook= NULL; free_list(m_first); m_n_points= 0; } From 279a907fd0dea30be6d11fc4a5d63b1b98d0b329 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 22 Apr 2019 17:10:42 -0700 Subject: [PATCH 17/25] MDEV-17605 Statistics for InnoDB table is wrong if persistent statistics is used The command SHOW INDEXES ignored setting of the system variable use_stat_tables to the value of 'preferably' and and showed statistical data received from the engine. Similarly queries over the table STATISTICS from INFORMATION_SCHEMA ignored this setting. It happened because the function fill_schema_table_by_open() did not read any data from statistical tables. --- mysql-test/r/stat_tables.result | 69 ++++++++++++++++++++++++++ mysql-test/r/stat_tables_innodb.result | 69 ++++++++++++++++++++++++++ mysql-test/t/stat_tables.test | 54 ++++++++++++++++++++ sql/sql_class.h | 3 ++ sql/sql_show.cc | 5 ++ sql/sql_statistics.cc | 5 +- 6 files changed, 204 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/stat_tables.result b/mysql-test/r/stat_tables.result index 3ebc3b47833..bd3e9ed7a40 100644 --- a/mysql-test/r/stat_tables.result +++ b/mysql-test/r/stat_tables.result @@ -625,3 +625,72 @@ MAX(pk) NULL DROP TABLE t1; set use_stat_tables=@save_use_stat_tables; +# +# MDEV-17605: SHOW INDEXES with use_stat_tables='preferably' +# +set use_stat_tables='preferably'; +CREATE DATABASE dbt3_s001; +use dbt3_s001; +set @save_optimizer_switch=@@optimizer_switch; +set optimizer_switch='extended_keys=off'; +select * from mysql.table_stats; +db_name table_name cardinality +dbt3_s001 lineitem 6005 +select * from mysql.index_stats; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_receiptdate 1 2.6477 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +SHOW INDEXES FROM lineitem; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment +lineitem 0 PRIMARY 1 l_orderkey A 1500 NULL NULL BTREE +lineitem 0 PRIMARY 2 l_linenumber A 6005 NULL NULL BTREE +lineitem 1 i_l_shipdate 1 l_shipDATE A 2266 NULL NULL YES BTREE +lineitem 1 i_l_suppkey_partkey 1 l_partkey A 200 NULL NULL YES BTREE +lineitem 1 i_l_suppkey_partkey 2 l_suppkey A 699 NULL NULL YES BTREE +lineitem 1 i_l_partkey 1 l_partkey A 200 NULL NULL YES BTREE +lineitem 1 i_l_suppkey 1 l_suppkey A 10 NULL NULL YES BTREE +lineitem 1 i_l_receiptdate 1 l_receiptDATE A 2268 NULL NULL YES BTREE +lineitem 1 i_l_orderkey 1 l_orderkey A 1500 NULL NULL BTREE +lineitem 1 i_l_orderkey_quantity 1 l_orderkey A 1500 NULL NULL BTREE +lineitem 1 i_l_orderkey_quantity 2 l_quantity A 5771 NULL NULL YES BTREE +lineitem 1 i_l_commitdate 1 l_commitDATE A 2210 NULL NULL YES BTREE +SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='lineitem'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME NON_UNIQUE INDEX_SCHEMA INDEX_NAME SEQ_IN_INDEX COLUMN_NAME COLLATION CARDINALITY SUB_PART PACKED NULLABLE INDEX_TYPE COMMENT INDEX_COMMENT +def dbt3_s001 lineitem 0 dbt3_s001 PRIMARY 1 l_orderkey A 1500 NULL NULL BTREE +def dbt3_s001 lineitem 0 dbt3_s001 PRIMARY 2 l_linenumber A 6005 NULL NULL BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_shipdate 1 l_shipDATE A 2266 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_suppkey_partkey 1 l_partkey A 200 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_suppkey_partkey 2 l_suppkey A 699 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_partkey 1 l_partkey A 200 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_suppkey 1 l_suppkey A 10 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_receiptdate 1 l_receiptDATE A 2268 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_orderkey 1 l_orderkey A 1500 NULL NULL BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_orderkey_quantity 1 l_orderkey A 1500 NULL NULL BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_orderkey_quantity 2 l_quantity A 5771 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_commitdate 1 l_commitDATE A 2210 NULL NULL YES BTREE +SELECT +COUNT(DISTINCT l_orderkey), COUNT(DISTINCT l_orderkey,l_linenumber), +COUNT(DISTINCT l_shipDATE), +COUNT(DISTINCT l_partkey), COUNT(DISTINCT l_partkey,l_suppkey), +COUNT(DISTINCT l_suppkey), COUNT(DISTINCT l_receiptDATE), +COUNT(DISTINCT l_orderkey, l_quantity), COUNT(DISTINCT l_commitDATE) +FROM lineitem; +COUNT(DISTINCT l_orderkey) COUNT(DISTINCT l_orderkey,l_linenumber) COUNT(DISTINCT l_shipDATE) COUNT(DISTINCT l_partkey) COUNT(DISTINCT l_partkey,l_suppkey) COUNT(DISTINCT l_suppkey) COUNT(DISTINCT l_receiptDATE) COUNT(DISTINCT l_orderkey, l_quantity) COUNT(DISTINCT l_commitDATE) +1500 6005 2266 200 700 10 2268 5772 2211 +set optimizer_switch=@save_optimizer_switch; +DROP DATABASE dbt3_s001; +delete from mysql.table_stats; +delete from mysql.column_stats; +delete from mysql.index_stats; +set @save_optimizer_switch=@@optimizer_switch; +set use_stat_tables=@save_use_stat_tables; diff --git a/mysql-test/r/stat_tables_innodb.result b/mysql-test/r/stat_tables_innodb.result index b0f794c6cee..579d19462e7 100644 --- a/mysql-test/r/stat_tables_innodb.result +++ b/mysql-test/r/stat_tables_innodb.result @@ -652,5 +652,74 @@ MAX(pk) NULL DROP TABLE t1; set use_stat_tables=@save_use_stat_tables; +# +# MDEV-17605: SHOW INDEXES with use_stat_tables='preferably' +# +set use_stat_tables='preferably'; +CREATE DATABASE dbt3_s001; +use dbt3_s001; +set @save_optimizer_switch=@@optimizer_switch; +set optimizer_switch='extended_keys=off'; +select * from mysql.table_stats; +db_name table_name cardinality +dbt3_s001 lineitem 6005 +select * from mysql.index_stats; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_receiptdate 1 2.6477 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +SHOW INDEXES FROM lineitem; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment +lineitem 0 PRIMARY 1 l_orderkey A 1500 NULL NULL BTREE +lineitem 0 PRIMARY 2 l_linenumber A 6005 NULL NULL BTREE +lineitem 1 i_l_shipdate 1 l_shipDATE A 2266 NULL NULL YES BTREE +lineitem 1 i_l_suppkey_partkey 1 l_partkey A 200 NULL NULL YES BTREE +lineitem 1 i_l_suppkey_partkey 2 l_suppkey A 699 NULL NULL YES BTREE +lineitem 1 i_l_partkey 1 l_partkey A 200 NULL NULL YES BTREE +lineitem 1 i_l_suppkey 1 l_suppkey A 10 NULL NULL YES BTREE +lineitem 1 i_l_receiptdate 1 l_receiptDATE A 2268 NULL NULL YES BTREE +lineitem 1 i_l_orderkey 1 l_orderkey A 1500 NULL NULL BTREE +lineitem 1 i_l_orderkey_quantity 1 l_orderkey A 1500 NULL NULL BTREE +lineitem 1 i_l_orderkey_quantity 2 l_quantity A 5771 NULL NULL YES BTREE +lineitem 1 i_l_commitdate 1 l_commitDATE A 2210 NULL NULL YES BTREE +SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='lineitem'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME NON_UNIQUE INDEX_SCHEMA INDEX_NAME SEQ_IN_INDEX COLUMN_NAME COLLATION CARDINALITY SUB_PART PACKED NULLABLE INDEX_TYPE COMMENT INDEX_COMMENT +def dbt3_s001 lineitem 0 dbt3_s001 PRIMARY 1 l_orderkey A 1500 NULL NULL BTREE +def dbt3_s001 lineitem 0 dbt3_s001 PRIMARY 2 l_linenumber A 6005 NULL NULL BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_shipdate 1 l_shipDATE A 2266 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_suppkey_partkey 1 l_partkey A 200 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_suppkey_partkey 2 l_suppkey A 699 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_partkey 1 l_partkey A 200 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_suppkey 1 l_suppkey A 10 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_receiptdate 1 l_receiptDATE A 2268 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_orderkey 1 l_orderkey A 1500 NULL NULL BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_orderkey_quantity 1 l_orderkey A 1500 NULL NULL BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_orderkey_quantity 2 l_quantity A 5771 NULL NULL YES BTREE +def dbt3_s001 lineitem 1 dbt3_s001 i_l_commitdate 1 l_commitDATE A 2210 NULL NULL YES BTREE +SELECT +COUNT(DISTINCT l_orderkey), COUNT(DISTINCT l_orderkey,l_linenumber), +COUNT(DISTINCT l_shipDATE), +COUNT(DISTINCT l_partkey), COUNT(DISTINCT l_partkey,l_suppkey), +COUNT(DISTINCT l_suppkey), COUNT(DISTINCT l_receiptDATE), +COUNT(DISTINCT l_orderkey, l_quantity), COUNT(DISTINCT l_commitDATE) +FROM lineitem; +COUNT(DISTINCT l_orderkey) COUNT(DISTINCT l_orderkey,l_linenumber) COUNT(DISTINCT l_shipDATE) COUNT(DISTINCT l_partkey) COUNT(DISTINCT l_partkey,l_suppkey) COUNT(DISTINCT l_suppkey) COUNT(DISTINCT l_receiptDATE) COUNT(DISTINCT l_orderkey, l_quantity) COUNT(DISTINCT l_commitDATE) +1500 6005 2266 200 700 10 2268 5772 2211 +set optimizer_switch=@save_optimizer_switch; +DROP DATABASE dbt3_s001; +delete from mysql.table_stats; +delete from mysql.column_stats; +delete from mysql.index_stats; +set @save_optimizer_switch=@@optimizer_switch; +set use_stat_tables=@save_use_stat_tables; set optimizer_switch=@save_optimizer_switch_for_stat_tables_test; SET SESSION STORAGE_ENGINE=DEFAULT; diff --git a/mysql-test/t/stat_tables.test b/mysql-test/t/stat_tables.test index b89ab2bbd2d..97f9f08569f 100644 --- a/mysql-test/t/stat_tables.test +++ b/mysql-test/t/stat_tables.test @@ -402,3 +402,57 @@ SELECT MAX(pk) FROM t1; DROP TABLE t1; set use_stat_tables=@save_use_stat_tables; + +--echo # +--echo # MDEV-17605: SHOW INDEXES with use_stat_tables='preferably' +--echo # + +set use_stat_tables='preferably'; + +CREATE DATABASE dbt3_s001; + +use dbt3_s001; + +set @save_optimizer_switch=@@optimizer_switch; +set optimizer_switch='extended_keys=off'; + +--disable_query_log +--disable_result_log +--disable_warnings +--source include/dbt3_s001.inc +create index i_p_retailprice on part(p_retailprice); +delete from mysql.table_stats; +delete from mysql.column_stats; +delete from mysql.index_stats; +ANALYZE TABLE lineitem; +FLUSH TABLE mysql.table_stats, mysql.index_stats; +--enable_warnings +--enable_result_log +--enable_query_log + +select * from mysql.table_stats; +select * from mysql.index_stats; + +SHOW INDEXES FROM lineitem; + +SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='lineitem'; + +SELECT + COUNT(DISTINCT l_orderkey), COUNT(DISTINCT l_orderkey,l_linenumber), + COUNT(DISTINCT l_shipDATE), + COUNT(DISTINCT l_partkey), COUNT(DISTINCT l_partkey,l_suppkey), + COUNT(DISTINCT l_suppkey), COUNT(DISTINCT l_receiptDATE), + COUNT(DISTINCT l_orderkey, l_quantity), COUNT(DISTINCT l_commitDATE) +FROM lineitem; + +set optimizer_switch=@save_optimizer_switch; + +DROP DATABASE dbt3_s001; + +delete from mysql.table_stats; +delete from mysql.column_stats; +delete from mysql.index_stats; + +set @save_optimizer_switch=@@optimizer_switch; + +set use_stat_tables=@save_use_stat_tables; diff --git a/sql/sql_class.h b/sql/sql_class.h index 6640e02147a..2517f5cc06f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2199,6 +2199,9 @@ public: */ bool create_tmp_table_for_derived; + /* The flag to force reading statistics from EITS tables */ + bool force_read_stats; + bool save_prep_leaf_list; /* container for handler's private per-connection data */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 73f0f564c4c..46914ea14c4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4273,6 +4273,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, 'only_view_structure()'. */ lex->sql_command= SQLCOM_SHOW_FIELDS; + thd->force_read_stats= get_schema_table_idx(schema_table) == SCH_STATISTICS; result= (open_temporary_tables(thd, table_list) || open_normal_and_derived_tables(thd, table_list, (MYSQL_OPEN_IGNORE_FLUSH | @@ -4280,6 +4281,10 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, (can_deadlock ? MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)), DT_PREPARE | DT_CREATE)); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + thd->force_read_stats= false; + /* Restore old value of sql_command back as it is being looked at in process_table() function. diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index d3a2094e272..b435971a4d6 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -2177,7 +2177,10 @@ inline bool statistics_for_command_is_needed(THD *thd) { if (thd->bootstrap || thd->variables.use_stat_tables == NEVER) return FALSE; - + + if (thd->force_read_stats) + return TRUE; + switch(thd->lex->sql_command) { case SQLCOM_SELECT: case SQLCOM_INSERT: From 9dcfd6be94bd83d14fd48d69ce487eb0ea3fe37f Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 23 Apr 2019 09:11:42 +0400 Subject: [PATCH 18/25] MDEV-9465 The constructor StringBuffer(const char *str, size_t length, const CHARSET_INFO *cs) looks suspicious Removing the suspicious constructor, it's not used in the code anyway. --- sql/sql_string.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sql/sql_string.h b/sql/sql_string.h index b8979d397e6..ba28dbeea26 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -649,11 +649,6 @@ public: { length(0); } - StringBuffer(const char *str, size_t length_arg, CHARSET_INFO *cs) - : String(buff, buff_sz, cs) - { - set(str, length_arg, cs); - } }; From 6b5d3c51b3d4260ba692bb84c64eb2705635e051 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 23 Apr 2019 12:44:09 +0100 Subject: [PATCH 19/25] Do fast exit with error code and FATAL ERROR message, if innodb cannot start during prepare. Otherwise, it is possible for "prepare" to exit with error code 0, even if it did not work at all. --- extra/mariabackup/xtrabackup.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 2b8abc04446..4b6ba960c05 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -1989,10 +1989,8 @@ static bool innodb_init() { dberr_t err = innobase_start_or_create_for_mysql(); if (err != DB_SUCCESS) { - msg("mariabackup: innodb_init() returned %d (%s).", + die("mariabackup: innodb_init() returned %d (%s).", err, ut_strerr(err)); - innodb_shutdown(); - return(TRUE); } return(FALSE); From 38b6dc5a3d11d9917c02ad10293807b5a0a4f5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 23 Apr 2019 17:25:07 +0300 Subject: [PATCH 20/25] Fix the linking of async_example --- client/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6ef14ff7283..56e891ea284 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2008, 2018, MariaDB Corporation +# Copyright (c) 2008, 2019, 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 @@ -80,7 +80,7 @@ ENDIF(WIN32) ADD_EXECUTABLE(async_example async_example.c) TARGET_LINK_LIBRARIES(async_example mysqlclient) -SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin +SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin async_example PROPERTIES HAS_CXX TRUE) ADD_DEFINITIONS(-DHAVE_DLOPEN) From e5aa8ea52552759453fdd86eeab0007df1781b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 23 Apr 2019 17:56:43 +0300 Subject: [PATCH 21/25] MDEV-18139 ALTER IGNORE ... ADD FOREIGN KEY causes bogus error dict_create_foreign_constraints_low(): Tolerate the keywords IGNORE and ONLINE between the keywords ALTER and TABLE. We should really remove the hacky FOREIGN KEY constraint parser from InnoDB. --- mysql-test/suite/innodb/r/foreign_key.result | 20 +++++++++++++++++ mysql-test/suite/innodb/t/foreign_key.test | 22 +++++++++++++++++++ storage/innobase/dict/dict0dict.c | 23 +++++++++++++++----- storage/xtradb/dict/dict0dict.c | 23 +++++++++++++++----- 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 mysql-test/suite/innodb/r/foreign_key.result create mode 100644 mysql-test/suite/innodb/t/foreign_key.test diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result new file mode 100644 index 00000000000..39f99513ee0 --- /dev/null +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -0,0 +1,20 @@ +# +# MDEV-18630 Conditional jump or move depends on uninitialised value +# in ib_push_warning / dict_create_foreign_constraints_low +# +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (a) REFERENCES t2 (b); +ERROR HY000: Can't create table 'test.#sql-temporary' (errno: 150) +SHOW WARNINGS; +Level Code Message +Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Referenced table `test`.`t2` not found in the data dictionary near 'FOREIGN KEY (a) REFERENCES t2 (b)'. +Error 1005 Can't create table 'test.#sql-temporary' (errno: 150) +DROP TABLE t1; +# +# MDEV-18139 ALTER IGNORE ... ADD FOREIGN KEY causes bogus error +# +CREATE TABLE t1 (f1 INT, f2 INT, f3 INT, KEY(f1)) ENGINE=InnoDB; +CREATE TABLE t2 (f INT, KEY(f)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FOREIGN KEY (f2) REFERENCES t2 (f); +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f1); +DROP TABLE t1, t2; diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test new file mode 100644 index 00000000000..281193d60ec --- /dev/null +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -0,0 +1,22 @@ +--source include/have_innodb.inc + +--echo # +--echo # MDEV-18630 Conditional jump or move depends on uninitialised value +--echo # in ib_push_warning / dict_create_foreign_constraints_low +--echo # +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +--replace_regex /#sql-[0-9_a-f-]*/#sql-temporary/ +--error ER_CANT_CREATE_TABLE +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (a) REFERENCES t2 (b); +--replace_regex /#sql-[0-9_a-f-]*/#sql-temporary/ +SHOW WARNINGS; +DROP TABLE t1; + +--echo # +--echo # MDEV-18139 ALTER IGNORE ... ADD FOREIGN KEY causes bogus error +--echo # +CREATE TABLE t1 (f1 INT, f2 INT, f3 INT, KEY(f1)) ENGINE=InnoDB; +CREATE TABLE t2 (f INT, KEY(f)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FOREIGN KEY (f2) REFERENCES t2 (f); +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f1); +DROP TABLE t1, t2; diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index 588c2968b6a..fcb9a48e4c0 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -3787,6 +3788,9 @@ dict_create_foreign_constraints_low( } goto loop; + } else { + strncpy(create_name, name, sizeof create_name); + create_name[(sizeof create_name) - 1] = '\0'; } if (table == NULL) { @@ -3811,11 +3815,20 @@ dict_create_foreign_constraints_low( goto loop; } - ptr = dict_accept(cs, ptr, "TABLE", &success); - - if (!success) { - - goto loop; + orig = ptr; + for (;;) { + ptr = dict_accept(cs, ptr, "TABLE", &success); + if (success) { + break; + } + ptr = dict_accept(cs, ptr, "ONLINE", &success); + if (success) { + continue; + } + ptr = dict_accept(cs, ptr, "IGNORE", &success); + if (!success) { + goto loop; + } } /* We are doing an ALTER TABLE: scan the table name we are altering */ diff --git a/storage/xtradb/dict/dict0dict.c b/storage/xtradb/dict/dict0dict.c index 5da4509599e..12d3a600e54 100644 --- a/storage/xtradb/dict/dict0dict.c +++ b/storage/xtradb/dict/dict0dict.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -3921,6 +3922,9 @@ dict_create_foreign_constraints_low( } goto loop; + } else { + strncpy(create_name, name, sizeof create_name); + create_name[(sizeof create_name) - 1] = '\0'; } if (table == NULL) { @@ -3945,11 +3949,20 @@ dict_create_foreign_constraints_low( goto loop; } - ptr = dict_accept(cs, ptr, "TABLE", &success); - - if (!success) { - - goto loop; + orig = ptr; + for (;;) { + ptr = dict_accept(cs, ptr, "TABLE", &success); + if (success) { + break; + } + ptr = dict_accept(cs, ptr, "ONLINE", &success); + if (success) { + continue; + } + ptr = dict_accept(cs, ptr, "IGNORE", &success); + if (!success) { + goto loop; + } } /* We are doing an ALTER TABLE: scan the table name we are altering */ From 5fc8dd8b82e995b3c685f4e71700201097431358 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 23 Apr 2019 23:10:46 -0700 Subject: [PATCH 22/25] MDEV-17796 WHERE filter is ignored by DISTINCT IFNULL(GROUP_CONCAT(X), Y) with GROUP BY + ORDER BY The method JOIN::create_postjoin_aggr_table() should not call call JOIN::add_sorting_to_table() unless the first non-constant join table is passed as the first parameter to the method. --- mysql-test/r/order_by.result | 13 +++++++++++++ mysql-test/t/order_by.test | 14 ++++++++++++++ sql/sql_select.cc | 3 ++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index 1ca3034c610..28b63dab22e 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -3253,3 +3253,16 @@ Warnings: Note 1003 select `test`.`wings`.`id` AS `wing_id`,`test`.`wings`.`department_id` AS `department_id` from `test`.`wings` semi join (`test`.`books`) where `test`.`books`.`library_id` = 8663 and `test`.`books`.`scheduled_for_removal` = 0 and `test`.`wings`.`id` = `test`.`books`.`wings_id` order by `test`.`wings`.`id` set optimizer_switch= @save_optimizer_switch; DROP TABLE books, wings; +# +# MDEV-17796: query with DISTINCT, GROUP BY and ORDER BY +# +CREATE TABLE t1 (id int, gr int, v1 varchar(10)); +INSERT INTO t1 VALUES (1,1,'A'), (2,2,'B'), (3,3,NULL), (4,4,'C'); +SELECT DISTINCT NULLIF(GROUP_CONCAT(v1), null) FROM t1 +WHERE gr in (4,2) +GROUP BY id +ORDER BY id+1 DESC; +NULLIF(GROUP_CONCAT(v1), null) +C +B +DROP TABLE t1; diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index b047a31c863..d67c67de89c 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -2187,3 +2187,17 @@ eval explain extended $q; set optimizer_switch= @save_optimizer_switch; DROP TABLE books, wings; + +--echo # +--echo # MDEV-17796: query with DISTINCT, GROUP BY and ORDER BY +--echo # + +CREATE TABLE t1 (id int, gr int, v1 varchar(10)); +INSERT INTO t1 VALUES (1,1,'A'), (2,2,'B'), (3,3,NULL), (4,4,'C'); + +SELECT DISTINCT NULLIF(GROUP_CONCAT(v1), null) FROM t1 + WHERE gr in (4,2) +GROUP BY id +ORDER BY id+1 DESC; + +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c3acdf7415b..3c1cea6be51 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3007,7 +3007,8 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List *table_fields, if (setup_sum_funcs(thd, sum_funcs)) goto err; - if (!group_list && !table->distinct && order && simple_order) + if (!group_list && !table->distinct && order && simple_order && + tab == join_tab + const_tables) { DBUG_PRINT("info",("Sorting for order")); THD_STAGE_INFO(thd, stage_sorting_for_order); From cb8d888c42aa9504db2df686f0e963b99079e287 Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Wed, 24 Apr 2019 11:40:52 +0530 Subject: [PATCH 23/25] MDEV-17260: Memory leaks in mysqlbinlog Problem: ======== The mysqlbinlog tool is leaking memory, causing failures in various tests when compiling and testing with AddressSanitizer or LeakSanitizer like this: cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN:BOOL=ON /path/to/source make -j$(nproc) cd mysql-test ASAN_OPTIONS=abort_on_error=1 ./mtr --parallel=auto rpl.rpl_row_mysqlbinlog CURRENT_TEST: rpl.rpl_row_mysqlbinlog Direct leak of 112 byte(s) in 1 object(s) allocated from: #0 0x4eff87 in __interceptor_malloc (/dev/shm/5.5/client/mysqlbinlog+0x4eff87) #1 0x60eaab in my_malloc /mariadb/5.5/mysys/my_malloc.c:41:10 #2 0x5300dd in Log_event::read_log_event(char const*, unsigned int, char const**, Format_description_log_event const*, char) /mariadb/5.5/sql/log_event.cc:1568: #3 0x564a9c in dump_remote_log_entries(st_print_event_info*, char const*) /mariadb/5.5/client/mysqlbinlog.cc:1978:17 Analysis: ======== 'mysqlbinlog' tool is being used to read binary log events from a remote server. While reading binary log, if a fake rotate event is found following actions are taken. If 'to-last-log' option is specified, then fake rotate event is processed. In the absence of 'to-last-log' skip the fake rotate event. In this skipped case the fake rotate event object is not getting cleaned up resulting in memory leak. Fix: === Cleanup the fake rotate event. This issues is already fixed in MariaDB 10.0.23 and higher versions as part of commit c3018b0ff4fb02c029787d03867adf0530607bab --- client/mysqlbinlog.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 250dc609891..1a11e3e697b 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -2020,6 +2020,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, if ((rev->ident_len != logname_len) || memcmp(rev->new_log_ident, logname, logname_len)) { + delete ev; DBUG_RETURN(OK_CONTINUE); } /* @@ -2028,6 +2029,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, log. If we are running with to_last_remote_log, we print it, because it serves as a useful marker between binlogs then. */ + delete ev; continue; } len= 1; // fake Rotate, so don't increment old_off From 1f1a61cfc41a01ffa65d568eebdc037a54b5c463 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Wed, 24 Apr 2019 00:16:56 +0530 Subject: [PATCH 24/25] MDEV-15837: Assertion `item1->type() == Item::FIELD_ITEM && item2->type() == Item::FIELD_ITEM' failed in compare_order_elements function The issue here is the function compare_order_lists() is called for the order by list of the window functions so that those window function that can be computed together are adjacent. So in the function compare_order_list we iterate over all the elements in the order list of the two functions and compare the items in their order by clause. The function compare_order_elements() is called for each item in the order by clause. This function assumes that all the items that are in the order by list would be of the type Item::FIELD_ITEM. The case we have is that we have constants in the order by clause. We should ignore the constant and only compare items of the type Item::FIELD_ITEM in compare_order_elements() --- mysql-test/r/win.result | 12 ++++++++++++ mysql-test/t/win.test | 10 ++++++++++ sql/sql_window.cc | 16 ++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 083b5dfd16d..0ddffc551dc 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -3506,5 +3506,17 @@ id a b sum_a_b 2 2 2 4 drop table t1; # +# MDEV-15837: Assertion `item1->type() == Item::FIELD_ITEM && item2->type() == Item::FIELD_ITEM' +# failed in compare_order_elements function +# +CREATE TABLE t1 (a1 int); +insert into t1 values (1),(2),(3); +SELECT rank() OVER (ORDER BY 1), ROW_NUMBER() OVER (ORDER BY (EXPORT_SET(5,'Y','N',',',4))) FROM t1; +rank() OVER (ORDER BY 1) ROW_NUMBER() OVER (ORDER BY (EXPORT_SET(5,'Y','N',',',4))) +1 1 +1 2 +1 3 +drop table t1; +# # End of 10.2 tests # diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index 0f02b1414a0..fd31e9d4bd9 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -2255,6 +2255,16 @@ select e.id, from t1 e; drop table t1; +--echo # +--echo # MDEV-15837: Assertion `item1->type() == Item::FIELD_ITEM && item2->type() == Item::FIELD_ITEM' +--echo # failed in compare_order_elements function +--echo # + +CREATE TABLE t1 (a1 int); +insert into t1 values (1),(2),(3); +SELECT rank() OVER (ORDER BY 1), ROW_NUMBER() OVER (ORDER BY (EXPORT_SET(5,'Y','N',',',4))) FROM t1; +drop table t1; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 465c6ae032c..310cf5bfd91 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -342,6 +342,22 @@ int compare_order_lists(SQL_I_List *part_list1, for ( ; elem1 && elem2; elem1= elem1->next, elem2= elem2->next) { int cmp; + // remove all constants as we don't need them for comparision + while(elem1 && ((*elem1->item)->real_item())->const_item()) + { + elem1= elem1->next; + continue; + } + + while(elem2 && ((*elem2->item)->real_item())->const_item()) + { + elem2= elem2->next; + continue; + } + + if (!elem1 || !elem2) + break; + if ((cmp= compare_order_elements(elem1, elem2))) return cmp; } From d5da8ae04d57556f517c0f03afeafe73c6cc75d1 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 24 Apr 2019 12:31:24 +0530 Subject: [PATCH 25/25] MDEV-15772 Potential list overrun during XA recovery InnoDB could return the same list again and again if the buffer passed to trx_recover_for_mysql() is smaller than the number of transactions that InnoDB recovered in XA PREPARE state. We introduce the transaction state TRX_PREPARED_RECOVERED, which is like TRX_PREPARED, but will be set during trx_recover_for_mysql() so that each transaction will only be returned once. Because init_server_components() is invoking ha_recover() twice, we must reset the state of the transactions back to TRX_PREPARED after returning the complete list, so that repeated traversals will see the complete list again, instead of seeing an empty list. Without this tweak, the test main.tc_heuristic_recover would hang in MariaDB 10.1. --- mysql-test/suite/innodb/r/xa_debug.result | 310 ++++++++++++++++++++++ mysql-test/suite/innodb/t/xa_debug.test | 45 ++++ sql/handler.cc | 1 + storage/innobase/include/trx0sys.ic | 12 +- storage/innobase/include/trx0trx.h | 3 + storage/innobase/lock/lock0lock.c | 39 +-- storage/innobase/read/read0read.c | 36 ++- storage/innobase/trx/trx0roll.c | 2 + storage/innobase/trx/trx0sys.c | 3 +- storage/innobase/trx/trx0trx.c | 27 +- storage/xtradb/include/trx0trx.h | 3 + storage/xtradb/lock/lock0lock.c | 39 +-- storage/xtradb/trx/trx0roll.c | 2 + storage/xtradb/trx/trx0sys.c | 3 +- storage/xtradb/trx/trx0trx.c | 27 +- 15 files changed, 481 insertions(+), 71 deletions(-) create mode 100644 mysql-test/suite/innodb/r/xa_debug.result create mode 100644 mysql-test/suite/innodb/t/xa_debug.test diff --git a/mysql-test/suite/innodb/r/xa_debug.result b/mysql-test/suite/innodb/r/xa_debug.result new file mode 100644 index 00000000000..43a3b299468 --- /dev/null +++ b/mysql-test/suite/innodb/r/xa_debug.result @@ -0,0 +1,310 @@ +call mtr.add_suppression("Found 50 prepared XA transactions"); +create table t1 (a int) engine=innodb; +insert into t1 values(1); +xa start 'test50'; +insert into t1 values(1); +xa end 'test50'; +xa prepare 'test50'; +xa start 'test49'; +insert into t1 values(1); +xa end 'test49'; +xa prepare 'test49'; +xa start 'test48'; +insert into t1 values(1); +xa end 'test48'; +xa prepare 'test48'; +xa start 'test47'; +insert into t1 values(1); +xa end 'test47'; +xa prepare 'test47'; +xa start 'test46'; +insert into t1 values(1); +xa end 'test46'; +xa prepare 'test46'; +xa start 'test45'; +insert into t1 values(1); +xa end 'test45'; +xa prepare 'test45'; +xa start 'test44'; +insert into t1 values(1); +xa end 'test44'; +xa prepare 'test44'; +xa start 'test43'; +insert into t1 values(1); +xa end 'test43'; +xa prepare 'test43'; +xa start 'test42'; +insert into t1 values(1); +xa end 'test42'; +xa prepare 'test42'; +xa start 'test41'; +insert into t1 values(1); +xa end 'test41'; +xa prepare 'test41'; +xa start 'test40'; +insert into t1 values(1); +xa end 'test40'; +xa prepare 'test40'; +xa start 'test39'; +insert into t1 values(1); +xa end 'test39'; +xa prepare 'test39'; +xa start 'test38'; +insert into t1 values(1); +xa end 'test38'; +xa prepare 'test38'; +xa start 'test37'; +insert into t1 values(1); +xa end 'test37'; +xa prepare 'test37'; +xa start 'test36'; +insert into t1 values(1); +xa end 'test36'; +xa prepare 'test36'; +xa start 'test35'; +insert into t1 values(1); +xa end 'test35'; +xa prepare 'test35'; +xa start 'test34'; +insert into t1 values(1); +xa end 'test34'; +xa prepare 'test34'; +xa start 'test33'; +insert into t1 values(1); +xa end 'test33'; +xa prepare 'test33'; +xa start 'test32'; +insert into t1 values(1); +xa end 'test32'; +xa prepare 'test32'; +xa start 'test31'; +insert into t1 values(1); +xa end 'test31'; +xa prepare 'test31'; +xa start 'test30'; +insert into t1 values(1); +xa end 'test30'; +xa prepare 'test30'; +xa start 'test29'; +insert into t1 values(1); +xa end 'test29'; +xa prepare 'test29'; +xa start 'test28'; +insert into t1 values(1); +xa end 'test28'; +xa prepare 'test28'; +xa start 'test27'; +insert into t1 values(1); +xa end 'test27'; +xa prepare 'test27'; +xa start 'test26'; +insert into t1 values(1); +xa end 'test26'; +xa prepare 'test26'; +xa start 'test25'; +insert into t1 values(1); +xa end 'test25'; +xa prepare 'test25'; +xa start 'test24'; +insert into t1 values(1); +xa end 'test24'; +xa prepare 'test24'; +xa start 'test23'; +insert into t1 values(1); +xa end 'test23'; +xa prepare 'test23'; +xa start 'test22'; +insert into t1 values(1); +xa end 'test22'; +xa prepare 'test22'; +xa start 'test21'; +insert into t1 values(1); +xa end 'test21'; +xa prepare 'test21'; +xa start 'test20'; +insert into t1 values(1); +xa end 'test20'; +xa prepare 'test20'; +xa start 'test19'; +insert into t1 values(1); +xa end 'test19'; +xa prepare 'test19'; +xa start 'test18'; +insert into t1 values(1); +xa end 'test18'; +xa prepare 'test18'; +xa start 'test17'; +insert into t1 values(1); +xa end 'test17'; +xa prepare 'test17'; +xa start 'test16'; +insert into t1 values(1); +xa end 'test16'; +xa prepare 'test16'; +xa start 'test15'; +insert into t1 values(1); +xa end 'test15'; +xa prepare 'test15'; +xa start 'test14'; +insert into t1 values(1); +xa end 'test14'; +xa prepare 'test14'; +xa start 'test13'; +insert into t1 values(1); +xa end 'test13'; +xa prepare 'test13'; +xa start 'test12'; +insert into t1 values(1); +xa end 'test12'; +xa prepare 'test12'; +xa start 'test11'; +insert into t1 values(1); +xa end 'test11'; +xa prepare 'test11'; +xa start 'test10'; +insert into t1 values(1); +xa end 'test10'; +xa prepare 'test10'; +xa start 'test9'; +insert into t1 values(1); +xa end 'test9'; +xa prepare 'test9'; +xa start 'test8'; +insert into t1 values(1); +xa end 'test8'; +xa prepare 'test8'; +xa start 'test7'; +insert into t1 values(1); +xa end 'test7'; +xa prepare 'test7'; +xa start 'test6'; +insert into t1 values(1); +xa end 'test6'; +xa prepare 'test6'; +xa start 'test5'; +insert into t1 values(1); +xa end 'test5'; +xa prepare 'test5'; +xa start 'test4'; +insert into t1 values(1); +xa end 'test4'; +xa prepare 'test4'; +xa start 'test3'; +insert into t1 values(1); +xa end 'test3'; +xa prepare 'test3'; +xa start 'test2'; +insert into t1 values(1); +xa end 'test2'; +xa prepare 'test2'; +xa start 'test1'; +insert into t1 values(1); +xa end 'test1'; +xa prepare 'test1'; +xa recover; +formatID gtrid_length bqual_length data +1 5 0 test1 +1 5 0 test2 +1 5 0 test3 +1 5 0 test4 +1 5 0 test5 +1 5 0 test6 +1 5 0 test7 +1 5 0 test8 +1 5 0 test9 +1 6 0 test10 +1 6 0 test11 +1 6 0 test12 +1 6 0 test13 +1 6 0 test14 +1 6 0 test15 +1 6 0 test16 +1 6 0 test17 +1 6 0 test18 +1 6 0 test19 +1 6 0 test20 +1 6 0 test21 +1 6 0 test22 +1 6 0 test23 +1 6 0 test24 +1 6 0 test25 +1 6 0 test26 +1 6 0 test27 +1 6 0 test28 +1 6 0 test29 +1 6 0 test30 +1 6 0 test31 +1 6 0 test32 +1 6 0 test33 +1 6 0 test34 +1 6 0 test35 +1 6 0 test36 +1 6 0 test37 +1 6 0 test38 +1 6 0 test39 +1 6 0 test40 +1 6 0 test41 +1 6 0 test42 +1 6 0 test43 +1 6 0 test44 +1 6 0 test45 +1 6 0 test46 +1 6 0 test47 +1 6 0 test48 +1 6 0 test49 +1 6 0 test50 +xa recover; +formatID gtrid_length bqual_length data +1 5 0 test1 +1 5 0 test2 +1 5 0 test3 +1 5 0 test4 +1 5 0 test5 +1 5 0 test6 +1 5 0 test7 +1 5 0 test8 +1 5 0 test9 +1 6 0 test10 +1 6 0 test11 +1 6 0 test12 +1 6 0 test13 +1 6 0 test14 +1 6 0 test15 +1 6 0 test16 +1 6 0 test17 +1 6 0 test18 +1 6 0 test19 +1 6 0 test20 +1 6 0 test21 +1 6 0 test22 +1 6 0 test23 +1 6 0 test24 +1 6 0 test25 +1 6 0 test26 +1 6 0 test27 +1 6 0 test28 +1 6 0 test29 +1 6 0 test30 +1 6 0 test31 +1 6 0 test32 +1 6 0 test33 +1 6 0 test34 +1 6 0 test35 +1 6 0 test36 +1 6 0 test37 +1 6 0 test38 +1 6 0 test39 +1 6 0 test40 +1 6 0 test41 +1 6 0 test42 +1 6 0 test43 +1 6 0 test44 +1 6 0 test45 +1 6 0 test46 +1 6 0 test47 +1 6 0 test48 +1 6 0 test49 +1 6 0 test50 +xa recover; +formatID gtrid_length bqual_length data +drop table t1; diff --git a/mysql-test/suite/innodb/t/xa_debug.test b/mysql-test/suite/innodb/t/xa_debug.test new file mode 100644 index 00000000000..5724891bb65 --- /dev/null +++ b/mysql-test/suite/innodb/t/xa_debug.test @@ -0,0 +1,45 @@ +-- source include/have_innodb.inc +-- source include/have_debug.inc +-- source include/not_embedded.inc + +call mtr.add_suppression("Found 50 prepared XA transactions"); +create table t1 (a int) engine=innodb; +insert into t1 values(1); + +let $trial = 50; +while ($trial) +{ +--connect (con$trial, localhost, root,,) +let $st_pre = `select concat('test', $trial)`; +eval xa start '$st_pre'; +insert into t1 values(1); +eval xa end '$st_pre'; +eval xa prepare '$st_pre'; +dec $trial; +} + +connection default; +# Kill and restart the server. +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 0 +-- source include/wait_until_disconnected.inc + +-- exec echo "restart:--debug_dbug=+d,min_xa_len" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc +-- disable_reconnect +--sorted_result +xa recover; +--sorted_result +xa recover; +--disable_query_log +let $trial = 50; +while ($trial) +{ +let $st_pre = `select concat('test', $trial)`; +eval xa commit '$st_pre'; +dec $trial; +} +--enable_query_log +xa recover; +drop table t1; diff --git a/sql/handler.cc b/sql/handler.cc index ab4d9fd37c9..8c6270a042e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1740,6 +1740,7 @@ int ha_recover(HASH *commit_list) for (info.len= MAX_XID_LIST_SIZE ; info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2) { + DBUG_EXECUTE_IF("min_xa_len", info.len = 16;); info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0)); } if (!info.list) diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index 4e7c0ee1f3a..5d33f348822 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -353,10 +354,13 @@ trx_is_active( } trx = trx_get_on_id(trx_id); - if (trx && (trx->conc_state == TRX_ACTIVE - || trx->conc_state == TRX_PREPARED)) { - - return(TRUE); + if (trx) { + switch (trx->conc_state) { + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + return(TRUE); + } } return(FALSE); diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 4ade245f03e..9cbc7987eff 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 2019, 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 @@ -741,6 +742,8 @@ struct trx_struct{ #define TRX_ACTIVE 1 #define TRX_COMMITTED_IN_MEMORY 2 #define TRX_PREPARED 3 /* Support for 2PC/XA */ +#define TRX_PREPARED_RECOVERED 4 /* XA PREPARE transaction that + was returned to ha_recover() */ /* Transaction execution states when trx->conc_state == TRX_ACTIVE */ #define TRX_QUE_RUNNING 0 /* transaction is running */ diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c index 6a3ca383ade..28ecb30e2b7 100644 --- a/storage/innobase/lock/lock0lock.c +++ b/storage/innobase/lock/lock0lock.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -4782,6 +4783,22 @@ print_rec: } #ifdef UNIV_DEBUG +/** Validate the state of a transaction that holds locks */ +static void lock_trx_state_validate(const trx_t* trx) +{ + switch (trx->conc_state) { + case TRX_NOT_STARTED: + break; + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + case TRX_COMMITTED_IN_MEMORY: + return; + + } + ut_ad(!"wrong state"); +} + /*********************************************************************//** Validates the lock queue on a table. @return TRUE if ok */ @@ -4798,9 +4815,7 @@ lock_table_queue_validate( lock = UT_LIST_GET_FIRST(table->locks); while (lock) { - ut_a(((lock->trx)->conc_state == TRX_ACTIVE) - || ((lock->trx)->conc_state == TRX_PREPARED) - || ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY)); + ut_d(lock_trx_state_validate(lock->trx)); if (!lock_get_wait(lock)) { @@ -4848,15 +4863,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - switch(lock->trx->conc_state) { - case TRX_ACTIVE: - case TRX_PREPARED: - case TRX_COMMITTED_IN_MEMORY: - break; - default: - ut_error; - } - + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (lock_get_wait(lock)) { @@ -4935,9 +4942,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - ut_a(lock->trx->conc_state == TRX_ACTIVE - || lock->trx->conc_state == TRX_PREPARED - || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (index) { @@ -5014,10 +5019,8 @@ loop: } } + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); - ut_a(lock->trx->conc_state == TRX_ACTIVE - || lock->trx->conc_state == TRX_PREPARED - || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); # ifdef UNIV_SYNC_DEBUG /* Only validate the record queues when this thread is not diff --git a/storage/innobase/read/read0read.c b/storage/innobase/read/read0read.c index b87a3715f65..1de78aae708 100644 --- a/storage/innobase/read/read0read.c +++ b/storage/innobase/read/read0read.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 2019, 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 @@ -272,15 +273,19 @@ read_view_open_now( view->low_limit_id = view->low_limit_no; n = 0; - trx = UT_LIST_GET_FIRST(trx_sys->trx_list); /* No active transaction should be visible, except cr_trx */ - while (trx) { - if (trx->id != cr_trx_id - && (trx->conc_state == TRX_ACTIVE - || trx->conc_state == TRX_PREPARED)) { + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + if (trx->id == cr_trx_id) { + continue; + } + switch (trx->conc_state) { + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: read_view_set_nth_trx_id(view, n, trx->id); n++; @@ -296,8 +301,6 @@ read_view_open_now( view->low_limit_no = trx->no; } } - - trx = UT_LIST_GET_NEXT(trx_list, trx); } view->n_trx_ids = n; @@ -437,18 +440,15 @@ read_cursor_view_create_for_mysql( view->low_limit_id = view->low_limit_no; n = 0; - trx = UT_LIST_GET_FIRST(trx_sys->trx_list); /* No active transaction should be visible */ - - while (trx) { - - if (trx->conc_state == TRX_ACTIVE - || trx->conc_state == TRX_PREPARED) { - - read_view_set_nth_trx_id(view, n, trx->id); - - n++; + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + switch (trx->conc_state) { + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + read_view_set_nth_trx_id(view, n++, trx->id); /* NOTE that a transaction whose trx number is < trx_sys->max_trx_id can still be active, if it is @@ -461,8 +461,6 @@ read_cursor_view_create_for_mysql( view->low_limit_no = trx->no; } } - - trx = UT_LIST_GET_NEXT(trx_list, trx); } view->n_trx_ids = n; diff --git a/storage/innobase/trx/trx0roll.c b/storage/innobase/trx/trx0roll.c index 638e06191bc..a6fa9dc73a9 100644 --- a/storage/innobase/trx/trx0roll.c +++ b/storage/innobase/trx/trx0roll.c @@ -2,6 +2,7 @@ Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2017, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -564,6 +565,7 @@ loop: switch (trx->conc_state) { case TRX_NOT_STARTED: case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: continue; case TRX_COMMITTED_IN_MEMORY: diff --git a/storage/innobase/trx/trx0sys.c b/storage/innobase/trx/trx0sys.c index 256e22d1b50..8fcc935e673 100644 --- a/storage/innobase/trx/trx0sys.c +++ b/storage/innobase/trx/trx0sys.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -1031,7 +1032,7 @@ trx_sys_init_at_db_start(void) trx = UT_LIST_GET_FIRST(trx_sys->trx_list); for (;;) { - + ut_ad(trx->conc_state != TRX_PREPARED_RECOVERED); if (trx->conc_state != TRX_PREPARED) { rows_to_undo += trx->undo_no; } diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index 151e70013f9..7a53a78f2d9 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved. +Copyright (c) 2019, 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 @@ -348,7 +349,8 @@ trx_free_prepared( trx_t* trx) /*!< in, own: trx object */ { ut_ad(mutex_own(&kernel_mutex)); - ut_a(trx->conc_state == TRX_PREPARED); + ut_a(trx->conc_state == TRX_PREPARED + || trx->conc_state == TRX_PREPARED_RECOVERED); ut_a(trx->magic_n == TRX_MAGIC_N); /* Prepared transactions are sort of active; they allow @@ -932,10 +934,11 @@ trx_commit_off_kernel( lsn = 0; } - ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED); + ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED + || trx->conc_state == TRX_PREPARED_RECOVERED); ut_ad(mutex_own(&kernel_mutex)); - if (UNIV_UNLIKELY(trx->conc_state == TRX_PREPARED)) { + if (UNIV_UNLIKELY(trx->conc_state != TRX_ACTIVE)) { ut_a(trx_n_prepared > 0); trx_n_prepared--; } @@ -2072,6 +2075,7 @@ trx_recover_for_mysql( while (trx) { if (trx->conc_state == TRX_PREPARED) { + trx->conc_state = TRX_PREPARED_RECOVERED; xid_list[count] = trx->xid; if (count == 0) { @@ -2096,13 +2100,25 @@ trx_recover_for_mysql( count++; if (count == len) { - break; + goto partial; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } + /* After returning the full list, reset the state, because + init_server_components() wants to recover the collection of + transactions twice, by first calling tc_log->open() and then + ha_recover() directly. */ + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + if (trx->conc_state == TRX_PREPARED_RECOVERED) { + trx->conc_state = TRX_PREPARED; + } + } + +partial: mutex_exit(&kernel_mutex); if (count > 0){ @@ -2144,7 +2160,8 @@ trx_get_trx_by_xid( the same */ if (trx->is_recovered - && trx->conc_state == TRX_PREPARED + && (trx->conc_state == TRX_PREPARED + || trx->conc_state == TRX_PREPARED_RECOVERED) && xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data, diff --git a/storage/xtradb/include/trx0trx.h b/storage/xtradb/include/trx0trx.h index fcfcb14ae3b..c3844cce84e 100644 --- a/storage/xtradb/include/trx0trx.h +++ b/storage/xtradb/include/trx0trx.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 2019, 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 @@ -802,6 +803,8 @@ struct trx_struct{ #define TRX_ACTIVE 1 #define TRX_COMMITTED_IN_MEMORY 2 #define TRX_PREPARED 3 /* Support for 2PC/XA */ +#define TRX_PREPARED_RECOVERED 4 /* XA PREPARE transaction that + was returned to ha_recover() */ /* Transaction execution states when trx->conc_state == TRX_ACTIVE */ #define TRX_QUE_RUNNING 0 /* transaction is running */ diff --git a/storage/xtradb/lock/lock0lock.c b/storage/xtradb/lock/lock0lock.c index 6e574eb282e..d1be28c7f30 100644 --- a/storage/xtradb/lock/lock0lock.c +++ b/storage/xtradb/lock/lock0lock.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -4811,6 +4812,22 @@ print_rec: } #ifdef UNIV_DEBUG +/** Validate the state of a transaction that holds locks */ +static void lock_trx_state_validate(const trx_t* trx) +{ + switch (trx->state) { + case TRX_NOT_STARTED: + break; + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + case TRX_COMMITTED_IN_MEMORY: + return; + + } + ut_ad(!"wrong state"); +} + /*********************************************************************//** Validates the lock queue on a table. @return TRUE if ok */ @@ -4827,9 +4844,7 @@ lock_table_queue_validate( lock = UT_LIST_GET_FIRST(table->locks); while (lock) { - ut_a(((lock->trx)->state == TRX_ACTIVE) - || ((lock->trx)->state == TRX_PREPARED) - || ((lock->trx)->state == TRX_COMMITTED_IN_MEMORY)); + ut_d(lock_trx_state_validate(lock->trx)); if (!lock_get_wait(lock)) { @@ -4877,15 +4892,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - switch(lock->trx->state) { - case TRX_ACTIVE: - case TRX_PREPARED: - case TRX_COMMITTED_IN_MEMORY: - break; - default: - ut_error; - } - + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (lock_get_wait(lock)) { @@ -4964,9 +4971,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - ut_a(lock->trx->state == TRX_ACTIVE - || lock->trx->state == TRX_PREPARED - || lock->trx->state == TRX_COMMITTED_IN_MEMORY); + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (index) { @@ -5043,10 +5048,8 @@ loop: } } + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); - ut_a(lock->trx->state == TRX_ACTIVE - || lock->trx->state == TRX_PREPARED - || lock->trx->state == TRX_COMMITTED_IN_MEMORY); # ifdef UNIV_SYNC_DEBUG /* Only validate the record queues when this thread is not diff --git a/storage/xtradb/trx/trx0roll.c b/storage/xtradb/trx/trx0roll.c index 8c0438ab975..e715780f165 100644 --- a/storage/xtradb/trx/trx0roll.c +++ b/storage/xtradb/trx/trx0roll.c @@ -2,6 +2,7 @@ Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2017, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -564,6 +565,7 @@ loop: switch (trx->state) { case TRX_NOT_STARTED: case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: continue; case TRX_COMMITTED_IN_MEMORY: diff --git a/storage/xtradb/trx/trx0sys.c b/storage/xtradb/trx/trx0sys.c index f801331dbf0..ea3196d182c 100644 --- a/storage/xtradb/trx/trx0sys.c +++ b/storage/xtradb/trx/trx0sys.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, 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 @@ -1355,7 +1356,7 @@ trx_sys_init_at_db_start(void) trx = UT_LIST_GET_FIRST(trx_sys->trx_list); for (;;) { - + ut_ad(trx->state != TRX_PREPARED_RECOVERED); if (trx->state != TRX_PREPARED) { rows_to_undo += trx->undo_no; } diff --git a/storage/xtradb/trx/trx0trx.c b/storage/xtradb/trx/trx0trx.c index dd7b8bfdd1d..da9de9f004b 100644 --- a/storage/xtradb/trx/trx0trx.c +++ b/storage/xtradb/trx/trx0trx.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved. +Copyright (c) 2019, 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 @@ -481,7 +482,8 @@ trx_free_prepared( trx_t* trx) /*!< in, own: trx object */ { ut_ad(mutex_own(&kernel_mutex)); - ut_a(trx->state == TRX_PREPARED); + ut_a(trx->state == TRX_PREPARED + || trx->state == TRX_PREPARED_RECOVERED); ut_a(trx->magic_n == TRX_MAGIC_N); /* Prepared transactions are sort of active; they allow @@ -1148,10 +1150,11 @@ trx_commit_off_kernel( lsn = 0; } - ut_ad(trx->state == TRX_ACTIVE || trx->state == TRX_PREPARED); + ut_ad(trx->state == TRX_ACTIVE || trx->state == TRX_PREPARED + || trx->state == TRX_PREPARED_RECOVERED); ut_ad(mutex_own(&kernel_mutex)); - if (UNIV_UNLIKELY(trx->state == TRX_PREPARED)) { + if (UNIV_UNLIKELY(trx->state != TRX_ACTIVE)) { ut_a(trx_n_prepared > 0); trx_n_prepared--; } @@ -2359,6 +2362,7 @@ trx_recover_for_mysql( while (trx) { if (trx->state == TRX_PREPARED) { + trx->state = TRX_PREPARED_RECOVERED; xid_list[count] = trx->xid; if (count == 0) { @@ -2383,13 +2387,25 @@ trx_recover_for_mysql( count++; if (count == len) { - break; + goto partial; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } + /* After returning the full list, reset the state, because + init_server_components() wants to recover the collection of + transactions twice, by first calling tc_log->open() and then + ha_recover() directly. */ + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + if (trx->state == TRX_PREPARED_RECOVERED) { + trx->state = TRX_PREPARED; + } + } + +partial: mutex_exit(&kernel_mutex); if (count > 0){ @@ -2431,7 +2447,8 @@ trx_get_trx_by_xid( the same */ if (trx->is_recovered - && trx->state == TRX_PREPARED + && (trx->state == TRX_PREPARED + || trx->state == TRX_PREPARED_RECOVERED) && xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data,