diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2022-01-26 11:01:39 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2022-01-26 18:43:05 +0100 |
commit | 358921ce32203a9a8dd277a5ba7ac177c9e79e53 (patch) | |
tree | 41831126d41212b52264d32c6fe9bff93380f063 | |
parent | 349a595bc448fe654087f1e6444b17e9ee1583fc (diff) | |
download | mariadb-git-358921ce32203a9a8dd277a5ba7ac177c9e79e53.tar.gz |
MDEV-26938 Support descending indexes internally in InnoDB
This is loosely based on the InnoDB changes in
mysql/mysql-server@97fd8b1b6993340b361fa7f85da86a308f0b5e0c
that I had developed in 2015 or 2016.
For each B-tree key field, we will allow a flag ASC/DESC to be associated.
When PRIMARY KEY fields are internally appended to secondary indexes,
the ASC/DESC attribute will be inherited, so that covering index scans
will work as expected.
Note: Until the subsequent commit, the DESC attribute will be ignored
(no HA_REVERSE_SORT flag will be written to .frm files).
dict_field_t::descending: A new flag to denote descending order.
cmp_data(), cmp_dfield_dfield(): Add a new parameter descending.
cmp_dtuple_rec(), cmp_dtuple_rec_with_match(): Add a parameter "index".
dtuple_coll_eq(): Replaces dtuple_coll_cmp().
cmp_dfield_dfield_eq_prefix(): Replaces cmp_dfield_dfield_like_prefix().
dict_index_t::is_btree(): Check whether the index is a regular
B-tree index (not SPATIAL, FULLTEXT, or the ibuf.index,
or a corrupted index.
btr_cur_search_to_nth_level_func(): Only attempt to use
the adaptive hash index if index->is_btree().
This function may also be invoked on ibuf.index, and
cmp_dtuple_rec_with_match_bytes() will no longer work on ibuf.index
because it assumes that the index and record fields exactly match.
The ibuf.index is a special variadic index tree.
Thanks to Thirunarayanan Balathandayuthapani for fixing some bugs:
MDEV-27439, MDEV-27374/MDEV-27445.
43 files changed, 797 insertions, 644 deletions
diff --git a/mysql-test/suite/innodb/r/autoinc_persist,desc.rdiff b/mysql-test/suite/innodb/r/autoinc_persist,desc.rdiff new file mode 100644 index 00000000000..eeef34e071d --- /dev/null +++ b/mysql-test/suite/innodb/r/autoinc_persist,desc.rdiff @@ -0,0 +1,162 @@ +@@ -13,7 +13,7 @@ + # + # Pre-create several tables + SET SQL_MODE='STRICT_ALL_TABLES'; +-CREATE TABLE t1(a TINYINT AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t1(a TINYINT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t1 VALUES(0), (0), (0), (0), (-1), (-10), (0), + (20), (30), (31); + SELECT * FROM t1; +@@ -28,7 +28,7 @@ + 20 + 30 + 31 +-CREATE TABLE t2(a TINYINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t2(a TINYINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t2 VALUES(-5); + ERROR 22003: Out of range value for column 'a' at row 1 + INSERT INTO t2 VALUES(0), (0), (0), (0), (8), (10), (0), +@@ -45,7 +45,7 @@ + 20 + 30 + 31 +-CREATE TABLE t3(a SMALLINT AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t3(a SMALLINT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t3 VALUES(0), (0), (0), (0), (-1), (-10), (0), + (20), (30), (31), (1024), (4096); + SELECT * FROM t3; +@@ -62,7 +62,7 @@ + 31 + 1024 + 4096 +-CREATE TABLE t4(a SMALLINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t4(a SMALLINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t4 VALUES(-5); + ERROR 22003: Out of range value for column 'a' at row 1 + INSERT INTO t4 VALUES(0), (0), (0), (0), (8), (10), (0), +@@ -81,7 +81,7 @@ + 31 + 1024 + 4096 +-CREATE TABLE t5(a MEDIUMINT AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t5(a MEDIUMINT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t5 VALUES(0), (0), (0), (0), (-1), (-10), (0), + (20), (30), (31), (1000000), (1000005); + SELECT * FROM t5; +@@ -98,7 +98,7 @@ + 31 + 1000000 + 1000005 +-CREATE TABLE t6(a MEDIUMINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t6(a MEDIUMINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t6 VALUES(-5); + ERROR 22003: Out of range value for column 'a' at row 1 + INSERT INTO t6 VALUES(0), (0), (0), (0), (8), (10), (0), +@@ -117,7 +117,7 @@ + 31 + 1000000 + 1000005 +-CREATE TABLE t7(a INT AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t7(a INT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t7 VALUES(0), (0), (0), (0), (-1), (-10), (0), + (20), (30), (31), (100000000), (100000008); + SELECT * FROM t7; +@@ -134,7 +134,7 @@ + 31 + 100000000 + 100000008 +-CREATE TABLE t8(a INT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t8(a INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t8 VALUES(-5); + ERROR 22003: Out of range value for column 'a' at row 1 + INSERT INTO t8 VALUES(0), (0), (0), (0), (8), (10), (0), +@@ -153,7 +153,7 @@ + 31 + 100000000 + 100000008 +-CREATE TABLE t9(a BIGINT AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t9(a BIGINT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t9 VALUES(0), (0), (0), (0), (-1), (-10), (0), + (20), (30), (31), (100000000000), (100000000006); + SELECT * FROM t9; +@@ -170,7 +170,7 @@ + 31 + 100000000000 + 100000000006 +-CREATE TABLE t10(a BIGINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t10(a BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t10 VALUES(-5); + ERROR 22003: Out of range value for column 'a' at row 1 + INSERT INTO t10 VALUES(0), (0), (0), (0), (8), (10), (0), +@@ -189,7 +189,7 @@ + 31 + 100000000000 + 100000000006 +-CREATE TABLE t11(a FLOAT AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t11(a FLOAT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0), + (20), (30), (31); + SELECT * FROM t11; +@@ -204,7 +204,7 @@ + 20 + 30 + 31 +-CREATE TABLE t12(a DOUBLE AUTO_INCREMENT KEY) ENGINE = InnoDB; ++CREATE TABLE t12(a DOUBLE AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB; + INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0), + (20), (30), (31); + SELECT * FROM t12; +@@ -242,7 +242,7 @@ + SELECT MAX(a) AS `Expect 100000000000` FROM t9; + Expect 100000000000 + 100000000000 +-CREATE TABLE t13(a INT AUTO_INCREMENT KEY) ENGINE = InnoDB, ++CREATE TABLE t13(a INT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE = InnoDB, + AUTO_INCREMENT = 1234; + # restart + SHOW CREATE TABLE t13; +@@ -842,7 +842,7 @@ + 126 + DROP TABLE t_copy, it_copy; + # Scenario 9: Test the sql_mode = NO_AUTO_VALUE_ON_ZERO +-CREATE TABLE t30 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT, key(b)) ENGINE = InnoDB; ++CREATE TABLE t30 (a INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(a DESC), b INT, key(b)) ENGINE = InnoDB; + set SQL_MODE = NO_AUTO_VALUE_ON_ZERO; + INSERT INTO t30 VALUES(NULL, 1), (200, 2), (0, 3); + INSERT INTO t30(b) VALUES(4), (5), (6), (7); +@@ -869,7 +869,7 @@ + set global innodb_flush_log_at_trx_commit=1; + CREATE TABLE t31 (a INT) ENGINE = InnoDB; + INSERT INTO t31 VALUES(1), (2); +-ALTER TABLE t31 ADD b INT AUTO_INCREMENT PRIMARY KEY; ++ALTER TABLE t31 ADD b INT AUTO_INCREMENT, ADD PRIMARY KEY(b DESC); + INSERT INTO t31 VALUES(3, 0), (4, NULL), (5, NULL); + INSERT INTO t31 VALUES(6, 0); + ERROR 23000: Duplicate entry '0' for key 'PRIMARY' +@@ -882,7 +882,7 @@ + 5 4 + SET SQL_MODE = 0; + # Scenario 10: Rollback would not rollback the counter +-CREATE TABLE t32 (a BIGINT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; ++CREATE TABLE t32 (a BIGINT AUTO_INCREMENT, PRIMARY KEY(a DESC)) ENGINE=InnoDB; + INSERT INTO t32 VALUES(0), (0); + # Ensure that all changes before the server is killed are persisted. + set global innodb_flush_log_at_trx_commit=1; +@@ -897,7 +897,7 @@ + # increasing the counter + CREATE TABLE t33 ( + a BIGINT NOT NULL PRIMARY KEY, +-b BIGINT NOT NULL AUTO_INCREMENT, KEY(b)) ENGINE = InnoDB; ++b BIGINT NOT NULL AUTO_INCREMENT, INDEX(b DESC)) ENGINE = InnoDB; + INSERT INTO t33 VALUES(1, NULL); + INSERT INTO t33 VALUES(2, NULL); + INSERT INTO t33 VALUES(2, NULL); +@@ -965,7 +965,7 @@ + DROP TABLE t33; + CREATE TABLE t33 ( + a BIGINT NOT NULL PRIMARY KEY, +-b BIGINT NOT NULL AUTO_INCREMENT, KEY(b)) ENGINE = InnoDB; ++b BIGINT NOT NULL AUTO_INCREMENT, INDEX(b DESC)) ENGINE = InnoDB; + ALTER TABLE t33 DISCARD TABLESPACE; + restore: t33 .ibd and .cfg files + ALTER TABLE t33 IMPORT TABLESPACE; diff --git a/mysql-test/suite/innodb/r/autoinc_persist.result b/mysql-test/suite/innodb/r/autoinc_persist.result index ee796160406..91d6d908a82 100644 --- a/mysql-test/suite/innodb/r/autoinc_persist.result +++ b/mysql-test/suite/innodb/r/autoinc_persist.result @@ -242,7 +242,7 @@ DELETE FROM t9 WHERE a > 100000000000; SELECT MAX(a) AS `Expect 100000000000` FROM t9; Expect 100000000000 100000000000 -CREATE TABLE t13(a INT AUTO_INCREMENT PRIMARY KEY) ENGINE = InnoDB, +CREATE TABLE t13(a INT AUTO_INCREMENT KEY) ENGINE = InnoDB, AUTO_INCREMENT = 1234; # restart SHOW CREATE TABLE t13; @@ -882,8 +882,7 @@ a b 5 4 SET SQL_MODE = 0; # Scenario 10: Rollback would not rollback the counter -CREATE TABLE t32 ( -a BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +CREATE TABLE t32 (a BIGINT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; INSERT INTO t32 VALUES(0), (0); # Ensure that all changes before the server is killed are persisted. set global innodb_flush_log_at_trx_commit=1; @@ -898,8 +897,7 @@ ROLLBACK; # increasing the counter CREATE TABLE t33 ( a BIGINT NOT NULL PRIMARY KEY, -b BIGINT NOT NULL AUTO_INCREMENT, -KEY(b)) ENGINE = InnoDB; +b BIGINT NOT NULL AUTO_INCREMENT, KEY(b)) ENGINE = InnoDB; INSERT INTO t33 VALUES(1, NULL); INSERT INTO t33 VALUES(2, NULL); INSERT INTO t33 VALUES(2, NULL); @@ -967,8 +965,7 @@ UNLOCK TABLES; DROP TABLE t33; CREATE TABLE t33 ( a BIGINT NOT NULL PRIMARY KEY, -b BIGINT NOT NULL AUTO_INCREMENT, -KEY(b)) ENGINE = InnoDB; +b BIGINT NOT NULL AUTO_INCREMENT, KEY(b)) ENGINE = InnoDB; ALTER TABLE t33 DISCARD TABLESPACE; restore: t33 .ibd and .cfg files ALTER TABLE t33 IMPORT TABLESPACE; diff --git a/mysql-test/suite/innodb/r/innodb-index.result b/mysql-test/suite/innodb/r/innodb-index.result index 1bcb4e620bb..b0c0c26afd4 100644 --- a/mysql-test/suite/innodb/r/innodb-index.result +++ b/mysql-test/suite/innodb/r/innodb-index.result @@ -1947,3 +1947,51 @@ Warnings: Warning 1082 InnoDB: Table test/t contains 0 indexes inside InnoDB, which is different from the number of indexes 1 defined in the MariaDB Warning 1082 InnoDB: Table test/t contains 0 indexes inside InnoDB, which is different from the number of indexes 1 defined in the MariaDB DROP TABLE t; +# +# MDEV-27374 InnoDB table becomes corrupt after renaming DESC-indexed column +# +CREATE TABLE t1 (a VARCHAR(8), PRIMARY KEY(a DESC)) ENGINE=InnoDB; +ALTER TABLE t1 RENAME COLUMN a TO b, ALGORITHM=INPLACE; +SELECT TABLE_ID INTO @table_id FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME="test/t1"; +SELECT INDEX_ID INTO @index_id FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE TABLE_ID = @table_id; +SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_FIELDS WHERE INDEX_ID=@index_id; +NAME +b +DROP TABLE t1; +# +# MDEV-27432 ASC/DESC primary and unique keys cause index +# inconsistency between InnoDB and server +# +CREATE TABLE t1 (id INT NOT NULL, UNIQUE(id DESC)) ENGINE=InnoDB; +ALTER TABLE t1 ADD PRIMARY KEY (id), ALGORITHM=INPLACE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +DROP TABLE t1; +# +# MDEV-27445 Index inconsistency and assertion failure after +# renaming virtual column with DESC key +# +CREATE TABLE t1(a INT, b INT AS (a), KEY(a, b DESC)) ENGINE=InnoDB; +ALTER TABLE t1 RENAME COLUMN IF EXISTS b TO v; +ALTER TABLE t1 FORCE; +DROP TABLE t1; +# +# MDEV-27592 DESC primary index fails to set wide format +# while renaming the column +# +CREATE TABLE t1 (id INT PRIMARY KEY, a CHAR(8), b INT, KEY(a DESC,b)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,'foo',10); +ALTER TABLE t1 RENAME COLUMN b TO c, ALGORITHM=INPLACE; +SELECT TABLE_ID INTO @table_id FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME="test/t1"; +SELECT INDEX_ID INTO @index_id FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE TABLE_ID = @table_id ORDER BY INDEX_ID DESC LIMIT 1; +SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_FIELDS WHERE INDEX_ID=@index_id; +NAME +a +c +DROP TABLE t1; +# End of 10.8 tests diff --git a/mysql-test/suite/innodb/t/autoinc_persist.combinations b/mysql-test/suite/innodb/t/autoinc_persist.combinations new file mode 100644 index 00000000000..4ae2948e523 --- /dev/null +++ b/mysql-test/suite/innodb/t/autoinc_persist.combinations @@ -0,0 +1,2 @@ +[asc] +[desc] diff --git a/mysql-test/suite/innodb/t/autoinc_persist.test b/mysql-test/suite/innodb/t/autoinc_persist.test index 6e094b40e02..c6135b5580c 100644 --- a/mysql-test/suite/innodb/t/autoinc_persist.test +++ b/mysql-test/suite/innodb/t/autoinc_persist.test @@ -2,6 +2,19 @@ # Restarting is not supported when testing the embedded server. --source include/not_embedded.inc +if ($MTR_COMBINATION_DESC) { + let $AUTO_INCREMENT_KEY_a= AUTO_INCREMENT, PRIMARY KEY(a DESC); + let $AUTO_INCREMENT_KEY_b= AUTO_INCREMENT, INDEX(b DESC); + let $AUTO_INCREMENT_PRIMARY_KEY_a= AUTO_INCREMENT, PRIMARY KEY(a DESC); + let $AUTO_INCREMENT_PRIMARY_KEY_b= AUTO_INCREMENT, ADD PRIMARY KEY(b DESC); +} +if (!$MTR_COMBINATION_DESC) { + let $AUTO_INCREMENT_KEY_a= AUTO_INCREMENT KEY; + let $AUTO_INCREMENT_KEY_b= AUTO_INCREMENT, KEY(b); + let $AUTO_INCREMENT_PRIMARY_KEY_a= AUTO_INCREMENT PRIMARY KEY; + let $AUTO_INCREMENT_PRIMARY_KEY_b= AUTO_INCREMENT PRIMARY KEY; +} + --echo # --echo # MDEV-6076 Persistent AUTO_INCREMENT for InnoDB --echo # @@ -20,72 +33,72 @@ SET SQL_MODE='STRICT_ALL_TABLES'; -CREATE TABLE t1(a TINYINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t1(a TINYINT $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; INSERT INTO t1 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31); SELECT * FROM t1; -CREATE TABLE t2(a TINYINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t2(a TINYINT UNSIGNED $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; --error ER_WARN_DATA_OUT_OF_RANGE INSERT INTO t2 VALUES(-5); INSERT INTO t2 VALUES(0), (0), (0), (0), (8), (10), (0), (20), (30), (31); SELECT * FROM t2; -CREATE TABLE t3(a SMALLINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t3(a SMALLINT $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; INSERT INTO t3 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31), (1024), (4096); SELECT * FROM t3; -CREATE TABLE t4(a SMALLINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t4(a SMALLINT UNSIGNED $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; --error ER_WARN_DATA_OUT_OF_RANGE INSERT INTO t4 VALUES(-5); INSERT INTO t4 VALUES(0), (0), (0), (0), (8), (10), (0), (20), (30), (31), (1024), (4096); SELECT * FROM t4; -CREATE TABLE t5(a MEDIUMINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t5(a MEDIUMINT $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; INSERT INTO t5 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31), (1000000), (1000005); SELECT * FROM t5; -CREATE TABLE t6(a MEDIUMINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t6(a MEDIUMINT UNSIGNED $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; --error ER_WARN_DATA_OUT_OF_RANGE INSERT INTO t6 VALUES(-5); INSERT INTO t6 VALUES(0), (0), (0), (0), (8), (10), (0), (20), (30), (31), (1000000), (1000005); SELECT * FROM t6; -CREATE TABLE t7(a INT AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t7(a INT $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; INSERT INTO t7 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31), (100000000), (100000008); SELECT * FROM t7; -CREATE TABLE t8(a INT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t8(a INT UNSIGNED $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; --error ER_WARN_DATA_OUT_OF_RANGE INSERT INTO t8 VALUES(-5); INSERT INTO t8 VALUES(0), (0), (0), (0), (8), (10), (0), (20), (30), (31), (100000000), (100000008); SELECT * FROM t8; -CREATE TABLE t9(a BIGINT AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t9(a BIGINT $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; INSERT INTO t9 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31), (100000000000), (100000000006); SELECT * FROM t9; -CREATE TABLE t10(a BIGINT UNSIGNED AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t10(a BIGINT UNSIGNED $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; --error ER_WARN_DATA_OUT_OF_RANGE INSERT INTO t10 VALUES(-5); INSERT INTO t10 VALUES(0), (0), (0), (0), (8), (10), (0), (20), (30), (31), (100000000000), (100000000006); SELECT * FROM t10; -CREATE TABLE t11(a FLOAT AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t11(a FLOAT $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; INSERT INTO t11 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31); SELECT * FROM t11; -CREATE TABLE t12(a DOUBLE AUTO_INCREMENT KEY) ENGINE = InnoDB; +eval CREATE TABLE t12(a DOUBLE $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB; INSERT INTO t12 VALUES(0), (0), (0), (0), (-1), (-10), (0), (20), (30), (31); SELECT * FROM t12; @@ -105,7 +118,7 @@ SELECT MAX(a) AS `Expect 100000000` FROM t7; DELETE FROM t9 WHERE a > 100000000000; SELECT MAX(a) AS `Expect 100000000000` FROM t9; -CREATE TABLE t13(a INT AUTO_INCREMENT PRIMARY KEY) ENGINE = InnoDB, +eval CREATE TABLE t13(a INT $AUTO_INCREMENT_KEY_a) ENGINE = InnoDB, AUTO_INCREMENT = 1234; --source include/restart_mysqld.inc @@ -439,7 +452,7 @@ INSERT INTO t3 VALUES(0), (0), (200), (210); --echo # Scenario 9: Test the sql_mode = NO_AUTO_VALUE_ON_ZERO -CREATE TABLE t30 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT, key(b)) ENGINE = InnoDB; +eval CREATE TABLE t30 (a INT NOT NULL $AUTO_INCREMENT_PRIMARY_KEY_a, b INT, key(b)) ENGINE = InnoDB; set SQL_MODE = NO_AUTO_VALUE_ON_ZERO; @@ -454,7 +467,7 @@ set global innodb_flush_log_at_trx_commit=1; CREATE TABLE t31 (a INT) ENGINE = InnoDB; INSERT INTO t31 VALUES(1), (2); -ALTER TABLE t31 ADD b INT AUTO_INCREMENT PRIMARY KEY; +eval ALTER TABLE t31 ADD b INT $AUTO_INCREMENT_PRIMARY_KEY_b; INSERT INTO t31 VALUES(3, 0), (4, NULL), (5, NULL); --error ER_DUP_ENTRY INSERT INTO t31 VALUES(6, 0); @@ -463,8 +476,7 @@ SELECT * FROM t31; SET SQL_MODE = 0; --echo # Scenario 10: Rollback would not rollback the counter -CREATE TABLE t32 ( -a BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +eval CREATE TABLE t32 (a BIGINT $AUTO_INCREMENT_PRIMARY_KEY_a) ENGINE=InnoDB; INSERT INTO t32 VALUES(0), (0); @@ -480,10 +492,9 @@ ROLLBACK; --echo # Scenario 11: Test duplicate primary key/secondary key will not stop --echo # increasing the counter -CREATE TABLE t33 ( +eval CREATE TABLE t33 ( a BIGINT NOT NULL PRIMARY KEY, -b BIGINT NOT NULL AUTO_INCREMENT, -KEY(b)) ENGINE = InnoDB; +b BIGINT NOT NULL $AUTO_INCREMENT_KEY_b) ENGINE = InnoDB; INSERT INTO t33 VALUES(1, NULL); INSERT INTO t33 VALUES(2, NULL); @@ -539,10 +550,9 @@ ib_backup_tablespaces("test", "t33"); EOF UNLOCK TABLES; DROP TABLE t33; -CREATE TABLE t33 ( +eval CREATE TABLE t33 ( a BIGINT NOT NULL PRIMARY KEY, -b BIGINT NOT NULL AUTO_INCREMENT, -KEY(b)) ENGINE = InnoDB; +b BIGINT NOT NULL $AUTO_INCREMENT_KEY_b) ENGINE = InnoDB; ALTER TABLE t33 DISCARD TABLESPACE; perl; do "$ENV{MTR_SUITE_DIR}/include/innodb-util.pl"; diff --git a/mysql-test/suite/innodb/t/innodb-index.test b/mysql-test/suite/innodb/t/innodb-index.test index c7aa6250da5..03cbb5aef9d 100644 --- a/mysql-test/suite/innodb/t/innodb-index.test +++ b/mysql-test/suite/innodb/t/innodb-index.test @@ -1193,6 +1193,48 @@ SHOW CREATE TABLE t; --disable_prepare_warnings DROP TABLE t; +--echo # +--echo # MDEV-27374 InnoDB table becomes corrupt after renaming DESC-indexed column +--echo # +CREATE TABLE t1 (a VARCHAR(8), PRIMARY KEY(a DESC)) ENGINE=InnoDB; +ALTER TABLE t1 RENAME COLUMN a TO b, ALGORITHM=INPLACE; +SELECT TABLE_ID INTO @table_id FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME="test/t1"; +SELECT INDEX_ID INTO @index_id FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE TABLE_ID = @table_id; +SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_FIELDS WHERE INDEX_ID=@index_id; +DROP TABLE t1; + +--echo # +--echo # MDEV-27432 ASC/DESC primary and unique keys cause index +--echo # inconsistency between InnoDB and server +--echo # +CREATE TABLE t1 (id INT NOT NULL, UNIQUE(id DESC)) ENGINE=InnoDB; +ALTER TABLE t1 ADD PRIMARY KEY (id), ALGORITHM=INPLACE; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--echo # +--echo # MDEV-27445 Index inconsistency and assertion failure after +--echo # renaming virtual column with DESC key +--echo # +CREATE TABLE t1(a INT, b INT AS (a), KEY(a, b DESC)) ENGINE=InnoDB; +ALTER TABLE t1 RENAME COLUMN IF EXISTS b TO v; +ALTER TABLE t1 FORCE; +DROP TABLE t1; + +--echo # +--echo # MDEV-27592 DESC primary index fails to set wide format +--echo # while renaming the column +--echo # +CREATE TABLE t1 (id INT PRIMARY KEY, a CHAR(8), b INT, KEY(a DESC,b)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,'foo',10); +ALTER TABLE t1 RENAME COLUMN b TO c, ALGORITHM=INPLACE; +SELECT TABLE_ID INTO @table_id FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME="test/t1"; +SELECT INDEX_ID INTO @index_id FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE TABLE_ID = @table_id ORDER BY INDEX_ID DESC LIMIT 1; +SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_FIELDS WHERE INDEX_ID=@index_id; +DROP TABLE t1; + +--echo # End of 10.8 tests + --disable_query_log call mtr.add_suppression("InnoDB: Tablespace .* was not found at .*t[12].ibd."); diff --git a/mysql-test/suite/innodb_fts/r/create.result b/mysql-test/suite/innodb_fts/r/create.result index 3ca24f5253d..7be9333e647 100644 --- a/mysql-test/suite/innodb_fts/r/create.result +++ b/mysql-test/suite/innodb_fts/r/create.result @@ -187,4 +187,16 @@ FTS_DOC_ID bigint unsigned not null, unique key FTS_DOC_ID_INDEX(FTS_DOC_ID, f1), fulltext (f2)) engine=innodb; -ERROR 42000: Incorrect index name 'FTS_DOC_ID_INDEX' +ERROR HY000: Index 'FTS_DOC_ID_INDEX' is of wrong type for an InnoDB FULLTEXT index +# +# MDEV-26938 Support descending indexes internally in InnoDB +# +CREATE TABLE t1(a INT PRIMARY KEY, b TEXT, FTS_DOC_ID BIGINT UNSIGNED NOT NULL, +UNIQUE KEY FTS_DOC_ID_INDEX(FTS_DOC_ID DESC), FULLTEXT(b)) +ENGINE=InnoDB; +DROP TABLE t1; +CREATE TABLE t1(a INT PRIMARY KEY, b TEXT, FTS_DOC_ID BIGINT UNSIGNED NOT NULL, +UNIQUE KEY FTS_DOC_ID_INDEX(FTS_DOC_ID DESC)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FULLTEXT INDEX(b), ALGORITHM=INPLACE; +ALTER TABLE t1 ADD FULLTEXT INDEX(b), ALGORITHM=COPY; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_fts/t/create.test b/mysql-test/suite/innodb_fts/t/create.test index 38c93de4982..6f5da11a66c 100644 --- a/mysql-test/suite/innodb_fts/t/create.test +++ b/mysql-test/suite/innodb_fts/t/create.test @@ -110,10 +110,29 @@ SET GLOBAL innodb_optimize_fulltext_only= @optimize_fulltext.save; --echo # --echo # MDEV-24403 Segfault on CREATE TABLE with explicit FTS_DOC_ID_INDEX by multiple fields --echo # ---error ER_WRONG_NAME_FOR_INDEX +--error ER_INNODB_FT_WRONG_DOCID_INDEX create table t1 ( f1 int, f2 text, FTS_DOC_ID bigint unsigned not null, unique key FTS_DOC_ID_INDEX(FTS_DOC_ID, f1), fulltext (f2)) engine=innodb; + +--echo # +--echo # MDEV-26938 Support descending indexes internally in InnoDB +--echo # + +# Unfortunately, the HA_REVERSE_SORT flag is not being stored in the .frm file. +#FIXME: --error ER_INNODB_FT_WRONG_DOCID_INDEX +CREATE TABLE t1(a INT PRIMARY KEY, b TEXT, FTS_DOC_ID BIGINT UNSIGNED NOT NULL, + UNIQUE KEY FTS_DOC_ID_INDEX(FTS_DOC_ID DESC), FULLTEXT(b)) +ENGINE=InnoDB; +DROP TABLE t1; + +CREATE TABLE t1(a INT PRIMARY KEY, b TEXT, FTS_DOC_ID BIGINT UNSIGNED NOT NULL, + UNIQUE KEY FTS_DOC_ID_INDEX(FTS_DOC_ID DESC)) ENGINE=InnoDB; +#FIXME: --error ER_INNODB_FT_WRONG_DOCID_INDEX +ALTER TABLE t1 ADD FULLTEXT INDEX(b), ALGORITHM=INPLACE; +#FIXME: --error ER_INNODB_FT_WRONG_DOCID_INDEX +ALTER TABLE t1 ADD FULLTEXT INDEX(b), ALGORITHM=COPY; +DROP TABLE t1; diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index a2ea7c24541..af46ff9e687 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -203,7 +203,6 @@ SET(INNOBASE_SOURCES include/que0types.h include/read0types.h include/rem0cmp.h - include/rem0cmp.ic include/rem0rec.h include/rem0rec.ic include/rem0types.h diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index bd18fd2d45f..08a0ae55793 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -2228,7 +2228,8 @@ btr_page_insert_fits( rec = page_rec_get_next(page_get_infimum_rec(page)); end_rec = page_rec_get_next(btr_cur_get_rec(cursor)); - } else if (cmp_dtuple_rec(tuple, split_rec, *offsets) >= 0) { + } else if (cmp_dtuple_rec(tuple, split_rec, cursor->index, + *offsets) >= 0) { rec = page_rec_get_next(page_get_infimum_rec(page)); end_rec = split_rec; @@ -2514,7 +2515,7 @@ btr_page_tuple_smaller( ? cursor->index->n_core_fields : 0, n_uniq, heap); - return(cmp_dtuple_rec(tuple, first_rec, *offsets) < 0); + return cmp_dtuple_rec(tuple, first_rec, cursor->index, *offsets) < 0; } /** Insert the tuple into the right sibling page, if the cursor is at the end @@ -2804,7 +2805,8 @@ func_start: n_uniq, heap); insert_left = !tuple - || cmp_dtuple_rec(tuple, split_rec, *offsets) < 0; + || cmp_dtuple_rec(tuple, split_rec, cursor->index, + *offsets) < 0; if (!insert_left && new_page_zip && n_iterations > 0) { /* If a compressed page has already been split, @@ -4233,7 +4235,8 @@ btr_check_node_ptr( tuple, btr_cur_get_rec(&cursor), PAGE_CUR_WITHIN)); } else { - ut_a(!cmp_dtuple_rec(tuple, btr_cur_get_rec(&cursor), offsets)); + ut_a(!cmp_dtuple_rec(tuple, btr_cur_get_rec(&cursor), index, + offsets)); } func_exit: mem_heap_free(heap); @@ -4731,7 +4734,7 @@ loop: /* For spatial index, we cannot guarantee the key ordering across pages, so skip the record compare verification for now. Will enhanced in special R-Tree index validation scheme */ - if (!dict_index_is_spatial(index) + if (index->is_btree() && cmp_rec_rec(rec, right_rec, offsets, offsets2, index) >= 0) { @@ -4766,9 +4769,7 @@ loop: in parent level and linked pages in the child level. 2) Search parent from root is very costly for R-tree. We will add special validation mechanism for R-tree later (WL #7520) */ - if (!dict_index_is_spatial(index) - && block->page.id().page_no() != dict_index_get_page(index)) { - + if (index->is_btree() && block->page.id().page_no() != index->page) { /* Check father node pointers */ rec_t* node_ptr; @@ -4825,7 +4826,7 @@ loop: page_rec_get_next(page_get_infimum_rec(page)), 0, heap, btr_page_get_level(page)); - if (cmp_dtuple_rec(node_ptr_tuple, node_ptr, + if (cmp_dtuple_rec(node_ptr_tuple, node_ptr, index, offsets)) { const rec_t* first_rec = page_rec_get_next( page_get_infimum_rec(page)); diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index ceac301a502..5f6562e5394 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -1411,7 +1411,7 @@ btr_cur_search_to_nth_level_func( # endif /* PAGE_CUR_LE_OR_EXTENDS */ && info->last_hash_succ && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG) - && !index->is_spatial() && !index->table->is_temporary() + && index->is_btree() && !index->table->is_temporary() && btr_search_guess_on_hash(index, info, tuple, mode, latch_mode, cursor, ahi_latch, mtr)) { @@ -1636,7 +1636,6 @@ retry_page_get: case BTR_INSERT_OP: case BTR_INSERT_IGNORE_UNIQUE_OP: ut_ad(buf_mode == BUF_GET_IF_IN_POOL); - ut_ad(!dict_index_is_spatial(index)); if (ibuf_insert(IBUF_OP_INSERT, tuple, index, page_id, zip_size, cursor->thr)) { @@ -1649,7 +1648,6 @@ retry_page_get: case BTR_DELMARK_OP: ut_ad(buf_mode == BUF_GET_IF_IN_POOL); - ut_ad(!dict_index_is_spatial(index)); if (ibuf_insert(IBUF_OP_DELETE_MARK, tuple, index, page_id, zip_size, @@ -1664,7 +1662,7 @@ retry_page_get: case BTR_DELETE_OP: ut_ad(buf_mode == BUF_GET_IF_IN_POOL_OR_WATCH); - ut_ad(!dict_index_is_spatial(index)); + ut_ad(index->is_btree()); auto& chain = buf_pool.page_hash.cell_get( page_id.fold()); @@ -1959,9 +1957,8 @@ retry_page_get: DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1; } #ifdef BTR_CUR_HASH_ADAPT - } else if (height == 0 && btr_search_enabled - && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG) - && !dict_index_is_spatial(index)) { + } else if (height == 0 && index->is_btree() && btr_search_enabled + && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG)) { /* The adaptive hash index is only used when searching for leaf pages (height==0), but not in r-trees. We only need the byte prefix comparison for the purpose diff --git a/storage/innobase/btr/btr0pcur.cc b/storage/innobase/btr/btr0pcur.cc index ac91f87c8e1..9c32236db9c 100644 --- a/storage/innobase/btr/btr0pcur.cc +++ b/storage/innobase/btr/btr0pcur.cc @@ -430,7 +430,7 @@ btr_pcur_restore_position( rec_offs_init(offsets); if (cursor->rel_pos == BTR_PCUR_ON && btr_pcur_is_on_user_rec(cursor) - && !cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor), + && !cmp_dtuple_rec(tuple, btr_pcur_get_rec(cursor), index, rec_get_offsets(btr_pcur_get_rec(cursor), index, offsets, index->n_core_fields, diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index 96bbd8af3c1..6589c72ac4a 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -796,7 +796,8 @@ btr_search_check_guess( offsets = rec_get_offsets(rec, cursor->index, offsets, cursor->index->n_core_fields, n_unique, &heap); - cmp = cmp_dtuple_rec_with_match(tuple, rec, offsets, &match); + cmp = cmp_dtuple_rec_with_match(tuple, rec, cursor->index, offsets, + &match); if (mode == PAGE_CUR_GE) { if (cmp > 0) { @@ -848,7 +849,7 @@ btr_search_check_guess( cursor->index->n_core_fields, n_unique, &heap); cmp = cmp_dtuple_rec_with_match( - tuple, prev_rec, offsets, &match); + tuple, prev_rec, cursor->index, offsets, &match); if (mode == PAGE_CUR_GE) { success = cmp > 0; } else { @@ -872,7 +873,7 @@ btr_search_check_guess( cursor->index->n_core_fields, n_unique, &heap); cmp = cmp_dtuple_rec_with_match( - tuple, next_rec, offsets, &match); + tuple, next_rec, cursor->index, offsets, &match); if (mode == PAGE_CUR_LE) { success = cmp < 0; cursor->up_match = match; diff --git a/storage/innobase/data/data0data.cc b/storage/innobase/data/data0data.cc index 13efd7f3665..e63ba450ea1 100644 --- a/storage/innobase/data/data0data.cc +++ b/storage/innobase/data/data0data.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2020, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -79,40 +79,6 @@ void dtuple_t::trim(const dict_index_t& index) n_fields = i; } -/** Compare two data tuples. -@param[in] tuple1 first data tuple -@param[in] tuple2 second data tuple -@return positive, 0, negative if tuple1 is greater, equal, less, than tuple2, -respectively */ -int -dtuple_coll_cmp( - const dtuple_t* tuple1, - const dtuple_t* tuple2) -{ - ulint n_fields; - ulint i; - int cmp; - - ut_ad(tuple1 != NULL); - ut_ad(tuple2 != NULL); - ut_ad(tuple1->magic_n == DATA_TUPLE_MAGIC_N); - ut_ad(tuple2->magic_n == DATA_TUPLE_MAGIC_N); - ut_ad(dtuple_check_typed(tuple1)); - ut_ad(dtuple_check_typed(tuple2)); - - n_fields = dtuple_get_n_fields(tuple1); - - cmp = (int) n_fields - (int) dtuple_get_n_fields(tuple2); - - for (i = 0; cmp == 0 && i < n_fields; i++) { - const dfield_t* field1 = dtuple_get_nth_field(tuple1, i); - const dfield_t* field2 = dtuple_get_nth_field(tuple2, i); - cmp = cmp_dfield_dfield(field1, field2); - } - - return(cmp); -} - /*********************************************************************//** Sets number of fields used in a tuple. Normally this is set in dtuple_create, but if you want later to set it smaller, you can use this. */ diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index f1bc2fcd980..68bb2c44d3f 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -536,15 +536,15 @@ dict_create_sys_fields_tuple( dict_field_t* field; dfield_t* dfield; byte* ptr; - ibool index_contains_column_prefix_field = FALSE; - ulint j; + bool wide_pos = false; ut_ad(index); ut_ad(heap); - for (j = 0; j < index->n_fields; j++) { - if (dict_index_get_nth_field(index, j)->prefix_len > 0) { - index_contains_column_prefix_field = TRUE; + for (unsigned j = 0; j < index->n_fields; j++) { + const dict_field_t* f = dict_index_get_nth_field(index, j); + if (f->prefix_len || f->descending) { + wide_pos = true; break; } } @@ -569,12 +569,15 @@ dict_create_sys_fields_tuple( ptr = static_cast<byte*>(mem_heap_alloc(heap, 4)); - if (index_contains_column_prefix_field) { - /* If there are column prefix fields in the index, then - we store the number of the field to the 2 HIGH bytes - and the prefix length to the 2 low bytes, */ - - mach_write_to_4(ptr, (fld_no << 16) + field->prefix_len); + if (wide_pos) { + /* If there are column prefixes or columns with + descending order in the index, then we write the + field number to the 16 most significant bits, + the DESC flag to bit 15, and the prefix length + in the 15 least significant bits. */ + mach_write_to_4(ptr, (fld_no << 16) + | (!!field->descending) << 15 + | field->prefix_len); } else { /* Else we store the number of the field to the 2 LOW bytes. This is to keep the storage format compatible with diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index a990a518d5b..54172b9a408 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -2,7 +2,7 @@ Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2021, MariaDB Corporation. +Copyright (c) 2013, 2022, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -1233,7 +1233,7 @@ static bool dict_table_can_be_evicted(dict_table_t *table) dict_index_t *dict_index_t::clone() const { ut_ad(n_fields); - ut_ad(!(type & (DICT_IBUF | DICT_SPATIAL | DICT_FTS))); + ut_ad(is_btree()); ut_ad(online_status == ONLINE_INDEX_COMPLETE); ut_ad(is_committed()); ut_ad(!is_dummy); @@ -2253,15 +2253,14 @@ found: return(TRUE); } -/*******************************************************************//** -Adds a column to index. */ -void -dict_index_add_col( -/*===============*/ - dict_index_t* index, /*!< in/out: index */ - const dict_table_t* table, /*!< in: table */ - dict_col_t* col, /*!< in: column */ - ulint prefix_len) /*!< in: column prefix length */ +/** Add a column to an index. +@param index index +@param table table +@param col column +@param prefix_len column prefix length +@param descending whether to use descending order */ +void dict_index_add_col(dict_index_t *index, const dict_table_t *table, + dict_col_t *col, ulint prefix_len, bool descending) { dict_field_t* field; const char* col_name; @@ -2299,6 +2298,8 @@ dict_index_add_col( field->fixed_len = 0; } + field->descending = descending; + /* The comparison limit above must be constant. If it were changed, the disk format of some fixed-length columns would change, which would be a disaster. */ @@ -2330,7 +2331,7 @@ dict_index_copy( field = dict_index_get_nth_field(index2, i); dict_index_add_col(index1, index2->table, field->col, - field->prefix_len); + field->prefix_len, field->descending); } } @@ -2644,17 +2645,12 @@ dict_index_build_internal_non_clust( index entry uniquely */ for (i = 0; i < clust_index->n_uniq; i++) { - field = dict_index_get_nth_field(clust_index, i); - if (!indexed[field->col->ind]) { - dict_index_add_col(new_index, table, field->col, - field->prefix_len); - } else if (dict_index_is_spatial(index)) { - /*For spatial index, we still need to add the - field to index. */ + if (!indexed[field->col->ind] || index->is_spatial()) { dict_index_add_col(new_index, table, field->col, - field->prefix_len); + field->prefix_len, + field->descending); } } @@ -4724,7 +4720,7 @@ dict_foreign_qualify_index( return(false); } - if (index->type & (DICT_SPATIAL | DICT_FTS | DICT_CORRUPT)) { + if (!index->is_btree()) { return false; } diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 20ab06c8461..0b73e8452a6 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -1467,6 +1467,7 @@ dict_load_field_low( ulint len; unsigned pos_and_prefix_len; unsigned prefix_len; + bool descending; bool first_field; ulint position; @@ -1522,10 +1523,12 @@ err_len: } if (first_field || pos_and_prefix_len > 0xFFFFUL) { - prefix_len = pos_and_prefix_len & 0xFFFFUL; + prefix_len = pos_and_prefix_len & 0x7FFFUL; + descending = (pos_and_prefix_len & 0x8000UL); position = (pos_and_prefix_len & 0xFFFF0000UL) >> 16; } else { prefix_len = 0; + descending = false; position = pos_and_prefix_len & 0xFFFFUL; } @@ -1549,7 +1552,7 @@ err_len: if (index) { dict_mem_index_add_field( index, mem_heap_strdupl(heap, (const char*) field, len), - prefix_len); + prefix_len, descending); } else { ut_a(sys_field); ut_a(pos); @@ -1557,6 +1560,7 @@ err_len: sys_field->name = mem_heap_strdupl( heap, (const char*) field, len); sys_field->prefix_len = prefix_len & ((1U << 12) - 1); + sys_field->descending = descending; *pos = position; } @@ -2966,13 +2970,12 @@ loop: following call does the comparison in the latin1_swedish_ci charset-collation, in a case-insensitive way. */ - if (0 != cmp_data_data(dfield_get_type(dfield)->mtype, - dfield_get_type(dfield)->prtype, - static_cast<const byte*>( - dfield_get_data(dfield)), - dfield_get_len(dfield), - field, len)) { - + if (cmp_data(dfield_get_type(dfield)->mtype, + dfield_get_type(dfield)->prtype, + false, + static_cast<const byte*>(dfield_get_data(dfield)), + dfield_get_len(dfield), + field, len)) { goto load_next_index; } diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 1a8d5764d0f..10e225941a1 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -1046,32 +1046,6 @@ dict_mem_table_free_foreign_vcol_set( } /**********************************************************************//** -Adds a field definition to an index. NOTE: does not take a copy -of the column name if the field is a column. The memory occupied -by the column name may be released only after publishing the index. */ -void -dict_mem_index_add_field( -/*=====================*/ - dict_index_t* index, /*!< in: index */ - const char* name, /*!< in: column name */ - ulint prefix_len) /*!< in: 0 or the column prefix length - in a MySQL index like - INDEX (textcol(25)) */ -{ - dict_field_t* field; - - ut_ad(index); - ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); - - index->n_def++; - - field = dict_index_get_nth_field(index, unsigned(index->n_def) - 1); - - field->name = name; - field->prefix_len = prefix_len & ((1U << 12) - 1); -} - -/**********************************************************************//** Frees an index memory object. */ void dict_mem_index_free( diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index fd744e6e2f5..b406028aa87 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -1123,7 +1123,7 @@ btr_estimate_number_of_different_key_vals(dict_index_t* index, std::vector<index_field_stats_t> result; - ut_ad(!index->is_spatial()); + ut_ad(index->is_btree()); n_cols = dict_index_get_n_unique(index); @@ -1515,7 +1515,7 @@ dict_stats_update_transient( ut_ad(!dict_index_is_ibuf(index)); - if (index->type & (DICT_FTS | DICT_SPATIAL)) { + if (!index->is_btree()) { continue; } @@ -2510,8 +2510,7 @@ static index_stats_t dict_stats_analyze_index(dict_index_t* index) ut_ad(!index->table->stats_mutex_is_owner()); ut_ad(index->table->get_ref_count()); - /* Disable update statistic for Rtree */ - if (dict_index_is_spatial(index)) { + if (!index->is_btree()) { DBUG_RETURN(result); } @@ -2869,9 +2868,7 @@ dict_stats_update_persistent( index != NULL; index = dict_table_get_next_index(index)) { - ut_ad(!dict_index_is_ibuf(index)); - - if (index->type & (DICT_FTS | DICT_SPATIAL)) { + if (!index->is_btree()) { continue; } diff --git a/storage/innobase/eval/eval0eval.cc b/storage/innobase/eval/eval0eval.cc index 73ab113cff5..bafb0b55d09 100644 --- a/storage/innobase/eval/eval0eval.cc +++ b/storage/innobase/eval/eval0eval.cc @@ -144,8 +144,8 @@ eval_cmp_like( switch (op) { case IB_LIKE_PREFIX: arg4 = que_node_get_next(arg3); - return(!cmp_dfield_dfield_like_prefix(que_node_get_val(arg1), - que_node_get_val(arg4))); + return(cmp_dfield_dfield_eq_prefix(que_node_get_val(arg1), + que_node_get_val(arg4))); case IB_LIKE_EXACT: return(!cmp_dfield_dfield(que_node_get_val(arg1), que_node_get_val(arg2))); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index f79e58523f6..56250ea5486 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -5406,6 +5406,11 @@ innobase_match_index_columns( col_type= DATA_FIXBINARY; } + if (innodb_idx_fld->descending + != !!(key_part->key_part_flag & HA_REVERSE_SORT)) { + DBUG_RETURN(FALSE); + } + if (col_type != mtype) { /* If the col_type we get from mysql type is a geometry data type, we should check if mtype is a legacy type @@ -10830,7 +10835,9 @@ create_index( } dict_mem_index_add_field(index, field->field_name.str, - 0); + 0, + key->key_part->key_part_flag + & HA_REVERSE_SORT); } DBUG_RETURN(convert_error_code_to_mysql( @@ -10924,7 +10931,9 @@ create_index( index->type |= DICT_VIRTUAL; } - dict_mem_index_add_field(index, field_name, prefix_len); + dict_mem_index_add_field(index, field_name, prefix_len, + key_part->key_part_flag + & HA_REVERSE_SORT); } ut_ad(key->flags & HA_FULLTEXT || !(index->type & DICT_FTS)); @@ -11484,6 +11493,8 @@ bool create_table_info_t::innobase_table_flags() /* Do a pre-check on FTS DOC ID index */ if (!(key->flags & HA_NOSAME) + || key->user_defined_key_parts != 1 + || (key->key_part[0].key_part_flag & HA_REVERSE_SORT) || strcmp(key->name.str, FTS_DOC_ID_INDEX_NAME) || strcmp(key->key_part[0].field->field_name.str, FTS_DOC_ID_COL_NAME)) { diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 075239306b0..c92fb447c3d 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -3600,6 +3600,8 @@ innobase_create_index_field_def( index_field->col_no = key_part->fieldnr - num_v; } + index_field->descending= !!(key_part->key_part_flag & HA_REVERSE_SORT); + if (DATA_LARGE_MTYPE(col_type) || (key_part->length < field->pack_length() && field->type() != MYSQL_TYPE_VARCHAR) @@ -3701,6 +3703,7 @@ innobase_create_index_def( index->fields[0].col_no = key->key_part[0].fieldnr - num_v; index->fields[0].prefix_len = 0; index->fields[0].is_v_col = false; + index->fields[0].descending = false; /* Currently, the spatial index cannot be created on virtual columns. It is blocked in the SQL layer. */ @@ -3756,6 +3759,8 @@ innobase_fts_check_doc_id_index( if ((key.flags & HA_NOSAME) && key.user_defined_key_parts == 1 + && !(key.key_part[0].key_part_flag + & HA_REVERSE_SORT) && !strcmp(key.name.str, FTS_DOC_ID_INDEX_NAME) && !strcmp(key.key_part[0].field->field_name.str, FTS_DOC_ID_COL_NAME)) { @@ -3785,7 +3790,7 @@ innobase_fts_check_doc_id_index( } if (!dict_index_is_unique(index) - || dict_index_get_n_unique(index) > 1 + || dict_index_get_n_unique(index) != 1 || strcmp(index->name, FTS_DOC_ID_INDEX_NAME)) { return(FTS_INCORRECT_DOC_ID_INDEX); } @@ -3796,6 +3801,7 @@ innobase_fts_check_doc_id_index( /* The column would be of a BIGINT data type */ if (strcmp(field->name, FTS_DOC_ID_COL_NAME) == 0 + && !field->descending && field->col->mtype == DATA_INT && field->col->len == 8 && field->col->prtype & DATA_NOT_NULL @@ -3837,6 +3843,7 @@ innobase_fts_check_doc_id_index_in_def( named as "FTS_DOC_ID_INDEX" and on column "FTS_DOC_ID" */ if (!(key->flags & HA_NOSAME) || key->user_defined_key_parts != 1 + || (key->key_part[0].key_part_flag & HA_REVERSE_SORT) || strcmp(key->name.str, FTS_DOC_ID_INDEX_NAME) || strcmp(key->key_part[0].field->field_name.str, FTS_DOC_ID_COL_NAME)) { @@ -4034,6 +4041,7 @@ created_clustered: index->n_fields = 1; index->fields->col_no = fts_doc_id_col; index->fields->prefix_len = 0; + index->fields->descending = false; index->fields->is_v_col = false; index->ind_type = DICT_UNIQUE; ut_ad(!rebuild @@ -4643,7 +4651,8 @@ columns are removed from the PK; (3) Changing the order of existing PK columns; (4) Decreasing the prefix length just like removing existing PK columns follows rule(1), Increasing the prefix length just like adding existing -PK columns follows rule(2). +PK columns follows rule(2); +(5) Changing the ASC/DESC attribute of the existing PK columns. @param[in] col_map mapping of old column numbers to new ones @param[in] ha_alter_info Data used during in-place alter @param[in] old_clust_index index to be compared @@ -4736,10 +4745,16 @@ innobase_pk_order_preserved( continue; } + const dict_field_t &of = old_clust_index->fields[old_field]; + const dict_field_t &nf = new_clust_index->fields[new_field]; + + if (of.descending != nf.descending) { + return false; + } + /* Check prefix length change. */ const lint prefix_change = innobase_pk_col_prefix_compare( - new_clust_index->fields[new_field].prefix_len, - old_clust_index->fields[old_field].prefix_len); + nf.prefix_len, of.prefix_len); if (prefix_change < 0) { /* If a column's prefix length is decreased, it should @@ -8923,7 +8938,7 @@ innobase_rename_column_try( const char* to) { dberr_t error; - bool clust_has_prefixes = false; + bool clust_has_wide_format = false; DBUG_ENTER("innobase_rename_column_try"); @@ -8944,10 +8959,11 @@ innobase_rename_column_try( index != NULL; index = dict_table_get_next_index(index)) { - bool has_prefixes = false; + bool wide_format = false; for (size_t i = 0; i < dict_index_get_n_fields(index); i++) { - if (dict_index_get_nth_field(index, i)->prefix_len) { - has_prefixes = true; + dict_field_t* field= dict_index_get_nth_field(index, i); + if (field->prefix_len || field->descending) { + wide_format = true; break; } } @@ -8962,8 +8978,10 @@ innobase_rename_column_try( } pars_info_t* info = pars_info_create(); - ulint pos = has_prefixes ? i << 16 | f.prefix_len : i; - + ulint pos = wide_format + ? i << 16 | f.prefix_len + | !!f.descending << 15 + : i; pars_info_add_ull_literal(info, "indexid", index->id); pars_info_add_int4_literal(info, "nth", pos); pars_info_add_str_literal(info, "new", to); @@ -8983,15 +9001,16 @@ innobase_rename_column_try( goto err_exit; } - if (!has_prefixes || !clust_has_prefixes - || f.prefix_len) { + if (!wide_format || !clust_has_wide_format + || f.prefix_len || f.descending) { continue; } /* For secondary indexes, the - has_prefixes check can be 'polluted' - by PRIMARY KEY column prefix. Try also - the simpler encoding of SYS_FIELDS.POS. */ + wide_format check can be 'polluted' + by PRIMARY KEY column prefix or descending + field. Try also the simpler encoding + of SYS_FIELDS.POS. */ info = pars_info_create(); pars_info_add_ull_literal(info, "indexid", index->id); @@ -9013,7 +9032,7 @@ innobase_rename_column_try( } if (index == dict_table_get_first_index(ctx.old_table)) { - clust_has_prefixes = has_prefixes; + clust_has_wide_format = wide_format; } } diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index f41e3f659fc..413d0ccef90 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -1657,6 +1657,7 @@ ibuf_entry_build( dfield_copy(field, entry_field); ifield = dict_index_get_nth_field(index, i); + ut_ad(!ifield->descending); /* Prefix index columns of fixed-length columns are of fixed length. However, in the function call below, dfield_get_type(entry_field) contains the fixed length @@ -3491,7 +3492,7 @@ ibuf_insert( ut_ad(dtuple_check_typed(entry)); ut_ad(page_id.space() != SRV_TMP_SPACE_ID); - + ut_ad(index->is_btree()); ut_a(!dict_index_is_clust(index)); ut_ad(!index->table->is_temporary()); @@ -3825,7 +3826,7 @@ dump: &offsets, heap, mtr, &page_cur); - ut_ad(!cmp_dtuple_rec(entry, rec, offsets)); + ut_ad(!cmp_dtuple_rec(entry, rec, index, offsets)); lock_rec_restore_from_page_infimum(*block, rec, block->page.id()); } else { diff --git a/storage/innobase/include/data0data.h b/storage/innobase/include/data0data.h index fc774b6ee60..c6ba41bbfdb 100644 --- a/storage/innobase/include/data0data.h +++ b/storage/innobase/include/data0data.h @@ -316,16 +316,6 @@ dtuple_get_n_ext( /*=============*/ const dtuple_t* tuple) /*!< in: tuple */ MY_ATTRIBUTE((nonnull)); -/** Compare two data tuples. -@param[in] tuple1 first data tuple -@param[in] tuple2 second data tuple -@return positive, 0, negative if tuple1 is greater, equal, less, than tuple2, -respectively */ -int -dtuple_coll_cmp( - const dtuple_t* tuple1, - const dtuple_t* tuple2) - MY_ATTRIBUTE((warn_unused_result)); /** Fold a prefix given as the number of fields of a tuple. @param[in] tuple index record @param[in] n_fields number of complete fields to fold diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 505fe76b008..7cb254bdc1c 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -1108,16 +1108,16 @@ dict_table_get_nth_col_pos( ulint n, /*!< in: column number */ ulint* prefix_col_pos) /*!< out: col num if prefix */ MY_ATTRIBUTE((nonnull(1), warn_unused_result)); -/*******************************************************************//** -Adds a column to index. */ -void -dict_index_add_col( -/*===============*/ - dict_index_t* index, /*!< in/out: index */ - const dict_table_t* table, /*!< in: table */ - dict_col_t* col, /*!< in: column */ - ulint prefix_len) /*!< in: column prefix length */ - MY_ATTRIBUTE((nonnull)); +/** Add a column to an index. +@param index index +@param table table +@param col column +@param prefix_len column prefix length +@param descending whether to use descending order */ +void dict_index_add_col(dict_index_t *index, const dict_table_t *table, + dict_col_t *col, ulint prefix_len, + bool descending= false) + MY_ATTRIBUTE((nonnull)); /*******************************************************************//** Copies types of fields contained in index to tuple. */ diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 744ef5316ef..54e2a99e4bd 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -396,18 +396,7 @@ dict_mem_index_create( ulint type, /*!< in: DICT_UNIQUE, DICT_CLUSTERED, ... ORed */ ulint n_fields); /*!< in: number of fields */ -/**********************************************************************//** -Adds a field definition to an index. NOTE: does not take a copy -of the column name if the field is a column. The memory occupied -by the column name may be released only after publishing the index. */ -void -dict_mem_index_add_field( -/*=====================*/ - dict_index_t* index, /*!< in: index */ - const char* name, /*!< in: column name */ - ulint prefix_len); /*!< in: 0 or the column prefix length - in a MySQL index like - INDEX (textcol(25)) */ + /**********************************************************************//** Frees an index memory object. */ void @@ -886,9 +875,11 @@ struct dict_field_t{ unsigned fixed_len:10; /*!< 0 or the fixed length of the column if smaller than DICT_ANTELOPE_MAX_INDEX_COL_LEN */ + /** 1=DESC, 0=ASC */ + unsigned descending:1; /** Zero-initialize all fields */ - dict_field_t() : col(NULL), name(NULL), prefix_len(0), fixed_len(0) {} + dict_field_t() { memset((void*) this, 0, sizeof *this); } /** Check whether two index fields are equivalent. @param[in] old the other index field @@ -1415,6 +1406,21 @@ inline void dict_col_t::detach(const dict_index_t &index) reinterpret_cast<dict_v_col_t*>(this)->detach(index); } +/** Add a field definition to an index. +@param index index +@param name pointer to column name +@param prefix_len column prefix length, or 0 +@param descending whether to use descending order */ +inline void dict_mem_index_add_field(dict_index_t *index, const char *name, + ulint prefix_len, bool descending= false) +{ + ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); + dict_field_t &field= index->fields[index->n_def++]; + field.name= name; + field.prefix_len= prefix_len & ((1U << 12) - 1); + field.descending= descending; +} + /** The status of online index creation */ enum online_index_status { /** the index is complete and ready for access */ diff --git a/storage/innobase/include/ibuf0ibuf.ic b/storage/innobase/include/ibuf0ibuf.ic index 2c2620511c7..2d8265d2206 100644 --- a/storage/innobase/include/ibuf0ibuf.ic +++ b/storage/innobase/include/ibuf0ibuf.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -130,12 +131,17 @@ ibuf_should_try( a secondary index when we decide */ { - return(innodb_change_buffering - && ibuf.max_size != 0 - && !dict_index_is_clust(index) - && !dict_index_is_spatial(index) - && index->table->quiesce == QUIESCE_NONE - && (ignore_sec_unique || !dict_index_is_unique(index))); + if (!innodb_change_buffering || !ibuf.max_size || index->is_clust() || + index->is_spatial()) + return false; + if (!ignore_sec_unique && index->is_unique()) + return false; + if (index->table->quiesce != QUIESCE_NONE) + return false; + for (unsigned i= 0; i < index->n_fields; i++) + if (index->fields[i].descending) + return false; + return true; } /******************************************************************//** diff --git a/storage/innobase/include/page0cur.ic b/storage/innobase/include/page0cur.ic index 5ee96dd716d..2b945053f6e 100644 --- a/storage/innobase/include/page0cur.ic +++ b/storage/innobase/include/page0cur.ic @@ -272,6 +272,7 @@ page_cur_tuple_insert( index, rec, *offsets, mtr); } - ut_ad(!rec || !cmp_dtuple_rec(tuple, rec, *offsets)); + ut_ad(!rec || !cmp_dtuple_rec(tuple, rec, index, *offsets)); return(rec); } + diff --git a/storage/innobase/include/page0page.h b/storage/innobase/include/page0page.h index 41e46c2d051..848de6fa4cf 100644 --- a/storage/innobase/include/page0page.h +++ b/storage/innobase/include/page0page.h @@ -1161,7 +1161,7 @@ page_find_rec_with_heap_no( @return the last record, not delete-marked @retval infimum record if all records are delete-marked */ const rec_t* -page_find_rec_max_not_deleted( +page_find_rec_last_not_deleted( const page_t* page); #endif /* !UNIV_INNOCHECKSUM */ diff --git a/storage/innobase/include/rem0cmp.h b/storage/innobase/include/rem0cmp.h index 8d770405fcc..3a30f5a92f3 100644 --- a/storage/innobase/include/rem0cmp.h +++ b/storage/innobase/include/rem0cmp.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2020, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -24,8 +24,7 @@ Comparison services for records Created 7/1/1994 Heikki Tuuri ************************************************************************/ -#ifndef rem0cmp_h -#define rem0cmp_h +#pragma once #include "data0data.h" #include "data0type.h" @@ -43,39 +42,40 @@ cmp_cols_are_equal( ibool check_charsets); /*!< in: whether to check charsets */ /** Compare two data fields. -@param[in] mtype main type -@param[in] prtype precise type -@param[in] data1 data field -@param[in] len1 length of data1 in bytes, or UNIV_SQL_NULL -@param[in] data2 data field -@param[in] len2 length of data2 in bytes, or UNIV_SQL_NULL +@param mtype main type +@param prtype precise type +@param descending whether to use descending order +@param data1 data field +@param len1 length of data1 in bytes, or UNIV_SQL_NULL +@param data2 data field +@param len2 length of data2 in bytes, or UNIV_SQL_NULL @return the comparison result of data1 and data2 @retval 0 if data1 is equal to data2 @retval negative if data1 is less than data2 @retval positive if data1 is greater than data2 */ -int -cmp_data_data( - ulint mtype, - ulint prtype, - const byte* data1, - ulint len1, - const byte* data2, - ulint len2) - MY_ATTRIBUTE((warn_unused_result)); +int cmp_data(ulint mtype, ulint prtype, bool descending, + const byte *data1, size_t len1, const byte *data2, size_t len2) + MY_ATTRIBUTE((warn_unused_result)); /** Compare two data fields. -@param[in] dfield1 data field; must have type field set -@param[in] dfield2 data field +@param dfield1 data field; must have type field set +@param dfield2 data field +@param descending whether to use descending order @return the comparison result of dfield1 and dfield2 @retval 0 if dfield1 is equal to dfield2 @retval negative if dfield1 is less than dfield2 @retval positive if dfield1 is greater than dfield2 */ -UNIV_INLINE -int -cmp_dfield_dfield( -/*==============*/ - const dfield_t* dfield1,/*!< in: data field; must have type field set */ - const dfield_t* dfield2);/*!< in: data field */ +inline int cmp_dfield_dfield(const dfield_t *dfield1, const dfield_t *dfield2, + bool descending= false) +{ + ut_ad(dfield_check_typed(dfield1)); + const dtype_t *type= dfield_get_type(dfield1); + return cmp_data(type->mtype, type->prtype, descending, + static_cast<const byte*>(dfield_get_data(dfield1)), + dfield_get_len(dfield1), + static_cast<const byte*>(dfield_get_data(dfield2)), + dfield_get_len(dfield2)); +} #ifdef UNIV_DEBUG /** Compare a GIS data tuple to a physical record. @@ -103,15 +103,15 @@ inline int cmp_geometry_field(const void *a, const void *b) double x2= mach_double_read(mbr2); if (x1 > x2) return 1; - if (x2 > x1) + if (x1 < x2) return -1; - double y1= mach_double_read(mbr1 + sizeof(double) * SPDIMS); - double y2= mach_double_read(mbr2 + sizeof(double) * SPDIMS); + x1= mach_double_read(mbr1 + sizeof(double) * SPDIMS); + x2= mach_double_read(mbr2 + sizeof(double) * SPDIMS); - if (y1 > y2) + if (x1 > x2) return 1; - if (y2 > y1) + if (x1 < x2) return -1; /* left lower corner (xmin, ymin) overlaps, now right upper corner */ @@ -120,41 +120,39 @@ inline int cmp_geometry_field(const void *a, const void *b) if (x1 > x2) return 1; - if (x2 > x1) + if (x1 < x2) return -1; - y1= mach_double_read(mbr1 + sizeof(double) * 2 + sizeof(double)); - y2= mach_double_read(mbr2 + sizeof(double) * 2 + sizeof(double)); + x1= mach_double_read(mbr1 + sizeof(double) * 2 + sizeof(double)); + x2= mach_double_read(mbr2 + sizeof(double) * 2 + sizeof(double)); - if (y1 > y2) + if (x1 > x2) return 1; - if (y2 > y1) + if (x1 < x2) return -1; return 0; } /** Compare a data tuple to a physical record. -@param[in] dtuple data tuple -@param[in] rec B-tree record -@param[in] offsets rec_get_offsets(rec) -@param[in] n_cmp number of fields to compare -@param[in,out] matched_fields number of completely matched fields +@param dtuple data tuple +@param rec B-tree index record +@param index B-tree index +@param offsets rec_get_offsets(rec,index) +@param n_cmp number of fields to compare +@param matched_fields number of completely matched fields @return the comparison result of dtuple and rec @retval 0 if dtuple is equal to rec @retval negative if dtuple is less than rec @retval positive if dtuple is greater than rec */ -int -cmp_dtuple_rec_with_match_low( - const dtuple_t* dtuple, - const rec_t* rec, - const rec_offs* offsets, - ulint n_cmp, - ulint* matched_fields) - MY_ATTRIBUTE((nonnull)); -#define cmp_dtuple_rec_with_match(tuple,rec,offsets,fields) \ +int cmp_dtuple_rec_with_match_low(const dtuple_t *dtuple, const rec_t *rec, + const dict_index_t *index, + const rec_offs *offsets, + ulint n_cmp, ulint *matched_fields) + MY_ATTRIBUTE((nonnull)); +#define cmp_dtuple_rec_with_match(tuple,rec,index,offsets,fields) \ cmp_dtuple_rec_with_match_low( \ - tuple,rec,offsets,dtuple_get_n_fields_cmp(tuple),fields) + tuple,rec,index,offsets,dtuple_get_n_fields_cmp(tuple),fields) /** Compare a data tuple to a physical record. @param[in] dtuple data tuple @param[in] rec B-tree or R-tree index record @@ -178,28 +176,32 @@ cmp_dtuple_rec_with_match_bytes( MY_ATTRIBUTE((warn_unused_result)); /** Compare a data tuple to a physical record. @see cmp_dtuple_rec_with_match -@param[in] dtuple data tuple -@param[in] rec B-tree record -@param[in] offsets rec_get_offsets(rec) +@param dtuple data tuple +@param rec index record +@param index index +@param offsets rec_get_offsets(rec, index) @return the comparison result of dtuple and rec @retval 0 if dtuple is equal to rec @retval negative if dtuple is less than rec @retval positive if dtuple is greater than rec */ -int -cmp_dtuple_rec( - const dtuple_t* dtuple, - const rec_t* rec, - const rec_offs* offsets); -/**************************************************************//** -Checks if a dtuple is a prefix of a record. The last field in dtuple -is allowed to be a prefix of the corresponding field in the record. -@return TRUE if prefix */ -ibool -cmp_dtuple_is_prefix_of_rec( -/*========================*/ - const dtuple_t* dtuple, /*!< in: data tuple */ - const rec_t* rec, /*!< in: physical record */ - const rec_offs* offsets);/*!< in: array returned by rec_get_offsets() */ +inline int cmp_dtuple_rec(const dtuple_t *dtuple, const rec_t *rec, + const dict_index_t *index, const rec_offs *offsets) +{ + ulint matched= 0; + return cmp_dtuple_rec_with_match(dtuple, rec, index, offsets, &matched); +} + +/** Check if a dtuple is a prefix of a record. +@param dtuple data tuple +@param rec index record +@param index index +@param offsets rec_get_offsets(rec) +@return whether dtuple is a prefix of rec */ +bool cmp_dtuple_is_prefix_of_rec(const dtuple_t *dtuple, const rec_t *rec, + const dict_index_t *index, + const rec_offs *offsets) + MY_ATTRIBUTE((nonnull, warn_unused_result)); + /** Compare two physical records that contain the same number of columns, none of which are stored externally. @retval positive if rec1 (including non-ordering columns) is greater than rec2 @@ -246,18 +248,39 @@ cmp_rec_rec( MY_ATTRIBUTE((nonnull(1,2,3,4,5))); /** Compare two data fields. -@param[in] dfield1 data field -@param[in] dfield2 data field +@param dfield1 data field +@param dfield2 data field @return the comparison result of dfield1 and dfield2 -@retval 0 if dfield1 is equal to dfield2, or a prefix of dfield1 -@retval negative if dfield1 is less than dfield2 -@retval positive if dfield1 is greater than dfield2 */ -UNIV_INLINE -int -cmp_dfield_dfield_like_prefix( - const dfield_t* dfield1, - const dfield_t* dfield2); +@retval true if dfield1 is equal to dfield2, or a prefix of dfield1 +@retval false otherwise */ +inline bool cmp_dfield_dfield_eq_prefix(const dfield_t *dfield1, + const dfield_t *dfield2) +{ + ut_ad(dfield_check_typed(dfield1)); + ut_ad(dfield_check_typed(dfield2)); + const dtype_t *type= dfield_get_type(dfield1); -#include "rem0cmp.ic" +#ifdef UNIV_DEBUG + switch (type->prtype & DATA_MYSQL_TYPE_MASK) { + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_VARCHAR: + break; + default: + ut_error; + } +#endif /* UNIV_DEBUG */ -#endif + uint cs_num= dtype_get_charset_coll(type->prtype); + CHARSET_INFO *cs= get_charset(cs_num, MYF(MY_WME)); + ut_a(cs); + return !cs->strnncoll(static_cast<const uchar*>(dfield_get_data(dfield1)), + dfield_get_len(dfield1), + static_cast<const uchar*>(dfield_get_data(dfield2)), + dfield_get_len(dfield2), 1); +} diff --git a/storage/innobase/include/rem0cmp.ic b/storage/innobase/include/rem0cmp.ic deleted file mode 100644 index 6e21382d187..00000000000 --- a/storage/innobase/include/rem0cmp.ic +++ /dev/null @@ -1,107 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2020, MariaDB Corporation. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -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 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 Street, Fifth Floor, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/*******************************************************************//** -@file include/rem0cmp.ic -Comparison services for records - -Created 7/1/1994 Heikki Tuuri -************************************************************************/ - -#include <mysql_com.h> -#include <my_sys.h> - -/** Compare two data fields. -@param[in] dfield1 data field; must have type field set -@param[in] dfield2 data field -@return the comparison result of dfield1 and dfield2 -@retval 0 if dfield1 is equal to dfield2 -@retval negative if dfield1 is less than dfield2 -@retval positive if dfield1 is greater than dfield2 */ -UNIV_INLINE -int -cmp_dfield_dfield( - const dfield_t* dfield1, - const dfield_t* dfield2) -{ - const dtype_t* type; - - ut_ad(dfield_check_typed(dfield1)); - - type = dfield_get_type(dfield1); - - return(cmp_data_data(type->mtype, type->prtype, - (const byte*) dfield_get_data(dfield1), - dfield_get_len(dfield1), - (const byte*) dfield_get_data(dfield2), - dfield_get_len(dfield2))); -} - -/** Compare two data fields. -@param[in] dfield1 data field -@param[in] dfield2 data field -@return the comparison result of dfield1 and dfield2 -@retval 0 if dfield1 is equal to dfield2, or a prefix of dfield1 -@retval negative if dfield1 is less than dfield2 -@retval positive if dfield1 is greater than dfield2 */ -UNIV_INLINE -int -cmp_dfield_dfield_like_prefix( - const dfield_t* dfield1, - const dfield_t* dfield2) -{ - const dtype_t* type; - - ut_ad(dfield_check_typed(dfield1)); - ut_ad(dfield_check_typed(dfield2)); - - type = dfield_get_type(dfield1); - -#ifdef UNIV_DEBUG - switch (type->prtype & DATA_MYSQL_TYPE_MASK) { - case MYSQL_TYPE_BIT: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_VARCHAR: - break; - default: - ut_error; - } -#endif /* UNIV_DEBUG */ - - uint cs_num = (uint) dtype_get_charset_coll(type->prtype); - - if (CHARSET_INFO* cs = get_charset(cs_num, MYF(MY_WME))) { - return(cs->strnncoll( - static_cast<const uchar*>( - dfield_get_data(dfield1)), - dfield_get_len(dfield1), - static_cast<const uchar*>( - dfield_get_data(dfield2)), - dfield_get_len(dfield2), - 1)); - } - - ib::fatal() << "Unable to find charset-collation " << cs_num; - return(0); -} diff --git a/storage/innobase/include/row0merge.h b/storage/innobase/include/row0merge.h index 64d97e6a777..580dbc65c00 100644 --- a/storage/innobase/include/row0merge.h +++ b/storage/innobase/include/row0merge.h @@ -109,6 +109,7 @@ struct index_field_t { ulint prefix_len; /*!< column prefix length, or 0 if indexing the whole column */ bool is_v_col; /*!< whether this is a virtual column */ + bool descending; /*!< whether to use DESC order */ }; /** Definition of an index being created */ diff --git a/storage/innobase/page/page0cur.cc b/storage/innobase/page/page0cur.cc index 41ae2638f47..b43f6e0b900 100644 --- a/storage/innobase/page/page0cur.cc +++ b/storage/innobase/page/page0cur.cc @@ -84,7 +84,8 @@ page_cur_try_search_shortcut( low_match = up_match = std::min(*ilow_matched_fields, *iup_matched_fields); - if (cmp_dtuple_rec_with_match(tuple, rec, offsets, &low_match) < 0) { + if (cmp_dtuple_rec_with_match(tuple, rec, index, offsets, + &low_match) < 0) { goto exit_func; } @@ -94,7 +95,7 @@ page_cur_try_search_shortcut( index->n_core_fields, dtuple_get_n_fields(tuple), &heap); - if (cmp_dtuple_rec_with_match(tuple, next_rec, offsets, + if (cmp_dtuple_rec_with_match(tuple, next_rec, index, offsets, &up_match) >= 0) { goto exit_func; } @@ -398,7 +399,7 @@ page_cur_search_with_match( dtuple_get_n_fields_cmp(tuple), &heap); cmp = cmp_dtuple_rec_with_match( - tuple, mid_rec, offsets, &cur_matched_fields); + tuple, mid_rec, index, offsets, &cur_matched_fields); if (cmp > 0) { low_slot_match: @@ -452,7 +453,7 @@ up_slot_match: dtuple_get_n_fields_cmp(tuple), &heap); cmp = cmp_dtuple_rec_with_match( - tuple, mid_rec, offsets, &cur_matched_fields); + tuple, mid_rec, index, offsets, &cur_matched_fields); if (cmp > 0) { low_rec_match: diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc index 2f85ef94233..3985f57a586 100644 --- a/storage/innobase/page/page0page.cc +++ b/storage/innobase/page/page0page.cc @@ -2466,7 +2466,7 @@ page_find_rec_with_heap_no( @return the last record, not delete-marked @retval infimum record if all records are delete-marked */ const rec_t* -page_find_rec_max_not_deleted( +page_find_rec_last_not_deleted( const page_t* page) { const rec_t* rec = page_get_infimum_rec(page); diff --git a/storage/innobase/pars/pars0opt.cc b/storage/innobase/pars/pars0opt.cc index e1a913b0179..c9b176150f6 100644 --- a/storage/innobase/pars/pars0opt.cc +++ b/storage/innobase/pars/pars0opt.cc @@ -356,6 +356,11 @@ opt_calc_index_goodness( n_fields = dict_index_get_n_unique_in_tree(index); for (j = 0; j < n_fields; j++) { + if (UNIV_UNLIKELY(index->fields[j].descending)) { + /* The internal InnoDB SQL parser does not + work with indexes that use DESC order. */ + return 0; + } col_no = dict_index_get_nth_col_no(index, j); @@ -575,8 +580,7 @@ opt_search_plan_for_table( best_index = index; /* Eliminate compiler warning */ best_goodness = 0; - /* should be do ... until ? comment by Jani */ - while (index) { + do { goodness = opt_calc_index_goodness(index, sel_node, i, index_plan, &last_op); if (goodness > best_goodness) { @@ -591,7 +595,7 @@ opt_search_plan_for_table( } dict_table_next_uncorrupted_index(index); - } + } while (index); plan->index = best_index; diff --git a/storage/innobase/rem/rem0cmp.cc b/storage/innobase/rem/rem0cmp.cc index 70c0255d46a..6b39f6cac56 100644 --- a/storage/innobase/rem/rem0cmp.cc +++ b/storage/innobase/rem/rem0cmp.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2020, MariaDB Corporation. +Copyright (c) 2020, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -213,37 +213,35 @@ cmp_decimal(const byte* a, ulint a_length, const byte* b, ulint b_length) } /** Compare two data fields. -@param[in] mtype main type -@param[in] prtype precise type -@param[in] data1 data field -@param[in] len1 length of data1 in bytes, or UNIV_SQL_NULL -@param[in] data2 data field -@param[in] len2 length of data2 in bytes, or UNIV_SQL_NULL +@param mtype main type +@param prtype precise type +@param descending whether to use descending order +@param data1 data field +@param len1 length of data1 in bytes, or UNIV_SQL_NULL +@param data2 data field +@param len2 length of data2 in bytes, or UNIV_SQL_NULL @return the comparison result of data1 and data2 @retval 0 if data1 is equal to data2 @retval negative if data1 is less than data2 @retval positive if data1 is greater than data2 */ -inline -int -cmp_data( - ulint mtype, - ulint prtype, - const byte* data1, - ulint len1, - const byte* data2, - ulint len2) +int cmp_data(ulint mtype, ulint prtype, bool descending, + const byte *data1, size_t len1, const byte *data2, size_t len2) { ut_ad(len1 != UNIV_SQL_DEFAULT); ut_ad(len2 != UNIV_SQL_DEFAULT); + int cmp; + if (len1 == UNIV_SQL_NULL || len2 == UNIV_SQL_NULL) { if (len1 == len2) { - return(0); + return 0; + } else { + /* We define the SQL null to be the smallest possible + value of a field. */ + cmp = len1 == UNIV_SQL_NULL ? -1 : 1; } - - /* We define the SQL null to be the smallest possible - value of a field. */ - return(len1 == UNIV_SQL_NULL ? -1 : 1); +func_exit: + return UNIV_UNLIKELY(descending) ? -cmp : cmp; } ulint pad; @@ -269,7 +267,8 @@ cmp_data( if (prtype & DATA_GIS_MBR) { ut_ad(len1 == DATA_MBR_LEN); ut_ad(len2 == DATA_MBR_LEN); - return cmp_geometry_field(data1, data2); + cmp = cmp_geometry_field(data1, data2); + goto func_exit; } pad = ULINT_UNDEFINED; break; @@ -286,43 +285,50 @@ cmp_data( /* fall through */ case DATA_VARMYSQL: case DATA_MYSQL: - return innobase_mysql_cmp(prtype, data1, len1, data2, len2); + cmp = innobase_mysql_cmp(prtype, data1, len1, data2, len2); + goto func_exit; case DATA_VARCHAR: case DATA_CHAR: - return my_charset_latin1.strnncollsp(data1, len1, data2, len2); + cmp = my_charset_latin1.strnncollsp(data1, len1, data2, len2); + goto func_exit; case DATA_DECIMAL: - return cmp_decimal(data1, len1, data2, len2); + cmp = cmp_decimal(data1, len1, data2, len2); + goto func_exit; case DATA_DOUBLE: { double d_1 = mach_double_read(data1); double d_2 = mach_double_read(data2); if (d_1 > d_2) { - return 1; + cmp = 1; } else if (d_2 > d_1) { - return -1; + cmp = -1; + } else { + return 0; } } - return 0; + goto func_exit; case DATA_FLOAT: float f_1 = mach_float_read(data1); float f_2 = mach_float_read(data2); if (f_1 > f_2) { - return 1; + cmp = 1; } else if (f_2 > f_1) { - return -1; + cmp = -1; + } else { + return 0; } - return 0; + goto func_exit; } ulint len = std::min(len1, len2); - int cmp = len ? memcmp(data1, data2, len) : 0; + cmp = len ? memcmp(data1, data2, len) : 0; if (cmp) { - return (cmp); + goto func_exit; } data1 += len; @@ -333,7 +339,7 @@ cmp_data( cmp = (int) (len1 - len2); if (!cmp || pad == ULINT_UNDEFINED) { - return(cmp); + goto func_exit; } len = 0; @@ -342,65 +348,46 @@ cmp_data( do { cmp = static_cast<int>( mach_read_from_1(&data1[len++]) - pad); - } while (cmp == 0 && len < len1); + if (cmp) { + goto func_exit; + } + } while (len < len1); } else { ut_ad(len2 > 0); do { cmp = static_cast<int>( pad - mach_read_from_1(&data2[len++])); - } while (cmp == 0 && len < len2); + if (cmp) { + goto func_exit; + } + } while (len < len2); } - return(cmp); -} - -/** Compare two data fields. -@param[in] mtype main type -@param[in] prtype precise type -@param[in] data1 data field -@param[in] len1 length of data1 in bytes, or UNIV_SQL_NULL -@param[in] data2 data field -@param[in] len2 length of data2 in bytes, or UNIV_SQL_NULL -@return the comparison result of data1 and data2 -@retval 0 if data1 is equal to data2 -@retval negative if data1 is less than data2 -@retval positive if data1 is greater than data2 */ -int -cmp_data_data( - ulint mtype, - ulint prtype, - const byte* data1, - ulint len1, - const byte* data2, - ulint len2) -{ - return(cmp_data(mtype, prtype, data1, len1, data2, len2)); + return 0; } /** Compare a data tuple to a physical record. -@param[in] dtuple data tuple -@param[in] rec B-tree record -@param[in] offsets rec_get_offsets(rec) -@param[in] n_cmp number of fields to compare -@param[in,out] matched_fields number of completely matched fields +@param dtuple data tuple +@param rec B-tree index record +@param index B-tree index +@param offsets rec_get_offsets(rec,index) +@param n_cmp number of fields to compare +@param matched_fields number of completely matched fields @return the comparison result of dtuple and rec @retval 0 if dtuple is equal to rec @retval negative if dtuple is less than rec @retval positive if dtuple is greater than rec */ -int -cmp_dtuple_rec_with_match_low( - const dtuple_t* dtuple, - const rec_t* rec, - const rec_offs* offsets, - ulint n_cmp, - ulint* matched_fields) +int cmp_dtuple_rec_with_match_low(const dtuple_t *dtuple, const rec_t *rec, + const dict_index_t *index, + const rec_offs *offsets, + ulint n_cmp, ulint *matched_fields) { ulint cur_field; /* current field number */ - int ret; /* return value */ + int ret = 0; /* return value */ ut_ad(dtuple_check_typed(dtuple)); - ut_ad(rec_offs_validate(rec, NULL, offsets)); + ut_ad(rec_offs_validate(rec, index, offsets)); cur_field = *matched_fields; @@ -414,6 +401,7 @@ cmp_dtuple_rec_with_match_low( rec_offs_comp(offsets)); ulint tup_info = dtuple_get_info_bits(dtuple); + /* The "infimum node pointer" is always first. */ if (UNIV_UNLIKELY(rec_info & REC_INFO_MIN_REC_FLAG)) { ret = !(tup_info & REC_INFO_MIN_REC_FLAG); goto order_resolved; @@ -454,7 +442,8 @@ cmp_dtuple_rec_with_match_low( ut_ad(!dfield_is_ext(dtuple_field)); - ret = cmp_data(type->mtype, type->prtype, + ret = cmp_data(type->mtype, type->prtype, !index->is_ibuf() + && index->fields[cur_field].descending, dtuple_b_ptr, dtuple_f_len, rec_b_ptr, rec_f_len); if (ret) { @@ -462,8 +451,6 @@ cmp_dtuple_rec_with_match_low( } } - ret = 0; /* If we ran out of fields, dtuple was equal to rec - up to the common fields */ order_resolved: *matched_fields = cur_field; return(ret); @@ -534,6 +521,7 @@ cmp_dtuple_rec_with_match_bytes( ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!(REC_INFO_MIN_REC_FLAG & dtuple_get_info_bits(dtuple))); + ut_ad(!index->is_ibuf()); if (UNIV_UNLIKELY(REC_INFO_MIN_REC_FLAG & rec_get_info_bits(rec, rec_offs_comp(offsets)))) { @@ -546,7 +534,7 @@ cmp_dtuple_rec_with_match_bytes( ulint cur_field = *matched_fields; ulint cur_bytes = *matched_bytes; ulint n_cmp = dtuple_get_n_fields_cmp(dtuple); - int ret; + int ret = 0; ut_ad(n_cmp <= dtuple_get_n_fields(dtuple)); ut_ad(cur_field <= n_cmp); @@ -607,7 +595,7 @@ cmp_dtuple_rec_with_match_bytes( } /* fall through */ default: - ret = cmp_data(type->mtype, type->prtype, + ret = cmp_data(type->mtype, type->prtype, false, dtuple_b_ptr, dtuple_f_len, rec_b_ptr, rec_f_len); @@ -671,62 +659,29 @@ next_field: ut_ad(cur_bytes == 0); - ret = 0; /* If we ran out of fields, dtuple was equal to rec - up to the common fields */ order_resolved: *matched_fields = cur_field; *matched_bytes = cur_bytes; - return(ret); + return !ret || UNIV_LIKELY(!index->fields[cur_field].descending) + ? ret : -ret; } -/** Compare a data tuple to a physical record. -@see cmp_dtuple_rec_with_match -@param[in] dtuple data tuple -@param[in] rec B-tree record -@param[in] offsets rec_get_offsets(rec); may be NULL -for ROW_FORMAT=REDUNDANT -@return the comparison result of dtuple and rec -@retval 0 if dtuple is equal to rec -@retval negative if dtuple is less than rec -@retval positive if dtuple is greater than rec */ -int -cmp_dtuple_rec( - const dtuple_t* dtuple, - const rec_t* rec, - const rec_offs* offsets) +/** Check if a dtuple is a prefix of a record. +@param dtuple data tuple +@param rec index record +@param index index +@param offsets rec_get_offsets(rec) +@return whether dtuple is a prefix of rec */ +bool cmp_dtuple_is_prefix_of_rec(const dtuple_t *dtuple, const rec_t *rec, + const dict_index_t *index, + const rec_offs *offsets) { - ulint matched_fields = 0; - - ut_ad(rec_offs_validate(rec, NULL, offsets)); - return(cmp_dtuple_rec_with_match(dtuple, rec, offsets, - &matched_fields)); -} - -/**************************************************************//** -Checks if a dtuple is a prefix of a record. The last field in dtuple -is allowed to be a prefix of the corresponding field in the record. -@return TRUE if prefix */ -ibool -cmp_dtuple_is_prefix_of_rec( -/*========================*/ - const dtuple_t* dtuple, /*!< in: data tuple */ - const rec_t* rec, /*!< in: physical record */ - const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ -{ - ulint n_fields; - ulint matched_fields = 0; - - ut_ad(rec_offs_validate(rec, NULL, offsets)); - n_fields = dtuple_get_n_fields(dtuple); - - if (n_fields > rec_offs_n_fields(offsets)) { - ut_ad(0); - return(FALSE); - } - - cmp_dtuple_rec_with_match(dtuple, rec, offsets, &matched_fields); - return(matched_fields == n_fields); + ulint matched_fields= 0; + ulint n_fields= dtuple_get_n_fields(dtuple); + ut_ad(n_fields <= rec_offs_n_fields(offsets)); + cmp_dtuple_rec_with_match(dtuple, rec, index, offsets, &matched_fields); + return matched_fields == n_fields; } /*************************************************************//** @@ -749,7 +704,7 @@ cmp_rec_rec_simple_field( const byte* rec2_b_ptr; ulint rec1_f_len; ulint rec2_f_len; - const dict_col_t* col = dict_index_get_nth_col(index, n); + const dict_field_t* field = dict_index_get_nth_field(index, n); ut_ad(!rec_offs_nth_extern(offsets1, n)); ut_ad(!rec_offs_nth_extern(offsets2, n)); @@ -757,8 +712,9 @@ cmp_rec_rec_simple_field( rec1_b_ptr = rec_get_nth_field(rec1, offsets1, n, &rec1_f_len); rec2_b_ptr = rec_get_nth_field(rec2, offsets2, n, &rec2_f_len); - return(cmp_data(col->mtype, col->prtype, - rec1_b_ptr, rec1_f_len, rec2_b_ptr, rec2_f_len)); + return cmp_data(field->col->mtype, field->col->prtype, + field->descending, + rec1_b_ptr, rec1_f_len, rec2_b_ptr, rec2_f_len); } /** Compare two physical records that contain the same number of columns, @@ -916,16 +872,19 @@ cmp_rec_rec( for (; cur_field < n_fields; cur_field++) { ulint mtype; ulint prtype; + bool descending; if (UNIV_UNLIKELY(dict_index_is_ibuf(index))) { /* This is for the insert buffer B-tree. */ mtype = DATA_BINARY; prtype = 0; + descending = false; } else { - const dict_col_t* col = dict_index_get_nth_col( + const dict_field_t* field = dict_index_get_nth_field( index, cur_field); - mtype = col->mtype; - prtype = col->prtype; + descending = field->descending; + mtype = field->col->mtype; + prtype = field->col->prtype; if (UNIV_LIKELY(!dict_index_is_spatial(index))) { } else if (cur_field == 0) { @@ -961,9 +920,8 @@ cmp_rec_rec( goto order_resolved; } - ret = cmp_data(mtype, prtype, - rec1_b_ptr, rec1_f_len, - rec2_b_ptr, rec2_f_len); + ret = cmp_data(mtype, prtype, descending, + rec1_b_ptr, rec1_f_len, rec2_b_ptr, rec2_f_len); if (ret) { goto order_resolved; } @@ -978,28 +936,3 @@ order_resolved: } return ret; } - -#ifdef UNIV_COMPILE_TEST_FUNCS - -#ifdef HAVE_UT_CHRONO_T - -void -test_cmp_data_data(ulint len) -{ - int i; - static byte zeros[64]; - - if (len > sizeof zeros) { - len = sizeof zeros; - } - - ut_chrono_t ch(__func__); - - for (i = 1000000; i > 0; i--) { - i += cmp_data(DATA_INT, 0, zeros, len, zeros, len); - } -} - -#endif /* HAVE_UT_CHRONO_T */ - -#endif /* UNIV_COMPILE_TEST_FUNCS */ diff --git a/storage/innobase/row/row0ftsort.cc b/storage/innobase/row/row0ftsort.cc index cc3f19bafc8..5a172fe5da7 100644 --- a/storage/innobase/row/row0ftsort.cc +++ b/storage/innobase/row/row0ftsort.cc @@ -96,6 +96,7 @@ row_merge_create_fts_sort_index( field = dict_index_get_nth_field(new_index, 0); field->name = NULL; field->prefix_len = 0; + field->descending = false; field->col = static_cast<dict_col_t*>( mem_heap_zalloc(new_index->heap, sizeof(dict_col_t))); field->col->prtype = idx_field->col->prtype | DATA_NOT_NULL; @@ -112,6 +113,7 @@ row_merge_create_fts_sort_index( field = dict_index_get_nth_field(new_index, 1); field->name = NULL; field->prefix_len = 0; + field->descending = false; field->col = static_cast<dict_col_t*>( mem_heap_zalloc(new_index->heap, sizeof(dict_col_t))); field->col->mtype = DATA_INT; @@ -151,6 +153,7 @@ row_merge_create_fts_sort_index( field = dict_index_get_nth_field(new_index, 2); field->name = NULL; field->prefix_len = 0; + field->descending = false; field->col = static_cast<dict_col_t*>( mem_heap_zalloc(new_index->heap, sizeof(dict_col_t))); field->col->mtype = DATA_INT; diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index b7688c7bfe4..cf108753180 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1658,7 +1658,7 @@ row_ins_check_foreign_constraint( } } - cmp = cmp_dtuple_rec(entry, rec, offsets); + cmp = cmp_dtuple_rec(entry, rec, check_index, offsets); if (cmp == 0) { if (rec_get_deleted_flag(rec, @@ -1989,7 +1989,7 @@ row_ins_dupl_error_with_rec( matched_fields = 0; - cmp_dtuple_rec_with_match(entry, rec, offsets, &matched_fields); + cmp_dtuple_rec_with_match(entry, rec, index, offsets, &matched_fields); if (matched_fields < n_unique) { @@ -2122,7 +2122,7 @@ row_ins_scan_sec_index_for_duplicate( continue; } - cmp = cmp_dtuple_rec(entry, rec, offsets); + cmp = cmp_dtuple_rec(entry, rec, index, offsets); if (cmp == 0) { if (row_ins_dupl_error_with_rec(rec, entry, @@ -2159,18 +2159,20 @@ end_scan: } /** Checks for a duplicate when the table is being rebuilt online. +@param n_uniq index->db_trx_id() +@param entry entry being inserted +@param rec clustered index record at insert position +@param index clustered index +@param offsets rec_get_offsets(rec) @retval DB_SUCCESS when no duplicate is detected @retval DB_SUCCESS_LOCKED_REC when rec is an exact match of entry or a newer version of entry (the entry should not be inserted) @retval DB_DUPLICATE_KEY when entry is a duplicate of rec */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) dberr_t -row_ins_duplicate_online( -/*=====================*/ - ulint n_uniq, /*!< in: offset of DB_TRX_ID */ - const dtuple_t* entry, /*!< in: entry that is being inserted */ - const rec_t* rec, /*!< in: clustered index record */ - rec_offs* offsets)/*!< in/out: rec_get_offsets(rec) */ +row_ins_duplicate_online(ulint n_uniq, const dtuple_t *entry, + const rec_t *rec, const dict_index_t *index, + rec_offs *offsets) { ulint fields = 0; @@ -2178,11 +2180,11 @@ row_ins_duplicate_online( in the new table. */ ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets))); ut_ad(dtuple_get_n_fields_cmp(entry) == n_uniq); + ut_ad(n_uniq == index->db_trx_id()); - /* Compare the PRIMARY KEY fields and the - DB_TRX_ID, DB_ROLL_PTR. */ - cmp_dtuple_rec_with_match_low( - entry, rec, offsets, n_uniq + 2, &fields); + /* Compare the PRIMARY KEY fields and the DB_TRX_ID, DB_ROLL_PTR. */ + cmp_dtuple_rec_with_match_low(entry, rec, index, offsets, n_uniq + 2, + &fields); if (fields < n_uniq) { /* Not a duplicate. */ @@ -2191,7 +2193,7 @@ row_ins_duplicate_online( ulint trx_id_len; - if (fields == n_uniq + 2 + if (fields == n_uniq && memcmp(rec_get_nth_field(rec, offsets, n_uniq, &trx_id_len), reset_trx_id, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN)) { ut_ad(trx_id_len == DATA_TRX_ID_LEN); @@ -2227,7 +2229,8 @@ row_ins_duplicate_error_in_clust_online( *offsets = rec_get_offsets(rec, cursor->index, *offsets, cursor->index->n_fields, ULINT_UNDEFINED, heap); - err = row_ins_duplicate_online(n_uniq, entry, rec, *offsets); + err = row_ins_duplicate_online(n_uniq, entry, + rec, cursor->index, *offsets); if (err != DB_SUCCESS) { return(err); } @@ -2239,7 +2242,8 @@ row_ins_duplicate_error_in_clust_online( *offsets = rec_get_offsets(rec, cursor->index, *offsets, cursor->index->n_fields, ULINT_UNDEFINED, heap); - err = row_ins_duplicate_online(n_uniq, entry, rec, *offsets); + err = row_ins_duplicate_online(n_uniq, entry, + rec, cursor->index, *offsets); } return(err); diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 7b1e1b5597f..d4ffe9f2b30 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -928,6 +928,7 @@ static MY_ATTRIBUTE((warn_unused_result)) int row_merge_tuple_cmp( /*================*/ + const dict_index_t* index, /*< in: index tree */ ulint n_uniq, /*!< in: number of unique fields */ ulint n_field,/*!< in: number of fields */ const mtuple_t& a, /*!< in: first tuple to be compared */ @@ -939,6 +940,7 @@ row_merge_tuple_cmp( const dfield_t* af = a.fields; const dfield_t* bf = b.fields; ulint n = n_uniq; + const dict_field_t* f = index->fields; ut_ad(n_uniq > 0); ut_ad(n_uniq <= n_field); @@ -947,7 +949,7 @@ row_merge_tuple_cmp( found or we run out of fields to compare. If !cmp at the end, the tuples are equal. */ do { - cmp = cmp_dfield_dfield(af++, bf++); + cmp = cmp_dfield_dfield(af++, bf++, (f++)->descending); } while (!cmp && --n); if (cmp) { @@ -972,7 +974,7 @@ no_report: /* The n_uniq fields were equal, but we compare all fields so that we will get the same (internal) order as in the B-tree. */ for (n = n_field - n_uniq + 1; --n; ) { - cmp = cmp_dfield_dfield(af++, bf++); + cmp = cmp_dfield_dfield(af++, bf++, (f++)->descending); if (cmp) { return(cmp); } @@ -992,7 +994,7 @@ UT_SORT_FUNCTION_BODY(). @param low lower bound of the sorting area, inclusive @param high upper bound of the sorting area, inclusive */ #define row_merge_tuple_sort_ctx(tuples, aux, low, high) \ - row_merge_tuple_sort(n_uniq, n_field, dup, tuples, aux, low, high) + row_merge_tuple_sort(index,n_uniq,n_field,dup, tuples, aux, low, high) /** Wrapper for row_merge_tuple_cmp() to inject some more context to UT_SORT_FUNCTION_BODY(). @param a first tuple to be compared @@ -1000,7 +1002,7 @@ UT_SORT_FUNCTION_BODY(). @return positive, 0, negative, if a is greater, equal, less, than b, respectively */ #define row_merge_tuple_cmp_ctx(a,b) \ - row_merge_tuple_cmp(n_uniq, n_field, a, b, dup) + row_merge_tuple_cmp(index, n_uniq, n_field, a, b, dup) /**********************************************************************//** Merge sort the tuple buffer in main memory. */ @@ -1008,6 +1010,7 @@ static void row_merge_tuple_sort( /*=================*/ + const dict_index_t* index, /*!< in: index tree */ ulint n_uniq, /*!< in: number of unique fields */ ulint n_field,/*!< in: number of fields */ row_merge_dup_t* dup, /*!< in/out: reporter of duplicates @@ -1035,12 +1038,9 @@ row_merge_buf_sort( row_merge_dup_t* dup) /*!< in/out: reporter of duplicates (NULL if non-unique index) */ { - ut_ad(!dict_index_is_spatial(buf->index)); - - row_merge_tuple_sort(dict_index_get_n_unique(buf->index), - dict_index_get_n_fields(buf->index), - dup, - buf->tuples, buf->tmp_tuples, 0, buf->n_tuples); + ut_ad(!buf->index->is_spatial()); + row_merge_tuple_sort(buf->index, buf->index->n_uniq, buf->index->n_fields, + dup, buf->tuples, buf->tmp_tuples, 0, buf->n_tuples); } /** Write the blob field data to temporary file and fill the offset, @@ -1722,11 +1722,10 @@ row_mtuple_cmp( const mtuple_t* current_mtuple, row_merge_dup_t* dup) { - ut_ad(dict_index_is_clust(dup->index)); - const ulint n_unique = dict_index_get_n_unique(dup->index); - - return(row_merge_tuple_cmp( - n_unique, n_unique, *current_mtuple, *prev_mtuple, dup)); + ut_ad(dup->index->is_primary()); + const ulint n_uniq= dup->index->n_uniq; + return row_merge_tuple_cmp(dup->index, n_uniq, n_uniq, + *current_mtuple, *prev_mtuple, dup); } /** Insert cached spatial index rows. @@ -4528,7 +4527,8 @@ row_merge_create_index( name = dict_table_get_col_name(table, ifield->col_no); } - dict_mem_index_add_field(index, name, ifield->prefix_len); + dict_mem_index_add_field(index, name, ifield->prefix_len, + ifield->descending); } if (n_add_vcol) { diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 757ead4f0e9..7594877d80e 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -3086,7 +3086,8 @@ func_exit: if (prev_entry != NULL) { matched_fields = 0; - cmp = cmp_dtuple_rec_with_match(prev_entry, rec, offsets, + cmp = cmp_dtuple_rec_with_match(prev_entry, + rec, index, offsets, &matched_fields); contains_null = FALSE; diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 706a9aecd8f..937a8481b47 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -1306,7 +1306,7 @@ purge_node_t::validate_pcur() part in persistent cursor. Both cases we store n_uniq fields of the cluster index and so it is fine to do the comparison. We note this dependency here as pcur and ref belong to different modules. */ - int st = cmp_dtuple_rec(ref, pcur.old_rec, offsets); + int st = cmp_dtuple_rec(ref, pcur.old_rec, clust_index, offsets); if (st != 0) { ib::error() << "Purge node pcur validation failed"; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index d5fd340ad81..ab4da856d1d 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -149,7 +149,7 @@ row_sel_sec_rec_is_for_blob( return false; } - return(!cmp_data_data(mtype, prtype, buf, len, sec_field, sec_len)); + return !cmp_data(mtype, prtype, false, buf, len, sec_field, sec_len); } /** Function to read the secondary spatial index, calculate @@ -390,9 +390,8 @@ check_for_blob: } } - if (0 != cmp_data_data(col->mtype, col->prtype, - clust_field, len, - sec_field, sec_len)) { + if (cmp_data(col->mtype, col->prtype, false, + clust_field, len, sec_field, sec_len)) { return DB_SUCCESS; } } @@ -3275,7 +3274,7 @@ class Row_sel_get_clust_rec_for_mysql ulint len1, len2; const byte *b1= rec_get_nth_field(cached_clust_rec, offsets, n, &len1); const byte *b2= rec_get_nth_field(cached_old_vers, vers_offs, n, &len2); - ut_ad(!cmp_data_data(col->mtype, col->prtype, b1, len1, b2, len2)); + ut_ad(!cmp_data(col->mtype, col->prtype, false, b1, len1, b2, len2)); } } #endif @@ -5022,7 +5021,7 @@ wrong_offs: /* fputs("Comparing rec and search tuple\n", stderr); */ - if (0 != cmp_dtuple_rec(search_tuple, rec, offsets)) { + if (cmp_dtuple_rec(search_tuple, rec, index, offsets)) { if (set_also_gap_locks && !dict_index_is_spatial(index)) { @@ -5057,7 +5056,8 @@ wrong_offs: } else if (match_mode == ROW_SEL_EXACT_PREFIX) { - if (!cmp_dtuple_is_prefix_of_rec(search_tuple, rec, offsets)) { + if (!cmp_dtuple_is_prefix_of_rec(search_tuple, rec, + index, offsets)) { if (set_also_gap_locks && !dict_index_is_spatial(index)) { @@ -5178,7 +5178,7 @@ wrong_offs: && direction == 0 && dtuple_get_n_fields_cmp(search_tuple) == dict_index_get_n_unique(index) - && 0 == cmp_dtuple_rec(search_tuple, rec, offsets)) { + && !cmp_dtuple_rec(search_tuple, rec, index, offsets)) { no_gap_lock: lock_type = LOCK_REC_NOT_GAP; } @@ -6112,24 +6112,36 @@ row_search_get_max_rec( { btr_pcur_t pcur; const rec_t* rec; + const bool desc = index->fields[0].descending; /* Open at the high/right end (false), and init cursor */ btr_pcur_open_at_index_side( - false, index, BTR_SEARCH_LEAF, &pcur, true, 0, mtr); + desc, index, BTR_SEARCH_LEAF, &pcur, true, 0, mtr); - do { - const page_t* page; - - page = btr_pcur_get_page(&pcur); - rec = page_find_rec_max_not_deleted(page); - - if (page_rec_is_user_rec(rec)) { - break; - } else { - rec = NULL; + if (desc) { + const bool comp = index->table->not_redundant(); + while (btr_pcur_move_to_next_user_rec(&pcur, mtr)) { + rec = btr_pcur_get_rec(&pcur); + if (rec_is_metadata(rec, *index)) { + continue; + } + if (!rec_get_deleted_flag(rec, comp)) { + goto found; + } } - btr_pcur_move_before_first_on_page(&pcur); - } while (btr_pcur_move_to_prev(&pcur, mtr)); + } else { + do { + rec = page_find_rec_last_not_deleted( + btr_pcur_get_page(&pcur)); + if (page_rec_is_user_rec(rec)) { + goto found; + } + btr_pcur_move_before_first_on_page(&pcur); + } while (btr_pcur_move_to_prev(&pcur, mtr)); + } + + rec = nullptr; +found: btr_pcur_close(&pcur); ut_ad(!rec diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc index 695c6dba472..4a07072fdd6 100644 --- a/storage/innobase/row/row0vers.cc +++ b/storage/innobase/row/row0vers.cc @@ -325,7 +325,7 @@ not_locked: /* We check if entry and rec are identified in the alphabetical ordering */ - if (0 == cmp_dtuple_rec(entry, rec, offsets)) { + if (0 == cmp_dtuple_rec(entry, rec, index, offsets)) { /* The delete marks of rec and prev_version should be equal for rec to be in the state required by prev_version */ @@ -343,7 +343,7 @@ not_locked: dtuple_set_types_binary( entry, dtuple_get_n_fields(entry)); - if (0 != cmp_dtuple_rec(entry, rec, offsets)) { + if (cmp_dtuple_rec(entry, rec, index, offsets)) { break; } @@ -730,7 +730,7 @@ row_vers_vc_matches_cluster( /* The index field mismatch */ if (v_heap - || cmp_dfield_dfield(field2, field1) != 0) { + || cmp_dfield_dfield(field2, field1)) { if (v_heap) { dtuple_dup_v_fld(*vrow, v_heap); } @@ -833,6 +833,21 @@ row_vers_build_cur_vrow( return(cur_vrow); } +/** @return whether two data tuples are equal */ +static bool dtuple_coll_eq(const dtuple_t &tuple1, const dtuple_t &tuple2) +{ + ut_ad(tuple1.magic_n == DATA_TUPLE_MAGIC_N); + ut_ad(tuple2.magic_n == DATA_TUPLE_MAGIC_N); + ut_ad(dtuple_check_typed(&tuple1)); + ut_ad(dtuple_check_typed(&tuple2)); + ut_ad(tuple1.n_fields == tuple2.n_fields); + + for (ulint i= 0; i < tuple1.n_fields; i++) + if (cmp_dfield_dfield(&tuple1.fields[i], &tuple2.fields[i])) + return false; + return true; +} + /** Finds out if a version of the record, where the version >= the current purge view, should have ientry as its secondary index entry. We check if there is any not delete marked version of the record where the trx @@ -933,7 +948,7 @@ row_vers_old_has_index_entry( entry = row_build_index_entry( row, ext, index, heap); - if (entry && !dtuple_coll_cmp(ientry, entry)) { + if (entry && dtuple_coll_eq(*ientry, *entry)) { goto unsafe_to_purge; } } else { @@ -988,7 +1003,7 @@ row_vers_old_has_index_entry( the clustered index record has already been updated to a different binary value in a char field, but the collation identifies the old and new value anyway! */ - if (entry && !dtuple_coll_cmp(ientry, entry)) { + if (entry && dtuple_coll_eq(*ientry, *entry)) { unsafe_to_purge: mem_heap_free(heap); @@ -1086,7 +1101,7 @@ unsafe_to_purge: a char field, but the collation identifies the old and new value anyway! */ - if (entry && !dtuple_coll_cmp(ientry, entry)) { + if (entry && dtuple_coll_eq(*ientry, *entry)) { goto unsafe_to_purge; } } |