diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 07f92fac239..ed0dac37f51 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -429,13 +429,32 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * modify the copies we obtain from the relcache to have the * correct varno for the parent relation, so that they match up * correctly against qual clauses. + * + * After fixing the varnos, we need to run the index expressions + * and predicate through const-simplification again, using a valid + * "root". This ensures that NullTest quals for Vars can be + * properly reduced. */ info->indexprs = RelationGetIndexExpressions(indexRelation); info->indpred = RelationGetIndexPredicate(indexRelation); - if (info->indexprs && varno != 1) - ChangeVarNodes((Node *) info->indexprs, 1, varno, 0); - if (info->indpred && varno != 1) - ChangeVarNodes((Node *) info->indpred, 1, varno, 0); + if (info->indexprs) + { + if (varno != 1) + ChangeVarNodes((Node *) info->indexprs, 1, varno, 0); + + info->indexprs = (List *) + eval_const_expressions(root, (Node *) info->indexprs); + } + if (info->indpred) + { + if (varno != 1) + ChangeVarNodes((Node *) info->indpred, 1, varno, 0); + + info->indpred = (List *) + eval_const_expressions(root, + (Node *) make_ands_explicit(info->indpred)); + info->indpred = make_ands_implicit((Expr *) info->indpred); + } /* Build targetlist using the completed indexprs data */ info->indextlist = build_index_tlist(root, info, relation); @@ -1047,8 +1066,13 @@ infer_arbiter_indexes(PlannerInfo *root) /* Expression attributes (if any) must match */ idxExprs = RelationGetIndexExpressions(idxRel); - if (idxExprs && varno != 1) - ChangeVarNodes((Node *) idxExprs, 1, varno, 0); + if (idxExprs) + { + if (varno != 1) + ChangeVarNodes((Node *) idxExprs, 1, varno, 0); + + idxExprs = (List *) eval_const_expressions(root, (Node *) idxExprs); + } /* * If arbiterElems are present, check them. (Note that if a @@ -1109,8 +1133,16 @@ infer_arbiter_indexes(PlannerInfo *root) continue; predExprs = RelationGetIndexPredicate(idxRel); - if (predExprs && varno != 1) - ChangeVarNodes((Node *) predExprs, 1, varno, 0); + if (predExprs) + { + if (varno != 1) + ChangeVarNodes((Node *) predExprs, 1, varno, 0); + + predExprs = (List *) + eval_const_expressions(root, + (Node *) make_ands_explicit(predExprs)); + predExprs = make_ands_implicit((Expr *) predExprs); + } /* * Partial indexes affect each form of ON CONFLICT differently: if a diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 0e82ca1867a..2a0a457a7d9 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -9547,7 +9547,7 @@ left join j2 on j1.id1 = j2.id1 where j1.id2 = 1; Output: j2.id1, j2.id2 (8 rows) -create unique index j1_id2_idx on j1(id2) where id2 is not null; +create unique index j1_id2_idx on j1(id2) where id2 > 0; -- ensure we don't use a partial unique index as unique proofs explain (verbose, costs off) select * from j1 diff --git a/src/test/regress/expected/predicate.out b/src/test/regress/expected/predicate.out index 66fb0854b88..94c343fe030 100644 --- a/src/test/regress/expected/predicate.out +++ b/src/test/regress/expected/predicate.out @@ -443,3 +443,44 @@ SELECT * FROM pred_tab2, pred_tab1 WHERE pred_tab1.a IS NULL OR pred_tab1.b < 2; RESET constraint_exclusion; DROP TABLE pred_tab1; DROP TABLE pred_tab2; +-- Validate that NullTest quals in index expressions and predicate are reduced correctly +CREATE TABLE pred_tab (a int, b int NOT NULL, c int NOT NULL); +INSERT INTO pred_tab SELECT i, i, i FROM generate_series(1, 1000) i; +CREATE INDEX pred_tab_exprs_idx ON pred_tab ((a < 5 AND b IS NOT NULL AND c IS NOT NULL)); +CREATE INDEX pred_tab_pred_idx ON pred_tab (a) WHERE b IS NOT NULL AND c IS NOT NULL; +ANALYZE pred_tab; +-- Ensure that index pred_tab_exprs_idx is used +EXPLAIN (COSTS OFF) +SELECT * FROM pred_tab WHERE (a < 5 AND b IS NOT NULL AND c IS NOT NULL) IS TRUE; + QUERY PLAN +------------------------------------------------- + Index Scan using pred_tab_exprs_idx on pred_tab + Index Cond: ((a < 5) = true) +(2 rows) + +SELECT * FROM pred_tab WHERE (a < 5 AND b IS NOT NULL AND c IS NOT NULL) IS TRUE; + a | b | c +---+---+--- + 1 | 1 | 1 + 2 | 2 | 2 + 3 | 3 | 3 + 4 | 4 | 4 +(4 rows) + +-- Ensure that index pred_tab_pred_idx is used +EXPLAIN (COSTS OFF) +SELECT * FROM pred_tab WHERE a < 3 AND b IS NOT NULL AND c IS NOT NULL; + QUERY PLAN +------------------------------------------------ + Index Scan using pred_tab_pred_idx on pred_tab + Index Cond: (a < 3) +(2 rows) + +SELECT * FROM pred_tab WHERE a < 3 AND b IS NOT NULL AND c IS NOT NULL; + a | b | c +---+---+--- + 1 | 1 | 1 + 2 | 2 | 2 +(2 rows) + +DROP TABLE pred_tab; diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index c6b8b09a381..c47118e9291 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -3599,7 +3599,7 @@ explain (verbose, costs off) select * from j1 left join j2 on j1.id1 = j2.id1 where j1.id2 = 1; -create unique index j1_id2_idx on j1(id2) where id2 is not null; +create unique index j1_id2_idx on j1(id2) where id2 > 0; -- ensure we don't use a partial unique index as unique proofs explain (verbose, costs off) diff --git a/src/test/regress/sql/predicate.sql b/src/test/regress/sql/predicate.sql index 32302d60b6d..7d4fda1bc18 100644 --- a/src/test/regress/sql/predicate.sql +++ b/src/test/regress/sql/predicate.sql @@ -221,3 +221,22 @@ SELECT * FROM pred_tab2, pred_tab1 WHERE pred_tab1.a IS NULL OR pred_tab1.b < 2; RESET constraint_exclusion; DROP TABLE pred_tab1; DROP TABLE pred_tab2; + +-- Validate that NullTest quals in index expressions and predicate are reduced correctly +CREATE TABLE pred_tab (a int, b int NOT NULL, c int NOT NULL); +INSERT INTO pred_tab SELECT i, i, i FROM generate_series(1, 1000) i; +CREATE INDEX pred_tab_exprs_idx ON pred_tab ((a < 5 AND b IS NOT NULL AND c IS NOT NULL)); +CREATE INDEX pred_tab_pred_idx ON pred_tab (a) WHERE b IS NOT NULL AND c IS NOT NULL; +ANALYZE pred_tab; + +-- Ensure that index pred_tab_exprs_idx is used +EXPLAIN (COSTS OFF) +SELECT * FROM pred_tab WHERE (a < 5 AND b IS NOT NULL AND c IS NOT NULL) IS TRUE; +SELECT * FROM pred_tab WHERE (a < 5 AND b IS NOT NULL AND c IS NOT NULL) IS TRUE; + +-- Ensure that index pred_tab_pred_idx is used +EXPLAIN (COSTS OFF) +SELECT * FROM pred_tab WHERE a < 3 AND b IS NOT NULL AND c IS NOT NULL; +SELECT * FROM pred_tab WHERE a < 3 AND b IS NOT NULL AND c IS NOT NULL; + +DROP TABLE pred_tab;