From 084f2c943583e89011c6d901bcbdbd5a4574258a Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 11 Sep 2023 08:27:55 +0900 Subject: [PATCH] Fix crash with IndexScan hints and ordering properties of index AMs IndexScan hints on indexes that do not support ordering, like GiST or GIN would lead to crashes. The cause is a useless access to nonexistent arrays for ordering attributes in IndexOptInfo, as these indexes don't use that. Index AMs that exist out of PostgreSQL core could equally trigger this failure. Since we already confirmed that the attributes and the access methods are the same as the parent index, the two are assumed to have the same ordering properties, hence there should be no need to bother checking the ordering properties of the parent. This is a backpatch of the following commits, down to all supported versions: - 23cabaf902b0, to fix a crash with indexes that do not support ordered operations, like GIN or GiST. - b48c7ae99912, to disable COSTS in the test added in the first commit. - 297defe87653, as an extra cleanup patch for the test. Thanks to Yusuke Egashira for pointing out that this fix was not backpatched properly. Author: Masahiro Ikeda Backpatch-through: 11 --- expected/pg_hint_plan.out | 26 ++++++++++++++++++++++++++ pg_hint_plan.c | 23 ++++++++++++++++++----- sql/pg_hint_plan.sql | 9 +++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/expected/pg_hint_plan.out b/expected/pg_hint_plan.out index aeeb87be..5ad6d233 100644 --- a/expected/pg_hint_plan.out +++ b/expected/pg_hint_plan.out @@ -4484,6 +4484,32 @@ error hint: Index Cond: (id < 10) (7 rows) +-- IndexScan is safe for unordered indexes +CREATE TABLE ischk (a text, b tsvector) PARTITION BY LIST(a); +CREATE TABLE ischk_d1 PARTITION OF ischk FOR VALUES IN (0); +CREATE TABLE ischk_d2 PARTITION OF ischk FOR VALUES IN (1); +CREATE INDEX ischk_idx ON ischk USING gin (b); +/*+ IndexScan(ischk ischk_idx) */ +EXPLAIN (COSTS false) SELECT * FROM ischk WHERE b = 'x'; +LOG: available indexes for IndexScan(ischk_d1): ischk_d1_b_idx +LOG: available indexes for IndexScan(ischk_d2): ischk_d2_b_idx +LOG: pg_hint_plan: +used hint: +IndexScan(ischk ischk_idx) +not used hint: +duplication hint: +error hint: + + QUERY PLAN +----------------------------------------- + Append + -> Seq Scan on ischk_d1 ischk_1 + Filter: (b = '''x'''::tsvector) + -> Seq Scan on ischk_d2 ischk_2 + Filter: (b = '''x'''::tsvector) +(5 rows) + +DROP TABLE ischk; -- quote test /*+SeqScan("""t1 ) ")IndexScan("t 2 """)HashJoin("""t1 ) "T3"t 2 """)Leading("""t1 ) "T3"t 2 """)Set(application_name"a a a"" a A")*/ EXPLAIN (COSTS false) SELECT * FROM t1 """t1 ) ", t2 "t 2 """, t3 "T3" WHERE """t1 ) ".id = "t 2 """.id AND """t1 ) ".id = "T3".id; diff --git a/pg_hint_plan.c b/pg_hint_plan.c index 9f825834..8c09b3f8 100644 --- a/pg_hint_plan.c +++ b/pg_hint_plan.c @@ -3519,11 +3519,24 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel, /* deny if any of column attributes don't match */ if (strcmp(p_attname, c_attname) != 0 || p_info->indcollation[i] != info->indexcollations[i] || - p_info->opclass[i] != info->opcintype[i]|| - ((p_info->indoption[i] & INDOPTION_DESC) != 0) - != info->reverse_sort[i] || - ((p_info->indoption[i] & INDOPTION_NULLS_FIRST) != 0) - != info->nulls_first[i]) + p_info->opclass[i] != info->opcintype[i]) + break; + + /* + * Compare index ordering if this index is ordered. + * + * We already confirmed that this and the parent indexes + * share the same column set (actually only the length of + * the column set is compard, though.) and index access + * method. So if this index is unordered, the parent can be + * assumed to be be unodered. Thus no need to bother + * checking the parent's orderedness. + */ + if (info->sortopfamily != NULL && + (((p_info->indoption[i] & INDOPTION_DESC) != 0) + != info->reverse_sort[i] || + ((p_info->indoption[i] & INDOPTION_NULLS_FIRST) != 0) + != info->nulls_first[i])) break; } diff --git a/sql/pg_hint_plan.sql b/sql/pg_hint_plan.sql index 18023f06..77b0619c 100644 --- a/sql/pg_hint_plan.sql +++ b/sql/pg_hint_plan.sql @@ -483,6 +483,15 @@ EXPLAIN (COSTS false) SELECT * FROM ONLY p1, t1 WHERE p1.id >= 50 AND p1.id <= 5 /*+TidScan(p1)*/ EXPLAIN (COSTS false) SELECT * FROM ONLY p1, t1 WHERE p1.id >= 50 AND p1.id <= 51 AND p1.ctid = '(1,1)' AND p1.id = t1.id AND t1.id < 10; +-- IndexScan is safe for unordered indexes +CREATE TABLE ischk (a text, b tsvector) PARTITION BY LIST(a); +CREATE TABLE ischk_d1 PARTITION OF ischk FOR VALUES IN (0); +CREATE TABLE ischk_d2 PARTITION OF ischk FOR VALUES IN (1); +CREATE INDEX ischk_idx ON ischk USING gin (b); +/*+ IndexScan(ischk ischk_idx) */ +EXPLAIN (COSTS false) SELECT * FROM ischk WHERE b = 'x'; +DROP TABLE ischk; + -- quote test /*+SeqScan("""t1 ) ")IndexScan("t 2 """)HashJoin("""t1 ) "T3"t 2 """)Leading("""t1 ) "T3"t 2 """)Set(application_name"a a a"" a A")*/ EXPLAIN (COSTS false) SELECT * FROM t1 """t1 ) ", t2 "t 2 """, t3 "T3" WHERE """t1 ) ".id = "t 2 """.id AND """t1 ) ".id = "T3".id;