@@ -115,6 +115,9 @@ static List *find_nonnullable_vars_walker(Node *node, bool top_level);
115115static bool is_strict_saop (ScalarArrayOpExpr * expr , bool falseOK );
116116static Node * eval_const_expressions_mutator (Node * node ,
117117 eval_const_expressions_context * context );
118+ static bool contain_non_const_walker (Node * node , void * context );
119+ static bool ece_function_is_safe (Oid funcid ,
120+ eval_const_expressions_context * context );
118121static List * simplify_or_arguments (List * args ,
119122 eval_const_expressions_context * context ,
120123 bool * haveNull , bool * forceTrue );
@@ -2502,6 +2505,37 @@ estimate_expression_value(PlannerInfo *root, Node *node)
25022505 return eval_const_expressions_mutator (node , & context );
25032506}
25042507
2508+ /*
2509+ * The generic case in eval_const_expressions_mutator is to recurse using
2510+ * expression_tree_mutator, which will copy the given node unchanged but
2511+ * const-simplify its arguments (if any) as far as possible. If the node
2512+ * itself does immutable processing, and each of its arguments were reduced
2513+ * to a Const, we can then reduce it to a Const using evaluate_expr. (Some
2514+ * node types need more complicated logic; for example, a CASE expression
2515+ * might be reducible to a constant even if not all its subtrees are.)
2516+ */
2517+ #define ece_generic_processing (node ) \
2518+ expression_tree_mutator((Node *) (node), eval_const_expressions_mutator, \
2519+ (void *) context)
2520+
2521+ /*
2522+ * Check whether all arguments of the given node were reduced to Consts.
2523+ * By going directly to expression_tree_walker, contain_non_const_walker
2524+ * is not applied to the node itself, only to its children.
2525+ */
2526+ #define ece_all_arguments_const (node ) \
2527+ (!expression_tree_walker((Node *) (node), contain_non_const_walker, NULL))
2528+
2529+ /* Generic macro for applying evaluate_expr */
2530+ #define ece_evaluate_expr (node ) \
2531+ ((Node *) evaluate_expr((Expr *) (node), \
2532+ exprType((Node *) (node)), \
2533+ exprTypmod((Node *) (node)), \
2534+ exprCollation((Node *) (node))))
2535+
2536+ /*
2537+ * Recursive guts of eval_const_expressions/estimate_expression_value
2538+ */
25052539static Node *
25062540eval_const_expressions_mutator (Node * node ,
25072541 eval_const_expressions_context * context )
@@ -2830,6 +2864,25 @@ eval_const_expressions_mutator(Node *node,
28302864 newexpr -> location = expr -> location ;
28312865 return (Node * ) newexpr ;
28322866 }
2867+ case T_ScalarArrayOpExpr :
2868+ {
2869+ ScalarArrayOpExpr * saop ;
2870+
2871+ /* Copy the node and const-simplify its arguments */
2872+ saop = (ScalarArrayOpExpr * ) ece_generic_processing (node );
2873+
2874+ /* Make sure we know underlying function */
2875+ set_sa_opfuncid (saop );
2876+
2877+ /*
2878+ * If all arguments are Consts, and it's a safe function, we
2879+ * can fold to a constant
2880+ */
2881+ if (ece_all_arguments_const (saop ) &&
2882+ ece_function_is_safe (saop -> opfuncid , context ))
2883+ return ece_evaluate_expr (saop );
2884+ return (Node * ) saop ;
2885+ }
28332886 case T_BoolExpr :
28342887 {
28352888 BoolExpr * expr = (BoolExpr * ) node ;
@@ -3054,47 +3107,24 @@ eval_const_expressions_mutator(Node *node,
30543107 }
30553108 case T_ArrayCoerceExpr :
30563109 {
3057- ArrayCoerceExpr * expr = (ArrayCoerceExpr * ) node ;
3058- Expr * arg ;
3059- Expr * elemexpr ;
3060- ArrayCoerceExpr * newexpr ;
3061-
3062- /*
3063- * Reduce constants in the ArrayCoerceExpr's argument and
3064- * per-element expressions, then build a new ArrayCoerceExpr.
3065- */
3066- arg = (Expr * ) eval_const_expressions_mutator ((Node * ) expr -> arg ,
3067- context );
3068- elemexpr = (Expr * ) eval_const_expressions_mutator ((Node * ) expr -> elemexpr ,
3069- context );
3110+ ArrayCoerceExpr * ac ;
30703111
3071- newexpr = makeNode (ArrayCoerceExpr );
3072- newexpr -> arg = arg ;
3073- newexpr -> elemexpr = elemexpr ;
3074- newexpr -> resulttype = expr -> resulttype ;
3075- newexpr -> resulttypmod = expr -> resulttypmod ;
3076- newexpr -> resultcollid = expr -> resultcollid ;
3077- newexpr -> coerceformat = expr -> coerceformat ;
3078- newexpr -> location = expr -> location ;
3112+ /* Copy the node and const-simplify its arguments */
3113+ ac = (ArrayCoerceExpr * ) ece_generic_processing (node );
30793114
30803115 /*
3081- * If constant argument and per-element expression is
3116+ * If constant argument and the per-element expression is
30823117 * immutable, we can simplify the whole thing to a constant.
30833118 * Exception: although contain_mutable_functions considers
30843119 * CoerceToDomain immutable for historical reasons, let's not
30853120 * do so here; this ensures coercion to an array-over-domain
30863121 * does not apply the domain's constraints until runtime.
30873122 */
3088- if (arg && IsA (arg , Const ) &&
3089- elemexpr && !IsA (elemexpr , CoerceToDomain ) &&
3090- !contain_mutable_functions ((Node * ) elemexpr ))
3091- return (Node * ) evaluate_expr ((Expr * ) newexpr ,
3092- newexpr -> resulttype ,
3093- newexpr -> resulttypmod ,
3094- newexpr -> resultcollid );
3095-
3096- /* Else we must return the partially-simplified node */
3097- return (Node * ) newexpr ;
3123+ if (ac -> arg && IsA (ac -> arg , Const ) &&
3124+ ac -> elemexpr && !IsA (ac -> elemexpr , CoerceToDomain ) &&
3125+ !contain_mutable_functions ((Node * ) ac -> elemexpr ))
3126+ return ece_evaluate_expr (ac );
3127+ return (Node * ) ac ;
30983128 }
30993129 case T_CollateExpr :
31003130 {
@@ -3286,41 +3316,22 @@ eval_const_expressions_mutator(Node *node,
32863316 else
32873317 return copyObject (node );
32883318 }
3319+ case T_ArrayRef :
32893320 case T_ArrayExpr :
3321+ case T_RowExpr :
32903322 {
3291- ArrayExpr * arrayexpr = (ArrayExpr * ) node ;
3292- ArrayExpr * newarray ;
3293- bool all_const = true;
3294- List * newelems ;
3295- ListCell * element ;
3296-
3297- newelems = NIL ;
3298- foreach (element , arrayexpr -> elements )
3299- {
3300- Node * e ;
3301-
3302- e = eval_const_expressions_mutator ((Node * ) lfirst (element ),
3303- context );
3304- if (!IsA (e , Const ))
3305- all_const = false;
3306- newelems = lappend (newelems , e );
3307- }
3323+ /*
3324+ * Generic handling for node types whose own processing is
3325+ * known to be immutable, and for which we need no smarts
3326+ * beyond "simplify if all inputs are constants".
3327+ */
33083328
3309- newarray = makeNode (ArrayExpr );
3310- newarray -> array_typeid = arrayexpr -> array_typeid ;
3311- newarray -> array_collid = arrayexpr -> array_collid ;
3312- newarray -> element_typeid = arrayexpr -> element_typeid ;
3313- newarray -> elements = newelems ;
3314- newarray -> multidims = arrayexpr -> multidims ;
3315- newarray -> location = arrayexpr -> location ;
3316-
3317- if (all_const )
3318- return (Node * ) evaluate_expr ((Expr * ) newarray ,
3319- newarray -> array_typeid ,
3320- exprTypmod (node ),
3321- newarray -> array_collid );
3322-
3323- return (Node * ) newarray ;
3329+ /* Copy the node and const-simplify its arguments */
3330+ node = ece_generic_processing (node );
3331+ /* If all arguments are Consts, we can fold to a constant */
3332+ if (ece_all_arguments_const (node ))
3333+ return ece_evaluate_expr (node );
3334+ return node ;
33243335 }
33253336 case T_CoalesceExpr :
33263337 {
@@ -3397,7 +3408,8 @@ eval_const_expressions_mutator(Node *node,
33973408 * simple Var. (This case won't be generated directly by the
33983409 * parser, because ParseComplexProjection short-circuits it.
33993410 * But it can arise while simplifying functions.) Also, we
3400- * can optimize field selection from a RowExpr construct.
3411+ * can optimize field selection from a RowExpr construct, or
3412+ * of course from a constant.
34013413 *
34023414 * However, replacing a whole-row Var in this way has a
34033415 * pitfall: if we've already built the rel targetlist for the
@@ -3412,6 +3424,8 @@ eval_const_expressions_mutator(Node *node,
34123424 * We must also check that the declared type of the field is
34133425 * still the same as when the FieldSelect was created --- this
34143426 * can change if someone did ALTER COLUMN TYPE on the rowtype.
3427+ * If it isn't, we skip the optimization; the case will
3428+ * probably fail at runtime, but that's not our problem here.
34153429 */
34163430 FieldSelect * fselect = (FieldSelect * ) node ;
34173431 FieldSelect * newfselect ;
@@ -3462,6 +3476,17 @@ eval_const_expressions_mutator(Node *node,
34623476 newfselect -> resulttype = fselect -> resulttype ;
34633477 newfselect -> resulttypmod = fselect -> resulttypmod ;
34643478 newfselect -> resultcollid = fselect -> resultcollid ;
3479+ if (arg && IsA (arg , Const ))
3480+ {
3481+ Const * con = (Const * ) arg ;
3482+
3483+ if (rowtype_field_matches (con -> consttype ,
3484+ newfselect -> fieldnum ,
3485+ newfselect -> resulttype ,
3486+ newfselect -> resulttypmod ,
3487+ newfselect -> resultcollid ))
3488+ return ece_evaluate_expr (newfselect );
3489+ }
34653490 return (Node * ) newfselect ;
34663491 }
34673492 case T_NullTest :
@@ -3557,6 +3582,13 @@ eval_const_expressions_mutator(Node *node,
35573582 }
35583583 case T_BooleanTest :
35593584 {
3585+ /*
3586+ * This case could be folded into the generic handling used
3587+ * for ArrayRef etc. But because the simplification logic is
3588+ * so trivial, applying evaluate_expr() to perform it would be
3589+ * a heavy overhead. BooleanTest is probably common enough to
3590+ * justify keeping this bespoke implementation.
3591+ */
35603592 BooleanTest * btest = (BooleanTest * ) node ;
35613593 BooleanTest * newbtest ;
35623594 Node * arg ;
@@ -3630,14 +3662,57 @@ eval_const_expressions_mutator(Node *node,
36303662 }
36313663
36323664 /*
3633- * For any node type not handled above, we recurse using
3634- * expression_tree_mutator, which will copy the node unchanged but try to
3635- * simplify its arguments (if any) using this routine. For example: we
3636- * cannot eliminate an ArrayRef node, but we might be able to simplify
3637- * constant expressions in its subscripts .
3665+ * For any node type not handled above, copy the node unchanged but
3666+ * const-simplify its subexpressions. This is the correct thing for node
3667+ * types whose behavior might change between planning and execution, such
3668+ * as CoerceToDomain. It's also a safe default for new node types not
3669+ * known to this routine .
36383670 */
3639- return expression_tree_mutator (node , eval_const_expressions_mutator ,
3640- (void * ) context );
3671+ return ece_generic_processing (node );
3672+ }
3673+
3674+ /*
3675+ * Subroutine for eval_const_expressions: check for non-Const nodes.
3676+ *
3677+ * We can abort recursion immediately on finding a non-Const node. This is
3678+ * critical for performance, else eval_const_expressions_mutator would take
3679+ * O(N^2) time on non-simplifiable trees. However, we do need to descend
3680+ * into List nodes since expression_tree_walker sometimes invokes the walker
3681+ * function directly on List subtrees.
3682+ */
3683+ static bool
3684+ contain_non_const_walker (Node * node , void * context )
3685+ {
3686+ if (node == NULL )
3687+ return false;
3688+ if (IsA (node , Const ))
3689+ return false;
3690+ if (IsA (node , List ))
3691+ return expression_tree_walker (node , contain_non_const_walker , context );
3692+ /* Otherwise, abort the tree traversal and return true */
3693+ return true;
3694+ }
3695+
3696+ /*
3697+ * Subroutine for eval_const_expressions: check if a function is OK to evaluate
3698+ */
3699+ static bool
3700+ ece_function_is_safe (Oid funcid , eval_const_expressions_context * context )
3701+ {
3702+ char provolatile = func_volatile (funcid );
3703+
3704+ /*
3705+ * Ordinarily we are only allowed to simplify immutable functions. But for
3706+ * purposes of estimation, we consider it okay to simplify functions that
3707+ * are merely stable; the risk that the result might change from planning
3708+ * time to execution time is worth taking in preference to not being able
3709+ * to estimate the value at all.
3710+ */
3711+ if (provolatile == PROVOLATILE_IMMUTABLE )
3712+ return true;
3713+ if (context -> estimate && provolatile == PROVOLATILE_STABLE )
3714+ return true;
3715+ return false;
36413716}
36423717
36433718/*
0 commit comments