Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2072932

Browse files
committed
Suppress unnecessary RelabelType nodes in yet more cases.
Commit a477bfc fixed eval_const_expressions() to ensure that it didn't generate unnecessary RelabelType nodes, but I failed to notice that some other places in the planner had the same issue. Really noplace in the planner should be using plain makeRelabelType(), for fear of generating expressions that should be equal() to semantically equivalent trees, but aren't. An example is that because canonicalize_ec_expression() failed to be careful about this, we could end up with an equivalence class containing both a plain Const, and a Const-with-RelabelType representing exactly the same value. So far as I can tell this led to no visible misbehavior, but we did waste a bunch of cycles generating and evaluating "Const = Const-with-RelabelType" to prove such entries are redundant. Hence, move the support function added by a477bfc to where it can be more generally useful, and use it in the places where planner code previously used makeRelabelType. Back-patch to v12, like the previous patch. While I have no concrete evidence of any real misbehavior here, it's certainly possible that I overlooked a case where equivalent expressions that aren't equal() could cause a user-visible problem. In any case carrying extra RelabelType nodes through planning to execution isn't very desirable. Discussion: https://postgr.es/m/1311836.1597781384@sss.pgh.pa.us
1 parent 3e98c0b commit 2072932

File tree

5 files changed

+106
-119
lines changed

5 files changed

+106
-119
lines changed

src/backend/nodes/nodeFuncs.c

+62-13
Original file line numberDiff line numberDiff line change
@@ -575,27 +575,76 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
575575
return false;
576576
}
577577

578+
/*
579+
* applyRelabelType
580+
* Add a RelabelType node if needed to make the expression expose
581+
* the specified type, typmod, and collation.
582+
*
583+
* This is primarily intended to be used during planning. Therefore, it must
584+
* maintain the post-eval_const_expressions invariants that there are not
585+
* adjacent RelabelTypes, and that the tree is fully const-folded (hence,
586+
* we mustn't return a RelabelType atop a Const). If we do find a Const,
587+
* we'll modify it in-place if "overwrite_ok" is true; that should only be
588+
* passed as true if caller knows the Const is newly generated.
589+
*/
590+
Node *
591+
applyRelabelType(Node *arg, Oid rtype, int32 rtypmod, Oid rcollid,
592+
CoercionForm rformat, int rlocation, bool overwrite_ok)
593+
{
594+
/*
595+
* If we find stacked RelabelTypes (eg, from foo::int::oid) we can discard
596+
* all but the top one, and must do so to ensure that semantically
597+
* equivalent expressions are equal().
598+
*/
599+
while (arg && IsA(arg, RelabelType))
600+
arg = (Node *) ((RelabelType *) arg)->arg;
601+
602+
if (arg && IsA(arg, Const))
603+
{
604+
/* Modify the Const directly to preserve const-flatness. */
605+
Const *con = (Const *) arg;
606+
607+
if (!overwrite_ok)
608+
con = copyObject(con);
609+
con->consttype = rtype;
610+
con->consttypmod = rtypmod;
611+
con->constcollid = rcollid;
612+
/* We keep the Const's original location. */
613+
return (Node *) con;
614+
}
615+
else if (exprType(arg) == rtype &&
616+
exprTypmod(arg) == rtypmod &&
617+
exprCollation(arg) == rcollid)
618+
{
619+
/* Sometimes we find a nest of relabels that net out to nothing. */
620+
return arg;
621+
}
622+
else
623+
{
624+
/* Nope, gotta have a RelabelType. */
625+
RelabelType *newrelabel = makeNode(RelabelType);
626+
627+
newrelabel->arg = (Expr *) arg;
628+
newrelabel->resulttype = rtype;
629+
newrelabel->resulttypmod = rtypmod;
630+
newrelabel->resultcollid = rcollid;
631+
newrelabel->relabelformat = rformat;
632+
newrelabel->location = rlocation;
633+
return (Node *) newrelabel;
634+
}
635+
}
636+
578637
/*
579638
* relabel_to_typmod
580639
* Add a RelabelType node that changes just the typmod of the expression.
581640
*
582-
* This is primarily intended to be used during planning. Therefore, it
583-
* strips any existing RelabelType nodes to maintain the planner's invariant
584-
* that there are not adjacent RelabelTypes.
641+
* Convenience function for a common usage of applyRelabelType.
585642
*/
586643
Node *
587644
relabel_to_typmod(Node *expr, int32 typmod)
588645
{
589-
Oid type = exprType(expr);
590-
Oid coll = exprCollation(expr);
591-
592-
/* Strip any existing RelabelType node(s) */
593-
while (expr && IsA(expr, RelabelType))
594-
expr = (Node *) ((RelabelType *) expr)->arg;
595-
596-
/* Apply new typmod, preserving the previous exposed type and collation */
597-
return (Node *) makeRelabelType((Expr *) expr, type, typmod, coll,
598-
COERCE_EXPLICIT_CAST);
646+
return applyRelabelType(expr, exprType(expr), typmod, exprCollation(expr),
647+
COERCE_EXPLICIT_CAST, -1, false);
599648
}
600649

