mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
Suppress unnecessary RelabelType nodes in yet more cases.
Commita477bfc1d
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 bya477bfc1d
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
This commit is contained in:
@ -575,27 +575,76 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* applyRelabelType
|
||||
* Add a RelabelType node if needed to make the expression expose
|
||||
* the specified type, typmod, and collation.
|
||||
*
|
||||
* This is primarily intended to be used during planning. Therefore, it must
|
||||
* maintain the post-eval_const_expressions invariants that there are not
|
||||
* adjacent RelabelTypes, and that the tree is fully const-folded (hence,
|
||||
* we mustn't return a RelabelType atop a Const). If we do find a Const,
|
||||
* we'll modify it in-place if "overwrite_ok" is true; that should only be
|
||||
* passed as true if caller knows the Const is newly generated.
|
||||
*/
|
||||
Node *
|
||||
applyRelabelType(Node *arg, Oid rtype, int32 rtypmod, Oid rcollid,
|
||||
CoercionForm rformat, int rlocation, bool overwrite_ok)
|
||||
{
|
||||
/*
|
||||
* If we find stacked RelabelTypes (eg, from foo::int::oid) we can discard
|
||||
* all but the top one, and must do so to ensure that semantically
|
||||
* equivalent expressions are equal().
|
||||
*/
|
||||
while (arg && IsA(arg, RelabelType))
|
||||
arg = (Node *) ((RelabelType *) arg)->arg;
|
||||
|
||||
if (arg && IsA(arg, Const))
|
||||
{
|
||||
/* Modify the Const directly to preserve const-flatness. */
|
||||
Const *con = (Const *) arg;
|
||||
|
||||
if (!overwrite_ok)
|
||||
con = copyObject(con);
|
||||
con->consttype = rtype;
|
||||
con->consttypmod = rtypmod;
|
||||
con->constcollid = rcollid;
|
||||
/* We keep the Const's original location. */
|
||||
return (Node *) con;
|
||||
}
|
||||
else if (exprType(arg) == rtype &&
|
||||
exprTypmod(arg) == rtypmod &&
|
||||
exprCollation(arg) == rcollid)
|
||||
{
|
||||
/* Sometimes we find a nest of relabels that net out to nothing. */
|
||||
return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nope, gotta have a RelabelType. */
|
||||
RelabelType *newrelabel = makeNode(RelabelType);
|
||||
|
||||
newrelabel->arg = (Expr *) arg;
|
||||
newrelabel->resulttype = rtype;
|
||||
newrelabel->resulttypmod = rtypmod;
|
||||
newrelabel->resultcollid = rcollid;
|
||||
newrelabel->relabelformat = rformat;
|
||||
newrelabel->location = rlocation;
|
||||
return (Node *) newrelabel;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* relabel_to_typmod
|
||||
* Add a RelabelType node that changes just the typmod of the expression.
|
||||
*
|
||||
* This is primarily intended to be used during planning. Therefore, it
|
||||
* strips any existing RelabelType nodes to maintain the planner's invariant
|
||||
* that there are not adjacent RelabelTypes.
|
||||
* Convenience function for a common usage of applyRelabelType.
|
||||
*/
|
||||
Node *
|
||||
relabel_to_typmod(Node *expr, int32 typmod)
|
||||
{
|
||||
Oid type = exprType(expr);
|
||||
Oid coll = exprCollation(expr);
|
||||
|
||||
/* Strip any existing RelabelType node(s) */
|
||||
while (expr && IsA(expr, RelabelType))
|
||||
expr = (Node *) ((RelabelType *) expr)->arg;
|
||||
|
||||
/* Apply new typmod, preserving the previous exposed type and collation */
|
||||
return (Node *) makeRelabelType((Expr *) expr, type, typmod, coll,
|
||||
COERCE_EXPLICIT_CAST);
|
||||
return applyRelabelType(expr, exprType(expr), typmod, exprCollation(expr),
|
||||
COERCE_EXPLICIT_CAST, -1, false);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user