+ /* Record the external dependencies */
+ recordMultipleDependencies(depender,
+ context.addrs->refs, context.addrs->numrefs,
+ behavior);
+
+ free_object_addresses(context.addrs);
+}
+
+/*
+ * Recursively search an expression tree for object references.
+ *
+ * Note: we avoid creating references to columns of tables that participate
+ * in an SQL JOIN construct, but are not actually used anywhere in the query.
+ * To do so, we do not scan the joinaliasvars list of a join RTE while
+ * scanning the query rangetable, but instead scan each individual entry
+ * of the alias list when we find a reference to it.
+ *
+ * Note: in many cases we do not need to create dependencies on the datatypes
+ * involved in an expression, because we'll have an indirect dependency via
+ * some other object. For instance Var nodes depend on a column which depends
+ * on the datatype, and OpExpr nodes depend on the operator which depends on
+ * the datatype. However we do need a type dependency if there is no such
+ * indirect dependency, as for example in Const and CoerceToDomain nodes.
+ */
+static bool
+find_expr_references_walker(Node *node,
+ find_expr_references_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ List *rtable;
+ RangeTblEntry *rte;
+
+ /* Find matching rtable entry, or complain if not found */
+ if (var->varlevelsup >= list_length(context->rtables))
+ elog(ERROR, "invalid varlevelsup %d", var->varlevelsup);
+ rtable = (List *) list_nth(context->rtables, var->varlevelsup);
+ if (var->varno <= 0 || var->varno > list_length(rtable))
+ elog(ERROR, "invalid varno %d", var->varno);
+ rte = rt_fetch(var->varno, rtable);
+
+ /*
+ * A whole-row Var references no specific columns, so adds no new
+ * dependency.
+ */
+ if (var->varattno == InvalidAttrNumber)
+ return false;
+ if (rte->rtekind == RTE_RELATION)
+ {
+ /* If it's a plain relation, reference this column */
+ add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
+ context->addrs);
+ }
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ /* Scan join output column to add references to join inputs */
+ List *save_rtables;
+
+ /* We must make the context appropriate for join's level */
+ save_rtables = context->rtables;
+ context->rtables = list_copy_tail(context->rtables,
+ var->varlevelsup);
+ if (var->varattno <= 0 ||
+ var->varattno > list_length(rte->joinaliasvars))
+ elog(ERROR, "invalid varattno %d", var->varattno);
+ find_expr_references_walker((Node *) list_nth(rte->joinaliasvars,
+ var->varattno - 1),
+ context);
+ list_free(context->rtables);
+ context->rtables = save_rtables;
+ }
+ return false;
+ }
+ if (IsA(node, Const))
+ {
+ Const *con = (Const *) node;
+ Oid objoid;
+
+ /* A constant must depend on the constant's datatype */
+ add_object_address(OCLASS_TYPE, con->consttype, 0,
+ context->addrs);
+
+ /*
+ * If it's a regclass or similar literal referring to an existing
+ * object, add a reference to that object. (Currently, only the
+ * regclass and regconfig cases have any likely use, but we may as
+ * well handle all the OID-alias datatypes consistently.)
+ */
+ if (!con->constisnull)
+ {
+ switch (con->consttype)
+ {
+ case REGPROCOID:
+ case REGPROCEDUREOID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(PROCOID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_PROC, objoid, 0,
+ context->addrs);
+ break;
+ case REGOPEROID:
+ case REGOPERATOROID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(OPEROID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_OPERATOR, objoid, 0,
+ context->addrs);
+ break;
+ case REGCLASSOID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_CLASS, objoid, 0,
+ context->addrs);
+ break;
+ case REGTYPEOID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(TYPEOID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_TYPE, objoid, 0,
+ context->addrs);
+ break;
+ case REGCONFIGOID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(TSCONFIGOID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_TSCONFIG, objoid, 0,
+ context->addrs);
+ break;
+ case REGDICTIONARYOID:
+ objoid = DatumGetObjectId(con->constvalue);
+ if (SearchSysCacheExists(TSDICTOID,
+ ObjectIdGetDatum(objoid),
+ 0, 0, 0))
+ add_object_address(OCLASS_TSDICT, objoid, 0,
+ context->addrs);
+ break;
+ }
+ }
+ return false;
+ }
+ if (IsA(node, Param))
+ {
+ Param *param = (Param *) node;
+
+ /* A parameter must depend on the parameter's datatype */
+ add_object_address(OCLASS_TYPE, param->paramtype, 0,
+ context->addrs);
+ }
+ if (IsA(node, FuncExpr))
+ {
+ FuncExpr *funcexpr = (FuncExpr *) node;
+
+ add_object_address(OCLASS_PROC, funcexpr->funcid, 0,
+ context->addrs);
+ /* fall through to examine arguments */
+ }
+ if (IsA(node, OpExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) node;
+
+ add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
+ context->addrs);
+ /* fall through to examine arguments */
+ }
+ if (IsA(node, DistinctExpr))
+ {
+ DistinctExpr *distinctexpr = (DistinctExpr *) node;
+
+ add_object_address(OCLASS_OPERATOR, distinctexpr->opno, 0,
+ context->addrs);
+ /* fall through to examine arguments */
+ }
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+
+ add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
+ context->addrs);
+ /* fall through to examine arguments */
+ }
+ if (IsA(node, NullIfExpr))
+ {
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
+
+ add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
+ context->addrs);
+ /* fall through to examine arguments */
+ }
+ if (IsA(node, Aggref))
+ {
+ Aggref *aggref = (Aggref *) node;
+
+ add_object_address(OCLASS_PROC, aggref->aggfnoid, 0,
+ context->addrs);
+ /* fall through to examine arguments */
+ }
+ if (is_subplan(node))
+ {
+ /* Extra work needed here if we ever need this case */
+ elog(ERROR, "already-planned subqueries not supported");
+ }
+ if (IsA(node, RelabelType))
+ {
+ RelabelType *relab = (RelabelType *) node;
+
+ /* since there is no function dependency, need to depend on type */
+ add_object_address(OCLASS_TYPE, relab->resulttype, 0,
+ context->addrs);
+ }
+ if (IsA(node, CoerceViaIO))
+ {
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+
+ /* since there is no exposed function, need to depend on type */
+ add_object_address(OCLASS_TYPE, iocoerce->resulttype, 0,
+ context->addrs);
+ }
+ if (IsA(node, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+
+ if (OidIsValid(acoerce->elemfuncid))
+ add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0,
+ context->addrs);
+ add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
+ context->addrs);
+ /* fall through to examine arguments */
+ }
+ if (IsA(node, ConvertRowtypeExpr))
+ {
+ ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
+
+ /* since there is no function dependency, need to depend on type */
+ add_object_address(OCLASS_TYPE, cvt->resulttype, 0,
+ context->addrs);
+ }
+ if (IsA(node, RowExpr))
+ {
+ RowExpr *rowexpr = (RowExpr *) node;
+
+ add_object_address(OCLASS_TYPE, rowexpr->row_typeid, 0,
+ context->addrs);
+ }
+ if (IsA(node, RowCompareExpr))
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *l;
+
+ foreach(l, rcexpr->opnos)
+ {
+ add_object_address(OCLASS_OPERATOR, lfirst_oid(l), 0,
+ context->addrs);
+ }
+ foreach(l, rcexpr->opfamilies)
+ {
+ add_object_address(OCLASS_OPFAMILY, lfirst_oid(l), 0,
+ context->addrs);
+ }
+ /* fall through to examine arguments */
+ }
+ if (IsA(node, CoerceToDomain))
+ {
+ CoerceToDomain *cd = (CoerceToDomain *) node;
+
+ add_object_address(OCLASS_TYPE, cd->resulttype, 0,
+ context->addrs);
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ Query *query = (Query *) node;
+ ListCell *rtable;
+ bool result;
+
+ /*
+ * Add whole-relation refs for each plain relation mentioned in the
+ * subquery's rtable, as well as datatype refs for any datatypes used
+ * as a RECORD function's output. (Note: query_tree_walker takes care
+ * of recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need to
+ * do that here. But keep it from looking at join alias lists.)
+ */
+ foreach(rtable, query->rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable);
+ ListCell *ct;
+
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ add_object_address(OCLASS_CLASS, rte->relid, 0,
+ context->addrs);
+ break;
+ case RTE_FUNCTION:
+ foreach(ct, rte->funccoltypes)
+ {
+ add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0,
+ context->addrs);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Examine substructure of query */
+ context->rtables = lcons(query->rtable, context->rtables);
+ result = query_tree_walker(query,
+ find_expr_references_walker,
+ (void *) context,
+ QTW_IGNORE_JOINALIASES);
+ context->rtables = list_delete_first(context->rtables);
+ return result;
+ }
+ return expression_tree_walker(node, find_expr_references_walker,
+ (void *) context);
+}
+
+/*
+ * Given an array of dependency references, eliminate any duplicates.
+ */
+static void
+eliminate_duplicate_dependencies(ObjectAddresses *addrs)
+{
+ ObjectAddress *priorobj;
+ int oldref,
+ newrefs;
+