diff options
| author | Richard Guo | 2025-12-09 07:56:26 +0000 |
|---|---|---|
| committer | Richard Guo | 2025-12-09 07:56:26 +0000 |
| commit | c925ad30b047ec1388247539e86729d584c34b8d (patch) | |
| tree | 038f5d6fbc32cba9065c62de7fe85b6160457c10 /src/backend/optimizer | |
| parent | 04396eacd3faeaa4fa3d084a6749e4e384bdf0db (diff) | |
Fix const-simplification for index expressions and predicate
Similar to the issue with constraint and statistics expressions fixed
in 317c117d6, index expressions and predicate can also suffer from
incorrect reduction of NullTest clauses during const-simplification,
due to unfixed varnos and the use of a NULL root. It has been
reported that this issue can cause the planner to fail to pick up a
partial index that it previously matched successfully.
Because we need to cache the const-simplified index expressions and
predicate in the relcache entry, we cannot fix the Vars before
applying eval_const_expressions. To ensure proper reduction of
NullTest clauses, this patch runs eval_const_expressions a second time
-- after the Vars have been fixed and with a valid root.
It could be argued that the additional call to eval_const_expressions
might increase planning time, but I don't think that's a concern. It
only runs when index expressions and predicate are present; it is
relatively cheap when run on small expression trees (which is
typically the case for index expressions and predicate), and it runs
on expressions that have already been const-simplified once, making
the second pass even cheaper. In return, in cases like the one
reported, it allows the planner to match and use partial indexes,
which can lead to significant execution-time improvements.
Bug: #19007
Reported-by: Bryan Fox <bryfox@gmail.com>
Author: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/19007-4cc6e252ed8aa54a@postgresql.org
Diffstat (limited to 'src/backend/optimizer')
| -rw-r--r-- | src/backend/optimizer/util/plancat.c | 48 |
1 files changed, 40 insertions, 8 deletions
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 |
