summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2019-08-16 10:51:20 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2019-08-16 10:51:20 +0300
commitc221bcdce7714a74b89a02de941e8d8df2994ce3 (patch)
tree87a4e2d595ea3cc1465f212f26a9b9030655aa0c
parent3bbf008096fc997556459442bc2503c504248345 (diff)
parent395e1dcd176bbf0fcdb5d156bc58604590245a26 (diff)
downloadmariadb-git-c221bcdce7714a74b89a02de941e8d8df2994ce3.tar.gz
Merge 10.3 into 10.4
-rw-r--r--extra/innochecksum.cc11
-rw-r--r--mysql-test/main/win.result35
-rw-r--r--mysql-test/main/win.test23
-rw-r--r--mysql-test/suite/galera/disabled.def1
-rw-r--r--mysql-test/suite/innodb/r/innodb_bug14704286.result65
-rw-r--r--mysql-test/suite/innodb/r/innodb_bug84958.result81
-rw-r--r--mysql-test/suite/innodb/r/undo_truncate_recover.result2
-rw-r--r--mysql-test/suite/innodb/t/innodb_bug14704286.test95
-rw-r--r--mysql-test/suite/innodb/t/innodb_bug84958.test86
-rw-r--r--mysql-test/suite/innodb/t/undo_truncate_recover.opt1
-rw-r--r--mysql-test/suite/innodb/t/undo_truncate_recover.test3
-rw-r--r--mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result32
-rw-r--r--mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test64
-rw-r--r--mysql-test/valgrind.supp120
-rw-r--r--sql/item.h3
-rw-r--r--sql/item_windowfunc.h2
-rw-r--r--sql/sql_list.h10
-rw-r--r--sql/sql_table.cc60
-rw-r--r--storage/innobase/row/row0ins.cc12
-rw-r--r--storage/innobase/row/row0log.cc8
-rw-r--r--storage/innobase/row/row0mysql.cc14
-rw-r--r--storage/innobase/row/row0sel.cc74
-rw-r--r--storage/rocksdb/ha_rocksdb.cc4
-rw-r--r--storage/rocksdb/mysql-test/rocksdb/suite.pm4
24 files changed, 563 insertions, 247 deletions
diff --git a/extra/innochecksum.cc b/extra/innochecksum.cc
index f7fbb76b66b..aa591de944e 100644
--- a/extra/innochecksum.cc
+++ b/extra/innochecksum.cc
@@ -1557,10 +1557,8 @@ int main(
byte* xdes = NULL;
/* bytes read count */
ulint bytes;
- /* current time */
- time_t now;
/* last time */
- time_t lastt;
+ time_t lastt = 0;
/* stat, to get file size. */
#ifdef _WIN32
struct _stat64 st;
@@ -1903,7 +1901,6 @@ int main(
/* main checksumming loop */
cur_page_num = start_page ? start_page : cur_page_num + 1;
- lastt = 0;
while (!feof(fil_in)) {
bytes = read_file(buf, partial_page_read,
@@ -1982,12 +1979,10 @@ first_non_zero:
if (verbose && !read_from_stdin) {
if ((cur_page_num % 64) == 0) {
- now = time(0);
+ time_t now = time(0);
if (!lastt) {
lastt= now;
- }
- if (now - lastt >= 1
- && is_log_enabled) {
+ } else if (now - lastt >= 1 && is_log_enabled) {
fprintf(log_file, "page::%llu "
"okay: %.3f%% done\n",
(cur_page_num - 1),
diff --git a/mysql-test/main/win.result b/mysql-test/main/win.result
index 51a0f35ad61..4adb5c84f1e 100644
--- a/mysql-test/main/win.result
+++ b/mysql-test/main/win.result
@@ -3654,6 +3654,41 @@ d x
00:00:02 NULL
DROP TABLE t1;
#
+# MDEV-20351 Window function BIT_OR() OVER (..) return a wrong data type
+#
+CREATE TABLE t1 (pk INT, a INT, b BIGINT UNSIGNED);
+INSERT INTO t1 VALUES (1, 0, 1), (2, 0, 18446744073709551615);
+CREATE TABLE t2 AS
+SELECT pk, a, bit_or(b) AS bit_or FROM t1 GROUP BY pk;
+SHOW CREATE TABLE t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `pk` int(11) DEFAULT NULL,
+ `a` int(11) DEFAULT NULL,
+ `bit_or` bigint(21) unsigned NOT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM t1;
+pk a b
+1 0 1
+2 0 18446744073709551615
+DROP TABLE t2;
+CREATE OR REPLACE TABLE t2 AS
+SELECT pk, a, BIT_OR(b) OVER (PARTITION BY a ORDER BY pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS bit_or
+FROM t1;
+SHOW CREATE TABLE t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `pk` int(11) DEFAULT NULL,
+ `a` int(11) DEFAULT NULL,
+ `bit_or` bigint(21) unsigned NOT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM t2;
+pk a bit_or
+1 0 18446744073709551615
+2 0 18446744073709551615
+DROP TABLE t2;
+DROP TABLE t1;
+#
# End of 10.3 tests
#
#
diff --git a/mysql-test/main/win.test b/mysql-test/main/win.test
index 01b8f17b0f2..3d5fb2240de 100644
--- a/mysql-test/main/win.test
+++ b/mysql-test/main/win.test
@@ -2359,6 +2359,29 @@ INSERT INTO t1 VALUES ('00:00:01'),('00:00:02');
SELECT *, LEAD(d) OVER (ORDER BY d) AS x FROM t1;
DROP TABLE t1;
+
+--echo #
+--echo # MDEV-20351 Window function BIT_OR() OVER (..) return a wrong data type
+--echo #
+CREATE TABLE t1 (pk INT, a INT, b BIGINT UNSIGNED);
+INSERT INTO t1 VALUES (1, 0, 1), (2, 0, 18446744073709551615);
+
+CREATE TABLE t2 AS
+SELECT pk, a, bit_or(b) AS bit_or FROM t1 GROUP BY pk;
+SHOW CREATE TABLE t2;
+SELECT * FROM t1;
+DROP TABLE t2;
+
+CREATE OR REPLACE TABLE t2 AS
+SELECT pk, a, BIT_OR(b) OVER (PARTITION BY a ORDER BY pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS bit_or
+ FROM t1;
+SHOW CREATE TABLE t2;
+SELECT * FROM t2;
+DROP TABLE t2;
+
+DROP TABLE t1;
+
+
--echo #
--echo # End of 10.3 tests
--echo #
diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def
index 92a806710da..6306686e53d 100644
--- a/mysql-test/suite/galera/disabled.def
+++ b/mysql-test/suite/galera/disabled.def
@@ -47,3 +47,4 @@ galera_sst_mariabackup_encrypt_with_key : MDEV-19926 Galera SST tests fail
galera_wan : MDEV-17259: Test failure on galera.galera_wan
mysql-wsrep#198 : MDEV-18935 Galera test mysql-wsrep#198 sporaric assertion transaction.cpp:362: int wsrep::transaction::before_commit(): Assertion `state() == s_executing || state() == s_committing || state() == s_must_abort || state() == s_replaying' failed.
partition : MDEV-19958 Galera test failure on galera.partition
+query_cache: MDEV-15805 Test failure on galera.query_cache \ No newline at end of file
diff --git a/mysql-test/suite/innodb/r/innodb_bug14704286.result b/mysql-test/suite/innodb/r/innodb_bug14704286.result
deleted file mode 100644
index f84d5206e07..00000000000
--- a/mysql-test/suite/innodb/r/innodb_bug14704286.result
+++ /dev/null
@@ -1,65 +0,0 @@
-use test;
-drop table if exists t1;
-create table t1 (id int primary key, value int, value2 int,
-value3 int, index(value,value2)) engine=innodb;
-insert into t1 values
-(10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14),
-(15,15,15,15),(16,16,16,16),(17,17,17,17),(18,18,18,18),(19,19,19,19),
-(20,20,20,20);
-connect conn1, localhost, root,,;
-connect conn2, localhost, root,,;
-connect conn3, localhost, root,,;
-connection conn1;
-use test;
-start transaction with consistent snapshot;
-connection conn2;
-use test;
-CREATE PROCEDURE update_t1()
-BEGIN
-DECLARE i INT DEFAULT 1;
-while (i <= 5000) DO
-update test.t1 set value2=value2+1, value3=value3+1 where id=12;
-SET i = i + 1;
-END WHILE;
-END|
-set autocommit=0;
-CALL update_t1();
-select * from t1;
-id value value2 value3
-10 10 10 10
-11 11 11 11
-12 12 5012 5012
-13 13 13 13
-14 14 14 14
-15 15 15 15
-16 16 16 16
-17 17 17 17
-18 18 18 18
-19 19 19 19
-20 20 20 20
-set autocommit=1;
-select * from t1;
-id value value2 value3
-10 10 10 10
-11 11 11 11
-12 12 5012 5012
-13 13 13 13
-14 14 14 14
-15 15 15 15
-16 16 16 16
-17 17 17 17
-18 18 18 18
-19 19 19 19
-20 20 20 20
-connection conn1;
-select * from t1 force index(value) where value=12;
-connection conn3;
-kill query @id;
-connection conn1;
-ERROR 70100: Query execution was interrupted
-connection default;
-disconnect conn1;
-disconnect conn2;
-disconnect conn3;
-drop procedure if exists update_t1;
-drop table if exists t1;
diff --git a/mysql-test/suite/innodb/r/innodb_bug84958.result b/mysql-test/suite/innodb/r/innodb_bug84958.result
new file mode 100644
index 00000000000..1a59a10eb2f
--- /dev/null
+++ b/mysql-test/suite/innodb/r/innodb_bug84958.result
@@ -0,0 +1,81 @@
+#
+# Bug #84958 InnoDB's MVCC has O(N^2) behaviors
+# https://bugs.mysql.com/bug.php?id=84958
+#
+# Set up the test with a procedure and a function.
+#
+CREATE PROCEDURE insert_n(start int, end int)
+BEGIN
+DECLARE i INT DEFAULT start;
+WHILE i <= end do
+INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = i;
+SET i = i + 1;
+END WHILE;
+END~~
+CREATE FUNCTION num_pages_get()
+RETURNS INT
+BEGIN
+DECLARE ret INT;
+SELECT variable_value INTO ret
+FROM information_schema.global_status
+WHERE variable_name = 'innodb_buffer_pool_read_requests';
+RETURN ret;
+END~~
+#
+# Create a table with one record in it and start an RR transaction
+#
+CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,b), KEY (b,c))
+ENGINE=InnoDB;
+BEGIN;
+SELECT * FROM t1;
+a b c
+#
+# Create 100 newer record versions in con2 and con3
+#
+connect con2, localhost, root,,;
+connection con2;
+INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = NULL;
+CALL insert_n(1, 50);;
+connect con3, localhost, root,,;
+connection con3;
+CALL insert_n(51, 100);;
+connection con2;
+connection con3;
+INSERT INTO t1 VALUES (1, 2, 1) ON DUPLICATE KEY UPDATE c = NULL;
+connection default;
+#
+# Connect to default and record how many pages were accessed
+# when selecting the record using the secondary key.
+#
+SET @num_pages_1 = num_pages_get();
+SELECT * FROM t1 force index (b);
+a b c
+SET @num_pages_2= num_pages_get();
+SELECT @num_pages_2 - @num_pages_1 < 500;
+@num_pages_2 - @num_pages_1 < 500
+1
+#
+# Commit and show the final record.
+#
+SELECT * FROM t1;
+a b c
+SELECT * FROM t1 force index (b);
+a b c
+COMMIT;
+SELECT * FROM t1 force index (b);
+a b c
+1 2 NULL
+SELECT * FROM t1;
+a b c
+1 2 NULL
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check status OK
+#
+# Cleanup
+#
+disconnect con2;
+disconnect con3;
+DROP TABLE t1;
+DROP PROCEDURE insert_n;
+DROP FUNCTION num_pages_get;
diff --git a/mysql-test/suite/innodb/r/undo_truncate_recover.result b/mysql-test/suite/innodb/r/undo_truncate_recover.result
index 212a1658e18..428a2dec0bb 100644
--- a/mysql-test/suite/innodb/r/undo_truncate_recover.result
+++ b/mysql-test/suite/innodb/r/undo_truncate_recover.result
@@ -9,8 +9,8 @@ update t1 set c = 'MariaDB';
update t1 set c = 'InnoDB';
set global debug_dbug = '+d,ib_undo_trunc';
commit;
+drop table t1;
call mtr.add_suppression("InnoDB: innodb_undo_tablespaces=0 disables dedicated undo log tablespaces");
SET GLOBAL innodb_fast_shutdown=0;
FOUND 1 /ib_undo_trunc/ in mysqld.1.err
# restart: with restart_parameters
-drop table t1;
diff --git a/mysql-test/suite/innodb/t/innodb_bug14704286.test b/mysql-test/suite/innodb/t/innodb_bug14704286.test
deleted file mode 100644
index fb5e6b829a1..00000000000
--- a/mysql-test/suite/innodb/t/innodb_bug14704286.test
+++ /dev/null
@@ -1,95 +0,0 @@
---source include/have_innodb.inc
-
-#
-# create test-bed to run test
-#
-use test;
---disable_warnings
-drop table if exists t1;
---enable_warnings
-create table t1 (id int primary key, value int, value2 int,
-value3 int, index(value,value2)) engine=innodb;
-
-insert into t1 values
-(10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14),
-(15,15,15,15),(16,16,16,16),(17,17,17,17),(18,18,18,18),(19,19,19,19),
-(20,20,20,20);
-let $ID= `SELECT @id := CONNECTION_ID()`;
-
-#
-# we need multiple connections as we need to keep one connection
-# active with trx requesting consistent read.
-#
-connect (conn1, localhost, root,,);
-connect (conn2, localhost, root,,);
-connect (conn3, localhost, root,,);
-
-#
-# start trx with consistent read
-#
-connection conn1;
-use test;
-
-start transaction with consistent snapshot;
-
-#
-# update table such that secondary index is updated.
-#
-connection conn2;
-use test;
-delimiter |;
-CREATE PROCEDURE update_t1()
-BEGIN
- DECLARE i INT DEFAULT 1;
- while (i <= 5000) DO
- update test.t1 set value2=value2+1, value3=value3+1 where id=12;
- SET i = i + 1;
- END WHILE;
-END|
-
-delimiter ;|
-set autocommit=0;
-CALL update_t1();
-select * from t1;
-set autocommit=1;
-select * from t1;
-
-#
-# Now try to fire select query from connection-1 enforcing
-# use of secondary index.
-#
-connection conn1;
-let $ID= `SELECT @id := CONNECTION_ID()`;
-#--error ER_QUERY_INTERRUPTED
---send
-select * from t1 force index(value) where value=12;
-
-#
-# select is going to take good time so let's kill query.
-#
-connection conn3;
-let $wait_condition=
- select * from information_schema.processlist where state = 'Sending data' and
- info = 'select * from t1 force index(value) where value=12';
---source include/wait_condition.inc
-let $ignore= `SELECT @id := $ID`;
-kill query @id;
-
-#
-# reap the value of connection-1
-#
-connection conn1;
---error ER_QUERY_INTERRUPTED
-reap;
-
-#
-# clean test-bed.
-#
-connection default;
-disconnect conn1;
-disconnect conn2;
-disconnect conn3;
-drop procedure if exists update_t1;
-drop table if exists t1;
-
-
diff --git a/mysql-test/suite/innodb/t/innodb_bug84958.test b/mysql-test/suite/innodb/t/innodb_bug84958.test
new file mode 100644
index 00000000000..4456df21cb9
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_bug84958.test
@@ -0,0 +1,86 @@
+--echo #
+--echo # Bug #84958 InnoDB's MVCC has O(N^2) behaviors
+--echo # https://bugs.mysql.com/bug.php?id=84958
+--echo #
+--echo # Set up the test with a procedure and a function.
+--echo #
+
+--source include/have_innodb.inc
+
+DELIMITER ~~;
+CREATE PROCEDURE insert_n(start int, end int)
+BEGIN
+ DECLARE i INT DEFAULT start;
+ WHILE i <= end do
+ INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = i;
+ SET i = i + 1;
+ END WHILE;
+END~~
+
+CREATE FUNCTION num_pages_get()
+RETURNS INT
+BEGIN
+ DECLARE ret INT;
+ SELECT variable_value INTO ret
+ FROM information_schema.global_status
+ WHERE variable_name = 'innodb_buffer_pool_read_requests';
+ RETURN ret;
+END~~
+DELIMITER ;~~
+
+--echo #
+--echo # Create a table with one record in it and start an RR transaction
+--echo #
+CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,b), KEY (b,c))
+ENGINE=InnoDB;
+BEGIN;
+SELECT * FROM t1;
+
+--echo #
+--echo # Create 100 newer record versions in con2 and con3
+--echo #
+connect (con2, localhost, root,,);
+connection con2;
+INSERT INTO t1 VALUES (1, 2, 3) ON DUPLICATE KEY UPDATE c = NULL;
+--send CALL insert_n(1, 50);
+
+connect (con3, localhost, root,,);
+connection con3;
+--send CALL insert_n(51, 100);
+
+connection con2;
+reap;
+connection con3;
+reap;
+INSERT INTO t1 VALUES (1, 2, 1) ON DUPLICATE KEY UPDATE c = NULL;
+
+connection default;
+
+--echo #
+--echo # Connect to default and record how many pages were accessed
+--echo # when selecting the record using the secondary key.
+--echo #
+SET @num_pages_1 = num_pages_get();
+SELECT * FROM t1 force index (b);
+SET @num_pages_2= num_pages_get();
+
+SELECT @num_pages_2 - @num_pages_1 < 500;
+
+--echo #
+--echo # Commit and show the final record.
+--echo #
+SELECT * FROM t1;
+SELECT * FROM t1 force index (b);
+COMMIT;
+SELECT * FROM t1 force index (b);
+SELECT * FROM t1;
+CHECK TABLE t1;
+
+--echo #
+--echo # Cleanup
+--echo #
+disconnect con2;
+disconnect con3;
+DROP TABLE t1;
+DROP PROCEDURE insert_n;
+DROP FUNCTION num_pages_get;
diff --git a/mysql-test/suite/innodb/t/undo_truncate_recover.opt b/mysql-test/suite/innodb/t/undo_truncate_recover.opt
new file mode 100644
index 00000000000..a1207721427
--- /dev/null
+++ b/mysql-test/suite/innodb/t/undo_truncate_recover.opt
@@ -0,0 +1 @@
+--innodb-purge-threads=1
diff --git a/mysql-test/suite/innodb/t/undo_truncate_recover.test b/mysql-test/suite/innodb/t/undo_truncate_recover.test
index a143c25eaf0..64b1f6f23a1 100644
--- a/mysql-test/suite/innodb/t/undo_truncate_recover.test
+++ b/mysql-test/suite/innodb/t/undo_truncate_recover.test
@@ -35,6 +35,7 @@ update t1 set c = 'MariaDB';
update t1 set c = 'InnoDB';
eval set global debug_dbug = '+d,$SEARCH_PATTERN';
commit;
+drop table t1;
call mtr.add_suppression("InnoDB: innodb_undo_tablespaces=0 disables dedicated undo log tablespaces");
SET GLOBAL innodb_fast_shutdown=0;
--source include/shutdown_mysqld.inc
@@ -51,5 +52,3 @@ if ($checksum_algorithm == "strict_crc32")
let $restart_parameters= $restart_parameters --innodb_checksum_algorithm=strict_full_crc32;
}
--source include/start_mysqld.inc
-
-drop table t1;
diff --git a/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result
new file mode 100644
index 00000000000..df36fa82e0f
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result
@@ -0,0 +1,32 @@
+include/master-slave.inc
+[connection master]
+create table t1 (a int) engine=innodb;
+create table t2 (b longblob) engine=innodb;
+create table t3 (c int) engine=innodb;
+insert into t2 values (repeat('b',1024*1024));
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2';
+drop table t1, t2, t3;
+connect foo,localhost,root;
+set debug_sync='now SIGNAL go';
+kill query CONNECTION_ID;
+connection master;
+ERROR 70100: Query execution was interrupted
+"Tables t2 and t3 should be listed"
+SHOW TABLES;
+Tables_in_test
+t2
+t3
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */
+connection slave;
+drop table t2, t3;
+connection master;
+set debug_sync='RESET';
+drop table t2, t3;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test
new file mode 100644
index 00000000000..281e2a2ab47
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test
@@ -0,0 +1,64 @@
+# ==== Purpose ====
+#
+# Check that when the execution of a DROP TABLE command with single table
+# fails it should not be written to the binary log. Also test that when the
+# execution of DROP TABLE command with multiple tables fails the command
+# should be written into the binary log.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 0 - Create tables named t1, t2, t3
+# 1 - Execute DROP TABLE t1,t2,t3 command.
+# 2 - Kill the DROP TABLE command while it is trying to drop table 't2'.
+# 3 - Verify that tables t2,t3 are present after the DROP command execution
+# was interrupted.
+# 4 - Check that table 't1' is present in binary log as part of DROP
+# command.
+#
+# ==== References ====
+#
+# MDEV-20348: DROP TABLE IF EXISTS killed on master but was replicated.
+#
+
+--source include/have_innodb.inc
+--source include/have_debug_sync.inc
+--source include/have_binlog_format_statement.inc
+--source include/master-slave.inc
+
+create table t1 (a int) engine=innodb;
+create table t2 (b longblob) engine=innodb;
+create table t3 (c int) engine=innodb;
+insert into t2 values (repeat('b',1024*1024));
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
+
+let $id=`select connection_id()`;
+set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2';
+send drop table t1, t2, t3;
+
+connect foo,localhost,root;
+set debug_sync='now SIGNAL go';
+let $wait_condition=select 1 from information_schema.processlist where state like 'debug sync point:%';
+source include/wait_condition.inc;
+--replace_result $id CONNECTION_ID
+eval kill query $id;
+
+connection master;
+error ER_QUERY_INTERRUPTED;
+reap;
+
+--echo "Tables t2 and t3 should be listed"
+SHOW TABLES;
+--source include/show_binlog_events.inc
+--sync_slave_with_master
+drop table t2, t3;
+
+connection master;
+set debug_sync='RESET';
+drop table t2, t3;
+
+source include/rpl_end.inc;
diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp
index 6e275ac478e..e58d0a10124 100644
--- a/mysql-test/valgrind.supp
+++ b/mysql-test/valgrind.supp
@@ -1,5 +1,5 @@
# Copyright (c) 2005, 2015, Oracle and/or its affiliates.
-# Copyright (c) 2008, 2017, MariaDB
+# Copyright (c) 2008, 2019, MariaDB
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
@@ -1799,3 +1799,121 @@
fun:FIPS_mode_set
obj:/usr/lib64/libcrypto.so*
}
+
+
+
+##
+## The following is a copy of facebook/mysql-5.6 suppressions:
+##
+
+#
+# RocksDB Storage Engine suppressions start
+#
+
+{
+ Still reachable for once-per-process initializations
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZN7rocksdb16ThreadStatusUtil19NewColumnFamilyInfoEPKNS_2DBEPKNS_16ColumnFamilyDataERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPKNS_3EnvE
+ fun:_ZNK7rocksdb6DBImpl21NewThreadStatusCfInfoEPNS_16ColumnFamilyDataE
+ fun:_ZN7rocksdb2DB4OpenERKNS_9DBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISD_EEPSC_IPNS_18ColumnFamilyHandleESaISJ_EEPPS0_
+ fun:_ZN7rocksdb13TransactionDB4OpenERKNS_9DBOptionsERKNS_20TransactionDBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISG_EEPSF_IPNS_18ColumnFamilyHandleESaISM_EEPPS0_
+ fun:_ZN7myrocksL17rocksdb_init_funcEPv
+}
+
+
+{
+ Still reachable for once-per-process initializations 2
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZN7rocksdb16ThreadStatusUtil19NewColumnFamilyInfoEPKNS_2DBEPKNS_16ColumnFamilyDataERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPKNS_3EnvE
+ fun:_ZNK7rocksdb6DBImpl21NewThreadStatusCfInfoEPNS_16ColumnFamilyDataE
+ fun:_ZN7rocksdb6DBImpl4OpenERKNS_9DBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISD_EEPSC_IPNS_18ColumnFamilyHandleESaISJ_EEPPNS_2DBEbb
+ fun:_ZN7rocksdb13TransactionDB4OpenERKNS_9DBOptionsERKNS_20TransactionDBOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22ColumnFamilyDescriptorESaISG_EEPSF_IPNS_18ColumnFamilyHandleESaISM_EEPPS0_
+# fun:_ZN7myrocksL17rocksdb_init_funcEPv
+# ^ commenting the above out because we are hitting the --num-callers=16
+# limitation that MTR sets for valgrind
+}
+
+{
+ Still reachable for once-per-process initializations 3
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZN7rocksdb16ThreadStatusUtil19NewColumnFamilyInfoEPKNS_2DBEPKNS_16ColumnFamilyDataERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPKNS_3EnvE
+ fun:_ZNK7rocksdb6DBImpl21NewThreadStatusCfInfoEPNS_16ColumnFamilyDataE
+ fun:_ZN7rocksdb6DBImpl22CreateColumnFamilyImplERKNS_19ColumnFamilyOptionsERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPPNS_18ColumnFamilyHandleE
+}
+
+{
+ Still reachable for once-per-process initializations
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZN7rocksdb19ThreadStatusUpdater14RegisterThreadENS_12ThreadStatus10ThreadTypeEm
+ fun:_ZN7rocksdb16ThreadStatusUtil14RegisterThreadEPKNS_3EnvENS_12ThreadStatus10ThreadTypeE
+ fun:_ZN7rocksdb14ThreadPoolImpl4Impl15BGThreadWrapperEPv
+ ...
+}
+
+{
+ Still reachable for once-per-process initializations
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZN7rocksdb14ThreadLocalPtr14InitSingletonsEv
+ fun:_ZN7rocksdb3Env7DefaultEv
+ fun:_ZN7rocksdb9DBOptionsC1Ev
+ ...
+ fun:_ZN7myrocksL27rdb_init_rocksdb_db_optionsEv
+}
+
+{
+ Still reachable for once-per-process initializations
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZN7rocksdb12_GLOBAL__N_18PosixEnv*
+ fun:_ZN7rocksdb3Env7DefaultEv
+ fun:_ZN7rocksdb9DBOptionsC1Ev
+ ...
+ fun:_ZN7myrocksL27rdb_init_rocksdb_db_optionsEv
+}
+
+{
+ Still reachable for thread local storage initialization (SetHandle)
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZNSt13unordered_mapIjPFvPvESt4hashIjESt8equal_toIjESaISt4pairIKjS2_EEEixERS8_
+ fun:_ZN7rocksdb14ThreadLocalPtr10StaticMeta10SetHandlerEjPFvPvE
+ fun:_ZN7rocksdb14ThreadLocalPtrC1EPFvPvE
+ ...
+}
+
+{
+ Still reachable for thread local storage initialization (ReclaimId)
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_ZN7rocksdb10autovectorIjLm8EE9push_backERKj
+ fun:_ZN7rocksdb14ThreadLocalPtr10StaticMeta9ReclaimIdEj
+ fun:_ZN7rocksdb14ThreadLocalPtrD1Ev
+ ...
+}
+
+{
+ Static initialization
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:_Z41__static_initialization_and_destruction_0ii
+ ...
+}
+
+##
+## RocksDB Storage Engine suppressions end
+##
+
diff --git a/sql/item.h b/sql/item.h
index c39a3514d04..5cda78d42fa 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, 2018, MariaDB Corporation
+ Copyright (c) 2009, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -719,7 +719,6 @@ public:
class Item: public Value_source,
public Type_all_attributes
{
- void operator=(Item &);
/**
The index in the JOIN::join_tab array of the JOIN_TAB this Item is attached
to. Items are attached (or 'pushed') to JOIN_TABs during optimization by the
diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h
index 5c63eb11547..5b3aa4f0e41 100644
--- a/sql/item_windowfunc.h
+++ b/sql/item_windowfunc.h
@@ -1294,7 +1294,7 @@ public:
bool fix_length_and_dec()
{
- decimals = window_func()->decimals;
+ Type_std_attributes::set(window_func());
return FALSE;
}
diff --git a/sql/sql_list.h b/sql/sql_list.h
index f5d8ed98b02..3585adbd714 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -1,6 +1,7 @@
#ifndef INCLUDES_MYSQL_SQL_LIST_H
#define INCLUDES_MYSQL_SQL_LIST_H
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -48,6 +49,14 @@ public:
next= elements ? tmp.next : &first;
}
+ SQL_I_List& operator=(const SQL_I_List &tmp)
+ {
+ elements= tmp.elements;
+ first= tmp.first;
+ next= tmp.next;
+ return *this;
+ }
+
inline void empty()
{
elements= 0;
@@ -488,7 +497,6 @@ template <class T> class List :public base_list
{
public:
inline List() :base_list() {}
- inline List(const List<T> &tmp) :base_list(tmp) {}
inline List(const List<T> &tmp, MEM_ROOT *mem_root) :
base_list(tmp, mem_root) {}
inline bool push_back(T *a) { return base_list::push_back(a); }
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index f94676730b7..f3e4d35c0f5 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -2392,35 +2392,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
/* remove .frm file and engine files */
path_length= build_table_filename(path, sizeof(path) - 1, db.str, alias.str,
reg_ext, 0);
-
- /*
- This handles the case where a "DROP" was executed and a regular
- table "may be" dropped as drop_temporary is FALSE and error is
- TRUE. If the error was FALSE a temporary table was dropped and
- regardless of the status of drop_temporary a "DROP TEMPORARY"
- must be used.
- */
- if (!dont_log_query)
- {
- /*
- Note that unless if_exists is TRUE or a temporary table was deleted,
- there is no means to know if the statement should be written to the
- binary log. See further information on this variable in what follows.
- */
- non_tmp_table_deleted= (if_exists ? TRUE : non_tmp_table_deleted);
- /*
- Don't write the database name if it is the current one (or if
- thd->db is NULL).
- */
- if (thd->db.str == NULL || cmp(&db, &thd->db) != 0)
- {
- append_identifier(thd, &built_query, &db);
- built_query.append(".");
- }
-
- append_identifier(thd, &built_query, &table->table_name);
- built_query.append(",");
- }
}
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
error= 0;
@@ -2500,9 +2471,16 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
// Remove extension for delete
*(end= path + path_length - reg_ext_length)= '\0';
- error= ha_delete_table(thd, table_type, path, &db, &table->table_name,
- !dont_log_query);
- if (!error)
+ if ((error= ha_delete_table(thd, table_type, path, &db, &table->table_name,
+ !dont_log_query)))
+ {
+ if (thd->is_killed())
+ {
+ error= -1;
+ goto err;
+ }
+ }
+ else
{
/* Delete the table definition file */
strmov(end,reg_ext);
@@ -2546,7 +2524,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (error)
{
if (wrong_tables.length())
- wrong_tables.append(',');
+ wrong_tables.append(',');
wrong_tables.append(&db);
wrong_tables.append('.');
wrong_tables.append(&table->table_name);
@@ -2559,6 +2537,22 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
mysql_audit_drop_table(thd, table);
}
+ if (!dont_log_query && !drop_temporary)
+ {
+ non_tmp_table_deleted= (if_exists ? TRUE : non_tmp_table_deleted);
+ /*
+ Don't write the database name if it is the current one (or if
+ thd->db is NULL).
+ */
+ if (thd->db.str == NULL || cmp(&db, &thd->db) != 0)
+ {
+ append_identifier(thd, &built_query, &db);
+ built_query.append(".");
+ }
+
+ append_identifier(thd, &built_query, &table->table_name);
+ built_query.append(",");
+ }
DBUG_PRINT("table", ("table: %p s: %p", table->table,
table->table ? table->table->s : NULL));
}
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index e1749f9ea78..a036821cab1 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -71,7 +71,11 @@ ins_node_create(
ins_node_t* node;
node = static_cast<ins_node_t*>(
- mem_heap_alloc(heap, sizeof(ins_node_t)));
+ mem_heap_zalloc(heap, sizeof(ins_node_t)));
+
+ if (!node) {
+ return(NULL);
+ }
node->common.type = QUE_NODE_INSERT;
@@ -79,12 +83,6 @@ ins_node_create(
node->state = INS_NODE_SET_IX_LOCK;
node->table = table;
- node->index = NULL;
- node->entry = NULL;
-
- node->select = NULL;
-
- node->trx_id = 0;
node->entry_sys_heap = mem_heap_create(128);
diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc
index 25930cb86f1..1a10657cf99 100644
--- a/storage/innobase/row/row0log.cc
+++ b/storage/innobase/row/row0log.cc
@@ -1005,7 +1005,7 @@ row_log_table_low(
extra_size = rec_extra_size + is_instant;
unsigned fake_extra_size = 0;
- byte fake_extra_buf[2];
+ byte fake_extra_buf[3];
if (is_instant && UNIV_UNLIKELY(!index->is_instant())) {
/* The source table was emptied after ALTER TABLE
started, and it was converted to non-instant format.
@@ -1017,9 +1017,9 @@ row_log_table_low(
fake_extra_size = rec_get_n_add_field_len(n_add);
ut_ad(fake_extra_size == 1 || fake_extra_size == 2);
extra_size += fake_extra_size;
- byte* fake_extra = fake_extra_buf + fake_extra_size - 1;
+ byte* fake_extra = fake_extra_buf + fake_extra_size;
rec_set_n_add_field(fake_extra, n_add);
- ut_ad(fake_extra + 1 == fake_extra_buf);
+ ut_ad(fake_extra == fake_extra_buf);
}
mrec_size = ROW_LOG_HEADER_SIZE
@@ -1078,7 +1078,7 @@ row_log_table_low(
memcpy(b, rec - rec_extra_size - omit_size, rec_extra_size);
b += rec_extra_size;
- memcpy(b, fake_extra_buf, fake_extra_size);
+ memcpy(b, fake_extra_buf + 1, fake_extra_size);
b += fake_extra_size;
ulint len;
ulint trx_id_offs = rec_get_nth_field_offs(
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 6d70cbfa0fa..3ca8b54354c 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -2151,7 +2151,9 @@ This is used in UPDATE CASCADE/SET NULL of a system versioning table.
@return DB_SUCCESS or some error */
static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node)
{
- const trx_t* trx = thr_get_trx(thr);
+ trx_t* trx = thr_get_trx(thr);
+ dfield_t* row_end;
+ char row_end_data[8];
dict_table_t* table = node->table;
ut_ad(table->versioned());
@@ -2162,10 +2164,15 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node)
ins_node_t* insert_node =
ins_node_create(INS_DIRECT, table, node->historical_heap);
+ if (!insert_node) {
+ trx->error_state = DB_OUT_OF_MEMORY;
+ goto exit;
+ }
+
+ insert_node->common.parent = thr;
ins_node_set_new_row(insert_node, row);
- dfield_t* row_end = dtuple_get_nth_field(row, table->vers_end);
- char row_end_data[8];
+ 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);
dfield_set_data(row_end, row_end_data, 8);
@@ -2203,6 +2210,7 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node)
}
}
exit:
+ que_graph_free_recursive(insert_node);
mem_heap_free(node->historical_heap);
node->historical_heap = NULL;
return trx->error_state;
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index f7eec2dc8c2..6c7a54ece7d 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -3199,14 +3199,29 @@ row_sel_build_prev_vers_for_mysql(
return(err);
}
+/** Helper class to cache clust_rec and old_ver */
+class Row_sel_get_clust_rec_for_mysql
+{
+ const rec_t *cached_clust_rec;
+ rec_t *cached_old_vers;
+
+public:
+ Row_sel_get_clust_rec_for_mysql() :
+ cached_clust_rec(NULL), cached_old_vers(NULL) {}
+
+ dberr_t operator()(row_prebuilt_t *prebuilt, dict_index_t *sec_index,
+ const rec_t *rec, que_thr_t *thr, const rec_t **out_rec,
+ ulint **offsets, mem_heap_t **offset_heap,
+ dtuple_t **vrow, mtr_t *mtr);
+};
+
/*********************************************************************//**
Retrieves the clustered index record corresponding to a record in a
non-clustered index. Does the necessary locking. Used in the MySQL
interface.
@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */
-static MY_ATTRIBUTE((warn_unused_result))
dberr_t
-row_sel_get_clust_rec_for_mysql(
+Row_sel_get_clust_rec_for_mysql::operator()(
/*============================*/
row_prebuilt_t* prebuilt,/*!< in: prebuilt struct in the handle */
dict_index_t* sec_index,/*!< in: secondary index where rec resides */
@@ -3398,15 +3413,36 @@ row_sel_get_clust_rec_for_mysql(
clust_rec, clust_index, *offsets,
&trx->read_view)) {
- /* The following call returns 'offsets' associated with
- 'old_vers' */
- err = row_sel_build_prev_vers_for_mysql(
- &trx->read_view, clust_index, prebuilt,
- clust_rec, offsets, offset_heap, &old_vers,
- vrow, mtr);
+ if (clust_rec != cached_clust_rec) {
+ /* The following call returns 'offsets' associated with
+ 'old_vers' */
+ err = row_sel_build_prev_vers_for_mysql(
+ &trx->read_view, clust_index, prebuilt,
+ clust_rec, offsets, offset_heap, &old_vers,
+ vrow, mtr);
+
+ if (err != DB_SUCCESS) {
- if (err != DB_SUCCESS || old_vers == NULL) {
+ goto err_exit;
+ }
+ cached_clust_rec = clust_rec;
+ cached_old_vers = old_vers;
+ } else {
+ err = DB_SUCCESS;
+ old_vers = cached_old_vers;
+
+ /* The offsets need not be same for the latest
+ version of clust_rec and its old version
+ old_vers. Re-calculate the offsets for old_vers. */
+
+ if (old_vers != NULL) {
+ *offsets = rec_get_offsets(
+ old_vers, clust_index, *offsets,
+ true, ULINT_UNDEFINED, offset_heap);
+ }
+ }
+ if (old_vers == NULL) {
goto err_exit;
}
@@ -4160,6 +4196,7 @@ row_search_mvcc(
dtuple_t* vrow = NULL;
const rec_t* result_rec = NULL;
const rec_t* clust_rec;
+ Row_sel_get_clust_rec_for_mysql row_sel_get_clust_rec_for_mysql;
dberr_t err = DB_SUCCESS;
ibool unique_search = FALSE;
ibool mtr_has_extra_clust_latch = FALSE;
@@ -4594,7 +4631,7 @@ wait_table_again:
if (err != DB_SUCCESS) {
rec = NULL;
- goto lock_wait_or_error;
+ goto page_read_error;
}
pcur->trx_if_known = trx;
@@ -4648,7 +4685,7 @@ wait_table_again:
index->table->file_unreadable = true;
}
rec = NULL;
- goto lock_wait_or_error;
+ goto page_read_error;
}
}
@@ -4669,7 +4706,7 @@ rec_loop:
if (!index->table->is_readable()) {
err = DB_DECRYPTION_FAILED;
- goto lock_wait_or_error;
+ goto page_read_error;
}
ut_ad(!!page_rec_is_comp(rec) == comp);
@@ -4776,7 +4813,7 @@ wrong_offs:
ut_ad(0);
err = DB_CORRUPTION;
- goto lock_wait_or_error;
+ goto page_read_error;
} else {
/* The user may be dumping a corrupt table. Jump
over the corruption to recover as much as possible. */
@@ -5604,6 +5641,10 @@ not_moved:
goto normal_return;
lock_wait_or_error:
+ if (!dict_index_is_spatial(index)) {
+ btr_pcur_store_position(pcur, &mtr);
+ }
+page_read_error:
/* Reset the old and new "did semi-consistent read" flags. */
if (UNIV_UNLIKELY(prebuilt->row_read_type
== ROW_READ_DID_SEMI_CONSISTENT)) {
@@ -5611,13 +5652,6 @@ lock_wait_or_error:
}
did_semi_consistent_read = FALSE;
- /*-------------------------------------------------------------*/
- if (!dict_index_is_spatial(index)) {
- if (rec) {
- btr_pcur_store_position(pcur, &mtr);
- }
- }
-
lock_table_wait:
mtr.commit();
mtr_has_extra_clust_latch = FALSE;
diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc
index 8ef8c349e9f..1929c5235f7 100644
--- a/storage/rocksdb/ha_rocksdb.cc
+++ b/storage/rocksdb/ha_rocksdb.cc
@@ -5836,11 +5836,11 @@ static int rocksdb_done_func(void *const p) {
// Disown the cache data since we're shutting down.
// This results in memory leaks but it improved the shutdown time.
// Don't disown when running under valgrind
-#ifndef HAVE_purify
+#ifndef HAVE_valgrind
if (rocksdb_tbl_options->block_cache) {
rocksdb_tbl_options->block_cache->DisownData();
}
-#endif /* HAVE_purify */
+#endif /* HAVE_valgrind */
/*
MariaDB: don't clear rocksdb_db_options and rocksdb_tbl_options.
diff --git a/storage/rocksdb/mysql-test/rocksdb/suite.pm b/storage/rocksdb/mysql-test/rocksdb/suite.pm
index 3423d34ded5..e4f074df471 100644
--- a/storage/rocksdb/mysql-test/rocksdb/suite.pm
+++ b/storage/rocksdb/mysql-test/rocksdb/suite.pm
@@ -20,8 +20,8 @@ my $sst_dump=
return "RocksDB is not compiled, no sst_dump" unless $sst_dump;
$ENV{MARIAROCKS_SST_DUMP}="$sst_dump";
-# Temporarily disable testing under valgrind, due to MDEV-12439
-return "RocksDB tests disabled under valgrind" if ($::opt_valgrind);
+## Temporarily disable testing under valgrind, due to MDEV-12439
+#return "RocksDB tests disabled under valgrind" if ($::opt_valgrind);
bless { };