diff options
Diffstat (limited to 'src/backend/nodes/nodeFuncs.c')
-rw-r--r-- | src/backend/nodes/nodeFuncs.c | 75 |
1 files changed, 62 insertions, 13 deletions
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 6924a4920ef..64c7eca95b0 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -577,26 +577,75 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod) } /* + * 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); } /* |