summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2022-01-26 11:01:39 +0200
committerSergei Golubchik <serg@mariadb.org>2022-01-26 18:43:05 +0100
commit358921ce32203a9a8dd277a5ba7ac177c9e79e53 (patch)
tree41831126d41212b52264d32c6fe9bff93380f063
parent349a595bc448fe654087f1e6444b17e9ee1583fc (diff)
downloadmariadb-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.
-rw-r--r--mysql-test/suite/innodb/r/autoinc_persist,desc.rdiff162
-rw-r--r--mysql-test/suite/innodb/r/autoinc_persist.result11
-rw-r--r--mysql-test/suite/innodb/r/innodb-index.result48
-rw-r--r--mysql-test/suite/innodb/t/autoinc_persist.combinations2
-rw-r--r--mysql-test/suite/innodb/t/autoinc_persist.test56
-rw-r--r--mysql-test/suite/innodb/t/innodb-index.test42
-rw-r--r--mysql-test/suite/innodb_fts/r/create.result14
-rw-r--r--mysql-test/suite/innodb_fts/t/create.test21
-rw-r--r--storage/innobase/CMakeLists.txt1
-rw-r--r--storage/innobase/btr/btr0btr.cc19
-rw-r--r--storage/innobase/btr/btr0cur.cc11
-rw-r--r--storage/innobase/btr/btr0pcur.cc2
-rw-r--r--storage/innobase/btr/btr0sea.cc7
-rw-r--r--storage/innobase/data/data0data.cc36
-rw-r--r--storage/innobase/dict/dict0crea.cc25
-rw-r--r--storage/innobase/dict/dict0dict.cc38
-rw-r--r--storage/innobase/dict/dict0load.cc21
-rw-r--r--storage/innobase/dict/dict0mem.cc26
-rw-r--r--storage/innobase/dict/dict0stats.cc11
-rw-r--r--storage/innobase/eval/eval0eval.cc4
-rw-r--r--storage/innobase/handler/ha_innodb.cc15
-rw-r--r--storage/innobase/handler/handler0alter.cc51
-rw-r--r--storage/innobase/ibuf/ibuf0ibuf.cc5
-rw-r--r--storage/innobase/include/data0data.h10
-rw-r--r--storage/innobase/include/dict0dict.h20
-rw-r--r--storage/innobase/include/dict0mem.h32
-rw-r--r--storage/innobase/include/ibuf0ibuf.ic18
-rw-r--r--storage/innobase/include/page0cur.ic3
-rw-r--r--storage/innobase/include/page0page.h2
-rw-r--r--storage/innobase/include/rem0cmp.h185
-rw-r--r--storage/innobase/include/rem0cmp.ic107
-rw-r--r--storage/innobase/include/row0merge.h1
-rw-r--r--storage/innobase/page/page0cur.cc9
-rw-r--r--storage/innobase/page/page0page.cc2
-rw-r--r--storage/innobase/pars/pars0opt.cc10
-rw-r--r--storage/innobase/rem/rem0cmp.cc257
-rw-r--r--storage/innobase/row/row0ftsort.cc3
-rw-r--r--storage/innobase/row/row0ins.cc36
-rw-r--r--storage/innobase/row/row0merge.cc32
-rw-r--r--storage/innobase/row/row0mysql.cc3
-rw-r--r--storage/innobase/row/row0purge.cc2
-rw-r--r--storage/innobase/row/row0sel.cc54
-rw-r--r--storage/innobase/row/row0vers.cc27
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;
}
}