aboutsummaryrefslogtreecommitdiff
path: root/src/backend/nodes/nodeFuncs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/nodes/nodeFuncs.c')
-rw-r--r--src/backend/nodes/nodeFuncs.c75
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);
}
/*