601650
/*

src/backend/optimizer/path/equivclass.c

+17-26
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,6 @@ process_equivalence(PlannerInfo *root,
490490
* work to not label the collation at all in EC members, but this is risky
491491
* since some parts of the system expect exprCollation() to deliver the
492492
* right answer for a sort key.)
493-
*
494-
* Note this code assumes that the expression has already been through
495-
* eval_const_expressions, so there are no CollateExprs and no redundant
496-
* RelabelTypes.
497493
*/
498494
Expr *
499495
canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
@@ -514,29 +510,24 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
514510
exprCollation((Node *) expr) != req_collation)
515511
{
516512
/*
517-
* Strip any existing RelabelType, then add a new one if needed. This
518-
* is to preserve the invariant of no redundant RelabelTypes.
519-
*
520-
* If we have to change the exposed type of the stripped expression,
521-
* set typmod to -1 (since the new type may not have the same typmod
522-
* interpretation). If we only have to change collation, preserve the
523-
* exposed typmod.
513+
* If we have to change the type of the expression, set typmod to -1,
514+
* since the new type may not have the same typmod interpretation.
515+
* When we only have to change collation, preserve the exposed typmod.
516+
*/
517+
int32 req_typmod;
518+
519+
if (expr_type != req_type)
520+
req_typmod = -1;
521+
else
522+
req_typmod = exprTypmod((Node *) expr);
523+
524+
/*
525+
* Use applyRelabelType so that we preserve const-flatness. This is
526+
* important since eval_const_expressions has already been applied.
524527
*/
525-
while (expr && IsA(expr, RelabelType))
526-
expr = (Expr *) ((RelabelType *) expr)->arg;
527-
528-
if (exprType((Node *) expr) != req_type)
529-
expr = (Expr *) makeRelabelType(expr,
530-
req_type,
531-
-1,
532-
req_collation,
533-
COERCE_IMPLICIT_CAST);
534-
else if (exprCollation((Node *) expr) != req_collation)
535-
expr = (Expr *) makeRelabelType(expr,
536-
req_type,
537-
exprTypmod((Node *) expr),
538-
req_collation,
539-
COERCE_IMPLICIT_CAST);
528+
expr = (Expr *) applyRelabelType((Node *) expr,
529+
req_type, req_typmod, req_collation,
530+
COERCE_IMPLICIT_CAST, -1, false);
540531
}
541532

542533
return expr;

src/backend/optimizer/prep/prepunion.c

