Skip to content

Commit

Permalink
Fix crash with IndexScan hints and ordering properties of index AMs
Browse files Browse the repository at this point in the history
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:
- 23cabaf, to fix a crash with indexes that do not support ordered
operations, like GIN or GiST.
- b48c7ae, to disable COSTS in the test added in the first commit.
- 297defe, 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
  • Loading branch information
michaelpq committed Sep 10, 2023
1 parent 257259e commit bed874e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 5 deletions.
26 changes: 26 additions & 0 deletions expected/pg_hint_plan.out
Original file line number Diff line number Diff line change
Expand Up @@ -4418,6 +4418,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;
Expand Down
23 changes: 18 additions & 5 deletions pg_hint_plan.c
Original file line number Diff line number Diff line change
Expand Up @@ -3583,11 +3583,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;
}

Expand Down
9 changes: 9 additions & 0 deletions sql/pg_hint_plan.sql
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,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;
Expand Down

0 comments on commit bed874e

Please sign in to comment.