summaryrefslogtreecommitdiffstats
path: root/src/backend/nodes/nodeFuncs.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/backend/nodes/nodeFuncs.c4116
1 files changed, 4116 insertions, 0 deletions
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
new file mode 100644
index 0000000..94b697b
--- /dev/null
+++ b/src/backend/nodes/nodeFuncs.c
@@ -0,0 +1,4116 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeFuncs.c
+ * Various general-purpose manipulations of Node trees
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/nodes/nodeFuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "miscadmin.h"
+#include "nodes/execnodes.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "nodes/pathnodes.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+
+static bool expression_returns_set_walker(Node *node, void *context);
+static int leftmostLoc(int loc1, int loc2);
+static bool fix_opfuncids_walker(Node *node, void *context);
+static bool planstate_walk_subplans(List *plans, bool (*walker) (),
+ void *context);
+static bool planstate_walk_members(PlanState **planstates, int nplans,
+ bool (*walker) (), void *context);
+
+
+/*
+ * exprType -
+ * returns the Oid of the type of the expression's result.
+ */
+Oid
+exprType(const Node *expr)
+{
+ Oid type;
+
+ if (!expr)
+ return InvalidOid;
+
+ switch (nodeTag(expr))
+ {
+ case T_Var:
+ type = ((const Var *) expr)->vartype;
+ break;
+ case T_Const:
+ type = ((const Const *) expr)->consttype;
+ break;
+ case T_Param:
+ type = ((const Param *) expr)->paramtype;
+ break;
+ case T_Aggref:
+ type = ((const Aggref *) expr)->aggtype;
+ break;
+ case T_GroupingFunc:
+ type = INT4OID;
+ break;
+ case T_WindowFunc:
+ type = ((const WindowFunc *) expr)->wintype;
+ break;
+ case T_SubscriptingRef:
+ type = ((const SubscriptingRef *) expr)->refrestype;
+ break;
+ case T_FuncExpr:
+ type = ((const FuncExpr *) expr)->funcresulttype;
+ break;
+ case T_NamedArgExpr:
+ type = exprType((Node *) ((const NamedArgExpr *) expr)->arg);
+ break;
+ case T_OpExpr:
+ type = ((const OpExpr *) expr)->opresulttype;
+ break;
+ case T_DistinctExpr:
+ type = ((const DistinctExpr *) expr)->opresulttype;
+ break;
+ case T_NullIfExpr:
+ type = ((const NullIfExpr *) expr)->opresulttype;
+ break;
+ case T_ScalarArrayOpExpr:
+ type = BOOLOID;
+ break;
+ case T_BoolExpr:
+ type = BOOLOID;
+ break;
+ case T_SubLink:
+ {
+ const SubLink *sublink = (const SubLink *) expr;
+
+ if (sublink->subLinkType == EXPR_SUBLINK ||
+ sublink->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the type of the subselect's first target column */
+ Query *qtree = (Query *) sublink->subselect;
+ TargetEntry *tent;
+
+ if (!qtree || !IsA(qtree, Query))
+ elog(ERROR, "cannot get type for untransformed sublink");
+ tent = linitial_node(TargetEntry, qtree->targetList);
+ Assert(!tent->resjunk);
+ type = exprType((Node *) tent->expr);
+ if (sublink->subLinkType == ARRAY_SUBLINK)
+ {
+ type = get_promoted_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(exprType((Node *) tent->expr)))));
+ }
+ }
+ else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
+ {
+ /* MULTIEXPR is always considered to return RECORD */
+ type = RECORDOID;
+ }
+ else
+ {
+ /* for all other sublink types, result is boolean */
+ type = BOOLOID;
+ }
+ }
+ break;
+ case T_SubPlan:
+ {
+ const SubPlan *subplan = (const SubPlan *) expr;
+
+ if (subplan->subLinkType == EXPR_SUBLINK ||
+ subplan->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the type of the subselect's first target column */
+ type = subplan->firstColType;
+ if (subplan->subLinkType == ARRAY_SUBLINK)
+ {
+ type = get_promoted_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(subplan->firstColType))));
+ }
+ }
+ else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
+ {
+ /* MULTIEXPR is always considered to return RECORD */
+ type = RECORDOID;
+ }
+ else
+ {
+ /* for all other subplan types, result is boolean */
+ type = BOOLOID;
+ }
+ }
+ break;
+ case T_AlternativeSubPlan:
+ {
+ const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr;
+
+ /* subplans should all return the same thing */
+ type = exprType((Node *) linitial(asplan->subplans));
+ }
+ break;
+ case T_FieldSelect:
+ type = ((const FieldSelect *) expr)->resulttype;
+ break;
+ case T_FieldStore:
+ type = ((const FieldStore *) expr)->resulttype;
+ break;
+ case T_RelabelType:
+ type = ((const RelabelType *) expr)->resulttype;
+ break;
+ case T_CoerceViaIO:
+ type = ((const CoerceViaIO *) expr)->resulttype;
+ break;
+ case T_ArrayCoerceExpr:
+ type = ((const ArrayCoerceExpr *) expr)->resulttype;
+ break;
+ case T_ConvertRowtypeExpr:
+ type = ((const ConvertRowtypeExpr *) expr)->resulttype;
+ break;
+ case T_CollateExpr:
+ type = exprType((Node *) ((const CollateExpr *) expr)->arg);
+ break;
+ case T_CaseExpr:
+ type = ((const CaseExpr *) expr)->casetype;
+ break;
+ case T_CaseTestExpr:
+ type = ((const CaseTestExpr *) expr)->typeId;
+ break;
+ case T_ArrayExpr:
+ type = ((const ArrayExpr *) expr)->array_typeid;
+ break;
+ case T_RowExpr:
+ type = ((const RowExpr *) expr)->row_typeid;
+ break;
+ case T_RowCompareExpr:
+ type = BOOLOID;
+ break;
+ case T_CoalesceExpr:
+ type = ((const CoalesceExpr *) expr)->coalescetype;
+ break;
+ case T_MinMaxExpr:
+ type = ((const MinMaxExpr *) expr)->minmaxtype;
+ break;
+ case T_SQLValueFunction:
+ type = ((const SQLValueFunction *) expr)->type;
+ break;
+ case T_XmlExpr:
+ if (((const XmlExpr *) expr)->op == IS_DOCUMENT)
+ type = BOOLOID;
+ else if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE)
+ type = TEXTOID;
+ else
+ type = XMLOID;
+ break;
+ case T_NullTest:
+ type = BOOLOID;
+ break;
+ case T_BooleanTest:
+ type = BOOLOID;
+ break;
+ case T_CoerceToDomain:
+ type = ((const CoerceToDomain *) expr)->resulttype;
+ break;
+ case T_CoerceToDomainValue:
+ type = ((const CoerceToDomainValue *) expr)->typeId;
+ break;
+ case T_SetToDefault:
+ type = ((const SetToDefault *) expr)->typeId;
+ break;
+ case T_CurrentOfExpr:
+ type = BOOLOID;
+ break;
+ case T_NextValueExpr:
+ type = ((const NextValueExpr *) expr)->typeId;
+ break;
+ case T_InferenceElem:
+ {
+ const InferenceElem *n = (const InferenceElem *) expr;
+
+ type = exprType((Node *) n->expr);
+ }
+ break;
+ case T_PlaceHolderVar:
+ type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
+ type = InvalidOid; /* keep compiler quiet */
+ break;
+ }
+ return type;
+}
+
+/*
+ * exprTypmod -
+ * returns the type-specific modifier of the expression's result type,
+ * if it can be determined. In many cases, it can't and we return -1.
+ */
+int32
+exprTypmod(const Node *expr)
+{
+ if (!expr)
+ return -1;
+
+ switch (nodeTag(expr))
+ {
+ case T_Var:
+ return ((const Var *) expr)->vartypmod;
+ case T_Const:
+ return ((const Const *) expr)->consttypmod;
+ case T_Param:
+ return ((const Param *) expr)->paramtypmod;
+ case T_SubscriptingRef:
+ return ((const SubscriptingRef *) expr)->reftypmod;
+ case T_FuncExpr:
+ {
+ int32 coercedTypmod;
+
+ /* Be smart about length-coercion functions... */
+ if (exprIsLengthCoercion(expr, &coercedTypmod))
+ return coercedTypmod;
+ }
+ break;
+ case T_NamedArgExpr:
+ return exprTypmod((Node *) ((const NamedArgExpr *) expr)->arg);
+ case T_NullIfExpr:
+ {
+ /*
+ * Result is either first argument or NULL, so we can report
+ * first argument's typmod if known.
+ */
+ const NullIfExpr *nexpr = (const NullIfExpr *) expr;
+
+ return exprTypmod((Node *) linitial(nexpr->args));
+ }
+ break;
+ case T_SubLink:
+ {
+ const SubLink *sublink = (const SubLink *) expr;
+
+ if (sublink->subLinkType == EXPR_SUBLINK ||
+ sublink->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the typmod of the subselect's first target column */
+ Query *qtree = (Query *) sublink->subselect;
+ TargetEntry *tent;
+
+ if (!qtree || !IsA(qtree, Query))
+ elog(ERROR, "cannot get type for untransformed sublink");
+ tent = linitial_node(TargetEntry, qtree->targetList);
+ Assert(!tent->resjunk);
+ return exprTypmod((Node *) tent->expr);
+ /* note we don't need to care if it's an array */
+ }
+ /* otherwise, result is RECORD or BOOLEAN, typmod is -1 */
+ }
+ break;
+ case T_SubPlan:
+ {
+ const SubPlan *subplan = (const SubPlan *) expr;
+
+ if (subplan->subLinkType == EXPR_SUBLINK ||
+ subplan->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the typmod of the subselect's first target column */
+ /* note we don't need to care if it's an array */
+ return subplan->firstColTypmod;
+ }
+ /* otherwise, result is RECORD or BOOLEAN, typmod is -1 */
+ }
+ break;
+ case T_AlternativeSubPlan:
+ {
+ const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr;
+
+ /* subplans should all return the same thing */
+ return exprTypmod((Node *) linitial(asplan->subplans));
+ }
+ break;
+ case T_FieldSelect:
+ return ((const FieldSelect *) expr)->resulttypmod;
+ case T_RelabelType:
+ return ((const RelabelType *) expr)->resulttypmod;
+ case T_ArrayCoerceExpr:
+ return ((const ArrayCoerceExpr *) expr)->resulttypmod;
+ case T_CollateExpr:
+ return exprTypmod((Node *) ((const CollateExpr *) expr)->arg);
+ case T_CaseExpr:
+ {
+ /*
+ * If all the alternatives agree on type/typmod, return that
+ * typmod, else use -1
+ */
+ const CaseExpr *cexpr = (const CaseExpr *) expr;
+ Oid casetype = cexpr->casetype;
+ int32 typmod;
+ ListCell *arg;
+
+ if (!cexpr->defresult)
+ return -1;
+ if (exprType((Node *) cexpr->defresult) != casetype)
+ return -1;
+ typmod = exprTypmod((Node *) cexpr->defresult);
+ if (typmod < 0)
+ return -1; /* no point in trying harder */
+ foreach(arg, cexpr->args)
+ {
+ CaseWhen *w = lfirst_node(CaseWhen, arg);
+
+ if (exprType((Node *) w->result) != casetype)
+ return -1;
+ if (exprTypmod((Node *) w->result) != typmod)
+ return -1;
+ }
+ return typmod;
+ }
+ break;
+ case T_CaseTestExpr:
+ return ((const CaseTestExpr *) expr)->typeMod;
+ case T_ArrayExpr:
+ {
+ /*
+ * If all the elements agree on type/typmod, return that
+ * typmod, else use -1
+ */
+ const ArrayExpr *arrayexpr = (const ArrayExpr *) expr;
+ Oid commontype;
+ int32 typmod;
+ ListCell *elem;
+
+ if (arrayexpr->elements == NIL)
+ return -1;
+ typmod = exprTypmod((Node *) linitial(arrayexpr->elements));
+ if (typmod < 0)
+ return -1; /* no point in trying harder */
+ if (arrayexpr->multidims)
+ commontype = arrayexpr->array_typeid;
+ else
+ commontype = arrayexpr->element_typeid;
+ foreach(elem, arrayexpr->elements)
+ {
+ Node *e = (Node *) lfirst(elem);
+
+ if (exprType(e) != commontype)
+ return -1;
+ if (exprTypmod(e) != typmod)
+ return -1;
+ }
+ return typmod;
+ }
+ break;
+ case T_CoalesceExpr:
+ {
+ /*
+ * If all the alternatives agree on type/typmod, return that
+ * typmod, else use -1
+ */
+ const CoalesceExpr *cexpr = (const CoalesceExpr *) expr;
+ Oid coalescetype = cexpr->coalescetype;
+ int32 typmod;
+ ListCell *arg;
+
+ if (exprType((Node *) linitial(cexpr->args)) != coalescetype)
+ return -1;
+ typmod = exprTypmod((Node *) linitial(cexpr->args));
+ if (typmod < 0)
+ return -1; /* no point in trying harder */
+ for_each_from(arg, cexpr->args, 1)
+ {
+ Node *e = (Node *) lfirst(arg);
+
+ if (exprType(e) != coalescetype)
+ return -1;
+ if (exprTypmod(e) != typmod)
+ return -1;
+ }
+ return typmod;
+ }
+ break;
+ case T_MinMaxExpr:
+ {
+ /*
+ * If all the alternatives agree on type/typmod, return that
+ * typmod, else use -1
+ */
+ const MinMaxExpr *mexpr = (const MinMaxExpr *) expr;
+ Oid minmaxtype = mexpr->minmaxtype;
+ int32 typmod;
+ ListCell *arg;
+
+ if (exprType((Node *) linitial(mexpr->args)) != minmaxtype)
+ return -1;
+ typmod = exprTypmod((Node *) linitial(mexpr->args));
+ if (typmod < 0)
+ return -1; /* no point in trying harder */
+ for_each_from(arg, mexpr->args, 1)
+ {
+ Node *e = (Node *) lfirst(arg);
+
+ if (exprType(e) != minmaxtype)
+ return -1;
+ if (exprTypmod(e) != typmod)
+ return -1;
+ }
+ return typmod;
+ }
+ break;
+ case T_SQLValueFunction:
+ return ((const SQLValueFunction *) expr)->typmod;
+ case T_CoerceToDomain:
+ return ((const CoerceToDomain *) expr)->resulttypmod;
+ case T_CoerceToDomainValue:
+ return ((const CoerceToDomainValue *) expr)->typeMod;
+ case T_SetToDefault:
+ return ((const SetToDefault *) expr)->typeMod;
+ case T_PlaceHolderVar:
+ return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+ default:
+ break;
+ }
+ return -1;
+}
+
+/*
+ * exprIsLengthCoercion
+ * Detect whether an expression tree is an application of a datatype's
+ * typmod-coercion function. Optionally extract the result's typmod.
+ *
+ * If coercedTypmod is not NULL, the typmod is stored there if the expression
+ * is a length-coercion function, else -1 is stored there.
+ *
+ * Note that a combined type-and-length coercion will be treated as a
+ * length coercion by this routine.
+ */
+bool
+exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
+{
+ if (coercedTypmod != NULL)
+ *coercedTypmod = -1; /* default result on failure */
+
+ /*
+ * Scalar-type length coercions are FuncExprs, array-type length coercions
+ * are ArrayCoerceExprs
+ */
+ if (expr && IsA(expr, FuncExpr))
+ {
+ const FuncExpr *func = (const FuncExpr *) expr;
+ int nargs;
+ Const *second_arg;
+
+ /*
+ * If it didn't come from a coercion context, reject.
+ */
+ if (func->funcformat != COERCE_EXPLICIT_CAST &&
+ func->funcformat != COERCE_IMPLICIT_CAST)
+ return false;
+
+ /*
+ * If it's not a two-argument or three-argument function with the
+ * second argument being an int4 constant, it can't have been created
+ * from a length coercion (it must be a type coercion, instead).
+ */
+ nargs = list_length(func->args);
+ if (nargs < 2 || nargs > 3)
+ return false;
+
+ second_arg = (Const *) lsecond(func->args);
+ if (!IsA(second_arg, Const) ||
+ second_arg->consttype != INT4OID ||
+ second_arg->constisnull)
+ return false;
+
+ /*
+ * OK, it is indeed a length-coercion function.
+ */
+ if (coercedTypmod != NULL)
+ *coercedTypmod = DatumGetInt32(second_arg->constvalue);
+
+ return true;
+ }
+
+ if (expr && IsA(expr, ArrayCoerceExpr))
+ {
+ const ArrayCoerceExpr *acoerce = (const ArrayCoerceExpr *) expr;
+
+ /* It's not a length coercion unless there's a nondefault typmod */
+ if (acoerce->resulttypmod < 0)
+ return false;
+
+ /*
+ * OK, it is indeed a length-coercion expression.
+ */
+ if (coercedTypmod != NULL)
+ *coercedTypmod = acoerce->resulttypmod;
+
+ return true;
+ }
+
+ 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.
+ *
+ * Convenience function for a common usage of applyRelabelType.
+ */
+Node *
+relabel_to_typmod(Node *expr, int32 typmod)
+{
+ return applyRelabelType(expr, exprType(expr), typmod, exprCollation(expr),
+ COERCE_EXPLICIT_CAST, -1, false);
+}
+
+/*
+ * strip_implicit_coercions: remove implicit coercions at top level of tree
+ *
+ * This doesn't modify or copy the input expression tree, just return a
+ * pointer to a suitable place within it.
+ *
+ * Note: there isn't any useful thing we can do with a RowExpr here, so
+ * just return it unchanged, even if it's marked as an implicit coercion.
+ */
+Node *
+strip_implicit_coercions(Node *node)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, FuncExpr))
+ {
+ FuncExpr *f = (FuncExpr *) node;
+
+ if (f->funcformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions(linitial(f->args));
+ }
+ else if (IsA(node, RelabelType))
+ {
+ RelabelType *r = (RelabelType *) node;
+
+ if (r->relabelformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) r->arg);
+ }
+ else if (IsA(node, CoerceViaIO))
+ {
+ CoerceViaIO *c = (CoerceViaIO *) node;
+
+ if (c->coerceformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) c->arg);
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *c = (ArrayCoerceExpr *) node;
+
+ if (c->coerceformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) c->arg);
+ }
+ else if (IsA(node, ConvertRowtypeExpr))
+ {
+ ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node;
+
+ if (c->convertformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) c->arg);
+ }
+ else if (IsA(node, CoerceToDomain))
+ {
+ CoerceToDomain *c = (CoerceToDomain *) node;
+
+ if (c->coercionformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) c->arg);
+ }
+ return node;
+}
+
+/*
+ * expression_returns_set
+ * Test whether an expression returns a set result.
+ *
+ * Because we use expression_tree_walker(), this can also be applied to
+ * whole targetlists; it'll produce true if any one of the tlist items
+ * returns a set.
+ */
+bool
+expression_returns_set(Node *clause)
+{
+ return expression_returns_set_walker(clause, NULL);
+}
+
+static bool
+expression_returns_set_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, FuncExpr))
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+
+ if (expr->funcretset)
+ return true;
+ /* else fall through to check args */
+ }
+ if (IsA(node, OpExpr))
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ if (expr->opretset)
+ return true;
+ /* else fall through to check args */
+ }
+
+ /*
+ * If you add any more cases that return sets, also fix
+ * expression_returns_set_rows() in clauses.c and IS_SRF_CALL() in
+ * tlist.c.
+ */
+
+ /* Avoid recursion for some cases that parser checks not to return a set */
+ if (IsA(node, Aggref))
+ return false;
+ if (IsA(node, GroupingFunc))
+ return false;
+ if (IsA(node, WindowFunc))
+ return false;
+
+ return expression_tree_walker(node, expression_returns_set_walker,
+ context);
+}
+
+
+/*
+ * exprCollation -
+ * returns the Oid of the collation of the expression's result.
+ *
+ * Note: expression nodes that can invoke functions generally have an
+ * "inputcollid" field, which is what the function should use as collation.
+ * That is the resolved common collation of the node's inputs. It is often
+ * but not always the same as the result collation; in particular, if the
+ * function produces a non-collatable result type from collatable inputs
+ * or vice versa, the two are different.
+ */
+Oid
+exprCollation(const Node *expr)
+{
+ Oid coll;
+
+ if (!expr)
+ return InvalidOid;
+
+ switch (nodeTag(expr))
+ {
+ case T_Var:
+ coll = ((const Var *) expr)->varcollid;
+ break;
+ case T_Const:
+ coll = ((const Const *) expr)->constcollid;
+ break;
+ case T_Param:
+ coll = ((const Param *) expr)->paramcollid;
+ break;
+ case T_Aggref:
+ coll = ((const Aggref *) expr)->aggcollid;
+ break;
+ case T_GroupingFunc:
+ coll = InvalidOid;
+ break;
+ case T_WindowFunc:
+ coll = ((const WindowFunc *) expr)->wincollid;
+ break;
+ case T_SubscriptingRef:
+ coll = ((const SubscriptingRef *) expr)->refcollid;
+ break;
+ case T_FuncExpr:
+ coll = ((const FuncExpr *) expr)->funccollid;
+ break;
+ case T_NamedArgExpr:
+ coll = exprCollation((Node *) ((const NamedArgExpr *) expr)->arg);
+ break;
+ case T_OpExpr:
+ coll = ((const OpExpr *) expr)->opcollid;
+ break;
+ case T_DistinctExpr:
+ coll = ((const DistinctExpr *) expr)->opcollid;
+ break;
+ case T_NullIfExpr:
+ coll = ((const NullIfExpr *) expr)->opcollid;
+ break;
+ case T_ScalarArrayOpExpr:
+ /* ScalarArrayOpExpr's result is boolean ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_BoolExpr:
+ /* BoolExpr's result is boolean ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_SubLink:
+ {
+ const SubLink *sublink = (const SubLink *) expr;
+
+ if (sublink->subLinkType == EXPR_SUBLINK ||
+ sublink->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the collation of subselect's first target column */
+ Query *qtree = (Query *) sublink->subselect;
+ TargetEntry *tent;
+
+ if (!qtree || !IsA(qtree, Query))
+ elog(ERROR, "cannot get collation for untransformed sublink");
+ tent = linitial_node(TargetEntry, qtree->targetList);
+ Assert(!tent->resjunk);
+ coll = exprCollation((Node *) tent->expr);
+ /* collation doesn't change if it's converted to array */
+ }
+ else
+ {
+ /* otherwise, SubLink's result is RECORD or BOOLEAN */
+ coll = InvalidOid; /* ... so it has no collation */
+ }
+ }
+ break;
+ case T_SubPlan:
+ {
+ const SubPlan *subplan = (const SubPlan *) expr;
+
+ if (subplan->subLinkType == EXPR_SUBLINK ||
+ subplan->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the collation of subselect's first target column */
+ coll = subplan->firstColCollation;
+ /* collation doesn't change if it's converted to array */
+ }
+ else
+ {
+ /* otherwise, SubPlan's result is RECORD or BOOLEAN */
+ coll = InvalidOid; /* ... so it has no collation */
+ }
+ }
+ break;
+ case T_AlternativeSubPlan:
+ {
+ const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr;
+
+ /* subplans should all return the same thing */
+ coll = exprCollation((Node *) linitial(asplan->subplans));
+ }
+ break;
+ case T_FieldSelect:
+ coll = ((const FieldSelect *) expr)->resultcollid;
+ break;
+ case T_FieldStore:
+ /* FieldStore's result is composite ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_RelabelType:
+ coll = ((const RelabelType *) expr)->resultcollid;
+ break;
+ case T_CoerceViaIO:
+ coll = ((const CoerceViaIO *) expr)->resultcollid;
+ break;
+ case T_ArrayCoerceExpr:
+ coll = ((const ArrayCoerceExpr *) expr)->resultcollid;
+ break;
+ case T_ConvertRowtypeExpr:
+ /* ConvertRowtypeExpr's result is composite ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_CollateExpr:
+ coll = ((const CollateExpr *) expr)->collOid;
+ break;
+ case T_CaseExpr:
+ coll = ((const CaseExpr *) expr)->casecollid;
+ break;
+ case T_CaseTestExpr:
+ coll = ((const CaseTestExpr *) expr)->collation;
+ break;
+ case T_ArrayExpr:
+ coll = ((const ArrayExpr *) expr)->array_collid;
+ break;
+ case T_RowExpr:
+ /* RowExpr's result is composite ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_RowCompareExpr:
+ /* RowCompareExpr's result is boolean ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_CoalesceExpr:
+ coll = ((const CoalesceExpr *) expr)->coalescecollid;
+ break;
+ case T_MinMaxExpr:
+ coll = ((const MinMaxExpr *) expr)->minmaxcollid;
+ break;
+ case T_SQLValueFunction:
+ /* Returns either NAME or a non-collatable type */
+ if (((const SQLValueFunction *) expr)->type == NAMEOID)
+ coll = C_COLLATION_OID;
+ else
+ coll = InvalidOid;
+ break;
+ case T_XmlExpr:
+
+ /*
+ * XMLSERIALIZE returns text from non-collatable inputs, so its
+ * collation is always default. The other cases return boolean or
+ * XML, which are non-collatable.
+ */
+ if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE)
+ coll = DEFAULT_COLLATION_OID;
+ else
+ coll = InvalidOid;
+ break;
+ case T_NullTest:
+ /* NullTest's result is boolean ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_BooleanTest:
+ /* BooleanTest's result is boolean ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_CoerceToDomain:
+ coll = ((const CoerceToDomain *) expr)->resultcollid;
+ break;
+ case T_CoerceToDomainValue:
+ coll = ((const CoerceToDomainValue *) expr)->collation;
+ break;
+ case T_SetToDefault:
+ coll = ((const SetToDefault *) expr)->collation;
+ break;
+ case T_CurrentOfExpr:
+ /* CurrentOfExpr's result is boolean ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_NextValueExpr:
+ /* NextValueExpr's result is an integer type ... */
+ coll = InvalidOid; /* ... so it has no collation */
+ break;
+ case T_InferenceElem:
+ coll = exprCollation((Node *) ((const InferenceElem *) expr)->expr);
+ break;
+ case T_PlaceHolderVar:
+ coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
+ coll = InvalidOid; /* keep compiler quiet */
+ break;
+ }
+ return coll;
+}
+
+/*
+ * exprInputCollation -
+ * returns the Oid of the collation a function should use, if available.
+ *
+ * Result is InvalidOid if the node type doesn't store this information.
+ */
+Oid
+exprInputCollation(const Node *expr)
+{
+ Oid coll;
+
+ if (!expr)
+ return InvalidOid;
+
+ switch (nodeTag(expr))
+ {
+ case T_Aggref:
+ coll = ((const Aggref *) expr)->inputcollid;
+ break;
+ case T_WindowFunc:
+ coll = ((const WindowFunc *) expr)->inputcollid;
+ break;
+ case T_FuncExpr:
+ coll = ((const FuncExpr *) expr)->inputcollid;
+ break;
+ case T_OpExpr:
+ coll = ((const OpExpr *) expr)->inputcollid;
+ break;
+ case T_DistinctExpr:
+ coll = ((const DistinctExpr *) expr)->inputcollid;
+ break;
+ case T_NullIfExpr:
+ coll = ((const NullIfExpr *) expr)->inputcollid;
+ break;
+ case T_ScalarArrayOpExpr:
+ coll = ((const ScalarArrayOpExpr *) expr)->inputcollid;
+ break;
+ case T_MinMaxExpr:
+ coll = ((const MinMaxExpr *) expr)->inputcollid;
+ break;
+ default:
+ coll = InvalidOid;
+ break;
+ }
+ return coll;
+}
+
+/*
+ * exprSetCollation -
+ * Assign collation information to an expression tree node.
+ *
+ * Note: since this is only used during parse analysis, we don't need to
+ * worry about subplans or PlaceHolderVars.
+ */
+void
+exprSetCollation(Node *expr, Oid collation)
+{
+ switch (nodeTag(expr))
+ {
+ case T_Var:
+ ((Var *) expr)->varcollid = collation;
+ break;
+ case T_Const:
+ ((Const *) expr)->constcollid = collation;
+ break;
+ case T_Param:
+ ((Param *) expr)->paramcollid = collation;
+ break;
+ case T_Aggref:
+ ((Aggref *) expr)->aggcollid = collation;
+ break;
+ case T_GroupingFunc:
+ Assert(!OidIsValid(collation));
+ break;
+ case T_WindowFunc:
+ ((WindowFunc *) expr)->wincollid = collation;
+ break;
+ case T_SubscriptingRef:
+ ((SubscriptingRef *) expr)->refcollid = collation;
+ break;
+ case T_FuncExpr:
+ ((FuncExpr *) expr)->funccollid = collation;
+ break;
+ case T_NamedArgExpr:
+ Assert(collation == exprCollation((Node *) ((NamedArgExpr *) expr)->arg));
+ break;
+ case T_OpExpr:
+ ((OpExpr *) expr)->opcollid = collation;
+ break;
+ case T_DistinctExpr:
+ ((DistinctExpr *) expr)->opcollid = collation;
+ break;
+ case T_NullIfExpr:
+ ((NullIfExpr *) expr)->opcollid = collation;
+ break;
+ case T_ScalarArrayOpExpr:
+ /* ScalarArrayOpExpr's result is boolean ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_BoolExpr:
+ /* BoolExpr's result is boolean ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_SubLink:
+#ifdef USE_ASSERT_CHECKING
+ {
+ SubLink *sublink = (SubLink *) expr;
+
+ if (sublink->subLinkType == EXPR_SUBLINK ||
+ sublink->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the collation of subselect's first target column */
+ Query *qtree = (Query *) sublink->subselect;
+ TargetEntry *tent;
+
+ if (!qtree || !IsA(qtree, Query))
+ elog(ERROR, "cannot set collation for untransformed sublink");
+ tent = linitial_node(TargetEntry, qtree->targetList);
+ Assert(!tent->resjunk);
+ Assert(collation == exprCollation((Node *) tent->expr));
+ }
+ else
+ {
+ /* otherwise, result is RECORD or BOOLEAN */
+ Assert(!OidIsValid(collation));
+ }
+ }
+#endif /* USE_ASSERT_CHECKING */
+ break;
+ case T_FieldSelect:
+ ((FieldSelect *) expr)->resultcollid = collation;
+ break;
+ case T_FieldStore:
+ /* FieldStore's result is composite ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_RelabelType:
+ ((RelabelType *) expr)->resultcollid = collation;
+ break;
+ case T_CoerceViaIO:
+ ((CoerceViaIO *) expr)->resultcollid = collation;
+ break;
+ case T_ArrayCoerceExpr:
+ ((ArrayCoerceExpr *) expr)->resultcollid = collation;
+ break;
+ case T_ConvertRowtypeExpr:
+ /* ConvertRowtypeExpr's result is composite ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_CaseExpr:
+ ((CaseExpr *) expr)->casecollid = collation;
+ break;
+ case T_ArrayExpr:
+ ((ArrayExpr *) expr)->array_collid = collation;
+ break;
+ case T_RowExpr:
+ /* RowExpr's result is composite ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_RowCompareExpr:
+ /* RowCompareExpr's result is boolean ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_CoalesceExpr:
+ ((CoalesceExpr *) expr)->coalescecollid = collation;
+ break;
+ case T_MinMaxExpr:
+ ((MinMaxExpr *) expr)->minmaxcollid = collation;
+ break;
+ case T_SQLValueFunction:
+ Assert((((SQLValueFunction *) expr)->type == NAMEOID) ?
+ (collation == C_COLLATION_OID) :
+ (collation == InvalidOid));
+ break;
+ case T_XmlExpr:
+ Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ?
+ (collation == DEFAULT_COLLATION_OID) :
+ (collation == InvalidOid));
+ break;
+ case T_NullTest:
+ /* NullTest's result is boolean ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_BooleanTest:
+ /* BooleanTest's result is boolean ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_CoerceToDomain:
+ ((CoerceToDomain *) expr)->resultcollid = collation;
+ break;
+ case T_CoerceToDomainValue:
+ ((CoerceToDomainValue *) expr)->collation = collation;
+ break;
+ case T_SetToDefault:
+ ((SetToDefault *) expr)->collation = collation;
+ break;
+ case T_CurrentOfExpr:
+ /* CurrentOfExpr's result is boolean ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ case T_NextValueExpr:
+ /* NextValueExpr's result is an integer type ... */
+ Assert(!OidIsValid(collation)); /* ... so never set a collation */
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
+ break;
+ }
+}
+
+/*
+ * exprSetInputCollation -
+ * Assign input-collation information to an expression tree node.
+ *
+ * This is a no-op for node types that don't store their input collation.
+ * Note we omit RowCompareExpr, which needs special treatment since it
+ * contains multiple input collation OIDs.
+ */
+void
+exprSetInputCollation(Node *expr, Oid inputcollation)
+{
+ switch (nodeTag(expr))
+ {
+ case T_Aggref:
+ ((Aggref *) expr)->inputcollid = inputcollation;
+ break;
+ case T_WindowFunc:
+ ((WindowFunc *) expr)->inputcollid = inputcollation;
+ break;
+ case T_FuncExpr:
+ ((FuncExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_OpExpr:
+ ((OpExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_DistinctExpr:
+ ((DistinctExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_NullIfExpr:
+ ((NullIfExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_ScalarArrayOpExpr:
+ ((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_MinMaxExpr:
+ ((MinMaxExpr *) expr)->inputcollid = inputcollation;
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ * exprLocation -
+ * returns the parse location of an expression tree, for error reports
+ *
+ * -1 is returned if the location can't be determined.
+ *
+ * For expressions larger than a single token, the intent here is to
+ * return the location of the expression's leftmost token, not necessarily
+ * the topmost Node's location field. For example, an OpExpr's location
+ * field will point at the operator name, but if it is not a prefix operator
+ * then we should return the location of the left-hand operand instead.
+ * The reason is that we want to reference the entire expression not just
+ * that operator, and pointing to its start seems to be the most natural way.
+ *
+ * The location is not perfect --- for example, since the grammar doesn't
+ * explicitly represent parentheses in the parsetree, given something that
+ * had been written "(a + b) * c" we are going to point at "a" not "(".
+ * But it should be plenty good enough for error reporting purposes.
+ *
+ * You might think that this code is overly general, for instance why check
+ * the operands of a FuncExpr node, when the function name can be expected
+ * to be to the left of them? There are a couple of reasons. The grammar
+ * sometimes builds expressions that aren't quite what the user wrote;
+ * for instance x IS NOT BETWEEN ... becomes a NOT-expression whose keyword
+ * pointer is to the right of its leftmost argument. Also, nodes that were
+ * inserted implicitly by parse analysis (such as FuncExprs for implicit
+ * coercions) will have location -1, and so we can have odd combinations of
+ * known and unknown locations in a tree.
+ */
+int
+exprLocation(const Node *expr)
+{
+ int loc;
+
+ if (expr == NULL)
+ return -1;
+ switch (nodeTag(expr))
+ {
+ case T_RangeVar:
+ loc = ((const RangeVar *) expr)->location;
+ break;
+ case T_TableFunc:
+ loc = ((const TableFunc *) expr)->location;
+ break;
+ case T_Var:
+ loc = ((const Var *) expr)->location;
+ break;
+ case T_Const:
+ loc = ((const Const *) expr)->location;
+ break;
+ case T_Param:
+ loc = ((const Param *) expr)->location;
+ break;
+ case T_Aggref:
+ /* function name should always be the first thing */
+ loc = ((const Aggref *) expr)->location;
+ break;
+ case T_GroupingFunc:
+ loc = ((const GroupingFunc *) expr)->location;
+ break;
+ case T_WindowFunc:
+ /* function name should always be the first thing */
+ loc = ((const WindowFunc *) expr)->location;
+ break;
+ case T_SubscriptingRef:
+ /* just use container argument's location */
+ loc = exprLocation((Node *) ((const SubscriptingRef *) expr)->refexpr);
+ break;
+ case T_FuncExpr:
+ {
+ const FuncExpr *fexpr = (const FuncExpr *) expr;
+
+ /* consider both function name and leftmost arg */
+ loc = leftmostLoc(fexpr->location,
+ exprLocation((Node *) fexpr->args));
+ }
+ break;
+ case T_NamedArgExpr:
+ {
+ const NamedArgExpr *na = (const NamedArgExpr *) expr;
+
+ /* consider both argument name and value */
+ loc = leftmostLoc(na->location,
+ exprLocation((Node *) na->arg));
+ }
+ break;
+ case T_OpExpr:
+ case T_DistinctExpr: /* struct-equivalent to OpExpr */
+ case T_NullIfExpr: /* struct-equivalent to OpExpr */
+ {
+ const OpExpr *opexpr = (const OpExpr *) expr;
+
+ /* consider both operator name and leftmost arg */
+ loc = leftmostLoc(opexpr->location,
+ exprLocation((Node *) opexpr->args));
+ }
+ break;
+ case T_ScalarArrayOpExpr:
+ {
+ const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr;
+
+ /* consider both operator name and leftmost arg */
+ loc = leftmostLoc(saopexpr->location,
+ exprLocation((Node *) saopexpr->args));
+ }
+ break;
+ case T_BoolExpr:
+ {
+ const BoolExpr *bexpr = (const BoolExpr *) expr;
+
+ /*
+ * Same as above, to handle either NOT or AND/OR. We can't
+ * special-case NOT because of the way that it's used for
+ * things like IS NOT BETWEEN.
+ */
+ loc = leftmostLoc(bexpr->location,
+ exprLocation((Node *) bexpr->args));
+ }
+ break;
+ case T_SubLink:
+ {
+ const SubLink *sublink = (const SubLink *) expr;
+
+ /* check the testexpr, if any, and the operator/keyword */
+ loc = leftmostLoc(exprLocation(sublink->testexpr),
+ sublink->location);
+ }
+ break;
+ case T_FieldSelect:
+ /* just use argument's location */
+ loc = exprLocation((Node *) ((const FieldSelect *) expr)->arg);
+ break;
+ case T_FieldStore:
+ /* just use argument's location */
+ loc = exprLocation((Node *) ((const FieldStore *) expr)->arg);
+ break;
+ case T_RelabelType:
+ {
+ const RelabelType *rexpr = (const RelabelType *) expr;
+
+ /* Much as above */
+ loc = leftmostLoc(rexpr->location,
+ exprLocation((Node *) rexpr->arg));
+ }
+ break;
+ case T_CoerceViaIO:
+ {
+ const CoerceViaIO *cexpr = (const CoerceViaIO *) expr;
+
+ /* Much as above */
+ loc = leftmostLoc(cexpr->location,
+ exprLocation((Node *) cexpr->arg));
+ }
+ break;
+ case T_ArrayCoerceExpr:
+ {
+ const ArrayCoerceExpr *cexpr = (const ArrayCoerceExpr *) expr;
+
+ /* Much as above */
+ loc = leftmostLoc(cexpr->location,
+ exprLocation((Node *) cexpr->arg));
+ }
+ break;
+ case T_ConvertRowtypeExpr:
+ {
+ const ConvertRowtypeExpr *cexpr = (const ConvertRowtypeExpr *) expr;
+
+ /* Much as above */
+ loc = leftmostLoc(cexpr->location,
+ exprLocation((Node *) cexpr->arg));
+ }
+ break;
+ case T_CollateExpr:
+ /* just use argument's location */
+ loc = exprLocation((Node *) ((const CollateExpr *) expr)->arg);
+ break;
+ case T_CaseExpr:
+ /* CASE keyword should always be the first thing */
+ loc = ((const CaseExpr *) expr)->location;
+ break;
+ case T_CaseWhen:
+ /* WHEN keyword should always be the first thing */
+ loc = ((const CaseWhen *) expr)->location;
+ break;
+ case T_ArrayExpr:
+ /* the location points at ARRAY or [, which must be leftmost */
+ loc = ((const ArrayExpr *) expr)->location;
+ break;
+ case T_RowExpr:
+ /* the location points at ROW or (, which must be leftmost */
+ loc = ((const RowExpr *) expr)->location;
+ break;
+ case T_RowCompareExpr:
+ /* just use leftmost argument's location */
+ loc = exprLocation((Node *) ((const RowCompareExpr *) expr)->largs);
+ break;
+ case T_CoalesceExpr:
+ /* COALESCE keyword should always be the first thing */
+ loc = ((const CoalesceExpr *) expr)->location;
+ break;
+ case T_MinMaxExpr:
+ /* GREATEST/LEAST keyword should always be the first thing */
+ loc = ((const MinMaxExpr *) expr)->location;
+ break;
+ case T_SQLValueFunction:
+ /* function keyword should always be the first thing */
+ loc = ((const SQLValueFunction *) expr)->location;
+ break;
+ case T_XmlExpr:
+ {
+ const XmlExpr *xexpr = (const XmlExpr *) expr;
+
+ /* consider both function name and leftmost arg */
+ loc = leftmostLoc(xexpr->location,
+ exprLocation((Node *) xexpr->args));
+ }
+ break;
+ case T_NullTest:
+ {
+ const NullTest *nexpr = (const NullTest *) expr;
+
+ /* Much as above */
+ loc = leftmostLoc(nexpr->location,
+ exprLocation((Node *) nexpr->arg));
+ }
+ break;
+ case T_BooleanTest:
+ {
+ const BooleanTest *bexpr = (const BooleanTest *) expr;
+
+ /* Much as above */
+ loc = leftmostLoc(bexpr->location,
+ exprLocation((Node *) bexpr->arg));
+ }
+ break;
+ case T_CoerceToDomain:
+ {
+ const CoerceToDomain *cexpr = (const CoerceToDomain *) expr;
+
+ /* Much as above */
+ loc = leftmostLoc(cexpr->location,
+ exprLocation((Node *) cexpr->arg));
+ }
+ break;
+ case T_CoerceToDomainValue:
+ loc = ((const CoerceToDomainValue *) expr)->location;
+ break;
+ case T_SetToDefault:
+ loc = ((const SetToDefault *) expr)->location;
+ break;
+ case T_TargetEntry:
+ /* just use argument's location */
+ loc = exprLocation((Node *) ((const TargetEntry *) expr)->expr);
+ break;
+ case T_IntoClause:
+ /* use the contained RangeVar's location --- close enough */
+ loc = exprLocation((Node *) ((const IntoClause *) expr)->rel);
+ break;
+ case T_List:
+ {
+ /* report location of first list member that has a location */
+ ListCell *lc;
+
+ loc = -1; /* just to suppress compiler warning */
+ foreach(lc, (const List *) expr)
+ {
+ loc = exprLocation((Node *) lfirst(lc));
+ if (loc >= 0)
+ break;
+ }
+ }
+ break;
+ case T_A_Expr:
+ {
+ const A_Expr *aexpr = (const A_Expr *) expr;
+
+ /* use leftmost of operator or left operand (if any) */
+ /* we assume right operand can't be to left of operator */
+ loc = leftmostLoc(aexpr->location,
+ exprLocation(aexpr->lexpr));
+ }
+ break;
+ case T_ColumnRef:
+ loc = ((const ColumnRef *) expr)->location;
+ break;
+ case T_ParamRef:
+ loc = ((const ParamRef *) expr)->location;
+ break;
+ case T_A_Const:
+ loc = ((const A_Const *) expr)->location;
+ break;
+ case T_FuncCall:
+ {
+ const FuncCall *fc = (const FuncCall *) expr;
+
+ /* consider both function name and leftmost arg */
+ /* (we assume any ORDER BY nodes must be to right of name) */
+ loc = leftmostLoc(fc->location,
+ exprLocation((Node *) fc->args));
+ }
+ break;
+ case T_A_ArrayExpr:
+ /* the location points at ARRAY or [, which must be leftmost */
+ loc = ((const A_ArrayExpr *) expr)->location;
+ break;
+ case T_ResTarget:
+ /* we need not examine the contained expression (if any) */
+ loc = ((const ResTarget *) expr)->location;
+ break;
+ case T_MultiAssignRef:
+ loc = exprLocation(((const MultiAssignRef *) expr)->source);
+ break;
+ case T_TypeCast:
+ {
+ const TypeCast *tc = (const TypeCast *) expr;
+
+ /*
+ * This could represent CAST(), ::, or TypeName 'literal', so
+ * any of the components might be leftmost.
+ */
+ loc = exprLocation(tc->arg);
+ loc = leftmostLoc(loc, tc->typeName->location);
+ loc = leftmostLoc(loc, tc->location);
+ }
+ break;
+ case T_CollateClause:
+ /* just use argument's location */
+ loc = exprLocation(((const CollateClause *) expr)->arg);
+ break;
+ case T_SortBy:
+ /* just use argument's location (ignore operator, if any) */
+ loc = exprLocation(((const SortBy *) expr)->node);
+ break;
+ case T_WindowDef:
+ loc = ((const WindowDef *) expr)->location;
+ break;
+ case T_RangeTableSample:
+ loc = ((const RangeTableSample *) expr)->location;
+ break;
+ case T_TypeName:
+ loc = ((const TypeName *) expr)->location;
+ break;
+ case T_ColumnDef:
+ loc = ((const ColumnDef *) expr)->location;
+ break;
+ case T_Constraint:
+ loc = ((const Constraint *) expr)->location;
+ break;
+ case T_FunctionParameter:
+ /* just use typename's location */
+ loc = exprLocation((Node *) ((const FunctionParameter *) expr)->argType);
+ break;
+ case T_XmlSerialize:
+ /* XMLSERIALIZE keyword should always be the first thing */
+ loc = ((const XmlSerialize *) expr)->location;
+ break;
+ case T_GroupingSet:
+ loc = ((const GroupingSet *) expr)->location;
+ break;
+ case T_WithClause:
+ loc = ((const WithClause *) expr)->location;
+ break;
+ case T_InferClause:
+ loc = ((const InferClause *) expr)->location;
+ break;
+ case T_OnConflictClause:
+ loc = ((const OnConflictClause *) expr)->location;
+ break;
+ case T_CTESearchClause:
+ loc = ((const CTESearchClause *) expr)->location;
+ break;
+ case T_CTECycleClause:
+ loc = ((const CTECycleClause *) expr)->location;
+ break;
+ case T_CommonTableExpr:
+ loc = ((const CommonTableExpr *) expr)->location;
+ break;
+ case T_PlaceHolderVar:
+ /* just use argument's location */
+ loc = exprLocation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+ break;
+ case T_InferenceElem:
+ /* just use nested expr's location */
+ loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr);
+ break;
+ case T_PartitionElem:
+ loc = ((const PartitionElem *) expr)->location;
+ break;
+ case T_PartitionSpec:
+ loc = ((const PartitionSpec *) expr)->location;
+ break;
+ case T_PartitionBoundSpec:
+ loc = ((const PartitionBoundSpec *) expr)->location;
+ break;
+ case T_PartitionRangeDatum:
+ loc = ((const PartitionRangeDatum *) expr)->location;
+ break;
+ default:
+ /* for any other node type it's just unknown... */
+ loc = -1;
+ break;
+ }
+ return loc;
+}
+
+/*
+ * leftmostLoc - support for exprLocation
+ *
+ * Take the minimum of two parse location values, but ignore unknowns
+ */
+static int
+leftmostLoc(int loc1, int loc2)
+{
+ if (loc1 < 0)
+ return loc2;
+ else if (loc2 < 0)
+ return loc1;
+ else
+ return Min(loc1, loc2);
+}
+
+
+/*
+ * fix_opfuncids
+ * Calculate opfuncid field from opno for each OpExpr node in given tree.
+ * The given tree can be anything expression_tree_walker handles.
+ *
+ * The argument is modified in-place. (This is OK since we'd want the
+ * same change for any node, even if it gets visited more than once due to
+ * shared structure.)
+ */
+void
+fix_opfuncids(Node *node)
+{
+ /* This tree walk requires no special setup, so away we go... */
+ fix_opfuncids_walker(node, NULL);
+}
+
+static bool
+fix_opfuncids_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, OpExpr))
+ set_opfuncid((OpExpr *) node);
+ else if (IsA(node, DistinctExpr))
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, NullIfExpr))
+ set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, ScalarArrayOpExpr))
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
+ return expression_tree_walker(node, fix_opfuncids_walker, context);
+}
+
+/*
+ * set_opfuncid
+ * Set the opfuncid (procedure OID) in an OpExpr node,
+ * if it hasn't been set already.
+ *
+ * Because of struct equivalence, this can also be used for
+ * DistinctExpr and NullIfExpr nodes.
+ */
+void
+set_opfuncid(OpExpr *opexpr)
+{
+ if (opexpr->opfuncid == InvalidOid)
+ opexpr->opfuncid = get_opcode(opexpr->opno);
+}
+
+/*
+ * set_sa_opfuncid
+ * As above, for ScalarArrayOpExpr nodes.
+ */
+void
+set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
+{
+ if (opexpr->opfuncid == InvalidOid)
+ opexpr->opfuncid = get_opcode(opexpr->opno);
+}
+
+
+/*
+ * check_functions_in_node -
+ * apply checker() to each function OID contained in given expression node
+ *
+ * Returns true if the checker() function does; for nodes representing more
+ * than one function call, returns true if the checker() function does so
+ * for any of those functions. Returns false if node does not invoke any
+ * SQL-visible function. Caller must not pass node == NULL.
+ *
+ * This function examines only the given node; it does not recurse into any
+ * sub-expressions. Callers typically prefer to keep control of the recursion
+ * for themselves, in case additional checks should be made, or because they
+ * have special rules about which parts of the tree need to be visited.
+ *
+ * Note: we ignore MinMaxExpr, SQLValueFunction, XmlExpr, CoerceToDomain,
+ * and NextValueExpr nodes, because they do not contain SQL function OIDs.
+ * However, they can invoke SQL-visible functions, so callers should take
+ * thought about how to treat them.
+ */
+bool
+check_functions_in_node(Node *node, check_function_callback checker,
+ void *context)
+{
+ switch (nodeTag(node))
+ {
+ case T_Aggref:
+ {
+ Aggref *expr = (Aggref *) node;
+
+ if (checker(expr->aggfnoid, context))
+ return true;
+ }
+ break;
+ case T_WindowFunc:
+ {
+ WindowFunc *expr = (WindowFunc *) node;
+
+ if (checker(expr->winfnoid, context))
+ return true;
+ }
+ break;
+ case T_FuncExpr:
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+
+ if (checker(expr->funcid, context))
+ return true;
+ }
+ break;
+ case T_OpExpr:
+ case T_DistinctExpr: /* struct-equivalent to OpExpr */
+ case T_NullIfExpr: /* struct-equivalent to OpExpr */
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ /* Set opfuncid if it wasn't set already */
+ set_opfuncid(expr);
+ if (checker(expr->opfuncid, context))
+ return true;
+ }
+ break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ set_sa_opfuncid(expr);
+ if (checker(expr->opfuncid, context))
+ return true;
+ }
+ break;
+ case T_CoerceViaIO:
+ {
+ CoerceViaIO *expr = (CoerceViaIO *) node;
+ Oid iofunc;
+ Oid typioparam;
+ bool typisvarlena;
+
+ /* check the result type's input function */
+ getTypeInputInfo(expr->resulttype,
+ &iofunc, &typioparam);
+ if (checker(iofunc, context))
+ return true;
+ /* check the input type's output function */
+ getTypeOutputInfo(exprType((Node *) expr->arg),
+ &iofunc, &typisvarlena);
+ if (checker(iofunc, context))
+ return true;
+ }
+ break;
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *opid;
+
+ foreach(opid, rcexpr->opnos)
+ {
+ Oid opfuncid = get_opcode(lfirst_oid(opid));
+
+ if (checker(opfuncid, context))
+ return true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+
+/*
+ * Standard expression-tree walking support
+ *
+ * We used to have near-duplicate code in many different routines that
+ * understood how to recurse through an expression node tree. That was
+ * a pain to maintain, and we frequently had bugs due to some particular
+ * routine neglecting to support a particular node type. In most cases,
+ * these routines only actually care about certain node types, and don't
+ * care about other types except insofar as they have to recurse through
+ * non-primitive node types. Therefore, we now provide generic tree-walking
+ * logic to consolidate the redundant "boilerplate" code. There are
+ * two versions: expression_tree_walker() and expression_tree_mutator().
+ */
+
+/*
+ * expression_tree_walker() is designed to support routines that traverse
+ * a tree in a read-only fashion (although it will also work for routines
+ * that modify nodes in-place but never add/delete/replace nodes).
+ * A walker routine should look like this:
+ *
+ * bool my_walker (Node *node, my_struct *context)
+ * {
+ * if (node == NULL)
+ * return false;
+ * // check for nodes that special work is required for, eg:
+ * if (IsA(node, Var))
+ * {
+ * ... do special actions for Var nodes
+ * }
+ * else if (IsA(node, ...))
+ * {
+ * ... do special actions for other node types
+ * }
+ * // for any node type not specially processed, do:
+ * return expression_tree_walker(node, my_walker, (void *) context);
+ * }
+ *
+ * The "context" argument points to a struct that holds whatever context
+ * information the walker routine needs --- it can be used to return data
+ * gathered by the walker, too. This argument is not touched by
+ * expression_tree_walker, but it is passed down to recursive sub-invocations
+ * of my_walker. The tree walk is started from a setup routine that
+ * fills in the appropriate context struct, calls my_walker with the top-level
+ * node of the tree, and then examines the results.
+ *
+ * The walker routine should return "false" to continue the tree walk, or
+ * "true" to abort the walk and immediately return "true" to the top-level
+ * caller. This can be used to short-circuit the traversal if the walker
+ * has found what it came for. "false" is returned to the top-level caller
+ * iff no invocation of the walker returned "true".
+ *
+ * The node types handled by expression_tree_walker include all those
+ * normally found in target lists and qualifier clauses during the planning
+ * stage. In particular, it handles List nodes since a cnf-ified qual clause
+ * will have List structure at the top level, and it handles TargetEntry nodes
+ * so that a scan of a target list can be handled without additional code.
+ * Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are
+ * handled, so that query jointrees and setOperation trees can be processed
+ * without additional code.
+ *
+ * expression_tree_walker will handle SubLink nodes by recursing normally
+ * into the "testexpr" subtree (which is an expression belonging to the outer
+ * plan). It will also call the walker on the sub-Query node; however, when
+ * expression_tree_walker itself is called on a Query node, it does nothing
+ * and returns "false". The net effect is that unless the walker does
+ * something special at a Query node, sub-selects will not be visited during
+ * an expression tree walk. This is exactly the behavior wanted in many cases
+ * --- and for those walkers that do want to recurse into sub-selects, special
+ * behavior is typically needed anyway at the entry to a sub-select (such as
+ * incrementing a depth counter). A walker that wants to examine sub-selects
+ * should include code along the lines of:
+ *
+ * if (IsA(node, Query))
+ * {
+ * adjust context for subquery;
+ * result = query_tree_walker((Query *) node, my_walker, context,
+ * 0); // adjust flags as needed
+ * restore context if needed;
+ * return result;
+ * }
+ *
+ * query_tree_walker is a convenience routine (see below) that calls the
+ * walker on all the expression subtrees of the given Query node.
+ *
+ * expression_tree_walker will handle SubPlan nodes by recursing normally
+ * into the "testexpr" and the "args" list (which are expressions belonging to
+ * the outer plan). It will not touch the completed subplan, however. Since
+ * there is no link to the original Query, it is not possible to recurse into
+ * subselects of an already-planned expression tree. This is OK for current
+ * uses, but may need to be revisited in future.
+ */
+
+bool
+expression_tree_walker(Node *node,
+ bool (*walker) (),
+ void *context)
+{
+ ListCell *temp;
+
+ /*
+ * The walker has already visited the current node, and so we need only
+ * recurse into any sub-nodes it has.
+ *
+ * We assume that the walker is not interested in List nodes per se, so
+ * when we expect a List we just recurse directly to self without
+ * bothering to call the walker.
+ */
+ if (node == NULL)
+ return false;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ switch (nodeTag(node))
+ {
+ case T_Var:
+ case T_Const:
+ case T_Param:
+ case T_CaseTestExpr:
+ case T_SQLValueFunction:
+ case T_CoerceToDomainValue:
+ case T_SetToDefault:
+ case T_CurrentOfExpr:
+ case T_NextValueExpr:
+ case T_RangeTblRef:
+ case T_SortGroupClause:
+ case T_CTESearchClause:
+ /* primitive node types with no expression subnodes */
+ break;
+ case T_WithCheckOption:
+ return walker(((WithCheckOption *) node)->qual, context);
+ case T_Aggref:
+ {
+ Aggref *expr = (Aggref *) node;
+
+ /* recurse directly on List */
+ if (expression_tree_walker((Node *) expr->aggdirectargs,
+ walker, context))
+ return true;
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ if (expression_tree_walker((Node *) expr->aggorder,
+ walker, context))
+ return true;
+ if (expression_tree_walker((Node *) expr->aggdistinct,
+ walker, context))
+ return true;
+ if (walker((Node *) expr->aggfilter, context))
+ return true;
+ }
+ break;
+ case T_GroupingFunc:
+ {
+ GroupingFunc *grouping = (GroupingFunc *) node;
+
+ if (expression_tree_walker((Node *) grouping->args,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_WindowFunc:
+ {
+ WindowFunc *expr = (WindowFunc *) node;
+
+ /* recurse directly on List */
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ if (walker((Node *) expr->aggfilter, context))
+ return true;
+ }
+ break;
+ case T_SubscriptingRef:
+ {
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
+
+ /* recurse directly for upper/lower container index lists */
+ if (expression_tree_walker((Node *) sbsref->refupperindexpr,
+ walker, context))
+ return true;
+ if (expression_tree_walker((Node *) sbsref->reflowerindexpr,
+ walker, context))
+ return true;
+ /* walker must see the refexpr and refassgnexpr, however */
+ if (walker(sbsref->refexpr, context))
+ return true;
+
+ if (walker(sbsref->refassgnexpr, context))
+ return true;
+ }
+ break;
+ case T_FuncExpr:
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_NamedArgExpr:
+ return walker(((NamedArgExpr *) node)->arg, context);
+ case T_OpExpr:
+ case T_DistinctExpr: /* struct-equivalent to OpExpr */
+ case T_NullIfExpr: /* struct-equivalent to OpExpr */
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_BoolExpr:
+ {
+ BoolExpr *expr = (BoolExpr *) node;
+
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_SubLink:
+ {
+ SubLink *sublink = (SubLink *) node;
+
+ if (walker(sublink->testexpr, context))
+ return true;
+
+ /*
+ * Also invoke the walker on the sublink's Query node, so it
+ * can recurse into the sub-query if it wants to.
+ */
+ return walker(sublink->subselect, context);
+ }
+ break;
+ case T_SubPlan:
+ {
+ SubPlan *subplan = (SubPlan *) node;
+
+ /* recurse into the testexpr, but not into the Plan */
+ if (walker(subplan->testexpr, context))
+ return true;
+ /* also examine args list */
+ if (expression_tree_walker((Node *) subplan->args,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_AlternativeSubPlan:
+ return walker(((AlternativeSubPlan *) node)->subplans, context);
+ case T_FieldSelect:
+ return walker(((FieldSelect *) node)->arg, context);
+ case T_FieldStore:
+ {
+ FieldStore *fstore = (FieldStore *) node;
+
+ if (walker(fstore->arg, context))
+ return true;
+ if (walker(fstore->newvals, context))
+ return true;
+ }
+ break;
+ case T_RelabelType:
+ return walker(((RelabelType *) node)->arg, context);
+ case T_CoerceViaIO:
+ return walker(((CoerceViaIO *) node)->arg, context);
+ case T_ArrayCoerceExpr:
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+
+ if (walker(acoerce->arg, context))
+ return true;
+ if (walker(acoerce->elemexpr, context))
+ return true;
+ }
+ break;
+ case T_ConvertRowtypeExpr:
+ return walker(((ConvertRowtypeExpr *) node)->arg, context);
+ case T_CollateExpr:
+ return walker(((CollateExpr *) node)->arg, context);
+ case T_CaseExpr:
+ {
+ CaseExpr *caseexpr = (CaseExpr *) node;
+
+ if (walker(caseexpr->arg, context))
+ return true;
+ /* we assume walker doesn't care about CaseWhens, either */
+ foreach(temp, caseexpr->args)
+ {
+ CaseWhen *when = lfirst_node(CaseWhen, temp);
+
+ if (walker(when->expr, context))
+ return true;
+ if (walker(when->result, context))
+ return true;
+ }
+ if (walker(caseexpr->defresult, context))
+ return true;
+ }
+ break;
+ case T_ArrayExpr:
+ return walker(((ArrayExpr *) node)->elements, context);
+ case T_RowExpr:
+ /* Assume colnames isn't interesting */
+ return walker(((RowExpr *) node)->args, context);
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+
+ if (walker(rcexpr->largs, context))
+ return true;
+ if (walker(rcexpr->rargs, context))
+ return true;
+ }
+ break;
+ case T_CoalesceExpr:
+ return walker(((CoalesceExpr *) node)->args, context);
+ case T_MinMaxExpr:
+ return walker(((MinMaxExpr *) node)->args, context);
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+
+ if (walker(xexpr->named_args, context))
+ return true;
+ /* we assume walker doesn't care about arg_names */
+ if (walker(xexpr->args, context))
+ return true;
+ }
+ break;
+ case T_NullTest:
+ return walker(((NullTest *) node)->arg, context);
+ case T_BooleanTest:
+ return walker(((BooleanTest *) node)->arg, context);
+ case T_CoerceToDomain:
+ return walker(((CoerceToDomain *) node)->arg, context);
+ case T_TargetEntry:
+ return walker(((TargetEntry *) node)->expr, context);
+ case T_Query:
+ /* Do nothing with a sub-Query, per discussion above */
+ break;
+ case T_WindowClause:
+ {
+ WindowClause *wc = (WindowClause *) node;
+
+ if (walker(wc->partitionClause, context))
+ return true;
+ if (walker(wc->orderClause, context))
+ return true;
+ if (walker(wc->startOffset, context))
+ return true;
+ if (walker(wc->endOffset, context))
+ return true;
+ }
+ break;
+ case T_CTECycleClause:
+ {
+ CTECycleClause *cc = (CTECycleClause *) node;
+
+ if (walker(cc->cycle_mark_value, context))
+ return true;
+ if (walker(cc->cycle_mark_default, context))
+ return true;
+ }
+ break;
+ case T_CommonTableExpr:
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) node;
+
+ /*
+ * Invoke the walker on the CTE's Query node, so it can
+ * recurse into the sub-query if it wants to.
+ */
+ if (walker(cte->ctequery, context))
+ return true;
+
+ if (walker(cte->search_clause, context))
+ return true;
+ if (walker(cte->cycle_clause, context))
+ return true;
+ }
+ break;
+ case T_List:
+ foreach(temp, (List *) node)
+ {
+ if (walker((Node *) lfirst(temp), context))
+ return true;
+ }
+ break;
+ case T_FromExpr:
+ {
+ FromExpr *from = (FromExpr *) node;
+
+ if (walker(from->fromlist, context))
+ return true;
+ if (walker(from->quals, context))
+ return true;
+ }
+ break;
+ case T_OnConflictExpr:
+ {
+ OnConflictExpr *onconflict = (OnConflictExpr *) node;
+
+ if (walker((Node *) onconflict->arbiterElems, context))
+ return true;
+ if (walker(onconflict->arbiterWhere, context))
+ return true;
+ if (walker(onconflict->onConflictSet, context))
+ return true;
+ if (walker(onconflict->onConflictWhere, context))
+ return true;
+ if (walker(onconflict->exclRelTlist, context))
+ return true;
+ }
+ break;
+ case T_PartitionPruneStepOp:
+ {
+ PartitionPruneStepOp *opstep = (PartitionPruneStepOp *) node;
+
+ if (walker((Node *) opstep->exprs, context))
+ return true;
+ }
+ break;
+ case T_PartitionPruneStepCombine:
+ /* no expression subnodes */
+ break;
+ case T_JoinExpr:
+ {
+ JoinExpr *join = (JoinExpr *) node;
+
+ if (walker(join->larg, context))
+ return true;
+ if (walker(join->rarg, context))
+ return true;
+ if (walker(join->quals, context))
+ return true;
+
+ /*
+ * alias clause, using list are deemed uninteresting.
+ */
+ }
+ break;
+ case T_SetOperationStmt:
+ {
+ SetOperationStmt *setop = (SetOperationStmt *) node;
+
+ if (walker(setop->larg, context))
+ return true;
+ if (walker(setop->rarg, context))
+ return true;
+
+ /* groupClauses are deemed uninteresting */
+ }
+ break;
+ case T_IndexClause:
+ {
+ IndexClause *iclause = (IndexClause *) node;
+
+ if (walker(iclause->rinfo, context))
+ return true;
+ if (expression_tree_walker((Node *) iclause->indexquals,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_PlaceHolderVar:
+ return walker(((PlaceHolderVar *) node)->phexpr, context);
+ case T_InferenceElem:
+ return walker(((InferenceElem *) node)->expr, context);
+ case T_AppendRelInfo:
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+ if (expression_tree_walker((Node *) appinfo->translated_vars,
+ walker, context))
+ return true;
+ }
+ break;
+ case T_PlaceHolderInfo:
+ return walker(((PlaceHolderInfo *) node)->ph_var, context);
+ case T_RangeTblFunction:
+ return walker(((RangeTblFunction *) node)->funcexpr, context);
+ case T_TableSampleClause:
+ {
+ TableSampleClause *tsc = (TableSampleClause *) node;
+
+ if (expression_tree_walker((Node *) tsc->args,
+ walker, context))
+ return true;
+ if (walker((Node *) tsc->repeatable, context))
+ return true;
+ }
+ break;
+ case T_TableFunc:
+ {
+ TableFunc *tf = (TableFunc *) node;
+
+ if (walker(tf->ns_uris, context))
+ return true;
+ if (walker(tf->docexpr, context))
+ return true;
+ if (walker(tf->rowexpr, context))
+ return true;
+ if (walker(tf->colexprs, context))
+ return true;
+ if (walker(tf->coldefexprs, context))
+ return true;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
+ break;
+ }
+ return false;
+}
+
+/*
+ * query_tree_walker --- initiate a walk of a Query's expressions
+ *
+ * This routine exists just to reduce the number of places that need to know
+ * where all the expression subtrees of a Query are. Note it can be used
+ * for starting a walk at top level of a Query regardless of whether the
+ * walker intends to descend into subqueries. It is also useful for
+ * descending into subqueries within a walker.
+ *
+ * Some callers want to suppress visitation of certain items in the sub-Query,
+ * typically because they need to process them specially, or don't actually
+ * want to recurse into subqueries. This is supported by the flags argument,
+ * which is the bitwise OR of flag values to add or suppress visitation of
+ * indicated items. (More flag bits may be added as needed.)
+ */
+bool
+query_tree_walker(Query *query,
+ bool (*walker) (),
+ void *context,
+ int flags)
+{
+ Assert(query != NULL && IsA(query, Query));
+
+ /*
+ * We don't walk any utilityStmt here. However, we can't easily assert
+ * that it is absent, since there are at least two code paths by which
+ * action statements from CREATE RULE end up here, and NOTIFY is allowed
+ * in a rule action.
+ */
+
+ if (walker((Node *) query->targetList, context))
+ return true;
+ if (walker((Node *) query->withCheckOptions, context))
+ return true;
+ if (walker((Node *) query->onConflict, context))
+ return true;
+ if (walker((Node *) query->returningList, context))
+ return true;
+ if (walker((Node *) query->jointree, context))
+ return true;
+ if (walker(query->setOperations, context))
+ return true;
+ if (walker(query->havingQual, context))
+ return true;
+ if (walker(query->limitOffset, context))
+ return true;
+ if (walker(query->limitCount, context))
+ return true;
+
+ /*
+ * Most callers aren't interested in SortGroupClause nodes since those
+ * don't contain actual expressions. However they do contain OIDs which
+ * may be needed by dependency walkers etc.
+ */
+ if ((flags & QTW_EXAMINE_SORTGROUP))
+ {
+ if (walker((Node *) query->groupClause, context))
+ return true;
+ if (walker((Node *) query->windowClause, context))
+ return true;
+ if (walker((Node *) query->sortClause, context))
+ return true;
+ if (walker((Node *) query->distinctClause, context))
+ return true;
+ }
+ else
+ {
+ /*
+ * But we need to walk the expressions under WindowClause nodes even
+ * if we're not interested in SortGroupClause nodes.
+ */
+ ListCell *lc;
+
+ foreach(lc, query->windowClause)
+ {
+ WindowClause *wc = lfirst_node(WindowClause, lc);
+
+ if (walker(wc->startOffset, context))
+ return true;
+ if (walker(wc->endOffset, context))
+ return true;
+ }
+ }
+
+ /*
+ * groupingSets and rowMarks are not walked:
+ *
+ * groupingSets contain only ressortgrouprefs (integers) which are
+ * meaningless without the corresponding groupClause or tlist.
+ * Accordingly, any walker that needs to care about them needs to handle
+ * them itself in its Query processing.
+ *
+ * rowMarks is not walked because it contains only rangetable indexes (and
+ * flags etc.) and therefore should be handled at Query level similarly.
+ */
+
+ if (!(flags & QTW_IGNORE_CTE_SUBQUERIES))
+ {
+ if (walker((Node *) query->cteList, context))
+ return true;
+ }
+ if (!(flags & QTW_IGNORE_RANGE_TABLE))
+ {
+ if (range_table_walker(query->rtable, walker, context, flags))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * range_table_walker is just the part of query_tree_walker that scans
+ * a query's rangetable. This is split out since it can be useful on
+ * its own.
+ */
+bool
+range_table_walker(List *rtable,
+ bool (*walker) (),
+ void *context,
+ int flags)
+{
+ ListCell *rt;
+
+ foreach(rt, rtable)
+ {
+ RangeTblEntry *rte = lfirst_node(RangeTblEntry, rt);
+
+ if (range_table_entry_walker(rte, walker, context, flags))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Some callers even want to scan the expressions in individual RTEs.
+ */
+bool
+range_table_entry_walker(RangeTblEntry *rte,
+ bool (*walker) (),
+ void *context,
+ int flags)
+{
+ /*
+ * Walkers might need to examine the RTE node itself either before or
+ * after visiting its contents (or, conceivably, both). Note that if you
+ * specify neither flag, the walker won't be called on the RTE at all.
+ */
+ if (flags & QTW_EXAMINE_RTES_BEFORE)
+ if (walker(rte, context))
+ return true;
+
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ if (walker(rte->tablesample, context))
+ return true;
+ break;
+ case RTE_SUBQUERY:
+ if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
+ if (walker(rte->subquery, context))
+ return true;
+ break;
+ case RTE_JOIN:
+ if (!(flags & QTW_IGNORE_JOINALIASES))
+ if (walker(rte->joinaliasvars, context))
+ return true;
+ break;
+ case RTE_FUNCTION:
+ if (walker(rte->functions, context))
+ return true;
+ break;
+ case RTE_TABLEFUNC:
+ if (walker(rte->tablefunc, context))
+ return true;
+ break;
+ case RTE_VALUES:
+ if (walker(rte->values_lists, context))
+ return true;
+ break;
+ case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
+ case RTE_RESULT:
+ /* nothing to do */
+ break;
+ }
+
+ if (walker(rte->securityQuals, context))
+ return true;
+
+ if (flags & QTW_EXAMINE_RTES_AFTER)
+ if (walker(rte, context))
+ return true;
+
+ return false;
+}
+
+
+/*
+ * expression_tree_mutator() is designed to support routines that make a
+ * modified copy of an expression tree, with some nodes being added,
+ * removed, or replaced by new subtrees. The original tree is (normally)
+ * not changed. Each recursion level is responsible for returning a copy of
+ * (or appropriately modified substitute for) the subtree it is handed.
+ * A mutator routine should look like this:
+ *
+ * Node * my_mutator (Node *node, my_struct *context)
+ * {
+ * if (node == NULL)
+ * return NULL;
+ * // check for nodes that special work is required for, eg:
+ * if (IsA(node, Var))
+ * {
+ * ... create and return modified copy of Var node
+ * }
+ * else if (IsA(node, ...))
+ * {
+ * ... do special transformations of other node types
+ * }
+ * // for any node type not specially processed, do:
+ * return expression_tree_mutator(node, my_mutator, (void *) context);
+ * }
+ *
+ * The "context" argument points to a struct that holds whatever context
+ * information the mutator routine needs --- it can be used to return extra
+ * data gathered by the mutator, too. This argument is not touched by
+ * expression_tree_mutator, but it is passed down to recursive sub-invocations
+ * of my_mutator. The tree walk is started from a setup routine that
+ * fills in the appropriate context struct, calls my_mutator with the
+ * top-level node of the tree, and does any required post-processing.
+ *
+ * Each level of recursion must return an appropriately modified Node.
+ * If expression_tree_mutator() is called, it will make an exact copy
+ * of the given Node, but invoke my_mutator() to copy the sub-node(s)
+ * of that Node. In this way, my_mutator() has full control over the
+ * copying process but need not directly deal with expression trees
+ * that it has no interest in.
+ *
+ * Just as for expression_tree_walker, the node types handled by
+ * expression_tree_mutator include all those normally found in target lists
+ * and qualifier clauses during the planning stage.
+ *
+ * expression_tree_mutator will handle SubLink nodes by recursing normally
+ * into the "testexpr" subtree (which is an expression belonging to the outer
+ * plan). It will also call the mutator on the sub-Query node; however, when
+ * expression_tree_mutator itself is called on a Query node, it does nothing
+ * and returns the unmodified Query node. The net effect is that unless the
+ * mutator does something special at a Query node, sub-selects will not be
+ * visited or modified; the original sub-select will be linked to by the new
+ * SubLink node. Mutators that want to descend into sub-selects will usually
+ * do so by recognizing Query nodes and calling query_tree_mutator (below).
+ *
+ * expression_tree_mutator will handle a SubPlan node by recursing into the
+ * "testexpr" and the "args" list (which belong to the outer plan), but it
+ * will simply copy the link to the inner plan, since that's typically what
+ * expression tree mutators want. A mutator that wants to modify the subplan
+ * can force appropriate behavior by recognizing SubPlan expression nodes
+ * and doing the right thing.
+ */
+
+Node *
+expression_tree_mutator(Node *node,
+ Node *(*mutator) (),
+ void *context)
+{
+ /*
+ * The mutator has already decided not to modify the current node, but we
+ * must call the mutator for any sub-nodes.
+ */
+
+#define FLATCOPY(newnode, node, nodetype) \
+ ( (newnode) = (nodetype *) palloc(sizeof(nodetype)), \
+ memcpy((newnode), (node), sizeof(nodetype)) )
+
+#define CHECKFLATCOPY(newnode, node, nodetype) \
+ ( AssertMacro(IsA((node), nodetype)), \
+ (newnode) = (nodetype *) palloc(sizeof(nodetype)), \
+ memcpy((newnode), (node), sizeof(nodetype)) )
+
+#define MUTATE(newfield, oldfield, fieldtype) \
+ ( (newfield) = (fieldtype) mutator((Node *) (oldfield), context) )
+
+ if (node == NULL)
+ return NULL;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ switch (nodeTag(node))
+ {
+ /*
+ * Primitive node types with no expression subnodes. Var and
+ * Const are frequent enough to deserve special cases, the others
+ * we just use copyObject for.
+ */
+ case T_Var:
+ {
+ Var *var = (Var *) node;
+ Var *newnode;
+
+ FLATCOPY(newnode, var, Var);
+ return (Node *) newnode;
+ }
+ break;
+ case T_Const:
+ {
+ Const *oldnode = (Const *) node;
+ Const *newnode;
+
+ FLATCOPY(newnode, oldnode, Const);
+ /* XXX we don't bother with datumCopy; should we? */
+ return (Node *) newnode;
+ }
+ break;
+ case T_Param:
+ case T_CaseTestExpr:
+ case T_SQLValueFunction:
+ case T_CoerceToDomainValue:
+ case T_SetToDefault:
+ case T_CurrentOfExpr:
+ case T_NextValueExpr:
+ case T_RangeTblRef:
+ case T_SortGroupClause:
+ case T_CTESearchClause:
+ return (Node *) copyObject(node);
+ case T_WithCheckOption:
+ {
+ WithCheckOption *wco = (WithCheckOption *) node;
+ WithCheckOption *newnode;
+
+ FLATCOPY(newnode, wco, WithCheckOption);
+ MUTATE(newnode->qual, wco->qual, Node *);
+ return (Node *) newnode;
+ }
+ case T_Aggref:
+ {
+ Aggref *aggref = (Aggref *) node;
+ Aggref *newnode;
+
+ FLATCOPY(newnode, aggref, Aggref);
+ /* assume mutation doesn't change types of arguments */
+ newnode->aggargtypes = list_copy(aggref->aggargtypes);
+ MUTATE(newnode->aggdirectargs, aggref->aggdirectargs, List *);
+ MUTATE(newnode->args, aggref->args, List *);
+ MUTATE(newnode->aggorder, aggref->aggorder, List *);
+ MUTATE(newnode->aggdistinct, aggref->aggdistinct, List *);
+ MUTATE(newnode->aggfilter, aggref->aggfilter, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_GroupingFunc:
+ {
+ GroupingFunc *grouping = (GroupingFunc *) node;
+ GroupingFunc *newnode;
+
+ FLATCOPY(newnode, grouping, GroupingFunc);
+ MUTATE(newnode->args, grouping->args, List *);
+
+ /*
+ * We assume here that mutating the arguments does not change
+ * the semantics, i.e. that the arguments are not mutated in a
+ * way that makes them semantically different from their
+ * previously matching expressions in the GROUP BY clause.
+ *
+ * If a mutator somehow wanted to do this, it would have to
+ * handle the refs and cols lists itself as appropriate.
+ */
+ newnode->refs = list_copy(grouping->refs);
+ newnode->cols = list_copy(grouping->cols);
+
+ return (Node *) newnode;
+ }
+ break;
+ case T_WindowFunc:
+ {
+ WindowFunc *wfunc = (WindowFunc *) node;
+ WindowFunc *newnode;
+
+ FLATCOPY(newnode, wfunc, WindowFunc);
+ MUTATE(newnode->args, wfunc->args, List *);
+ MUTATE(newnode->aggfilter, wfunc->aggfilter, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_SubscriptingRef:
+ {
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
+ SubscriptingRef *newnode;
+
+ FLATCOPY(newnode, sbsref, SubscriptingRef);
+ MUTATE(newnode->refupperindexpr, sbsref->refupperindexpr,
+ List *);
+ MUTATE(newnode->reflowerindexpr, sbsref->reflowerindexpr,
+ List *);
+ MUTATE(newnode->refexpr, sbsref->refexpr,
+ Expr *);
+ MUTATE(newnode->refassgnexpr, sbsref->refassgnexpr,
+ Expr *);
+
+ return (Node *) newnode;
+ }
+ break;
+ case T_FuncExpr:
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+ FuncExpr *newnode;
+
+ FLATCOPY(newnode, expr, FuncExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_NamedArgExpr:
+ {
+ NamedArgExpr *nexpr = (NamedArgExpr *) node;
+ NamedArgExpr *newnode;
+
+ FLATCOPY(newnode, nexpr, NamedArgExpr);
+ MUTATE(newnode->arg, nexpr->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_OpExpr:
+ {
+ OpExpr *expr = (OpExpr *) node;
+ OpExpr *newnode;
+
+ FLATCOPY(newnode, expr, OpExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_DistinctExpr:
+ {
+ DistinctExpr *expr = (DistinctExpr *) node;
+ DistinctExpr *newnode;
+
+ FLATCOPY(newnode, expr, DistinctExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_NullIfExpr:
+ {
+ NullIfExpr *expr = (NullIfExpr *) node;
+ NullIfExpr *newnode;
+
+ FLATCOPY(newnode, expr, NullIfExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+ ScalarArrayOpExpr *newnode;
+
+ FLATCOPY(newnode, expr, ScalarArrayOpExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_BoolExpr:
+ {
+ BoolExpr *expr = (BoolExpr *) node;
+ BoolExpr *newnode;
+
+ FLATCOPY(newnode, expr, BoolExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_SubLink:
+ {
+ SubLink *sublink = (SubLink *) node;
+ SubLink *newnode;
+
+ FLATCOPY(newnode, sublink, SubLink);
+ MUTATE(newnode->testexpr, sublink->testexpr, Node *);
+
+ /*
+ * Also invoke the mutator on the sublink's Query node, so it
+ * can recurse into the sub-query if it wants to.
+ */
+ MUTATE(newnode->subselect, sublink->subselect, Node *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_SubPlan:
+ {
+ SubPlan *subplan = (SubPlan *) node;
+ SubPlan *newnode;
+
+ FLATCOPY(newnode, subplan, SubPlan);
+ /* transform testexpr */
+ MUTATE(newnode->testexpr, subplan->testexpr, Node *);
+ /* transform args list (params to be passed to subplan) */
+ MUTATE(newnode->args, subplan->args, List *);
+ /* but not the sub-Plan itself, which is referenced as-is */
+ return (Node *) newnode;
+ }
+ break;
+ case T_AlternativeSubPlan:
+ {
+ AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
+ AlternativeSubPlan *newnode;
+
+ FLATCOPY(newnode, asplan, AlternativeSubPlan);
+ MUTATE(newnode->subplans, asplan->subplans, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_FieldSelect:
+ {
+ FieldSelect *fselect = (FieldSelect *) node;
+ FieldSelect *newnode;
+
+ FLATCOPY(newnode, fselect, FieldSelect);
+ MUTATE(newnode->arg, fselect->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_FieldStore:
+ {
+ FieldStore *fstore = (FieldStore *) node;
+ FieldStore *newnode;
+
+ FLATCOPY(newnode, fstore, FieldStore);
+ MUTATE(newnode->arg, fstore->arg, Expr *);
+ MUTATE(newnode->newvals, fstore->newvals, List *);
+ newnode->fieldnums = list_copy(fstore->fieldnums);
+ return (Node *) newnode;
+ }
+ break;
+ case T_RelabelType:
+ {
+ RelabelType *relabel = (RelabelType *) node;
+ RelabelType *newnode;
+
+ FLATCOPY(newnode, relabel, RelabelType);
+ MUTATE(newnode->arg, relabel->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CoerceViaIO:
+ {
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+ CoerceViaIO *newnode;
+
+ FLATCOPY(newnode, iocoerce, CoerceViaIO);
+ MUTATE(newnode->arg, iocoerce->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_ArrayCoerceExpr:
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+ ArrayCoerceExpr *newnode;
+
+ FLATCOPY(newnode, acoerce, ArrayCoerceExpr);
+ MUTATE(newnode->arg, acoerce->arg, Expr *);
+ MUTATE(newnode->elemexpr, acoerce->elemexpr, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_ConvertRowtypeExpr:
+ {
+ ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
+ ConvertRowtypeExpr *newnode;
+
+ FLATCOPY(newnode, convexpr, ConvertRowtypeExpr);
+ MUTATE(newnode->arg, convexpr->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CollateExpr:
+ {
+ CollateExpr *collate = (CollateExpr *) node;
+ CollateExpr *newnode;
+
+ FLATCOPY(newnode, collate, CollateExpr);
+ MUTATE(newnode->arg, collate->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CaseExpr:
+ {
+ CaseExpr *caseexpr = (CaseExpr *) node;
+ CaseExpr *newnode;
+
+ FLATCOPY(newnode, caseexpr, CaseExpr);
+ MUTATE(newnode->arg, caseexpr->arg, Expr *);
+ MUTATE(newnode->args, caseexpr->args, List *);
+ MUTATE(newnode->defresult, caseexpr->defresult, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CaseWhen:
+ {
+ CaseWhen *casewhen = (CaseWhen *) node;
+ CaseWhen *newnode;
+
+ FLATCOPY(newnode, casewhen, CaseWhen);
+ MUTATE(newnode->expr, casewhen->expr, Expr *);
+ MUTATE(newnode->result, casewhen->result, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_ArrayExpr:
+ {
+ ArrayExpr *arrayexpr = (ArrayExpr *) node;
+ ArrayExpr *newnode;
+
+ FLATCOPY(newnode, arrayexpr, ArrayExpr);
+ MUTATE(newnode->elements, arrayexpr->elements, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_RowExpr:
+ {
+ RowExpr *rowexpr = (RowExpr *) node;
+ RowExpr *newnode;
+
+ FLATCOPY(newnode, rowexpr, RowExpr);
+ MUTATE(newnode->args, rowexpr->args, List *);
+ /* Assume colnames needn't be duplicated */
+ return (Node *) newnode;
+ }
+ break;
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ RowCompareExpr *newnode;
+
+ FLATCOPY(newnode, rcexpr, RowCompareExpr);
+ MUTATE(newnode->largs, rcexpr->largs, List *);
+ MUTATE(newnode->rargs, rcexpr->rargs, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CoalesceExpr:
+ {
+ CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+ CoalesceExpr *newnode;
+
+ FLATCOPY(newnode, coalesceexpr, CoalesceExpr);
+ MUTATE(newnode->args, coalesceexpr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_MinMaxExpr:
+ {
+ MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+ MinMaxExpr *newnode;
+
+ FLATCOPY(newnode, minmaxexpr, MinMaxExpr);
+ MUTATE(newnode->args, minmaxexpr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+ XmlExpr *newnode;
+
+ FLATCOPY(newnode, xexpr, XmlExpr);
+ MUTATE(newnode->named_args, xexpr->named_args, List *);
+ /* assume mutator does not care about arg_names */
+ MUTATE(newnode->args, xexpr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_NullTest:
+ {
+ NullTest *ntest = (NullTest *) node;
+ NullTest *newnode;
+
+ FLATCOPY(newnode, ntest, NullTest);
+ MUTATE(newnode->arg, ntest->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_BooleanTest:
+ {
+ BooleanTest *btest = (BooleanTest *) node;
+ BooleanTest *newnode;
+
+ FLATCOPY(newnode, btest, BooleanTest);
+ MUTATE(newnode->arg, btest->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CoerceToDomain:
+ {
+ CoerceToDomain *ctest = (CoerceToDomain *) node;
+ CoerceToDomain *newnode;
+
+ FLATCOPY(newnode, ctest, CoerceToDomain);
+ MUTATE(newnode->arg, ctest->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_TargetEntry:
+ {
+ TargetEntry *targetentry = (TargetEntry *) node;
+ TargetEntry *newnode;
+
+ FLATCOPY(newnode, targetentry, TargetEntry);
+ MUTATE(newnode->expr, targetentry->expr, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_Query:
+ /* Do nothing with a sub-Query, per discussion above */
+ return node;
+ case T_WindowClause:
+ {
+ WindowClause *wc = (WindowClause *) node;
+ WindowClause *newnode;
+
+ FLATCOPY(newnode, wc, WindowClause);
+ MUTATE(newnode->partitionClause, wc->partitionClause, List *);
+ MUTATE(newnode->orderClause, wc->orderClause, List *);
+ MUTATE(newnode->startOffset, wc->startOffset, Node *);
+ MUTATE(newnode->endOffset, wc->endOffset, Node *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CTECycleClause:
+ {
+ CTECycleClause *cc = (CTECycleClause *) node;
+ CTECycleClause *newnode;
+
+ FLATCOPY(newnode, cc, CTECycleClause);
+ MUTATE(newnode->cycle_mark_value, cc->cycle_mark_value, Node *);
+ MUTATE(newnode->cycle_mark_default, cc->cycle_mark_default, Node *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_CommonTableExpr:
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) node;
+ CommonTableExpr *newnode;
+
+ FLATCOPY(newnode, cte, CommonTableExpr);
+
+ /*
+ * Also invoke the mutator on the CTE's Query node, so it can
+ * recurse into the sub-query if it wants to.
+ */
+ MUTATE(newnode->ctequery, cte->ctequery, Node *);
+
+ MUTATE(newnode->search_clause, cte->search_clause, CTESearchClause *);
+ MUTATE(newnode->cycle_clause, cte->cycle_clause, CTECycleClause *);
+
+ return (Node *) newnode;
+ }
+ break;
+ case T_List:
+ {
+ /*
+ * We assume the mutator isn't interested in the list nodes
+ * per se, so just invoke it on each list element. NOTE: this
+ * would fail badly on a list with integer elements!
+ */
+ List *resultlist;
+ ListCell *temp;
+
+ resultlist = NIL;
+ foreach(temp, (List *) node)
+ {
+ resultlist = lappend(resultlist,
+ mutator((Node *) lfirst(temp),
+ context));
+ }
+ return (Node *) resultlist;
+ }
+ break;
+ case T_FromExpr:
+ {
+ FromExpr *from = (FromExpr *) node;
+ FromExpr *newnode;
+
+ FLATCOPY(newnode, from, FromExpr);
+ MUTATE(newnode->fromlist, from->fromlist, List *);
+ MUTATE(newnode->quals, from->quals, Node *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_OnConflictExpr:
+ {
+ OnConflictExpr *oc = (OnConflictExpr *) node;
+ OnConflictExpr *newnode;
+
+ FLATCOPY(newnode, oc, OnConflictExpr);
+ MUTATE(newnode->arbiterElems, oc->arbiterElems, List *);
+ MUTATE(newnode->arbiterWhere, oc->arbiterWhere, Node *);
+ MUTATE(newnode->onConflictSet, oc->onConflictSet, List *);
+ MUTATE(newnode->onConflictWhere, oc->onConflictWhere, Node *);
+ MUTATE(newnode->exclRelTlist, oc->exclRelTlist, List *);
+
+ return (Node *) newnode;
+ }
+ break;
+ case T_PartitionPruneStepOp:
+ {
+ PartitionPruneStepOp *opstep = (PartitionPruneStepOp *) node;
+ PartitionPruneStepOp *newnode;
+
+ FLATCOPY(newnode, opstep, PartitionPruneStepOp);
+ MUTATE(newnode->exprs, opstep->exprs, List *);
+
+ return (Node *) newnode;
+ }
+ break;
+ case T_PartitionPruneStepCombine:
+ /* no expression sub-nodes */
+ return (Node *) copyObject(node);
+ case T_JoinExpr:
+ {
+ JoinExpr *join = (JoinExpr *) node;
+ JoinExpr *newnode;
+
+ FLATCOPY(newnode, join, JoinExpr);
+ MUTATE(newnode->larg, join->larg, Node *);
+ MUTATE(newnode->rarg, join->rarg, Node *);
+ MUTATE(newnode->quals, join->quals, Node *);
+ /* We do not mutate alias or using by default */
+ return (Node *) newnode;
+ }
+ break;
+ case T_SetOperationStmt:
+ {
+ SetOperationStmt *setop = (SetOperationStmt *) node;
+ SetOperationStmt *newnode;
+
+ FLATCOPY(newnode, setop, SetOperationStmt);
+ MUTATE(newnode->larg, setop->larg, Node *);
+ MUTATE(newnode->rarg, setop->rarg, Node *);
+ /* We do not mutate groupClauses by default */
+ return (Node *) newnode;
+ }
+ break;
+ case T_IndexClause:
+ {
+ IndexClause *iclause = (IndexClause *) node;
+ IndexClause *newnode;
+
+ FLATCOPY(newnode, iclause, IndexClause);
+ MUTATE(newnode->rinfo, iclause->rinfo, RestrictInfo *);
+ MUTATE(newnode->indexquals, iclause->indexquals, List *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_PlaceHolderVar:
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ PlaceHolderVar *newnode;
+
+ FLATCOPY(newnode, phv, PlaceHolderVar);
+ MUTATE(newnode->phexpr, phv->phexpr, Expr *);
+ /* Assume we need not copy the relids bitmapset */
+ return (Node *) newnode;
+ }
+ break;
+ case T_InferenceElem:
+ {
+ InferenceElem *inferenceelemdexpr = (InferenceElem *) node;
+ InferenceElem *newnode;
+
+ FLATCOPY(newnode, inferenceelemdexpr, InferenceElem);
+ MUTATE(newnode->expr, newnode->expr, Node *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_AppendRelInfo:
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+ AppendRelInfo *newnode;
+
+ FLATCOPY(newnode, appinfo, AppendRelInfo);
+ MUTATE(newnode->translated_vars, appinfo->translated_vars, List *);
+ /* Assume nothing need be done with parent_colnos[] */
+ return (Node *) newnode;
+ }
+ break;
+ case T_PlaceHolderInfo:
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+ PlaceHolderInfo *newnode;
+
+ FLATCOPY(newnode, phinfo, PlaceHolderInfo);
+ MUTATE(newnode->ph_var, phinfo->ph_var, PlaceHolderVar *);
+ /* Assume we need not copy the relids bitmapsets */
+ return (Node *) newnode;
+ }
+ break;
+ case T_RangeTblFunction:
+ {
+ RangeTblFunction *rtfunc = (RangeTblFunction *) node;
+ RangeTblFunction *newnode;
+
+ FLATCOPY(newnode, rtfunc, RangeTblFunction);
+ MUTATE(newnode->funcexpr, rtfunc->funcexpr, Node *);
+ /* Assume we need not copy the coldef info lists */
+ return (Node *) newnode;
+ }
+ break;
+ case T_TableSampleClause:
+ {
+ TableSampleClause *tsc = (TableSampleClause *) node;
+ TableSampleClause *newnode;
+
+ FLATCOPY(newnode, tsc, TableSampleClause);
+ MUTATE(newnode->args, tsc->args, List *);
+ MUTATE(newnode->repeatable, tsc->repeatable, Expr *);
+ return (Node *) newnode;
+ }
+ break;
+ case T_TableFunc:
+ {
+ TableFunc *tf = (TableFunc *) node;
+ TableFunc *newnode;
+
+ FLATCOPY(newnode, tf, TableFunc);
+ MUTATE(newnode->ns_uris, tf->ns_uris, List *);
+ MUTATE(newnode->docexpr, tf->docexpr, Node *);
+ MUTATE(newnode->rowexpr, tf->rowexpr, Node *);
+ MUTATE(newnode->colexprs, tf->colexprs, List *);
+ MUTATE(newnode->coldefexprs, tf->coldefexprs, List *);
+ return (Node *) newnode;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
+ break;
+ }
+ /* can't get here, but keep compiler happy */
+ return NULL;
+}
+
+
+/*
+ * query_tree_mutator --- initiate modification of a Query's expressions
+ *
+ * This routine exists just to reduce the number of places that need to know
+ * where all the expression subtrees of a Query are. Note it can be used
+ * for starting a walk at top level of a Query regardless of whether the
+ * mutator intends to descend into subqueries. It is also useful for
+ * descending into subqueries within a mutator.
+ *
+ * Some callers want to suppress mutating of certain items in the Query,
+ * typically because they need to process them specially, or don't actually
+ * want to recurse into subqueries. This is supported by the flags argument,
+ * which is the bitwise OR of flag values to suppress mutating of
+ * indicated items. (More flag bits may be added as needed.)
+ *
+ * Normally the Query node itself is copied, but some callers want it to be
+ * modified in-place; they must pass QTW_DONT_COPY_QUERY in flags. All
+ * modified substructure is safely copied in any case.
+ */
+Query *
+query_tree_mutator(Query *query,
+ Node *(*mutator) (),
+ void *context,
+ int flags)
+{
+ Assert(query != NULL && IsA(query, Query));
+
+ if (!(flags & QTW_DONT_COPY_QUERY))
+ {
+ Query *newquery;
+
+ FLATCOPY(newquery, query, Query);
+ query = newquery;
+ }
+
+ MUTATE(query->targetList, query->targetList, List *);
+ MUTATE(query->withCheckOptions, query->withCheckOptions, List *);
+ MUTATE(query->onConflict, query->onConflict, OnConflictExpr *);
+ MUTATE(query->returningList, query->returningList, List *);
+ MUTATE(query->jointree, query->jointree, FromExpr *);
+ MUTATE(query->setOperations, query->setOperations, Node *);
+ MUTATE(query->havingQual, query->havingQual, Node *);
+ MUTATE(query->limitOffset, query->limitOffset, Node *);
+ MUTATE(query->limitCount, query->limitCount, Node *);
+
+ /*
+ * Most callers aren't interested in SortGroupClause nodes since those
+ * don't contain actual expressions. However they do contain OIDs, which
+ * may be of interest to some mutators.
+ */
+
+ if ((flags & QTW_EXAMINE_SORTGROUP))
+ {
+ MUTATE(query->groupClause, query->groupClause, List *);
+ MUTATE(query->windowClause, query->windowClause, List *);
+ MUTATE(query->sortClause, query->sortClause, List *);
+ MUTATE(query->distinctClause, query->distinctClause, List *);
+ }
+ else
+ {
+ /*
+ * But we need to mutate the expressions under WindowClause nodes even
+ * if we're not interested in SortGroupClause nodes.
+ */
+ List *resultlist;
+ ListCell *temp;
+
+ resultlist = NIL;
+ foreach(temp, query->windowClause)
+ {
+ WindowClause *wc = lfirst_node(WindowClause, temp);
+ WindowClause *newnode;
+
+ FLATCOPY(newnode, wc, WindowClause);
+ MUTATE(newnode->startOffset, wc->startOffset, Node *);
+ MUTATE(newnode->endOffset, wc->endOffset, Node *);
+
+ resultlist = lappend(resultlist, (Node *) newnode);
+ }
+ query->windowClause = resultlist;
+ }
+
+ /*
+ * groupingSets and rowMarks are not mutated:
+ *
+ * groupingSets contain only ressortgroup refs (integers) which are
+ * meaningless without the groupClause or tlist. Accordingly, any mutator
+ * that needs to care about them needs to handle them itself in its Query
+ * processing.
+ *
+ * rowMarks contains only rangetable indexes (and flags etc.) and
+ * therefore should be handled at Query level similarly.
+ */
+
+ if (!(flags & QTW_IGNORE_CTE_SUBQUERIES))
+ MUTATE(query->cteList, query->cteList, List *);
+ else /* else copy CTE list as-is */
+ query->cteList = copyObject(query->cteList);
+ query->rtable = range_table_mutator(query->rtable,
+ mutator, context, flags);
+ return query;
+}
+
+/*
+ * range_table_mutator is just the part of query_tree_mutator that processes
+ * a query's rangetable. This is split out since it can be useful on
+ * its own.
+ */
+List *
+range_table_mutator(List *rtable,
+ Node *(*mutator) (),
+ void *context,
+ int flags)
+{
+ List *newrt = NIL;
+ ListCell *rt;
+
+ foreach(rt, rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+ RangeTblEntry *newrte;
+
+ FLATCOPY(newrte, rte, RangeTblEntry);
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ MUTATE(newrte->tablesample, rte->tablesample,
+ TableSampleClause *);
+ /* we don't bother to copy eref, aliases, etc; OK? */
+ break;
+ case RTE_SUBQUERY:
+ if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
+ {
+ CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
+ MUTATE(newrte->subquery, newrte->subquery, Query *);
+ }
+ else
+ {
+ /* else, copy RT subqueries as-is */
+ newrte->subquery = copyObject(rte->subquery);
+ }
+ break;
+ case RTE_JOIN:
+ if (!(flags & QTW_IGNORE_JOINALIASES))
+ MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
+ else
+ {
+ /* else, copy join aliases as-is */
+ newrte->joinaliasvars = copyObject(rte->joinaliasvars);
+ }
+ break;
+ case RTE_FUNCTION:
+ MUTATE(newrte->functions, rte->functions, List *);
+ break;
+ case RTE_TABLEFUNC:
+ MUTATE(newrte->tablefunc, rte->tablefunc, TableFunc *);
+ break;
+ case RTE_VALUES:
+ MUTATE(newrte->values_lists, rte->values_lists, List *);
+ break;
+ case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
+ case RTE_RESULT:
+ /* nothing to do */
+ break;
+ }
+ MUTATE(newrte->securityQuals, rte->securityQuals, List *);
+ newrt = lappend(newrt, newrte);
+ }
+ return newrt;
+}
+
+/*
+ * query_or_expression_tree_walker --- hybrid form
+ *
+ * This routine will invoke query_tree_walker if called on a Query node,
+ * else will invoke the walker directly. This is a useful way of starting
+ * the recursion when the walker's normal change of state is not appropriate
+ * for the outermost Query node.
+ */
+bool
+query_or_expression_tree_walker(Node *node,
+ bool (*walker) (),
+ void *context,
+ int flags)
+{
+ if (node && IsA(node, Query))
+ return query_tree_walker((Query *) node,
+ walker,
+ context,
+ flags);
+ else
+ return walker(node, context);
+}
+
+/*
+ * query_or_expression_tree_mutator --- hybrid form
+ *
+ * This routine will invoke query_tree_mutator if called on a Query node,
+ * else will invoke the mutator directly. This is a useful way of starting
+ * the recursion when the mutator's normal change of state is not appropriate
+ * for the outermost Query node.
+ */
+Node *
+query_or_expression_tree_mutator(Node *node,
+ Node *(*mutator) (),
+ void *context,
+ int flags)
+{
+ if (node && IsA(node, Query))
+ return (Node *) query_tree_mutator((Query *) node,
+ mutator,
+ context,
+ flags);
+ else
+ return mutator(node, context);
+}
+
+
+/*
+ * raw_expression_tree_walker --- walk raw parse trees
+ *
+ * This has exactly the same API as expression_tree_walker, but instead of
+ * walking post-analysis parse trees, it knows how to walk the node types
+ * found in raw grammar output. (There is not currently any need for a
+ * combined walker, so we keep them separate in the name of efficiency.)
+ * Unlike expression_tree_walker, there is no special rule about query
+ * boundaries: we descend to everything that's possibly interesting.
+ *
+ * Currently, the node type coverage here extends only to DML statements
+ * (SELECT/INSERT/UPDATE/DELETE) and nodes that can appear in them, because
+ * this is used mainly during analysis of CTEs, and only DML statements can
+ * appear in CTEs.
+ */
+bool
+raw_expression_tree_walker(Node *node,
+ bool (*walker) (),
+ void *context)
+{
+ ListCell *temp;
+
+ /*
+ * The walker has already visited the current node, and so we need only
+ * recurse into any sub-nodes it has.
+ */
+ if (node == NULL)
+ return false;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ switch (nodeTag(node))
+ {
+ case T_SetToDefault:
+ case T_CurrentOfExpr:
+ case T_SQLValueFunction:
+ case T_Integer:
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ case T_Null:
+ case T_ParamRef:
+ case T_A_Const:
+ case T_A_Star:
+ /* primitive node types with no subnodes */
+ break;
+ case T_Alias:
+ /* we assume the colnames list isn't interesting */
+ break;
+ case T_RangeVar:
+ return walker(((RangeVar *) node)->alias, context);
+ case T_GroupingFunc:
+ return walker(((GroupingFunc *) node)->args, context);
+ case T_SubLink:
+ {
+ SubLink *sublink = (SubLink *) node;
+
+ if (walker(sublink->testexpr, context))
+ return true;
+ /* we assume the operName is not interesting */
+ if (walker(sublink->subselect, context))
+ return true;
+ }
+ break;
+ case T_CaseExpr:
+ {
+ CaseExpr *caseexpr = (CaseExpr *) node;
+
+ if (walker(caseexpr->arg, context))
+ return true;
+ /* we assume walker doesn't care about CaseWhens, either */
+ foreach(temp, caseexpr->args)
+ {
+ CaseWhen *when = lfirst_node(CaseWhen, temp);
+
+ if (walker(when->expr, context))
+ return true;
+ if (walker(when->result, context))
+ return true;
+ }
+ if (walker(caseexpr->defresult, context))
+ return true;
+ }
+ break;
+ case T_RowExpr:
+ /* Assume colnames isn't interesting */
+ return walker(((RowExpr *) node)->args, context);
+ case T_CoalesceExpr:
+ return walker(((CoalesceExpr *) node)->args, context);
+ case T_MinMaxExpr:
+ return walker(((MinMaxExpr *) node)->args, context);
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+
+ if (walker(xexpr->named_args, context))
+ return true;
+ /* we assume walker doesn't care about arg_names */
+ if (walker(xexpr->args, context))
+ return true;
+ }
+ break;
+ case T_NullTest:
+ return walker(((NullTest *) node)->arg, context);
+ case T_BooleanTest:
+ return walker(((BooleanTest *) node)->arg, context);
+ case T_JoinExpr:
+ {
+ JoinExpr *join = (JoinExpr *) node;
+
+ if (walker(join->larg, context))
+ return true;
+ if (walker(join->rarg, context))
+ return true;
+ if (walker(join->quals, context))
+ return true;
+ if (walker(join->alias, context))
+ return true;
+ /* using list is deemed uninteresting */
+ }
+ break;
+ case T_IntoClause:
+ {
+ IntoClause *into = (IntoClause *) node;
+
+ if (walker(into->rel, context))
+ return true;
+ /* colNames, options are deemed uninteresting */
+ /* viewQuery should be null in raw parsetree, but check it */
+ if (walker(into->viewQuery, context))
+ return true;
+ }
+ break;
+ case T_List:
+ foreach(temp, (List *) node)
+ {
+ if (walker((Node *) lfirst(temp), context))
+ return true;
+ }
+ break;
+ case T_InsertStmt:
+ {
+ InsertStmt *stmt = (InsertStmt *) node;
+
+ if (walker(stmt->relation, context))
+ return true;
+ if (walker(stmt->cols, context))
+ return true;
+ if (walker(stmt->selectStmt, context))
+ return true;
+ if (walker(stmt->onConflictClause, context))
+ return true;
+ if (walker(stmt->returningList, context))
+ return true;
+ if (walker(stmt->withClause, context))
+ return true;
+ }
+ break;
+ case T_DeleteStmt:
+ {
+ DeleteStmt *stmt = (DeleteStmt *) node;
+
+ if (walker(stmt->relation, context))
+ return true;
+ if (walker(stmt->usingClause, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ if (walker(stmt->returningList, context))
+ return true;
+ if (walker(stmt->withClause, context))
+ return true;
+ }
+ break;
+ case T_UpdateStmt:
+ {
+ UpdateStmt *stmt = (UpdateStmt *) node;
+
+ if (walker(stmt->relation, context))
+ return true;
+ if (walker(stmt->targetList, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ if (walker(stmt->fromClause, context))
+ return true;
+ if (walker(stmt->returningList, context))
+ return true;
+ if (walker(stmt->withClause, context))
+ return true;
+ }
+ break;
+ case T_SelectStmt:
+ {
+ SelectStmt *stmt = (SelectStmt *) node;
+
+ if (walker(stmt->distinctClause, context))
+ return true;
+ if (walker(stmt->intoClause, context))
+ return true;
+ if (walker(stmt->targetList, context))
+ return true;
+ if (walker(stmt->fromClause, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ if (walker(stmt->groupClause, context))
+ return true;
+ if (walker(stmt->havingClause, context))
+ return true;
+ if (walker(stmt->windowClause, context))
+ return true;
+ if (walker(stmt->valuesLists, context))
+ return true;
+ if (walker(stmt->sortClause, context))
+ return true;
+ if (walker(stmt->limitOffset, context))
+ return true;
+ if (walker(stmt->limitCount, context))
+ return true;
+ if (walker(stmt->lockingClause, context))
+ return true;
+ if (walker(stmt->withClause, context))
+ return true;
+ if (walker(stmt->larg, context))
+ return true;
+ if (walker(stmt->rarg, context))
+ return true;
+ }
+ break;
+ case T_PLAssignStmt:
+ {
+ PLAssignStmt *stmt = (PLAssignStmt *) node;
+
+ if (walker(stmt->indirection, context))
+ return true;
+ if (walker(stmt->val, context))
+ return true;
+ }
+ break;
+ case T_A_Expr:
+ {
+ A_Expr *expr = (A_Expr *) node;
+
+ if (walker(expr->lexpr, context))
+ return true;
+ if (walker(expr->rexpr, context))
+ return true;
+ /* operator name is deemed uninteresting */
+ }
+ break;
+ case T_BoolExpr:
+ {
+ BoolExpr *expr = (BoolExpr *) node;
+
+ if (walker(expr->args, context))
+ return true;
+ }
+ break;
+ case T_ColumnRef:
+ /* we assume the fields contain nothing interesting */
+ break;
+ case T_FuncCall:
+ {
+ FuncCall *fcall = (FuncCall *) node;
+
+ if (walker(fcall->args, context))
+ return true;
+ if (walker(fcall->agg_order, context))
+ return true;
+ if (walker(fcall->agg_filter, context))
+ return true;
+ if (walker(fcall->over, context))
+ return true;
+ /* function name is deemed uninteresting */
+ }
+ break;
+ case T_NamedArgExpr:
+ return walker(((NamedArgExpr *) node)->arg, context);
+ case T_A_Indices:
+ {
+ A_Indices *indices = (A_Indices *) node;
+
+ if (walker(indices->lidx, context))
+ return true;
+ if (walker(indices->uidx, context))
+ return true;
+ }
+ break;
+ case T_A_Indirection:
+ {
+ A_Indirection *indir = (A_Indirection *) node;
+
+ if (walker(indir->arg, context))
+ return true;
+ if (walker(indir->indirection, context))
+ return true;
+ }
+ break;
+ case T_A_ArrayExpr:
+ return walker(((A_ArrayExpr *) node)->elements, context);
+ case T_ResTarget:
+ {
+ ResTarget *rt = (ResTarget *) node;
+
+ if (walker(rt->indirection, context))
+ return true;
+ if (walker(rt->val, context))
+ return true;
+ }
+ break;
+ case T_MultiAssignRef:
+ return walker(((MultiAssignRef *) node)->source, context);
+ case T_TypeCast:
+ {
+ TypeCast *tc = (TypeCast *) node;
+
+ if (walker(tc->arg, context))
+ return true;
+ if (walker(tc->typeName, context))
+ return true;
+ }
+ break;
+ case T_CollateClause:
+ return walker(((CollateClause *) node)->arg, context);
+ case T_SortBy:
+ return walker(((SortBy *) node)->node, context);
+ case T_WindowDef:
+ {
+ WindowDef *wd = (WindowDef *) node;
+
+ if (walker(wd->partitionClause, context))
+ return true;
+ if (walker(wd->orderClause, context))
+ return true;
+ if (walker(wd->startOffset, context))
+ return true;
+ if (walker(wd->endOffset, context))
+ return true;
+ }
+ break;
+ case T_RangeSubselect:
+ {
+ RangeSubselect *rs = (RangeSubselect *) node;
+
+ if (walker(rs->subquery, context))
+ return true;
+ if (walker(rs->alias, context))
+ return true;
+ }
+ break;
+ case T_RangeFunction:
+ {
+ RangeFunction *rf = (RangeFunction *) node;
+
+ if (walker(rf->functions, context))
+ return true;
+ if (walker(rf->alias, context))
+ return true;
+ if (walker(rf->coldeflist, context))
+ return true;
+ }
+ break;
+ case T_RangeTableSample:
+ {
+ RangeTableSample *rts = (RangeTableSample *) node;
+
+ if (walker(rts->relation, context))
+ return true;
+ /* method name is deemed uninteresting */
+ if (walker(rts->args, context))
+ return true;
+ if (walker(rts->repeatable, context))
+ return true;
+ }
+ break;
+ case T_RangeTableFunc:
+ {
+ RangeTableFunc *rtf = (RangeTableFunc *) node;
+
+ if (walker(rtf->docexpr, context))
+ return true;
+ if (walker(rtf->rowexpr, context))
+ return true;
+ if (walker(rtf->namespaces, context))
+ return true;
+ if (walker(rtf->columns, context))
+ return true;
+ if (walker(rtf->alias, context))
+ return true;
+ }
+ break;
+ case T_RangeTableFuncCol:
+ {
+ RangeTableFuncCol *rtfc = (RangeTableFuncCol *) node;
+
+ if (walker(rtfc->colexpr, context))
+ return true;
+ if (walker(rtfc->coldefexpr, context))
+ return true;
+ }
+ break;
+ case T_TypeName:
+ {
+ TypeName *tn = (TypeName *) node;
+
+ if (walker(tn->typmods, context))
+ return true;
+ if (walker(tn->arrayBounds, context))
+ return true;
+ /* type name itself is deemed uninteresting */
+ }
+ break;
+ case T_ColumnDef:
+ {
+ ColumnDef *coldef = (ColumnDef *) node;
+
+ if (walker(coldef->typeName, context))
+ return true;
+ if (walker(coldef->compression, context))
+ return true;
+ if (walker(coldef->raw_default, context))
+ return true;
+ if (walker(coldef->collClause, context))
+ return true;
+ /* for now, constraints are ignored */
+ }
+ break;
+ case T_IndexElem:
+ {
+ IndexElem *indelem = (IndexElem *) node;
+
+ if (walker(indelem->expr, context))
+ return true;
+ /* collation and opclass names are deemed uninteresting */
+ }
+ break;
+ case T_GroupingSet:
+ return walker(((GroupingSet *) node)->content, context);
+ case T_LockingClause:
+ return walker(((LockingClause *) node)->lockedRels, context);
+ case T_XmlSerialize:
+ {
+ XmlSerialize *xs = (XmlSerialize *) node;
+
+ if (walker(xs->expr, context))
+ return true;
+ if (walker(xs->typeName, context))
+ return true;
+ }
+ break;
+ case T_WithClause:
+ return walker(((WithClause *) node)->ctes, context);
+ case T_InferClause:
+ {
+ InferClause *stmt = (InferClause *) node;
+
+ if (walker(stmt->indexElems, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ }
+ break;
+ case T_OnConflictClause:
+ {
+ OnConflictClause *stmt = (OnConflictClause *) node;
+
+ if (walker(stmt->infer, context))
+ return true;
+ if (walker(stmt->targetList, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ }
+ break;
+ case T_CommonTableExpr:
+ /* search_clause and cycle_clause are not interesting here */
+ return walker(((CommonTableExpr *) node)->ctequery, context);
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
+ break;
+ }
+ return false;
+}
+
+/*
+ * planstate_tree_walker --- walk plan state trees
+ *
+ * The walker has already visited the current node, and so we need only
+ * recurse into any sub-nodes it has.
+ */
+bool
+planstate_tree_walker(PlanState *planstate,
+ bool (*walker) (),
+ void *context)
+{
+ Plan *plan = planstate->plan;
+ ListCell *lc;
+
+ /* Guard against stack overflow due to overly complex plan trees */
+ check_stack_depth();
+
+ /* initPlan-s */
+ if (planstate_walk_subplans(planstate->initPlan, walker, context))
+ return true;
+
+ /* lefttree */
+ if (outerPlanState(planstate))
+ {
+ if (walker(outerPlanState(planstate), context))
+ return true;
+ }
+
+ /* righttree */
+ if (innerPlanState(planstate))
+ {
+ if (walker(innerPlanState(planstate), context))
+ return true;
+ }
+
+ /* special child plans */
+ switch (nodeTag(plan))
+ {
+ case T_Append:
+ if (planstate_walk_members(((AppendState *) planstate)->appendplans,
+ ((AppendState *) planstate)->as_nplans,
+ walker, context))
+ return true;
+ break;
+ case T_MergeAppend:
+ if (planstate_walk_members(((MergeAppendState *) planstate)->mergeplans,
+ ((MergeAppendState *) planstate)->ms_nplans,
+ walker, context))
+ return true;
+ break;
+ case T_BitmapAnd:
+ if (planstate_walk_members(((BitmapAndState *) planstate)->bitmapplans,
+ ((BitmapAndState *) planstate)->nplans,
+ walker, context))
+ return true;
+ break;
+ case T_BitmapOr:
+ if (planstate_walk_members(((BitmapOrState *) planstate)->bitmapplans,
+ ((BitmapOrState *) planstate)->nplans,
+ walker, context))
+ return true;
+ break;
+ case T_SubqueryScan:
+ if (walker(((SubqueryScanState *) planstate)->subplan, context))
+ return true;
+ break;
+ case T_CustomScan:
+ foreach(lc, ((CustomScanState *) planstate)->custom_ps)
+ {
+ if (walker((PlanState *) lfirst(lc), context))
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* subPlan-s */
+ if (planstate_walk_subplans(planstate->subPlan, walker, context))
+ return true;
+
+ return false;
+}
+
+/*
+ * Walk a list of SubPlans (or initPlans, which also use SubPlan nodes).
+ */
+static bool
+planstate_walk_subplans(List *plans,
+ bool (*walker) (),
+ void *context)
+{
+ ListCell *lc;
+
+ foreach(lc, plans)
+ {
+ SubPlanState *sps = lfirst_node(SubPlanState, lc);
+
+ if (walker(sps->planstate, context))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Walk the constituent plans of a ModifyTable, Append, MergeAppend,
+ * BitmapAnd, or BitmapOr node.
+ */
+static bool
+planstate_walk_members(PlanState **planstates, int nplans,
+ bool (*walker) (), void *context)
+{
+ int j;
+
+ for (j = 0; j < nplans; j++)
+ {
+ if (walker(planstates[j], context))
+ return true;
+ }
+
+ return false;
+}