From 7fb17e42cf2f6f309f43907f2db84389d8d895e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 10 Jan 2011 15:34:45 +0200 Subject: [PATCH 01/42] Bug#59181 InnoDB compilation failure on the Sun Studio compiler Define UNIV_PREFETCH_R(add) as sun_prefetch_read_many((void*) addr), because apparently some versions of the Sun library omit the const qualifier. --- storage/innodb_plugin/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index bbff8ddf1e3..4425950748b 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -412,7 +412,7 @@ it is read or written. */ /* Use sun_prefetch when compile with Sun Studio */ # define UNIV_EXPECT(expr,value) (expr) # define UNIV_LIKELY_NULL(expr) (expr) -# define UNIV_PREFETCH_R(addr) sun_prefetch_read_many(addr) +# define UNIV_PREFETCH_R(addr) sun_prefetch_read_many((void*) addr) # define UNIV_PREFETCH_RW(addr) sun_prefetch_write_many(addr) #else /* Dummy versions of the macros */ From 634fe860562f7249c55a0f946a1fb50972b9f9ff Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 12 Jan 2011 17:53:05 +0200 Subject: [PATCH 02/42] Suppress InnoDB warning about long semaphore wait if running under Valgrind Sometimes Valgrind could be extremely slow and could trigger the InnoDB diagnostic message making the test to fail. --- mysql-test/suite/innodb/t/innodb_bug56143.test | 5 +++++ mysql-test/suite/innodb_plugin/t/innodb_bug56143.test | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_bug56143.test b/mysql-test/suite/innodb/t/innodb_bug56143.test index 1218ae6621c..b69d0048ee8 100644 --- a/mysql-test/suite/innodb/t/innodb_bug56143.test +++ b/mysql-test/suite/innodb/t/innodb_bug56143.test @@ -8,6 +8,11 @@ -- disable_query_log -- disable_result_log +if ($VALGRIND_TEST) +{ + call mtr.add_suppression("InnoDB: Warning: a long semaphore wait:"); +} + SET foreign_key_checks=0; DROP TABLE IF EXISTS bug56143; CREATE TABLE `bug56143` ( diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug56143.test b/mysql-test/suite/innodb_plugin/t/innodb_bug56143.test index 7c7472303db..0f135a44f5d 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug56143.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug56143.test @@ -8,6 +8,11 @@ -- disable_query_log -- disable_result_log +if ($VALGRIND_TEST) +{ + call mtr.add_suppression("InnoDB: Warning: a long semaphore wait:"); +} + SET foreign_key_checks=0; DROP TABLE IF EXISTS bug56143_1; From 9cd4d4984025857782e12e53d32cea5e4b7684e5 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Fri, 14 Jan 2011 09:02:28 -0800 Subject: [PATCH 03/42] Fix Bug#30423 "InnoDBs treatment of NULL in index stats causes bad "rows examined" estimates". This change implements "innodb_stats_method" with options of "nulls_equal", "nulls_unequal" and "null_ignored". rb://553 approved by Marko --- .../suite/innodb/r/innodb_bug30423.result | 95 ++++++++ .../suite/innodb/t/innodb_bug30423.test | 211 ++++++++++++++++++ .../innodb_plugin/r/innodb_bug30423.result | 95 ++++++++ .../innodb_plugin/t/innodb_bug30423.test | 211 ++++++++++++++++++ storage/innobase/btr/btr0cur.c | 146 +++++++++--- storage/innobase/dict/dict0dict.c | 10 + storage/innobase/handler/ha_innodb.cc | 95 +++++++- storage/innobase/include/btr0cur.h | 5 +- storage/innobase/include/dict0mem.h | 6 + storage/innobase/include/rem0cmp.h | 4 + storage/innobase/include/rem0cmp.ic | 2 +- storage/innobase/include/srv0srv.h | 18 ++ storage/innobase/rem/rem0cmp.c | 14 +- storage/innobase/srv/srv0srv.c | 5 + storage/innodb_plugin/ChangeLog | 8 + storage/innodb_plugin/btr/btr0cur.c | 150 ++++++++++--- storage/innodb_plugin/dict/dict0dict.c | 10 + storage/innodb_plugin/handler/ha_innodb.cc | 95 +++++++- storage/innodb_plugin/include/btr0cur.h | 5 +- storage/innodb_plugin/include/dict0mem.h | 6 + storage/innodb_plugin/include/rem0cmp.h | 4 + storage/innodb_plugin/include/rem0cmp.ic | 2 +- storage/innodb_plugin/include/srv0srv.h | 18 ++ storage/innodb_plugin/rem/rem0cmp.c | 14 +- storage/innodb_plugin/srv/srv0srv.c | 5 + 25 files changed, 1157 insertions(+), 77 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug30423.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug30423.test create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug30423.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug30423.test diff --git a/mysql-test/suite/innodb/r/innodb_bug30423.result b/mysql-test/suite/innodb/r/innodb_bug30423.result new file mode 100644 index 00000000000..a19809366ae --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug30423.result @@ -0,0 +1,95 @@ +set global innodb_stats_method = default; +select @@innodb_stats_method; +@@innodb_stats_method +nulls_equal +select count(*) from bug30243_3 where org_id is not NULL; +count(*) +20 +select count(*) from bug30243_3 where org_id is NULL; +count(*) +16384 +select count(*) from bug30243_2 where org_id is not NULL; +count(*) +224 +select count(*) from bug30243_2 where org_id is NULL; +count(*) +65536 +select @@innodb_stats_method; +@@innodb_stats_method +nulls_equal +analyze table bug30243_1; +Table Op Msg_type Msg_text +test.bug30243_1 analyze status OK +analyze table bug30243_2; +Table Op Msg_type Msg_text +test.bug30243_2 analyze status OK +analyze table bug30243_3; +Table Op Msg_type Msg_text +test.bug30243_3 analyze status OK +set global innodb_stats_method = "NULL"; +ERROR 42000: Variable 'stats_method' can't be set to the value of 'NULL' +set global innodb_stats_method = "nulls_ignored"; +select @@innodb_stats_method; +@@innodb_stats_method +nulls_ignored +analyze table bug30243_1; +Table Op Msg_type Msg_text +test.bug30243_1 analyze status OK +analyze table bug30243_2; +Table Op Msg_type Msg_text +test.bug30243_2 analyze status OK +analyze table bug30243_3; +Table Op Msg_type Msg_text +test.bug30243_3 analyze status OK +explain SELECT COUNT(*), 0 +FROM bug30243_1 orgs +LEFT JOIN bug30243_3 sa_opportunities +ON orgs.org_id=sa_opportunities.org_id +LEFT JOIN bug30243_2 contacts +ON orgs.org_id=contacts.org_id ; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE orgs index NULL org_id 4 NULL 128 Using index +1 SIMPLE sa_opportunities ref org_id org_id 5 test.orgs.org_id 1 Using index +1 SIMPLE contacts ref contacts$org_id contacts$org_id 5 test.orgs.org_id 1 Using index +select @@innodb_stats_method; +@@innodb_stats_method +nulls_ignored +set global innodb_stats_method = "nulls_unequal"; +select @@innodb_stats_method; +@@innodb_stats_method +nulls_unequal +analyze table bug30243_1; +Table Op Msg_type Msg_text +test.bug30243_1 analyze status OK +analyze table bug30243_2; +Table Op Msg_type Msg_text +test.bug30243_2 analyze status OK +analyze table bug30243_3; +Table Op Msg_type Msg_text +test.bug30243_3 analyze status OK +explain SELECT COUNT(*), 0 +FROM bug30243_1 orgs +LEFT JOIN bug30243_3 sa_opportunities +ON orgs.org_id=sa_opportunities.org_id +LEFT JOIN bug30243_2 contacts +ON orgs.org_id=contacts.org_id; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE orgs index NULL org_id 4 NULL 128 Using index +1 SIMPLE sa_opportunities ref org_id org_id 5 test.orgs.org_id 1 Using index +1 SIMPLE contacts ref contacts$org_id contacts$org_id 5 test.orgs.org_id 1 Using index +SELECT COUNT(*) FROM table_bug30423 WHERE org_id IS NULL; +COUNT(*) +1024 +set global innodb_stats_method = "nulls_unequal"; +analyze table table_bug30423; +Table Op Msg_type Msg_text +test.table_bug30423 analyze status OK +set global innodb_stats_method = "nulls_ignored"; +analyze table table_bug30423; +Table Op Msg_type Msg_text +test.table_bug30423 analyze status OK +set global innodb_stats_method = nulls_equal; +drop table bug30243_2; +drop table bug30243_1; +drop table bug30243_3; +drop table table_bug30423; diff --git a/mysql-test/suite/innodb/t/innodb_bug30423.test b/mysql-test/suite/innodb/t/innodb_bug30423.test new file mode 100644 index 00000000000..f2a3ee8d099 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug30423.test @@ -0,0 +1,211 @@ +# Test for Bug #30423, InnoDBs treatment of NULL in index stats causes +# bad "rows examined" estimates. +# Implemented InnoDB system variable "innodb_stats_method" with +# "nulls_equal" (default), "nulls_unequal", and "nulls_ignored" options. + +-- source include/have_innodb.inc + +let $innodb_stats_method_orig = `select @@innodb_stats_method`; + +# default setting for innodb_stats_method is "nulls_equal" +set global innodb_stats_method = default; + +select @@innodb_stats_method; + +# create three tables, bug30243_1, bug30243_2 and bug30243_3. +# The test scenario is adopted from original bug #30423 report. +# table bug30243_1 and bug30243_3 have many NULL values + +-- disable_result_log +-- disable_query_log + +DROP TABLE IF EXISTS bug30243_1; +CREATE TABLE bug30243_1 ( + org_id int(11) NOT NULL default '0', + UNIQUE KEY (org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +LOCK TABLES bug30243_1 WRITE; +INSERT INTO bug30243_1 VALUES (11),(15),(16),(17),(19),(20),(21),(23),(24), +(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(37),(38),(40),(41), +(42),(43),(44),(45),(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56), +(57),(58),(59),(60),(61),(62),(63),(64),(65),(66),(67),(68),(69),(70),(71), +(72),(73),(74),(75),(76),(77),(78),(79),(80),(81),(82),(83),(84),(85),(86), +(87),(88),(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),(100),(101), +(102),(103),(104),(105),(106),(107),(108),(109),(110),(111),(112),(113),(114), +(115),(116),(117),(118),(119),(120),(121),(122),(123),(124),(125),(126),(127), +(128),(129),(130),(131),(132),(133),(134),(135),(136),(137),(138),(139),(140), +(141),(142),(143),(144),(145); +UNLOCK TABLES; + +DROP TABLE IF EXISTS bug30243_3; +CREATE TABLE bug30243_3 ( + org_id int(11) default NULL, + KEY (org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO bug30243_3 VALUES (NULL); + +begin; +let $i=14; +while ($i) +{ + INSERT INTO bug30243_3 SELECT NULL FROM bug30243_3; + dec $i; +} + +INSERT INTO bug30243_3 VALUES (34),(34),(35),(56),(58),(62),(62),(64),(65),(66),(80),(135),(137),(138),(139),(140),(142),(143),(144),(145); +commit; + +DROP TABLE IF EXISTS bug30243_2; +CREATE TABLE bug30243_2 ( + org_id int(11) default NULL, + KEY `contacts$org_id` (org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO bug30243_2 VALUES (NULL); + +begin; +let $i=16; +while ($i) +{ + INSERT INTO bug30243_2 SELECT NULL FROM bug30243_2; + dec $i; +} + +INSERT INTO bug30243_2 VALUES (11),(15),(16),(17),(20),(21),(23),(24),(25), +(26),(27),(28),(29),(30),(31),(32),(33),(34),(37),(38),(40),(41),(42),(43), +(44),(45),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(48), +(48),(50),(51),(52),(52),(53),(54),(55),(57),(60),(61),(62),(62),(62),(62), +(62),(63),(64),(64),(65),(66),(66),(67),(68),(69),(70),(71),(72),(73),(74), +(75),(76),(77),(78),(79),(80),(80),(81),(82),(83),(84),(85),(86),(87),(88), +(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),(100),(101),(102), +(103),(104),(105),(106),(107),(108),(109),(110),(111),(112),(113),(114), +(115),(116),(117),(118),(119),(120),(121),(122),(123),(124),(125),(126), +(127),(128),(129),(130),(131),(132),(133),(133),(135),(135),(135),(135), +(136),(136),(138),(138),(139),(139),(139),(140),(141),(141),(142),(143), +(143),(145),(145); +commit; + + +-- enable_result_log +-- enable_query_log + +# check tables's value +select count(*) from bug30243_3 where org_id is not NULL; +select count(*) from bug30243_3 where org_id is NULL; + +select count(*) from bug30243_2 where org_id is not NULL; +select count(*) from bug30243_2 where org_id is NULL; + +select @@innodb_stats_method; + +analyze table bug30243_1; +analyze table bug30243_2; +analyze table bug30243_3; + +# Following query plan shows that we over estimate the rows per +# unique value (since there are many NULLs). +# Skip this query log since the stats estimate could vary from runs +-- disable_query_log +-- disable_result_log +explain SELECT COUNT(*), 0 + FROM bug30243_1 orgs + LEFT JOIN bug30243_3 sa_opportunities + ON orgs.org_id=sa_opportunities.org_id + LEFT JOIN bug30243_2 contacts + ON orgs.org_id=contacts.org_id ; +-- enable_query_log +-- enable_result_log + +# following set operation will fail +#--error ER_WRONG_VALUE_FOR_VAR +--error 1231 +set global innodb_stats_method = "NULL"; + +set global innodb_stats_method = "nulls_ignored"; + +select @@innodb_stats_method; + +# Regenerate the stats with "nulls_ignored" option + +analyze table bug30243_1; +analyze table bug30243_2; +analyze table bug30243_3; + +# Following query plan shows that we get the correct rows per +# unique value (should be approximately 1 row per value) +explain SELECT COUNT(*), 0 + FROM bug30243_1 orgs + LEFT JOIN bug30243_3 sa_opportunities + ON orgs.org_id=sa_opportunities.org_id + LEFT JOIN bug30243_2 contacts + ON orgs.org_id=contacts.org_id ; + +select @@innodb_stats_method; + +# Try the "nulls_unequal" option +set global innodb_stats_method = "nulls_unequal"; + +select @@innodb_stats_method; + +analyze table bug30243_1; +analyze table bug30243_2; +analyze table bug30243_3; + +# Following query plan shows that we get the correct rows per +# unique value (~1) +explain SELECT COUNT(*), 0 + FROM bug30243_1 orgs + LEFT JOIN bug30243_3 sa_opportunities + ON orgs.org_id=sa_opportunities.org_id + LEFT JOIN bug30243_2 contacts + ON orgs.org_id=contacts.org_id; + + +# Create a table with all NULL values, make sure the stats calculation +# does not crash with table of all NULL values +-- disable_query_log +CREATE TABLE table_bug30423 ( + org_id int(11) default NULL, + KEY(org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO `table_bug30423` VALUES (NULL); + +begin; +let $i=10; +while ($i) +{ + INSERT INTO table_bug30423 SELECT NULL FROM table_bug30423; + dec $i; +} +commit; + +-- enable_query_log + +SELECT COUNT(*) FROM table_bug30423 WHERE org_id IS NULL; + +# calculate the statistics for the table for "nulls_ignored" and +# "nulls_unequal" option +set global innodb_stats_method = "nulls_unequal"; +analyze table table_bug30423; + +set global innodb_stats_method = "nulls_ignored"; +analyze table table_bug30423; + + +eval set global innodb_stats_method = $innodb_stats_method_orig; + +drop table bug30243_2; + +drop table bug30243_1; + +drop table bug30243_3; + +drop table table_bug30423; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug30423.result b/mysql-test/suite/innodb_plugin/r/innodb_bug30423.result new file mode 100644 index 00000000000..a19809366ae --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug30423.result @@ -0,0 +1,95 @@ +set global innodb_stats_method = default; +select @@innodb_stats_method; +@@innodb_stats_method +nulls_equal +select count(*) from bug30243_3 where org_id is not NULL; +count(*) +20 +select count(*) from bug30243_3 where org_id is NULL; +count(*) +16384 +select count(*) from bug30243_2 where org_id is not NULL; +count(*) +224 +select count(*) from bug30243_2 where org_id is NULL; +count(*) +65536 +select @@innodb_stats_method; +@@innodb_stats_method +nulls_equal +analyze table bug30243_1; +Table Op Msg_type Msg_text +test.bug30243_1 analyze status OK +analyze table bug30243_2; +Table Op Msg_type Msg_text +test.bug30243_2 analyze status OK +analyze table bug30243_3; +Table Op Msg_type Msg_text +test.bug30243_3 analyze status OK +set global innodb_stats_method = "NULL"; +ERROR 42000: Variable 'stats_method' can't be set to the value of 'NULL' +set global innodb_stats_method = "nulls_ignored"; +select @@innodb_stats_method; +@@innodb_stats_method +nulls_ignored +analyze table bug30243_1; +Table Op Msg_type Msg_text +test.bug30243_1 analyze status OK +analyze table bug30243_2; +Table Op Msg_type Msg_text +test.bug30243_2 analyze status OK +analyze table bug30243_3; +Table Op Msg_type Msg_text +test.bug30243_3 analyze status OK +explain SELECT COUNT(*), 0 +FROM bug30243_1 orgs +LEFT JOIN bug30243_3 sa_opportunities +ON orgs.org_id=sa_opportunities.org_id +LEFT JOIN bug30243_2 contacts +ON orgs.org_id=contacts.org_id ; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE orgs index NULL org_id 4 NULL 128 Using index +1 SIMPLE sa_opportunities ref org_id org_id 5 test.orgs.org_id 1 Using index +1 SIMPLE contacts ref contacts$org_id contacts$org_id 5 test.orgs.org_id 1 Using index +select @@innodb_stats_method; +@@innodb_stats_method +nulls_ignored +set global innodb_stats_method = "nulls_unequal"; +select @@innodb_stats_method; +@@innodb_stats_method +nulls_unequal +analyze table bug30243_1; +Table Op Msg_type Msg_text +test.bug30243_1 analyze status OK +analyze table bug30243_2; +Table Op Msg_type Msg_text +test.bug30243_2 analyze status OK +analyze table bug30243_3; +Table Op Msg_type Msg_text +test.bug30243_3 analyze status OK +explain SELECT COUNT(*), 0 +FROM bug30243_1 orgs +LEFT JOIN bug30243_3 sa_opportunities +ON orgs.org_id=sa_opportunities.org_id +LEFT JOIN bug30243_2 contacts +ON orgs.org_id=contacts.org_id; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE orgs index NULL org_id 4 NULL 128 Using index +1 SIMPLE sa_opportunities ref org_id org_id 5 test.orgs.org_id 1 Using index +1 SIMPLE contacts ref contacts$org_id contacts$org_id 5 test.orgs.org_id 1 Using index +SELECT COUNT(*) FROM table_bug30423 WHERE org_id IS NULL; +COUNT(*) +1024 +set global innodb_stats_method = "nulls_unequal"; +analyze table table_bug30423; +Table Op Msg_type Msg_text +test.table_bug30423 analyze status OK +set global innodb_stats_method = "nulls_ignored"; +analyze table table_bug30423; +Table Op Msg_type Msg_text +test.table_bug30423 analyze status OK +set global innodb_stats_method = nulls_equal; +drop table bug30243_2; +drop table bug30243_1; +drop table bug30243_3; +drop table table_bug30423; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug30423.test b/mysql-test/suite/innodb_plugin/t/innodb_bug30423.test new file mode 100644 index 00000000000..458c2967e19 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug30423.test @@ -0,0 +1,211 @@ +# Test for Bug #30423, InnoDBs treatment of NULL in index stats causes +# bad "rows examined" estimates. +# Implemented InnoDB system variable "innodb_stats_method" with +# "nulls_equal" (default), "nulls_unequal", and "nulls_ignored" options. + +-- source include/have_innodb_plugin.inc + +let $innodb_stats_method_orig = `select @@innodb_stats_method`; + +# default setting for innodb_stats_method is "nulls_equal" +set global innodb_stats_method = default; + +select @@innodb_stats_method; + +# create three tables, bug30243_1, bug30243_2 and bug30243_3. +# The test scenario is adopted from original bug #30423 report. +# table bug30243_1 and bug30243_3 have many NULL values + +-- disable_result_log +-- disable_query_log + +DROP TABLE IF EXISTS bug30243_1; +CREATE TABLE bug30243_1 ( + org_id int(11) NOT NULL default '0', + UNIQUE KEY (org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +LOCK TABLES bug30243_1 WRITE; +INSERT INTO bug30243_1 VALUES (11),(15),(16),(17),(19),(20),(21),(23),(24), +(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(37),(38),(40),(41), +(42),(43),(44),(45),(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56), +(57),(58),(59),(60),(61),(62),(63),(64),(65),(66),(67),(68),(69),(70),(71), +(72),(73),(74),(75),(76),(77),(78),(79),(80),(81),(82),(83),(84),(85),(86), +(87),(88),(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),(100),(101), +(102),(103),(104),(105),(106),(107),(108),(109),(110),(111),(112),(113),(114), +(115),(116),(117),(118),(119),(120),(121),(122),(123),(124),(125),(126),(127), +(128),(129),(130),(131),(132),(133),(134),(135),(136),(137),(138),(139),(140), +(141),(142),(143),(144),(145); +UNLOCK TABLES; + +DROP TABLE IF EXISTS bug30243_3; +CREATE TABLE bug30243_3 ( + org_id int(11) default NULL, + KEY (org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO bug30243_3 VALUES (NULL); + +begin; +let $i=14; +while ($i) +{ + INSERT INTO bug30243_3 SELECT NULL FROM bug30243_3; + dec $i; +} + +INSERT INTO bug30243_3 VALUES (34),(34),(35),(56),(58),(62),(62),(64),(65),(66),(80),(135),(137),(138),(139),(140),(142),(143),(144),(145); +commit; + +DROP TABLE IF EXISTS bug30243_2; +CREATE TABLE bug30243_2 ( + org_id int(11) default NULL, + KEY `contacts$org_id` (org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO bug30243_2 VALUES (NULL); + +begin; +let $i=16; +while ($i) +{ + INSERT INTO bug30243_2 SELECT NULL FROM bug30243_2; + dec $i; +} + +INSERT INTO bug30243_2 VALUES (11),(15),(16),(17),(20),(21),(23),(24),(25), +(26),(27),(28),(29),(30),(31),(32),(33),(34),(37),(38),(40),(41),(42),(43), +(44),(45),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46), +(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(46),(48), +(48),(50),(51),(52),(52),(53),(54),(55),(57),(60),(61),(62),(62),(62),(62), +(62),(63),(64),(64),(65),(66),(66),(67),(68),(69),(70),(71),(72),(73),(74), +(75),(76),(77),(78),(79),(80),(80),(81),(82),(83),(84),(85),(86),(87),(88), +(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),(100),(101),(102), +(103),(104),(105),(106),(107),(108),(109),(110),(111),(112),(113),(114), +(115),(116),(117),(118),(119),(120),(121),(122),(123),(124),(125),(126), +(127),(128),(129),(130),(131),(132),(133),(133),(135),(135),(135),(135), +(136),(136),(138),(138),(139),(139),(139),(140),(141),(141),(142),(143), +(143),(145),(145); +commit; + + +-- enable_result_log +-- enable_query_log + +# check tables's value +select count(*) from bug30243_3 where org_id is not NULL; +select count(*) from bug30243_3 where org_id is NULL; + +select count(*) from bug30243_2 where org_id is not NULL; +select count(*) from bug30243_2 where org_id is NULL; + +select @@innodb_stats_method; + +analyze table bug30243_1; +analyze table bug30243_2; +analyze table bug30243_3; + +# Following query plan shows that we over estimate the rows per +# unique value (since there are many NULLs). +# Skip this query log since the stats estimate could vary from runs +-- disable_query_log +-- disable_result_log +explain SELECT COUNT(*), 0 + FROM bug30243_1 orgs + LEFT JOIN bug30243_3 sa_opportunities + ON orgs.org_id=sa_opportunities.org_id + LEFT JOIN bug30243_2 contacts + ON orgs.org_id=contacts.org_id ; +-- enable_query_log +-- enable_result_log + +# following set operation will fail +#--error ER_WRONG_VALUE_FOR_VAR +--error 1231 +set global innodb_stats_method = "NULL"; + +set global innodb_stats_method = "nulls_ignored"; + +select @@innodb_stats_method; + +# Regenerate the stats with "nulls_ignored" option + +analyze table bug30243_1; +analyze table bug30243_2; +analyze table bug30243_3; + +# Following query plan shows that we get the correct rows per +# unique value (should be approximately 1 row per value) +explain SELECT COUNT(*), 0 + FROM bug30243_1 orgs + LEFT JOIN bug30243_3 sa_opportunities + ON orgs.org_id=sa_opportunities.org_id + LEFT JOIN bug30243_2 contacts + ON orgs.org_id=contacts.org_id ; + +select @@innodb_stats_method; + +# Try the "nulls_unequal" option +set global innodb_stats_method = "nulls_unequal"; + +select @@innodb_stats_method; + +analyze table bug30243_1; +analyze table bug30243_2; +analyze table bug30243_3; + +# Following query plan shows that we get the correct rows per +# unique value (~1) +explain SELECT COUNT(*), 0 + FROM bug30243_1 orgs + LEFT JOIN bug30243_3 sa_opportunities + ON orgs.org_id=sa_opportunities.org_id + LEFT JOIN bug30243_2 contacts + ON orgs.org_id=contacts.org_id; + + +# Create a table with all NULL values, make sure the stats calculation +# does not crash with table of all NULL values +-- disable_query_log +CREATE TABLE table_bug30423 ( + org_id int(11) default NULL, + KEY(org_id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO `table_bug30423` VALUES (NULL); + +begin; +let $i=10; +while ($i) +{ + INSERT INTO table_bug30423 SELECT NULL FROM table_bug30423; + dec $i; +} +commit; + +-- enable_query_log + +SELECT COUNT(*) FROM table_bug30423 WHERE org_id IS NULL; + +# calculate the statistics for the table for "nulls_ignored" and +# "nulls_unequal" option +set global innodb_stats_method = "nulls_unequal"; +analyze table table_bug30423; + +set global innodb_stats_method = "nulls_ignored"; +analyze table table_bug30423; + + +eval set global innodb_stats_method = $innodb_stats_method_orig; + +drop table bug30243_2; + +drop table bug30243_1; + +drop table bug30243_3; + +drop table table_bug30423; diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index a7160d74a32..9f4babfaae6 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -66,6 +66,13 @@ this many index pages */ /*--------------------------------------*/ #define BTR_BLOB_HDR_SIZE 8 +/* Estimated table level stats from sampled value. */ +#define BTR_TABLE_STATS_FROM_SAMPLE(value, index, ext_size, not_empty) \ + ((value * (ib_longlong) index->stat_n_leaf_pages \ + + BTR_KEY_VAL_ESTIMATE_N_PAGES - 1 + ext_size \ + + not_empty) \ + / (BTR_KEY_VAL_ESTIMATE_N_PAGES + ext_size)) + /*********************************************************************** Marks all extern fields in a record as owned by the record. This function should be called if the delete mark of a record is removed: a not delete @@ -2834,10 +2841,55 @@ btr_estimate_n_rows_in_range( } } +/*********************************************************************** +Record the number of non_null key values in a given index for +each n-column prefix of the index where n < dict_index_get_n_unique(index). +The estimates are eventually stored in the array: +index->stat_n_non_null_key_vals. */ +static +void +btr_record_not_null_field_in_rec( +/*=============================*/ + rec_t* rec, /* in: physical record */ + ulint n_unique, /* in: dict_index_get_n_unique(index), + number of columns uniquely determine + an index entry */ + const ulint* offsets, /* in: rec_get_offsets(rec, index), + its size could be for all fields or + that of "n_unique" */ + ib_longlong* n_not_null) /* in/out: array to record number of + not null rows for n-column prefix */ +{ + ulint i; + + ut_ad(rec_offs_n_fields(offsets) >= n_unique); + + if (n_not_null == NULL) { + return; + } + + for (i = 0; i < n_unique; i++) { + ulint rec_len; + byte* field; + + field = rec_get_nth_field(rec, offsets, i, &rec_len); + + if (rec_len != UNIV_SQL_NULL) { + n_not_null[i]++; + } else { + /* Break if we hit the first NULL value */ + break; + } + } +} + /*********************************************************************** Estimates the number of different key values in a given index, for each n-column prefix of the index where n <= dict_index_get_n_unique(index). -The estimates are stored in the array index->stat_n_diff_key_vals. */ +The estimates are stored in the array index->stat_n_diff_key_vals. +If innodb_stats_method is "nulls_ignored", we also record the number of +non-null values for each prefix and store the estimates in +array index->stat_n_non_null_key_vals. */ void btr_estimate_number_of_different_key_vals( @@ -2851,6 +2903,8 @@ btr_estimate_number_of_different_key_vals( ulint matched_fields; ulint matched_bytes; ib_longlong* n_diff; + ib_longlong* n_not_null; + ibool stats_null_not_equal; ulint not_empty_flag = 0; ulint total_external_size = 0; ulint i; @@ -2858,24 +2912,47 @@ btr_estimate_number_of_different_key_vals( ulint add_on; mtr_t mtr; mem_heap_t* heap = NULL; - ulint offsets_rec_[REC_OFFS_NORMAL_SIZE]; - ulint offsets_next_rec_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets_rec = offsets_rec_; - ulint* offsets_next_rec= offsets_next_rec_; - *offsets_rec_ = (sizeof offsets_rec_) / sizeof *offsets_rec_; - *offsets_next_rec_ - = (sizeof offsets_next_rec_) / sizeof *offsets_next_rec_; + ulint* offsets_rec = NULL; + ulint* offsets_next_rec = NULL; n_cols = dict_index_get_n_unique(index); - n_diff = mem_alloc((n_cols + 1) * sizeof(ib_longlong)); + heap = mem_heap_create((sizeof *n_diff + sizeof *n_not_null) + * (n_cols + 1) + + dict_index_get_n_fields(index) + * (sizeof *offsets_rec + + sizeof *offsets_next_rec)); - memset(n_diff, 0, (n_cols + 1) * sizeof(ib_longlong)); + n_diff = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_longlong)); + + n_not_null = NULL; + + /* Check srv_innodb_stats_method setting, and decide whether we + need to record non-null value and also decide if NULL is + considered equal (by setting stats_null_not_equal value) */ + switch (srv_innodb_stats_method) { + case SRV_STATS_NULLS_IGNORED: + n_not_null = mem_heap_zalloc(heap, (n_cols + 1) + * sizeof *n_not_null); + /* fall through */ + + case SRV_STATS_NULLS_UNEQUAL: + /* for both SRV_STATS_NULLS_IGNORED and SRV_STATS_NULLS_UNEQUAL + case, we will treat NULLs as unequal value */ + stats_null_not_equal = TRUE; + break; + + case SRV_STATS_NULLS_EQUAL: + stats_null_not_equal = FALSE; + break; + + default: + ut_error; + } /* We sample some pages in the index to get an estimate */ for (i = 0; i < BTR_KEY_VAL_ESTIMATE_N_PAGES; i++) { - rec_t* supremum; mtr_start(&mtr); btr_cur_open_at_rnd_pos(index, BTR_SEARCH_LEAF, &cursor, &mtr); @@ -2888,18 +2965,22 @@ btr_estimate_number_of_different_key_vals( page = btr_cur_get_page(&cursor); - supremum = page_get_supremum_rec(page); rec = page_rec_get_next(page_get_infimum_rec(page)); - if (rec != supremum) { + if (!page_rec_is_supremum(rec)) { not_empty_flag = 1; offsets_rec = rec_get_offsets(rec, index, offsets_rec, ULINT_UNDEFINED, &heap); + + if (n_not_null) { + btr_record_not_null_field_in_rec( + rec, n_cols, offsets_rec, n_not_null); + } } - while (rec != supremum) { + while (!page_rec_is_supremum(rec)) { rec_t* next_rec = page_rec_get_next(rec); - if (next_rec == supremum) { + if (page_rec_is_supremum(next_rec)) { break; } @@ -2911,7 +2992,8 @@ btr_estimate_number_of_different_key_vals( cmp_rec_rec_with_match(rec, next_rec, offsets_rec, offsets_next_rec, - index, &matched_fields, + index, stats_null_not_equal, + &matched_fields, &matched_bytes); for (j = matched_fields + 1; j <= n_cols; j++) { @@ -2921,6 +3003,12 @@ btr_estimate_number_of_different_key_vals( n_diff[j]++; } + if (n_not_null) { + btr_record_not_null_field_in_rec( + next_rec, n_cols, offsets_next_rec, + n_not_null); + } + total_external_size += btr_rec_get_externally_stored_len( rec, offsets_rec); @@ -2971,14 +3059,8 @@ btr_estimate_number_of_different_key_vals( included in index->stat_n_leaf_pages) */ for (j = 0; j <= n_cols; j++) { - index->stat_n_diff_key_vals[j] - = ((n_diff[j] - * (ib_longlong)index->stat_n_leaf_pages - + BTR_KEY_VAL_ESTIMATE_N_PAGES - 1 - + total_external_size - + not_empty_flag) - / (BTR_KEY_VAL_ESTIMATE_N_PAGES - + total_external_size)); + index->stat_n_diff_key_vals[j] = BTR_TABLE_STATS_FROM_SAMPLE( + n_diff[j], index, total_external_size, not_empty_flag); /* If the tree is small, smaller than 10 * BTR_KEY_VAL_ESTIMATE_N_PAGES + total_external_size, then @@ -2997,12 +3079,20 @@ btr_estimate_number_of_different_key_vals( } index->stat_n_diff_key_vals[j] += add_on; + + /* Update the stat_n_non_null_key_vals[] with our + sampled result. stat_n_non_null_key_vals[] is created + and initialized to zero in dict_index_add_to_cache(), + along with stat_n_diff_key_vals[] array */ + if (n_not_null != NULL && (j < n_cols)) { + index->stat_n_non_null_key_vals[j] = + BTR_TABLE_STATS_FROM_SAMPLE( + n_not_null[j], index, + total_external_size, not_empty_flag); + } } - mem_free(n_diff); - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } + mem_heap_free(heap); } /*================== EXTERNAL STORAGE OF BIG FIELDS ===================*/ diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index fda6555e082..beea0a2f411 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -1358,6 +1358,12 @@ dict_index_add_to_cache( new_index->heap, (1 + dict_index_get_n_unique(new_index)) * sizeof(ib_longlong)); + + new_index->stat_n_non_null_key_vals = mem_heap_zalloc( + new_index->heap, + (1 + dict_index_get_n_unique(new_index)) + * sizeof(*new_index->stat_n_non_null_key_vals)); + /* Give some sensible values to stat_n_... in case we do not calculate statistics quickly enough */ @@ -3817,6 +3823,10 @@ dict_update_statistics_low( for (i = dict_index_get_n_unique(index); i; ) { index->stat_n_diff_key_vals[i--] = 1; } + + memset(index->stat_n_non_null_key_vals, 0, + (1 + dict_index_get_n_unique(index)) + * sizeof(*index->stat_n_non_null_key_vals)); } index = dict_table_get_next_index(index); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 4c52326a58a..6f58fd70fbd 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -130,6 +130,25 @@ static my_bool innobase_adaptive_hash_index = TRUE; static char* internal_innobase_data_file_path = NULL; +/* Possible values for system variable "innodb_stats_method". The values +are defined the same as its corresponding MyISAM system variable +"myisam_stats_method"(see "myisam_stats_method_names"), for better usability */ +static const char* innodb_stats_method_names[] = { + "nulls_equal", + "nulls_unequal", + "nulls_ignored", + NullS +}; + +/* Used to define an enumerate type of the system variable innodb_stats_method. +This is the same as "myisam_stats_method_typelib" */ +static TYPELIB innodb_stats_method_typelib = { + array_elements(innodb_stats_method_names) - 1, + "innodb_stats_method_typelib", + innodb_stats_method_names, + NULL +}; + /* The following counter is used to convey information to InnoDB about server activity: in selects it is not sensible to call srv_active_wake_master_thread after each fetch or search, we only do @@ -6362,6 +6381,65 @@ ha_innobase::read_time( return(ranges + (double) rows / (double) total_rows * time_for_scan); } +/************************************************************************* +Calculate Record Per Key value. Need to exclude the NULL value if +innodb_stats_method is set to "nulls_ignored" */ +static +ha_rows +innodb_rec_per_key( +/*===============*/ + /* out: estimated record per key + value */ + dict_index_t* index, /* in: dict_index_t structure */ + ulint i, /* in: the column we are + calculating rec per key */ + ha_rows records) /* in: estimated total records */ +{ + ha_rows rec_per_key; + + ut_ad(i < dict_index_get_n_unique(index)); + + /* Note the stat_n_diff_key_vals[] stores the diff value with + n-prefix indexing, so it is always stat_n_diff_key_vals[i + 1] */ + if (index->stat_n_diff_key_vals[i + 1] == 0) { + + rec_per_key = records; + } else if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED) { + ib_longlong num_null; + + /* Number of rows with NULL value in this + field */ + num_null = records - index->stat_n_non_null_key_vals[i]; + + /* In theory, index->stat_n_non_null_key_vals[i] + should always be less than the number of records. + Since this is statistics value, the value could + have slight discrepancy. But we will make sure + the number of null values is not a negative number. */ + num_null = (num_null < 0) ? 0 : num_null; + + /* If the number of NULL values is the same as or + large than that of the distinct values, we could + consider that the table consists mostly of NULL value. + Set rec_per_key to 1. */ + if (index->stat_n_diff_key_vals[i + 1] <= num_null) { + rec_per_key = 1; + } else { + /* Need to exclude rows with NULL values from + rec_per_key calculation */ + rec_per_key = (ha_rows)( + (records - num_null) + / (index->stat_n_diff_key_vals[i + 1] + - num_null)); + } + } else { + rec_per_key = (ha_rows) + (records / index->stat_n_diff_key_vals[i + 1]); + } + + return(rec_per_key); +} + /************************************************************************* Returns statistics information of the table to the MySQL interpreter, in various fields of the handle object. */ @@ -6568,13 +6646,8 @@ ha_innobase::info_low( break; } - if (index->stat_n_diff_key_vals[j + 1] == 0) { - - rec_per_key = stats.records; - } else { - rec_per_key = (ha_rows)(stats.records / - index->stat_n_diff_key_vals[j + 1]); - } + rec_per_key = innodb_rec_per_key( + index, j, stats.records); /* Since MySQL seems to favor table scans too much over index searches, we pretend @@ -8990,6 +9063,13 @@ static MYSQL_SYSVAR_LONG(autoinc_lock_mode, innobase_autoinc_lock_mode, AUTOINC_OLD_STYLE_LOCKING, /* Minimum value */ AUTOINC_NO_LOCKING, 0); /* Maximum value */ +static MYSQL_SYSVAR_ENUM(stats_method, srv_innodb_stats_method, + PLUGIN_VAR_RQCMDARG, + "Specifies how InnoDB index statistics collection code should " + "treat NULLs. Possible values are NULLS_EQUAL (default), " + "NULLS_UNEQUAL and NULLS_IGNORED", + NULL, NULL, SRV_STATS_NULLS_EQUAL, &innodb_stats_method_typelib); + #if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG static MYSQL_SYSVAR_UINT(change_buffering_debug, ibuf_debug, PLUGIN_VAR_RQCMDARG, @@ -9031,6 +9111,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(stats_on_metadata), MYSQL_SYSVAR(use_legacy_cardinality_algorithm), MYSQL_SYSVAR(adaptive_hash_index), + MYSQL_SYSVAR(stats_method), MYSQL_SYSVAR(status_file), MYSQL_SYSVAR(support_xa), MYSQL_SYSVAR(sync_spin_loops), diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index 213dcb7f568..20235c55f22 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -404,7 +404,10 @@ btr_estimate_n_rows_in_range( /*********************************************************************** Estimates the number of different key values in a given index, for each n-column prefix of the index where n <= dict_index_get_n_unique(index). -The estimates are stored in the array index->stat_n_diff_key_vals. */ +The estimates are stored in the array index->stat_n_diff_key_vals. +If innodb_stats_method is nulls_ignored, we also record the number of +non-null values for each prefix and stored the estimates in +array index->stat_n_non_null_key_vals. */ void btr_estimate_number_of_different_key_vals( diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 2f2a7441478..83dbf65ea41 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -222,6 +222,12 @@ struct dict_index_struct{ for this index, for each n-column prefix where n <= dict_get_n_unique(index); we periodically calculate new estimates */ + ib_longlong* stat_n_non_null_key_vals; + /* approximate number of non-null key values + for this index, for each column where + n < dict_get_n_unique(index); This + is used when innodb_stats_method is + "nulls_ignored". */ ulint stat_index_size; /* approximate index size in database pages */ ulint stat_n_leaf_pages; diff --git a/storage/innobase/include/rem0cmp.h b/storage/innobase/include/rem0cmp.h index c6a6e5de4db..22a22d13e17 100644 --- a/storage/innobase/include/rem0cmp.h +++ b/storage/innobase/include/rem0cmp.h @@ -141,6 +141,10 @@ cmp_rec_rec_with_match( const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ dict_index_t* index, /* in: data dictionary index */ + ibool nulls_unequal, + /* in: TRUE if this is for index statistics + cardinality estimation, and innodb_stats_method + is "nulls_unequal" or "nulls_ignored" */ ulint* matched_fields, /* in/out: number of already completely matched fields; when the function returns, contains the value the for current diff --git a/storage/innobase/include/rem0cmp.ic b/storage/innobase/include/rem0cmp.ic index 52dc7ff5dc9..45e12301a3c 100644 --- a/storage/innobase/include/rem0cmp.ic +++ b/storage/innobase/include/rem0cmp.ic @@ -72,5 +72,5 @@ cmp_rec_rec( ulint match_b = 0; return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index, - &match_f, &match_b)); + FALSE, &match_f, &match_b)); } diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 3dd4bb961f9..811074b2be8 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -91,6 +91,11 @@ extern ulint srv_lock_table_size; extern ulint srv_n_file_io_threads; +/* The "innodb_stats_method" setting, decides how InnoDB is going +to treat NULL value when collecting statistics. It is not defined +as enum type because the configure option takes unsigned integer type. */ +extern ulong srv_innodb_stats_method; + #ifdef UNIV_LOG_ARCHIVE extern ibool srv_log_archive_on; extern ibool srv_archive_recovery; @@ -286,6 +291,19 @@ of lower numbers are included. */ #define SRV_FORCE_NO_LOG_REDO 6 /* do not do the log roll-forward in connection with recovery */ +/* Alternatives for srv_innodb_stats_method, which could be changed by +setting innodb_stats_method */ +enum srv_stats_method_name_enum { + SRV_STATS_NULLS_EQUAL, /* All NULL values are treated as + equal. This is the default setting + for innodb_stats_method */ + SRV_STATS_NULLS_UNEQUAL, /* All NULL values are treated as + NOT equal. */ + SRV_STATS_NULLS_IGNORED /* NULL values are ignored */ +}; + +typedef enum srv_stats_method_name_enum srv_stats_method_name_t; + /************************************************************************* Boots Innobase server. */ diff --git a/storage/innobase/rem/rem0cmp.c b/storage/innobase/rem/rem0cmp.c index ca0ec663548..2939c119e2e 100644 --- a/storage/innobase/rem/rem0cmp.c +++ b/storage/innobase/rem/rem0cmp.c @@ -720,6 +720,10 @@ cmp_rec_rec_with_match( const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ dict_index_t* index, /* in: data dictionary index */ + ibool nulls_unequal, + /* in: TRUE if this is for index statistics + cardinality estimation, and innodb_stats_method + is "nulls_unequal" or "nulls_ignored" */ ulint* matched_fields, /* in/out: number of already completely matched fields; when the function returns, contains the value the for current @@ -821,9 +825,13 @@ cmp_rec_rec_with_match( || rec2_f_len == UNIV_SQL_NULL) { if (rec1_f_len == rec2_f_len) { - - goto next_field; - + /* This is limited to stats collection, + cannot use it for regular search */ + if (nulls_unequal) { + ret = -1; + } else { + goto next_field; + } } else if (rec2_f_len == UNIV_SQL_NULL) { /* We define the SQL null to be the diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 5b1184fb416..9c34e73109c 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -218,6 +218,11 @@ ulong srv_max_buf_pool_modified_pct = 90; /* variable counts amount of data read in total (in bytes) */ ulint srv_data_read = 0; +/* Internal setting for "innodb_stats_method". Decides how InnoDB treats +NULL value when collecting statistics. By default, it is set to +SRV_STATS_NULLS_EQUAL(0), ie. all NULL value are treated equal */ +ulong srv_innodb_stats_method = SRV_STATS_NULLS_EQUAL; + /* here we count the amount of data written in total (in bytes) */ ulint srv_data_written = 0; diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 8eb63fe8c78..43ffa762ddb 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,11 @@ +2011-01-14 The InnoDB Team + * btr/btr0cur.c, dict/dict0dict.c, handler/ha_innodb.cc, + include/btr0cur.h, include/dict0mem.h, include/rem0cmp.h, + include/rem0cmp.ic, include/srv0srv.h, rem/rem0cmp.c, + srv/srv0srv.c, innodb_bug30423.test: + Fix Bug#30423 InnoDBs treatment of NULL in index stats causes + bad "rows examined" estimates + 2011-01-06 The InnoDB Team * handler/i_s.cc, include/trx0i_s.h, trx/trx0i_s.c: Fix Bug#55397 cannot select from innodb_trx when trx_query contains diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index c57255a25ae..1fb0bc39933 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -100,6 +100,18 @@ can be released by page reorganize, then it is reorganized */ /*--------------------------------------*/ #define BTR_BLOB_HDR_SIZE 8 /*!< Size of a BLOB part header, in bytes */ + +/** Estimated table level stats from sampled value. +@param value sampled stats +@param index index being sampled +@param sample number of sampled rows +@param ext_size external stored data size +@param not_empty table not empty +@return estimated table wide stats from sampled value */ +#define BTR_TABLE_STATS_FROM_SAMPLE(value, index, sample, ext_size, not_empty)\ + (((value) * (ib_int64_t) index->stat_n_leaf_pages \ + + (sample) - 1 + (ext_size) + (not_empty)) / ((sample) + (ext_size))) + /* @} */ #endif /* !UNIV_HOTBACKUP */ @@ -3200,10 +3212,55 @@ btr_estimate_n_rows_in_range( } } +/*******************************************************************//** +Record the number of non_null key values in a given index for +each n-column prefix of the index where n < dict_index_get_n_unique(index). +The estimates are eventually stored in the array: +index->stat_n_non_null_key_vals. */ +static +void +btr_record_not_null_field_in_rec( +/*=============================*/ + rec_t* rec, /*!< in: physical record */ + ulint n_unique, /*!< in: dict_index_get_n_unique(index), + number of columns uniquely determine + an index entry */ + const ulint* offsets, /*!< in: rec_get_offsets(rec, index), + its size could be for all fields or + that of "n_unique" */ + ib_int64_t* n_not_null) /*!< in/out: array to record number of + not null rows for n-column prefix */ +{ + ulint i; + + ut_ad(rec_offs_n_fields(offsets) >= n_unique); + + if (n_not_null == NULL) { + return; + } + + for (i = 0; i < n_unique; i++) { + ulint rec_len; + byte* field; + + field = rec_get_nth_field(rec, offsets, i, &rec_len); + + if (rec_len != UNIV_SQL_NULL) { + n_not_null[i]++; + } else { + /* Break if we hit the first NULL value */ + break; + } + } +} + /*******************************************************************//** Estimates the number of different key values in a given index, for each n-column prefix of the index where n <= dict_index_get_n_unique(index). -The estimates are stored in the array index->stat_n_diff_key_vals. */ +The estimates are stored in the array index->stat_n_diff_key_vals. +If innodb_stats_method is "nulls_ignored", we also record the number of +non-null values for each prefix and store the estimates in +array index->stat_n_non_null_key_vals. */ UNIV_INTERN void btr_estimate_number_of_different_key_vals( @@ -3217,6 +3274,8 @@ btr_estimate_number_of_different_key_vals( ulint matched_fields; ulint matched_bytes; ib_int64_t* n_diff; + ib_int64_t* n_not_null; + ibool stats_null_not_equal; ullint n_sample_pages; /* number of pages to sample */ ulint not_empty_flag = 0; ulint total_external_size = 0; @@ -3225,16 +3284,43 @@ btr_estimate_number_of_different_key_vals( ullint add_on; mtr_t mtr; mem_heap_t* heap = NULL; - ulint offsets_rec_[REC_OFFS_NORMAL_SIZE]; - ulint offsets_next_rec_[REC_OFFS_NORMAL_SIZE]; - ulint* offsets_rec = offsets_rec_; - ulint* offsets_next_rec= offsets_next_rec_; - rec_offs_init(offsets_rec_); - rec_offs_init(offsets_next_rec_); + ulint* offsets_rec = NULL; + ulint* offsets_next_rec = NULL; n_cols = dict_index_get_n_unique(index); - n_diff = mem_zalloc((n_cols + 1) * sizeof(ib_int64_t)); + heap = mem_heap_create((sizeof *n_diff + sizeof *n_not_null) + * (n_cols + 1) + + dict_index_get_n_fields(index) + * (sizeof *offsets_rec + + sizeof *offsets_next_rec)); + + n_diff = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t)); + + n_not_null = NULL; + + /* Check srv_innodb_stats_method setting, and decide whether we + need to record non-null value and also decide if NULL is + considered equal (by setting stats_null_not_equal value) */ + switch (srv_innodb_stats_method) { + case SRV_STATS_NULLS_IGNORED: + n_not_null = mem_heap_zalloc(heap, (n_cols + 1) + * sizeof *n_not_null); + /* fall through */ + + case SRV_STATS_NULLS_UNEQUAL: + /* for both SRV_STATS_NULLS_IGNORED and SRV_STATS_NULLS_UNEQUAL + case, we will treat NULLs as unequal value */ + stats_null_not_equal = TRUE; + break; + + case SRV_STATS_NULLS_EQUAL: + stats_null_not_equal = FALSE; + break; + + default: + ut_error; + } /* It makes no sense to test more pages than are contained in the index, thus we lower the number if it is too high */ @@ -3251,7 +3337,6 @@ btr_estimate_number_of_different_key_vals( /* We sample some pages in the index to get an estimate */ for (i = 0; i < n_sample_pages; i++) { - rec_t* supremum; mtr_start(&mtr); btr_cur_open_at_rnd_pos(index, BTR_SEARCH_LEAF, &cursor, &mtr); @@ -3264,18 +3349,22 @@ btr_estimate_number_of_different_key_vals( page = btr_cur_get_page(&cursor); - supremum = page_get_supremum_rec(page); rec = page_rec_get_next(page_get_infimum_rec(page)); - if (rec != supremum) { + if (!page_rec_is_supremum(rec)) { not_empty_flag = 1; offsets_rec = rec_get_offsets(rec, index, offsets_rec, ULINT_UNDEFINED, &heap); + + if (n_not_null) { + btr_record_not_null_field_in_rec( + rec, n_cols, offsets_rec, n_not_null); + } } - while (rec != supremum) { + while (!page_rec_is_supremum(rec)) { rec_t* next_rec = page_rec_get_next(rec); - if (next_rec == supremum) { + if (page_rec_is_supremum(next_rec)) { break; } @@ -3287,7 +3376,8 @@ btr_estimate_number_of_different_key_vals( cmp_rec_rec_with_match(rec, next_rec, offsets_rec, offsets_next_rec, - index, &matched_fields, + index, stats_null_not_equal, + &matched_fields, &matched_bytes); for (j = matched_fields + 1; j <= n_cols; j++) { @@ -3297,6 +3387,12 @@ btr_estimate_number_of_different_key_vals( n_diff[j]++; } + if (n_not_null) { + btr_record_not_null_field_in_rec( + next_rec, n_cols, offsets_next_rec, + n_not_null); + } + total_external_size += btr_rec_get_externally_stored_len( rec, offsets_rec); @@ -3348,13 +3444,9 @@ btr_estimate_number_of_different_key_vals( for (j = 0; j <= n_cols; j++) { index->stat_n_diff_key_vals[j] - = ((n_diff[j] - * (ib_int64_t)index->stat_n_leaf_pages - + n_sample_pages - 1 - + total_external_size - + not_empty_flag) - / (n_sample_pages - + total_external_size)); + = BTR_TABLE_STATS_FROM_SAMPLE( + n_diff[j], index, n_sample_pages, + total_external_size, not_empty_flag); /* If the tree is small, smaller than 10 * n_sample_pages + total_external_size, then @@ -3373,12 +3465,20 @@ btr_estimate_number_of_different_key_vals( } index->stat_n_diff_key_vals[j] += add_on; + + /* Update the stat_n_non_null_key_vals[] with our + sampled result. stat_n_non_null_key_vals[] is created + and initialized to zero in dict_index_add_to_cache(), + along with stat_n_diff_key_vals[] array */ + if (n_not_null != NULL && (j < n_cols)) { + index->stat_n_non_null_key_vals[j] = + BTR_TABLE_STATS_FROM_SAMPLE( + n_not_null[j], index, n_sample_pages, + total_external_size, not_empty_flag); + } } - mem_free(n_diff); - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } + mem_heap_free(heap); } /*================== EXTERNAL STORAGE OF BIG FIELDS ===================*/ diff --git a/storage/innodb_plugin/dict/dict0dict.c b/storage/innodb_plugin/dict/dict0dict.c index 67765555658..ff56e9cb76a 100644 --- a/storage/innodb_plugin/dict/dict0dict.c +++ b/storage/innodb_plugin/dict/dict0dict.c @@ -1669,6 +1669,12 @@ undo_size_ok: new_index->heap, (1 + dict_index_get_n_unique(new_index)) * sizeof(ib_int64_t)); + + new_index->stat_n_non_null_key_vals = mem_heap_zalloc( + new_index->heap, + (1 + dict_index_get_n_unique(new_index)) + * sizeof(*new_index->stat_n_non_null_key_vals)); + /* Give some sensible values to stat_n_... in case we do not calculate statistics quickly enough */ @@ -4291,6 +4297,10 @@ dict_update_statistics( for (i = dict_index_get_n_unique(index); i; ) { index->stat_n_diff_key_vals[i--] = 1; } + + memset(index->stat_n_non_null_key_vals, 0, + (1 + dict_index_get_n_unique(index)) + * sizeof(*index->stat_n_non_null_key_vals)); } index = dict_table_get_next_index(index); diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 86168e2bc9b..2d60c7397b0 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -174,6 +174,25 @@ static char* internal_innobase_data_file_path = NULL; static char* innodb_version_str = (char*) INNODB_VERSION_STR; +/** Possible values for system variable "innodb_stats_method". The values +are defined the same as its corresponding MyISAM system variable +"myisam_stats_method"(see "myisam_stats_method_names"), for better usability */ +static const char* innodb_stats_method_names[] = { + "nulls_equal", + "nulls_unequal", + "nulls_ignored", + NullS +}; + +/** Used to define an enumerate type of the system variable innodb_stats_method. +This is the same as "myisam_stats_method_typelib" */ +static TYPELIB innodb_stats_method_typelib = { + array_elements(innodb_stats_method_names) - 1, + "innodb_stats_method_typelib", + innodb_stats_method_names, + NULL +}; + /* The following counter is used to convey information to InnoDB about server activity: in selects it is not sensible to call srv_active_wake_master_thread after each fetch or search, we only do @@ -7507,6 +7526,65 @@ innobase_get_mysql_key_number_for_index( return(0); } + +/*********************************************************************//** +Calculate Record Per Key value. Need to exclude the NULL value if +innodb_stats_method is set to "nulls_ignored" +@return estimated record per key value */ +static +ha_rows +innodb_rec_per_key( +/*===============*/ + dict_index_t* index, /*!< in: dict_index_t structure */ + ulint i, /*!< in: the column we are + calculating rec per key */ + ha_rows records) /*!< in: estimated total records */ +{ + ha_rows rec_per_key; + + ut_ad(i < dict_index_get_n_unique(index)); + + /* Note the stat_n_diff_key_vals[] stores the diff value with + n-prefix indexing, so it is always stat_n_diff_key_vals[i + 1] */ + if (index->stat_n_diff_key_vals[i + 1] == 0) { + + rec_per_key = records; + } else if (srv_innodb_stats_method == SRV_STATS_NULLS_IGNORED) { + ib_int64_t num_null; + + /* Number of rows with NULL value in this + field */ + num_null = records - index->stat_n_non_null_key_vals[i]; + + /* In theory, index->stat_n_non_null_key_vals[i] + should always be less than the number of records. + Since this is statistics value, the value could + have slight discrepancy. But we will make sure + the number of null values is not a negative number. */ + num_null = (num_null < 0) ? 0 : num_null; + + /* If the number of NULL values is the same as or + large than that of the distinct values, we could + consider that the table consists mostly of NULL value. + Set rec_per_key to 1. */ + if (index->stat_n_diff_key_vals[i + 1] <= num_null) { + rec_per_key = 1; + } else { + /* Need to exclude rows with NULL values from + rec_per_key calculation */ + rec_per_key = (ha_rows)( + (records - num_null) + / (index->stat_n_diff_key_vals[i + 1] + - num_null)); + } + } else { + rec_per_key = (ha_rows) + (records / index->stat_n_diff_key_vals[i + 1]); + } + + return(rec_per_key); +} + /*********************************************************************//** Returns statistics information of the table to the MySQL interpreter, in various fields of the handle object. */ @@ -7737,13 +7815,8 @@ ha_innobase::info_low( break; } - if (index->stat_n_diff_key_vals[j + 1] == 0) { - - rec_per_key = stats.records; - } else { - rec_per_key = (ha_rows)(stats.records / - index->stat_n_diff_key_vals[j + 1]); - } + rec_per_key = innodb_rec_per_key( + index, j, stats.records); /* Since MySQL seems to favor table scans too much over index searches, we pretend @@ -10934,6 +11007,13 @@ static MYSQL_SYSVAR_STR(change_buffering, innobase_change_buffering, innodb_change_buffering_validate, innodb_change_buffering_update, "inserts"); +static MYSQL_SYSVAR_ENUM(stats_method, srv_innodb_stats_method, + PLUGIN_VAR_RQCMDARG, + "Specifies how InnoDB index statistics collection code should " + "treat NULLs. Possible values are NULLS_EQUAL (default), " + "NULLS_UNEQUAL and NULLS_IGNORED", + NULL, NULL, SRV_STATS_NULLS_EQUAL, &innodb_stats_method_typelib); + #if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG static MYSQL_SYSVAR_UINT(change_buffering_debug, ibuf_debug, PLUGIN_VAR_RQCMDARG, @@ -10988,6 +11068,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(stats_on_metadata), MYSQL_SYSVAR(stats_sample_pages), MYSQL_SYSVAR(adaptive_hash_index), + MYSQL_SYSVAR(stats_method), MYSQL_SYSVAR(replication_delay), MYSQL_SYSVAR(status_file), MYSQL_SYSVAR(strict_mode), diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index b477ad0320a..cb8cb399715 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -478,7 +478,10 @@ btr_estimate_n_rows_in_range( /*******************************************************************//** Estimates the number of different key values in a given index, for each n-column prefix of the index where n <= dict_index_get_n_unique(index). -The estimates are stored in the array index->stat_n_diff_key_vals. */ +The estimates are stored in the array index->stat_n_diff_key_vals. +If innodb_stats_method is nulls_ignored, we also record the number of +non-null values for each prefix and stored the estimates in +array index->stat_n_non_null_key_vals. */ UNIV_INTERN void btr_estimate_number_of_different_key_vals( diff --git a/storage/innodb_plugin/include/dict0mem.h b/storage/innodb_plugin/include/dict0mem.h index 19782c2e76a..09a068ccb93 100644 --- a/storage/innodb_plugin/include/dict0mem.h +++ b/storage/innodb_plugin/include/dict0mem.h @@ -321,6 +321,12 @@ struct dict_index_struct{ dict_get_n_unique(index); we periodically calculate new estimates */ + ib_int64_t* stat_n_non_null_key_vals; + /* approximate number of non-null key values + for this index, for each column where + n < dict_get_n_unique(index); This + is used when innodb_stats_method is + "nulls_ignored". */ ulint stat_index_size; /*!< approximate index size in database pages */ diff --git a/storage/innodb_plugin/include/rem0cmp.h b/storage/innodb_plugin/include/rem0cmp.h index 2f751a38864..a908521c9f7 100644 --- a/storage/innodb_plugin/include/rem0cmp.h +++ b/storage/innodb_plugin/include/rem0cmp.h @@ -165,6 +165,10 @@ cmp_rec_rec_with_match( const ulint* offsets1,/*!< in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, index) */ dict_index_t* index, /*!< in: data dictionary index */ + ibool nulls_unequal, + /* in: TRUE if this is for index statistics + cardinality estimation, and innodb_stats_method + is "nulls_unequal" or "nulls_ignored" */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when the function returns, contains the value the for current diff --git a/storage/innodb_plugin/include/rem0cmp.ic b/storage/innodb_plugin/include/rem0cmp.ic index 39ef5f4fba3..63415fe7837 100644 --- a/storage/innodb_plugin/include/rem0cmp.ic +++ b/storage/innodb_plugin/include/rem0cmp.ic @@ -87,5 +87,5 @@ cmp_rec_rec( ulint match_b = 0; return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index, - &match_f, &match_b)); + FALSE, &match_f, &match_b)); } diff --git a/storage/innodb_plugin/include/srv0srv.h b/storage/innodb_plugin/include/srv0srv.h index 7aa2ce74720..91ae895040c 100644 --- a/storage/innodb_plugin/include/srv0srv.h +++ b/storage/innodb_plugin/include/srv0srv.h @@ -154,6 +154,11 @@ capacity. PCT_IO(5) -> returns the number of IO operations that is 5% of the max where max is srv_io_capacity. */ #define PCT_IO(p) ((ulong) (srv_io_capacity * ((double) p / 100.0))) +/* The "innodb_stats_method" setting, decides how InnoDB is going +to treat NULL value when collecting statistics. It is not defined +as enum type because the configure option takes unsigned integer type. */ +extern ulong srv_innodb_stats_method; + #ifdef UNIV_LOG_ARCHIVE extern ibool srv_log_archive_on; extern ibool srv_archive_recovery; @@ -363,6 +368,19 @@ enum { in connection with recovery */ }; +/* Alternatives for srv_innodb_stats_method, which could be changed by +setting innodb_stats_method */ +enum srv_stats_method_name_enum { + SRV_STATS_NULLS_EQUAL, /* All NULL values are treated as + equal. This is the default setting + for innodb_stats_method */ + SRV_STATS_NULLS_UNEQUAL, /* All NULL values are treated as + NOT equal. */ + SRV_STATS_NULLS_IGNORED /* NULL values are ignored */ +}; + +typedef enum srv_stats_method_name_enum srv_stats_method_name_t; + #ifndef UNIV_HOTBACKUP /** Types of threads existing in the system. */ enum srv_thread_type { diff --git a/storage/innodb_plugin/rem/rem0cmp.c b/storage/innodb_plugin/rem/rem0cmp.c index 35b67992558..04d2c15437b 100644 --- a/storage/innodb_plugin/rem/rem0cmp.c +++ b/storage/innodb_plugin/rem/rem0cmp.c @@ -862,6 +862,10 @@ cmp_rec_rec_with_match( const ulint* offsets1,/*!< in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, index) */ dict_index_t* index, /*!< in: data dictionary index */ + ibool nulls_unequal, + /* in: TRUE if this is for index statistics + cardinality estimation, and innodb_stats_method + is "nulls_unequal" or "nulls_ignored" */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when the function returns, contains the value the for current @@ -961,9 +965,13 @@ cmp_rec_rec_with_match( || rec2_f_len == UNIV_SQL_NULL) { if (rec1_f_len == rec2_f_len) { - - goto next_field; - + /* This is limited to stats collection, + cannot use it for regular search */ + if (nulls_unequal) { + ret = -1; + } else { + goto next_field; + } } else if (rec2_f_len == UNIV_SQL_NULL) { /* We define the SQL null to be the diff --git a/storage/innodb_plugin/srv/srv0srv.c b/storage/innodb_plugin/srv/srv0srv.c index f7e7e351bdc..3cf17f33c40 100644 --- a/storage/innodb_plugin/srv/srv0srv.c +++ b/storage/innodb_plugin/srv/srv0srv.c @@ -243,6 +243,11 @@ UNIV_INTERN ulong srv_max_buf_pool_modified_pct = 75; /* variable counts amount of data read in total (in bytes) */ UNIV_INTERN ulint srv_data_read = 0; +/* Internal setting for "innodb_stats_method". Decides how InnoDB treats +NULL value when collecting statistics. By default, it is set to +SRV_STATS_NULLS_EQUAL(0), ie. all NULL value are treated equal */ +ulong srv_innodb_stats_method = SRV_STATS_NULLS_EQUAL; + /* here we count the amount of data written in total (in bytes) */ UNIV_INTERN ulint srv_data_written = 0; From 1f3975b4f8b22eef97b2d86b8ecbc17c90c5f1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 17 Jan 2011 14:06:48 +0200 Subject: [PATCH 04/42] Non-functional changes. Remove the unused data type dict_cluster_t. Remove a bogus comment about latching order. --- storage/innobase/include/dict0types.h | 5 ----- storage/innobase/include/trx0rseg.h | 4 +--- storage/innodb_plugin/include/dict0types.h | 5 ----- storage/innodb_plugin/include/trx0rseg.h | 4 +--- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/storage/innobase/include/dict0types.h b/storage/innobase/include/dict0types.h index b90545f2105..6674b5ff397 100644 --- a/storage/innobase/include/dict0types.h +++ b/storage/innobase/include/dict0types.h @@ -16,11 +16,6 @@ typedef struct dict_index_struct dict_index_t; typedef struct dict_table_struct dict_table_t; typedef struct dict_foreign_struct dict_foreign_t; -/* A cluster object is a table object with the type field set to -DICT_CLUSTERED */ - -typedef dict_table_t dict_cluster_t; - typedef struct ind_node_struct ind_node_t; typedef struct tab_node_struct tab_node_t; diff --git a/storage/innobase/include/trx0rseg.h b/storage/innobase/include/trx0rseg.h index 46ba010bd1d..22f8aa89181 100644 --- a/storage/innobase/include/trx0rseg.h +++ b/storage/innobase/include/trx0rseg.h @@ -121,9 +121,7 @@ struct trx_rseg_struct{ ulint id; /* rollback segment id == the index of its slot in the trx system file copy */ mutex_t mutex; /* mutex protecting the fields in this - struct except id; NOTE that the latching - order must always be kernel mutex -> - rseg mutex */ + struct except id, which is constant */ ulint space; /* space where the rollback segment is header is placed */ ulint page_no;/* page number of the rollback segment diff --git a/storage/innodb_plugin/include/dict0types.h b/storage/innodb_plugin/include/dict0types.h index 7ad69193cc9..f14b59a19d4 100644 --- a/storage/innodb_plugin/include/dict0types.h +++ b/storage/innodb_plugin/include/dict0types.h @@ -33,11 +33,6 @@ typedef struct dict_index_struct dict_index_t; typedef struct dict_table_struct dict_table_t; typedef struct dict_foreign_struct dict_foreign_t; -/* A cluster object is a table object with the type field set to -DICT_CLUSTERED */ - -typedef dict_table_t dict_cluster_t; - typedef struct ind_node_struct ind_node_t; typedef struct tab_node_struct tab_node_t; diff --git a/storage/innodb_plugin/include/trx0rseg.h b/storage/innodb_plugin/include/trx0rseg.h index a25d84f1e84..e3674089735 100644 --- a/storage/innodb_plugin/include/trx0rseg.h +++ b/storage/innodb_plugin/include/trx0rseg.h @@ -135,9 +135,7 @@ struct trx_rseg_struct{ ulint id; /*!< rollback segment id == the index of its slot in the trx system file copy */ mutex_t mutex; /*!< mutex protecting the fields in this - struct except id; NOTE that the latching - order must always be kernel mutex -> - rseg mutex */ + struct except id, which is constant */ ulint space; /*!< space where the rollback segment is header is placed */ ulint zip_size;/* compressed page size of space From 359bddbee1a27864a38195e85fceab8a1678081d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 18 Jan 2011 12:25:13 +0200 Subject: [PATCH 05/42] Bug#59579 rw_lock_debug_print outputs to stderr rw_lock_debug_print(): Add parameter FILE* for specifying the output stream. rw_lock_list_print_info(): Invoke rw_lock_debug_print() on file, not stderr. --- storage/innobase/include/sync0rw.h | 3 ++- storage/innobase/sync/sync0arr.c | 4 ++-- storage/innobase/sync/sync0rw.c | 19 ++++++++++--------- storage/innodb_plugin/ChangeLog | 6 ++++++ storage/innodb_plugin/include/sync0rw.h | 3 ++- storage/innodb_plugin/sync/sync0arr.c | 4 ++-- storage/innodb_plugin/sync/sync0rw.c | 19 ++++++++++--------- 7 files changed, 34 insertions(+), 24 deletions(-) diff --git a/storage/innobase/include/sync0rw.h b/storage/innobase/include/sync0rw.h index 008df80a2c7..dd898557d6e 100644 --- a/storage/innobase/include/sync0rw.h +++ b/storage/innobase/include/sync0rw.h @@ -1,7 +1,7 @@ /****************************************************** The read-write lock (for threads, not for database transactions) -(c) 1995 Innobase Oy +Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. Created 9/11/1995 Heikki Tuuri *******************************************************/ @@ -409,6 +409,7 @@ Prints info of a debug struct. */ void rw_lock_debug_print( /*================*/ + FILE* f, /* in: output stream */ rw_lock_debug_t* info); /* in: debug struct */ #endif /* UNIV_SYNC_DEBUG */ diff --git a/storage/innobase/sync/sync0arr.c b/storage/innobase/sync/sync0arr.c index 154593a9035..41d3492c8c9 100644 --- a/storage/innobase/sync/sync0arr.c +++ b/storage/innobase/sync/sync0arr.c @@ -1,7 +1,7 @@ /****************************************************** The wait array used in synchronization primitives -(c) 1995 Innobase Oy +Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. Created 9/5/1995 Heikki Tuuri *******************************************************/ @@ -709,7 +709,7 @@ print: fprintf(stderr, "rw-lock %p ", (void*) lock); sync_array_cell_print(stderr, cell); - rw_lock_debug_print(debug); + rw_lock_debug_print(stderr, debug); return(TRUE); } } diff --git a/storage/innobase/sync/sync0rw.c b/storage/innobase/sync/sync0rw.c index 0b05fb826ac..ef4c07e8c26 100644 --- a/storage/innobase/sync/sync0rw.c +++ b/storage/innobase/sync/sync0rw.c @@ -1,7 +1,7 @@ /****************************************************** The read-write lock (for thread synchronization) -(c) 1995 Innobase Oy +Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. Created 9/11/1995 Heikki Tuuri *******************************************************/ @@ -830,7 +830,7 @@ rw_lock_list_print_info( info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { - rw_lock_debug_print(info); + rw_lock_debug_print(file, info); info = UT_LIST_GET_NEXT(list, info); } } @@ -870,7 +870,7 @@ rw_lock_print( info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { - rw_lock_debug_print(info); + rw_lock_debug_print(stderr, info); info = UT_LIST_GET_NEXT(list, info); } } @@ -882,28 +882,29 @@ Prints info of a debug struct. */ void rw_lock_debug_print( /*================*/ + FILE* f, /* in: output stream */ rw_lock_debug_t* info) /* in: debug struct */ { ulint rwt; rwt = info->lock_type; - fprintf(stderr, "Locked: thread %lu file %s line %lu ", + fprintf(f, "Locked: thread %lu file %s line %lu ", (ulong) os_thread_pf(info->thread_id), info->file_name, (ulong) info->line); if (rwt == RW_LOCK_SHARED) { - fputs("S-LOCK", stderr); + fputs("S-LOCK", f); } else if (rwt == RW_LOCK_EX) { - fputs("X-LOCK", stderr); + fputs("X-LOCK", f); } else if (rwt == RW_LOCK_WAIT_EX) { - fputs("WAIT X-LOCK", stderr); + fputs("WAIT X-LOCK", f); } else { ut_error; } if (info->pass != 0) { - fprintf(stderr, " pass value %lu", (ulong) info->pass); + fprintf(f, " pass value %lu", (ulong) info->pass); } - putc('\n', stderr); + putc('\n', f); } /******************************************************************* diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 43ffa762ddb..4d35bcff4a1 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-01-18 The InnoDB Team + + * include/sync0rw.h, sync/sync0arr.c, sync/sync0rw.c: + Fix Bug#59579 rw_lock_debug_print outputs to stderr, not to + SHOW ENGINE INNODB STATUS + 2011-01-14 The InnoDB Team * btr/btr0cur.c, dict/dict0dict.c, handler/ha_innodb.cc, include/btr0cur.h, include/dict0mem.h, include/rem0cmp.h, diff --git a/storage/innodb_plugin/include/sync0rw.h b/storage/innodb_plugin/include/sync0rw.h index 175f3deb77c..47f7dbfe0eb 100644 --- a/storage/innodb_plugin/include/sync0rw.h +++ b/storage/innodb_plugin/include/sync0rw.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by @@ -490,6 +490,7 @@ UNIV_INTERN void rw_lock_debug_print( /*================*/ + FILE* f, /*!< in: output stream */ rw_lock_debug_t* info); /*!< in: debug struct */ #endif /* UNIV_SYNC_DEBUG */ diff --git a/storage/innodb_plugin/sync/sync0arr.c b/storage/innodb_plugin/sync/sync0arr.c index 3c825e2202b..ad29b90d344 100644 --- a/storage/innodb_plugin/sync/sync0arr.c +++ b/storage/innodb_plugin/sync/sync0arr.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by @@ -715,7 +715,7 @@ print: fprintf(stderr, "rw-lock %p ", (void*) lock); sync_array_cell_print(stderr, cell); - rw_lock_debug_print(debug); + rw_lock_debug_print(stderr, debug); return(TRUE); } } diff --git a/storage/innodb_plugin/sync/sync0rw.c b/storage/innodb_plugin/sync/sync0rw.c index 572c3690a7f..00e0324becd 100644 --- a/storage/innodb_plugin/sync/sync0rw.c +++ b/storage/innodb_plugin/sync/sync0rw.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by @@ -925,7 +925,7 @@ rw_lock_list_print_info( info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { - rw_lock_debug_print(info); + rw_lock_debug_print(file, info); info = UT_LIST_GET_NEXT(list, info); } } @@ -973,7 +973,7 @@ rw_lock_print( info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { - rw_lock_debug_print(info); + rw_lock_debug_print(stderr, info); info = UT_LIST_GET_NEXT(list, info); } } @@ -985,28 +985,29 @@ UNIV_INTERN void rw_lock_debug_print( /*================*/ + FILE* f, /*!< in: output stream */ rw_lock_debug_t* info) /*!< in: debug struct */ { ulint rwt; rwt = info->lock_type; - fprintf(stderr, "Locked: thread %lu file %s line %lu ", + fprintf(f, "Locked: thread %lu file %s line %lu ", (ulong) os_thread_pf(info->thread_id), info->file_name, (ulong) info->line); if (rwt == RW_LOCK_SHARED) { - fputs("S-LOCK", stderr); + fputs("S-LOCK", f); } else if (rwt == RW_LOCK_EX) { - fputs("X-LOCK", stderr); + fputs("X-LOCK", f); } else if (rwt == RW_LOCK_WAIT_EX) { - fputs("WAIT X-LOCK", stderr); + fputs("WAIT X-LOCK", f); } else { ut_error; } if (info->pass != 0) { - fprintf(stderr, " pass value %lu", (ulong) info->pass); + fprintf(f, " pass value %lu", (ulong) info->pass); } - putc('\n', stderr); + putc('\n', f); } /***************************************************************//** From d33fbe44ffed64c54203aece512a7c2c6a7abece Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Tue, 18 Jan 2011 23:55:39 +0100 Subject: [PATCH 06/42] Bug#59418: parts suite have several failures with --embedded-server The test were using external tools not available for embedded. Fixed by rewriting the test to not rely on external tools like the mysql-client Also fixed some non portable --exec commands and replaced #p# to #P# to pass on windows. --- mysql-test/suite/parts/inc/partition_check_drop.inc | 4 ++-- .../suite/parts/inc/partition_layout_check1.inc | 13 +++++-------- .../suite/parts/inc/partition_layout_check2.inc | 7 +++---- .../suite/parts/r/partition_recover_myisam.result | 2 +- .../suite/parts/t/partition_debug_sync_innodb.test | 2 ++ .../suite/parts/t/partition_recover_myisam.test | 6 +++++- .../suite/parts/t/partition_special_innodb.test | 4 ++-- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/mysql-test/suite/parts/inc/partition_check_drop.inc b/mysql-test/suite/parts/inc/partition_check_drop.inc index daaa5e541c7..137de4165a0 100644 --- a/mysql-test/suite/parts/inc/partition_check_drop.inc +++ b/mysql-test/suite/parts/inc/partition_check_drop.inc @@ -66,10 +66,10 @@ if ($found_garbage) } # Do a manual cleanup, because the following tests should not suffer from # remaining files - --exec rm -f $MYSQLD_DATADIR/test/t1* || true + --remove_files_wildcard $MYSQLD_DATADIR/test t1* if ($with_directories) { - --exec rm -f $MYSQLTEST_VARDIR/tmp/t1* || true + --remove_files_wildcard $MYSQLTEST_VARDIR/tmp t1* } } --enable_query_log diff --git a/mysql-test/suite/parts/inc/partition_layout_check1.inc b/mysql-test/suite/parts/inc/partition_layout_check1.inc index bca41b6f9c7..66a93d5d8d7 100644 --- a/mysql-test/suite/parts/inc/partition_layout_check1.inc +++ b/mysql-test/suite/parts/inc/partition_layout_check1.inc @@ -29,14 +29,10 @@ DELETE FROM t0_definition; let $MYSQLD_DATADIR= `select LEFT(@@datadir, LENGTH(@@datadir)-1)`; #echo MYSQLD_DATADIR: $MYSQLD_DATADIR; -# Dump the current definition of the table t1 to tmp1 -# This complicated method - let another mysqltest collect the output - is used -# because of two reasons +# Save the current definition of the table t1 # - SHOW CREATE TABLE t1 is at least currently most probably more reliable than # the corresponding SELECT on the INFORMATION_SCHEMA -# - SHOW CREATE TABLE .. cannot write its out put into a file like SELECT -let $show_file= $MYSQLD_DATADIR/test/tmp1; ---exec echo "SHOW CREATE TABLE t1; exit; " | $MYSQL_TEST > $show_file 2>&1 +let $show_create= `SHOW CREATE TABLE t1`; if ($do_file_tests) { # List the files belonging to the table t1 @@ -57,12 +53,13 @@ if (!$do_file_tests) # Insert the current definition of the table t1 into t0_definition eval INSERT INTO t0_definition SET state = 'old', - create_command = load_file('$show_file'), + create_command = "$show_create", file_list = @aux; # Print the create table statement into the protocol +# Added the concat to avoid changing the result files --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR '\r' '' -SELECT create_command FROM t0_definition WHERE state = 'old'; +SELECT concat('SHOW CREATE TABLE t1;\nTable\tCreate Table\n',create_command,'\n') as `create_command` FROM t0_definition WHERE state = 'old'; if ($do_file_tests) { # We stored the list of files, therefore printing the content makes sense diff --git a/mysql-test/suite/parts/inc/partition_layout_check2.inc b/mysql-test/suite/parts/inc/partition_layout_check2.inc index 7ff871a4c45..2f74455a811 100644 --- a/mysql-test/suite/parts/inc/partition_layout_check2.inc +++ b/mysql-test/suite/parts/inc/partition_layout_check2.inc @@ -28,9 +28,8 @@ DELETE FROM t0_definition WHERE state = 'new'; let $MYSQLD_DATADIR= `select LEFT(@@datadir, LENGTH(@@datadir)-1)`; #echo MYSQLD_DATADIR: $MYSQLD_DATADIR; -# Dump the current definition of the table t1 to tmp1 -let $show_file= $MYSQLD_DATADIR/test/tmp1; ---exec echo "SHOW CREATE TABLE t1; exit; " | $MYSQL_TEST > $show_file 2>&1 +# Save the current definition of the table t1 +let $show_create= `SHOW CREATE TABLE t1`; if ($do_file_tests) { @@ -52,7 +51,7 @@ if (!$do_file_tests) # Insert the current definition of the table t1 into t0_definition eval INSERT INTO t0_definition SET state = 'new', - create_command = load_file('$show_file'), + create_command = "$show_create", file_list = @aux; # Print the old and new table layout, if they differ diff --git a/mysql-test/suite/parts/r/partition_recover_myisam.result b/mysql-test/suite/parts/r/partition_recover_myisam.result index 49775ee498e..cf3ccaa73b3 100644 --- a/mysql-test/suite/parts/r/partition_recover_myisam.result +++ b/mysql-test/suite/parts/r/partition_recover_myisam.result @@ -1,4 +1,4 @@ -call mtr.add_suppression("./test/t1_will_crash"); +call mtr.add_suppression("t1_will_crash"); call mtr.add_suppression("Got an error from unknown thread, ha_myisam.cc"); CREATE TABLE t1_will_crash (a INT, KEY (a)) ENGINE=MyISAM; INSERT INTO t1_will_crash VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11); diff --git a/mysql-test/suite/parts/t/partition_debug_sync_innodb.test b/mysql-test/suite/parts/t/partition_debug_sync_innodb.test index 79ef3d537bf..e9abe134d84 100644 --- a/mysql-test/suite/parts/t/partition_debug_sync_innodb.test +++ b/mysql-test/suite/parts/t/partition_debug_sync_innodb.test @@ -14,6 +14,7 @@ partition by range (a) insert into t1 values (1), (11), (21), (33); SELECT * FROM t1; SHOW CREATE TABLE t1; +--replace_result #p# #P# --list_files $MYSQLD_DATADIR/test SET DEBUG_SYNC='before_open_in_get_all_tables SIGNAL parked WAIT_FOR open'; @@ -36,6 +37,7 @@ ALTER TABLE t1 REORGANIZE PARTITION p0 INTO disconnect con1; connection default; --reap +--replace_result #p# #P# --list_files $MYSQLD_DATADIR/test SHOW CREATE TABLE t1; SELECT * FROM t1; diff --git a/mysql-test/suite/parts/t/partition_recover_myisam.test b/mysql-test/suite/parts/t/partition_recover_myisam.test index 64bc821ac37..043ec16a9f4 100644 --- a/mysql-test/suite/parts/t/partition_recover_myisam.test +++ b/mysql-test/suite/parts/t/partition_recover_myisam.test @@ -1,6 +1,6 @@ # test the auto-recover (--myisam-recover) of partitioned myisam tables -call mtr.add_suppression("./test/t1_will_crash"); +call mtr.add_suppression("t1_will_crash"); call mtr.add_suppression("Got an error from unknown thread, ha_myisam.cc"); --source include/have_partition.inc @@ -20,6 +20,8 @@ FLUSH TABLES; let $MYSQLD_DATADIR= `select @@datadir`; --remove_file $MYSQLD_DATADIR/test/t1_will_crash.MYI --copy_file std_data/corrupt_t1.MYI $MYSQLD_DATADIR/test/t1_will_crash.MYI +--replace_result \\ / +--replace_regex /Table '.*data/Table './ SELECT * FROM t1_will_crash; DROP TABLE t1_will_crash; CREATE TABLE t1_will_crash (a INT, KEY (a)) @@ -33,5 +35,7 @@ FLUSH TABLES; --echo # head -c1024 t1#P#p1.MYI > corrupt_t1#P#p1.MYI --remove_file $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYI --copy_file std_data/corrupt_t1#P#p1.MYI $MYSQLD_DATADIR/test/t1_will_crash#P#p1.MYI +--replace_result \\ / +--replace_regex /Table '.*data/Table './ SELECT * FROM t1_will_crash; DROP TABLE t1_will_crash; diff --git a/mysql-test/suite/parts/t/partition_special_innodb.test b/mysql-test/suite/parts/t/partition_special_innodb.test index eac19f6d588..f237b02f331 100644 --- a/mysql-test/suite/parts/t/partition_special_innodb.test +++ b/mysql-test/suite/parts/t/partition_special_innodb.test @@ -58,8 +58,8 @@ ENGINE = InnoDB PARTITION BY HASH (a) PARTITIONS 2; -connect (con1,127.0.0.1,root,,test,$MASTER_MYPORT,); -connect (con2,127.0.0.1,root,,test,$MASTER_MYPORT,); +connect (con1, localhost, root,,); +connect (con2, localhost, root,,); --connection con1 SET autocommit=OFF; From a9d18719f639c61632ccc1feb059e927ab8e6b10 Mon Sep 17 00:00:00 2001 From: Sandeep Doddaballapur Date: Tue, 25 Jan 2011 12:14:28 +0530 Subject: [PATCH 07/42] --- mysql-test/r/csv_not_null.result | 3 +++ mysql-test/t/csv_not_null.test | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/csv_not_null.result b/mysql-test/r/csv_not_null.result index af583a36837..aed9bcb1587 100644 --- a/mysql-test/r/csv_not_null.result +++ b/mysql-test/r/csv_not_null.result @@ -19,13 +19,16 @@ INSERT INTO t1 VALUES(); SELECT * FROM t1; a b c d e f 0 foo 0000-00-00 +INSERT INTO t1 VALUES(default,default,default,default,default,default); SELECT * FROM t1; a b c d e f 0 foo 0000-00-00 +0 foo 0000-00-00 INSERT INTO t1 VALUES(0,'abc','def','ghi','bar','1999-12-31'); SELECT * FROM t1; a b c d e f 0 foo 0000-00-00 +0 foo 0000-00-00 0 abc def ghi bar 1999-12-31 # === insert failures === INSERT INTO t1 VALUES(NULL,'ab','a','b','foo','2007-01-01'); diff --git a/mysql-test/t/csv_not_null.test b/mysql-test/t/csv_not_null.test index 03ed566fb22..bebea53b2f7 100644 --- a/mysql-test/t/csv_not_null.test +++ b/mysql-test/t/csv_not_null.test @@ -55,10 +55,8 @@ INSERT INTO t1 VALUES(); SELECT * FROM t1; -- disable_warnings -# NOTE - Test disabled due to enum crash for this INSERT -# See Bug#33717 - INSERT...(default) fails for enum. -# Crashes CSV tables, loads spaces for MyISAM -#INSERT INTO t1 VALUES(default,default,default,default,default,default); +# Bug#33717 - INSERT...(default) fails for enum. +INSERT INTO t1 VALUES(default,default,default,default,default,default); -- enable_warnings SELECT * FROM t1; From 60a622d1c1940f80829a4df312ff49a6feae265e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 25 Jan 2011 09:56:18 +0200 Subject: [PATCH 08/42] Bug#59707 Unused compression-related parameters in buffer pool functions buf_block_alloc(): ulint zip_size is always 0. buf_LRU_get_free_block(): ulint zip_size is always 0. buf_LRU_free_block(): ibool* buf_pool_mutex_released is always NULL. Remove these parameters. buf_LRU_get_free_block(): Simplify the initialization of block->page.zip and release buf_pool_mutex() earlier. --- storage/innodb_plugin/ChangeLog | 9 +++++ storage/innodb_plugin/btr/btr0btr.c | 2 +- storage/innodb_plugin/btr/btr0cur.c | 5 ++- storage/innodb_plugin/btr/btr0sea.c | 2 +- storage/innodb_plugin/buf/buf0buddy.c | 2 +- storage/innodb_plugin/buf/buf0buf.c | 14 ++++---- storage/innodb_plugin/buf/buf0lru.c | 44 +++++------------------- storage/innodb_plugin/include/buf0buf.h | 6 ++-- storage/innodb_plugin/include/buf0buf.ic | 8 ++--- storage/innodb_plugin/include/buf0lru.h | 14 +++----- storage/innodb_plugin/mem/mem0mem.c | 2 +- storage/innodb_plugin/page/page0zip.c | 2 +- 12 files changed, 40 insertions(+), 70 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 4d35bcff4a1..24cac7ac2be 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,12 @@ +2011-01-25 The InnoDB Team + + * btr/btr0btr.c, btr/btr0cur.c, btr/btr0sea.c, + buf/buf0buddy.c, buf/buf0buf.c, buf/buf0lru.c, + include/buf0buf.h, include/buf0buf.ic, include/buf0lru.h, + mem/mem0mem.c, page/page0zip.c: + Fix Bug#59707 Unused compression-related parameters + in buffer pool functions + 2011-01-18 The InnoDB Team * include/sync0rw.h, sync/sync0arr.c, sync/sync0rw.c: diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index 32e2caecdb8..3d8d6048603 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -979,7 +979,7 @@ btr_page_reorganize_low( log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); #ifndef UNIV_HOTBACKUP - temp_block = buf_block_alloc(0); + temp_block = buf_block_alloc(); #else /* !UNIV_HOTBACKUP */ ut_ad(block == back_block1); temp_block = back_block2; diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 1fb0bc39933..f41b125b281 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -3767,13 +3767,12 @@ btr_blob_free( && buf_block_get_space(block) == space && buf_block_get_page_no(block) == page_no) { - if (buf_LRU_free_block(&block->page, all, NULL) - != BUF_LRU_FREED + if (buf_LRU_free_block(&block->page, all) != BUF_LRU_FREED && all && block->page.zip.data) { /* Attempt to deallocate the uncompressed page if the whole block cannot be deallocted. */ - buf_LRU_free_block(&block->page, FALSE, NULL); + buf_LRU_free_block(&block->page, FALSE); } } diff --git a/storage/innodb_plugin/btr/btr0sea.c b/storage/innodb_plugin/btr/btr0sea.c index 035fdbb61d2..9835efcf712 100644 --- a/storage/innodb_plugin/btr/btr0sea.c +++ b/storage/innodb_plugin/btr/btr0sea.c @@ -141,7 +141,7 @@ btr_search_check_free_space_in_heap(void) be enough free space in the hash table. */ if (heap->free_block == NULL) { - buf_block_t* block = buf_block_alloc(0); + buf_block_t* block = buf_block_alloc(); rw_lock_x_lock(&btr_search_latch); diff --git a/storage/innodb_plugin/buf/buf0buddy.c b/storage/innodb_plugin/buf/buf0buddy.c index ee5a569c3ff..63c99571510 100644 --- a/storage/innodb_plugin/buf/buf0buddy.c +++ b/storage/innodb_plugin/buf/buf0buddy.c @@ -327,7 +327,7 @@ buf_buddy_alloc_low( /* Try replacing an uncompressed page in the buffer pool. */ buf_pool_mutex_exit(); - block = buf_LRU_get_free_block(0); + block = buf_LRU_get_free_block(); *lru = TRUE; buf_pool_mutex_enter(); diff --git a/storage/innodb_plugin/buf/buf0buf.c b/storage/innodb_plugin/buf/buf0buf.c index dac416f9472..6e76e4c65be 100644 --- a/storage/innodb_plugin/buf/buf0buf.c +++ b/storage/innodb_plugin/buf/buf0buf.c @@ -1283,7 +1283,7 @@ shrink_again: buf_LRU_make_block_old(&block->page); dirty++; - } else if (buf_LRU_free_block(&block->page, TRUE, NULL) + } else if (buf_LRU_free_block(&block->page, TRUE) != BUF_LRU_FREED) { nonfree++; } @@ -1729,8 +1729,7 @@ err_exit: mutex_enter(block_mutex); /* Discard the uncompressed page frame if possible. */ - if (buf_LRU_free_block(bpage, FALSE, NULL) - == BUF_LRU_FREED) { + if (buf_LRU_free_block(bpage, FALSE) == BUF_LRU_FREED) { mutex_exit(block_mutex); goto lookup; @@ -2165,7 +2164,7 @@ wait_until_unfixed: buf_pool_mutex_exit(); mutex_exit(&buf_pool_zip_mutex); - block = buf_LRU_get_free_block(0); + block = buf_LRU_get_free_block(); ut_a(block); buf_pool_mutex_enter(); @@ -2291,8 +2290,7 @@ wait_until_unfixed: /* Try to evict the block from the buffer pool, to use the insert buffer as much as possible. */ - if (buf_LRU_free_block(&block->page, TRUE, NULL) - == BUF_LRU_FREED) { + if (buf_LRU_free_block(&block->page, TRUE) == BUF_LRU_FREED) { buf_pool_mutex_exit(); mutex_exit(&block->mutex); fprintf(stderr, @@ -2829,7 +2827,7 @@ buf_page_init_for_read( && UNIV_LIKELY(!recv_recovery_is_on())) { block = NULL; } else { - block = buf_LRU_get_free_block(0); + block = buf_LRU_get_free_block(); ut_ad(block); } @@ -3001,7 +2999,7 @@ buf_page_create( ut_ad(mtr->state == MTR_ACTIVE); ut_ad(space || !zip_size); - free_block = buf_LRU_get_free_block(0); + free_block = buf_LRU_get_free_block(); buf_pool_mutex_enter(); diff --git a/storage/innodb_plugin/buf/buf0lru.c b/storage/innodb_plugin/buf/buf0lru.c index e4cf218bf2e..39feb06ff23 100644 --- a/storage/innodb_plugin/buf/buf0lru.c +++ b/storage/innodb_plugin/buf/buf0lru.c @@ -575,7 +575,7 @@ buf_LRU_free_from_unzip_LRU_list( ut_ad(block->page.in_LRU_list); mutex_enter(&block->mutex); - freed = buf_LRU_free_block(&block->page, FALSE, NULL); + freed = buf_LRU_free_block(&block->page, FALSE); mutex_exit(&block->mutex); switch (freed) { @@ -636,7 +636,7 @@ buf_LRU_free_from_common_LRU_list( mutex_enter(block_mutex); accessed = buf_page_is_accessed(bpage); - freed = buf_LRU_free_block(bpage, TRUE, NULL); + freed = buf_LRU_free_block(bpage, TRUE); mutex_exit(block_mutex); switch (freed) { @@ -798,10 +798,8 @@ LRU list to the free list. @return the free control block, in state BUF_BLOCK_READY_FOR_USE */ UNIV_INTERN buf_block_t* -buf_LRU_get_free_block( -/*===================*/ - ulint zip_size) /*!< in: compressed page size in bytes, - or 0 if uncompressed tablespace */ +buf_LRU_get_free_block(void) +/*========================*/ { buf_block_t* block = NULL; ibool freed; @@ -877,26 +875,10 @@ loop: /* If there is a block in the free list, take it */ block = buf_LRU_get_free_only(); + buf_pool_mutex_exit(); + if (block) { - -#ifdef UNIV_DEBUG - block->page.zip.m_start = -#endif /* UNIV_DEBUG */ - block->page.zip.m_end = - block->page.zip.m_nonempty = - block->page.zip.n_blobs = 0; - - if (UNIV_UNLIKELY(zip_size)) { - ibool lru; - page_zip_set_size(&block->page.zip, zip_size); - block->page.zip.data = buf_buddy_alloc(zip_size, &lru); - UNIV_MEM_DESC(block->page.zip.data, zip_size, block); - } else { - page_zip_set_size(&block->page.zip, 0); - block->page.zip.data = NULL; - } - - buf_pool_mutex_exit(); + memset(&block->page.zip, 0, sizeof block->page.zip); if (started_monitor) { srv_print_innodb_monitor = mon_value_was; @@ -908,8 +890,6 @@ loop: /* If no block was in the free list, search from the end of the LRU list and try to free a block there */ - buf_pool_mutex_exit(); - freed = buf_LRU_search_and_free_block(n_iterations); if (freed > 0) { @@ -1378,12 +1358,8 @@ enum buf_lru_free_block_status buf_LRU_free_block( /*===============*/ buf_page_t* bpage, /*!< in: block to be freed */ - ibool zip, /*!< in: TRUE if should remove also the + ibool zip) /*!< in: TRUE if should remove also the compressed page of an uncompressed page */ - ibool* buf_pool_mutex_released) - /*!< in: pointer to a variable that will - be assigned TRUE if buf_pool_mutex - was temporarily released, or NULL */ { buf_page_t* b = NULL; mutex_t* block_mutex = buf_page_get_mutex(bpage); @@ -1554,10 +1530,6 @@ alloc: b->io_fix = BUF_IO_READ; } - if (buf_pool_mutex_released) { - *buf_pool_mutex_released = TRUE; - } - buf_pool_mutex_exit(); mutex_exit(block_mutex); diff --git a/storage/innodb_plugin/include/buf0buf.h b/storage/innodb_plugin/include/buf0buf.h index cd4ee5906f0..d903b443920 100644 --- a/storage/innodb_plugin/include/buf0buf.h +++ b/storage/innodb_plugin/include/buf0buf.h @@ -165,10 +165,8 @@ Allocates a buffer block. @return own: the allocated block, in state BUF_BLOCK_MEMORY */ UNIV_INLINE buf_block_t* -buf_block_alloc( -/*============*/ - ulint zip_size); /*!< in: compressed page size in bytes, - or 0 if uncompressed tablespace */ +buf_block_alloc(void); +/*=================*/ /********************************************************************//** Frees a buffer block which does not contain a file page. */ UNIV_INLINE diff --git a/storage/innodb_plugin/include/buf0buf.ic b/storage/innodb_plugin/include/buf0buf.ic index 23db684806c..0025bef5aac 100644 --- a/storage/innodb_plugin/include/buf0buf.ic +++ b/storage/innodb_plugin/include/buf0buf.ic @@ -719,14 +719,12 @@ Allocates a buffer block. @return own: the allocated block, in state BUF_BLOCK_MEMORY */ UNIV_INLINE buf_block_t* -buf_block_alloc( -/*============*/ - ulint zip_size) /*!< in: compressed page size in bytes, - or 0 if uncompressed tablespace */ +buf_block_alloc(void) +/*=================*/ { buf_block_t* block; - block = buf_LRU_get_free_block(zip_size); + block = buf_LRU_get_free_block(); buf_block_set_state(block, BUF_BLOCK_MEMORY); diff --git a/storage/innodb_plugin/include/buf0lru.h b/storage/innodb_plugin/include/buf0lru.h index 5a9cfd059f3..d543bce53cd 100644 --- a/storage/innodb_plugin/include/buf0lru.h +++ b/storage/innodb_plugin/include/buf0lru.h @@ -110,12 +110,9 @@ enum buf_lru_free_block_status buf_LRU_free_block( /*===============*/ buf_page_t* bpage, /*!< in: block to be freed */ - ibool zip, /*!< in: TRUE if should remove also the + ibool zip) /*!< in: TRUE if should remove also the compressed page of an uncompressed page */ - ibool* buf_pool_mutex_released); - /*!< in: pointer to a variable that will - be assigned TRUE if buf_pool_mutex - was temporarily released, or NULL */ + __attribute__((nonnull)); /******************************************************************//** Try to free a replaceable block. @return TRUE if found and freed */ @@ -146,10 +143,9 @@ LRU list to the free list. @return the free control block, in state BUF_BLOCK_READY_FOR_USE */ UNIV_INTERN buf_block_t* -buf_LRU_get_free_block( -/*===================*/ - ulint zip_size); /*!< in: compressed page size in bytes, - or 0 if uncompressed tablespace */ +buf_LRU_get_free_block(void) +/*========================*/ + __attribute__((warn_unused_result)); /******************************************************************//** Puts a block back to the free list. */ diff --git a/storage/innodb_plugin/mem/mem0mem.c b/storage/innodb_plugin/mem/mem0mem.c index 1dd4db30841..86100b04fd6 100644 --- a/storage/innodb_plugin/mem/mem0mem.c +++ b/storage/innodb_plugin/mem/mem0mem.c @@ -347,7 +347,7 @@ mem_heap_create_block( return(NULL); } } else { - buf_block = buf_block_alloc(0); + buf_block = buf_block_alloc(); } block = (mem_block_t*) buf_block->frame; diff --git a/storage/innodb_plugin/page/page0zip.c b/storage/innodb_plugin/page/page0zip.c index d3b1edefc6b..bb9b0995c72 100644 --- a/storage/innodb_plugin/page/page0zip.c +++ b/storage/innodb_plugin/page/page0zip.c @@ -4439,7 +4439,7 @@ page_zip_reorganize( log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); #ifndef UNIV_HOTBACKUP - temp_block = buf_block_alloc(0); + temp_block = buf_block_alloc(); btr_search_drop_page_hash_index(block); block->check_index_page_at_flush = TRUE; #else /* !UNIV_HOTBACKUP */ From 46b7ef69916635ca0575ea4898ed8980f4bf6f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 25 Jan 2011 11:54:50 +0200 Subject: [PATCH 09/42] Bug#59464 Race condition in row_vers_build_for_semi_consistent_read row_vers_build_for_semi_consistent_read(): Dereference version_trx before releasing kernel_mutex, but not thereafter. --- storage/innobase/row/row0vers.c | 10 +++++++--- storage/innodb_plugin/ChangeLog | 5 +++++ storage/innodb_plugin/row/row0vers.c | 10 +++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/storage/innobase/row/row0vers.c b/storage/innobase/row/row0vers.c index f4adfa855df..23aca8c3f2e 100644 --- a/storage/innobase/row/row0vers.c +++ b/storage/innobase/row/row0vers.c @@ -593,11 +593,15 @@ row_vers_build_for_semi_consistent_read( mutex_enter(&kernel_mutex); version_trx = trx_get_on_id(version_trx_id); + if (version_trx + && (version_trx->conc_state == TRX_COMMITTED_IN_MEMORY + || version_trx->conc_state == TRX_NOT_STARTED)) { + + version_trx = NULL; + } mutex_exit(&kernel_mutex); - if (!version_trx - || version_trx->conc_state == TRX_NOT_STARTED - || version_trx->conc_state == TRX_COMMITTED_IN_MEMORY) { + if (!version_trx) { /* We found a version that belongs to a committed transaction: return it. */ diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 24cac7ac2be..d5e9a6bc825 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2011-01-25 The InnoDB Team + + * row/row0vers.c: + Fix Bug#59464 Race condition in row_vers_build_for_semi_consistent_read + 2011-01-25 The InnoDB Team * btr/btr0btr.c, btr/btr0cur.c, btr/btr0sea.c, diff --git a/storage/innodb_plugin/row/row0vers.c b/storage/innodb_plugin/row/row0vers.c index b6d35363f08..d4fde0b939b 100644 --- a/storage/innodb_plugin/row/row0vers.c +++ b/storage/innodb_plugin/row/row0vers.c @@ -669,11 +669,15 @@ row_vers_build_for_semi_consistent_read( mutex_enter(&kernel_mutex); version_trx = trx_get_on_id(version_trx_id); + if (version_trx + && (version_trx->conc_state == TRX_COMMITTED_IN_MEMORY + || version_trx->conc_state == TRX_NOT_STARTED)) { + + version_trx = NULL; + } mutex_exit(&kernel_mutex); - if (!version_trx - || version_trx->conc_state == TRX_NOT_STARTED - || version_trx->conc_state == TRX_COMMITTED_IN_MEMORY) { + if (!version_trx) { /* We found a version that belongs to a committed transaction: return it. */ From 896e0ba4e0304fbd1b056022d8e27f6ce146a83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 25 Jan 2011 12:17:28 +0200 Subject: [PATCH 10/42] Bug#59486 Incorrect usage of UNIV_UNLIKELY() in mlog_parse_string() mlog_parse_string(): Enclose the comparison in UNIV_UNLIKELY, not the comparand. --- storage/innodb_plugin/ChangeLog | 5 +++++ storage/innodb_plugin/mtr/mtr0log.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index d5e9a6bc825..cac72fd3075 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2011-01-25 The InnoDB Team + + * mtr/mtr0log.c: + Bug#59486 Incorrect usage of UNIV_UNLIKELY() in mlog_parse_string() + 2011-01-25 The InnoDB Team * row/row0vers.c: diff --git a/storage/innodb_plugin/mtr/mtr0log.c b/storage/innodb_plugin/mtr/mtr0log.c index 3f3dab36b76..3349036b5b3 100644 --- a/storage/innodb_plugin/mtr/mtr0log.c +++ b/storage/innodb_plugin/mtr/mtr0log.c @@ -408,7 +408,7 @@ mlog_parse_string( ptr += 2; if (UNIV_UNLIKELY(offset >= UNIV_PAGE_SIZE) - || UNIV_UNLIKELY(len + offset) > UNIV_PAGE_SIZE) { + || UNIV_UNLIKELY(len + offset > UNIV_PAGE_SIZE)) { recv_sys->found_corrupt_log = TRUE; return(NULL); From e44703db76e87fccbcc2e51606f04b18b55a0544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 25 Jan 2011 15:43:08 +0200 Subject: [PATCH 11/42] Bug#59585 Fix 58912 introduces compiler warning due to potentially uninitialized variable row_upd_changes_ord_field_binary(): Initialize dfield_len to suppress the warning. The compiler cannot know that row_ext_lookup() does initialize dfield_len for us, as it is defined in a different module. --- storage/innodb_plugin/ChangeLog | 6 ++++++ storage/innodb_plugin/row/row0upd.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index cac72fd3075..e2fdeecfcc1 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-01-25 The InnoDB Team + + * row/row0upd.c: + Bug#59585 Fix 58912 introduces compiler warning + due to potentially uninitialized variable + 2011-01-25 The InnoDB Team * mtr/mtr0log.c: diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 4aa1474a25b..691d263e6ed 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1252,6 +1252,10 @@ row_upd_changes_ord_field_binary( || dfield_is_null(dfield)) { /* do nothing special */ } else if (UNIV_LIKELY_NULL(ext)) { + /* Silence a compiler warning without + silencing a Valgrind error. */ + dfield_len = 0; + UNIV_MEM_INVALID(&dfield_len, sizeof dfield_len); /* See if the column is stored externally. */ buf = row_ext_lookup(ext, col_no, &dfield_len); From de3c4428b8c759e85631d8d70b5845c872de5400 Mon Sep 17 00:00:00 2001 From: Karen Langford Date: Tue, 25 Jan 2011 15:42:40 +0100 Subject: [PATCH 12/42] Updating header copyright/README in source for 2011 --- README | 2 +- include/my_pthread.h | 2 +- include/my_sys.h | 2 +- mysys/my_fopen.c | 2 +- mysys/my_getsystime.c | 2 +- sql/handler.cc | 2 +- sql/item.h | 2 +- sql/item_cmpfunc.cc | 2 +- sql/item_geofunc.h | 2 +- sql/log.cc | 2 +- sql/net_serv.cc | 2 +- sql/sql_base.cc | 2 +- sql/sql_select.cc | 2 +- sql/sql_show.cc | 2 +- sql/sql_view.cc | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README b/README index 5463de4a7e8..48948352e12 100644 --- a/README +++ b/README @@ -5,7 +5,7 @@ For the avoidance of doubt, this particular copy of the software is released under the version 2 of the GNU General Public License. MySQL is brought to you by the MySQL team at Oracle. -Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. License information can be found in the COPYING file. diff --git a/include/my_pthread.h b/include/my_pthread.h index 3880511da2d..87cdaaad0dd 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/include/my_sys.h b/include/my_sys.h index 0ac220cec31..f6cd9dada99 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index b8373ecb3ab..72991490d75 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/mysys/my_getsystime.c b/mysys/my_getsystime.c index dfd99f6380d..ebe6cea842b 100644 --- a/mysys/my_getsystime.c +++ b/mysys/my_getsystime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 MySQL AB +/* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/handler.cc b/sql/handler.cc index 711d2942ce0..5968a78b587 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/item.h b/sql/item.h index 866d620d9d7..8568e89542e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 4f2034ae902..df5ed7f38de 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 08161badfd3..0a6e8d03a46 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/log.cc b/sql/log.cc index 23182fa1902..17642696e7d 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 0c559f5619d..e45d57e57dc 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 995520ab16f..9765148cda1 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8cc2ec6a0f8..95378db80d1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/sql_show.cc b/sql/sql_show.cc index cf6a34d4ef5..1524a8fb87f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/sql_view.cc b/sql/sql_view.cc index a25ef931344..ab6da7c1925 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 MySQL AB +/* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. 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 From 786ac62c82038ed42278b3699b0661f0bb3c80ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 27 Jan 2011 13:27:29 +0200 Subject: [PATCH 13/42] Bug#59440 Race condition in XA ROLLBACK and XA COMMIT after server restart trx_get_trx_by_xid(): Invalidate trx->xid after a successful lookup, so that subsequent callers will not find the same transaction. The only callers of trx_get_trx_by_xid() will be invoking innobase_commit_low() or innobase_rollback_trx(), and those code paths should not depend on trx->xid. rb://584 approved by Jimmy Yang --- storage/innobase/include/trx0trx.h | 5 +++-- storage/innobase/trx/trx0trx.c | 26 ++++++++++++------------- storage/innodb_plugin/ChangeLog | 6 ++++++ storage/innodb_plugin/include/trx0trx.h | 4 ++-- storage/innodb_plugin/trx/trx0trx.c | 25 +++++++++++------------- 5 files changed, 34 insertions(+), 32 deletions(-) diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 97a47d9f46e..4652f45892e 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -198,8 +198,9 @@ which is in the prepared state */ trx_t * trx_get_trx_by_xid( /*===============*/ - /* out: trx or NULL */ - XID* xid); /* in: X/Open XA transaction identification */ + /* out: trx or NULL; + on match, the trx->xid will be invalidated */ + const XID* xid); /* in: X/Open XA transaction identifier */ /************************************************************************** If required, flushes the log to disk if we called trx_commit_for_mysql() with trx->flush_log_later == TRUE. */ diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index 21f75e0818f..a82d7f452fc 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -2041,14 +2041,15 @@ which is in the prepared state */ trx_t* trx_get_trx_by_xid( /*===============*/ - /* out: trx or NULL */ - XID* xid) /* in: X/Open XA transaction identification */ + /* out: trx or NULL; + on match, the trx->xid will be invalidated */ + const XID* xid) /* in: X/Open XA transaction identifier */ { trx_t* trx; if (xid == NULL) { - return (NULL); + return(NULL); } mutex_enter(&kernel_mutex); @@ -2061,10 +2062,16 @@ trx_get_trx_by_xid( of gtrid_lenght+bqual_length bytes should be the same */ - if (xid->gtrid_length == trx->xid.gtrid_length + if (trx->conc_state == TRX_PREPARED + && xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data, xid->gtrid_length + xid->bqual_length) == 0) { + + /* Invalidate the XID, so that subsequent calls + will not find it. */ + memset(&trx->xid, 0, sizeof(trx->xid)); + trx->xid.formatID = -1; break; } @@ -2073,14 +2080,5 @@ trx_get_trx_by_xid( mutex_exit(&kernel_mutex); - if (trx) { - if (trx->conc_state != TRX_PREPARED) { - - return(NULL); - } - - return(trx); - } else { - return(NULL); - } + return(trx); } diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index e2fdeecfcc1..3e14b0052e7 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-01-27 The InnoDB Team + + * include/trx0trx.h, trx/trx0trx.c: + Bug#59440 Race condition in XA ROLLBACK and XA COMMIT + after server restart + 2011-01-25 The InnoDB Team * row/row0upd.c: diff --git a/storage/innodb_plugin/include/trx0trx.h b/storage/innodb_plugin/include/trx0trx.h index abd175d365b..833bae4a4ff 100644 --- a/storage/innodb_plugin/include/trx0trx.h +++ b/storage/innodb_plugin/include/trx0trx.h @@ -214,12 +214,12 @@ trx_recover_for_mysql( /*******************************************************************//** This function is used to find one X/Open XA distributed transaction which is in the prepared state -@return trx or NULL */ +@return trx or NULL; on match, the trx->xid will be invalidated */ UNIV_INTERN trx_t * trx_get_trx_by_xid( /*===============*/ - XID* xid); /*!< in: X/Open XA transaction identification */ + const XID* xid); /*!< in: X/Open XA transaction identifier */ /**********************************************************************//** If required, flushes the log to disk if we called trx_commit_for_mysql() with trx->flush_log_later == TRUE. diff --git a/storage/innodb_plugin/trx/trx0trx.c b/storage/innodb_plugin/trx/trx0trx.c index ee744fd58b1..f0bbf220815 100644 --- a/storage/innodb_plugin/trx/trx0trx.c +++ b/storage/innodb_plugin/trx/trx0trx.c @@ -2010,18 +2010,18 @@ trx_recover_for_mysql( /*******************************************************************//** This function is used to find one X/Open XA distributed transaction which is in the prepared state -@return trx or NULL */ +@return trx or NULL; on match, the trx->xid will be invalidated */ UNIV_INTERN trx_t* trx_get_trx_by_xid( /*===============*/ - XID* xid) /*!< in: X/Open XA transaction identification */ + const XID* xid) /*!< in: X/Open XA transaction identifier */ { trx_t* trx; if (xid == NULL) { - return (NULL); + return(NULL); } mutex_enter(&kernel_mutex); @@ -2034,10 +2034,16 @@ trx_get_trx_by_xid( of gtrid_lenght+bqual_length bytes should be the same */ - if (xid->gtrid_length == trx->xid.gtrid_length + if (trx->conc_state == TRX_PREPARED + && xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data, xid->gtrid_length + xid->bqual_length) == 0) { + + /* Invalidate the XID, so that subsequent calls + will not find it. */ + memset(&trx->xid, 0, sizeof(trx->xid)); + trx->xid.formatID = -1; break; } @@ -2046,14 +2052,5 @@ trx_get_trx_by_xid( mutex_exit(&kernel_mutex); - if (trx) { - if (trx->conc_state != TRX_PREPARED) { - - return(NULL); - } - - return(trx); - } else { - return(NULL); - } + return(trx); } From 62ab20034300b0483c8d73387794d881f12b809d Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 27 Jan 2011 23:47:24 +0100 Subject: [PATCH 14/42] Update of copyright headers for files I changed this year. --- sql/item.h | 2 +- sql/item_func.h | 2 +- sql/item_timefunc.h | 2 +- sql/sql_partition.cc | 2 +- sql/table.cc | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sql/item.h b/sql/item.h index 38b33bdf2fe..b187c1b6717 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/item_func.h b/sql/item_func.h index e9d91b56318..5dd68157e0b 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index f4299460abf..2d0a6217f0c 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2006 MySQL AB +/* Copyright (c) 2000, 2011 Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index cb55695d4c1..d10f695f535 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1,4 +1,4 @@ -/* Copyright 2005-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. 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 diff --git a/sql/table.cc b/sql/table.cc index b43d29294a8..7dbf02027fa 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 From 235e10d987222b8cc42e1f3a9a79834912d45026 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Fri, 28 Jan 2011 01:25:26 +0000 Subject: [PATCH 15/42] BUG#55675 rpl.rpl_log_pos fails sporadically with error binlog truncated in the middle There are two calls to read_log_event() on master in mysql_binlog_send(). Each call reads 19 bytes in this test case and the error of the second read_log_event() is reported to the slave. The second read_log_event() starts from position 94 (75 + 19) to 113 (75 + 19 + 19). Usually, there are two events in the binary log: . 0 - 3 - Header . 4 - 105 - Format Descriptor Event . 106 - 304 - Query Event and both reads fail because operations are reading from invalid positions as expected. However, mysql_binlog_send() does not use the same IO_CACHE that is used to write into binary log (i.e. mysql_bin_log.log_file) for the hot binary log. It opens the binary log file directly by calling open_binlog() and creates a separated IO_CACHE. So there is a possibly that after a master has flushed the binary log file, the content has been cached by the filesystem, and has not updated the disk file. If this happens, then a slave will only see part of the file, and thus the second read_log_event() will report event truncated error. To fix the problem, if the first read_log_event() has failed, we ensure that the second one will try to read from the same position. --- mysql-test/suite/rpl/t/disabled.def | 1 - sql/sql_repl.cc | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index 3b3a55fe4cd..33f65ff3ecc 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -11,7 +11,6 @@ ############################################################################## rpl_row_create_table : Bug#51574 Feb 27 2010 andrei failed different way than earlier with bug#45576 -rpl_log_pos : BUG#55675 Sep 10 2010 27 2010 alfranio rpl.rpl_log_pos fails sporadically with error binlog truncated in the middle rpl_get_master_version_and_clock : Bug#59178 Jan 05 2011 joro Valgrind warnings rpl_get_master_version_and_clock rpl_row_until : BUG#59543 Jan 26 2011 alfranio Replication test from eits suite rpl_row_until times out rpl_stm_until : BUG#59543 Jan 26 2011 alfranio Replication test from eits suite rpl_row_until times out diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 8c769ce6acf..2a1efab13a9 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -545,8 +545,10 @@ impossible position"; while (!net->error && net->vio != 0 && !thd->killed) { + my_off_t prev_pos= pos; while (!(error = Log_event::read_log_event(&log, packet, log_lock))) { + prev_pos= my_b_tell(&log); #ifndef DBUG_OFF if (max_binlog_dump_events && !left_events--) { @@ -613,8 +615,13 @@ impossible position"; here we were reading binlog that was not closed properly (as a result of a crash ?). treat any corruption as EOF */ - if (binlog_can_be_corrupted && error != LOG_READ_MEM) + if (binlog_can_be_corrupted && + error != LOG_READ_MEM && error != LOG_READ_EOF) + { + my_b_seek(&log, prev_pos); error=LOG_READ_EOF; + } + /* TODO: now that we are logging the offset, check to make sure the recorded offset and the actual match. From 71e8043bae2071ba875b18326504b1058b8deb98 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Fri, 28 Jan 2011 00:50:10 -0800 Subject: [PATCH 16/42] Fix Bug #59465 btr_estimate_number_of_different_key_vals use incorrect offset for external_size rb://581 approved by Marko --- storage/innobase/btr/btr0cur.c | 10 +++++----- storage/innodb_plugin/ChangeLog | 7 +++++++ storage/innodb_plugin/btr/btr0cur.c | 10 +++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index 9f4babfaae6..6c0497cbd41 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -2981,6 +2981,9 @@ btr_estimate_number_of_different_key_vals( while (!page_rec_is_supremum(rec)) { rec_t* next_rec = page_rec_get_next(rec); if (page_rec_is_supremum(next_rec)) { + total_external_size += + btr_rec_get_externally_stored_len( + rec, offsets_rec); break; } @@ -2988,7 +2991,8 @@ btr_estimate_number_of_different_key_vals( matched_bytes = 0; offsets_next_rec = rec_get_offsets(next_rec, index, offsets_next_rec, - n_cols, &heap); + ULINT_UNDEFINED, + &heap); cmp_rec_rec_with_match(rec, next_rec, offsets_rec, offsets_next_rec, @@ -3043,10 +3047,6 @@ btr_estimate_number_of_different_key_vals( } } - offsets_rec = rec_get_offsets(rec, index, offsets_rec, - ULINT_UNDEFINED, &heap); - total_external_size += btr_rec_get_externally_stored_len( - rec, offsets_rec); mtr_commit(&mtr); } diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 3e14b0052e7..7a901fc1fa1 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-01-27 The InnoDB Team + + * btr/btr0cur.c: + Bug#59465 btr_estimate_number_of_different_key_vals use + incorrect offset for external_size + 2011-01-27 The InnoDB Team * include/trx0trx.h, trx/trx0trx.c: @@ -29,6 +35,7 @@ Fix Bug#59707 Unused compression-related parameters in buffer pool functions +>>>>>>> MERGE-SOURCE 2011-01-18 The InnoDB Team * include/sync0rw.h, sync/sync0arr.c, sync/sync0rw.c: diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index f41b125b281..874db3066b5 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -3365,6 +3365,9 @@ btr_estimate_number_of_different_key_vals( while (!page_rec_is_supremum(rec)) { rec_t* next_rec = page_rec_get_next(rec); if (page_rec_is_supremum(next_rec)) { + total_external_size += + btr_rec_get_externally_stored_len( + rec, offsets_rec); break; } @@ -3372,7 +3375,8 @@ btr_estimate_number_of_different_key_vals( matched_bytes = 0; offsets_next_rec = rec_get_offsets(next_rec, index, offsets_next_rec, - n_cols, &heap); + ULINT_UNDEFINED, + &heap); cmp_rec_rec_with_match(rec, next_rec, offsets_rec, offsets_next_rec, @@ -3427,10 +3431,6 @@ btr_estimate_number_of_different_key_vals( } } - offsets_rec = rec_get_offsets(rec, index, offsets_rec, - ULINT_UNDEFINED, &heap); - total_external_size += btr_rec_get_externally_stored_len( - rec, offsets_rec); mtr_commit(&mtr); } From 8ce9b992462f0d2ef5e435e4ecda4700fa7db652 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Fri, 28 Jan 2011 12:09:15 +0000 Subject: [PATCH 17/42] BUG#59338 Inconsistency in binlog for statements that don't change any rows STATEMENT SBR In SBR, if a statement does not fail, it is always written to the binary log, regardless if rows are changed or not. If there is a failure, a statement is only written to the binary log if a non-transactional (.e.g. MyIsam) engine is updated. INSERT ON DUPLICATE KEY UPDATE and INSERT IGNORE were not following the rule above and were not written to the binary log, if then engine was Innodb. mysql-test/extra/rpl_tests/rpl_insert_duplicate.test: Added test case. mysql-test/extra/rpl_tests/rpl_insert_ignore.test: Updated test case. mysql-test/include/commit.inc: Updated test case as the calls to the binary log have changed for INSERT ON DUPLICATE and INSERT IGNORE. mysql-test/r/commit_1innodb.result: Updated result file. mysql-test/suite/rpl/r/rpl_insert_duplicate.result: Added test case. mysql-test/suite/rpl/r/rpl_insert_ignore.result: Updated result file. mysql-test/suite/rpl/t/rpl_insert_duplicate.test: Added test case. mysql-test/suite/rpl/t/rpl_insert_ignore.test: Improved test case. --- .../extra/rpl_tests/rpl_insert_duplicate.test | 64 +++++++++++++++++ .../extra/rpl_tests/rpl_insert_ignore.test | 70 +++++++++++-------- mysql-test/include/commit.inc | 8 +-- mysql-test/r/commit_1innodb.result | 8 +-- .../suite/rpl/r/rpl_insert_duplicate.result | 29 ++++++++ .../suite/rpl/r/rpl_insert_ignore.result | 64 +++++++---------- .../suite/rpl/t/rpl_insert_duplicate.test | 14 ++++ mysql-test/suite/rpl/t/rpl_insert_ignore.test | 8 ++- sql/sql_insert.cc | 2 +- 9 files changed, 189 insertions(+), 78 deletions(-) create mode 100644 mysql-test/extra/rpl_tests/rpl_insert_duplicate.test create mode 100644 mysql-test/suite/rpl/r/rpl_insert_duplicate.result create mode 100644 mysql-test/suite/rpl/t/rpl_insert_duplicate.test diff --git a/mysql-test/extra/rpl_tests/rpl_insert_duplicate.test b/mysql-test/extra/rpl_tests/rpl_insert_duplicate.test new file mode 100644 index 00000000000..77140174f4b --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_insert_duplicate.test @@ -0,0 +1,64 @@ +# BUG#59338 Inconsistency in binlog for statements that don't change any rows STATEMENT SBR +# In SBR, if a statement does not fail, it is always written to the binary log, +# regardless if rows are changed or not. If there is a failure, a statement is +# only written to the binary log if a non-transactional (.e.g. MyIsam) engine +# is updated. INSERT ON DUPLICATE KEY UPDATE was not following the rule above +# and was not written to the binary log, if then engine was Innodb. +# +# In this test case, we check if INSERT ON DUPLICATE KEY UPDATE that does not +# change anything is still written to the binary log. + +# Prepare environment +--connection master + +eval CREATE TABLE t1 ( + a INT UNSIGNED NOT NULL PRIMARY KEY +) ENGINE=$engine_type; + +eval CREATE TABLE t2 ( + a INT UNSIGNED +) ENGINE=$engine_type; + +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); + +# An insert duplicate that does not update anything must be written to the binary +# log in SBR and MIXED modes. We check this property by summing a before and after +# the update and comparing the binlog positions. The sum should be the same at both +# points and the statement should be in the binary log. +--let $binlog_file= query_get_value("SHOW MASTER STATUS", File, 1) +--let $binlog_start= query_get_value("SHOW MASTER STATUS", Position, 1) +--let $statement_file=INSERT INTO t1 SELECT t2.a FROM t2 ORDER BY t2.a ON DUPLICATE KEY UPDATE t1.a= t1.a +--eval $statement_file + +--let $assert_cond= SUM(a) = 1 FROM t1 +--let $assert_text= Sum of elements in t1 should be 1. +--source include/assert.inc + +if (`SELECT @@BINLOG_FORMAT = 'ROW'`) +{ + --let $binlog_position_cmp= = + --let $assert_cond= [SHOW MASTER STATUS, Position, 1] $binlog_position_cmp $binlog_start + --let $assert_text= In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged. +} +if (`SELECT @@BINLOG_FORMAT != 'ROW' && UPPER('$engine_type') = UPPER('Innodb')`) +{ + --let $assert_cond= \'[\'SHOW BINLOG EVENTS IN "$binlog_file" FROM $binlog_start LIMIT 1, 1\', Info, 1]\' LIKE \'%$statement_file\' + --let $assert_text= In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged. +} +if (`SELECT @@BINLOG_FORMAT != 'ROW' && UPPER('$engine_type') = UPPER('MyIsam')`) +{ + --let $assert_cond= \'[\'SHOW BINLOG EVENTS IN "$binlog_file" FROM $binlog_start LIMIT 0, 1\', Info, 1]\' LIKE \'%$statement_file\' + --let $assert_text= In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged. +} +--source include/assert.inc + +# Compare master and slave +--sync_slave_with_master +--let $diff_tables= master:test.t1 , slave:test.t1 +--source include/diff_tables.inc + +# Clean up +--connection master +drop table t1, t2; +--sync_slave_with_master diff --git a/mysql-test/extra/rpl_tests/rpl_insert_ignore.test b/mysql-test/extra/rpl_tests/rpl_insert_ignore.test index 43d45ef6c60..1eb66358423 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_ignore.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_ignore.test @@ -5,6 +5,7 @@ # Slave needs to be started with --innodb to store table in InnoDB. # Same test for MyISAM (which had no bug). +--connection master eval CREATE TABLE t1 ( a int unsigned not null auto_increment primary key, b int unsigned, @@ -32,38 +33,49 @@ INSERT INTO t2 VALUES (5, 4); INSERT INTO t2 VALUES (6, 6); INSERT IGNORE INTO t1 SELECT NULL, t2.b FROM t2 ORDER BY t2.a; +--let $assert_cond= COUNT(*) = 6 FROM t1 +--let $assert_text= Count of elements in t1 should be 6. +--source include/assert.inc -# Compare results +# Compare master and slave +--sync_slave_with_master +--let $diff_tables= master:test.t1 , slave:test.t1 +--source include/diff_tables.inc -SELECT * FROM t1 ORDER BY a; +# BUG#59338 Inconsistency in binlog for statements that don't change any rows STATEMENT SBR +# An insert ignore that does not update anything must be written to the binary log in SBR +# and MIXED modes. We check this property by counting occurrences in t1 before and after +# the insert and comparing the binlog positions. The count should be the same in both points +# and the statement should be in the binary log. +--connection master +--let $binlog_file= query_get_value("SHOW MASTER STATUS", File, 1) +--let $binlog_start= query_get_value("SHOW MASTER STATUS", Position, 1) +--let $statement_file=INSERT IGNORE INTO t1 SELECT NULL, t2.b FROM t2 ORDER BY t2.a +--eval $statement_file -sync_slave_with_master; -SELECT * FROM t1 ORDER BY a; +--let $assert_cond= COUNT(*) = 6 FROM t1 +--let $assert_text= Count of elements in t1 should be 6. +--source include/assert.inc -# Now do the same for MyISAM +if (`SELECT @@BINLOG_FORMAT = 'ROW'`) +{ + --let $binlog_position_cmp= = + --let $assert_cond= [SHOW MASTER STATUS, Position, 1] $binlog_position_cmp $binlog_start + --let $assert_text= In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged. +} +if (`SELECT @@BINLOG_FORMAT != 'ROW' && UPPER('$engine_type') = UPPER('Innodb')`) +{ + --let $assert_cond= \'[\'SHOW BINLOG EVENTS IN "$binlog_file" FROM $binlog_start LIMIT 2, 1\', Info, 1]\' LIKE \'%$statement_file\' + --let $assert_text= In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged. +} +if (`SELECT @@BINLOG_FORMAT != 'ROW' && UPPER('$engine_type') = UPPER('MyIsam')`) +{ + --let $assert_cond= \'[\'SHOW BINLOG EVENTS IN "$binlog_file" FROM $binlog_start LIMIT 1, 1\', Info, 1]\' LIKE \'%$statement_file\' + --let $assert_text= In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged. +} +--source include/assert.inc -connection master; -drop table t1; -eval CREATE TABLE t1 ( - a int unsigned not null auto_increment primary key, - b int unsigned, - unique (b) -) ENGINE=$engine_type2; - -INSERT INTO t1 VALUES (1, 1); -INSERT INTO t1 VALUES (2, 2); -INSERT INTO t1 VALUES (3, 3); -INSERT INTO t1 VALUES (4, 4); - -INSERT IGNORE INTO t1 SELECT NULL, t2.b FROM t2 ORDER BY t2.a; - -SELECT * FROM t1 ORDER BY a; - -sync_slave_with_master; -SELECT * FROM t1 ORDER BY a; - -connection master; +# Clean up +--connection master drop table t1, t2; -sync_slave_with_master; - -# End of 4.1 tests +--sync_slave_with_master diff --git a/mysql-test/include/commit.inc b/mysql-test/include/commit.inc index d412eae8364..7924f9bc96f 100644 --- a/mysql-test/include/commit.inc +++ b/mysql-test/include/commit.inc @@ -502,16 +502,16 @@ call p_verify_status_increment(2, 2, 2, 2); --echo # 12. Read-write statement: IODKU, change 0 rows. --echo # insert t1 set a=2 on duplicate key update a=2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); commit; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); --echo # 13. Read-write statement: INSERT IGNORE, change 0 rows. --echo # insert ignore t1 set a=2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); commit; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); --echo # 14. Read-write statement: INSERT IGNORE, change 1 row. --echo # diff --git a/mysql-test/r/commit_1innodb.result b/mysql-test/r/commit_1innodb.result index 1f0b2c8019b..d21af5ef555 100644 --- a/mysql-test/r/commit_1innodb.result +++ b/mysql-test/r/commit_1innodb.result @@ -518,21 +518,21 @@ SUCCESS # 12. Read-write statement: IODKU, change 0 rows. # insert t1 set a=2 on duplicate key update a=2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); SUCCESS commit; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); SUCCESS # 13. Read-write statement: INSERT IGNORE, change 0 rows. # insert ignore t1 set a=2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); SUCCESS commit; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 2, 1, 0); SUCCESS # 14. Read-write statement: INSERT IGNORE, change 1 row. diff --git a/mysql-test/suite/rpl/r/rpl_insert_duplicate.result b/mysql-test/suite/rpl/r/rpl_insert_duplicate.result new file mode 100644 index 00000000000..61ebbaaa5a9 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_insert_duplicate.result @@ -0,0 +1,29 @@ +include/master-slave.inc +[connection master] +CREATE TABLE t1 ( +a INT UNSIGNED NOT NULL PRIMARY KEY +) ENGINE=innodb; +CREATE TABLE t2 ( +a INT UNSIGNED +) ENGINE=innodb; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); +INSERT INTO t1 SELECT t2.a FROM t2 ORDER BY t2.a ON DUPLICATE KEY UPDATE t1.a= t1.a; +include/assert.inc [Sum of elements in t1 should be 1.] +include/assert.inc [In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged.] +include/diff_tables.inc [master:test.t1 , slave:test.t1] +drop table t1, t2; +CREATE TABLE t1 ( +a INT UNSIGNED NOT NULL PRIMARY KEY +) ENGINE=myisam; +CREATE TABLE t2 ( +a INT UNSIGNED +) ENGINE=myisam; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); +INSERT INTO t1 SELECT t2.a FROM t2 ORDER BY t2.a ON DUPLICATE KEY UPDATE t1.a= t1.a; +include/assert.inc [Sum of elements in t1 should be 1.] +include/assert.inc [In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged.] +include/diff_tables.inc [master:test.t1 , slave:test.t1] +drop table t1, t2; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_insert_ignore.result b/mysql-test/suite/rpl/r/rpl_insert_ignore.result index 6937c3d0987..5d9c4aec9f7 100644 --- a/mysql-test/suite/rpl/r/rpl_insert_ignore.result +++ b/mysql-test/suite/rpl/r/rpl_insert_ignore.result @@ -20,48 +20,36 @@ INSERT INTO t2 VALUES (4, 3); INSERT INTO t2 VALUES (5, 4); INSERT INTO t2 VALUES (6, 6); INSERT IGNORE INTO t1 SELECT NULL, t2.b FROM t2 ORDER BY t2.a; -SELECT * FROM t1 ORDER BY a; -a b -1 1 -2 2 -3 3 -4 4 -5 5 -6 6 -SELECT * FROM t1 ORDER BY a; -a b -1 1 -2 2 -3 3 -4 4 -5 5 -6 6 -drop table t1; +include/assert.inc [Count of elements in t1 should be 6.] +include/diff_tables.inc [master:test.t1 , slave:test.t1] +INSERT IGNORE INTO t1 SELECT NULL, t2.b FROM t2 ORDER BY t2.a; +include/assert.inc [Count of elements in t1 should be 6.] +include/assert.inc [In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged.] +drop table t1, t2; CREATE TABLE t1 ( a int unsigned not null auto_increment primary key, b int unsigned, unique (b) -) ENGINE=myisam; -INSERT INTO t1 VALUES (1, 1); -INSERT INTO t1 VALUES (2, 2); -INSERT INTO t1 VALUES (3, 3); -INSERT INTO t1 VALUES (4, 4); +) ENGINE=innodb; +CREATE TABLE t2 ( +a int unsigned, # to force INSERT SELECT to have a certain order +b int unsigned +) ENGINE=innodb; +INSERT INTO t1 VALUES (NULL, 1); +INSERT INTO t1 VALUES (NULL, 2); +INSERT INTO t1 VALUES (NULL, 3); +INSERT INTO t1 VALUES (NULL, 4); +INSERT INTO t2 VALUES (1, 1); +INSERT INTO t2 VALUES (2, 2); +INSERT INTO t2 VALUES (3, 5); +INSERT INTO t2 VALUES (4, 3); +INSERT INTO t2 VALUES (5, 4); +INSERT INTO t2 VALUES (6, 6); INSERT IGNORE INTO t1 SELECT NULL, t2.b FROM t2 ORDER BY t2.a; -SELECT * FROM t1 ORDER BY a; -a b -1 1 -2 2 -3 3 -4 4 -5 5 -6 6 -SELECT * FROM t1 ORDER BY a; -a b -1 1 -2 2 -3 3 -4 4 -5 5 -6 6 +include/assert.inc [Count of elements in t1 should be 6.] +include/diff_tables.inc [master:test.t1 , slave:test.t1] +INSERT IGNORE INTO t1 SELECT NULL, t2.b FROM t2 ORDER BY t2.a; +include/assert.inc [Count of elements in t1 should be 6.] +include/assert.inc [In SBR or MIXED modes, the event in the binlog should be the same that was executed. In RBR mode, binlog position should stay unchanged.] drop table t1, t2; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_insert_duplicate.test b/mysql-test/suite/rpl/t/rpl_insert_duplicate.test new file mode 100644 index 00000000000..7dd38a696ae --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_insert_duplicate.test @@ -0,0 +1,14 @@ +######################################### +# Wrapper for rpl_insert_duplicate.test # +######################################### +-- source include/master-slave.inc +-- source include/have_innodb.inc +#-- source include/have_binlog_format_mixed_or_statement.inc + +let $engine_type=innodb; +-- source extra/rpl_tests/rpl_insert_duplicate.test + +let $engine_type=myisam; +-- source extra/rpl_tests/rpl_insert_duplicate.test + +--source include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_insert_ignore.test b/mysql-test/suite/rpl/t/rpl_insert_ignore.test index 1d6c8e7168e..265a52fb51d 100644 --- a/mysql-test/suite/rpl/t/rpl_insert_ignore.test +++ b/mysql-test/suite/rpl/t/rpl_insert_ignore.test @@ -4,7 +4,11 @@ -- source include/not_ndb_default.inc -- source include/have_innodb.inc -- source include/master-slave.inc -let $engine_type=innodb; -let $engine_type2=myisam; + +-- let $engine_type=innodb -- source extra/rpl_tests/rpl_insert_ignore.test + +-- let $engine_type=innodb +-- source extra/rpl_tests/rpl_insert_ignore.test + --source include/rpl_end.inc diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f0735a9e093..e2f93ee4de5 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -881,7 +881,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, */ query_cache_invalidate3(thd, table_list, 1); } - if ((changed && error <= 0) || + if (error <= 0 || thd->transaction.stmt.modified_non_trans_table || was_insert_delayed) { From d83706bc74a5a53e9f4472e6846a73aa0d1df2d1 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 28 Jan 2011 13:37:39 +0100 Subject: [PATCH 18/42] minor fix of copyright header --- sql/item_timefunc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 2d0a6217f0c..9c1ac512bcb 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011 Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 From 26528be7112fadd5f23994f96174d3c45bd741b5 Mon Sep 17 00:00:00 2001 From: "John H. Embretsen" Date: Sat, 29 Jan 2011 12:38:36 +0100 Subject: [PATCH 19/42] Fix for Bug#56817 - mysqlhotcopy tests fail when DBI and DBD-mysql perl modules are not found Third updated patch - this version also includes copyright notice in added Perl script. This patch implements a check for such modules at runtime. If modules are not found or unable to load, the test is skipped with the following message: [ skipped ] Test needs Perl modules DBI and DBD::mysql Checks are done via a helper Perl script which looks for the module in a runtime environment that is as similar to that of the mysqlhotcopy script as possible (thus not intended for Windows environments at this time). The helper script tells mysql-test about the result by writing information to a temporary file that is later read by mysql-test. See comments in added files (have_dbi_dbd-mysql.inc and checkDBI_DBD-mysql.pl) for details. The patch also removes the mysqlhotcopy tests from the list of disabled tests. --- mysql-test/include/have_dbi_dbd-mysql.inc | 78 ++++++++++++++++++ mysql-test/include/mysqlhotcopy.inc | 14 ++++ mysql-test/std_data/checkDBI_DBD-mysql.pl | 97 +++++++++++++++++++++++ mysql-test/t/disabled.def | 2 - 4 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 mysql-test/include/have_dbi_dbd-mysql.inc create mode 100644 mysql-test/std_data/checkDBI_DBD-mysql.pl diff --git a/mysql-test/include/have_dbi_dbd-mysql.inc b/mysql-test/include/have_dbi_dbd-mysql.inc new file mode 100644 index 00000000000..212e36ac353 --- /dev/null +++ b/mysql-test/include/have_dbi_dbd-mysql.inc @@ -0,0 +1,78 @@ +# +# Originally created by John Embretsen, 2011-01-26. +# +# Checks for the existence of Perl modules DBI and DBD::mysql as seen from the +# perl installation used by "external" executable perl scripts, i.e. scripts +# that are executed as standalone scripts interpreted by the perl installation +# specified by the "shebang" line in the top of these scripts. +# +# If either module is not found, the test will be skipped. +# +# For use in tests that call perl scripts that require these modules. +# +# This file is intended to work on Unix. Windows may need different treatment. +# Reasoning: +# - "shebangs" are not relevant on Windows, but need to be handled here. +# - Perl scripts cannot be made executable on Windows, interpreter must be +# specified. +# +# Note that if there are multiple perl installations and not all have the +# required modules, this check may fail even if the perl in path does have +# the modules available. This may happen if the perl specified by the script's +# shebang (e.g. #!/usr/bin/perl) does not have these modules, and script is +# called without specifying the perl interpreter. However, this will be +# a correct result in cases where a test calls a script with a similar shebang. +# +################################################################################ + +--source include/not_windows.inc + +# We jump through some hoops since there is no direct way to check if an +# external command went OK or not from a mysql-test file: +# +# - In theory, we could do as simple as "exec perl -MDBI -MDBD::mysql -e 1", +# however we cannot check the result (exit code) from within a test script. +# Also, this may not yield the same result as other uses of perl due to the +# shebang issue mentioned above. +# - Instead we use a separate helper perl script that checks for the modules. +# - If the modules are found, the perl script leaves a file which sets a +# variable that can be read by this file. +# If the modules are not found, the perl script does not set this variable, +# but leaves an empty file instead. +# +# This is done because there is apparently no direct way to transfer +# information from perl to the test script itself. + +--disable_query_log +--disable_result_log +--disable_warnings + +# We do not use embedded perl in this script because that would not have yielded +# correct results for a situation where an external Perl script is called like +# "scriptname" instead of "perl scriptname" and the shebang in the script points +# to a specific perl that may be different than the perl in PATH. +# +# Instead, we call a separate helper script which checks for the modules in its +# own environment. We call it without "perl" in front. + +--let $perlChecker= $MYSQLTEST_VARDIR/std_data/checkDBI_DBD-mysql.pl +--let $resultFile= $MYSQL_TMP_DIR/dbidbd-mysql.txt + +# Make the script executable and execute it. +--chmod 0755 $perlChecker +--exec $perlChecker + +# Source the resulting temporary file and look for a variable being set. +--source $resultFile + +if (!$dbidbd) { + --skip Test needs Perl modules DBI and DBD::mysql +} + +# Clean up +--remove_file $resultFile + +--enable_query_log +--enable_result_log +--enable_warnings + diff --git a/mysql-test/include/mysqlhotcopy.inc b/mysql-test/include/mysqlhotcopy.inc index 91e0eff1e0f..110b0769ae1 100644 --- a/mysql-test/include/mysqlhotcopy.inc +++ b/mysql-test/include/mysqlhotcopy.inc @@ -4,12 +4,26 @@ --source include/not_windows.inc --source include/not_embedded.inc +--source include/have_dbi_dbd-mysql.inc if (!$MYSQLHOTCOPY) { + # Fail the test if the mysqlhotcopy script is missing. + # If the tool's location changes, mysql-test-run.pl must be updated to + # reflect this (look for "MYSQLHOTCOPY"). die due to missing mysqlhotcopy tool; } +# NOTE (johnemb, 2011-01-26): +# In this test mysqlhotcopy (a perl script) is executed as a standalone +# executable, i.e. not necessarily using the perl interpreter in PATH, +# because that is how the documentation demonstrates it. +# +# We include have_dbi_dbd-mysql.inc above so that the test will +# be skipped if Perl modules required by the mysqlhotcopy tool are not +# found when the script is run this way. + + let $MYSQLD_DATADIR= `SELECT @@datadir`; --disable_warnings DROP DATABASE IF EXISTS hotcopy_test; diff --git a/mysql-test/std_data/checkDBI_DBD-mysql.pl b/mysql-test/std_data/checkDBI_DBD-mysql.pl new file mode 100644 index 00000000000..f001e471081 --- /dev/null +++ b/mysql-test/std_data/checkDBI_DBD-mysql.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; version 2 +# of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +################################################################################ +# +# This perl script checks for availability of the Perl modules DBI and +# DBD::mysql using the "current" perl interpreter. +# +# Useful for test environment checking before testing executable perl scripts +# in the MySQL Server distribution. +# +# NOTE: The "shebang" on the first line of this script should always point to +# /usr/bin/perl, so that we can use this script to check whether or not we +# support running perl scripts with such a shebang without specifying the +# perl interpreter on the command line. Such a script is mysqlhotcopy. +# +# When run as "checkDBI_DBD-mysql.pl" the shebang line will be evaluated +# and used. When run as "perl checkDBI_DBD-mysql.pl" the shebang line is +# not used. +# +# NOTE: This script will create a temporary file in MTR's tmp dir. +# If modules are found, a mysql-test statement which sets a special +# variable is written to this file. If one of the modules is not found +# (or cannot be loaded), the file will remain empty. +# A test (or include file) which sources that file can then easily do +# an if-check on the special variable to determine success or failure. +# +# Example: +# +# --let $perlChecker= $MYSQLTEST_VARDIR/std_data/checkDBI_DBD-mysql.pl +# --let $resultFile= $MYSQL_TMP_DIR/dbidbd-mysql.txt +# --chmod 0755 $perlChecker +# --exec $perlChecker +# --source $resultFile +# if (!$dbidbd) { +# --skip Test needs Perl modules DBI and DBD::mysql +# } +# +# The calling script is also responsible for cleaning up after use: +# +# --remove_file $resultFile +# +# Windows notes: +# - shebangs may work differently - call this script with "perl " in front. +# +# See mysql-test/include/have_dbi_dbd-mysql.inc for example use of this script. +# This script should be executable for the user running MTR. +# +################################################################################ + +BEGIN { + # By using eval inside BEGIN we can suppress warnings and continue after. + # We need to catch "Can't locate" as well as "Can't load" errors. + eval{ + $FOUND_DBI=0; + $FOUND_DBD_MYSQL=0; + + # Check for DBI module: + $FOUND_DBI=1 if require DBI; + + # Check for DBD::mysql module + $FOUND_DBD_MYSQL=1 if require DBD::mysql; + }; +}; + +# Open a file to be used for transfer of result back to mysql-test. +# The file must be created whether we write to it or not, otherwise mysql-test +# will complain if trying to source it. +# An empty file indicates failure to load modules. +open(FILE, ">", $ENV{'MYSQL_TMP_DIR'}.'/dbidbd-mysql.txt'); + +if ($FOUND_DBI && $FOUND_DBD_MYSQL) { + # write a mysql-test command setting a variable to indicate success + print(FILE 'let $dbidbd= FOUND_DBI_DBD-MYSQL;'."\n"); +} + +# close the file. +close(FILE); + +1; + diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 4510652b03f..c244d08e308 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -11,7 +11,5 @@ ############################################################################## kill : Bug#37780 2008-12-03 HHunger need some changes to be robust enough for pushbuild. query_cache_28249 : Bug#43861 2009-03-25 main.query_cache_28249 fails sporadically -main.mysqlhotcopy_myisam : Bug#56817 2010-10-21 anitha mysqlhotcopy* fails -main.mysqlhotcopy_archive: Bug#56817 2010-10-21 anitha mysqlhotcopy* fails log_tables-big : Bug#48646 2010-11-15 mattiasj report already exists read_many_rows_innodb : Bug#37635 2010-11-15 mattiasj report already exists From e952ee1158be7611f3443bf9e1919652eb3b4602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 31 Jan 2011 09:56:51 +0200 Subject: [PATCH 20/42] Bug#59230 assert 0 row_upd_changes_ord_field_binary() in post-crash trx rollback or purge This patch does not relax the failing debug assertion during purge. That will be revisited once we have managed to repeat the assertion failure. row_upd_changes_ord_field_binary_func(): Renamed from row_upd_changes_ord_field_binary(). Add the parameter que_thr_t* in UNIV_DEBUG builds. When the off-page column cannot be retrieved, assert that the current transaction is a recovered one and that it is the one that is currently being rolled back. row_upd_changes_ord_field_binary(): A wrapper macro for row_upd_changes_ord_field_binary_func() that discards the que_thr_t* parameter unless UNIV_DEBUG is defined. row_purge_upd_exist_or_extern_func(): Renamed from row_purge_upd_exist_or_extern(). Add the parameter que_thr_t* in UNIV_DEBUG builds. row_purge_upd_exist_or_extern(): A wrapper macro for row_purge_upd_exist_or_extern_func() that discards the que_thr_t* parameter unless UNIV_DEBUG is defined. Make trx_roll_crash_recv_trx const. If there were a 'do not dereference' attribute, it would be appropriate as well. rb://588 approved by Jimmy Yang --- storage/innodb_plugin/ChangeLog | 7 +++++ storage/innodb_plugin/btr/btr0cur.c | 4 +-- storage/innodb_plugin/include/row0upd.h | 26 +++++++++++----- storage/innodb_plugin/row/row0purge.c | 21 ++++++++++--- storage/innodb_plugin/row/row0umod.c | 5 +-- storage/innodb_plugin/row/row0upd.c | 41 ++++++++++++++++--------- storage/innodb_plugin/trx/trx0roll.c | 4 +-- 7 files changed, 74 insertions(+), 34 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 18ab48f32a5..0cbdc8ed9d2 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,10 @@ +2011-01-31 The InnoDB Team + + * btr/btr0cur.c, include/row0upd.h, + row/row0purge.c, row/row0umod.c, row/row0upd.c: + Bug#59230 assert 0 row_upd_changes_ord_field_binary() + in post-crash rollback or purge + 2011-01-27 The InnoDB Team * btr/btr0cur.c: diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 874db3066b5..143135ef24c 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -1768,8 +1768,8 @@ btr_cur_update_in_place( NOT call it if index is secondary */ if (!dict_index_is_clust(index) - || row_upd_changes_ord_field_binary(NULL, NULL, - index, update)) { + || row_upd_changes_ord_field_binary(index, update, thr, + NULL, NULL)) { /* Remove possible hash index pointer to this record */ btr_search_update_hash_on_delete(cursor); diff --git a/storage/innodb_plugin/include/row0upd.h b/storage/innodb_plugin/include/row0upd.h index b61e6b6dca1..97b7ec49a17 100644 --- a/storage/innodb_plugin/include/row0upd.h +++ b/storage/innodb_plugin/include/row0upd.h @@ -280,19 +280,29 @@ NOTE: we compare the fields as binary strings! @return TRUE if update vector changes an ordering field in the index record */ UNIV_INTERN ibool -row_upd_changes_ord_field_binary( -/*=============================*/ +row_upd_changes_ord_field_binary_func( +/*==================================*/ + dict_index_t* index, /*!< in: index of the record */ + const upd_t* update, /*!< in: update vector for the row; NOTE: the + field numbers in this MUST be clustered index + positions! */ +#ifdef UNIV_DEBUG + const que_thr_t*thr, /*!< in: query thread */ +#endif /* UNIV_DEBUG */ const dtuple_t* row, /*!< in: old value of row, or NULL if the row and the data values in update are not known when this function is called, e.g., at compile time */ - const row_ext_t*ext, /*!< NULL, or prefixes of the externally + const row_ext_t*ext) /*!< NULL, or prefixes of the externally stored columns in the old row */ - dict_index_t* index, /*!< in: index of the record */ - const upd_t* update) /*!< in: update vector for the row; NOTE: the - field numbers in this MUST be clustered index - positions! */ - __attribute__((nonnull(3,4), warn_unused_result)); + __attribute__((nonnull(1,2), warn_unused_result)); +#ifdef UNIV_DEBUG +# define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \ + row_upd_changes_ord_field_binary_func(index,update,thr,row,ext) +#else /* UNIV_DEBUG */ +# define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \ + row_upd_changes_ord_field_binary_func(index,update,row,ext) +#endif /* UNIV_DEBUG */ /***********************************************************//** Checks if an update vector changes an ordering field of an index record. This function is fast if the update vector is short or the number of ordering diff --git a/storage/innodb_plugin/row/row0purge.c b/storage/innodb_plugin/row/row0purge.c index 8bf2ae0f458..c91ec2e8a3b 100644 --- a/storage/innodb_plugin/row/row0purge.c +++ b/storage/innodb_plugin/row/row0purge.c @@ -387,8 +387,11 @@ Purges an update of an existing record. Also purges an update of a delete marked record if that record contained an externally stored field. */ static void -row_purge_upd_exist_or_extern( -/*==========================*/ +row_purge_upd_exist_or_extern_func( +/*===============================*/ +#ifdef UNIV_DEBUG + const que_thr_t*thr, /*!< in: query thread */ +#endif /* UNIV_DEBUG */ purge_node_t* node) /*!< in: row purge node */ { mem_heap_t* heap; @@ -413,8 +416,8 @@ row_purge_upd_exist_or_extern( while (node->index != NULL) { index = node->index; - if (row_upd_changes_ord_field_binary(NULL, NULL, node->index, - node->update)) { + if (row_upd_changes_ord_field_binary(node->index, node->update, + thr, NULL, NULL)) { /* Build the older version of the index entry */ entry = row_build_index_entry(node->row, NULL, index, heap); @@ -496,6 +499,14 @@ skip_secondaries: } } +#ifdef UNIV_DEBUG +# define row_purge_upd_exist_or_extern(thr,node) \ + row_purge_upd_exist_or_extern_func(thr,node) +#else /* UNIV_DEBUG */ +# define row_purge_upd_exist_or_extern(thr,node) \ + row_purge_upd_exist_or_extern_func(node) +#endif /* UNIV_DEBUG */ + /***********************************************************//** Parses the row reference and other info in a modify undo log record. @return TRUE if purge operation required: NOTE that then the CALLER @@ -654,7 +665,7 @@ row_purge( } else if (updated_extern || node->rec_type == TRX_UNDO_UPD_EXIST_REC) { - row_purge_upd_exist_or_extern(node); + row_purge_upd_exist_or_extern(thr, node); } if (node->found_clust) { diff --git a/storage/innodb_plugin/row/row0umod.c b/storage/innodb_plugin/row/row0umod.c index 562f8093c38..f7736935489 100644 --- a/storage/innodb_plugin/row/row0umod.c +++ b/storage/innodb_plugin/row/row0umod.c @@ -668,8 +668,9 @@ row_undo_mod_upd_exist_sec( while (node->index != NULL) { index = node->index; - if (row_upd_changes_ord_field_binary( - node->row, node->ext, node->index, node->update)) { + if (row_upd_changes_ord_field_binary(node->index, node->update, + thr, + node->row, node->ext)) { /* Build the newest version of the index entry */ entry = row_build_index_entry(node->row, node->ext, diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 691d263e6ed..b5d4aeb434e 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1192,25 +1192,31 @@ NOTE: we compare the fields as binary strings! @return TRUE if update vector changes an ordering field in the index record */ UNIV_INTERN ibool -row_upd_changes_ord_field_binary( -/*=============================*/ +row_upd_changes_ord_field_binary_func( +/*==================================*/ + dict_index_t* index, /*!< in: index of the record */ + const upd_t* update, /*!< in: update vector for the row; NOTE: the + field numbers in this MUST be clustered index + positions! */ +#ifdef UNIV_DEBUG + const que_thr_t*thr, /*!< in: query thread */ +#endif /* UNIV_DEBUG */ const dtuple_t* row, /*!< in: old value of row, or NULL if the row and the data values in update are not known when this function is called, e.g., at compile time */ - const row_ext_t*ext, /*!< NULL, or prefixes of the externally + const row_ext_t*ext) /*!< NULL, or prefixes of the externally stored columns in the old row */ - dict_index_t* index, /*!< in: index of the record */ - const upd_t* update) /*!< in: update vector for the row; NOTE: the - field numbers in this MUST be clustered index - positions! */ { ulint n_unique; ulint i; const dict_index_t* clust_index; - ut_ad(update); ut_ad(index); + ut_ad(update); + ut_ad(thr); + ut_ad(thr->graph); + ut_ad(thr->graph->trx); n_unique = dict_index_get_n_unique(index); @@ -1263,9 +1269,14 @@ row_upd_changes_ord_field_binary( if (UNIV_LIKELY_NULL(buf)) { if (UNIV_UNLIKELY(buf == field_ref_zero)) { - /* This should never happen, but - we try to fail safe here. */ - ut_ad(0); + /* The externally stored field + was not written yet. This + record should only be seen by + recv_recovery_rollback_active(), + when the server had crashed before + storing the field. */ + ut_ad(thr->graph->trx->is_recovered); + ut_ad(trx_is_recv(thr->graph->trx)); return(TRUE); } @@ -1612,8 +1623,8 @@ row_upd_sec_step( ut_ad(!dict_index_is_clust(node->index)); if (node->state == UPD_NODE_UPDATE_ALL_SEC - || row_upd_changes_ord_field_binary(node->row, node->ext, - node->index, node->update)) { + || row_upd_changes_ord_field_binary(node->index, node->update, + thr, node->row, node->ext)) { return(row_upd_sec_index_entry(node, thr)); } @@ -2140,8 +2151,8 @@ exit_func: row_upd_store_row(node); - if (row_upd_changes_ord_field_binary(node->row, node->ext, index, - node->update)) { + if (row_upd_changes_ord_field_binary(index, node->update, thr, + node->row, node->ext)) { /* Update causes an ordering field (ordering fields within the B-tree) of the clustered index record to change: perform diff --git a/storage/innodb_plugin/trx/trx0roll.c b/storage/innodb_plugin/trx/trx0roll.c index 1a43e419214..a4bbf7fd652 100644 --- a/storage/innodb_plugin/trx/trx0roll.c +++ b/storage/innodb_plugin/trx/trx0roll.c @@ -48,8 +48,8 @@ Created 3/26/1996 Heikki Tuuri rollback */ #define TRX_ROLL_TRUNC_THRESHOLD 1 -/** In crash recovery, the current trx to be rolled back */ -static trx_t* trx_roll_crash_recv_trx = NULL; +/** In crash recovery, the current trx to be rolled back; NULL otherwise */ +static const trx_t* trx_roll_crash_recv_trx = NULL; /** In crash recovery we set this to the undo n:o of the current trx to be rolled back. Then we can print how many % the rollback has progressed. */ From 2d2f80e07e858f157a0823c5e886c3589cd1a435 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Mon, 31 Jan 2011 14:31:33 +0000 Subject: [PATCH 21/42] Post-fix for BUG#59338. --- mysql-test/suite/rpl/r/rpl_insert_ignore.result | 4 ++-- mysql-test/suite/rpl/t/rpl_insert_ignore.test | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_insert_ignore.result b/mysql-test/suite/rpl/r/rpl_insert_ignore.result index 5d9c4aec9f7..63b9244a40d 100644 --- a/mysql-test/suite/rpl/r/rpl_insert_ignore.result +++ b/mysql-test/suite/rpl/r/rpl_insert_ignore.result @@ -30,11 +30,11 @@ CREATE TABLE t1 ( a int unsigned not null auto_increment primary key, b int unsigned, unique (b) -) ENGINE=innodb; +) ENGINE=myisam; CREATE TABLE t2 ( a int unsigned, # to force INSERT SELECT to have a certain order b int unsigned -) ENGINE=innodb; +) ENGINE=myisam; INSERT INTO t1 VALUES (NULL, 1); INSERT INTO t1 VALUES (NULL, 2); INSERT INTO t1 VALUES (NULL, 3); diff --git a/mysql-test/suite/rpl/t/rpl_insert_ignore.test b/mysql-test/suite/rpl/t/rpl_insert_ignore.test index 265a52fb51d..4129d4d553e 100644 --- a/mysql-test/suite/rpl/t/rpl_insert_ignore.test +++ b/mysql-test/suite/rpl/t/rpl_insert_ignore.test @@ -8,7 +8,7 @@ -- let $engine_type=innodb -- source extra/rpl_tests/rpl_insert_ignore.test --- let $engine_type=innodb +-- let $engine_type=myisam -- source extra/rpl_tests/rpl_insert_ignore.test --source include/rpl_end.inc From 59269b1da1552ae21e935c04588844c573e9c0c3 Mon Sep 17 00:00:00 2001 From: Ole John Aske Date: Tue, 1 Feb 2011 13:20:16 +0100 Subject: [PATCH 22/42] Fix for bug#57030: ('BETWEEN' evaluation is incorrect') Root cause for this bug is that the optimizer try to detect& optimize the special case: ' BETWEEN c1 AND c1' and handle this as the condition ' = c1' This was implemented inside add_key_field(.. *field, *value[]...) which assumed field to refer key Field, and value[] to refer a [low...high] constant pair. value[0] and value[1] was then compared for equality. In a 'normal' BETWEEN condition of the form ' BETWEEN val1 and val2' the BETWEEN operation is represented with an argementlist containing the values [, val1, val2] - add_key_field() is then called with parameters field=, *value=val1. However, if the BETWEEN predicate specified: 1) ' BETWEEN AND the 'field' and 'value' arguments to add_key_field() had to be swapped. This was implemented by trying to cheat add_key_field() to handle it like: 2) ' GE AND LE' As we didn't really replace the BETWEEN operation with 'ge' and 'le', add_key_field() still handled it as a 'BETWEEN' and compared the (swapped) arguments and for equality. If they was equal, the condition 1) was incorrectly 'optimized' to: 3) ' EQ ' This fix moves this optimization of ' BETWEEN c1 AND c1' into add_key_fields() which then calls add_key_equal_fields() to collect key equality / comparison for the key fields in the BETWEEN condition. --- mysql-test/r/range.result | 101 ++++++++++++++++++++++++++++++++++++++ mysql-test/t/range.test | 67 +++++++++++++++++++++++++ sql/sql_select.cc | 98 ++++++++++++++++++++++-------------- 3 files changed, 228 insertions(+), 38 deletions(-) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index d989896514c..ae63edf87b9 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -1666,4 +1666,105 @@ c_key c_notkey 1 1 3 3 DROP TABLE t1; +# +# Bug #57030: 'BETWEEN' evaluation is incorrect +# +CREATE TABLE t1(pk INT PRIMARY KEY, i4 INT); +CREATE UNIQUE INDEX i4_uq ON t1(i4); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30); +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const i4_uq i4_uq 5 const 1 +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; +pk i4 +1 10 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const i4_uq i4_uq 5 const 1 +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; +pk i4 +1 10 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 3 Using where +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; +pk i4 +1 10 +2 20 +3 30 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 1 Using where +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; +pk i4 +1 10 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; +pk i4 +1 10 +2 20 +3 30 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 2 Using where +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; +pk i4 +1 10 +2 20 +3 30 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 1 Using where +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; +pk i4 +1 10 +2 20 +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL i4_uq NULL NULL NULL 3 +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.i4 1 Using where +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; +pk i4 pk i4 +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL i4_uq NULL NULL NULL 3 +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.i4 1 Using where +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; +pk i4 pk i4 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 0ad3d3e8504..6c9320b708a 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -1325,4 +1325,71 @@ SELECT * FROM t1 WHERE 2 NOT BETWEEN c_notkey AND c_key; DROP TABLE t1; +--echo # +--echo # Bug #57030: 'BETWEEN' evaluation is incorrect +--echo # + +# Test some BETWEEN predicates which does *not* follow the +# 'normal' pattern of BETWEEN AND + +CREATE TABLE t1(pk INT PRIMARY KEY, i4 INT); +CREATE UNIQUE INDEX i4_uq ON t1(i4); + +INSERT INTO t1 VALUES (1,10), (2,20), (3,30); + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; + +#Should detect the EQ_REF 't2.pk=t1.i4' +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; + +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; + +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8cc2ec6a0f8..e080dc9370c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3349,26 +3349,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, eq_func is NEVER true when num_values > 1 */ if (!eq_func) - { - /* - Additional optimization: if we're processing - "t.key BETWEEN c1 AND c1" then proceed as if we were processing - "t.key = c1". - TODO: This is a very limited fix. A more generic fix is possible. - There are 2 options: - A) Make equality propagation code be able to handle BETWEEN - (including cases like t1.key BETWEEN t2.key AND t3.key) - B) Make range optimizer to infer additional "t.key = c" equalities - and use them in equality propagation process (see details in - OptimizerKBAndTodo) - */ - if ((cond->functype() != Item_func::BETWEEN) || - ((Item_func_between*) cond)->negated || - !value[0]->eq(value[1], field->binary())) - return; - eq_func= TRUE; - } - + return; if (field->result_type() == STRING_RESULT) { if ((*value)->result_type() != STRING_RESULT) @@ -3564,9 +3545,65 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, case Item_func::OPTIMIZE_KEY: { Item **values; - // BETWEEN, IN, NE - if (is_local_field (cond_func->key_item()) && - !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) + /* + Build list of possible keys for 'a BETWEEN low AND high'. + It is handled similar to the equivalent condition + 'a >= low AND a <= high': + */ + if (cond_func->functype() == Item_func::BETWEEN) + { + Item_field *field_item; + bool equal_func= FALSE; + uint num_values= 2; + values= cond_func->arguments(); + + bool binary_cmp= (values[0]->real_item()->type() == Item::FIELD_ITEM) + ? ((Item_field*)values[0]->real_item())->field->binary() + : TRUE; + + /* + Additional optimization: If 'low = high': + Handle as if the condition was "t.key = low". + */ + if (!((Item_func_between*)cond_func)->negated && + values[1]->eq(values[2], binary_cmp)) + { + equal_func= TRUE; + num_values= 1; + } + + /* + Append keys for 'field value[]' if the + condition is of the form:: + ' BETWEEN value[1] AND value[2]' + */ + if (is_local_field (values[0])) + { + field_item= (Item_field *) (values[0]->real_item()); + add_key_equal_fields(key_fields, *and_level, cond_func, + field_item, equal_func, &values[1], + num_values, usable_tables, sargables); + } + /* + Append keys for 'value[0] field' if the + condition is of the form: + 'value[0] BETWEEN field1 AND field2' + */ + for (uint i= 1; i <= num_values; i++) + { + if (is_local_field (values[i])) + { + field_item= (Item_field *) (values[i]->real_item()); + add_key_equal_fields(key_fields, *and_level, cond_func, + field_item, equal_func, values, + 1, usable_tables, sargables); + } + } + } // if ( ... Item_func::BETWEEN) + + // IN, NE + else if (is_local_field (cond_func->key_item()) && + !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) { values= cond_func->arguments()+1; if (cond_func->functype() == Item_func::NE_FUNC && @@ -3580,21 +3617,6 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, cond_func->argument_count()-1, usable_tables, sargables); } - if (cond_func->functype() == Item_func::BETWEEN) - { - values= cond_func->arguments(); - for (uint i= 1 ; i < cond_func->argument_count() ; i++) - { - Item_field *field_item; - if (is_local_field (cond_func->arguments()[i])) - { - field_item= (Item_field *) (cond_func->arguments()[i]->real_item()); - add_key_equal_fields(key_fields, *and_level, cond_func, - field_item, 0, values, 1, usable_tables, - sargables); - } - } - } break; } case Item_func::OPTIMIZE_OP: From 1f796c2b646824aeac2927599ba3e51594cd3534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 14:05:12 +0200 Subject: [PATCH 23/42] Bug #55284 diagnostics: Enable UNIV_DEBUG_FILE_ACCESSES by UNIV_DEBUG It was the enabling of UNIV_DEBUG_FILE_ACCESSES that caught Bug #55284 in the first place. This is a very light piece of of debug code, and there really is no reason why it is not enabled in all debug builds. rb://551 approved by Jimmy Yang --- storage/innodb_plugin/buf/buf0buf.c | 30 +++++++++++++------------ storage/innodb_plugin/fsp/fsp0fsp.c | 8 +++---- storage/innodb_plugin/ibuf/ibuf0ibuf.c | 8 +++---- storage/innodb_plugin/include/buf0buf.h | 8 +++---- storage/innodb_plugin/include/univ.i | 5 ++--- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/storage/innodb_plugin/buf/buf0buf.c b/storage/innodb_plugin/buf/buf0buf.c index 6e76e4c65be..6bbd5565c58 100644 --- a/storage/innodb_plugin/buf/buf0buf.c +++ b/storage/innodb_plugin/buf/buf0buf.c @@ -657,9 +657,9 @@ buf_block_init( block->modify_clock = 0; -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG block->page.file_page_was_freed = FALSE; -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ block->check_index_page_at_flush = FALSE; block->index = NULL; @@ -1600,7 +1600,7 @@ buf_page_peek_if_search_hashed( return(is_hashed); } -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG /********************************************************************//** Sets file_page_was_freed TRUE if the page is found in the buffer pool. This function should be called when we free a file page and want the @@ -1621,6 +1621,8 @@ buf_page_set_file_page_was_freed( bpage = buf_page_hash_get(space, offset); if (bpage) { + /* bpage->file_page_was_freed can already hold + when this code is invoked from dict_drop_index_tree() */ bpage->file_page_was_freed = TRUE; } @@ -1656,7 +1658,7 @@ buf_page_reset_file_page_was_freed( return(bpage); } -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ /********************************************************************//** Get read access to a compressed page (usually of type @@ -1753,7 +1755,7 @@ got_block: buf_page_set_accessed_make_young(bpage, access_time); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(!bpage->file_page_was_freed); #endif @@ -2321,7 +2323,7 @@ wait_until_unfixed: buf_page_set_accessed_make_young(&block->page, access_time); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(!block->page.file_page_was_freed); #endif @@ -2479,7 +2481,7 @@ buf_page_optimistic_get( ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(block->page.file_page_was_freed == FALSE); #endif if (UNIV_UNLIKELY(!access_time)) { @@ -2587,7 +2589,7 @@ buf_page_get_known_nowait( ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(block->page.file_page_was_freed == FALSE); #endif @@ -2670,9 +2672,9 @@ buf_page_try_get_func( ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ut_a(block->page.file_page_was_freed == FALSE); -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); buf_pool->stat.n_page_gets++; @@ -2701,9 +2703,9 @@ buf_page_init_low( bpage->newest_modification = 0; bpage->oldest_modification = 0; HASH_INVALIDATE(bpage, hash); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG bpage->file_page_was_freed = FALSE; -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ } /********************************************************************//** @@ -3009,9 +3011,9 @@ buf_page_create( #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(space, offset) == 0); #endif -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG block->page.file_page_was_freed = FALSE; -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ /* Page can be found in buf_pool */ buf_pool_mutex_exit(); diff --git a/storage/innodb_plugin/fsp/fsp0fsp.c b/storage/innodb_plugin/fsp/fsp0fsp.c index e9d24b8fdf6..d091a14c474 100644 --- a/storage/innodb_plugin/fsp/fsp0fsp.c +++ b/storage/innodb_plugin/fsp/fsp0fsp.c @@ -3444,9 +3444,9 @@ fseg_free_page( fseg_free_page_low(seg_inode, space, zip_size, page, mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG buf_page_set_file_page_was_freed(space, page); -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ } /**********************************************************************//** @@ -3513,13 +3513,13 @@ fseg_free_extent( fsp_free_extent(space, zip_size, page, mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG for (i = 0; i < FSP_EXTENT_SIZE; i++) { buf_page_set_file_page_was_freed(space, first_page_in_extent + i); } -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ } /**********************************************************************//** diff --git a/storage/innodb_plugin/ibuf/ibuf0ibuf.c b/storage/innodb_plugin/ibuf/ibuf0ibuf.c index 701e8f0ef04..23981ac388e 100644 --- a/storage/innodb_plugin/ibuf/ibuf0ibuf.c +++ b/storage/innodb_plugin/ibuf/ibuf0ibuf.c @@ -1878,9 +1878,9 @@ ibuf_remove_free_page(void) fseg_free_page(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, IBUF_SPACE_ID, page_no, &mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG buf_page_reset_file_page_was_freed(IBUF_SPACE_ID, page_no); -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ ibuf_enter(); @@ -1922,9 +1922,9 @@ ibuf_remove_free_page(void) ibuf_bitmap_page_set_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_IBUF, FALSE, &mtr); -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG buf_page_set_file_page_was_freed(IBUF_SPACE_ID, page_no); -#endif +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ mtr_commit(&mtr); mutex_exit(&ibuf_mutex); diff --git a/storage/innodb_plugin/include/buf0buf.h b/storage/innodb_plugin/include/buf0buf.h index d903b443920..a16de67aa3a 100644 --- a/storage/innodb_plugin/include/buf0buf.h +++ b/storage/innodb_plugin/include/buf0buf.h @@ -368,7 +368,7 @@ buf_reset_check_index_page_at_flush( /*================================*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: page number */ -#ifdef UNIV_DEBUG_FILE_ACCESSES +#if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG /********************************************************************//** Sets file_page_was_freed TRUE if the page is found in the buffer pool. This function should be called when we free a file page and want the @@ -393,7 +393,7 @@ buf_page_reset_file_page_was_freed( /*===============================*/ ulint space, /*!< in: space id */ ulint offset); /*!< in: page number */ -#endif /* UNIV_DEBUG_FILE_ACCESSES */ +#endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ /********************************************************************//** Reads the freed_page_clock of a buffer block. @return freed_page_clock */ @@ -1135,11 +1135,11 @@ struct buf_page_struct{ 0 if the block was never accessed in the buffer pool */ /* @} */ -# ifdef UNIV_DEBUG_FILE_ACCESSES +# if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG ibool file_page_was_freed; /*!< this is set to TRUE when fsp frees a page in buffer pool */ -# endif /* UNIV_DEBUG_FILE_ACCESSES */ +# endif /* UNIV_DEBUG_FILE_ACCESSES || UNIV_DEBUG */ #endif /* !UNIV_HOTBACKUP */ }; diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index 4425950748b..4ba48b10896 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -182,9 +182,8 @@ command. Not tested on Windows. */ #define UNIV_DEBUG_LOCK_VALIDATE /* Enable ut_ad(lock_rec_validate_page()) assertions. */ -#define UNIV_DEBUG_FILE_ACCESSES /* Debug .ibd file access - (field file_page_was_freed - in buf_page_t) */ +#define UNIV_DEBUG_FILE_ACCESSES /* Enable freed block access + debugging without UNIV_DEBUG */ #define UNIV_LRU_DEBUG /* debug the buffer pool LRU */ #define UNIV_HASH_DEBUG /* debug HASH_ macros */ #define UNIV_LIST_DEBUG /* debug UT_LIST_ macros */ From f2eacde4cd905303ff906d9e33308b8ba03c17f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 14:10:12 +0200 Subject: [PATCH 24/42] Bug #55284 diagnostics: When UNIV_DEBUG, do not tolerate garbage in Antelope files in btr_check_blob_fil_page_type(). Unfortunately, we must keep the check in production builds, because InnoDB wrote uninitialized garbage to FIL_PAGE_TYPE until fairly recently (5.1.x). rb://546 approved by Jimmy Yang --- storage/innodb_plugin/btr/btr0cur.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 143135ef24c..46cd4a81ec5 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -4169,6 +4169,7 @@ btr_check_blob_fil_page_type( if (UNIV_UNLIKELY(type != FIL_PAGE_TYPE_BLOB)) { ulint flags = fil_space_get_flags(space_id); +#ifndef UNIV_DEBUG /* Improve debug test coverage */ if (UNIV_LIKELY ((flags & DICT_TF_FORMAT_MASK) == DICT_TF_FORMAT_51)) { /* Old versions of InnoDB did not initialize @@ -4177,6 +4178,7 @@ btr_check_blob_fil_page_type( a BLOB page that is in Antelope format.*/ return; } +#endif /* !UNIV_DEBUG */ ut_print_timestamp(stderr); fprintf(stderr, From 7c45708f5d29747da043aea144c2f8ba8c142c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 14:12:49 +0200 Subject: [PATCH 25/42] Non-functional changes (cleanup) made while narrowing down Bug #55284: row_purge(): Change the return type to void. (The return value always was DB_SUCCESS.) Remove some local variables. row_undo_mod_remove_clust_low(): Remove some local variables. rb://547 approved by Jimmy Yang --- storage/innodb_plugin/row/row0purge.c | 47 +++++++-------------------- storage/innodb_plugin/row/row0umod.c | 30 +++++------------ 2 files changed, 19 insertions(+), 58 deletions(-) diff --git a/storage/innodb_plugin/row/row0purge.c b/storage/innodb_plugin/row/row0purge.c index c91ec2e8a3b..752a2ec9e83 100644 --- a/storage/innodb_plugin/row/row0purge.c +++ b/storage/innodb_plugin/row/row0purge.c @@ -613,47 +613,32 @@ err_exit: /***********************************************************//** Fetches an undo log record and does the purge for the recorded operation. If none left, or the current purge completed, returns the control to the -parent node, which is always a query thread node. -@return DB_SUCCESS if operation successfully completed, else error code */ -static -ulint +parent node, which is always a query thread node. */ +static __attribute__((nonnull)) +void row_purge( /*======*/ purge_node_t* node, /*!< in: row purge node */ que_thr_t* thr) /*!< in: query thread */ { - roll_ptr_t roll_ptr; - ibool purge_needed; ibool updated_extern; - trx_t* trx; - ut_ad(node && thr); + ut_ad(node); + ut_ad(thr); - trx = thr_get_trx(thr); - - node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr, - &(node->reservation), + node->undo_rec = trx_purge_fetch_next_rec(&node->roll_ptr, + &node->reservation, node->heap); if (!node->undo_rec) { /* Purge completed for this query thread */ thr->run_node = que_node_get_parent(node); - return(DB_SUCCESS); + return; } - node->roll_ptr = roll_ptr; - - if (node->undo_rec == &trx_purge_dummy_rec) { - purge_needed = FALSE; - } else { - purge_needed = row_purge_parse_undo_rec(node, &updated_extern, - thr); - /* If purge_needed == TRUE, we must also remember to unfreeze - data dictionary! */ - } - - if (purge_needed) { + if (node->undo_rec != &trx_purge_dummy_rec + && row_purge_parse_undo_rec(node, &updated_extern, thr)) { node->found_clust = FALSE; node->index = dict_table_get_next_index( @@ -672,7 +657,7 @@ row_purge( btr_pcur_close(&(node->pcur)); } - row_mysql_unfreeze_data_dictionary(trx); + row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); } /* Do some cleanup */ @@ -680,8 +665,6 @@ row_purge( mem_heap_empty(node->heap); thr->run_node = node; - - return(DB_SUCCESS); } /***********************************************************//** @@ -695,9 +678,6 @@ row_purge_step( que_thr_t* thr) /*!< in: query thread */ { purge_node_t* node; -#ifdef UNIV_DEBUG - ulint err; -#endif /* UNIV_DEBUG */ ut_ad(thr); @@ -705,12 +685,7 @@ row_purge_step( ut_ad(que_node_get_type(node) == QUE_NODE_PURGE); -#ifdef UNIV_DEBUG - err = -#endif /* UNIV_DEBUG */ row_purge(node, thr); - ut_ad(err == DB_SUCCESS); - return(thr); } diff --git a/storage/innodb_plugin/row/row0umod.c b/storage/innodb_plugin/row/row0umod.c index f7736935489..5202a498eed 100644 --- a/storage/innodb_plugin/row/row0umod.c +++ b/storage/innodb_plugin/row/row0umod.c @@ -173,40 +173,26 @@ row_undo_mod_remove_clust_low( mtr_t* mtr, /*!< in: mtr */ ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { - btr_pcur_t* pcur; btr_cur_t* btr_cur; ulint err; - ibool success; ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC); - pcur = &(node->pcur); - btr_cur = btr_pcur_get_btr_cur(pcur); - success = btr_pcur_restore_position(mode, pcur, mtr); + /* Find out if the record has been purged already + or if we can remove it. */ - if (!success) { + if (!btr_pcur_restore_position(mode, &node->pcur, mtr) + || row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) { return(DB_SUCCESS); } - /* Find out if we can remove the whole clustered index record */ - - if (node->rec_type == TRX_UNDO_UPD_DEL_REC - && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) { - - /* Ok, we can remove */ - } else { - return(DB_SUCCESS); - } + btr_cur = btr_pcur_get_btr_cur(&node->pcur); if (mode == BTR_MODIFY_LEAF) { - success = btr_cur_optimistic_delete(btr_cur, mtr); - - if (success) { - err = DB_SUCCESS; - } else { - err = DB_FAIL; - } + err = btr_cur_optimistic_delete(btr_cur, mtr) + ? DB_SUCCESS + : DB_FAIL; } else { ut_ad(mode == BTR_MODIFY_TREE); From e805a0fd9de24cf1a9a6fb7bd10a336dbcd46fe9 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Wed, 2 Feb 2011 16:17:48 +0300 Subject: [PATCH 26/42] Fix for bug #58650 "Failing assertion: primary_key_no == -1 || primary_key_no == 0". Attempt to create InnoDB table with non-nullable column of geometry type having an unique key with length 12 on it and with some other candidate key led to server crash due to assertion failure in both non-debug and debug builds. The problem was that such a non-candidate key could have been sorted as the first key in table/.FRM, before any legit candidate keys. This resulted in assertion failure in InnoDB engine which assumes that primary key should either be the first key in table/.FRM or should not exist at all. The reason behind such an incorrect sorting was an wrong value of Create_field::key_length member for geometry field (which was set to its pack_length == 12) which confused code in mysql_prepare_create_table(), so it would skip marking such key as a key with partial segments. This patch fixes the problem by ensuring that this member gets the same value of Create_field::key_length member as for other blob fields (from which geometry field class is inherited), and as result unique keys on geometry fields are correctly marked as having partial segments. mysql-test/include/gis_keys.inc: Added test case for bug #58650 "Failing assertion: primary_key_no == -1 || primary_key_no == 0". mysql-test/r/gis.result: Added test case for bug #58650 "Failing assertion: primary_key_no == -1 || primary_key_no == 0". mysql-test/suite/innodb/r/innodb_gis.result: Added test case for bug #58650 "Failing assertion: primary_key_no == -1 || primary_key_no == 0". mysql-test/suite/innodb_plugin/r/innodb_gis.result: Added test case for bug #58650 "Failing assertion: primary_key_no == -1 || primary_key_no == 0". sql/field.cc: Changed Create_field::create_length_to_internal_length() to correctly set Create_field::key_length member for geometry fields. Similar to the blob types key_length for such fields should be the same as length and not field's packed length (which is always 12 for geometry). As result of this change code handling table creation now always correctly identifies btree/unique keys on geometry fields as partial keys, so such keys can't be erroneously treated as candidate keys and sorted in keys array in .FRM before legit candidate keys. This fixes bug #58650 "Failing assertion: primary_key_no == -1 || primary_key_no == 0" in which incorrect candidate key sorting led to assertion failure in InnoDB code. --- mysql-test/include/gis_keys.inc | 16 ++++++++++++++++ mysql-test/r/gis.result | 12 ++++++++++++ mysql-test/suite/innodb/r/innodb_gis.result | 12 ++++++++++++ .../suite/innodb_plugin/r/innodb_gis.result | 12 ++++++++++++ sql/field.cc | 1 + 5 files changed, 53 insertions(+) diff --git a/mysql-test/include/gis_keys.inc b/mysql-test/include/gis_keys.inc index c75311f062a..ad00c7e1ef9 100644 --- a/mysql-test/include/gis_keys.inc +++ b/mysql-test/include/gis_keys.inc @@ -44,3 +44,19 @@ SELECT COUNT(*) FROM t2 IGNORE INDEX(p) WHERE p=POINTFROMTEXT('POINT(1 2)'); DROP TABLE t1, t2; --echo End of 5.0 tests + + +--echo # +--echo # Test for bug #58650 "Failing assertion: primary_key_no == -1 || +--echo # primary_key_no == 0". +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings +--echo # The minimal test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12)), unique key a (a)); +drop table t1; +--echo # The original test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12))); +create unique index a on t1(a); +drop table t1; diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index f4aa361ffcf..a9beb9631ae 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -960,6 +960,18 @@ COUNT(*) 2 DROP TABLE t1, t2; End of 5.0 tests +# +# Test for bug #58650 "Failing assertion: primary_key_no == -1 || +# primary_key_no == 0". +# +drop table if exists t1; +# The minimal test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12)), unique key a (a)); +drop table t1; +# The original test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12))); +create unique index a on t1(a); +drop table t1; create table `t1` (`col002` point)engine=myisam; insert into t1 values (),(),(); select min(`col002`) from t1 union select `col002` from t1; diff --git a/mysql-test/suite/innodb/r/innodb_gis.result b/mysql-test/suite/innodb/r/innodb_gis.result index c6c775afc9f..a52a1387b6e 100644 --- a/mysql-test/suite/innodb/r/innodb_gis.result +++ b/mysql-test/suite/innodb/r/innodb_gis.result @@ -585,5 +585,17 @@ COUNT(*) 2 DROP TABLE t1, t2; End of 5.0 tests +# +# Test for bug #58650 "Failing assertion: primary_key_no == -1 || +# primary_key_no == 0". +# +drop table if exists t1; +# The minimal test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12)), unique key a (a)); +drop table t1; +# The original test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12))); +create unique index a on t1(a); +drop table t1; create table t1 (g geometry not null, spatial gk(g)) engine=innodb; ERROR HY000: The used table type doesn't support SPATIAL indexes diff --git a/mysql-test/suite/innodb_plugin/r/innodb_gis.result b/mysql-test/suite/innodb_plugin/r/innodb_gis.result index c6c775afc9f..a52a1387b6e 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb_gis.result +++ b/mysql-test/suite/innodb_plugin/r/innodb_gis.result @@ -585,5 +585,17 @@ COUNT(*) 2 DROP TABLE t1, t2; End of 5.0 tests +# +# Test for bug #58650 "Failing assertion: primary_key_no == -1 || +# primary_key_no == 0". +# +drop table if exists t1; +# The minimal test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12)), unique key a (a)); +drop table t1; +# The original test case. +create table t1 (a int not null, b linestring not null, unique key b (b(12))); +create unique index a on t1(a); +drop table t1; create table t1 (g geometry not null, spatial gk(g)) engine=innodb; ERROR HY000: The used table type doesn't support SPATIAL indexes diff --git a/sql/field.cc b/sql/field.cc index 11edd8bfc1a..1ad5e408e07 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -9476,6 +9476,7 @@ void Create_field::create_length_to_internal_length(void) case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VARCHAR: From 5adf2313f713821841d979472eef0d7b07658965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 2 Feb 2011 15:51:08 +0200 Subject: [PATCH 27/42] Bug #55284 diagnostics: Introduce UNIV_BLOB_LIGHT_DEBUG, enabled by UNIV_DEBUG btr_rec_get_field_ref_offs(), btr_rec_get_field_ref(): New functions. Get the pointer to an externally stored field. btr_cur_set_ownership_of_extern_field(): Assert that the BLOB has not already been disowned. btr_store_big_rec_extern_fields(): Rename to btr_store_big_rec_extern_fields_func() and add the debug parameter update_in_place. All pointers to externally stored columns in the record must either be zero or they must be pointers to inherited columns, owned by this record or an earlier record version. For any BLOB that is stored, the BLOB pointer must previously have been zero. When the function completes, all BLOB pointers must be nonzero and owned by the record. rb://549 approved by Jimmy Yang --- storage/innodb_plugin/btr/btr0cur.c | 150 ++++++++++++++++-------- storage/innodb_plugin/include/btr0cur.h | 42 ++++++- storage/innodb_plugin/include/univ.i | 2 + storage/innodb_plugin/row/row0ins.c | 2 +- storage/innodb_plugin/row/row0upd.c | 2 +- 5 files changed, 144 insertions(+), 54 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 46cd4a81ec5..704cc606a5f 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -186,7 +186,7 @@ static ulint btr_rec_get_externally_stored_len( /*==============================*/ - rec_t* rec, /*!< in: record */ + const rec_t* rec, /*!< in: record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ #endif /* !UNIV_HOTBACKUP */ @@ -3483,6 +3483,35 @@ btr_estimate_number_of_different_key_vals( /*================== EXTERNAL STORAGE OF BIG FIELDS ===================*/ +/***********************************************************//** +Gets the offset of the pointer to the externally stored part of a field. +@return offset of the pointer to the externally stored part */ +static +ulint +btr_rec_get_field_ref_offs( +/*=======================*/ + const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ + ulint n) /*!< in: index of the external field */ +{ + ulint field_ref_offs; + ulint local_len; + + ut_a(rec_offs_nth_extern(offsets, n)); + field_ref_offs = rec_get_nth_field_offs(offsets, n, &local_len); + ut_a(local_len != UNIV_SQL_NULL); + ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); + + return(field_ref_offs + local_len - BTR_EXTERN_FIELD_REF_SIZE); +} + +/** Gets a pointer to the externally stored part of a field. +@param rec record +@param offsets rec_get_offsets(rec) +@param n index of the externally stored field +@return pointer to the externally stored part */ +#define btr_rec_get_field_ref(rec, offsets, n) \ + ((rec) + btr_rec_get_field_ref_offs(offsets, n)) + /***********************************************************//** Gets the externally stored size of a record, in units of a database page. @return externally stored part, in units of a database page */ @@ -3490,28 +3519,27 @@ static ulint btr_rec_get_externally_stored_len( /*==============================*/ - rec_t* rec, /*!< in: record */ + const rec_t* rec, /*!< in: record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint n_fields; - byte* data; - ulint local_len; - ulint extern_len; ulint total_extern_len = 0; ulint i; ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); + + if (!rec_offs_any_extern(offsets)) { + return(0); + } + n_fields = rec_offs_n_fields(offsets); for (i = 0; i < n_fields; i++) { if (rec_offs_nth_extern(offsets, i)) { - data = rec_get_nth_field(rec, offsets, i, &local_len); - - local_len -= BTR_EXTERN_FIELD_REF_SIZE; - - extern_len = mach_read_from_4(data + local_len - + BTR_EXTERN_LEN + 4); + ulint extern_len = mach_read_from_4( + btr_rec_get_field_ref(rec, offsets, i) + + BTR_EXTERN_LEN + 4); total_extern_len += ut_calc_align(extern_len, UNIV_PAGE_SIZE); @@ -3541,7 +3569,7 @@ btr_cur_set_ownership_of_extern_field( ulint byte_val; data = rec_get_nth_field(rec, offsets, i, &local_len); - + ut_ad(rec_offs_nth_extern(offsets, i)); ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; @@ -3551,6 +3579,9 @@ btr_cur_set_ownership_of_extern_field( if (val) { byte_val = byte_val & (~BTR_EXTERN_OWNER_FLAG); } else { +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + ut_a(!(byte_val & BTR_EXTERN_OWNER_FLAG)); +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ byte_val = byte_val | BTR_EXTERN_OWNER_FLAG; } @@ -3788,8 +3819,8 @@ file segment of the index tree. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ UNIV_INTERN ulint -btr_store_big_rec_extern_fields( -/*============================*/ +btr_store_big_rec_extern_fields_func( +/*=================================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ buf_block_t* rec_block, /*!< in/out: block containing rec */ @@ -3798,11 +3829,17 @@ btr_store_big_rec_extern_fields( the "external storage" flags in offsets will not correspond to rec when this function returns */ - big_rec_t* big_rec_vec, /*!< in: vector containing fields +#ifdef UNIV_DEBUG + mtr_t* local_mtr, /*!< in: mtr containing the + latch to rec and to the tree */ +#endif /* UNIV_DEBUG */ +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + ibool update_in_place,/*! in: TRUE if the record is updated + in place (not delete+insert) */ +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ + const big_rec_t*big_rec_vec) /*!< in: vector containing fields to be stored externally */ - mtr_t* local_mtr __attribute__((unused))) /*!< in: mtr - containing the latch to rec and to the - tree */ + { ulint rec_page_no; byte* field_ref; @@ -3820,6 +3857,7 @@ btr_store_big_rec_extern_fields( z_stream c_stream; ut_ad(rec_offs_validate(rec, index, offsets)); + ut_ad(rec_offs_any_extern(offsets)); ut_ad(mtr_memo_contains(local_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(local_mtr, rec_block, MTR_MEMO_PAGE_X_FIX)); @@ -3851,21 +3889,37 @@ btr_store_big_rec_extern_fields( ut_a(err == Z_OK); } +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + /* All pointers to externally stored columns in the record + must either be zero or they must be pointers to inherited + columns, owned by this record or an earlier record version. */ + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (!rec_offs_nth_extern(offsets, i)) { + continue; + } + field_ref = btr_rec_get_field_ref(rec, offsets, i); + + ut_a(!(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG)); + /* Either this must be an update in place, + or the BLOB must be inherited, or the BLOB pointer + must be zero (will be written in this function). */ + ut_a(update_in_place + || (field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_INHERITED_FLAG) + || !memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + } +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ /* We have to create a file segment to the tablespace for each field and put the pointer to the field in rec */ for (i = 0; i < big_rec_vec->n_fields; i++) { - ut_ad(rec_offs_nth_extern(offsets, - big_rec_vec->fields[i].field_no)); - { - ulint local_len; - field_ref = rec_get_nth_field( - rec, offsets, big_rec_vec->fields[i].field_no, - &local_len); - ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); - local_len -= BTR_EXTERN_FIELD_REF_SIZE; - field_ref += local_len; - } + field_ref = btr_rec_get_field_ref( + rec, offsets, big_rec_vec->fields[i].field_no); +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + /* A zero BLOB pointer should have been initially inserted. */ + ut_a(!memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ extern_len = big_rec_vec->fields[i].len; UNIV_MEM_ASSERT_RW(big_rec_vec->fields[i].data, extern_len); @@ -4147,6 +4201,23 @@ next_zip_page: mem_heap_free(heap); } +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + /* All pointers to externally stored columns in the record + must be valid. */ + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (!rec_offs_nth_extern(offsets, i)) { + continue; + } + + field_ref = btr_rec_get_field_ref(rec, offsets, i); + + /* The pointer must not be zero. */ + ut_a(0 != memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + /* The column must not be disowned by this record. */ + ut_a(!(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG)); + } +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ return(DB_SUCCESS); } @@ -4228,23 +4299,13 @@ btr_free_externally_stored_field( ulint page_no; ulint next_page_no; mtr_t mtr; -#ifdef UNIV_DEBUG + ut_ad(mtr_memo_contains(local_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains_page(local_mtr, field_ref, MTR_MEMO_PAGE_X_FIX)); ut_ad(!rec || rec_offs_validate(rec, index, offsets)); - - if (rec) { - ulint local_len; - const byte* f = rec_get_nth_field(rec, offsets, - i, &local_len); - ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); - local_len -= BTR_EXTERN_FIELD_REF_SIZE; - f += local_len; - ut_ad(f == field_ref); - } -#endif /* UNIV_DEBUG */ + ut_ad(!rec || field_ref == btr_rec_get_field_ref(rec, offsets, i)); if (UNIV_UNLIKELY(!memcmp(field_ref, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE))) { @@ -4409,13 +4470,8 @@ btr_rec_free_externally_stored_fields( for (i = 0; i < n_fields; i++) { if (rec_offs_nth_extern(offsets, i)) { - ulint len; - byte* data - = rec_get_nth_field(rec, offsets, i, &len); - ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); - btr_free_externally_stored_field( - index, data + len - BTR_EXTERN_FIELD_REF_SIZE, + index, btr_rec_get_field_ref(rec, offsets, i), rec, offsets, page_zip, i, rb_ctx, mtr); } } diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index cb8cb399715..ece3621fa97 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -512,8 +512,8 @@ file segment of the index tree. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ UNIV_INTERN ulint -btr_store_big_rec_extern_fields( -/*============================*/ +btr_store_big_rec_extern_fields_func( +/*=================================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ buf_block_t* rec_block, /*!< in/out: block containing rec */ @@ -522,10 +522,42 @@ btr_store_big_rec_extern_fields( the "external storage" flags in offsets will not correspond to rec when this function returns */ - big_rec_t* big_rec_vec, /*!< in: vector containing fields +#ifdef UNIV_DEBUG + mtr_t* local_mtr, /*!< in: mtr containing the + latch to rec and to the tree */ +#endif /* UNIV_DEBUG */ +#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG + ibool update_in_place,/*! in: TRUE if the record is updated + in place (not delete+insert) */ +#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ + const big_rec_t*big_rec_vec) /*!< in: vector containing fields to be stored externally */ - mtr_t* local_mtr); /*!< in: mtr containing the latch to - rec and to the tree */ + __attribute__((nonnull)); + +/** Stores the fields in big_rec_vec to the tablespace and puts pointers to +them in rec. The extern flags in rec will have to be set beforehand. +The fields are stored on pages allocated from leaf node +file segment of the index tree. +@param index in: clustered index; MUST be X-latched by mtr +@param b in/out: block containing rec; MUST be X-latched by mtr +@param rec in/out: clustered index record +@param offsets in: rec_get_offsets(rec, index); + the "external storage" flags in offsets will not be adjusted +@param mtr in: mini-transaction that holds x-latch on index and b +@param upd in: TRUE if the record is updated in place (not delete+insert) +@param big in: vector containing fields to be stored externally +@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ +#ifdef UNIV_DEBUG +# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \ + btr_store_big_rec_extern_fields_func(index,b,rec,offsets,mtr,upd,big) +#elif defined UNIV_BLOB_LIGHT_DEBUG +# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \ + btr_store_big_rec_extern_fields_func(index,b,rec,offsets,upd,big) +#else +# define btr_store_big_rec_extern_fields(index,b,rec,offsets,mtr,upd,big) \ + btr_store_big_rec_extern_fields_func(index,b,rec,offsets,big) +#endif + /*******************************************************************//** Frees the space in an externally stored field to the file space management if the field in data is owned the externally stored field, diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index 4ba48b10896..690bfd5d6a9 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -177,6 +177,8 @@ command. Not tested on Windows. */ debugging without UNIV_DEBUG */ #define UNIV_BUF_DEBUG /* Enable buffer pool debugging without UNIV_DEBUG */ +#define UNIV_BLOB_LIGHT_DEBUG /* Enable off-page column + debugging without UNIV_DEBUG */ #define UNIV_DEBUG /* Enable ut_ad() assertions and disable UNIV_INLINE */ #define UNIV_DEBUG_LOCK_VALIDATE /* Enable diff --git a/storage/innodb_plugin/row/row0ins.c b/storage/innodb_plugin/row/row0ins.c index 298c601c7e3..8050c099751 100644 --- a/storage/innodb_plugin/row/row0ins.c +++ b/storage/innodb_plugin/row/row0ins.c @@ -2130,7 +2130,7 @@ function_exit: err = btr_store_big_rec_extern_fields( index, btr_cur_get_block(&cursor), - rec, offsets, big_rec, &mtr); + rec, offsets, &mtr, FALSE, big_rec); if (modify) { dtuple_big_rec_free(big_rec); diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index b5d4aeb434e..9ded3d68018 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1952,7 +1952,7 @@ row_upd_clust_rec( index, btr_cur_get_block(btr_cur), rec, rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap), - big_rec, mtr); + mtr, TRUE, big_rec); mtr_commit(mtr); } From a70c34bf0f34703fd330f8cb828e48b303c5296a Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 2 Feb 2011 18:51:35 +0200 Subject: [PATCH 28/42] Fixes for Bug #55755 and Bug #52315 part 2 Bug #55755 : Date STD variable signness breaks server on FreeBSD and OpenBSD * Added a check to configure on the size of time_t * Created a macro to check for a valid time_t that is safe to use with datetime functions and store in TIMESTAMP columns. * Used the macro consistently instead of the ad-hoc checks introduced by 52315 * Fixed compliation warnings on platforms where the size of time_t is smaller than the size of a long (e.g. OpenBSD 4.8 64 amd64). Bug #52315: utc_date() crashes when system time > year 2037 * Added a correct check for the timestamp range instead of just variable size check to SET TIMESTAMP. * Added overflow checking before converting to time_t. * Using a correct localized error message in this case instead of the generic error. * Added a test suite. * fixed the checks so that they check for unsigned time_t as well. Used the checks consistently across the source code. * fixed the original test case to expect the new error code. --- configure.in | 9 +++++++++ include/config-win.h | 5 +++++ include/my_time.h | 13 +++++++++++++ mysql-test/r/func_time.result | 17 +++++++++++++++++ mysql-test/t/func_time.test | 19 +++++++++++++++++++ mysql-test/t/variables.test | 2 +- sql-common/my_time.c | 2 +- sql/mysqld.cc | 14 +++++++------- sql/set_var.cc | 12 ++++++------ sql/sql_class.h | 2 +- 10 files changed, 79 insertions(+), 16 deletions(-) diff --git a/configure.in b/configure.in index fda56554879..523d36afaea 100644 --- a/configure.in +++ b/configure.in @@ -1956,6 +1956,15 @@ dnl MYSQL_CHECK_TIME_T +dnl +dnl check size of time_t +dnl + +AC_CHECK_SIZEOF(time_t, 8) +if test "$ac_cv_sizeof_time_t" -eq 0 +then + AC_MSG_ERROR("MySQL needs a time_t type.") +fi # do we need #pragma interface/#pragma implementation ? # yes if it's gcc 2.x, and not icc pretending to be gcc, and not cygwin diff --git a/include/config-win.h b/include/config-win.h index 84705809d7a..5e37d9e3a20 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -167,6 +167,11 @@ typedef uint rf_SetTimer; #define SIZEOF_LONG 4 #define SIZEOF_LONG_LONG 8 #define SIZEOF_OFF_T 8 +/* + The size of time_t depends on the compiler. + But it's 8 for all the supported VC versions. +*/ +#define SIZEOF_TIME_T 8 #ifdef _WIN64 #define SIZEOF_CHARP 8 #else diff --git a/include/my_time.h b/include/my_time.h index 014327d6fd8..5603535e0b7 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -53,6 +53,19 @@ typedef long my_time_t; #define YY_PART_YEAR 70 +/* + check for valid times only if the range of time_t is greater than + the range of my_time_t +*/ +#if SIZEOF_TIME_T > 4 || defined(TIME_T_UNSIGNED) +# define IS_TIME_T_VALID_FOR_TIMESTAMP(x) \ + ((x) <= TIMESTAMP_MAX_VALUE && \ + (x) >= TIMESTAMP_MIN_VALUE) +#else +# define IS_TIME_T_VALID_FOR_TIMESTAMP(x) \ + ((x) >= TIMESTAMP_MIN_VALUE) +#endif + /* Flags to str_to_datetime */ #define TIME_FUZZY_DATE 1 #define TIME_DATETIME_ONLY 2 diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index b3505795494..8638fb09e5a 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -1323,4 +1323,21 @@ SELECT '2008-02-18' + INTERVAL 1 FRAC_SECOND; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FRAC_SECOND' at line 1 SELECT '2008-02-18' - INTERVAL 1 FRAC_SECOND; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FRAC_SECOND' at line 1 +# +# Bug #52315 part 2 : utc_date() crashes when system time > year 2037 +# +SET TIMESTAMP=-147490000; +SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=2147483648; +SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=2147483646; +SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=2147483647; +SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=0; +SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=-1; +SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=1; +SELECT UTC_TIMESTAMP(); End of 5.0 tests diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 65d8764f2ce..475938b4458 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -838,4 +838,23 @@ SELECT '2008-02-18' + INTERVAL 1 FRAC_SECOND; --error ER_PARSE_ERROR SELECT '2008-02-18' - INTERVAL 1 FRAC_SECOND; + +--echo # +--echo # Bug #52315 part 2 : utc_date() crashes when system time > year 2037 +--echo # + +--disable_result_log +--error ER_WRONG_VALUE_FOR_VAR +SET TIMESTAMP=-147490000; SELECT UTC_TIMESTAMP(); +--error ER_WRONG_VALUE_FOR_VAR +SET TIMESTAMP=2147483648; SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=2147483646; SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=2147483647; SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=0; SELECT UTC_TIMESTAMP(); +--error ER_WRONG_VALUE_FOR_VAR +SET TIMESTAMP=-1; SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=1; SELECT UTC_TIMESTAMP(); +--enable_result_log + + --echo End of 5.0 tests diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index 73e5bbd9d87..778fb133045 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -776,7 +776,7 @@ SET @@myisam_mmap_size= 500M; --echo # Bug #52315: utc_date() crashes when system time > year 2037 --echo # ---error 0, ER_UNKNOWN_ERROR +--error 0, ER_WRONG_VALUE_FOR_VAR SET TIMESTAMP=2*1024*1024*1024; --echo #Should not crash --disable_result_log diff --git a/sql-common/my_time.c b/sql-common/my_time.c index ed1279f7afb..07cb8b25c46 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -985,7 +985,7 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, with unsigned time_t tmp+= shift*86400L might result in a number, larger then TIMESTAMP_MAX_VALUE, so another check will work. */ - if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE)) + if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp)) tmp= 0; return (my_time_t) tmp; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5f84d15588d..f026bab1c32 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2836,13 +2836,6 @@ static int init_common_variables(const char *conf_file_name, int argc, max_system_variables.pseudo_thread_id= (ulong)~0; server_start_time= flush_status_time= time((time_t*) 0); - /* TODO: remove this when my_time_t is 64 bit compatible */ - if (server_start_time >= (time_t) MY_TIME_T_MAX) - { - sql_print_error("This MySQL server doesn't support dates later then 2038"); - return 1; - } - if (init_thread_environment()) return 1; mysql_init_variables(); @@ -2882,6 +2875,13 @@ static int init_common_variables(const char *conf_file_name, int argc, mysql_slow_log.init_pthread_objects(); mysql_bin_log.init_pthread_objects(); + /* TODO: remove this when my_time_t is 64 bit compatible */ + if (!IS_TIME_T_VALID_FOR_TIMESTAMP(server_start_time)) + { + sql_print_error("This MySQL server doesn't support dates later then 2038"); + return 1; + } + if (gethostname(glob_hostname,sizeof(glob_hostname)) < 0) { strmake(glob_hostname, STRING_WITH_LEN("localhost")); diff --git a/sql/set_var.cc b/sql/set_var.cc index 9c327504577..fbc7cdbc232 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2714,14 +2714,14 @@ int set_var_collation_client::update(THD *thd) bool sys_var_timestamp::check(THD *thd, set_var *var) { - time_t val; + longlong val; var->save_result.ulonglong_value= var->value->val_int(); - val= (time_t) var->save_result.ulonglong_value; - if (val < (time_t) MY_TIME_T_MIN || val > (time_t) MY_TIME_T_MAX) + val= (longlong) var->save_result.ulonglong_value; + if (val != 0 && // this is how you set the default value + (val < TIMESTAMP_MIN_VALUE || val > TIMESTAMP_MAX_VALUE)) { - my_message(ER_UNKNOWN_ERROR, - "This version of MySQL doesn't support dates later than 2038", - MYF(0)); + char buf[64]; + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "timestamp", llstr(val, buf)); return TRUE; } return FALSE; diff --git a/sql/sql_class.h b/sql/sql_class.h index 2f4af15c02d..70086e2d944 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1710,7 +1710,7 @@ public: /*TODO: this will be obsolete when we have support for 64 bit my_time_t */ inline bool is_valid_time() { - return (start_time < (time_t) MY_TIME_T_MAX); + return (IS_TIME_T_VALID_FOR_TIMESTAMP(start_time)); } inline void insert_id(ulonglong id_arg) { From 0a8419dfdd6d80b6029c354fb0a82683b2fef617 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 2 Feb 2011 20:10:57 +0200 Subject: [PATCH 29/42] Bug #52315 part 2 addendum : reset back the timestamp --- mysql-test/r/func_time.result | 1 + mysql-test/t/func_time.test | 3 +++ 2 files changed, 4 insertions(+) diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 8638fb09e5a..0bca06a55e2 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -1340,4 +1340,5 @@ SET TIMESTAMP=-1; SELECT UTC_TIMESTAMP(); SET TIMESTAMP=1; SELECT UTC_TIMESTAMP(); +SET TIMESTAMP=0; End of 5.0 tests diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 475938b4458..7ec07dd786a 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -856,5 +856,8 @@ SET TIMESTAMP=-1; SELECT UTC_TIMESTAMP(); SET TIMESTAMP=1; SELECT UTC_TIMESTAMP(); --enable_result_log +#reset back the timestamp value +SET TIMESTAMP=0; + --echo End of 5.0 tests From cb10771fd820be7f6c28b689b9f93b78baa991af Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Thu, 3 Feb 2011 16:09:33 +0000 Subject: [PATCH 30/42] BUG#59147: rpl_circular_for_4_hosts fails sporadically There is one part of the test case that needs to break and re-establish the circular topology. For this the test stops the slave threads on a couple of servers and restarts them with START SLAVE. However, no check is done on the status of the IO or SQL threads before proceeding with the subsequent commands. Because rpl_only_running_threads is set to 1 this can lead to silently not syncing all slave threads as expected, ultimately resulting in unexpected results (and consequently on a failing test run). We fix this by replacing the START SLAVE instructions with calls to --source include/start_slave.inc, which will wait for the slave threads to be running (show 'Yes' in Slave_IO|SQL_Running fields of SHOW SLAVE STATUS) before proceeding. Additionally, we change rpl_sync.inc to make the IO thread report that it is running when its running status is any other than 'No'. --- mysql-test/include/rpl_sync.inc | 2 +- mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result | 4 ++-- mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/include/rpl_sync.inc b/mysql-test/include/rpl_sync.inc index a05bee23981..be2904528ff 100644 --- a/mysql-test/include/rpl_sync.inc +++ b/mysql-test/include/rpl_sync.inc @@ -88,7 +88,7 @@ while ($_rpl_i) { { --echo Sync IO: $_rpl_slave_io_running; Sync SQL: $_rpl_slave_sql_running } - --let $_rpl_slave_io_running= `SELECT IF('$_rpl_slave_io_running' = 'Yes', 1, '')` + --let $_rpl_slave_io_running= `SELECT IF('$_rpl_slave_io_running' != 'No', 1, '')` --let $_rpl_slave_sql_running= `SELECT IF('$_rpl_slave_sql_running' = 'Yes', 1, '')` if ($_rpl_slave_io_running) { diff --git a/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result b/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result index fb1d3f8258e..6981e549918 100644 --- a/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result +++ b/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result @@ -121,11 +121,11 @@ Master D 12 D * Remove wrong event from C and restore B->C->D * include/stop_slave.inc DELETE FROM t1 WHERE a = 6; -START SLAVE; +include/start_slave.inc RESET MASTER; RESET SLAVE; include/rpl_change_topology.inc [new topology=1->2->3->4->1] -START SLAVE; +include/start_slave.inc include/rpl_sync.inc * Check data inserted before restoring schema A->B->C->D->A * diff --git a/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test b/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test index 6099637e3e9..820ffc42933 100644 --- a/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test +++ b/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test @@ -175,7 +175,7 @@ SELECT 'Master D',a,b FROM t1 WHERE c = 3 ORDER BY a,b; source include/stop_slave.inc; --connection server_3 DELETE FROM t1 WHERE a = 6; -START SLAVE; +--source include/start_slave.inc --connection server_2 --sync_slave_with_master server_3 RESET MASTER; @@ -189,7 +189,7 @@ RESET SLAVE; --source include/rpl_change_topology.inc #--replace_result $SERVER_MYPORT_3 SERVER_MYPORT_3 $file_d LOG_FILE $pos_d LOG_POS #--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$SERVER_MYPORT_3,master_user='root',master_log_file='$file_d',master_log_pos=$pos_d -START SLAVE; +--source include/start_slave.inc --connection server_3 --sync_slave_with_master server_4 --source include/rpl_sync.inc From 378091e434d66dcc7081f991a07db2244dbb8cda Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Fri, 4 Feb 2011 10:47:46 +0600 Subject: [PATCH 31/42] Fixed bug#58026 - massive recursion and crash in regular expression handling. The problem was that parsing of nested regular expression involved recursive calls. Such recursion didn't take into account the amount of available stack space, which ended up leading to stack overflow crashes. mysql-test/t/not_embedded_server.test: Added test for bug#58026. regex/my_regex.h: added pointer to function as last argument of my_regex_init() for check enough memory in stack. regex/regcomp.c: p_ere() was modified: added call to function for check enough memory in stack. Function for check available stack space specified by global variable my_regex_enough_mem_in_stack. This variable set to NULL for embedded mysqld and to a pointer to function check_enough_stack_size otherwise. regex/reginit.c: my_regex_init was modified: pass a pointer to a function for check enough memory in stack space. Reset this pointer to NULL in my_regex_end. sql/mysqld.cc: Added function check_enough_stack_size() for check enough memory in stack. Passed this function as second argument to my_regex_init. For embedded mysqld passed NULL as second argument. --- mysql-test/r/not_embedded_server.result | 4 ++++ mysql-test/t/not_embedded_server.test | 10 ++++++++++ regex/my_regex.h | 4 +++- regex/regcomp.c | 15 +++++++++++++-- regex/reginit.c | 6 +++++- sql/mysqld.cc | 19 ++++++++++++++++++- 6 files changed, 53 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result index 60c92bd0196..7219e29b2a6 100644 --- a/mysql-test/r/not_embedded_server.result +++ b/mysql-test/r/not_embedded_server.result @@ -4,3 +4,7 @@ select 1; SHOW VARIABLES like 'slave_skip_errors'; Variable_name Value slave_skip_errors OFF +# +# Bug#58026: massive recursion and crash in regular expression handling +# +SELECT '1' RLIKE RPAD('1', 10000, '('); diff --git a/mysql-test/t/not_embedded_server.test b/mysql-test/t/not_embedded_server.test index fa2b659ec57..43cdb977386 100644 --- a/mysql-test/t/not_embedded_server.test +++ b/mysql-test/t/not_embedded_server.test @@ -42,4 +42,14 @@ select 1; SHOW VARIABLES like 'slave_skip_errors'; +--echo # +--echo # Bug#58026: massive recursion and crash in regular expression handling +--echo # + +--disable_result_log +--error ER_STACK_OVERRUN_NEED_MORE +SELECT '1' RLIKE RPAD('1', 10000, '('); +--enable_result_log + + # End of 5.1 tests diff --git a/regex/my_regex.h b/regex/my_regex.h index 0d1cedf5430..30896e29b91 100644 --- a/regex/my_regex.h +++ b/regex/my_regex.h @@ -28,6 +28,7 @@ typedef struct { /* === regcomp.c === */ +typedef int (*my_regex_stack_check_t)(); extern int my_regcomp(my_regex_t *, const char *, int, CHARSET_INFO *charset); #define REG_BASIC 0000 #define REG_EXTENDED 0001 @@ -76,7 +77,8 @@ extern void my_regfree(my_regex_t *); /* === reginit.c === */ -extern void my_regex_init(CHARSET_INFO *cs); /* Should be called for multithread progs */ +/* Should be called for multithread progs */ +extern void my_regex_init(CHARSET_INFO *cs, my_regex_stack_check_t func); extern void my_regex_end(void); /* If one wants a clean end */ #ifdef __cplusplus diff --git a/regex/regcomp.c b/regex/regcomp.c index 81c435ed552..e163a9ba7f4 100644 --- a/regex/regcomp.c +++ b/regex/regcomp.c @@ -31,6 +31,9 @@ struct parse { CHARSET_INFO *charset; /* for ctype things */ }; +/* Check if there is enough stack space for recursion. */ +my_regex_stack_check_t my_regex_enough_mem_in_stack= NULL; + #include "regcomp.ih" static char nuls[10]; /* place to point scanner in event of error */ @@ -117,7 +120,7 @@ CHARSET_INFO *charset; # define GOODFLAGS(f) ((f)&~REG_DUMP) #endif - my_regex_init(charset); /* Init cclass if neaded */ + my_regex_init(charset, NULL); /* Init cclass if neaded */ preg->charset=charset; cflags = GOODFLAGS(cflags); if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) @@ -222,7 +225,15 @@ int stop; /* character this ERE should end at */ /* do a bunch of concatenated expressions */ conc = HERE(); while (MORE() && (c = PEEK()) != '|' && c != stop) - p_ere_exp(p); + { + if (my_regex_enough_mem_in_stack && + my_regex_enough_mem_in_stack()) + { + SETERROR(REG_ESPACE); + return; + } + p_ere_exp(p); + } if(REQUIRE(HERE() != conc, REG_EMPTY)) {}/* require nonempty */ if (!EAT('|')) diff --git a/regex/reginit.c b/regex/reginit.c index 5980de24030..3d2cd64d1e7 100644 --- a/regex/reginit.c +++ b/regex/reginit.c @@ -4,10 +4,12 @@ #include #include #include "cclass.h" +#include "my_regex.h" static my_bool regex_inited=0; +extern my_regex_stack_check_t my_regex_enough_mem_in_stack; -void my_regex_init(CHARSET_INFO *cs) +void my_regex_init(CHARSET_INFO *cs, my_regex_stack_check_t func) { char buff[CCLASS_LAST][256]; int count[CCLASS_LAST]; @@ -16,6 +18,7 @@ void my_regex_init(CHARSET_INFO *cs) if (!regex_inited) { regex_inited=1; + my_regex_enough_mem_in_stack= func; bzero((uchar*) &count,sizeof(count)); for (i=1 ; i<= 255; i++) @@ -74,6 +77,7 @@ void my_regex_end() int i; for (i=0; i < CCLASS_LAST ; i++) free((char*) cclasses[i].chars); + my_regex_enough_mem_in_stack= NULL; regex_inited=0; } } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 54f93cccd5d..694d64c81df 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3034,6 +3034,19 @@ sizeof(load_default_groups)/sizeof(load_default_groups[0]); #endif +#ifndef EMBEDDED_LIBRARY +static +int +check_enough_stack_size() +{ + uchar stack_top; + + return check_stack_overrun(current_thd, STACK_MIN_SIZE, + &stack_top); +} +#endif + + /** Initialize one of the global date/time format variables. @@ -3415,7 +3428,11 @@ static int init_common_variables(const char *conf_file_name, int argc, #endif mysys_uses_curses=0; #ifdef USE_REGEX - my_regex_init(&my_charset_latin1); +#ifndef EMBEDDED_LIBRARY + my_regex_init(&my_charset_latin1, check_enough_stack_size); +#else + my_regex_init(&my_charset_latin1, NULL); +#endif #endif /* Process a comma-separated character set list and choose From 5082c4d1de5d9fb70fef89f6e89e00097ce9374e Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 4 Feb 2011 12:46:47 +0200 Subject: [PATCH 32/42] Fix Bug#59874 Valgrind warning in InnoDB compression code Add suppressions for a bogus valgrind warnings. --- mysql-test/valgrind.supp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 1016923a360..367dc62898a 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -744,3 +744,37 @@ Memcheck:Addr1 fun:buf_buddy_relocate } + +{ + Bug 59874 Valgrind warning in InnoDB compression code + Memcheck:Cond + obj:*/libz.so* + obj:*/libz.so* + fun:deflate + fun:btr_store_big_rec_extern_fields_func + fun:row_ins_index_entry_low + fun:row_ins_index_entry + fun:row_ins_index_entry_step + fun:row_ins + fun:row_ins_step + fun:row_insert_for_mysql +} + +{ + In page0zip.c we have already checked that the memory is initialized before calling deflate() + Memcheck:Cond + obj:*/libz.so* + obj:*/libz.so* + fun:deflate + fun:page_zip_compress + fun:page_cur_insert_rec_zip_reorg + fun:page_cur_insert_rec_zip + fun:page_cur_tuple_insert + fun:btr_cur_optimistic_insert + fun:row_ins_index_entry_low + fun:row_ins_index_entry + fun:row_ins_index_entry_step + fun:row_ins + fun:row_ins_step + fun:row_insert_for_mysql +} From cd51cbf1e53c01424d270c6a41bf6f2166949a31 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 4 Feb 2011 12:33:45 +0100 Subject: [PATCH 33/42] Bug #47141 "mysqltest" breaks because it tries to act on a debug option which is disabled Replace --debug with --loose-debug to prevent failure exit Update: added workaround for 50627, skip all debugging of mysqlbinlog --- mysql-test/mysql-test-run.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 03dd3e5bd3f..27e52d814e4 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1783,9 +1783,12 @@ sub executable_setup () { sub client_debug_arg($$) { my ($args, $client_name)= @_; + # Workaround for Bug #50627: drop any debug opt + return if $client_name =~ /^mysqlbinlog/; + if ( $opt_debug ) { mtr_add_arg($args, - "--debug=d:t:A,%s/log/%s.trace", + "--loose-debug=d:t:A,%s/log/%s.trace", $path_vardir_trace, $client_name) } } From 980868eb4e4db550497ba40674b5b64f2e062cd9 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Sat, 5 Feb 2011 11:02:00 +0600 Subject: [PATCH 34/42] Fixed bug#57450 - mysql client enter in an infinite loop if the standard input is a directory. The problem is that mysql monitor try to read from stdin without checking input source type. The solution is to stop reading data from standard input if a call to read(2) failed. A new test case was added into mysql.test. client/my_readline.h: Data members error and truncated was added to LINE_BUFFER structure. These data members used instead of out parameters in functions batch_readline, intern_read_line. client/mysql.cc: read_and_execute() was modified: set status.exit_status to 1 when the error occured while reading the next command line in non-interactive mode. Also the value of the truncated attribute of structure LINE_BUFF is taken into account only for non-iteractive mode. client/readline.cc: intern_read_line() was modified: cancel reading from input if fill_buffer() returns -1, e.g. if call to read failed. batch_readline was modified: set the error data member of LINE_BUFFER structure to value of my_errno when system error happened during call to my_read/my_realloc. mysql-test/t/mysql.test: Test for bug#57450 was added. --- client/my_readline.h | 4 +++- client/mysql.cc | 16 ++++++++++------ client/readline.cc | 33 ++++++++++++++++++++------------- mysql-test/t/mysql.test | 6 ++++++ 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/client/my_readline.h b/client/my_readline.h index 62ad19bece9..3376fc81761 100644 --- a/client/my_readline.h +++ b/client/my_readline.h @@ -25,9 +25,11 @@ typedef struct st_line_buffer uint eof; ulong max_size; ulong read_length; /* Length of last read string */ + int error; + bool truncated; } LINE_BUFFER; extern LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file); extern LINE_BUFFER *batch_readline_command(LINE_BUFFER *buffer, char * str); -extern char *batch_readline(LINE_BUFFER *buffer, bool *truncated); +extern char *batch_readline(LINE_BUFFER *buffer); extern void batch_readline_end(LINE_BUFFER *buffer); diff --git a/client/mysql.cc b/client/mysql.cc index dafe76a2401..b19aaf61e58 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1872,14 +1872,13 @@ static int read_and_execute(bool interactive) ulong line_number=0; bool ml_comment= 0; COMMANDS *com; - bool truncated= 0; status.exit_status=1; - + for (;;) { if (!interactive) { - line=batch_readline(status.line_buff, &truncated); + line=batch_readline(status.line_buff); /* Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF. Editors like "notepad" put this marker in @@ -1953,9 +1952,13 @@ static int read_and_execute(bool interactive) if (opt_outfile && line) fprintf(OUTFILE, "%s\n", line); } - if (!line) // End of file + // End of file or system error + if (!line) { - status.exit_status=0; + if (status.line_buff && status.line_buff->error) + status.exit_status= 1; + else + status.exit_status= 0; break; } @@ -1976,7 +1979,8 @@ static int read_and_execute(bool interactive) #endif continue; } - if (add_line(glob_buffer,line,&in_string,&ml_comment, truncated)) + if (add_line(glob_buffer, line, &in_string, &ml_comment, + status.line_buff ? status.line_buff->truncated : 0)) break; } /* if in batch mode, send last query even if it doesn't end with \g or go */ diff --git a/client/readline.cc b/client/readline.cc index b32cb71b0de..7f283502ecb 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -24,7 +24,7 @@ static bool init_line_buffer(LINE_BUFFER *buffer,File file,ulong size, ulong max_size); static bool init_line_buffer_from_string(LINE_BUFFER *buffer,char * str); static size_t fill_buffer(LINE_BUFFER *buffer); -static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length, bool *truncated); +static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length); LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) @@ -42,13 +42,12 @@ LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) } -char *batch_readline(LINE_BUFFER *line_buff, bool *truncated) +char *batch_readline(LINE_BUFFER *line_buff) { char *pos; ulong out_length; - DBUG_ASSERT(truncated != NULL); - if (!(pos=intern_read_line(line_buff,&out_length, truncated))) + if (!(pos=intern_read_line(line_buff, &out_length))) return 0; if (out_length && pos[out_length-1] == '\n') if (--out_length && pos[out_length-1] == '\r') /* Remove '\n' */ @@ -162,7 +161,10 @@ static size_t fill_buffer(LINE_BUFFER *buffer) if (!(buffer->buffer = (char*) my_realloc(buffer->buffer, buffer->bufread+1, MYF(MY_WME | MY_FAE)))) - return (uint) -1; + { + buffer->error= my_errno; + return (size_t) -1; + } buffer->start_of_line=buffer->buffer+start_offset; buffer->end=buffer->buffer+bufbytes; } @@ -177,7 +179,10 @@ static size_t fill_buffer(LINE_BUFFER *buffer) /* Read in new stuff. */ if ((read_count= my_read(buffer->file, (uchar*) buffer->end, read_count, MYF(MY_WME))) == MY_FILE_ERROR) + { + buffer->error= my_errno; return (size_t) -1; + } DBUG_PRINT("fill_buff", ("Got %lu bytes", (ulong) read_count)); @@ -198,8 +203,7 @@ static size_t fill_buffer(LINE_BUFFER *buffer) } - -char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length, bool *truncated) +char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length) { char *pos; size_t length; @@ -214,22 +218,25 @@ char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length, bool *truncated) if (pos == buffer->end) { /* - fill_buffer() can return 0 either on EOF in which case we abort - or when the internal buffer has hit the size limit. In the latter case - return what we have read so far and signal string truncation. + fill_buffer() can return NULL on EOF (in which case we abort), + on error, or when the internal buffer has hit the size limit. + In the latter case return what we have read so far and signal + string truncation. */ - if (!(length=fill_buffer(buffer)) || length == (uint) -1) + if (!(length= fill_buffer(buffer))) { if (buffer->eof) DBUG_RETURN(0); } + else if (length == (size_t) -1) + DBUG_RETURN(NULL); else continue; pos--; /* break line here */ - *truncated= 1; + buffer->truncated= 1; } else - *truncated= 0; + buffer->truncated= 0; buffer->end_of_line=pos+1; *out_length=(ulong) (pos + 1 - buffer->eof - buffer->start_of_line); DBUG_RETURN(buffer->start_of_line); diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index 2dcc77a16c2..aa774036d10 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -412,6 +412,12 @@ drop table t1; --echo --exec $MYSQL --skip-column-names --vertical test -e "select 1 as a" +# +# Bug#57450: mysql client enter in an infinite loop if the standard input is a directory +# +--error 1 +--exec $MYSQL < . + --echo --echo # From e29b40f86fcd75c72eb622f3219fbe386d81b8aa Mon Sep 17 00:00:00 2001 From: Vinay Fisrekar Date: Mon, 7 Feb 2011 11:10:35 +0530 Subject: [PATCH 35/42] Bug#59955 - engines/funcs/ps_string_not_null test needs better cleanup Correcting clean up command at the start of test. --- .../engines/funcs/r/ps_string_not_null.result | Bin 11681 -> 11681 bytes .../engines/funcs/t/ps_string_not_null.test | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/suite/engines/funcs/r/ps_string_not_null.result b/mysql-test/suite/engines/funcs/r/ps_string_not_null.result index 859fab8b49074619ac3d54a1bf72e5754c120deb..5f2a630811ccb413f7685d39eaa7a6d43c8408b7 100644 GIT binary patch delta 14 VcmZ1&y)b%$*koo2v5h*Vx&SF)1mXYy delta 12 TcmZ1&y)b%$7^Bff@h)8eA7TVi diff --git a/mysql-test/suite/engines/funcs/t/ps_string_not_null.test b/mysql-test/suite/engines/funcs/t/ps_string_not_null.test index f9e937cb24d..662adfd7a88 100644 --- a/mysql-test/suite/engines/funcs/t/ps_string_not_null.test +++ b/mysql-test/suite/engines/funcs/t/ps_string_not_null.test @@ -1,5 +1,5 @@ --disable_warnings -DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t1; --enable_warnings CREATE TABLE t1(c1 CHAR(100) NOT NULL); PREPARE stmt1 FROM 'INSERT INTO t1 (c1) VALUES(?)'; From 165ed4f1a853568d7c4605434bae762a53dffdb3 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 7 Feb 2011 11:13:54 +0200 Subject: [PATCH 36/42] Use fun:* instead of obj:*/libz.so* because when the bundled zlib is used (--with-zlib-dir=bundled) then there is no libz.so involved. --- mysql-test/valgrind.supp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 367dc62898a..6078006abd7 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -748,8 +748,8 @@ { Bug 59874 Valgrind warning in InnoDB compression code Memcheck:Cond - obj:*/libz.so* - obj:*/libz.so* + fun:* + fun:* fun:deflate fun:btr_store_big_rec_extern_fields_func fun:row_ins_index_entry_low @@ -763,8 +763,8 @@ { In page0zip.c we have already checked that the memory is initialized before calling deflate() Memcheck:Cond - obj:*/libz.so* - obj:*/libz.so* + fun:* + fun:* fun:deflate fun:page_zip_compress fun:page_cur_insert_rec_zip_reorg From 221ce9223da6c386c4b904cd7b79ef02e7845882 Mon Sep 17 00:00:00 2001 From: Ole John Aske Date: Mon, 7 Feb 2011 10:36:21 +0100 Subject: [PATCH 37/42] Fix for bug#59308: Incorrect result for SELECT DISTINCT ... ORDER BY DESC. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fix bug#59110: Memory leak of QUICK_SELECT_I allocated memory. Includes Jørgen Lølands review comments. Root cause of these bugs are that test_if_skip_sort_order() decided to revert the 'skip_sort_order' descision (and use filesort) after the query plan has been updated to reflect a 'skip' of the sort order. This might happen in 'check_reverse_order:' if we have a select->quick which could not be made descending by appending a QUICK_SELECT_DESC. (). The original 'save_quick' was then restored after the QEP has been modified, which caused: - An incorrect 'precomputed_group_by= TRUE' may have been set, and not reverted, as part of the already modifified QEP (Bug#59308) - A 'select->quick' might have been created which we fail to delete (bug#59110). This fix is a refactorication of test_if_skip_sort_order() where all logic related to modification of QEP (controlled by argument 'bool no_changes'), is moved to the end of test_if_skip_sort_order(), and done after *all* 'test_if_skip' checks has been performed - including the 'check_reverse_order:' checks. The refactorication above contains now intentional changes to the logic which has been moved to the end of the function. Furthermore, a smaller part of the fix address the handling of the select->quick objects which may already exists when we call 'test_if_skip_sort_order()' (save_quick) -and new select->quick's created during test_if_skip_sort_order(): - Before new select->quick may be created by calling ::test_quick_select(), we set 'select->quick= 0' to avoid that ::test_quick_select() prematurely delete the save_quick's. (After this call we may have both a 'save_quick' and 'select->quick') - All returns from ::test_if_skip_sort_order() where we may have both a 'save_quick' and a 'select->quick' has been changed to goto's to the exit points 'skiped_sort_order:' or 'need_filesort:' where we decide which of the QUICK_SELECT's to keep, and delete the other. --- mysql-test/r/order_by.result | 25 ++++ mysql-test/t/order_by.test | 17 +++ sql/sql_select.cc | 231 +++++++++++++++++++++-------------- 3 files changed, 181 insertions(+), 92 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index ba639fa9763..30879af418a 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -1638,4 +1638,29 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index NULL a 8 NULL 10 Using index; Using temporary; Using filesort 1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using where DROP TABLE t1, t2; +# +# Bug #59110: Memory leak of QUICK_SELECT_I allocated memory +# and +# Bug #59308: Incorrect result for +SELECT DISTINCT ... ORDER BY DESC + +# Use Valgrind to detect #59110! +# +CREATE TABLE t1 (a INT,KEY (a)); +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index a a 5 NULL 10 Using where; Using index; Using filesort +SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +a 1 +10 1 +9 1 +8 1 +7 1 +6 1 +5 1 +4 1 +3 1 +2 1 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index a8a6ad00648..1064320b65c 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -1492,4 +1492,21 @@ LIMIT 2; DROP TABLE t1, t2; +--echo # +--echo # Bug #59110: Memory leak of QUICK_SELECT_I allocated memory +--echo # and +--echo # Bug #59308: Incorrect result for +--echo SELECT DISTINCT ... ORDER BY DESC +--echo +--echo # Use Valgrind to detect #59110! +--echo # + +CREATE TABLE t1 (a INT,KEY (a)); +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); + +EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; + +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e080dc9370c..ba3008e0c38 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13364,12 +13364,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, { int ref_key; uint ref_key_parts; - int order_direction; + int order_direction= 0; uint used_key_parts; TABLE *table=tab->table; SQL_SELECT *select=tab->select; key_map usable_keys; QUICK_SELECT_I *save_quick= 0; + int best_key= -1; + DBUG_ENTER("test_if_skip_sort_order"); LINT_INIT(ref_key_parts); @@ -13473,13 +13475,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, new_ref_key_map.clear_all(); // Force the creation of quick select new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key. + select->quick= 0; if (select->test_quick_select(tab->join->thd, new_ref_key_map, 0, (tab->join->select_options & OPTION_FOUND_ROWS) ? HA_POS_ERROR : tab->join->unit->select_limit_cnt,0) <= 0) - DBUG_RETURN(0); + goto use_filesort; } ref_key= new_ref_key; } @@ -13504,7 +13507,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, int best_key_direction= 0; ha_rows best_records= 0; double read_time; - int best_key= -1; bool is_best_covering= FALSE; double fanout= 1; JOIN *join= tab->join; @@ -13681,72 +13683,21 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, tab->join->tables > tab->join->const_tables + 1) && ((unsigned) best_key != table->s->primary_key || !table->file->primary_key_is_clustered())) - DBUG_RETURN(0); + goto use_filesort; if (best_key >= 0) { - bool quick_created= FALSE; if (table->quick_keys.is_set(best_key) && best_key != ref_key) { key_map map; map.clear_all(); // Force the creation of quick select map.set_bit(best_key); // only best_key. - quick_created= - select->test_quick_select(join->thd, map, 0, - join->select_options & OPTION_FOUND_ROWS ? - HA_POS_ERROR : - join->unit->select_limit_cnt, - 0) > 0; - } - if (!no_changes) - { - /* - If ref_key used index tree reading only ('Using index' in EXPLAIN), - and best_key doesn't, then revert the decision. - */ - if (!table->covering_keys.is_set(best_key)) - table->set_keyread(FALSE); - if (!quick_created) - { - tab->index= best_key; - tab->read_first_record= best_key_direction > 0 ? - join_read_first:join_read_last; - tab->type=JT_NEXT; // Read with index_first(), index_next() - if (select && select->quick) - { - delete select->quick; - select->quick= 0; - } - if (table->covering_keys.is_set(best_key)) - table->set_keyread(TRUE); - table->file->ha_index_or_rnd_end(); - if (join->select_options & SELECT_DESCRIBE) - { - tab->ref.key= -1; - tab->ref.key_parts= 0; - if (select_limit < table_records) - tab->limit= select_limit; - } - } - else if (tab->type != JT_ALL) - { - /* - We're about to use a quick access to the table. - We need to change the access method so as the quick access - method is actually used. - */ - DBUG_ASSERT(tab->select->quick); - tab->type=JT_ALL; - tab->use_quick=1; - tab->ref.key= -1; - tab->ref.key_parts=0; // Don't use ref key. - tab->read_first_record= join_init_read_record; - if (tab->is_using_loose_index_scan()) - join->tmp_table_param.precomputed_group_by= TRUE; - /* - TODO: update the number of records in join->best_positions[tablenr] - */ - } + select->quick= 0; + select->test_quick_select(join->thd, map, 0, + join->select_options & OPTION_FOUND_ROWS ? + HA_POS_ERROR : + join->unit->select_limit_cnt, + 0); } order_direction= best_key_direction; /* @@ -13759,10 +13710,12 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, saved_best_key_parts : best_key_parts; } else - DBUG_RETURN(0); + goto use_filesort; } check_reverse_order: + DBUG_ASSERT(order_direction != 0); + if (order_direction == -1) // If ORDER BY ... DESC { if (select && select->quick) @@ -13771,9 +13724,10 @@ check_reverse_order: Don't reverse the sort order, if it's already done. (In some cases test_if_order_by_key() can be called multiple times */ - if (!select->quick->reverse_sorted()) + if (select->quick->reverse_sorted()) + goto skipped_filesort; + else { - QUICK_SELECT_DESC *tmp; int quick_type= select->quick->get_type(); if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || @@ -13781,39 +13735,132 @@ check_reverse_order: quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) { tab->limit= 0; - select->quick= save_quick; - DBUG_RETURN(0); // Use filesort + goto use_filesort; // Use filesort } - - /* ORDER BY range_key DESC */ - tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), - used_key_parts); - if (!tmp || tmp->error) - { - delete tmp; - select->quick= save_quick; - tab->limit= 0; - DBUG_RETURN(0); // Reverse sort not supported - } - select->quick=tmp; } } - else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL && - tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts) - { - /* - SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC - - Use a traversal function that starts by reading the last row - with key part (A) and then traverse the index backwards. - */ - tab->read_first_record= join_read_last_key; - tab->read_record.read_record= join_read_prev_same; - } } - else if (select && select->quick) - select->quick->sorted= 1; + + /* + Update query plan with access pattern for doing + ordered access according to what we have decided + above. + */ + if (!no_changes) // We are allowed to update QEP + { + if (best_key >= 0) + { + bool quick_created= + (select && select->quick && select->quick!=save_quick); + + /* + If ref_key used index tree reading only ('Using index' in EXPLAIN), + and best_key doesn't, then revert the decision. + */ + if (!table->covering_keys.is_set(best_key)) + table->set_keyread(FALSE); + if (!quick_created) + { + if (select) // Throw any existing quick select + select->quick= 0; // Cleanup either reset to save_quick, + // or 'delete save_quick' + tab->index= best_key; + tab->read_first_record= order_direction > 0 ? + join_read_first:join_read_last; + tab->type=JT_NEXT; // Read with index_first(), index_next() + + if (table->covering_keys.is_set(best_key)) + table->set_keyread(TRUE); + table->file->ha_index_or_rnd_end(); + if (tab->join->select_options & SELECT_DESCRIBE) + { + tab->ref.key= -1; + tab->ref.key_parts= 0; + if (select_limit < table->file->stats.records) + tab->limit= select_limit; + } + } + else if (tab->type != JT_ALL) + { + /* + We're about to use a quick access to the table. + We need to change the access method so as the quick access + method is actually used. + */ + DBUG_ASSERT(tab->select->quick); + tab->type=JT_ALL; + tab->use_quick=1; + tab->ref.key= -1; + tab->ref.key_parts=0; // Don't use ref key. + tab->read_first_record= join_init_read_record; + if (tab->is_using_loose_index_scan()) + tab->join->tmp_table_param.precomputed_group_by= TRUE; + /* + TODO: update the number of records in join->best_positions[tablenr] + */ + } + } // best_key >= 0 + + if (order_direction == -1) // If ORDER BY ... DESC + { + if (select && select->quick) + { + QUICK_SELECT_DESC *tmp; + /* ORDER BY range_key DESC */ + tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), + used_key_parts); + if (tmp && select->quick == save_quick) + save_quick= 0; // ::QUICK_SELECT_DESC consumed it + + if (!tmp || tmp->error) + { + delete tmp; + tab->limit= 0; + goto use_filesort; // Reverse sort failed -> filesort + } + select->quick= tmp; + } + else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL && + tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts) + { + /* + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC + + Use a traversal function that starts by reading the last row + with key part (A) and then traverse the index backwards. + */ + tab->read_first_record= join_read_last_key; + tab->read_record.read_record= join_read_prev_same; + } + } + else if (select && select->quick) + select->quick->sorted= 1; + + } // QEP has been modified + + /* + Cleanup: + We may have both a 'select->quick' and 'save_quick' (original) + at this point. Delete the one that we wan't use. + */ + +skipped_filesort: + // Keep current (ordered) select->quick + if (select && save_quick != select->quick) + { + delete save_quick; + save_quick= NULL; + } DBUG_RETURN(1); + +use_filesort: + // Restore original save_quick + if (select && select->quick != save_quick) + { + delete select->quick; + select->quick= save_quick; + } + DBUG_RETURN(0); } From 114ac525f22f9fcc2cc8df3906277f43ff487e2a Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 7 Feb 2011 12:42:21 +0100 Subject: [PATCH 38/42] Bug #59979 Add mtr option to run debug server, but without turning on debug Added --debug-server and use $opt_debug_server where appropriate Let --debug imply --debug-server When merging to 5.5, must adapt fix for 59148 Oops, set debug => debug-server too late, fixed --- mysql-test/mysql-test-run.pl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 27e52d814e4..502dff75b69 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -182,6 +182,7 @@ my $opt_cursor_protocol; my $opt_view_protocol; our $opt_debug; +our $opt_debug_server; our @opt_cases; # The test cases names in argv our $opt_embedded_server; @@ -936,6 +937,7 @@ sub command_line_setup { # Debugging 'debug' => \$opt_debug, + 'debug-server' => \$opt_debug_server, 'gdb' => \$opt_gdb, 'client-gdb' => \$opt_client_gdb, 'manual-gdb' => \$opt_manual_gdb, @@ -1082,6 +1084,9 @@ sub command_line_setup { my $path_share= dirname($path_language); $path_charsetsdir= mtr_path_exists("$path_share/charsets"); + # --debug implies we run debug server + $opt_debug_server= 1 if $opt_debug; + if (using_extern()) { # Connect to the running mysqld and find out what it supports @@ -1712,7 +1717,7 @@ sub find_mysqld { my @mysqld_names= ("mysqld", "mysqld-max-nt", "mysqld-max", "mysqld-nt"); - if ( $opt_debug ){ + if ( $opt_debug_server ){ # Put mysqld-debug first in the list of binaries to look for mtr_verbose("Adding mysqld-debug first in list of binaries to look for"); unshift(@mysqld_names, "mysqld-debug"); @@ -2402,9 +2407,9 @@ sub check_debug_support ($) { #mtr_report(" - binaries are not debug compiled"); $debug_compiled_binaries= 0; - if ( $opt_debug ) + if ( $opt_debug_server ) { - mtr_error("Can't use --debug, binaries does not support it"); + mtr_error("Can't use --debug[-server], binary does not support it"); } return; } @@ -5582,6 +5587,8 @@ Options for debugging the product client-gdb Start mysqltest client in gdb ddd Start mysqld in ddd debug Dump trace output for all servers and client programs + debug-server Use debug version of server, but without turning on + tracing debugger=NAME Start mysqld in the selected debugger gdb Start the mysqld(s) in gdb manual-debug Let user manually start mysqld in debugger, before From fe40394908ff018b4f6d0c2542152a90db875dfe Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 7 Feb 2011 16:45:37 +0200 Subject: [PATCH 39/42] Backport the fix for Bug#59875 Valgrind warning in buf0buddy.c from 5.5 This warning also happens in 5.1 with a slightly different codepath. --- mysql-test/valgrind.supp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 6078006abd7..1983a8e6137 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -778,3 +778,16 @@ fun:row_ins_step fun:row_insert_for_mysql } + +{ + Bug 59875 Valgrind warning in buf0buddy.c + Memcheck:Addr1 + fun:mach_read_from_4 + fun:buf_buddy_relocate + fun:buf_buddy_free_low + fun:buf_buddy_free + fun:buf_LRU_block_remove_hashed_page + fun:buf_LRU_invalidate_tablespace + fun:fil_delete_tablespace + fun:row_drop_table_for_mysql +} From 12c68dbf164aa5b666acb09f02a14b51c941ad94 Mon Sep 17 00:00:00 2001 From: Anitha Gopi Date: Tue, 8 Feb 2011 11:30:35 +0530 Subject: [PATCH 40/42] Removed the collections for mysql-5.1-bugteam. Removed 1st from weekly. This is part of default suites --- mysql-test/collections/default.weekly | 1 - mysql-test/collections/mysql-5.1-bugteam.daily | 5 ----- mysql-test/collections/mysql-5.1-bugteam.push | 4 ---- 3 files changed, 10 deletions(-) delete mode 100644 mysql-test/collections/mysql-5.1-bugteam.daily delete mode 100644 mysql-test/collections/mysql-5.1-bugteam.push diff --git a/mysql-test/collections/default.weekly b/mysql-test/collections/default.weekly index f10bc0776a0..a2765bcaf0c 100755 --- a/mysql-test/collections/default.weekly +++ b/mysql-test/collections/default.weekly @@ -1,4 +1,3 @@ -perl mysql-test-run.pl --timer --force --comment=1st --experimental=collections/default.experimental 1st perl mysql-test-run.pl --timer --force --comment=big-tests --experimental=collections/default.experimental --vardir=var-big-tests --big-test --testcase-timeout=60 --suite-timeout=600 parts.partition_alter1_2_ndb parts.part_supported_sql_func_innodb parts.partition_alter1_2_innodb parts.partition_alter4_innodb parts.partition_alter1_1_2_ndb parts.partition_alter1_1_2_innodb parts.partition_alter1_1_ndb rpl_ndb.rpl_truncate_7ndb_2 main.archive-big main.sum_distinct-big main.mysqlbinlog_row_big main.alter_table-big main.variables-big main.type_newdecimal-big main.read_many_rows_innodb main.log_tables-big main.count_distinct3 main.events_time_zone main.merge-big main.create-big main.events_stress main.ssl-big funcs_1.myisam_views-big perl mysql-test-run.pl --timer --force --parallel=auto --comment=eits-tests-myisam-engine --experimental=collections/default.experimental --vardir=var-stmt-eits-tests-myisam-engine --suite=engines/iuds,engines/funcs --suite-timeout=500 --max-test-fail=0 --retry-failure=0 --mysqld=--default-storage-engine=myisam perl mysql-test-run.pl --timer --force --parallel=auto --comment=eits-rpl-binlog-row-tests-myisam-engine --experimental=collections/default.experimental --vardir=var-binlog-row-eits-tests-myisam-engine --suite=engines/iuds,engines/funcs --suite-timeout=500 --max-test-fail=0 --retry-failure=0 --mysqld=--default-storage-engine=myisam --do-test=rpl --mysqld=--binlog-format=row diff --git a/mysql-test/collections/mysql-5.1-bugteam.daily b/mysql-test/collections/mysql-5.1-bugteam.daily deleted file mode 100644 index 0503bd49f73..00000000000 --- a/mysql-test/collections/mysql-5.1-bugteam.daily +++ /dev/null @@ -1,5 +0,0 @@ -perl mysql-test-run.pl --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=ps_row --vardir=var-ps_row --ps-protocol --mysqld=--binlog-format=row --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=embedded --vardir=var-emebbed --embedded --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=rpl_binlog_row --vardir=var-rpl_binlog_row --suite=rpl,binlog --mysqld=--binlog-format=row --experimental=collections/default.experimental -perl mysql-test-run.pl --timer --force --parallel=auto --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 --experimental=collections/default.experimental diff --git a/mysql-test/collections/mysql-5.1-bugteam.push b/mysql-test/collections/mysql-5.1-bugteam.push deleted file mode 100644 index d01b98eb87f..00000000000 --- a/mysql-test/collections/mysql-5.1-bugteam.push +++ /dev/null @@ -1,4 +0,0 @@ -perl mysql-test-run.pl --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental --skip-ndb -perl mysql-test-run.pl --timer --force --parallel=auto --comment=ps_row --vardir=var-ps_row --suite=main --ps-protocol --mysqld=--binlog-format=row --experimental=collections/default.experimental --skip-ndb -perl mysql-test-run.pl --timer --force --parallel=auto --comment=embedded --vardir=var-emebbed --suite=main --embedded --experimental=collections/default.experimental --skip-ndb -perl mysql-test-run.pl --timer --force --parallel=auto --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 --experimental=collections/default.experimental --skip-ndb From 994132add6118b8d91893d495a8cb628c1017da4 Mon Sep 17 00:00:00 2001 From: "John H. Embretsen" Date: Tue, 8 Feb 2011 10:51:09 +0100 Subject: [PATCH 41/42] Fix for Bug#59837 - mysqlhotcopy fails with wrong error (11) on specific platforms: Test failed on a certain Linux platform in automated environment. It turns out that this platform has an old version of Perl modules DBI and DBD::mysql installed, as well as the OS itself being relatively old. Allowing error code 11 to be returned from mysqlhotcopy on expected error seems harmless and will make the test pass also with older libraries. --- mysql-test/include/mysqlhotcopy.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/include/mysqlhotcopy.inc b/mysql-test/include/mysqlhotcopy.inc index 110b0769ae1..fcf57a68644 100644 --- a/mysql-test/include/mysqlhotcopy.inc +++ b/mysql-test/include/mysqlhotcopy.inc @@ -107,7 +107,7 @@ DROP DATABASE hotcopy_save; --replace_result $MYSQLD_DATADIR MYSQLD_DATADIR --list_files $MYSQLD_DATADIR/hotcopy_save --replace_result $MASTER_MYSOCK MASTER_MYSOCK ---error 9,2304 +--error 9,11,2304 --exec $MYSQLHOTCOPY --quiet -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save --replace_result $MASTER_MYSOCK MASTER_MYSOCK --exec $MYSQLHOTCOPY --quiet --allowold -S $MASTER_MYSOCK -u root hotcopy_test hotcopy_save From c46e20f6c1275df5890838f92dc070fc7de99f63 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 9 Feb 2011 12:46:12 +0600 Subject: [PATCH 42/42] Follow up fix for bug#57450. batch_readline_init() was modified - return an error if the input source is a directory or a block device. This follow-up is necessary because on some platforms, such as Solaris, call to read() from directory may be successful. --- client/mysql.cc | 2 ++ client/readline.cc | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/client/mysql.cc b/client/mysql.cc index b19aaf61e58..e7e7b244a49 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1131,6 +1131,8 @@ int main(int argc,char *argv[]) if (status.batch && !status.line_buff && !(status.line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, stdin))) { + put_info("Can't initialize batch_readline - may be the input source is " + "a directory or a block device.", INFO_ERROR, 0); free_defaults(defaults_argv); my_end(0); exit(1); diff --git a/client/readline.cc b/client/readline.cc index 7f283502ecb..9ac9ea5824e 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "my_readline.h" static bool init_line_buffer(LINE_BUFFER *buffer,File file,ulong size, @@ -30,6 +31,13 @@ static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length); LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) { LINE_BUFFER *line_buff; + MY_STAT input_file_stat; + + if (my_fstat(fileno(file), &input_file_stat, MYF(MY_WME)) || + MY_S_ISDIR(input_file_stat.st_mode) || + MY_S_ISBLK(input_file_stat.st_mode)) + return 0; + if (!(line_buff=(LINE_BUFFER*) my_malloc(sizeof(*line_buff),MYF(MY_WME | MY_ZEROFILL)))) return 0;