/*------------------------------------------------------------------------- * * nodeFuncs.c * Various general-purpose manipulations of Node trees * * Portions Copyright (c) 1996-2020, 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: { const SubscriptingRef *sbsref = (const SubscriptingRef *) expr; /* slice and/or store operations yield the container type */ if (sbsref->reflowerindexpr || sbsref->refassgnexpr) type = sbsref->refcontainertype; else type = sbsref->refelemtype; } 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: /* typmod is the same for container or element */ 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 */ } /* Avoid recursion for some cases that parser checks not to return a set */ if (IsA(node, Aggref)) 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: coll = InvalidOid; /* result is always boolean */ break; case T_BoolExpr: coll = InvalidOid; /* result is always boolean */ 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, result is RECORD or BOOLEAN */ coll = InvalidOid; } } 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, result is RECORD or BOOLEAN */ coll = InvalidOid; } } 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: coll = InvalidOid; /* result is always composite */ 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: coll = InvalidOid; /* result is always composite */ 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: coll = InvalidOid; /* result is always composite */ break; case T_RowCompareExpr: coll = InvalidOid; /* result is always boolean */ 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: coll = InvalidOid; /* result is always boolean */ break; case T_BooleanTest: coll = InvalidOid; /* result is always boolean */ 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: coll = InvalidOid; /* result is always boolean */ break; case T_NextValueExpr: coll = InvalidOid; /* result is always an integer type */ 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: Assert(!OidIsValid(collation)); /* result is always boolean */ break; case T_BoolExpr: Assert(!OidIsValid(collation)); /* result is always boolean */ 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: Assert(!OidIsValid(collation)); /* result is always composite */ 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: Assert(!OidIsValid(collation)); /* result is always composite */ break; case T_CaseExpr: ((CaseExpr *) expr)->casecollid = collation; break; case T_ArrayExpr: ((ArrayExpr *) expr)->array_collid = collation; break; case T_RowExpr: Assert(!OidIsValid(collation)); /* result is always composite */ break; case T_RowCompareExpr: Assert(!OidIsValid(collation)); /* result is always boolean */ 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: Assert(!OidIsValid(collation)); /* result is always boolean */ break; case T_BooleanTest: Assert(!OidIsValid(collation)); /* result is always boolean */ 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: Assert(!OidIsValid(collation)); /* result is always boolean */ break; case T_NextValueExpr: Assert(!OidIsValid(collation)); /* result is always an integer * type */ 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_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: /* 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_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. */ return walker(cte->ctequery, context); } 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: 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_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 *); 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_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->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: 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_ModifyTable: if (planstate_walk_members(((ModifyTableState *) planstate)->mt_plans, ((ModifyTableState *) planstate)->mt_nplans, walker, context)) return true; break; 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; }