+3-7
Original file line numberDiff line numberDiff line change
@@ -1200,13 +1200,9 @@ generate_setop_tlist(List *colTypes, List *colCollations,
12001200
* will reach the executor without any further processing.
12011201
*/
12021202
if (exprCollation(expr) != colColl)
1203-
{
1204-
expr = (Node *) makeRelabelType((Expr *) expr,
1205-
exprType(expr),
1206-
exprTypmod(expr),
1207-
colColl,
1208-
COERCE_IMPLICIT_CAST);
1209-
}
1203+
expr = applyRelabelType(expr,
1204+
exprType(expr), exprTypmod(expr), colColl,
1205+
COERCE_IMPLICIT_CAST, -1, false);
12101206

12111207
tle = makeTargetEntry((Expr *) expr,
12121208
(AttrNumber) resno++,

src/backend/optimizer/util/clauses.c

+21-73
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,6 @@ static Node *eval_const_expressions_mutator(Node *node,
120120
static bool contain_non_const_walker(Node *node, void *context);
121121
static bool ece_function_is_safe(Oid funcid,
122122
eval_const_expressions_context *context);
123-
static Node *apply_const_relabel(Node *arg, Oid rtype,
124-
int32 rtypmod, Oid rcollid,
125-
CoercionForm rformat, int rlocation);
126123
static List *simplify_or_arguments(List *args,
127124
eval_const_expressions_context *context,
128125
bool *haveNull, bool *forceTrue);
@@ -2819,12 +2816,13 @@ eval_const_expressions_mutator(Node *node,
28192816
arg = eval_const_expressions_mutator((Node *) relabel->arg,
28202817
context);
28212818
/* ... and attach a new RelabelType node, if needed */
2822-
return apply_const_relabel(arg,
2823-
relabel->resulttype,
2824-
relabel->resulttypmod,
2825-
relabel->resultcollid,
2826-
relabel->relabelformat,
2827-
relabel->location);
2819+
return applyRelabelType(arg,
2820+
relabel->resulttype,
2821+
relabel->resulttypmod,
2822+
relabel->resultcollid,
2823+
relabel->relabelformat,
2824+
relabel->location,
2825+
true);
28282826
}
28292827
case T_CoerceViaIO:
28302828
{
@@ -2971,12 +2969,13 @@ eval_const_expressions_mutator(Node *node,
29712969
arg = eval_const_expressions_mutator((Node *) collate->arg,
29722970
context);
29732971
/* ... and attach a new RelabelType node, if needed */
2974-
return apply_const_relabel(arg,
2975-
exprType(arg),
2976-
exprTypmod(arg),
2977-
collate->collOid,
2978-
COERCE_IMPLICIT_CAST,
2979-
collate->location);
2972+
return applyRelabelType(arg,
2973+
exprType(arg),
2974+
exprTypmod(arg),
2975+
collate->collOid,
2976+
COERCE_IMPLICIT_CAST,
2977+
collate->location,
2978+
true);
29802979
}
29812980
case T_CaseExpr:
29822981
{
@@ -3478,12 +3477,13 @@ eval_const_expressions_mutator(Node *node,
34783477
cdomain->resulttype);
34793478

34803479
/* Generate RelabelType to substitute for CoerceToDomain */
3481-
return apply_const_relabel(arg,
3482-
cdomain->resulttype,
3483-
cdomain->resulttypmod,
3484-
cdomain->resultcollid,
3485-
cdomain->coercionformat,
3486-
cdomain->location);
3480+
return applyRelabelType(arg,
3481+
cdomain->resulttype,
3482+
cdomain->resulttypmod,
3483+
cdomain->resultcollid,
3484+
cdomain->coercionformat,
3485+
cdomain->location,
3486+
true);
34873487
}
34883488

34893489
newcdomain = makeNode(CoerceToDomain);
@@ -3616,58 +3616,6 @@ ece_function_is_safe(Oid funcid, eval_const_expressions_context *context)
36163616
return false;
36173617
}
36183618

3619-
/*
3620-
* Subroutine for eval_const_expressions: apply RelabelType if needed
3621-
*/
3622-
static Node *
3623-
apply_const_relabel(Node *arg, Oid rtype, int32 rtypmod, Oid rcollid,
3624-
CoercionForm rformat, int rlocation)
3625-
{
3626-
/*
3627-
* If we find stacked RelabelTypes (eg, from foo::int::oid) we can discard
3628-
* all but the top one, and must do so to ensure that semantically
3629-
* equivalent expressions are equal().
3630-
*/
3631-
while (arg && IsA(arg, RelabelType))
3632-
arg = (Node *) ((RelabelType *) arg)->arg;
3633-
3634-
if (arg && IsA(arg, Const))
3635-
{
3636-
/*
3637-
* If it's a Const, just modify it in-place; since this is part of
3638-
* eval_const_expressions, we want to end up with a simple Const not
3639-
* an expression tree. We assume the Const is newly generated and
3640-
* hence safe to modify.
3641-
*/
3642-
Const *con = (Const *) arg;
3643-
3644-
con->consttype = rtype;
3645-
con->consttypmod = rtypmod;
3646-
con->constcollid = rcollid;
3647-
return (Node *) con;
3648-
}
3649-
else if (exprType(arg) == rtype &&
3650-
exprTypmod(arg) == rtypmod &&
3651-
exprCollation(arg) == rcollid)
3652-
{
3653-
/* Sometimes we find a nest of relabels that net out to nothing. */
3654-
return arg;
3655-
}
3656-
else
3657-
{
3658-
/* Nope, gotta have a RelabelType. */
3659-
RelabelType *newrelabel = makeNode(RelabelType);
3660-
3661-
newrelabel->arg = (Expr *) arg;
3662-
newrelabel->resulttype = rtype;
3663-
newrelabel->resulttypmod = rtypmod;
3664-
newrelabel->resultcollid = rcollid;
3665-
newrelabel->relabelformat = rformat;
3666-
newrelabel->location = rlocation;
3667-
return (Node *) newrelabel;
3668-
}
3669-
}
3670-
36713619
/*
36723620
* Subroutine for eval_const_expressions: process arguments of an OR clause
36733621
*

src/include/nodes/nodeFuncs.h

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ typedef bool (*check_function_callback) (Oid func_id, void *context);
3636
extern Oid exprType(const Node *expr);
3737
extern int32 exprTypmod(const Node *expr);
3838
extern bool exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod);
39+
extern Node *applyRelabelType(Node *arg, Oid rtype, int32 rtypmod, Oid rcollid,
40+
CoercionForm rformat, int rlocation,
41+
bool overwrite_ok);
3942
extern Node *relabel_to_typmod(Node *expr, int32 typmod);
4043
extern Node *strip_implicit_coercions(Node *node);
4144
extern bool expression_returns_set(Node *clause);

0 commit comments

Comments
 (0)