postgrespro.git / summary / log / commit / refs
commit 76a39f2295ecb040f2ea052320941e1eb9b526c0
Author: Peter Geoghegan <pg@bowt.ie>
Date: Mon Apr 01 00:24:04 2019 +0000
Fix nbtree high key "continuescan" row compare bug.
Commit 29b64d1d mishandled skipping over truncated high key attributes
during row comparisons. The row comparison key matching loop would loop
forever when a truncated attribute was encountered for a row compare
subkey. Fix by following the example of other code in the loop: advance
the current subkey, or break out of the loop when the last subkey is
reached.
Add test coverage for the relevant _bt_check_rowcompare() code path.
The new test case is somewhat tied to nbtree implementation details,
which isn't ideal, but seems unavoidable.
src/backend/access/nbtree/nbtutils.c | 3 +++
src/test/regress/expected/index_including.out | 21 ++++++++++++++++++++-
src/test/regress/sql/index_including.sql | 8 +++++++-
3 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 92b8b5f134..140ac92026 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1543,6 +1543,9 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts,
*/
Assert(ScanDirectionIsForward(dir));
cmpresult = 0;
+ if (subkey->sk_flags & SK_ROW_END)
+ break;
+ subkey++;
continue;
}
diff --git a/src/test/regress/expected/index_including.out b/src/test/regress/expected/index_including.out
index 77ec29f2a3..2405709f40 100644
--- a/src/test/regress/expected/index_including.out
+++ b/src/test/regress/expected/index_including.out
@@ -126,7 +126,7 @@ DETAIL: Key (c1, c2)=(1, 2) already exists.
INSERT INTO tbl SELECT 1, NULL, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x;
ERROR: null value in column "c2" violates not-null constraint
DETAIL: Failing row contains (1, null, 3, (4,4),(4,4)).
-INSERT INTO tbl SELECT x, 2*x, NULL, NULL FROM generate_series(1,10) AS x;
+INSERT INTO tbl SELECT x, 2*x, NULL, NULL FROM generate_series(1,300) AS x;
explain (costs off)
select * from tbl where (c1,c2,c3) < (2,5,1);
QUERY PLAN
@@ -144,7 +144,26 @@ select * from tbl where (c1,c2,c3) < (2,5,1);
2 | 4 | |
(2 rows)
+-- row comparison that compares high key at page boundary
+SET enable_seqscan = off;
+explain (costs off)
+select * from tbl where (c1,c2,c3) < (262,1,1) limit 1;
+ QUERY PLAN
+----------------------------------------------------
+ Limit
+ -> Index Only Scan using covering on tbl
+ Index Cond: (ROW(c1, c2) <= ROW(262, 1))
+ Filter: (ROW(c1, c2, c3) < ROW(262, 1, 1))
+(4 rows)
+
+select * from tbl where (c1,c2,c3) < (262,1,1) limit 1;
+ c1 | c2 | c3 | c4
+----+----+----+----
+ 1 | 2 | |
+(1 row)
+
DROP TABLE tbl;
+RESET enable_seqscan;
CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box,
UNIQUE(c1,c2) INCLUDE(c3,c4));
SELECT indexrelid::regclass, indnatts, indnkeyatts, indisunique, indisprimary, indkey, indclass FROM pg_index WHERE indrelid = 'tbl'::regclass::oid;
diff --git a/src/test/regress/sql/index_including.sql b/src/test/regress/sql/index_including.sql
index c0ae71d0cb..7e517483ad 100644
--- a/src/test/regress/sql/index_including.sql
+++ b/src/test/regress/sql/index_including.sql
@@ -72,11 +72,17 @@ SELECT pg_get_constraintdef(oid), conname, conkey FROM pg_constraint WHERE conre
-- ensure that constraint works
INSERT INTO tbl SELECT 1, 2, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x;
INSERT INTO tbl SELECT 1, NULL, 3*x, box('4,4,4,4') FROM generate_series(1,10) AS x;
-INSERT INTO tbl SELECT x, 2*x, NULL, NULL FROM generate_series(1,10) AS x;
+INSERT INTO tbl SELECT x, 2*x, NULL, NULL FROM generate_series(1,300) AS x;
explain (costs off)
select * from tbl where (c1,c2,c3) < (2,5,1);
select * from tbl where (c1,c2,c3) < (2,5,1);
+-- row comparison that compares high key at page boundary
+SET enable_seqscan = off;
+explain (costs off)
+select * from tbl where (c1,c2,c3) < (262,1,1) limit 1;
+select * from tbl where (c1,c2,c3) < (262,1,1) limit 1;
DROP TABLE tbl;
+RESET enable_seqscan;
CREATE TABLE tbl (c1 int,c2 int, c3 int, c4 box,
UNIQUE(c1,c2) INCLUDE(c3,c4));
[parent: 8fba397f0ca7]