Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3decd15

Browse files
committed
Teach eval_const_expressions() to handle some more cases.
Add some infrastructure (mostly macros) to make it easier to writetypical cases for constant-expression simplification. Add simplificationprocessing for ArrayRef, RowExpr, and ScalarArrayOpExpr node types,which formerly went unsimplified even if all their inputs were constants.Also teach it to simplify FieldSelect from a composite constant.Make use of the new infrastructure to reduce the amount of code neededfor the existing ArrayExpr and ArrayCoerceExpr cases.One existing test case changes output as a result of the fact thatRowExpr can now be folded to a constant. All the new code is exercisedby existing test cases according to gcov, so I feel no need to addadditional tests.Tom Lane, reviewed by Dmitry DolgovDiscussion:https://postgr.es/m/3be3b82c-e29c-b674-2163-bf47d98817b1@iki.fi
1 parent35c0754 commit3decd15

File tree

2 files changed

+150
-75
lines changed

2 files changed

+150
-75
lines changed

‎src/backend/optimizer/util/clauses.c

Lines changed: 147 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ static List *find_nonnullable_vars_walker(Node *node, bool top_level);
115115
staticboolis_strict_saop(ScalarArrayOpExpr*expr,boolfalseOK);
116116
staticNode*eval_const_expressions_mutator(Node*node,
117117
eval_const_expressions_context*context);
118+
staticboolcontain_non_const_walker(Node*node,void*context);
119+
staticboolece_function_is_safe(Oidfuncid,
120+
eval_const_expressions_context*context);
118121
staticList*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
returneval_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+
#defineece_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+
#defineece_all_arguments_const(node) \
2527+
(!expression_tree_walker((Node *) (node), contain_non_const_walker, NULL))
2528+
2529+
/* Generic macro for applying evaluate_expr */
2530+
#defineece_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+
*/
25052539
staticNode*
25062540
eval_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+
caseT_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+
returnece_evaluate_expr(saop);
2884+
return (Node*)saop;
2885+
}
28332886
caseT_BoolExpr:
28342887
{
28352888
BoolExpr*expr= (BoolExpr*)node;
@@ -3054,47 +3107,24 @@ eval_const_expressions_mutator(Node *node,
30543107
}
30553108
caseT_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 andtheper-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+
returnece_evaluate_expr(ac);
3127+
return (Node*)ac;
30983128
}
30993129
caseT_CollateExpr:
31003130
{
@@ -3286,41 +3316,22 @@ eval_const_expressions_mutator(Node *node,
32863316
else
32873317
returncopyObject(node);
32883318
}
3319+
caseT_ArrayRef:
32893320
caseT_ArrayExpr:
3321+
caseT_RowExpr:
32903322
{
3291-
ArrayExpr*arrayexpr= (ArrayExpr*)node;
3292-
ArrayExpr*newarray;
3293-
boolall_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+
returnece_evaluate_expr(node);
3334+
returnnode;
33243335
}
33253336
caseT_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+
returnece_evaluate_expr(newfselect);
3489+
}
34653490
return (Node*)newfselect;
34663491
}
34673492
caseT_NullTest:
@@ -3557,6 +3582,13 @@ eval_const_expressions_mutator(Node *node,
35573582
}
35583583
caseT_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-
returnexpression_tree_mutator(node,eval_const_expressions_mutator,
3640-
(void*)context);
3671+
returnece_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+
staticbool
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+
returnexpression_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+
staticbool
3700+
ece_function_is_safe(Oidfuncid,eval_const_expressions_context*context)
3701+
{
3702+
charprovolatile=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
/*

‎src/test/regress/expected/rowtypes.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,10 @@ ERROR: cannot compare dissimilar column types bigint and integer at record colu
307307
explain (costs off)
308308
select * from int8_tbl i8
309309
where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
310-
QUERY PLAN
311-
-----------------------------------------------------------------------------------------------------------------
310+
QUERY PLAN
311+
-------------------------------------------------------------------------------
312312
Seq Scan on int8_tbl i8
313-
Filter: (i8.* = ANY (ARRAY[ROW('123'::bigint, '456'::bigint)::int8_tbl, '(4567890123456789,123)'::int8_tbl]))
313+
Filter: (i8.* = ANY ('{"(123,456)","(4567890123456789,123)"}'::int8_tbl[]))
314314
(2 rows)
315315

316316
select * from int8_tbl i8

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp