From b388075267c1650a807e1441eb95516ae9cfac85 Mon Sep 17 00:00:00 2001 From: Evdokimov Ilia Date: Tue, 22 Sep 2026 17:05:54 +0500 Subject: [PATCH v3 2/2] Don't fold ON CONFLICT's arbiterWhere to constant false --- src/backend/optimizer/plan/planner.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index e98afd27efd..a7cfc23a63b 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -98,6 +98,7 @@ create_upper_paths_hook_type create_upper_paths_hook = NULL; #define EXPRKIND_TABLEFUNC 11 #define EXPRKIND_TABLEFUNC_LATERAL 12 #define EXPRKIND_GROUPEXPR 13 +#define EXPRKIND_ARBITER_WHERE 14 /* * Data specific to grouping sets @@ -1057,10 +1058,21 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name, preprocess_expression(root, (Node *) parse->onConflict->arbiterElems, EXPRKIND_ARBITER_ELEM); + /* + * arbiterWhere is never evaluated as a runtime qual: it is only + * used to match a partial index's predicate via + * predicate_implied_by() in infer_arbiter_indexes(). Use + * EXPRKIND_ARBITER_WHERE (not EXPRKIND_QUAL) so it still gets the + * usual qual-shaped preprocessing (AND/OR flattening, + * canonicalize_qual, implicit-AND format) but is not subject to the + * is_qual-only constant folding in eval_const_expressions_qual(), + * which would replace it with a bare Const and break the + * structural predicate match. + */ parse->onConflict->arbiterWhere = preprocess_expression(root, parse->onConflict->arbiterWhere, - EXPRKIND_QUAL); + EXPRKIND_ARBITER_WHERE); parse->onConflict->onConflictSet = (List *) preprocess_expression(root, (Node *) parse->onConflict->onConflictSet, @@ -1448,7 +1460,7 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind) /* * If it's a qual or havingQual, canonicalize it. */ - if (kind == EXPRKIND_QUAL) + if (kind == EXPRKIND_QUAL || kind == EXPRKIND_ARBITER_WHERE) { expr = (Node *) canonicalize_qual((Expr *) expr, false); @@ -1463,7 +1475,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind) * hashfuncid of any that might execute more quickly by using hash lookups * instead of a linear search. */ - if (kind == EXPRKIND_QUAL || kind == EXPRKIND_TARGET) + if (kind == EXPRKIND_QUAL || kind == EXPRKIND_TARGET || + kind == EXPRKIND_ARBITER_WHERE) { convert_saop_to_hashed_saop(expr); } @@ -1479,7 +1492,9 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind) /* Expand SubLinks to SubPlans */ if (root->parse->hasSubLinks) - expr = SS_process_sublinks(root, expr, (kind == EXPRKIND_QUAL)); + expr = SS_process_sublinks(root, expr, + (kind == EXPRKIND_QUAL || + kind == EXPRKIND_ARBITER_WHERE)); /* * XXX do not insert anything here unless you have grokked the comments in @@ -1496,7 +1511,7 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind) * would be unable to simplify a top-level AND correctly. Also, * SS_process_sublinks expects explicit-AND format.) */ - if (kind == EXPRKIND_QUAL) + if (kind == EXPRKIND_QUAL || kind == EXPRKIND_ARBITER_WHERE) expr = (Node *) make_ands_implicit((Expr *) expr); return expr; -- 2.43.0