From 178d32f03b84004af3611aab7c6fb9ce0b1388f4 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 1 Dec 2020 14:13:05 +0400 Subject: [PATCH 01/17] MDEV-24318 server_audit doesn't respect filters for PROXY_CONNECT events. The log line should be added behind the filters. --- mysql-test/suite/plugins/r/server_audit.result | 2 +- plugin/server_audit/server_audit.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 7c287b369b1..f309b6c95a3 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -440,8 +440,8 @@ TIME,HOSTNAME,plug,localhost,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proxies_priv, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'GRANT PROXY ON plug_dest TO plug',0 -TIME,HOSTNAME,plug,localhost,ID,0,PROXY_CONNECT,test,`plug_dest`@`%`,0 TIME,HOSTNAME,plug,localhost,ID,0,CONNECT,test,,0 +TIME,HOSTNAME,plug,localhost,ID,0,PROXY_CONNECT,test,`plug_dest`@`%`,0 TIME,HOSTNAME,plug,localhost,ID,0,DISCONNECT,test,,0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,db, diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index a4884f17c75..fe8bf4478fc 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -2067,13 +2067,9 @@ static void update_connection_info(struct connection_info *cn, { case MYSQL_AUDIT_CONNECTION_CONNECT: setup_connection_connect(cn, event); - if (event->status == 0 && event->proxy_user && event->proxy_user[0]) - log_proxy(cn, event); break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: *after_action= AA_CHANGE_USER; - if (event->proxy_user && event->proxy_user[0]) - log_proxy(cn, event); break; default:; } @@ -2191,6 +2187,8 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) { case MYSQL_AUDIT_CONNECTION_CONNECT: log_connection(cn, event, event->status ? "FAILED_CONNECT": "CONNECT"); + if (event->status == 0 && event->proxy_user && event->proxy_user[0]) + log_proxy(cn, event); break; case MYSQL_AUDIT_CONNECTION_DISCONNECT: if (use_event_data_for_disconnect) @@ -2200,6 +2198,8 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: log_connection(cn, event, "CHANGEUSER"); + if (event->proxy_user && event->proxy_user[0]) + log_proxy(cn, event); break; default:; } From b6ce493d53fe2a825fb5fe548ff128cdb11e0fab Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Wed, 2 Dec 2020 12:58:51 +0530 Subject: [PATCH 02/17] Fixing compile failure on kvm full-text --- sql/sql_type.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 0bae1e55145..32862be371a 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -5999,11 +5999,18 @@ LEX_CSTRING Charset::collation_specific_name() const for character sets and collations, so a collation name not necessarily starts with the character set name. */ + LEX_CSTRING retval; size_t csname_length= strlen(m_charset->csname); if (strncmp(m_charset->name, m_charset->csname, csname_length)) - return {NULL, 0}; + { + retval.str= NULL; + retval.length= 0; + return retval; + } const char *ptr= m_charset->name + csname_length; - return {ptr, strlen(ptr) }; + retval.str= ptr; + retval.length= strlen(ptr); + return retval; } From 1eb59c307de163b507efade1fc372d8ff2bb94b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 4 Dec 2020 11:46:47 +0200 Subject: [PATCH 03/17] MDEV-24340 Unique final message of InnoDB during shutdown innobase_space_shutdown(): Remove. We want this step to be executed before the message "InnoDB: Shutdown completed; log sequence number " is output by innodb_shutdown(). It used to be executed after that step. innodb_shutdown(): Duplicate the code that used to live in innobase_space_shutdown(). innobase_init_abort(): Merge with innobase_space_shutdown(). --- extra/mariabackup/xtrabackup.cc | 5 +---- storage/innobase/handler/ha_innodb.cc | 24 +++++------------------- storage/innobase/handler/ha_innodb.h | 3 --- storage/innobase/srv/srv0start.cc | 11 +++++++++++ 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 20a8a562fa5..241d7aeb198 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -6073,12 +6073,9 @@ static bool xtrabackup_prepare_func(char** argv) srv_shutdown_bg_undo_sources(); srv_purge_shutdown(); buf_flush_sync_all_buf_pools(); - innodb_shutdown(); - innobase_space_shutdown(); } - else - innodb_shutdown(); + innodb_shutdown(); innodb_free_param(); /* output to metadata file */ diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 04683a6843b..d7ccbd7f883 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3544,11 +3544,12 @@ ha_innobase::init_table_handle_for_HANDLER(void) reset_template(); } -/** Free tablespace resources allocated. */ -void innobase_space_shutdown() +/*********************************************************************//** +Free any resources that were allocated and return failure. +@return always return 1 */ +static int innobase_init_abort() { - DBUG_ENTER("innobase_space_shutdown"); - + DBUG_ENTER("innobase_init_abort"); srv_sys_space.shutdown(); if (srv_tmp_space.get_sanity_check_status()) { fil_space_close(srv_tmp_space.name()); @@ -3559,20 +3560,6 @@ void innobase_space_shutdown() #ifdef WITH_INNODB_DISALLOW_WRITES os_event_destroy(srv_allow_writes_event); #endif /* WITH_INNODB_DISALLOW_WRITES */ - - DBUG_VOID_RETURN; -} - -/*********************************************************************//** -Free any resources that were allocated and return failure. -@return always return 1 */ -static -int -innobase_init_abort() -/*=================*/ -{ - DBUG_ENTER("innobase_init_abort"); - innobase_space_shutdown(); DBUG_RETURN(1); } @@ -4417,7 +4404,6 @@ innobase_end(handlerton*, ha_panic_function) } innodb_shutdown(); - innobase_space_shutdown(); mysql_mutex_destroy(&commit_cond_m); mysql_cond_destroy(&commit_cond); diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index a02c897f7ae..b1c34f64326 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -976,6 +976,3 @@ which is in the prepared state @return 0 or error number */ int innobase_rollback_by_xid(handlerton* hton, XID* xid); - -/** Free tablespace resources allocated. */ -void innobase_space_shutdown(); diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 0d8ebbe98cd..a7be1cdbf21 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -2913,6 +2913,17 @@ innodb_shutdown() fclose(dict_foreign_err_file); } + srv_sys_space.shutdown(); + if (srv_tmp_space.get_sanity_check_status()) { + fil_space_close(srv_tmp_space.name()); + srv_tmp_space.delete_files(); + } + srv_tmp_space.shutdown(); + +#ifdef WITH_INNODB_DISALLOW_WRITES + os_event_destroy(srv_allow_writes_event); +#endif /* WITH_INNODB_DISALLOW_WRITES */ + if (srv_was_started && srv_print_verbose_log) { ib::info() << "Shutdown completed; log sequence number " << srv_shutdown_lsn; From 47c4caf1bfe1a2edc475aad6bba945a60e9cc754 Mon Sep 17 00:00:00 2001 From: Dan Solodko Date: Tue, 11 Aug 2020 18:02:39 +0300 Subject: [PATCH 04/17] mdev-22485 mysqlslap does not use current user as default --- client/mysqlslap.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/mysqlslap.c b/client/mysqlslap.c index 8c53cbb421e..19544384670 100644 --- a/client/mysqlslap.c +++ b/client/mysqlslap.c @@ -1193,9 +1193,6 @@ get_options(int *argc,char ***argv) if (debug_check_flag) my_end_arg= MY_CHECK_ERROR; - if (!user) - user= (char *)"root"; - /* If something is created and --no-drop is not specified, we drop the schema. From bc2dc83cb56851144a8c15e73a83c7817dc705a2 Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Tue, 1 Dec 2020 21:38:22 +0100 Subject: [PATCH 05/17] MDEV-22485: add the test case Closes PR #1649 --- mysql-test/main/mysqlslap.opt | 1 + mysql-test/main/mysqlslap.result | 100 +++++++++++++++++++++++++++++++ mysql-test/main/mysqlslap.test | 51 ++++++++++++++++ 3 files changed, 152 insertions(+) diff --git a/mysql-test/main/mysqlslap.opt b/mysql-test/main/mysqlslap.opt index 632f5b4c069..da7672849bb 100644 --- a/mysql-test/main/mysqlslap.opt +++ b/mysql-test/main/mysqlslap.opt @@ -1 +1,2 @@ --sql_mode='NO_ENGINE_SUBSTITUTION' +--log-output=TABLE,FILE --general-log=0 --slow-query-log=0 diff --git a/mysql-test/main/mysqlslap.result b/mysql-test/main/mysqlslap.result index 791cb5ac6b3..a83d4d38a16 100644 --- a/mysql-test/main/mysqlslap.result +++ b/mysql-test/main/mysqlslap.result @@ -258,3 +258,103 @@ DROP TABLE t1; # # Bug MDEV-15789 (Upstream: #80329): MYSQLSLAP OPTIONS --AUTO-GENERATE-SQL-GUID-PRIMARY and --AUTO-GENERATE-SQL-SECONDARY-INDEXES DONT WORK # +# +# MDEV-22485: mysqlslap does not use current user as default for connecting to server +# +set @old_general_log=@@global.general_log; +set @old_general_log_file=@@global.general_log_file; +select @old_general_log; +@old_general_log +0 +show global variables +where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; +Variable_name Value +general_log OFF +slow_query_log OFF +truncate table mysql.general_log; +select * from mysql.general_log; +event_time user_host thread_id server_id command_type argument +set global general_log=ON; +current_user.log; +@@current_user.log +current_user.log +SET GLOBAL general_log_file=general_log.log; +show global variables +where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; +Variable_name Value +general_log ON +slow_query_log OFF +flush logs; +create user anel@localhost; +grant all on *.* to anel@localhost; +connect con1,localhost,anel,,; +connection con1; +create table t(t int); +# Run mysqlslap user anel +Benchmark + Average number of seconds to run all queries: X seconds + Minimum number of seconds to run all queries: X seconds + Maximum number of seconds to run all queries: X seconds + Number of clients running queries: 1 + Average number of queries per client: 1 + +flush tables; +select user_host, command_type, argument from mysql.general_log; +user_host command_type argument +root[root] @ localhost [] Query select @@general_log_file +root[root] @ localhost [] Query SET GLOBAL general_log_file=current_user.log +root[root] @ localhost [] Query show global variables +where Variable_name = 'general_log' or Variable_name = 'slow_query_log' +root[root] @ localhost [] Query flush logs +root[root] @ localhost [] Query create user anel@localhost +root[root] @ localhost [] Query grant all on *.* to anel@localhost +[anel] @ localhost [] Connect anel@localhost as anonymous on test +anel[anel] @ localhost [] Query create table t(t int) +[root] @ localhost [] Connect root@localhost as anonymous on +[root] @ localhost [] Connect root@localhost as anonymous on test +root[root] @ localhost [] Query SELECT current_user() +root[root] @ localhost [] Quit +root[root] @ localhost [] Quit +anel[anel] @ localhost [] Query flush tables +anel[anel] @ localhost [] Query select user_host, command_type, argument from mysql.general_log +disconnect con1; +connection default; +# Run mysqlslap user root +Benchmark + Average number of seconds to run all queries: X seconds + Minimum number of seconds to run all queries: X seconds + Maximum number of seconds to run all queries: X seconds + Number of clients running queries: 1 + Average number of queries per client: 1 + +flush tables; +select user_host, command_type, argument from mysql.general_log; +user_host command_type argument +root[root] @ localhost [] Query select @@general_log_file +root[root] @ localhost [] Query SET GLOBAL general_log_file=current_user.log +root[root] @ localhost [] Query show global variables +where Variable_name = 'general_log' or Variable_name = 'slow_query_log' +root[root] @ localhost [] Query flush logs +root[root] @ localhost [] Query create user anel@localhost +root[root] @ localhost [] Query grant all on *.* to anel@localhost +[anel] @ localhost [] Connect anel@localhost as anonymous on test +anel[anel] @ localhost [] Query create table t(t int) +[root] @ localhost [] Connect root@localhost as anonymous on +[root] @ localhost [] Connect root@localhost as anonymous on test +root[root] @ localhost [] Query SELECT current_user() +root[root] @ localhost [] Quit +root[root] @ localhost [] Quit +anel[anel] @ localhost [] Query flush tables +anel[anel] @ localhost [] Query select user_host, command_type, argument from mysql.general_log +anel[anel] @ localhost [] Quit +[root] @ localhost [] Connect root@localhost as anonymous on +[root] @ localhost [] Connect root@localhost as anonymous on test +root[root] @ localhost [] Query SELECT current_user() +root[root] @ localhost [] Quit +root[root] @ localhost [] Quit +root[root] @ localhost [] Query flush tables +root[root] @ localhost [] Query select user_host, command_type, argument from mysql.general_log +drop user anel@localhost; +drop table t; +set global general_log= @old_general_log; +set global general_log_file= @old_general_log_file; diff --git a/mysql-test/main/mysqlslap.test b/mysql-test/main/mysqlslap.test index 81115d59d09..4e99b8f2cd2 100644 --- a/mysql-test/main/mysqlslap.test +++ b/mysql-test/main/mysqlslap.test @@ -88,3 +88,54 @@ DROP TABLE t1; --exec $MYSQL_SLAP --concurrency=1 --silent --iterations=1 --number-int-cols=2 --number-char-cols=3 --auto-generate-sql --auto-generate-sql-guid-primary --create-schema=slap --exec $MYSQL_SLAP --concurrency=1 --silent --iterations=1 --number-int-cols=2 --number-char-cols=3 --auto-generate-sql --auto-generate-sql-secondary-indexes=1 --create-schema=slap + +--echo # +--echo # MDEV-22485: mysqlslap does not use current user as default for connecting to server +--echo # +--disable_ps_protocol +set @old_general_log=@@global.general_log; +set @old_general_log_file=@@global.general_log_file; +select @old_general_log; + +show global variables +where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; +truncate table mysql.general_log; +# empty log at the beginning +select * from mysql.general_log; + +set global general_log=ON; +--replace_regex /[\/\w:](?!.log).*/current_user.log/ +select @@general_log_file; +--replace_regex /=.+"/=general_log.log/ +--let $gen_log_file=$MYSQLTEST_VARDIR/tmp/general_log.log +--replace_regex /".+"/general_log.log/ +--eval SET GLOBAL general_log_file="$gen_log_file" +show global variables +where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; +flush logs; +create user anel@localhost; +grant all on *.* to anel@localhost; +connect con1,localhost,anel,,; +connection con1; +create table t(t int); +--echo # Run mysqlslap user anel +--replace_regex /\d[.]\d+/X/ +--exec $MYSQL_SLAP --create-schema=test --query="SELECT current_user()" --concurrency=1 --iterations=1 +flush tables; +--replace_regex /".+/current_user.log/ /[::1]// +select user_host, command_type, argument from mysql.general_log; +disconnect con1; + +connection default; +--echo # Run mysqlslap user root +--replace_regex /\d[.]\d+/X/ +--exec $MYSQL_SLAP --create-schema=test --query="SELECT current_user()" --concurrency=1 --iterations=1 +flush tables; +#--exec $MYSQL_SLAP --concurrency=1 --silent --iterations=1 --number-int-cols=2 --number-char-cols=3 --auto-generate-sql --auto-generate-sql-guid-primary --create-schema=slap +--replace_regex /".+/current_user.log/ /[::1]// +select user_host, command_type, argument from mysql.general_log; +drop user anel@localhost; +drop table t; +set global general_log= @old_general_log; +set global general_log_file= @old_general_log_file; +--enable_ps_protocol From 86fc37b66877e45c38593e7af15159c32f81eb7c Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Sun, 22 Mar 2020 11:33:53 +0100 Subject: [PATCH 06/17] MDEV-19273: Server crash in MDL_ticket::has_stronger_or_equal_type or Assertion `thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str, table->table_name.str, MDL_SHARED)' failed in mysql_rm_table_no_locks Early report error in case of DROP SEQUENCE Do not use error variable for other purposes except error. --- mysql-test/suite/sql_sequence/create.result | 2 +- mysql-test/suite/sql_sequence/create.test | 2 +- mysql-test/suite/sql_sequence/other.result | 59 +++++++++++++++++++ mysql-test/suite/sql_sequence/other.test | 64 +++++++++++++++++++++ sql/sql_table.cc | 56 ++++++++++++++---- 5 files changed, 169 insertions(+), 14 deletions(-) diff --git a/mysql-test/suite/sql_sequence/create.result b/mysql-test/suite/sql_sequence/create.result index 14464c60e99..5a53a66c9a8 100644 --- a/mysql-test/suite/sql_sequence/create.result +++ b/mysql-test/suite/sql_sequence/create.result @@ -666,7 +666,7 @@ create temporary sequence s; drop temporary table s; create temporary table s (i int); drop temporary sequence s; -ERROR 42S02: Unknown SEQUENCE: 'test.s' +ERROR 42S02: 'test.s' is not a SEQUENCE drop table s; # # MDEV-15115 Assertion failure in CREATE SEQUENCE...ROW_FORMAT=REDUNDANT diff --git a/mysql-test/suite/sql_sequence/create.test b/mysql-test/suite/sql_sequence/create.test index 2c41fb3658b..ac3aae845cd 100644 --- a/mysql-test/suite/sql_sequence/create.test +++ b/mysql-test/suite/sql_sequence/create.test @@ -489,7 +489,7 @@ drop table s; create temporary sequence s; drop temporary table s; create temporary table s (i int); ---error ER_UNKNOWN_SEQUENCES +--error ER_NOT_SEQUENCE2 drop temporary sequence s; drop table s; diff --git a/mysql-test/suite/sql_sequence/other.result b/mysql-test/suite/sql_sequence/other.result index abc101b3c00..643233149d2 100644 --- a/mysql-test/suite/sql_sequence/other.result +++ b/mysql-test/suite/sql_sequence/other.result @@ -300,4 +300,63 @@ update t1 set p_first_name='Yunxi' where p_id=1; drop view v2; drop table t1,t2; drop sequence s1; +# +# MDEV-19273:Server crash in MDL_ticket::has_stronger_or_equal_type or +# Assertion `thd->mdl_context.is_lock_owner(MDL_key::TABLE, +# table->db.str, table->table_name.str, MDL_SHARED)' failed +# in mysql_rm_table_no_locks +# +CREATE TABLE t1 (a INT); +CREATE TEMPORARY TABLE tmp (b INT); +LOCK TABLE t1 READ; +DROP SEQUENCE tmp; +ERROR 42S02: 'test.tmp' is not a SEQUENCE +DROP TEMPORARY SEQUENCE tmp; +ERROR 42S02: 'test.tmp' is not a SEQUENCE +DROP SEQUENCE t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +DROP TEMPORARY SEQUENCE t1; +ERROR 42S02: Unknown SEQUENCE: 'test.t1' +UNLOCK TABLES; +DROP SEQUENCE t1; +ERROR 42S02: 'test.t1' is not a SEQUENCE +DROP TEMPORARY SEQUENCE t1; +ERROR 42S02: Unknown SEQUENCE: 'test.t1' +DROP TABLE t1; +CREATE TABLE t (a INT); +CREATE TEMPORARY TABLE s (f INT); +CREATE SEQUENCE s; +LOCK TABLE t WRITE; +DROP SEQUENCE s; +ERROR 42S02: 'test.s' is not a SEQUENCE +DROP TEMPORARY SEQUENCE s; +ERROR 42S02: 'test.s' is not a SEQUENCE +UNLOCK TABLES; +CREATE TEMPORARY SEQUENCE s; +LOCK TABLE t WRITE; +DROP TEMPORARY SEQUENCE s; +UNLOCK TABLES; +DROP TEMPORARY TABLE s; +DROP SEQUENCE s; +create table s(a INT); +CREATE TEMPORARY TABLE s (f INT); +LOCK TABLE t WRITE; +DROP TEMPORARY TABLE s; +CREATE TEMPORARY TABLE s (f INT); +DROP TABLE s; +DROP TABLE s; +ERROR HY000: Table 's' was not locked with LOCK TABLES +UNLOCK TABLES; +DROP TABLE s; +CREATE VIEW v1 as SELECT * FROM t; +CREATE SEQUENCE s; +DROP SEQUENCE IF EXISTS v1; +Warnings: +Note 4091 Unknown SEQUENCE: 'test.v1' +DROP VIEW IF EXISTS s; +Warnings: +Note 4092 Unknown VIEW: 'test.s' +DROP VIEW v1; +DROP SEQUENCE s; +DROP TABLE t; # End of 10.3 tests diff --git a/mysql-test/suite/sql_sequence/other.test b/mysql-test/suite/sql_sequence/other.test index 70c4efa40e5..0fbb2d0e2f9 100644 --- a/mysql-test/suite/sql_sequence/other.test +++ b/mysql-test/suite/sql_sequence/other.test @@ -315,4 +315,68 @@ drop view v2; drop table t1,t2; drop sequence s1; +--echo # +--echo # MDEV-19273:Server crash in MDL_ticket::has_stronger_or_equal_type or +--echo # Assertion `thd->mdl_context.is_lock_owner(MDL_key::TABLE, +--echo # table->db.str, table->table_name.str, MDL_SHARED)' failed +--echo # in mysql_rm_table_no_locks +--echo # + +CREATE TABLE t1 (a INT); +CREATE TEMPORARY TABLE tmp (b INT); +LOCK TABLE t1 READ; +--error ER_NOT_SEQUENCE2 +DROP SEQUENCE tmp; +--error ER_NOT_SEQUENCE2 +DROP TEMPORARY SEQUENCE tmp; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +DROP SEQUENCE t1; +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE t1; +UNLOCK TABLES; +--error ER_NOT_SEQUENCE2 +DROP SEQUENCE t1; +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE t1; + +# Cleanup +DROP TABLE t1; + + +CREATE TABLE t (a INT); +CREATE TEMPORARY TABLE s (f INT); +CREATE SEQUENCE s; +LOCK TABLE t WRITE; +--error ER_NOT_SEQUENCE2 +DROP SEQUENCE s; +--error ER_NOT_SEQUENCE2 +DROP TEMPORARY SEQUENCE s; +UNLOCK TABLES; +CREATE TEMPORARY SEQUENCE s; +LOCK TABLE t WRITE; +DROP TEMPORARY SEQUENCE s; +UNLOCK TABLES; +DROP TEMPORARY TABLE s; +DROP SEQUENCE s; + +create table s(a INT); +CREATE TEMPORARY TABLE s (f INT); +LOCK TABLE t WRITE; +DROP TEMPORARY TABLE s; +CREATE TEMPORARY TABLE s (f INT); +DROP TABLE s; +--error ER_TABLE_NOT_LOCKED +DROP TABLE s; +UNLOCK TABLES; +DROP TABLE s; + +CREATE VIEW v1 as SELECT * FROM t; +CREATE SEQUENCE s; + +DROP SEQUENCE IF EXISTS v1; +DROP VIEW IF EXISTS s; + +DROP VIEW v1; +DROP SEQUENCE s; +DROP TABLE t; --echo # End of 10.3 tests diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 96d270b25ff..55cc33b4680 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2314,8 +2314,11 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { bool is_trans= 0; bool table_creation_was_logged= 0; + bool real_table= FALSE; LEX_CSTRING db= table->db; handlerton *table_type= 0; + // reset error state for this table + error= 0; DBUG_PRINT("table", ("table_l: '%s'.'%s' table: %p s: %p", table->db.str, table->table_name.str, table->table, @@ -2331,9 +2334,35 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, thd->find_temporary_table(table) && table->mdl_request.ticket != NULL)); - if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table) || - (drop_sequence && table->table->s->table_type != TABLE_TYPE_SEQUENCE)) + if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) + real_table= TRUE; + else if (drop_sequence && + table->table->s->table_type != TABLE_TYPE_SEQUENCE) + { + was_table= (table->table->s->table_type == TABLE_TYPE_NORMAL); + was_view= (table->table->s->table_type == TABLE_TYPE_VIEW); + if (if_exists) + { + char buff[FN_REFLEN]; + String tbl_name(buff, sizeof(buff), system_charset_info); + tbl_name.length(0); + tbl_name.append(&db); + tbl_name.append('.'); + tbl_name.append(&table->table_name); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_NOT_SEQUENCE2, ER_THD(thd, ER_NOT_SEQUENCE2), + tbl_name.c_ptr_safe()); + + /* + Our job is done here. This statement was added to avoid executing + unnecessary code farther below which in some strange corner cases + caused the server to crash (see MDEV-17896). + */ + goto log_query; + } error= 1; + goto non_critical_err; + } else { table_creation_was_logged= table->table->s->table_creation_was_logged; @@ -2342,29 +2371,28 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, error= 1; goto err; } - error= 0; table->table= 0; } - if ((drop_temporary && if_exists) || !error) + if ((drop_temporary && if_exists) || !real_table) { /* This handles the case of temporary tables. We have the following cases: . "DROP TEMPORARY" was executed and a temporary table was affected - (i.e. drop_temporary && !error) or the if_exists was specified (i.e. - drop_temporary && if_exists). + (i.e. drop_temporary && !real_table) or the + if_exists was specified (i.e. drop_temporary && if_exists). . "DROP" was executed but a temporary table was affected (.i.e - !error). + !real_table). */ if (!dont_log_query && table_creation_was_logged) { /* - If there is an error, we don't know the type of the engine + If there is an real_table, we don't know the type of the engine at this point. So, we keep it in the trx-cache. */ - is_trans= error ? TRUE : is_trans; + is_trans= real_table ? TRUE : is_trans; if (is_trans) trans_tmp_table_deleted= TRUE; else @@ -2391,7 +2419,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, is no need to proceed with the code that tries to drop a regular table. */ - if (!error) continue; + if (!real_table) continue; } else if (!drop_temporary) { @@ -2407,7 +2435,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, reg_ext, 0); } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); - error= 0; if (drop_temporary || (ha_table_exists(thd, &db, &alias, &table_type, &is_sequence) == 0 && table_type == 0) || @@ -2447,6 +2474,11 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { non_tmp_error = (drop_temporary ? non_tmp_error : TRUE); error= 1; + /* + non critical error (only for this table), so we continue. + Next we write it to wrong_tables and continue this loop + The same as "goto non_critical_err". + */ } } else @@ -2540,7 +2572,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } non_tmp_error|= MY_TEST(error); } - +non_critical_err: if (error) { if (wrong_tables.length()) From 121582647ce999a5418cf705646744f2f7fe4065 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 11 Dec 2020 20:35:25 +0100 Subject: [PATCH 07/17] MDEV-24279 Segfault after 1 day and 5 minutes uptime feedback plugin now fakes a SHOW command to force create_schema_table() to instantiate the table at once, not lazily. The test from plugins.feedback_plugin_send applies. Caused by e64084d5a3a7 --- plugin/feedback/sender_thread.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/feedback/sender_thread.cc b/plugin/feedback/sender_thread.cc index 8e2e9dd5ed6..943443f60fe 100644 --- a/plugin/feedback/sender_thread.cc +++ b/plugin/feedback/sender_thread.cc @@ -117,6 +117,7 @@ static int prepare_for_fill(TABLE_LIST *tables) tables->init_one_table(&INFORMATION_SCHEMA_NAME, &tbl_name, 0, TL_READ); tables->schema_table= i_s_feedback; + tables->schema_table_reformed= 1; tables->table= create_schema_table(thd, tables); if (!tables->table) return 1; From 18256bce23a5e219a8f4424b56a4380cf8be330a Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 12 Dec 2020 12:10:37 +0100 Subject: [PATCH 08/17] remove the test for MDEV-22485 that doesn't test MDEV-22485 This reverts commit bc2dc83cb56851144a8c15e73a83c7817dc705a2. --- mysql-test/main/mysqlslap.opt | 1 - mysql-test/main/mysqlslap.result | 100 ------------------------------- mysql-test/main/mysqlslap.test | 51 ---------------- 3 files changed, 152 deletions(-) diff --git a/mysql-test/main/mysqlslap.opt b/mysql-test/main/mysqlslap.opt index da7672849bb..632f5b4c069 100644 --- a/mysql-test/main/mysqlslap.opt +++ b/mysql-test/main/mysqlslap.opt @@ -1,2 +1 @@ --sql_mode='NO_ENGINE_SUBSTITUTION' ---log-output=TABLE,FILE --general-log=0 --slow-query-log=0 diff --git a/mysql-test/main/mysqlslap.result b/mysql-test/main/mysqlslap.result index a83d4d38a16..791cb5ac6b3 100644 --- a/mysql-test/main/mysqlslap.result +++ b/mysql-test/main/mysqlslap.result @@ -258,103 +258,3 @@ DROP TABLE t1; # # Bug MDEV-15789 (Upstream: #80329): MYSQLSLAP OPTIONS --AUTO-GENERATE-SQL-GUID-PRIMARY and --AUTO-GENERATE-SQL-SECONDARY-INDEXES DONT WORK # -# -# MDEV-22485: mysqlslap does not use current user as default for connecting to server -# -set @old_general_log=@@global.general_log; -set @old_general_log_file=@@global.general_log_file; -select @old_general_log; -@old_general_log -0 -show global variables -where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; -Variable_name Value -general_log OFF -slow_query_log OFF -truncate table mysql.general_log; -select * from mysql.general_log; -event_time user_host thread_id server_id command_type argument -set global general_log=ON; -current_user.log; -@@current_user.log -current_user.log -SET GLOBAL general_log_file=general_log.log; -show global variables -where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; -Variable_name Value -general_log ON -slow_query_log OFF -flush logs; -create user anel@localhost; -grant all on *.* to anel@localhost; -connect con1,localhost,anel,,; -connection con1; -create table t(t int); -# Run mysqlslap user anel -Benchmark - Average number of seconds to run all queries: X seconds - Minimum number of seconds to run all queries: X seconds - Maximum number of seconds to run all queries: X seconds - Number of clients running queries: 1 - Average number of queries per client: 1 - -flush tables; -select user_host, command_type, argument from mysql.general_log; -user_host command_type argument -root[root] @ localhost [] Query select @@general_log_file -root[root] @ localhost [] Query SET GLOBAL general_log_file=current_user.log -root[root] @ localhost [] Query show global variables -where Variable_name = 'general_log' or Variable_name = 'slow_query_log' -root[root] @ localhost [] Query flush logs -root[root] @ localhost [] Query create user anel@localhost -root[root] @ localhost [] Query grant all on *.* to anel@localhost -[anel] @ localhost [] Connect anel@localhost as anonymous on test -anel[anel] @ localhost [] Query create table t(t int) -[root] @ localhost [] Connect root@localhost as anonymous on -[root] @ localhost [] Connect root@localhost as anonymous on test -root[root] @ localhost [] Query SELECT current_user() -root[root] @ localhost [] Quit -root[root] @ localhost [] Quit -anel[anel] @ localhost [] Query flush tables -anel[anel] @ localhost [] Query select user_host, command_type, argument from mysql.general_log -disconnect con1; -connection default; -# Run mysqlslap user root -Benchmark - Average number of seconds to run all queries: X seconds - Minimum number of seconds to run all queries: X seconds - Maximum number of seconds to run all queries: X seconds - Number of clients running queries: 1 - Average number of queries per client: 1 - -flush tables; -select user_host, command_type, argument from mysql.general_log; -user_host command_type argument -root[root] @ localhost [] Query select @@general_log_file -root[root] @ localhost [] Query SET GLOBAL general_log_file=current_user.log -root[root] @ localhost [] Query show global variables -where Variable_name = 'general_log' or Variable_name = 'slow_query_log' -root[root] @ localhost [] Query flush logs -root[root] @ localhost [] Query create user anel@localhost -root[root] @ localhost [] Query grant all on *.* to anel@localhost -[anel] @ localhost [] Connect anel@localhost as anonymous on test -anel[anel] @ localhost [] Query create table t(t int) -[root] @ localhost [] Connect root@localhost as anonymous on -[root] @ localhost [] Connect root@localhost as anonymous on test -root[root] @ localhost [] Query SELECT current_user() -root[root] @ localhost [] Quit -root[root] @ localhost [] Quit -anel[anel] @ localhost [] Query flush tables -anel[anel] @ localhost [] Query select user_host, command_type, argument from mysql.general_log -anel[anel] @ localhost [] Quit -[root] @ localhost [] Connect root@localhost as anonymous on -[root] @ localhost [] Connect root@localhost as anonymous on test -root[root] @ localhost [] Query SELECT current_user() -root[root] @ localhost [] Quit -root[root] @ localhost [] Quit -root[root] @ localhost [] Query flush tables -root[root] @ localhost [] Query select user_host, command_type, argument from mysql.general_log -drop user anel@localhost; -drop table t; -set global general_log= @old_general_log; -set global general_log_file= @old_general_log_file; diff --git a/mysql-test/main/mysqlslap.test b/mysql-test/main/mysqlslap.test index 4e99b8f2cd2..81115d59d09 100644 --- a/mysql-test/main/mysqlslap.test +++ b/mysql-test/main/mysqlslap.test @@ -88,54 +88,3 @@ DROP TABLE t1; --exec $MYSQL_SLAP --concurrency=1 --silent --iterations=1 --number-int-cols=2 --number-char-cols=3 --auto-generate-sql --auto-generate-sql-guid-primary --create-schema=slap --exec $MYSQL_SLAP --concurrency=1 --silent --iterations=1 --number-int-cols=2 --number-char-cols=3 --auto-generate-sql --auto-generate-sql-secondary-indexes=1 --create-schema=slap - ---echo # ---echo # MDEV-22485: mysqlslap does not use current user as default for connecting to server ---echo # ---disable_ps_protocol -set @old_general_log=@@global.general_log; -set @old_general_log_file=@@global.general_log_file; -select @old_general_log; - -show global variables -where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; -truncate table mysql.general_log; -# empty log at the beginning -select * from mysql.general_log; - -set global general_log=ON; ---replace_regex /[\/\w:](?!.log).*/current_user.log/ -select @@general_log_file; ---replace_regex /=.+"/=general_log.log/ ---let $gen_log_file=$MYSQLTEST_VARDIR/tmp/general_log.log ---replace_regex /".+"/general_log.log/ ---eval SET GLOBAL general_log_file="$gen_log_file" -show global variables -where Variable_name = 'general_log' or Variable_name = 'slow_query_log'; -flush logs; -create user anel@localhost; -grant all on *.* to anel@localhost; -connect con1,localhost,anel,,; -connection con1; -create table t(t int); ---echo # Run mysqlslap user anel ---replace_regex /\d[.]\d+/X/ ---exec $MYSQL_SLAP --create-schema=test --query="SELECT current_user()" --concurrency=1 --iterations=1 -flush tables; ---replace_regex /".+/current_user.log/ /[::1]// -select user_host, command_type, argument from mysql.general_log; -disconnect con1; - -connection default; ---echo # Run mysqlslap user root ---replace_regex /\d[.]\d+/X/ ---exec $MYSQL_SLAP --create-schema=test --query="SELECT current_user()" --concurrency=1 --iterations=1 -flush tables; -#--exec $MYSQL_SLAP --concurrency=1 --silent --iterations=1 --number-int-cols=2 --number-char-cols=3 --auto-generate-sql --auto-generate-sql-guid-primary --create-schema=slap ---replace_regex /".+/current_user.log/ /[::1]// -select user_host, command_type, argument from mysql.general_log; -drop user anel@localhost; -drop table t; -set global general_log= @old_general_log; -set global general_log_file= @old_general_log_file; ---enable_ps_protocol From e5d88e03beec38973539f5d290fa70f81be645ef Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Mon, 14 Dec 2020 16:51:30 +0530 Subject: [PATCH 09/17] MDEV-22740: UBSAN: sql/opt_split.cc:1150:28: runtime error: shift exponent 61 is too large for 32-bit type 'int' (on optimized builds) Use a ulonglong instead of uint when left shifting to calculate the table map for all the tables in a query --- sql/opt_split.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/opt_split.cc b/sql/opt_split.cc index 6807a623b73..fd7836f55cc 100644 --- a/sql/opt_split.cc +++ b/sql/opt_split.cc @@ -1149,7 +1149,7 @@ bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan, bool JOIN::fix_all_splittings_in_plan() { table_map prev_tables= 0; - table_map all_tables= (1 << table_count) - 1; + table_map all_tables= (table_map(1) << table_count) - 1; for (uint tablenr= 0; tablenr < table_count; tablenr++) { POSITION *cur_pos= &best_positions[tablenr]; From 71806bf37c45d5490d8382cba6a5cca6879fa0db Mon Sep 17 00:00:00 2001 From: Stepan Patryshev Date: Tue, 15 Dec 2020 18:05:56 +0200 Subject: [PATCH 10/17] MDEV-24414 Update and enable galera.galera_defaults --- mysql-test/suite/galera/disabled.def | 1 - mysql-test/suite/galera/t/galera_defaults.test | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index dd1721839e0..0692a02c7b0 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -14,7 +14,6 @@ MW-286 : MDEV-18464 Killing thread can cause mutex deadlock if done concurrently MW-328A : MDEV-22666 galera.MW-328A MTR failed: "Semaphore wait has lasted > 600 seconds" and do not release port 16002 MW-328B : MDEV-22666 galera.MW-328A MTR failed: "Semaphore wait has lasted > 600 seconds" and do not release port 16002 MW-329 : MDEV-19962 Galera test failure on MW-329 -galera.galera_defaults : MDEV-21494 Galera test sporadic failure on galera.galera_defaults galera_as_slave_replication_bundle : MDEV-15785 OPTION_GTID_BEGIN is set in Gtid_log_event::do_apply_event() galera_binlog_stmt_autoinc : MDEV-19959 Galera test failure on galera_binlog_stmt_autoinc galera_gcache_recover_manytrx : MDEV-18834 Galera test failure diff --git a/mysql-test/suite/galera/t/galera_defaults.test b/mysql-test/suite/galera/t/galera_defaults.test index 3d4a7da7b54..a9e10de0176 100644 --- a/mysql-test/suite/galera/t/galera_defaults.test +++ b/mysql-test/suite/galera/t/galera_defaults.test @@ -13,7 +13,7 @@ # Make sure that the test is operating on the right version of galera library. --disable_query_log ---let $galera_version=25.3.20 +--let $galera_version=25.3.31 source ../wsrep/include/check_galera_version.inc; --enable_query_log From 8de323f81acd830035a11058d5b76127d71616a4 Mon Sep 17 00:00:00 2001 From: Etienne Guesnet Date: Wed, 16 Dec 2020 08:44:58 +1100 Subject: [PATCH 11/17] MDEV-24099: sql/sql_insert ip_len issue on AIX ip_len has a different meaning on AIX so we use a different variable name here not to conflict. Backport from MDEV-20178 2f5d372444cff53914cfcd118e92a91f575cec35 --- sql/sql_insert.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b203fffaa72..8115b3c6397 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2690,7 +2690,7 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, delayed_row *row= 0; Delayed_insert *di=thd->di; const Discrete_interval *forced_auto_inc; - size_t user_len, host_len, ip_len; + size_t user_len, host_len, ip_length; DBUG_ENTER("write_delayed"); DBUG_PRINT("enter", ("query = '%s' length %lu", query.str, (ulong) query.length)); @@ -2724,7 +2724,7 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, goto err; } - user_len= host_len= ip_len= 0; + user_len= host_len= ip_length= 0; row->user= row->host= row->ip= NULL; if (thd->security_ctx) { @@ -2733,11 +2733,11 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, if (thd->security_ctx->host) host_len= strlen(thd->security_ctx->host) + 1; if (thd->security_ctx->ip) - ip_len= strlen(thd->security_ctx->ip) + 1; + ip_length= strlen(thd->security_ctx->ip) + 1; } /* This can't be THREAD_SPECIFIC as it's freed in delayed thread */ if (!(row->record= (char*) my_malloc(table->s->reclength + - user_len + host_len + ip_len, + user_len + host_len + ip_length, MYF(MY_WME)))) goto err; memcpy(row->record, table->record[0], table->s->reclength); @@ -2757,7 +2757,7 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, if (thd->security_ctx->ip) { row->ip= row->record + table->s->reclength + user_len + host_len; - memcpy(row->ip, thd->security_ctx->ip, ip_len); + memcpy(row->ip, thd->security_ctx->ip, ip_length); } } row->query_id= thd->query_id; From c742346e5041d85caaf30d2d6c10369603ad09ad Mon Sep 17 00:00:00 2001 From: Stepan Patryshev Date: Wed, 16 Dec 2020 18:29:06 +0200 Subject: [PATCH 12/17] TODO-2697 Enable galera.lp1376747-4 on 10.3 CS --- mysql-test/suite/galera/disabled.def | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index 0692a02c7b0..87dbb7a6487 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -23,7 +23,6 @@ galera_ssl_upgrade : MDEV-19950 Galera test failure on galera_ssl_upgrade galera_sst_mariabackup_encrypt_with_key : MDEV-21484 galera_sst_mariabackup_encrypt_with_key galera_var_node_address : MDEV-20485 Galera test failure galera_wan : MDEV-17259 Test failure on galera.galera_wan -lp1376747-4 : MDEV-21911 Galera test failure on lp1376747-4 partition : MDEV-19958 Galera test failure on galera.partition query_cache: MDEV-15805 Test failure on galera.query_cache sql_log_bin : MDEV-21491 galera.sql_log_bin From d4258f3a8fba0f8972714325c0dc0ca925879b86 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 22 Dec 2020 03:33:53 +0300 Subject: [PATCH 13/17] MDEV-22178 Assertion `info->alias.str' failed in partition_info::check_partition_info instead of ER_VERS_WRONG_PARTS Assign create_info->alias for ALTER TABLE since it is NULL and later accessed for printing error message. --- mysql-test/suite/versioning/r/partition.result | 7 +++++++ mysql-test/suite/versioning/t/partition.test | 9 +++++++++ sql/sql_table.cc | 1 + 3 files changed, 17 insertions(+) diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index a89053369a7..06aa0ded32d 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -683,4 +683,11 @@ create table t1 (a int) with system versioning partition by system_time alter table t1 add partition (partition p2); ERROR HY000: Wrong partitioning type, expected type: `SYSTEM_TIME` drop table t1; +# +# MDEV-22178 Assertion `info->alias.str' failed in partition_info::check_partition_info instead of ER_VERS_WRONG_PARTS +# +create or replace table t1 (a int) with system versioning; +alter table t1 partition by system_time (partition pn current); +ERROR HY000: Wrong partitions for `t1`: must have at least one HISTORY and exactly one last CURRENT +drop table t1; # End of 10.3 tests diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index 957fddc730d..454e4068e15 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -652,6 +652,15 @@ alter table t1 add partition (partition p2); # Cleanup drop table t1; +--echo # +--echo # MDEV-22178 Assertion `info->alias.str' failed in partition_info::check_partition_info instead of ER_VERS_WRONG_PARTS +--echo # +create or replace table t1 (a int) with system versioning; +--error ER_VERS_WRONG_PARTS +alter table t1 partition by system_time (partition pn current); +# Cleanup +drop table t1; + --echo # End of 10.3 tests --source suite/versioning/common_finish.inc diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 55cc33b4680..1237bba2286 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9850,6 +9850,7 @@ do_continue:; tmp_disable_binlog(thd); create_info->options|=HA_CREATE_TMP_ALTER; + create_info->alias= alter_ctx.table_name; error= create_table_impl(thd, &alter_ctx.db, &alter_ctx.table_name, &alter_ctx.new_db, &alter_ctx.tmp_name, From 7410ff436e95de09c2f3f0028e7af8b3a043028b Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 22 Dec 2020 03:33:53 +0300 Subject: [PATCH 14/17] MDEV-21138 Assertion `col->ord_part' or `f.col->ord_part' failed in row_build_index_entry_low First part (row0mysql.cc) fixes ins_node_set_new_row() usage workflow as it is designed to operate on empty row (see row_get_prebuilt_insert_row() for example). Second part (row0ins.cc) fixes duplicate key error in FTS_DOC_ID_INDEX since history rows must not generate entries in that index. We detect FTS_DOC_ID_INDEX by a number of attributes and skip it if the row is historical. Misc fixes: row_build_index_entry_low() does not accept non-NULL tuple for FTS index (subject assertion fails), assertion (index->type != DICT_FTS) adds code understanding. Now as historical_row is copied in row_update_vers_insert() there is no need to copy the row twice: ROW_COPY_POINTERS is used to build historical_row initially. dbug_print_rec() debug functions. --- mysql-test/suite/versioning/r/delete.result | 19 +++++++++ mysql-test/suite/versioning/r/update.result | 20 +++++++++ mysql-test/suite/versioning/t/delete.test | 15 +++++++ mysql-test/suite/versioning/t/update.test | 15 +++++++ storage/innobase/include/row0ins.h | 1 + storage/innobase/row/row0ins.cc | 31 +++++++++++++- storage/innobase/row/row0mysql.cc | 45 +++++++++++++++++---- storage/innobase/row/row0row.cc | 2 + storage/innobase/ut/ut0ut.cc | 39 ++++++++++++++++++ 9 files changed, 179 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/versioning/r/delete.result b/mysql-test/suite/versioning/r/delete.result index 5aa239b9cb8..4a0fec639b0 100644 --- a/mysql-test/suite/versioning/r/delete.result +++ b/mysql-test/suite/versioning/r/delete.result @@ -130,3 +130,22 @@ ERROR 42S02: Table 'test.xx' doesn't exist drop procedure pr; drop trigger tr; drop table t1; +# +# MDEV-21138 Assertion `col->ord_part' or `f.col->ord_part' failed in row_build_index_entry_low +# +create table t1 ( +f1 int, f2 text, f3 int, fulltext (f2), key(f1), key(f3), +foreign key r (f3) references t1 (f1) on delete set null) +with system versioning engine innodb; +insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1); +select f1, f3, check_row(row_start, row_end) from t1; +f1 f3 check_row(row_start, row_end) +1 1 CURRENT ROW +1 1 CURRENT ROW +delete from t1; +select f1, f3, check_row(row_start, row_end) from t1 for system_time all; +f1 f3 check_row(row_start, row_end) +1 1 HISTORICAL ROW +1 NULL ERROR: row_end == row_start +1 1 HISTORICAL ROW +drop table t1; diff --git a/mysql-test/suite/versioning/r/update.result b/mysql-test/suite/versioning/r/update.result index cd26c341113..0b239423834 100644 --- a/mysql-test/suite/versioning/r/update.result +++ b/mysql-test/suite/versioning/r/update.result @@ -241,6 +241,26 @@ B2 salary 1 2500 drop table t1; drop table t2; +# Ensure FTS retains correct history +create table t1 ( +x int, y text, fulltext (y), +row_start SYS_DATATYPE as row start invisible, +row_end SYS_DATATYPE as row end invisible, +period for system_time (row_start, row_end)) +with system versioning engine innodb; +insert into t1 values (1, repeat('LONG', 2048)); +update t1 set x= x + 1; +select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y; +x left(y, 4) length(y) check_row(row_start, row_end) +1 LONG 8192 HISTORICAL ROW +2 LONG 8192 CURRENT ROW +update t1 set y= 'SHORT'; +select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y; +x left(y, 4) length(y) check_row(row_start, row_end) +1 LONG 8192 HISTORICAL ROW +2 LONG 8192 HISTORICAL ROW +2 SHOR 5 CURRENT ROW +drop tables t1; ### Issue tempesta-tech/mariadb#365, bug 7 (duplicate of historical row) create or replace table t1 (a int primary key, b int) with system versioning engine myisam; diff --git a/mysql-test/suite/versioning/t/delete.test b/mysql-test/suite/versioning/t/delete.test index 492463f9395..e2f7240303d 100644 --- a/mysql-test/suite/versioning/t/delete.test +++ b/mysql-test/suite/versioning/t/delete.test @@ -94,4 +94,19 @@ drop procedure pr; drop trigger tr; drop table t1; +--echo # +--echo # MDEV-21138 Assertion `col->ord_part' or `f.col->ord_part' failed in row_build_index_entry_low +--echo # +create table t1 ( + f1 int, f2 text, f3 int, fulltext (f2), key(f1), key(f3), + foreign key r (f3) references t1 (f1) on delete set null) +with system versioning engine innodb; +insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1); +select f1, f3, check_row(row_start, row_end) from t1; +delete from t1; +select f1, f3, check_row(row_start, row_end) from t1 for system_time all; + +# cleanup +drop table t1; + --source suite/versioning/common_finish.inc diff --git a/mysql-test/suite/versioning/t/update.test b/mysql-test/suite/versioning/t/update.test index 06f81ea9064..f64dcfd0e5c 100644 --- a/mysql-test/suite/versioning/t/update.test +++ b/mysql-test/suite/versioning/t/update.test @@ -147,6 +147,21 @@ select @tmp2 = sys_trx_start as B2, salary from t2; drop table t1; drop table t2; +--echo # Ensure FTS retains correct history +replace_result $sys_datatype_expl SYS_DATATYPE; +eval create table t1 ( + x int, y text, fulltext (y), + row_start $sys_datatype_expl as row start invisible, + row_end $sys_datatype_expl as row end invisible, + period for system_time (row_start, row_end)) +with system versioning engine innodb; +insert into t1 values (1, repeat('LONG', 2048)); +update t1 set x= x + 1; +select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y; +update t1 set y= 'SHORT'; +select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y; +drop tables t1; + --echo ### Issue tempesta-tech/mariadb#365, bug 7 (duplicate of historical row) create or replace table t1 (a int primary key, b int) with system versioning engine myisam; diff --git a/storage/innobase/include/row0ins.h b/storage/innobase/include/row0ins.h index 34427dc6dc7..9a16394a052 100644 --- a/storage/innobase/include/row0ins.h +++ b/storage/innobase/include/row0ins.h @@ -206,6 +206,7 @@ struct ins_node_t if this is NULL, entry list should be created and buffers for sys fields in row allocated */ void vers_update_end(row_prebuilt_t *prebuilt, bool history_row); + bool vers_history_row() const; /* true if 'row' is historical */ }; /** Create an insert object. diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index d581fd21d99..88152841293 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -3566,6 +3566,16 @@ row_ins_get_row_from_select( } } +inline +bool ins_node_t::vers_history_row() const +{ + if (!table->versioned()) + return false; + dfield_t* row_end = dtuple_get_nth_field(row, table->vers_end); + return row_end->vers_history_row(); +} + + /***********************************************************//** Inserts a row to a table. @return DB_SUCCESS if operation successfully completed, else error @@ -3604,12 +3614,31 @@ row_ins( ut_ad(node->state == INS_NODE_INSERT_ENTRIES); while (node->index != NULL) { - if (node->index->type != DICT_FTS) { + dict_index_t *index = node->index; + /* + We do not insert history rows into FTS_DOC_ID_INDEX because + it is unique by FTS_DOC_ID only and we do not want to add + row_end to unique key. Fulltext field works the way new + FTS_DOC_ID is created on every fulltext UPDATE, so holding only + FTS_DOC_ID for history is enough. + */ + const unsigned type = index->type; + if (index->type & DICT_FTS) { + } else if (!(type & DICT_UNIQUE) || index->n_uniq > 1 + || !node->vers_history_row()) { + dberr_t err = row_ins_index_entry_step(node, thr); if (err != DB_SUCCESS) { DBUG_RETURN(err); } + } else { + /* Unique indexes with system versioning must contain + the version end column. The only exception is a hidden + FTS_DOC_ID_INDEX that InnoDB may create on a hidden or + user-created FTS_DOC_ID column. */ + ut_ad(!strcmp(index->name, FTS_DOC_ID_INDEX_NAME)); + ut_ad(!strcmp(index->fields[0].name, FTS_DOC_ID_COL_NAME)); } node->index = dict_table_get_next_index(node->index); diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index dc88b41c6b4..848f60c47d0 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2104,10 +2104,18 @@ row_mysql_unfreeze_data_dictionary( @param buf Buffer to hold start time data */ void thd_get_query_start_data(THD *thd, char *buf); -/** Function restores btr_pcur_t, creates dtuple_t from rec_t, -sets row_end = CURRENT_TIMESTAMP/trx->id, inserts it to a table and updates -table statistics. -This is used in UPDATE CASCADE/SET NULL of a system versioning table. +/** Insert history row when evaluating foreign key referential action. + +1. Create new dtuple_t 'row' from node->historical_row; +2. Update its row_end to current timestamp; +3. Insert it to a table; +4. Update table statistics. + +This is used in UPDATE CASCADE/SET NULL of a system versioned referenced table. + +node->historical_row: dtuple_t containing pointers of row changed by refertial +action. + @param[in] thr current query thread @param[in] node a node which just updated a row in a foreign table @return DB_SUCCESS or some error */ @@ -2119,9 +2127,16 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node) dict_table_t* table = node->table; ut_ad(table->versioned()); - dtuple_t* row = node->historical_row; - ut_ad(row); - node->historical_row = NULL; + dtuple_t* row; + const ulint n_cols = dict_table_get_n_cols(table); + const ulint n_v_cols = dict_table_get_n_v_cols(table); + + ut_ad(n_cols == dtuple_get_n_fields(node->historical_row)); + ut_ad(n_v_cols == dtuple_get_n_v_fields(node->historical_row)); + + row = dtuple_create_with_vcol(node->historical_heap, n_cols, n_v_cols); + + dict_table_copy_types(row, table); ins_node_t* insert_node = ins_node_create(INS_DIRECT, table, node->historical_heap); @@ -2134,6 +2149,22 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node) insert_node->common.parent = thr; ins_node_set_new_row(insert_node, row); + ut_ad(n_cols > DATA_N_SYS_COLS); + // Exclude DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR + for (ulint i = 0; i < n_cols - DATA_N_SYS_COLS; i++) { + dfield_t *dst= dtuple_get_nth_field(row, i); + dfield_t *src= dtuple_get_nth_field(node->historical_row, i); + dfield_copy(dst, src); + } + + for (ulint i = 0; i < n_v_cols; i++) { + dfield_t *dst= dtuple_get_nth_v_field(row, i); + dfield_t *src= dtuple_get_nth_v_field(node->historical_row, i); + dfield_copy(dst, src); + } + + node->historical_row = NULL; + row_end = dtuple_get_nth_field(row, table->vers_end); if (dict_table_get_nth_col(table, table->vers_end)->vers_native()) { mach_write_to_8(row_end_data, trx->id); diff --git a/storage/innobase/row/row0row.cc b/storage/innobase/row/row0row.cc index bcb128f2870..f6b7a9f49b6 100644 --- a/storage/innobase/row/row0row.cc +++ b/storage/innobase/row/row0row.cc @@ -294,6 +294,8 @@ row_build_index_entry_low( continue; } + ut_ad(!(index->type & DICT_FTS)); + if ((!ind_field || ind_field->prefix_len == 0) && (!dfield_is_ext(dfield) || dict_index_is_clust(index))) { diff --git a/storage/innobase/ut/ut0ut.cc b/storage/innobase/ut/ut0ut.cc index 6d2b84625f7..65a2c9356e2 100644 --- a/storage/innobase/ut/ut0ut.cc +++ b/storage/innobase/ut/ut0ut.cc @@ -38,6 +38,7 @@ Created 5/11/1994 Heikki Tuuri #include #include "log.h" #include "my_cpu.h" +#include "rem0rec.h" /**********************************************************//** Returns the number of milliseconds since some epoch. The @@ -627,4 +628,42 @@ fatal_or_error::~fatal_or_error() } // namespace ib +#ifndef DBUG_OFF +const char * dbug_print_rec(const rec_t* rec, const rec_offs* offsets) +{ + rec_printer r(rec, offsets); + return r.str().c_str(); +} + +const char * dbug_print_rec(const rec_t* rec, ulint info, const rec_offs* offsets) +{ + rec_printer r(rec, info, offsets); + return r.str().c_str(); +} + +const char * dbug_print_rec(const dtuple_t* tuple) +{ + rec_printer r(tuple); + return r.str().c_str(); +} + +const char * dbug_print_rec(const dfield_t* field, ulint n) +{ + rec_printer r(field, n); + return r.str().c_str(); +} + +const char * dbug_print_rec(const rec_t* rec, dict_index_t* index) +{ + rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs* offsets = offsets_; + rec_offs_init(offsets_); + mem_heap_t* tmp_heap = NULL; + offsets = rec_get_offsets(rec, index, offsets, true, + ULINT_UNDEFINED, &tmp_heap); + rec_printer r(rec, offsets); + return r.str().c_str(); +} +#endif /* !DBUG_OFF */ + #endif /* !UNIV_INNOCHECKSUM */ From 932ec586aada4bd78f613ee10750effc7f442327 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 22 Dec 2020 03:33:53 +0300 Subject: [PATCH 15/17] MDEV-23644 Assertion on evaluating foreign referential action for self-reference in system versioned table First part of the fix (row0mysql.cc) addresses external columns when adding history row on referential action. The full data must be retrieved before the row is inserted. Second part of the fix (the rest) avoids duplicate primary key error between the history row generated on referential action and the history row generated by SQL command. Both command and referential action can happen on same table since foreign key can be self-reference (parent and child tables are same). Moreover, the self-reference can refer multiple rows when the key is non-unique. In such case history is generated by referential action occured on first row but processed all rows by a matched key. The second round is when the next row is processed by a command but history already exists. In such case we check TRX_ID of existing history row and if it is the same we assume the above situation and skip adding one more history row or failing the command. --- mysql-test/suite/versioning/common.inc | 21 +++++++++++++++++++ mysql-test/suite/versioning/common_finish.inc | 2 ++ mysql-test/suite/versioning/r/delete.result | 8 +++---- mysql-test/suite/versioning/r/foreign.result | 16 ++++++++++++++ mysql-test/suite/versioning/r/trx_id.result | 8 +++++++ mysql-test/suite/versioning/t/delete.test | 4 ++-- mysql-test/suite/versioning/t/foreign.test | 18 ++++++++++++++++ mysql-test/suite/versioning/t/trx_id.test | 6 ++++++ sql/sql_delete.cc | 10 ++++++++- storage/innobase/data/data0data.cc | 2 +- storage/innobase/include/data0data.h | 10 +++++++++ storage/innobase/row/row0ins.cc | 12 +++++++++++ storage/innobase/row/row0mysql.cc | 21 ++++++++++++++++++- 13 files changed, 129 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/versioning/common.inc b/mysql-test/suite/versioning/common.inc index efb081a02e4..25adf15dd50 100644 --- a/mysql-test/suite/versioning/common.inc +++ b/mysql-test/suite/versioning/common.inc @@ -70,6 +70,11 @@ returns int deterministic return sys_trx_end = $sys_datatype_max; +eval create or replace function current_row_ts(sys_trx_end timestamp(6)) +returns int +deterministic + return convert_tz(sys_trx_end, '+00:00', @@time_zone) = TIMESTAMP'2038-01-19 03:14:07.999999'; + delimiter ~~; eval create or replace function check_row(row_start $sys_datatype_expl, row_end $sys_datatype_expl) returns varchar(255) @@ -86,4 +91,20 @@ begin end~~ delimiter ;~~ +delimiter ~~; +eval create or replace function check_row_ts(row_start timestamp(6), row_end timestamp(6)) +returns varchar(255) +deterministic +begin + if row_end < row_start then + return "ERROR: row_end < row_start"; + elseif row_end = row_start then + return "ERROR: row_end == row_start"; + elseif current_row_ts(row_end) then + return "CURRENT ROW"; + end if; + return "HISTORICAL ROW"; +end~~ +delimiter ;~~ + --enable_query_log diff --git a/mysql-test/suite/versioning/common_finish.inc b/mysql-test/suite/versioning/common_finish.inc index 61641c6c5ce..3c4e7b66ff3 100644 --- a/mysql-test/suite/versioning/common_finish.inc +++ b/mysql-test/suite/versioning/common_finish.inc @@ -4,5 +4,7 @@ drop procedure if exists verify_trt; drop procedure if exists verify_trt_dummy; drop function if exists current_row; drop function if exists check_row; +drop function if exists current_row_ts; +drop function if exists check_row_ts; --enable_warnings --enable_query_log diff --git a/mysql-test/suite/versioning/r/delete.result b/mysql-test/suite/versioning/r/delete.result index 4a0fec639b0..0f9e2c22130 100644 --- a/mysql-test/suite/versioning/r/delete.result +++ b/mysql-test/suite/versioning/r/delete.result @@ -138,13 +138,13 @@ f1 int, f2 text, f3 int, fulltext (f2), key(f1), key(f3), foreign key r (f3) references t1 (f1) on delete set null) with system versioning engine innodb; insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1); -select f1, f3, check_row(row_start, row_end) from t1; -f1 f3 check_row(row_start, row_end) +select f1, f3, check_row_ts(row_start, row_end) from t1; +f1 f3 check_row_ts(row_start, row_end) 1 1 CURRENT ROW 1 1 CURRENT ROW delete from t1; -select f1, f3, check_row(row_start, row_end) from t1 for system_time all; -f1 f3 check_row(row_start, row_end) +select f1, f3, check_row_ts(row_start, row_end) from t1 for system_time all; +f1 f3 check_row_ts(row_start, row_end) 1 1 HISTORICAL ROW 1 NULL ERROR: row_end == row_start 1 1 HISTORICAL ROW diff --git a/mysql-test/suite/versioning/r/foreign.result b/mysql-test/suite/versioning/r/foreign.result index 32b5e5cf3d8..e54afdbc74e 100644 --- a/mysql-test/suite/versioning/r/foreign.result +++ b/mysql-test/suite/versioning/r/foreign.result @@ -398,6 +398,8 @@ Warning 1265 Data truncated for column 'f12' at row 7 SET timestamp = 9; REPLACE INTO t2 SELECT * FROM t2; DROP TABLE t1, t2; +set timestamp= default; +set time_zone='+00:00'; # # MDEV-16210 FK constraints on versioned tables use historical rows, which may cause constraint violation # @@ -427,3 +429,17 @@ insert into t2 values (1), (1); # DELETE from foreign table is allowed delete from t2; drop tables t2, t1; +# +# MDEV-23644 Assertion on evaluating foreign referential action for self-reference in system versioned table +# +create table t1 (pk int primary key, f1 int,f2 int, f3 text, +key(f1), fulltext(f3), key(f3(10)), +foreign key (f2) references t1 (f1) on delete set null +) engine=innodb with system versioning; +insert into t1 values (1, 8, 8, 'SHORT'), (2, 8, 8, repeat('LONG', 8071)); +delete from t1; +select pk, f1, f2, left(f3, 4), check_row_ts(row_start, row_end) from t1 for system_time all order by pk; +pk f1 f2 left(f3, 4) check_row_ts(row_start, row_end) +1 8 8 SHOR HISTORICAL ROW +2 8 8 LONG HISTORICAL ROW +drop table t1; diff --git a/mysql-test/suite/versioning/r/trx_id.result b/mysql-test/suite/versioning/r/trx_id.result index 5dd9e7aa188..58d02505fb6 100644 --- a/mysql-test/suite/versioning/r/trx_id.result +++ b/mysql-test/suite/versioning/r/trx_id.result @@ -5,7 +5,15 @@ sys_trx_start bigint(20) unsigned as row start invisible, sys_trx_end bigint(20) unsigned as row end invisible, period for system_time (sys_trx_start, sys_trx_end) ) with system versioning; +# No history inside the transaction +start transaction; insert into t1 (x) values (1); +update t1 set x= x + 1; +update t1 set x= x + 1; +commit; +select *, sys_trx_start > 1, sys_trx_end from t1 for system_time all; +x sys_trx_start > 1 sys_trx_end +3 1 18446744073709551615 # ALTER ADD SYSTEM VERSIONING should write to mysql.transaction_registry set @@system_versioning_alter_history=keep; create or replace table t1 (x int); diff --git a/mysql-test/suite/versioning/t/delete.test b/mysql-test/suite/versioning/t/delete.test index e2f7240303d..a5a0497fef2 100644 --- a/mysql-test/suite/versioning/t/delete.test +++ b/mysql-test/suite/versioning/t/delete.test @@ -102,9 +102,9 @@ create table t1 ( foreign key r (f3) references t1 (f1) on delete set null) with system versioning engine innodb; insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1); -select f1, f3, check_row(row_start, row_end) from t1; +select f1, f3, check_row_ts(row_start, row_end) from t1; delete from t1; -select f1, f3, check_row(row_start, row_end) from t1 for system_time all; +select f1, f3, check_row_ts(row_start, row_end) from t1 for system_time all; # cleanup drop table t1; diff --git a/mysql-test/suite/versioning/t/foreign.test b/mysql-test/suite/versioning/t/foreign.test index 7493f99cba7..725f51f0660 100644 --- a/mysql-test/suite/versioning/t/foreign.test +++ b/mysql-test/suite/versioning/t/foreign.test @@ -421,6 +421,8 @@ REPLACE INTO t2 SELECT * FROM t2; # Cleanup DROP TABLE t1, t2; +set timestamp= default; +set time_zone='+00:00'; --let $datadir= `select @@datadir` --remove_file $datadir/test/t1.data --remove_file $datadir/test/t1.data.2 @@ -458,4 +460,20 @@ insert into t2 values (1), (1); delete from t2; drop tables t2, t1; +--echo # +--echo # MDEV-23644 Assertion on evaluating foreign referential action for self-reference in system versioned table +--echo # +create table t1 (pk int primary key, f1 int,f2 int, f3 text, + key(f1), fulltext(f3), key(f3(10)), + foreign key (f2) references t1 (f1) on delete set null +) engine=innodb with system versioning; + +insert into t1 values (1, 8, 8, 'SHORT'), (2, 8, 8, repeat('LONG', 8071)); + +delete from t1; +select pk, f1, f2, left(f3, 4), check_row_ts(row_start, row_end) from t1 for system_time all order by pk; + +# cleanup +drop table t1; + --source suite/versioning/common_finish.inc diff --git a/mysql-test/suite/versioning/t/trx_id.test b/mysql-test/suite/versioning/t/trx_id.test index 38724a47fd1..7dfc8acb080 100644 --- a/mysql-test/suite/versioning/t/trx_id.test +++ b/mysql-test/suite/versioning/t/trx_id.test @@ -14,7 +14,13 @@ create or replace table t1 ( period for system_time (sys_trx_start, sys_trx_end) ) with system versioning; +--echo # No history inside the transaction +start transaction; insert into t1 (x) values (1); +update t1 set x= x + 1; +update t1 set x= x + 1; +commit; +select *, sys_trx_start > 1, sys_trx_end from t1 for system_time all; --echo # ALTER ADD SYSTEM VERSIONING should write to mysql.transaction_registry set @@system_versioning_alter_history=keep; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 929455977e6..0857c4e5457 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -246,7 +246,15 @@ int TABLE::delete_row() store_record(this, record[1]); vers_update_end(); - return file->ha_update_row(record[1], record[0]); + int err= file->ha_update_row(record[1], record[0]); + /* + MDEV-23644: we get HA_ERR_FOREIGN_DUPLICATE_KEY iff we already got history + row with same trx_id which is the result of foreign key action, so we + don't need one more history row. + */ + if (err == HA_ERR_FOREIGN_DUPLICATE_KEY) + return file->ha_delete_row(record[0]); + return err; } diff --git a/storage/innobase/data/data0data.cc b/storage/innobase/data/data0data.cc index 47e58f1614a..3e23cd6f662 100644 --- a/storage/innobase/data/data0data.cc +++ b/storage/innobase/data/data0data.cc @@ -738,7 +738,7 @@ void dtuple_convert_back_big_rec( /*========================*/ dict_index_t* index MY_ATTRIBUTE((unused)), /*!< in: index */ - dtuple_t* entry, /*!< in: entry whose data was put to vector */ + dtuple_t* entry, /*!< in/out: entry whose data was put to vector */ big_rec_t* vector) /*!< in, own: big rec vector; it is freed in this function */ { diff --git a/storage/innobase/include/data0data.h b/storage/innobase/include/data0data.h index 11a7f2e516f..002332852b8 100644 --- a/storage/innobase/include/data0data.h +++ b/storage/innobase/include/data0data.h @@ -543,6 +543,16 @@ struct dtuple_t { inserted or updated. @param[in] index index possibly with instantly added columns */ void trim(const dict_index_t& index); + bool vers_history_row() const + { + for (ulint i = 0; i < n_fields; i++) { + const dfield_t* field = &fields[i]; + if (field->type.vers_sys_end()) { + return field->vers_history_row(); + } + } + return false; + } }; inline ulint dtuple_get_n_fields(const dtuple_t* tuple) diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 88152841293..43f80adebbd 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -2385,6 +2385,18 @@ row_ins_duplicate_error_in_clust( duplicate: trx->error_info = cursor->index; err = DB_DUPLICATE_KEY; + if (cursor->index->table->versioned() + && entry->vers_history_row()) + { + ulint trx_id_len; + byte *trx_id = rec_get_nth_field( + rec, offsets, n_unique, + &trx_id_len); + ut_ad(trx_id_len == DATA_TRX_ID_LEN); + if (trx->id == trx_read_trx_id(trx_id)) { + err = DB_FOREIGN_DUPLICATE_KEY; + } + } goto func_exit; } } diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 848f60c47d0..c643a907722 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2125,6 +2125,7 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node) dfield_t* row_end; char row_end_data[8]; dict_table_t* table = node->table; + page_size_t page_size= dict_table_page_size(table); ut_ad(table->versioned()); dtuple_t* row; @@ -2152,9 +2153,27 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node) ut_ad(n_cols > DATA_N_SYS_COLS); // Exclude DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR for (ulint i = 0; i < n_cols - DATA_N_SYS_COLS; i++) { - dfield_t *dst= dtuple_get_nth_field(row, i); dfield_t *src= dtuple_get_nth_field(node->historical_row, i); + dfield_t *dst= dtuple_get_nth_field(row, i); dfield_copy(dst, src); + if (dfield_is_ext(src)) { + byte *field_data + = static_cast(dfield_get_data(src)); + ulint ext_len; + ulint field_len = dfield_get_len(src); + + ut_a(field_len >= BTR_EXTERN_FIELD_REF_SIZE); + + ut_a(memcmp(field_data + field_len + - BTR_EXTERN_FIELD_REF_SIZE, + field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + + byte *data = btr_copy_externally_stored_field( + &ext_len, field_data, page_size, field_len, + node->historical_heap); + dfield_set_data(dst, data, ext_len); + } } for (ulint i = 0; i < n_v_cols; i++) { From 9b38ed4c85bda254f48c02890f134adf227700e3 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 22 Dec 2020 08:34:18 +0300 Subject: [PATCH 16/17] MDEV-23446 UPDATE does not insert history row if the row is not changed Add history row outside of compare_record() check. For TRX_ID versioning we have to fail can_compare_record to force InnoDB update which adds history row; and there in ha_innobase::update_row() is additional "row changed" check where we force history row anyway. --- mysql-test/suite/versioning/r/update.result | 29 +++++++++ mysql-test/suite/versioning/t/update.test | 25 ++++++++ sql/sql_update.cc | 69 +++++++++++---------- storage/innobase/handler/ha_innodb.cc | 14 +++++ 4 files changed, 105 insertions(+), 32 deletions(-) diff --git a/mysql-test/suite/versioning/r/update.result b/mysql-test/suite/versioning/r/update.result index 0b239423834..fbb9f541b06 100644 --- a/mysql-test/suite/versioning/r/update.result +++ b/mysql-test/suite/versioning/r/update.result @@ -370,3 +370,32 @@ insert into t1 (a) values (1), (2); update ignore t1 set a= 3; delete history from t1; drop table t1; +# +# MDEV-23446 UPDATE does not insert history row if the row is not changed +# +create table t1 ( +a int, +row_start SYS_DATATYPE as row start invisible, +row_end SYS_DATATYPE as row end invisible, +period for system_time (row_start, row_end)) with system versioning; +insert into t1 values (1); +update t1 set a= 1; +select *, check_row(row_start, row_end) from t1 for system_time all order by row_end; +a check_row(row_start, row_end) +1 HISTORICAL ROW +1 CURRENT ROW +# multi-update +create or replace table t2 like t1; +create or replace table t3 like t1; +insert into t2 values (1); +insert into t3 values (1); +update t2, t3 set t2.a= 1, t3.a= 1 where t2.a = t3.a; +select *, check_row(row_start, row_end) from t2 for system_time all order by row_end; +a check_row(row_start, row_end) +1 HISTORICAL ROW +1 CURRENT ROW +select *, check_row(row_start, row_end) from t2 for system_time all order by row_end; +a check_row(row_start, row_end) +1 HISTORICAL ROW +1 CURRENT ROW +drop tables t1, t2, t3; diff --git a/mysql-test/suite/versioning/t/update.test b/mysql-test/suite/versioning/t/update.test index f64dcfd0e5c..7f99e307942 100644 --- a/mysql-test/suite/versioning/t/update.test +++ b/mysql-test/suite/versioning/t/update.test @@ -301,4 +301,29 @@ delete history from t1; # cleanup drop table t1; +--echo # +--echo # MDEV-23446 UPDATE does not insert history row if the row is not changed +--echo # +replace_result $sys_datatype_expl SYS_DATATYPE; +eval create table t1 ( + a int, + row_start $sys_datatype_expl as row start invisible, + row_end $sys_datatype_expl as row end invisible, + period for system_time (row_start, row_end)) with system versioning; +insert into t1 values (1); +update t1 set a= 1; +select *, check_row(row_start, row_end) from t1 for system_time all order by row_end; + +--echo # multi-update +create or replace table t2 like t1; +create or replace table t3 like t1; +insert into t2 values (1); +insert into t3 values (1); +update t2, t3 set t2.a= 1, t3.a= 1 where t2.a = t3.a; +select *, check_row(row_start, row_end) from t2 for system_time all order by row_end; +select *, check_row(row_start, row_end) from t2 for system_time all order by row_end; + +# cleanup +drop tables t1, t2, t3; + source suite/versioning/common_finish.inc; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 318ab8e7f69..314256cd8c5 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -52,8 +52,9 @@ compare_record(TABLE*). */ bool records_are_comparable(const TABLE *table) { - return ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || - bitmap_is_subset(table->write_set, table->read_set); + return !table->versioned(VERS_TRX_ID) && + (((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || + bitmap_is_subset(table->write_set, table->read_set)); } @@ -960,25 +961,27 @@ update_begin: } else if (likely(!error)) { - if (has_vers_fields && table->versioned()) - { - if (table->versioned(VERS_TIMESTAMP)) - { - store_record(table, record[2]); - table->mark_columns_per_binlog_row_image(); - error= vers_insert_history_row(table); - restore_record(table, record[2]); - } - if (likely(!error)) - updated_sys_ver++; - } - if (likely(!error)) - updated++; + if (has_vers_fields && table->versioned(VERS_TRX_ID)) + updated_sys_ver++; + updated++; } if (unlikely(error) && (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL))) { + goto error; + } + } + + if (likely(!error) && has_vers_fields && table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + table->mark_columns_per_binlog_row_image(); + error= vers_insert_history_row(table); + restore_record(table, record[2]); + if (unlikely(error)) + { +error: /* If (ignore && error is ignorable) we don't have to do anything; otherwise... @@ -989,10 +992,11 @@ update_begin: flags|= ME_FATALERROR; /* Other handler errors are fatal */ prepare_record_for_error_message(error, table); - table->file->print_error(error,MYF(flags)); - error= 1; - break; - } + table->file->print_error(error,MYF(flags)); + error= 1; + break; + } + updated_sys_ver++; } if (table->triggers && @@ -2410,6 +2414,7 @@ int multi_update::send_data(List ¬_used_values) if (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)) { +error: /* If (ignore && error == is ignorable) we don't have to do anything; otherwise... @@ -2431,19 +2436,8 @@ int multi_update::send_data(List ¬_used_values) error= 0; updated--; } - else if (has_vers_fields && table->versioned()) + else if (has_vers_fields && table->versioned(VERS_TRX_ID)) { - if (table->versioned(VERS_TIMESTAMP)) - { - store_record(table, record[2]); - if (vers_insert_history_row(table)) - { - restore_record(table, record[2]); - error= 1; - break; - } - restore_record(table, record[2]); - } updated_sys_ver++; } /* non-transactional or transactional table got modified */ @@ -2457,6 +2451,17 @@ int multi_update::send_data(List ¬_used_values) } } } + if (has_vers_fields && table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + if (vers_insert_history_row(table)) + { + restore_record(table, record[2]); + goto error; + } + restore_record(table, record[2]); + updated_sys_ver++; + } if (table->triggers && unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index d1f155eecff..e66a340a7f0 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -8799,6 +8799,20 @@ ha_innobase::update_row( MySQL that the row is not really updated and it should not increase the count of updated rows. This is fix for http://bugs.mysql.com/29157 */ + if (m_prebuilt->versioned_write + && thd_sql_command(m_user_thd) != SQLCOM_ALTER_TABLE + /* Multiple UPDATE of same rows in single transaction create + historical rows only once. */ + && trx->id != table->vers_start_id()) { + error = row_insert_for_mysql((byte*) old_row, + m_prebuilt, + ROW_INS_HISTORICAL); + if (error != DB_SUCCESS) { + goto func_exit; + } + innobase_srv_conc_exit_innodb(m_prebuilt); + innobase_active_small(); + } DBUG_RETURN(HA_ERR_RECORD_IS_THE_SAME); } else { const bool vers_set_fields = m_prebuilt->versioned_write From 097786c485c6d10064e8bc593b6aa54e477712f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 23 Dec 2020 14:24:06 +0200 Subject: [PATCH 17/17] Partially revert 7410ff436e95de09c2f3f0028e7af8b3a043028b Remove the unused dbug_print_rec() functions because they break the clang build due to -Wreturn-stack-address --- storage/innobase/ut/ut0ut.cc | 39 ------------------------------------ 1 file changed, 39 deletions(-) diff --git a/storage/innobase/ut/ut0ut.cc b/storage/innobase/ut/ut0ut.cc index 65a2c9356e2..6d2b84625f7 100644 --- a/storage/innobase/ut/ut0ut.cc +++ b/storage/innobase/ut/ut0ut.cc @@ -38,7 +38,6 @@ Created 5/11/1994 Heikki Tuuri #include #include "log.h" #include "my_cpu.h" -#include "rem0rec.h" /**********************************************************//** Returns the number of milliseconds since some epoch. The @@ -628,42 +627,4 @@ fatal_or_error::~fatal_or_error() } // namespace ib -#ifndef DBUG_OFF -const char * dbug_print_rec(const rec_t* rec, const rec_offs* offsets) -{ - rec_printer r(rec, offsets); - return r.str().c_str(); -} - -const char * dbug_print_rec(const rec_t* rec, ulint info, const rec_offs* offsets) -{ - rec_printer r(rec, info, offsets); - return r.str().c_str(); -} - -const char * dbug_print_rec(const dtuple_t* tuple) -{ - rec_printer r(tuple); - return r.str().c_str(); -} - -const char * dbug_print_rec(const dfield_t* field, ulint n) -{ - rec_printer r(field, n); - return r.str().c_str(); -} - -const char * dbug_print_rec(const rec_t* rec, dict_index_t* index) -{ - rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; - rec_offs* offsets = offsets_; - rec_offs_init(offsets_); - mem_heap_t* tmp_heap = NULL; - offsets = rec_get_offsets(rec, index, offsets, true, - ULINT_UNDEFINED, &tmp_heap); - rec_printer r(rec, offsets); - return r.str().c_str(); -} -#endif /* !DBUG_OFF */ - #endif /* !UNIV_INNOCHECKSUM */