From b458431078ce934bde0f6ff230c4157fca9fd3a5 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Mon, 26 Jun 2023 15:11:10 +0000 Subject: [PATCH 1/1] Fix handling of unavailable indexes in Scan hints This commit now prevents restrict_indexes from removing any indexes from the relation index list if none of the hinted indexes are available. This safeguard prevents an IndexScan hint supplied without any available indexes to result in a sequential scan. --- expected/pg_hint_plan.out | 126 +++++++++++++++++++------------------- expected/ut-S.out | 62 +++++++++---------- pg_hint_plan.c | 56 ++++++++++++++--- 3 files changed, 142 insertions(+), 102 deletions(-) diff --git a/expected/pg_hint_plan.out b/expected/pg_hint_plan.out index 6804c25..bc1925a 100644 --- a/expected/pg_hint_plan.out +++ b/expected/pg_hint_plan.out @@ -5570,15 +5570,15 @@ EXPLAIN (COSTS false) SELECT * FROM t5 WHERE t5.id = 1; LOG: available indexes for IndexScan(t5): LOG: pg_hint_plan: used hint: -IndexScan(t5 no_exist) not used hint: +IndexScan(t5 no_exist) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +---------------------------------------------------------------------------------------- + Index Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+IndexScan(t5 t5_id1 t5_id2)*/ @@ -5618,15 +5618,15 @@ EXPLAIN (COSTS false) SELECT * FROM t5 WHERE t5.id = 1; LOG: available indexes for IndexScan(t5): LOG: pg_hint_plan: used hint: -IndexScan(t5 no_exist5 no_exist2) not used hint: +IndexScan(t5 no_exist5 no_exist2) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +---------------------------------------------------------------------------------------- + Index Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) -- outer inner @@ -6745,8 +6745,8 @@ LOG: available indexes for IndexScan(p2_c3_c1): LOG: available indexes for IndexScan(p2_c3_c2): LOG: pg_hint_plan: used hint: -IndexScan(p2 p2_val) not used hint: +IndexScan(p2 p2_val) duplication hint: error hint: @@ -6923,8 +6923,8 @@ LOG: available indexes for IndexScan(p2_c1_c1): LOG: available indexes for IndexScan(p2_c1_c2): LOG: pg_hint_plan: used hint: -IndexScan(p2 no_exist) not used hint: +IndexScan(p2 no_exist) duplication hint: error hint: @@ -7286,15 +7286,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for IndexScanRegexp(t5): LOG: pg_hint_plan: used hint: -IndexScanRegexp(t5 t5[^_].*) not used hint: +IndexScanRegexp(t5 t5[^_].*) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ IndexScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab)*/ @@ -7302,15 +7302,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for IndexScanRegexp(t5): LOG: pg_hint_plan: used hint: -IndexScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab) not used hint: +IndexScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ IndexScan(t5 t5_id[0-9].*)*/ @@ -7318,15 +7318,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for IndexScan(t5): LOG: pg_hint_plan: used hint: -IndexScan(t5 t5_id[0-9].*) not used hint: +IndexScan(t5 t5_id[0-9].*) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ IndexOnlyScanRegexp(t5 t5_[^i].*)*/ @@ -7366,15 +7366,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for IndexOnlyScanRegexp(t5): LOG: pg_hint_plan: used hint: -IndexOnlyScanRegexp(t5 t5[^_].*) not used hint: +IndexOnlyScanRegexp(t5 t5[^_].*) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ IndexOnlyScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab)*/ @@ -7382,15 +7382,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for IndexOnlyScanRegexp(t5): LOG: pg_hint_plan: used hint: -IndexOnlyScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab) not used hint: +IndexOnlyScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ IndexOnlyScan(t5 t5_id[0-9].*)*/ @@ -7398,15 +7398,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for IndexOnlyScan(t5): LOG: pg_hint_plan: used hint: -IndexOnlyScan(t5 t5_id[0-9].*) not used hint: +IndexOnlyScan(t5 t5_id[0-9].*) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ BitmapScanRegexp(t5 t5_[^i].*)*/ @@ -7450,15 +7450,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for BitmapScanRegexp(t5): LOG: pg_hint_plan: used hint: -BitmapScanRegexp(t5 t5[^_].*) not used hint: +BitmapScanRegexp(t5 t5[^_].*) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ BitmapScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab)*/ @@ -7466,15 +7466,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for BitmapScanRegexp(t5): LOG: pg_hint_plan: used hint: -BitmapScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab) not used hint: +BitmapScanRegexp(t5 ^.*t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) /*+ BitmapScan(t5 t5_id[0-9].*)*/ @@ -7482,15 +7482,15 @@ EXPLAIN (COSTS false) SELECT id FROM t5 WHERE id = 1; LOG: available indexes for BitmapScan(t5): LOG: pg_hint_plan: used hint: -BitmapScan(t5 t5_id[0-9].*) not used hint: +BitmapScan(t5 t5_id[0-9].*) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on t5 - Filter: (id = 1) + QUERY PLAN +--------------------------------------------------------------------------------------------- + Index Only Scan using t5_idaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa on t5 + Index Cond: (id = 1) (2 rows) -- Inheritance @@ -7613,8 +7613,8 @@ LOG: available indexes for IndexScanRegexp(p1_c3_c1): LOG: available indexes for IndexScanRegexp(p1_c3_c2): LOG: pg_hint_plan: used hint: -IndexScanRegexp(p1 p1[^_].*) not used hint: +IndexScanRegexp(p1 p1[^_].*) duplication hint: error hint: @@ -7654,8 +7654,8 @@ LOG: available indexes for IndexScan(p1_c3_c1): LOG: available indexes for IndexScan(p1_c3_c2): LOG: pg_hint_plan: used hint: -IndexScan(p1 p1_.*val2.*) not used hint: +IndexScan(p1 p1_.*val2.*) duplication hint: error hint: @@ -7777,8 +7777,8 @@ LOG: available indexes for IndexOnlyScanRegexp(p1_c3_c1): LOG: available indexes for IndexOnlyScanRegexp(p1_c3_c2): LOG: pg_hint_plan: used hint: -IndexOnlyScanRegexp(p1 p1[^_].*) not used hint: +IndexOnlyScanRegexp(p1 p1[^_].*) duplication hint: error hint: @@ -7818,8 +7818,8 @@ LOG: available indexes for IndexOnlyScan(p1_c3_c1): LOG: available indexes for IndexOnlyScan(p1_c3_c2): LOG: pg_hint_plan: used hint: -IndexOnlyScan(p1 p1_.*val2.*) not used hint: +IndexOnlyScan(p1 p1_.*val2.*) duplication hint: error hint: @@ -7959,8 +7959,8 @@ LOG: available indexes for BitmapScanRegexp(p1_c3_c1): LOG: available indexes for BitmapScanRegexp(p1_c3_c2): LOG: pg_hint_plan: used hint: -BitmapScanRegexp(p1 p1[^_].*) not used hint: +BitmapScanRegexp(p1 p1[^_].*) duplication hint: error hint: @@ -8000,8 +8000,8 @@ LOG: available indexes for BitmapScan(p1_c3_c1): LOG: available indexes for BitmapScan(p1_c3_c2): LOG: pg_hint_plan: used hint: -BitmapScan(p1 p1_.*val2.*) not used hint: +BitmapScan(p1 p1_.*val2.*) duplication hint: error hint: diff --git a/expected/ut-S.out b/expected/ut-S.out index 82cb440..047d1ce 100644 --- a/expected/ut-S.out +++ b/expected/ut-S.out @@ -4474,8 +4474,8 @@ error hint: LOG: available indexes for IndexScan(ti1): LOG: pg_hint_plan: used hint: -IndexScan(ti1 not_exist) not used hint: +IndexScan(ti1 not_exist) duplication hint: error hint: @@ -4483,8 +4483,8 @@ error hint: \! sql/maskout.sh results/ut-S.tmpout QUERY PLAN ---------------- - Seq Scan on ti1 (cost={inf}..{inf} rows=1 width=xxx) - Filter: (c1 = 100) + Index Scan using ti1_hash on ti1 (cost=xxx..xxx rows=1 width=xxx) + Index Cond: (c1 = 100) -- No. S-3-5-5 \o results/ut-S.tmpout @@ -4492,8 +4492,8 @@ error hint: LOG: available indexes for BitmapScan(ti1): LOG: pg_hint_plan: used hint: -BitmapScan(ti1 not_exist) not used hint: +BitmapScan(ti1 not_exist) duplication hint: error hint: @@ -4501,8 +4501,8 @@ error hint: \! sql/maskout.sh results/ut-S.tmpout QUERY PLAN ---------------- - Seq Scan on ti1 (cost={inf}..{inf} rows=1 width=xxx) - Filter: (c1 = 100) + Index Scan using ti1_hash on ti1 (cost=xxx..xxx rows=1 width=xxx) + Index Cond: (c1 = 100) -- No. S-3-5-6 \o results/ut-S.tmpout @@ -4510,8 +4510,8 @@ error hint: LOG: available indexes for IndexOnlyScan(ti1): LOG: pg_hint_plan: used hint: -IndexOnlyScan(ti1 not_exist) not used hint: +IndexOnlyScan(ti1 not_exist) duplication hint: error hint: @@ -4519,8 +4519,8 @@ error hint: \! sql/maskout.sh results/ut-S.tmpout QUERY PLAN ---------------- - Seq Scan on ti1 (cost={inf}..{inf} rows=1 width=xxx) - Filter: (c1 = 100) + Index Scan using ti1_hash on ti1 (cost=xxx..xxx rows=1 width=xxx) + Index Cond: (c1 = 100) -- No. S-3-5-7 EXPLAIN (COSTS false) SELECT * FROM s1.t1 WHERE t1.c1 = 1; @@ -5223,11 +5223,11 @@ error hint: Append (cost=xxx..xxx rows=4 width=xxx) -> Index Scan using p1_i2 on p1 p1_1 (cost=xxx..xxx rows=1 width=xxx) Index Cond: (c2 = 1) - -> Seq Scan on p1c1 p1_2 (cost={inf}..{inf} rows=1 width=xxx) + -> Seq Scan on p1c1 p1_2 (cost=xxx..xxx rows=1 width=xxx) Filter: (c2 = 1) - -> Seq Scan on p1c2 p1_3 (cost={inf}..{inf} rows=1 width=xxx) + -> Seq Scan on p1c2 p1_3 (cost=xxx..xxx rows=1 width=xxx) Filter: (c2 = 1) - -> Seq Scan on p1c3 p1_4 (cost={inf}..{inf} rows=1 width=xxx) + -> Seq Scan on p1c3 p1_4 (cost=xxx..xxx rows=1 width=xxx) Filter: (c2 = 1) -- No. S-3-10-5 @@ -5247,12 +5247,12 @@ error hint: \! sql/maskout.sh results/ut-S.tmpout QUERY PLAN ---------------- - Append (cost={inf}..{inf} rows=3 width=xxx) - -> Seq Scan on p2 p2_1 (cost={inf}..{inf} rows=1 width=xxx) + Append (cost=xxx..xxx rows=3 width=xxx) + -> Seq Scan on p2 p2_1 (cost=xxx..xxx rows=1 width=xxx) Filter: (c1 = 1) -> Index Scan using p2c1_pkey on p2c1 p2_2 (cost=xxx..xxx rows=1 width=xxx) Index Cond: (c1 = 1) - -> Seq Scan on p2c1c1 p2_3 (cost={inf}..{inf} rows=1 width=xxx) + -> Seq Scan on p2c1c1 p2_3 (cost=xxx..xxx rows=1 width=xxx) Filter: (c1 = 1) ---- @@ -5950,15 +5950,15 @@ EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; LOG: available indexes for IndexScanRegexp(ti1): LOG: pg_hint_plan: used hint: -IndexScanRegexp(ti1 no.*_exist) not used hint: +IndexScanRegexp(ti1 no.*_exist) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on ti1 - Filter: (c2 = 1) + QUERY PLAN +-------------------------------- + Index Scan using ti1_i4 on ti1 + Index Cond: (c2 = 1) (2 rows) -- No. S-3-14-4 @@ -6010,8 +6010,8 @@ LOG: available indexes for IndexScanRegexp(p1): LOG: available indexes for IndexScanRegexp(p1c1): LOG: pg_hint_plan: used hint: -IndexScanRegexp(p1 no.*_exist) not used hint: +IndexScanRegexp(p1 no.*_exist) duplication hint: error hint: @@ -6050,15 +6050,15 @@ EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; LOG: available indexes for IndexScan(ti1): LOG: pg_hint_plan: used hint: -IndexScan(ti1 not_exist) not used hint: +IndexScan(ti1 not_exist) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on ti1 - Filter: (c2 = 1) + QUERY PLAN +-------------------------------- + Index Scan using ti1_i4 on ti1 + Index Cond: (c2 = 1) (2 rows) -- No. S-3-15-3 @@ -6101,15 +6101,15 @@ EXPLAIN (COSTS false) SELECT * FROM s1.ti1 WHERE c2 = 1; LOG: available indexes for IndexScan(ti1): LOG: pg_hint_plan: used hint: -IndexScan(ti1 not_exist1 not_exist2) not used hint: +IndexScan(ti1 not_exist1 not_exist2) duplication hint: error hint: - QUERY PLAN --------------------- - Seq Scan on ti1 - Filter: (c2 = 1) + QUERY PLAN +-------------------------------- + Index Scan using ti1_i4 on ti1 + Index Cond: (c2 = 1) (2 rows) DELETE FROM pg_db_role_setting WHERE setrole = (SELECT oid FROM pg_roles WHERE rolname = current_user); diff --git a/pg_hint_plan.c b/pg_hint_plan.c index f788b7b..b556b8a 100644 --- a/pg_hint_plan.c +++ b/pg_hint_plan.c @@ -398,6 +398,7 @@ struct HintState RowsHint **rows_hints; /* parsed Rows hints */ ParallelHint **parallel_hints; /* parsed Parallel hints */ JoinMethodHint **memoize_hints; /* parsed Memoize hints */ + bool skip_scan_method; /* enforce the scan method ( or not )*/ }; /* @@ -1116,6 +1117,7 @@ HintStateCreate(void) hstate->set_hints = NULL; hstate->rows_hints = NULL; hstate->parallel_hints = NULL; + hstate->skip_scan_method = false; return hstate; } @@ -2745,7 +2747,8 @@ setup_parallel_plan_enforcement(ParallelHint *hint, HintState *state) /* * Setup GUC environment to enforce scan methods. If scanhint is NULL, reset - * GUCs to the saved state in state. + * GUCs to the saved state in state. Also, if skip_scan_method is true, reset + * to false and skip enforcing the scan method. */ static void setup_scan_method_enforcement(ScanMethodHint *scanhint, HintState *state) @@ -2754,6 +2757,12 @@ setup_scan_method_enforcement(ScanMethodHint *scanhint, HintState *state) GucContext context = state->context; unsigned char mask; + if (current_hint_state->skip_scan_method) + { + current_hint_state->skip_scan_method = false; + return; + } + if (scanhint) { enforce_mask = scanhint->enforce_mask; @@ -3429,6 +3438,7 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel, StringInfoData buf; RangeTblEntry *rte = root->simple_rte_array[rel->relid]; Oid relationObjectId = rte->relid; + List *unused_indexes = NIL; /* * We delete all the IndexOptInfo list and prevent you from being usable by @@ -3452,7 +3462,9 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel, /* * Leaving only an specified index, we delete it from a IndexOptInfo list - * other than it. + * other than it. However, if none of the specified indexes are available, + * then we keep all the indexes and skip enforcing the scan method. i.e., + * we skip the scan hint altogether for the relation. */ if (debug_level > 0) initStringInfo(&buf); @@ -3646,11 +3658,37 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel, } if (!use_index) - rel->indexlist = foreach_delete_current(rel->indexlist, cell); + unused_indexes = lappend_oid(unused_indexes, info->indexoid); pfree(indexname); } + if (list_length(unused_indexes) < list_length(rel->indexlist)) + { + foreach (cell, unused_indexes) + { + Oid final_oid = lfirst_oid(cell); + ListCell *l; + + foreach (l, rel->indexlist) + { + IndexOptInfo *info = (IndexOptInfo *) lfirst(l); + + if (info->indexoid == final_oid) + rel->indexlist = foreach_delete_current(rel->indexlist, l); + } + } + } else + /* + * If the hint does not have any matching indexes, we better + * skip applying the hinted scan method. For example if an + * IndexScan hint does not have any matching indexes, we + * should not enforce an enable_indexscan. + */ + current_hint_state->skip_scan_method = true; + + list_free(unused_indexes); + if (debug_level > 0) { StringInfoData rel_buf; @@ -3865,8 +3903,6 @@ setup_hint_enforcement(PlannerInfo *root, RelOptInfo *rel, { ScanMethodHint * pshint = current_hint_state->parent_scan_hint; - pshint->base.state = HINT_STATE_USED; - /* Apply index mask in the same manner to the parent. */ if (pshint->indexnames) { @@ -3915,12 +3951,16 @@ setup_hint_enforcement(PlannerInfo *root, RelOptInfo *rel, ret |= HINT_BM_SCAN_METHOD; - /* Setup scan enforcement environment */ - setup_scan_method_enforcement(shint, current_hint_state); - /* restrict unwanted inexes */ restrict_indexes(root, shint, rel, using_parent_hint); + /* Setup scan enforcement environment + * + * Must be called after restrict_indexes which sets + * current_hint_state->skip_scan_method. + */ + setup_scan_method_enforcement(shint, current_hint_state); + if (debug_level > 1) { char *additional_message = ""; -- 2.39.2