diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2021-03-05 10:36:51 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2021-03-05 10:36:51 +0200 |
commit | 8bab5bb332aec671febbfc1b9c30c2b269c1d7d4 (patch) | |
tree | 67f1dfddcba6458408565eb9065234a325156d66 | |
parent | 82efe4a15a985c3902e80eb7e1a70841c08d9f2e (diff) | |
parent | 5bd994b0d56d11bf62717a84172c49ca9ed37de4 (diff) | |
download | mariadb-git-8bab5bb332aec671febbfc1b9c30c2b269c1d7d4.tar.gz |
Merge 10.3 into 10.4
34 files changed, 743 insertions, 264 deletions
diff --git a/mysql-test/lib/My/Debugger.pm b/mysql-test/lib/My/Debugger.pm index d2add55d680..a2d5f2a5435 100644 --- a/mysql-test/lib/My/Debugger.pm +++ b/mysql-test/lib/My/Debugger.pm @@ -74,7 +74,8 @@ my %debuggers = ( options => '-f -o {log} {exe} {args}', }, rr => { - options => 'record -o {log} {exe} {args}', + options => '_RR_TRACE_DIR={log} rr record {exe} {args}', + run => 'env', pre => sub { ::mtr_error('rr requires kernel.perf_event_paranoid <= 1') if ::mtr_grab_file('/proc/sys/kernel/perf_event_paranoid') > 1; diff --git a/mysql-test/main/group_by.result b/mysql-test/main/group_by.result index 47f399ecd3d..db75287c61c 100644 --- a/mysql-test/main/group_by.result +++ b/mysql-test/main/group_by.result @@ -2941,5 +2941,34 @@ f COUNT(*) NULL 1 DROP TABLE t1; # +# MDEV-24710 Uninitialized value upon CREATE .. SELECT ... VALUE +# +CREATE TABLE t1 (a VARCHAR(8) NOT NULL DEFAULT ''); +INSERT INTO t1 (a) VALUES ('foo'); +CREATE TABLE t2 AS SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL; +SELECT * from t2; +f1 f2 +NULL NULL +SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL; +f1 f2 +NULL NULL +SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE 1=0; +f1 f2 +NULL NULL +drop table t1,t2; +# Extra test by to check the fix for MDEV-24710 +create table t20 (pk int primary key, a int); +insert into t20 values (1,1); +create table t21 (pk int primary key, b int not null); +insert into t21 values (1,1); +create table t22 (a int); +insert into t22 values (1),(2); +select a, (select max(t21.b) from t20 left join t21 on t21.pk=t20.a+10 +where t20.pk=1 and rand(123) < 0.5) as SUBQ from t22; +a SUBQ +1 NULL +2 NULL +drop table t20, t21, t22; +# # End of 10.3 tests # diff --git a/mysql-test/main/group_by.test b/mysql-test/main/group_by.test index 16cb7cfb9fd..3ca518420a0 100644 --- a/mysql-test/main/group_by.test +++ b/mysql-test/main/group_by.test @@ -2043,5 +2043,28 @@ SELECT d != '2023-03-04' AS f, COUNT(*) FROM t1 GROUP BY d WITH ROLLUP; DROP TABLE t1; --echo # +--echo # MDEV-24710 Uninitialized value upon CREATE .. SELECT ... VALUE +--echo # + +CREATE TABLE t1 (a VARCHAR(8) NOT NULL DEFAULT ''); +INSERT INTO t1 (a) VALUES ('foo'); +CREATE TABLE t2 AS SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL; +SELECT * from t2; +SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL; +SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE 1=0; +drop table t1,t2; + +--echo # Extra test by to check the fix for MDEV-24710 + +create table t20 (pk int primary key, a int); +insert into t20 values (1,1);create table t21 (pk int primary key, b int not null); +insert into t21 values (1,1); +create table t22 (a int); +insert into t22 values (1),(2); +select a, (select max(t21.b) from t20 left join t21 on t21.pk=t20.a+10 + where t20.pk=1 and rand(123) < 0.5) as SUBQ from t22; +drop table t20, t21, t22; + +--echo # --echo # End of 10.3 tests --echo # diff --git a/mysql-test/main/having.result b/mysql-test/main/having.result index 703f013c2da..51b88c5b8d2 100644 --- a/mysql-test/main/having.result +++ b/mysql-test/main/having.result @@ -847,6 +847,39 @@ t r DROP TABLE t1; DROP FUNCTION next_seq_value; DROP TABLE series; +# +# MDEV-24958 Server crashes in my_strtod / +# Value_source::Converter_strntod::Converter_strntod with DEFAULT(blob) +# +# MDEV-24942 Server crashes in _ma_rec_pack / _ma_write_blob_record with +# DEFAULT() on BLOB +# +CREATE TABLE t1 (id INT, f MEDIUMTEXT NOT NULL DEFAULT 'A'); +INSERT INTO t1 VALUES (1,'foo'),(2,'bar'); +SELECT f FROM t1 GROUP BY id ORDER BY DEFAULT(f); +f +foo +bar +SELECT DEFAULT(f) AS h FROM t1 HAVING h > 5; +h +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'A' +SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 0; +h +A +A +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'A' +SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 'A'; +h +A +A +alter table t1 add column b int default (rand()+1+3); +select default(b) AS h FROM t1 HAVING h > "2"; +h +# +# +drop table t1; # End of 10.3 tests # # MDEV-18681: AND formula in HAVING with several occurances diff --git a/mysql-test/main/having.test b/mysql-test/main/having.test index 072f1a088dc..7e0a0439f8e 100644 --- a/mysql-test/main/having.test +++ b/mysql-test/main/having.test @@ -891,6 +891,27 @@ DROP TABLE t1; DROP FUNCTION next_seq_value; DROP TABLE series; + +--echo # +--echo # MDEV-24958 Server crashes in my_strtod / +--echo # Value_source::Converter_strntod::Converter_strntod with DEFAULT(blob) +--echo # +--echo # MDEV-24942 Server crashes in _ma_rec_pack / _ma_write_blob_record with +--echo # DEFAULT() on BLOB +--echo # + +CREATE TABLE t1 (id INT, f MEDIUMTEXT NOT NULL DEFAULT 'A'); +INSERT INTO t1 VALUES (1,'foo'),(2,'bar'); +SELECT f FROM t1 GROUP BY id ORDER BY DEFAULT(f); +SELECT DEFAULT(f) AS h FROM t1 HAVING h > 5; +SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 0; +SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 'A'; + +alter table t1 add column b int default (rand()+1+3); +--replace_column 1 # +select default(b) AS h FROM t1 HAVING h > "2"; +drop table t1; + --echo # End of 10.3 tests --echo # diff --git a/mysql-test/main/ps.result b/mysql-test/main/ps.result index fd8f37bca0e..4d757986f9c 100644 --- a/mysql-test/main/ps.result +++ b/mysql-test/main/ps.result @@ -5501,6 +5501,23 @@ EXISTS(SELECT 1 FROM t1 GROUP BY a IN (select a from t1)) 0 DROP TABLE t1; # +# MDEV-25006: Failed assertion on executing EXPLAIN DELETE statement as a prepared statement +# +CREATE TABLE t1(c1 CHAR(255) PRIMARY KEY); +PREPARE stmt FROM 'EXPLAIN DELETE b FROM t1 AS a JOIN t1 AS b'; +EXECUTE stmt; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE a system NULL NULL NULL NULL 0 Const row not found +1 SIMPLE b system NULL NULL NULL NULL 0 Const row not found +DROP TABLE t1; +CREATE TABLE t1(a INT); +PREPARE stmt FROM 'EXPLAIN DELETE FROM t1.* USING t1'; +EXECUTE stmt; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 0 Const row not found +DEALLOCATE PREPARE stmt; +DROP TABLE t1; +# # End of 10.2 tests # # diff --git a/mysql-test/main/ps.test b/mysql-test/main/ps.test index 6a109951d0e..2ce78b78e90 100644 --- a/mysql-test/main/ps.test +++ b/mysql-test/main/ps.test @@ -4942,6 +4942,20 @@ EXECUTE stmt; DROP TABLE t1; --echo # +--echo # MDEV-25006: Failed assertion on executing EXPLAIN DELETE statement as a prepared statement +--echo # + +CREATE TABLE t1(c1 CHAR(255) PRIMARY KEY); +PREPARE stmt FROM 'EXPLAIN DELETE b FROM t1 AS a JOIN t1 AS b'; +EXECUTE stmt; +DROP TABLE t1; +CREATE TABLE t1(a INT); +PREPARE stmt FROM 'EXPLAIN DELETE FROM t1.* USING t1'; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +DROP TABLE t1; + +--echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/main/set_statement.result b/mysql-test/main/set_statement.result index 511ecf77357..53574fb4e4f 100644 --- a/mysql-test/main/set_statement.result +++ b/mysql-test/main/set_statement.result @@ -1217,6 +1217,31 @@ set @rnd=1; select @rnd; @rnd 0 +# +# MDEV-24860: Incorrect behaviour of SET STATEMENT in case +# it is executed as a prepared statement +# +PREPARE stmt FROM "SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR CREATE TABLE t1 AS SELECT CONCAT('abc') AS c1"; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +# Show definition of the table t1 created using Prepared Statement +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varchar(3) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +# Create the table t1 with the same definition as it used before +# using regular statement execution mode. +SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR CREATE TABLE t1 AS SELECT CONCAT('abc') AS c1; +# Show that the table has the same definition as it is in case the table +# created in prepared statement mode. +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varchar(3) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; create table t (a int); SET sql_mode=ORACLE; SET STATEMENT myisam_sort_buffer_size=800000 FOR OPTIMIZE TABLE t; @@ -1234,3 +1259,4 @@ SET sql_mode=ORACLE; SET STATEMENT max_statement_time=30 FOR DELETE FROM mysql.user where user = 'unknown'; SET sql_mode=default; SET STATEMENT max_statement_time=30 FOR DELETE FROM mysql.user where user = 'unknown'; +# End of 10.4 tests diff --git a/mysql-test/main/set_statement.test b/mysql-test/main/set_statement.test index 12a6ccad8f9..670e9862abc 100644 --- a/mysql-test/main/set_statement.test +++ b/mysql-test/main/set_statement.test @@ -1137,6 +1137,30 @@ while ($1) --echo # @rnd should be 0 select @rnd; + +--echo # +--echo # MDEV-24860: Incorrect behaviour of SET STATEMENT in case +--echo # it is executed as a prepared statement +--echo # +PREPARE stmt FROM "SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR CREATE TABLE t1 AS SELECT CONCAT('abc') AS c1"; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +--echo # Show definition of the table t1 created using Prepared Statement +SHOW CREATE TABLE t1; + +DROP TABLE t1; + +--echo # Create the table t1 with the same definition as it used before +--echo # using regular statement execution mode. +SET STATEMENT sql_mode = 'NO_ENGINE_SUBSTITUTION' FOR CREATE TABLE t1 AS SELECT CONCAT('abc') AS c1; + +--echo # Show that the table has the same definition as it is in case the table +--echo # created in prepared statement mode. +SHOW CREATE TABLE t1; + +DROP TABLE t1; + create table t (a int); SET sql_mode=ORACLE; SET STATEMENT myisam_sort_buffer_size=800000 FOR OPTIMIZE TABLE t; @@ -1152,3 +1176,5 @@ SET sql_mode=ORACLE; SET STATEMENT max_statement_time=30 FOR DELETE FROM mysql.user where user = 'unknown'; SET sql_mode=default; SET STATEMENT max_statement_time=30 FOR DELETE FROM mysql.user where user = 'unknown'; + +--echo # End of 10.4 tests diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result index 88780179a36..4525a50a2c5 100644 --- a/mysql-test/main/table_value_constr.result +++ b/mysql-test/main/table_value_constr.result @@ -2882,8 +2882,12 @@ deallocate prepare stmt; drop view v1; drop table t1,t2,t3; # -# End of 10.3 tests +# MDEV-24919: subselect formed by TVC and used in set function # +select sum((values(1))); +sum((values(1))) +1 +End of 10.3 tests # # MDEV-22610 Crash in INSERT INTO t1 (VALUES (DEFAULT) UNION VALUES (DEFAULT)) # diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test index dd090f97f38..55cf2fcd766 100644 --- a/mysql-test/main/table_value_constr.test +++ b/mysql-test/main/table_value_constr.test @@ -1517,9 +1517,13 @@ drop view v1; drop table t1,t2,t3; --echo # ---echo # End of 10.3 tests +--echo # MDEV-24919: subselect formed by TVC and used in set function --echo # +select sum((values(1))); + +--echo End of 10.3 tests + --echo # --echo # MDEV-22610 Crash in INSERT INTO t1 (VALUES (DEFAULT) UNION VALUES (DEFAULT)) --echo # diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index e0d2839dafd..d60c94cad40 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4966,7 +4966,7 @@ sub mysqld_start ($$) { $ENV{'MYSQLD_LAST_CMD'}= "$exe @$args"; My::Debugger::setup_args(\$args, \$exe, $mysqld->name()); - $ENV{'VALGRIND_TEST'}= $opt_valgrind = int($exe && $exe eq 'valgrind'); + $ENV{'VALGRIND_TEST'}= $opt_valgrind = int(($exe || '') eq 'valgrind'); # Remove the old pidfile if any unlink($mysqld->value('pid-file')); diff --git a/mysql-test/suite/innodb/r/truncate_foreign.result b/mysql-test/suite/innodb/r/truncate_foreign.result index fc09b74d62f..12a41860708 100644 --- a/mysql-test/suite/innodb/r/truncate_foreign.result +++ b/mysql-test/suite/innodb/r/truncate_foreign.result @@ -57,3 +57,14 @@ disconnect dml; connection default; SET DEBUG_SYNC = RESET; DROP TABLE child, parent; +# +# MDEV-24532 Table corruption ER_NO_SUCH_TABLE_IN_ENGINE or +# ER_CRASHED_ON_USAGE after ALTER on table with foreign key +# +CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FOREIGN KEY (b) REFERENCES t1 (a) ON UPDATE CASCADE; +LOCK TABLE t1 WRITE; +TRUNCATE TABLE t1; +ALTER TABLE t1 ADD c INT; +UNLOCK TABLES; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/truncate_foreign.test b/mysql-test/suite/innodb/t/truncate_foreign.test index d9d647e69f0..1c150e5db40 100644 --- a/mysql-test/suite/innodb/t/truncate_foreign.test +++ b/mysql-test/suite/innodb/t/truncate_foreign.test @@ -67,3 +67,16 @@ connection default; SET DEBUG_SYNC = RESET; DROP TABLE child, parent; + +--echo # +--echo # MDEV-24532 Table corruption ER_NO_SUCH_TABLE_IN_ENGINE or +--echo # ER_CRASHED_ON_USAGE after ALTER on table with foreign key +--echo # + +CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FOREIGN KEY (b) REFERENCES t1 (a) ON UPDATE CASCADE; +LOCK TABLE t1 WRITE; +TRUNCATE TABLE t1; +ALTER TABLE t1 ADD c INT; +UNLOCK TABLES; +DROP TABLE t1; diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 2c06b70c509..ac710a7ab85 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -118,6 +118,7 @@ CREATE USER u1 IDENTIFIED BY 'pwd-123'; GRANT ALL ON sa_db TO u2 IDENTIFIED BY "pwd-321"; SET PASSWORD FOR u1 = PASSWORD('pwd 098'); CREATE USER u3 IDENTIFIED BY ''; +ALTER USER u3 IDENTIFIED BY 'pwd-456'; drop user u1, u2, u3; set global server_audit_events='query_ddl'; create table t1(id int); @@ -393,6 +394,8 @@ TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proxies_priv, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,roles_mapping, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,global_priv, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'CREATE USER u3 IDENTIFIED BY *****',0 +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,global_priv, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'ALTER USER u3 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,db, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,tables_priv, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,columns_priv, diff --git a/mysql-test/suite/plugins/t/server_audit.test b/mysql-test/suite/plugins/t/server_audit.test index 0d65c451d08..8edf898a998 100644 --- a/mysql-test/suite/plugins/t/server_audit.test +++ b/mysql-test/suite/plugins/t/server_audit.test @@ -95,6 +95,7 @@ CREATE USER u1 IDENTIFIED BY 'pwd-123'; GRANT ALL ON sa_db TO u2 IDENTIFIED BY "pwd-321"; SET PASSWORD FOR u1 = PASSWORD('pwd 098'); CREATE USER u3 IDENTIFIED BY ''; +ALTER USER u3 IDENTIFIED BY 'pwd-456'; drop user u1, u2, u3; set global server_audit_events='query_ddl'; diff --git a/mysql-test/suite/unit/suite.pm b/mysql-test/suite/unit/suite.pm index b7a1f9ae871..43c9e115de6 100644 --- a/mysql-test/suite/unit/suite.pm +++ b/mysql-test/suite/unit/suite.pm @@ -20,7 +20,6 @@ sub start_test { ($path, $args) = ($cmd, , [ ]) } - my $oldpwd=getcwd(); chdir $::opt_vardir; my $proc=My::SafeProcess->new @@ -49,12 +48,12 @@ sub start_test { my ($command, %tests, $prefix); for (@ctest_list) { chomp; - if (/^\d+: Test command: +/) { - $command= $'; + if (/^\d+: Test command: +([^ \t]+)/) { + $command= $1; $prefix= /libmariadb/ ? 'conc_' : ''; - } elsif (/^ +Test +#\d+: +/) { - if ($command ne "NOT_AVAILABLE") { - $tests{$prefix.$'}=$command; + } elsif (/^ +Test +#\d+: ([^ \t]+)/) { + if ($command ne "NOT_AVAILABLE" && $command ne "/bin/sh") { + $tests{$prefix.$1}=$command; } } } diff --git a/mysys/file_logger.c b/mysys/file_logger.c index 71394be7afc..476ed44089e 100644 --- a/mysys/file_logger.c +++ b/mysys/file_logger.c @@ -150,23 +150,34 @@ exit: } +/* + Return 1 if we should rotate the log +*/ + +my_bool logger_time_to_rotate(LOGGER_HANDLE *log) +{ + my_off_t filesize; + if (log->rotations > 0 && + (filesize= my_tell(log->file, MYF(0))) != (my_off_t) -1 && + ((ulonglong) filesize >= log->size_limit)) + return 1; + return 0; +} + + int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap) { int result; - my_off_t filesize; char cvtbuf[1024]; size_t n_bytes; flogger_mutex_lock(&log->lock); - if (log->rotations > 0) - if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 || - ((unsigned long long)filesize >= log->size_limit && - do_rotate(log))) - { - result= -1; - errno= my_errno; - goto exit; /* Log rotation needed but failed */ - } + if (logger_time_to_rotate(log) && do_rotate(log)) + { + result= -1; + errno= my_errno; + goto exit; /* Log rotation needed but failed */ + } n_bytes= my_vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap); if (n_bytes >= sizeof(cvtbuf)) @@ -180,21 +191,18 @@ exit: } -int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size) +static int logger_write_r(LOGGER_HANDLE *log, my_bool allow_rotations, + const char *buffer, size_t size) { int result; - my_off_t filesize; flogger_mutex_lock(&log->lock); - if (log->rotations > 0) - if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 || - ((unsigned long long)filesize >= log->size_limit && - do_rotate(log))) - { - result= -1; - errno= my_errno; - goto exit; /* Log rotation needed but failed */ - } + if (allow_rotations && logger_time_to_rotate(log) && do_rotate(log)) + { + result= -1; + errno= my_errno; + goto exit; /* Log rotation needed but failed */ + } result= (int)my_write(log->file, (uchar *) buffer, size, MYF(0)); @@ -204,6 +212,11 @@ exit: } +int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size) +{ + return logger_write_r(log, TRUE, buffer, size); +} + int logger_rotate(LOGGER_HANDLE *log) { int result; diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 0bfe1232a0c..59a99ad5c00 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -16,7 +16,7 @@ #define PLUGIN_VERSION 0x104 -#define PLUGIN_STR_VERSION "1.4.10" +#define PLUGIN_STR_VERSION "1.4.11" #define _my_thread_var loc_thread_var @@ -140,6 +140,7 @@ static int loc_file_errno; #define logger_write loc_logger_write #define logger_rotate loc_logger_rotate #define logger_init_mutexts loc_logger_init_mutexts +#define logger_time_to_rotate loc_logger_time_to_rotate static size_t loc_write(File Filedes, const uchar *Buffer, size_t Count) @@ -554,22 +555,22 @@ static struct st_mysql_show_var audit_status[]= {0,0,0} }; -#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) -/* These belong to the service initialization */ +#ifdef HAVE_PSI_INTERFACE static PSI_mutex_key key_LOCK_operations; -static PSI_mutex_key key_LOCK_atomic; -static PSI_mutex_key key_LOCK_bigbuffer; static PSI_mutex_info mutex_key_list[]= { { &key_LOCK_operations, "SERVER_AUDIT_plugin::lock_operations", - PSI_FLAG_GLOBAL}, + PSI_FLAG_GLOBAL} +#ifndef FLOGGER_NO_PSI + , { &key_LOCK_atomic, "SERVER_AUDIT_plugin::lock_atomic", PSI_FLAG_GLOBAL}, { &key_LOCK_bigbuffer, "SERVER_AUDIT_plugin::lock_bigbuffer", PSI_FLAG_GLOBAL} +#endif /*FLOGGER_NO_PSI*/ }; -#endif -static mysql_mutex_t lock_operations; +#endif /*HAVE_PSI_INTERFACE*/ +static mysql_prlock_t lock_operations; static mysql_mutex_t lock_atomic; static mysql_mutex_t lock_bigbuffer; @@ -819,6 +820,7 @@ enum sa_keywords SQLCOM_DML, SQLCOM_GRANT, SQLCOM_CREATE_USER, + SQLCOM_ALTER_USER, SQLCOM_CHANGE_MASTER, SQLCOM_CREATE_SERVER, SQLCOM_SET_OPTION, @@ -926,6 +928,7 @@ struct sa_keyword passwd_keywords[]= { {3, "SET", &password_word, SQLCOM_SET_OPTION}, {5, "ALTER", &server_word, SQLCOM_ALTER_SERVER}, + {5, "ALTER", &user_word, SQLCOM_ALTER_USER}, {5, "GRANT", 0, SQLCOM_GRANT}, {6, "CREATE", &user_word, SQLCOM_CREATE_USER}, {6, "CREATE", &server_word, SQLCOM_CREATE_SERVER}, @@ -1320,19 +1323,41 @@ static void change_connection(struct connection_info *cn, event->ip, event->ip_length); } +/* + Write to the log + + @param take_lock If set, take a read lock (or write lock on rotate). + If not set, the caller has a already taken a write lock +*/ + static int write_log(const char *message, size_t len, int take_lock) { int result= 0; if (take_lock) - flogger_mutex_lock(&lock_operations); + { + /* Start by taking a read lock */ + mysql_prlock_rdlock(&lock_operations); + } if (output_type == OUTPUT_FILE) { - if (logfile && - (is_active= (logger_write(logfile, message, len) == (int) len))) - goto exit; - ++log_write_failures; - result= 1; + if (logfile) + { + my_bool allow_rotate= !take_lock; /* Allow rotate if caller write lock */ + if (take_lock && logger_time_to_rotate(logfile)) + { + /* We have to rotate the log, change above read lock to write lock */ + mysql_prlock_unlock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); + allow_rotate= 1; + } + if (!(is_active= (logger_write_r(logfile, allow_rotate, message, len) == + (int) len))) + { + ++log_write_failures; + result= 1; + } + } } else if (output_type == OUTPUT_SYSLOG) { @@ -1340,9 +1365,8 @@ static int write_log(const char *message, size_t len, int take_lock) syslog_priority_codes[syslog_priority], "%s %.*s", syslog_info, (int) len, message); } -exit: if (take_lock) - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); return result; } @@ -1590,7 +1614,7 @@ static int do_log_user(const char *name, int len, return 0; if (take_lock) - flogger_mutex_lock(&lock_operations); + mysql_prlock_rdlock(&lock_operations); if (incl_user_coll.n_users) { @@ -1606,7 +1630,7 @@ static int do_log_user(const char *name, int len, result= 1; if (take_lock) - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); return result; } @@ -1824,6 +1848,7 @@ do_log_query: { case SQLCOM_GRANT: case SQLCOM_CREATE_USER: + case SQLCOM_ALTER_USER: csize+= escape_string_hide_passwords(query, query_len, uh_buffer, uh_buffer_size, "IDENTIFIED", 10, "BY", 2, 0); @@ -2497,11 +2522,11 @@ static int server_audit_init(void *p __attribute__((unused))) servhost_len= (uint)strlen(servhost); logger_init_mutexes(); -#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) +#ifdef HAVE_PSI_INTERFACE if (PSI_server) PSI_server->register_mutex("server_audit", mutex_key_list, 1); #endif - flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST); + mysql_prlock_init(key_LOCK_operations, &lock_operations); flogger_mutex_init(key_LOCK_operations, &lock_atomic, MY_MUTEX_INIT_FAST); flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST); @@ -2589,7 +2614,7 @@ static int server_audit_deinit(void *p __attribute__((unused))) closelog(); (void) free(big_buffer); - flogger_mutex_destroy(&lock_operations); + mysql_prlock_destroy(&lock_operations); flogger_mutex_destroy(&lock_atomic); flogger_mutex_destroy(&lock_bigbuffer); @@ -2700,7 +2725,7 @@ static void update_file_path(MYSQL_THD thd, fprintf(stderr, "Log file name was changed to '%s'.\n", new_name); if (!maria_55_started || !debug_server_started) - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); if (logging) log_current_query(thd); @@ -2732,7 +2757,7 @@ static void update_file_path(MYSQL_THD thd, file_path= path_buffer; exit_func: if (!maria_55_started || !debug_server_started) - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); ADD_ATOMIC(internal_stop_logging, -1); } @@ -2748,9 +2773,9 @@ static void update_file_rotations(MYSQL_THD thd __attribute__((unused)), if (!logging || output_type != OUTPUT_FILE) return; - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); logfile->rotations= rotations; - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); } @@ -2766,9 +2791,9 @@ static void update_file_rotate_size(MYSQL_THD thd __attribute__((unused)), if (!logging || output_type != OUTPUT_FILE) return; - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); logfile->size_limit= file_rotate_size; - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); } @@ -2813,7 +2838,7 @@ static void update_incl_users(MYSQL_THD thd, char *new_users= (*(char **) save) ? *(char **) save : empty_str; size_t new_len= strlen(new_users) + 1; if (!maria_55_started || !debug_server_started) - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); mark_always_logged(thd); if (new_len > sizeof(incl_user_buffer)) @@ -2827,7 +2852,7 @@ static void update_incl_users(MYSQL_THD thd, error_header(); fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users); if (!maria_55_started || !debug_server_started) - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); } @@ -2838,7 +2863,7 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), char *new_users= (*(char **) save) ? *(char **) save : empty_str; size_t new_len= strlen(new_users) + 1; if (!maria_55_started || !debug_server_started) - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); mark_always_logged(thd); if (new_len > sizeof(excl_user_buffer)) @@ -2852,7 +2877,7 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), error_header(); fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users); if (!maria_55_started || !debug_server_started) - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); } @@ -2865,7 +2890,7 @@ static void update_output_type(MYSQL_THD thd, return; ADD_ATOMIC(internal_stop_logging, 1); - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); if (logging) { log_current_query(thd); @@ -2879,7 +2904,7 @@ static void update_output_type(MYSQL_THD thd, if (logging) start_logging(); - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); ADD_ATOMIC(internal_stop_logging, -1); } @@ -2909,9 +2934,9 @@ static void update_syslog_priority(MYSQL_THD thd __attribute__((unused)), if (syslog_priority == new_priority) return; - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); mark_always_logged(thd); - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); error_header(); fprintf(stderr, "SysLog priority was changed from '%s' to '%s'.\n", syslog_priority_names[syslog_priority], @@ -2930,7 +2955,7 @@ static void update_logging(MYSQL_THD thd, ADD_ATOMIC(internal_stop_logging, 1); if (!maria_55_started || !debug_server_started) - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); if ((logging= new_logging)) { start_logging(); @@ -2947,7 +2972,7 @@ static void update_logging(MYSQL_THD thd, } if (!maria_55_started || !debug_server_started) - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); ADD_ATOMIC(internal_stop_logging, -1); } @@ -2962,13 +2987,13 @@ static void update_mode(MYSQL_THD thd __attribute__((unused)), ADD_ATOMIC(internal_stop_logging, 1); if (!maria_55_started || !debug_server_started) - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); mark_always_logged(thd); error_header(); fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode); mode= new_mode; if (!maria_55_started || !debug_server_started) - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); ADD_ATOMIC(internal_stop_logging, -1); } @@ -2983,14 +3008,14 @@ static void update_syslog_ident(MYSQL_THD thd __attribute__((unused)), syslog_ident= syslog_ident_buffer; error_header(); fprintf(stderr, "SYSYLOG ident was changed to '%s'\n", syslog_ident); - flogger_mutex_lock(&lock_operations); + mysql_prlock_wrlock(&lock_operations); mark_always_logged(thd); if (logging && output_type == OUTPUT_SYSLOG) { stop_logging(); start_logging(); } - flogger_mutex_unlock(&lock_operations); + mysql_prlock_unlock(&lock_operations); } diff --git a/sql/handler.h b/sql/handler.h index 85da8bc4c22..81a18137955 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2,7 +2,7 @@ #define HANDLER_INCLUDED /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. - Copyright (c) 2009, 2020, MariaDB + Copyright (c) 2009, 2021, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -1725,6 +1725,12 @@ handlerton *ha_default_tmp_handlerton(THD *thd); /* can be replicated by wsrep replication provider plugin */ #define HTON_WSREP_REPLICATION (1 << 13) +/* + Table requires and close and reopen after truncate + If the handler has HTON_CAN_RECREATE, this flag is not used +*/ +#define HTON_REQUIRES_CLOSE_AFTER_TRUNCATE (1 << 18) + class Ha_trx_info; struct THD_TRANS diff --git a/sql/item.cc b/sql/item.cc index a658b105217..3c736316a42 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9297,7 +9297,7 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) def_field->reset_fields(); // If non-constant default value expression or a blob if (def_field->default_value && - (def_field->default_value->flags || def_field->flags & BLOB_FLAG)) + (def_field->default_value->flags || (def_field->flags & BLOB_FLAG))) { uchar *newptr= (uchar*) thd->alloc(1+def_field->pack_length()); if (!newptr) @@ -9400,11 +9400,60 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) return Item_field::save_in_field(field_arg, no_conversions); } +double Item_default_value::val_result() +{ + calculate(); + return Item_field::val_result(); +} + +longlong Item_default_value::val_int_result() +{ + calculate(); + return Item_field::val_int_result(); +} + +String *Item_default_value::str_result(String* tmp) +{ + calculate(); + return Item_field::str_result(tmp); +} + +bool Item_default_value::val_bool_result() +{ + calculate(); + return Item_field::val_bool_result(); +} + +bool Item_default_value::is_null_result() +{ + calculate(); + return Item_field::is_null_result(); +} + +my_decimal *Item_default_value::val_decimal_result(my_decimal *decimal_value) +{ + calculate(); + return Item_field::val_decimal_result(decimal_value); +} + +bool Item_default_value::get_date_result(THD *thd, MYSQL_TIME *ltime, + date_mode_t fuzzydate) +{ + calculate(); + return Item_field::get_date_result(thd, ltime, fuzzydate); +} + +bool Item_default_value::val_native_result(THD *thd, Native *to) +{ + calculate(); + return Item_field::val_native_result(thd, to); +} + table_map Item_default_value::used_tables() const { if (!field || !field->default_value) return static_cast<table_map>(0); - if (!field->default_value->expr) // not fully parsed field + if (!field->default_value->expr) // not fully parsed field return static_cast<table_map>(RAND_TABLE_BIT); return field->default_value->expr->used_tables(); } diff --git a/sql/item.h b/sql/item.h index 3f117eb6d6c..e2ddbc41542 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2,7 +2,7 @@ #define SQL_ITEM_INCLUDED /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2009, 2020, MariaDB Corporation. + Copyright (c) 2009, 2021, 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 @@ -6422,6 +6422,17 @@ public: my_decimal *val_decimal(my_decimal *decimal_value); bool get_date(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate); bool val_native(THD *thd, Native *to); + bool val_native_result(THD *thd, Native *to); + + /* Result variants */ + double val_result(); + longlong val_int_result(); + String *str_result(String* tmp); + my_decimal *val_decimal_result(my_decimal *val); + bool val_bool_result(); + bool is_null_result(); + bool get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); + bool send(Protocol *protocol, st_value *buffer); int save_in_field(Field *field_arg, bool no_conversions); bool save_in_param(THD *thd, Item_param *param) @@ -6450,6 +6461,8 @@ public: } Item *transform(THD *thd, Item_transformer transformer, uchar *args); + Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, + const Tmp_field_param *param); }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9d9831d9209..bcd091d620b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2008, 2020, MariaDB + Copyright (c) 2008, 2021, MariaDB 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 @@ -3306,6 +3306,146 @@ bool Sql_cmd_call::execute(THD *thd) /** + Check whether the SQL statement being processed is prepended by + SET STATEMENT clause and handle variables assignment if it is. + + @param thd thread handle + @param lex current lex + + @return false in case of success, true in case of error. +*/ + +bool run_set_statement_if_requested(THD *thd, LEX *lex) +{ + if (!lex->stmt_var_list.is_empty() && !thd->slave_thread) + { + Query_arena backup; + DBUG_PRINT("info", ("SET STATEMENT %d vars", lex->stmt_var_list.elements)); + + lex->old_var_list.empty(); + List_iterator_fast<set_var_base> it(lex->stmt_var_list); + set_var_base *var; + + if (lex->set_arena_for_set_stmt(&backup)) + return true; + + MEM_ROOT *mem_root= thd->mem_root; + while ((var= it++)) + { + DBUG_ASSERT(var->is_system()); + set_var *o= NULL, *v= (set_var*)var; + if (!v->var->is_set_stmt_ok()) + { + my_error(ER_SET_STATEMENT_NOT_SUPPORTED, MYF(0), v->var->name.str); + lex->reset_arena_for_set_stmt(&backup); + lex->old_var_list.empty(); + lex->free_arena_for_set_stmt(); + return true; + } + if (v->var->session_is_default(thd)) + o= new set_var(thd,v->type, v->var, &v->base, NULL); + else + { + switch (v->var->option.var_type & GET_TYPE_MASK) + { + case GET_BOOL: + case GET_INT: + case GET_LONG: + case GET_LL: + { + bool null_value; + longlong val= v->var->val_int(&null_value, thd, v->type, &v->base); + o= new set_var(thd, v->type, v->var, &v->base, + (null_value ? + (Item *) new (mem_root) Item_null(thd) : + (Item *) new (mem_root) Item_int(thd, val))); + } + break; + case GET_UINT: + case GET_ULONG: + case GET_ULL: + { + bool null_value; + ulonglong val= v->var->val_int(&null_value, thd, v->type, &v->base); + o= new set_var(thd, v->type, v->var, &v->base, + (null_value ? + (Item *) new (mem_root) Item_null(thd) : + (Item *) new (mem_root) Item_uint(thd, val))); + } + break; + case GET_DOUBLE: + { + bool null_value; + double val= v->var->val_real(&null_value, thd, v->type, &v->base); + o= new set_var(thd, v->type, v->var, &v->base, + (null_value ? + (Item *) new (mem_root) Item_null(thd) : + (Item *) new (mem_root) Item_float(thd, val, 1))); + } + break; + default: + case GET_NO_ARG: + case GET_DISABLED: + DBUG_ASSERT(0); + /* fall through */ + case 0: + case GET_FLAGSET: + case GET_ENUM: + case GET_SET: + case GET_STR: + case GET_STR_ALLOC: + { + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), v->var->charset(thd)),*val; + val= v->var->val_str(&tmp, thd, v->type, &v->base); + if (val) + { + Item_string *str= + new (mem_root) Item_string(thd, v->var->charset(thd), + val->ptr(), val->length()); + o= new set_var(thd, v->type, v->var, &v->base, str); + } + else + o= new set_var(thd, v->type, v->var, &v->base, + new (mem_root) Item_null(thd)); + } + break; + } + } + DBUG_ASSERT(o); + lex->old_var_list.push_back(o, thd->mem_root); + } + lex->reset_arena_for_set_stmt(&backup); + + if (lex->old_var_list.is_empty()) + lex->free_arena_for_set_stmt(); + + if (thd->is_error() || + sql_set_variables(thd, &lex->stmt_var_list, false)) + { + if (!thd->is_error()) + my_error(ER_WRONG_ARGUMENTS, MYF(0), "SET"); + lex->restore_set_statement_var(); + return true; + } + /* + The value of last_insert_id is remembered in THD to be written to binlog + when it's used *the first time* in the statement. But SET STATEMENT + must read the old value of last_insert_id to be able to restore it at + the end. This should not count at "reading of last_insert_id" and + should not remember last_insert_id for binlog. That is, it should clear + stmt_depends_on_first_successful_insert_id_in_prev_stmt flag. + */ + if (!thd->in_sub_stmt) + { + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; + } + } + return false; +} + + +/** Execute command saved in thd and lex->sql_command. @param thd Thread handle @@ -3590,127 +3730,13 @@ mysql_execute_command(THD *thd) thd->get_binlog_format(&orig_binlog_format, &orig_current_stmt_binlog_format); - if (!lex->stmt_var_list.is_empty() && !thd->slave_thread) - { - Query_arena backup; - DBUG_PRINT("info", ("SET STATEMENT %d vars", lex->stmt_var_list.elements)); - - lex->old_var_list.empty(); - List_iterator_fast<set_var_base> it(lex->stmt_var_list); - set_var_base *var; - - if (lex->set_arena_for_set_stmt(&backup)) - goto error; - - MEM_ROOT *mem_root= thd->mem_root; - while ((var= it++)) - { - DBUG_ASSERT(var->is_system()); - set_var *o= NULL, *v= (set_var*)var; - if (!v->var->is_set_stmt_ok()) - { - my_error(ER_SET_STATEMENT_NOT_SUPPORTED, MYF(0), v->var->name.str); - lex->reset_arena_for_set_stmt(&backup); - lex->old_var_list.empty(); - lex->free_arena_for_set_stmt(); - goto error; - } - if (v->var->session_is_default(thd)) - o= new set_var(thd,v->type, v->var, &v->base, NULL); - else - { - switch (v->var->option.var_type & GET_TYPE_MASK) - { - case GET_BOOL: - case GET_INT: - case GET_LONG: - case GET_LL: - { - bool null_value; - longlong val= v->var->val_int(&null_value, thd, v->type, &v->base); - o= new set_var(thd, v->type, v->var, &v->base, - (null_value ? - (Item *) new (mem_root) Item_null(thd) : - (Item *) new (mem_root) Item_int(thd, val))); - } - break; - case GET_UINT: - case GET_ULONG: - case GET_ULL: - { - bool null_value; - ulonglong val= v->var->val_int(&null_value, thd, v->type, &v->base); - o= new set_var(thd, v->type, v->var, &v->base, - (null_value ? - (Item *) new (mem_root) Item_null(thd) : - (Item *) new (mem_root) Item_uint(thd, val))); - } - break; - case GET_DOUBLE: - { - bool null_value; - double val= v->var->val_real(&null_value, thd, v->type, &v->base); - o= new set_var(thd, v->type, v->var, &v->base, - (null_value ? - (Item *) new (mem_root) Item_null(thd) : - (Item *) new (mem_root) Item_float(thd, val, 1))); - } - break; - default: - case GET_NO_ARG: - case GET_DISABLED: - DBUG_ASSERT(0); - /* fall through */ - case 0: - case GET_FLAGSET: - case GET_ENUM: - case GET_SET: - case GET_STR: - case GET_STR_ALLOC: - { - char buff[STRING_BUFFER_USUAL_SIZE]; - String tmp(buff, sizeof(buff), v->var->charset(thd)),*val; - val= v->var->val_str(&tmp, thd, v->type, &v->base); - if (val) - { - Item_string *str= new (mem_root) Item_string(thd, v->var->charset(thd), - val->ptr(), val->length()); - o= new set_var(thd, v->type, v->var, &v->base, str); - } - else - o= new set_var(thd, v->type, v->var, &v->base, - new (mem_root) Item_null(thd)); - } - break; - } - } - DBUG_ASSERT(o); - lex->old_var_list.push_back(o, thd->mem_root); - } - lex->reset_arena_for_set_stmt(&backup); - if (lex->old_var_list.is_empty()) - lex->free_arena_for_set_stmt(); - if (thd->is_error() || - (res= sql_set_variables(thd, &lex->stmt_var_list, false))) - { - if (!thd->is_error()) - my_error(ER_WRONG_ARGUMENTS, MYF(0), "SET"); - lex->restore_set_statement_var(); - goto error; - } - /* - The value of last_insert_id is remembered in THD to be written to binlog - when it's used *the first time* in the statement. But SET STATEMENT - must read the old value of last_insert_id to be able to restore it at - the end. This should not count at "reading of last_insert_id" and - should not remember last_insert_id for binlog. That is, it should clear - stmt_depends_on_first_successful_insert_id_in_prev_stmt flag. - */ - if (!thd->in_sub_stmt) - { - thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; - } - } + /* + Assign system variables with values specified by the clause + SET STATEMENT var1=value1 [, var2=value2, ...] FOR <statement> + if they are any. + */ + if (run_set_statement_if_requested(thd, lex)) + goto error; if (thd->lex->mi.connection_name.str == NULL) thd->lex->mi.connection_name= thd->variables.default_master_connection; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 1d25b898ca4..be37e3f6bb3 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -100,6 +100,7 @@ void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); int bootstrap(MYSQL_FILE *file); +bool run_set_statement_if_requested(THD *thd, LEX *lex); int mysql_execute_command(THD *thd); bool do_command(THD *thd); bool dispatch_command(enum enum_server_command command, THD *thd, diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ea6bfe03be5..26cf992920b 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -4241,6 +4241,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) */ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + /* + Set variables specified by + SET STATEMENT var1=value1 [, var2=value2, ...] FOR <statement> + clause for duration of prepare phase. Original values of variable + listed in the SET STATEMENT clause is restored right after return + from the function check_prepared_statement() + */ + if (likely(error == 0)) + error= run_set_statement_if_requested(thd, lex); + /* The only case where we should have items in the thd->free_list is after stmt->set_params_from_vars(), which may in some cases create @@ -4259,6 +4269,12 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_PREPARE; } + /* + Restore original values of variables modified on handling + SET STATEMENT clause. + */ + thd->lex->restore_set_statement_var(); + /* The order is important */ lex->unit.cleanup(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e62da0ef782..8d750281fff 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4653,6 +4653,9 @@ mysql_select(THD *thd, } else { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + /* When in EXPLAIN, delay deleting the joins so that they are still available when we're producing EXPLAIN EXTENDED warning text. @@ -14392,22 +14395,71 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables, DBUG_RETURN(0); } -/* - used only in JOIN::clear +/** + used only in JOIN::clear (always) and in do_select() + (if there where no matching rows) + + @param join JOIN + @param cleared_tables If not null, clear also const tables and mark all + cleared tables in the map. cleared_tables is only + set when called from do_select() when there is a + group function and there where no matching rows. */ -static void clear_tables(JOIN *join) + +static void clear_tables(JOIN *join, table_map *cleared_tables) { /* - must clear only the non-const tables, as const tables - are not re-calculated. + must clear only the non-const tables as const tables are not re-calculated. */ for (uint i= 0 ; i < join->table_count ; i++) { - if (!(join->table[i]->map & join->const_table_map)) - mark_as_null_row(join->table[i]); // All fields are NULL + TABLE *table= join->table[i]; + + if (table->null_row) + continue; // Nothing more to do + if (!(table->map & join->const_table_map) || cleared_tables) + { + if (cleared_tables) + { + (*cleared_tables)|= (((table_map) 1) << i); + if (table->s->null_bytes) + { + /* + Remember null bits for the record so that we can restore the + original const record in unclear_tables() + */ + memcpy(table->record[1], table->null_flags, table->s->null_bytes); + } + } + mark_as_null_row(table); // All fields are NULL + } + } +} + + +/** + Reverse null marking for tables and restore null bits. + + We have to do this because the tables may be re-used in a sub query + and the subquery will assume that the const tables contains the original + data before clear_tables(). +*/ + +static void unclear_tables(JOIN *join, table_map *cleared_tables) +{ + for (uint i= 0 ; i < join->table_count ; i++) + { + if ((*cleared_tables) & (((table_map) 1) << i)) + { + TABLE *table= join->table[i]; + if (table->s->null_bytes) + memcpy(table->null_flags, table->record[1], table->s->null_bytes); + unmark_as_null_row(table); + } } } + /***************************************************************************** Make som simple condition optimization: If there is a test 'field = const' change all refs to 'field' to 'const' @@ -17904,17 +17956,34 @@ Field *Item_field::create_tmp_field_ex(TABLE *table, src->set_field(field); if (!(result= create_tmp_field_from_item_field(table, NULL, param))) return NULL; - /* - Fields that are used as arguments to the DEFAULT() function already have - their data pointers set to the default value during name resolution. See - Item_default_value::fix_fields. - */ - if (type() != Item::DEFAULT_VALUE_ITEM && field->eq_def(result)) + if (field->eq_def(result)) src->set_default_field(field); return result; } +Field *Item_default_value::create_tmp_field_ex(TABLE *table, + Tmp_field_src *src, + const Tmp_field_param *param) +{ + if (field->default_value && (field->flags & BLOB_FLAG)) + { + /* + We have to use a copy function when using a blob with default value + as the we have to calculate the default value before we can use it. + */ + get_tmp_field_src(src, param); + return tmp_table_field_from_field_type(table); + } + /* + Same code as in Item_field::create_tmp_field_ex, except no default field + handling + */ + src->set_field(field); + return create_tmp_field_from_item_field(table, NULL, param); +} + + Field *Item_ref::create_tmp_field_ex(TABLE *table, Tmp_field_src *src, const Tmp_field_param *param) @@ -18022,7 +18091,13 @@ Field *Item_func_sp::create_tmp_field_ex(TABLE *table, the record in the original table. If modify_item is 0 then fill_record() will update the temporary table - + @param table_cant_handle_bit_fields + Set to 1 if the temporary table cannot handle bit + fields. Only set for heap tables when the bit field + is part of an index. + @param make_copy_field + Set when using with rollup when we want to have + an exact copy of the field. @retval 0 on error @retval @@ -19886,6 +19961,7 @@ do_select(JOIN *join, Procedure *procedure) if (join->only_const_tables() && !join->need_tmp) { Next_select_func end_select= setup_end_select_func(join, NULL); + /* HAVING will be checked after processing aggregate functions, But WHERE should checked here (we alredy have read tables). @@ -19912,12 +19988,29 @@ do_select(JOIN *join, Procedure *procedure) } else if (join->send_row_on_empty_set()) { + table_map cleared_tables= (table_map) 0; + if (end_select == end_send_group) + { + /* + Was a grouping query but we did not find any rows. In this case + we clear all tables to get null in any referenced fields, + like in case of: + SELECT MAX(a) AS f1, a AS f2 FROM t1 WHERE VALUE(a) IS NOT NULL + */ + clear_tables(join, &cleared_tables); + } if (!join->having || join->having->val_int()) { List<Item> *columns_list= (procedure ? &join->procedure_fields_list : join->fields); rc= join->result->send_data(*columns_list) > 0; } + /* + We have to remove the null markings from the tables as this table + may be part of a sub query that is re-evaluated + */ + if (cleared_tables) + unclear_tables(join, &cleared_tables); } /* An error can happen when evaluating the conds @@ -20891,8 +20984,8 @@ join_read_const_table(THD *thd, JOIN_TAB *tab, POSITION *pos) if ((table->null_row= MY_TEST((*tab->on_expr_ref)->val_int() == 0))) mark_as_null_row(table); } - if (!table->null_row) - table->maybe_null=0; + if (!table->null_row && ! tab->join->mixed_implicit_grouping) + table->maybe_null= 0; { JOIN *join= tab->join; @@ -26052,7 +26145,7 @@ int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg, TABL void JOIN::clear() { - clear_tables(this); + clear_tables(this, 0); copy_fields(&tmp_table_param); if (sum_funcs) diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index e7ecdda958e..c495a417961 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -465,6 +465,15 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) */ error= handler_truncate(thd, table_ref, FALSE); + if (error == TRUNCATE_OK && thd->locked_tables_mode && + (table_ref->table->file->ht->flags & + HTON_REQUIRES_CLOSE_AFTER_TRUNCATE)) + { + thd->locked_tables_list.mark_table_for_reopen(thd, table_ref->table); + if (unlikely(thd->locked_tables_list.reopen_tables(thd, true))) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + } + /* All effects of a TRUNCATE TABLE operation are committed even if truncation fails in the case of non transactional tables. Thus, the diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index 7f26256df09..0957de2fa5f 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -647,7 +647,7 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl, st_select_lex *parent_select) { LEX *lex= thd->lex; - select_result *save_result= thd->lex->result; + select_result *save_result= lex->result; uint8 save_derived_tables= lex->derived_tables; thd->lex->result= NULL; @@ -728,13 +728,13 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl, if (arena) thd->restore_active_arena(arena, &backup); - thd->lex->result= save_result; + lex->result= save_result; return wrapper_sl; err: if (arena) thd->restore_active_arena(arena, &backup); - thd->lex->result= save_result; + lex->result= save_result; lex->derived_tables= save_derived_tables; return 0; } @@ -818,14 +818,9 @@ Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) { if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE) ((subselect_single_select_engine *) engine)->change_select(wrapper_sl); - lex->current_select= wrapper_sl; - return wrapper_sl; - } - else - { - lex->current_select= parent_select; - return 0; } + lex->current_select= parent_select; + return wrapper_sl; } diff --git a/sql/table.h b/sql/table.h index 0237a3fe6cf..4a739ed1f9f 100644 --- a/sql/table.h +++ b/sql/table.h @@ -3245,6 +3245,12 @@ inline void mark_as_null_row(TABLE *table) bfill(table->null_flags,table->s->null_bytes,255); } +inline void unmark_as_null_row(TABLE *table) +{ + table->null_row=0; + table->status= STATUS_NO_RECORD; +} + bool is_simple_order(ORDER *order); class Open_tables_backup; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 5746ac703f6..76723d62761 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4119,8 +4119,10 @@ static int innodb_init(void* p) innobase_hton->flush_logs = innobase_flush_logs; innobase_hton->show_status = innobase_show_status; innobase_hton->flags = - HTON_SUPPORTS_EXTENDED_KEYS | HTON_SUPPORTS_FOREIGN_KEYS - | HTON_NATIVE_SYS_VERSIONING | HTON_WSREP_REPLICATION; + HTON_SUPPORTS_EXTENDED_KEYS | HTON_SUPPORTS_FOREIGN_KEYS | + HTON_NATIVE_SYS_VERSIONING | + HTON_WSREP_REPLICATION | + HTON_REQUIRES_CLOSE_AFTER_TRUNCATE; #ifdef WITH_WSREP innobase_hton->abort_transaction=wsrep_abort_transaction; diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index cb69e99a111..e1d9dea0dc5 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -4266,6 +4266,18 @@ lock_check_dict_lock( and release possible other transactions waiting because of these locks. */ void lock_release(trx_t* trx) { +#ifdef UNIV_DEBUG + std::set<table_id_t> to_evict; + if (innodb_evict_tables_on_commit_debug && !trx->is_recovered) +# if 1 /* if dict_stats_exec_sql() were not playing dirty tricks */ + if (!mutex_own(&dict_sys.mutex)) +# else /* this would be more proper way to do it */ + if (!trx->dict_operation_lock_mode && !trx->dict_operation) +# endif + for (const auto& p : trx->mod_tables) + if (!p.first->is_temporary()) + to_evict.emplace(p.first->id); +#endif ulint count = 0; trx_id_t max_trx_id = trx_sys.get_max_trx_id(); @@ -4314,6 +4326,25 @@ void lock_release(trx_t* trx) } lock_mutex_exit(); + +#ifdef UNIV_DEBUG + if (to_evict.empty()) { + return; + } + mutex_enter(&dict_sys.mutex); + lock_mutex_enter(); + for (table_id_t id : to_evict) { + if (dict_table_t *table = dict_table_open_on_id( + id, TRUE, DICT_TABLE_OP_OPEN_ONLY_IF_CACHED)) { + if (!table->get_ref_count() + && !UT_LIST_GET_LEN(table->locks)) { + dict_sys.remove(table, true); + } + } + } + lock_mutex_exit(); + mutex_exit(&dict_sys.mutex); +#endif } /* True if a lock mode is S or X */ diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 14bd684e2ce..1bcc92f8b97 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1272,16 +1272,6 @@ trx_update_mod_tables_timestamp( const time_t now = time(NULL); trx_mod_tables_t::const_iterator end = trx->mod_tables.end(); -#ifdef UNIV_DEBUG - const bool preserve_tables = !innodb_evict_tables_on_commit_debug - || trx->is_recovered /* avoid trouble with XA recovery */ -# if 1 /* if dict_stats_exec_sql() were not playing dirty tricks */ - || mutex_own(&dict_sys.mutex) -# else /* this would be more proper way to do it */ - || trx->dict_operation_lock_mode || trx->dict_operation -# endif - ; -#endif for (trx_mod_tables_t::const_iterator it = trx->mod_tables.begin(); it != end; @@ -1297,26 +1287,6 @@ trx_update_mod_tables_timestamp( intrusive. */ dict_table_t* table = it->first; table->update_time = now; -#ifdef UNIV_DEBUG - if (preserve_tables || table->get_ref_count() - || UT_LIST_GET_LEN(table->locks)) { - /* do not evict when committing DDL operations - or if some other transaction is holding the - table handle */ - continue; - } - /* recheck while holding the mutex that blocks - table->acquire() */ - mutex_enter(&dict_sys.mutex); - mutex_enter(&lock_sys.mutex); - const bool do_evict = !table->get_ref_count() - && !UT_LIST_GET_LEN(table->locks); - mutex_exit(&lock_sys.mutex); - if (do_evict) { - dict_sys.remove(table, true); - } - mutex_exit(&dict_sys.mutex); -#endif } trx->mod_tables.clear(); @@ -1402,16 +1372,9 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr) so that there will be no race condition in lock_release(). */ while (UNIV_UNLIKELY(is_referenced())) ut_delay(srv_spin_wait_delay); - release_locks(); - id= 0; } else - { ut_ad(read_only || !rsegs.m_redo.rseg); - release_locks(); - } - - DEBUG_SYNC_C("after_trx_committed_in_memory"); if (read_only || !rsegs.m_redo.rseg) { @@ -1424,6 +1387,10 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr) is_recovered= false; } + release_locks(); + id= 0; + DEBUG_SYNC_C("after_trx_committed_in_memory"); + while (dict_table_t *table= UT_LIST_GET_FIRST(lock.evicted_tables)) { UT_LIST_REMOVE(lock.evicted_tables, table); diff --git a/storage/maria/ma_recovery.c b/storage/maria/ma_recovery.c index 8f108e3f03f..be8a9fe8b2a 100644 --- a/storage/maria/ma_recovery.c +++ b/storage/maria/ma_recovery.c @@ -1453,6 +1453,7 @@ static int new_table(uint16 sid, const char *name, LSN lsn_of_file_id) } if (maria_is_crashed(info)) { + tprint(tracef, "\n"); eprint(tracef, "Table '%s' is crashed, skipping it. Please repair it with" " aria_chk -r", share->open_file_name.str); recovery_found_crashed_tables++; diff --git a/storage/maria/ma_recovery_util.c b/storage/maria/ma_recovery_util.c index 3bbda614991..fe43d812600 100644 --- a/storage/maria/ma_recovery_util.c +++ b/storage/maria/ma_recovery_util.c @@ -98,6 +98,7 @@ void eprint(FILE *trace_file __attribute__ ((unused)), fputc('\n', trace_file); if (trace_file != stderr) { + va_start(args, format); my_printv_error(HA_ERR_INITIALIZATION, format, MYF(0), args); } va_end(args); |