static bool stack_address_present_add_flags(const ObjectAddress *object,
int flags,
ObjectAddressStack *stack);
+static void add_type_addresses(Oid typeid, bool include_components, ObjectAddresses *addrs);
+static void add_type_component_addresses(Oid typeid, ObjectAddresses *addrs);
+static void add_class_component_addresses(Oid relid, ObjectAddresses *addrs);
static void DeleteInitPrivs(const ObjectAddress *object);
* whereas 'behavior' is used for everything else.
*
* NOTE: the caller should ensure that a whole-table dependency on the
- * specified relation is created separately, if one is needed. In particular,
- * a whole-row Var "relation.*" will not cause this routine to emit any
- * dependency item. This is appropriate behavior for subexpressions of an
- * ordinary query, so other cases need to cope as necessary.
+ * specified relation is created separately, if one is needed. E.g. SELECT
+ * FROM tbl will not cause this routine to emit any dependency items.
*/
void
recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
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. (We assume that there is a whole-table dependency
- * arising from each underlying rangetable entry. While we could
- * record such a dependency when finding a whole-row Var that
- * references a relation directly, it's quite unclear how to extend
- * that to whole-row Vars for JOINs, so it seems better to leave the
- * responsibility with the range table. Note that this poses some
- * risks for identifying dependencies of stand-alone expressions:
- * whole-table references may need to be created separately.)
- */
if (var->varattno == InvalidAttrNumber)
- return false;
+ {
+ /*
+ * A whole-row Var essentially references all current columns, so
+ * add dependencies for them - that allow adding new columns to
+ * the type, but not removing or altering the type of existing
+ * columns.
+ *
+ * That does not obviate the need for a whole-table dependency
+ * arising from each underlying rangetable entry - this would
+ * e.g. not do the right thing for column-less tables. Note that
+ * this requires some care for identifying dependencies of
+ * stand-alone expressions: whole-table references may need to be
+ * created separately.
+ */
+ add_class_component_addresses(rte->relid, context->addrs);
+ }
if (rte->rtekind == RTE_RELATION)
{
/* If it's a plain relation, reference this column */
Oid objoid;
/* A constant must depend on the constant's datatype */
- add_object_address(OCLASS_TYPE, con->consttype, 0,
- context->addrs);
+ add_type_addresses(con->consttype, true, context->addrs);
/*
* We must also depend on the constant's collation: it could be
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(TYPEOID,
ObjectIdGetDatum(objoid)))
- add_object_address(OCLASS_TYPE, objoid, 0,
- context->addrs);
+ add_type_addresses(objoid, false, context->addrs);
break;
case REGCONFIGOID:
objoid = DatumGetObjectId(con->constvalue);
Param *param = (Param *) node;
/* A parameter must depend on the parameter's datatype */
- add_object_address(OCLASS_TYPE, param->paramtype, 0,
- context->addrs);
+ add_type_addresses(param->paramtype, true, context->addrs);
/* and its collation, just as for Consts */
if (OidIsValid(param->paramcollid) &&
param->paramcollid != DEFAULT_COLLATION_OID)
add_object_address(OCLASS_PROC, funcexpr->funcid, 0,
context->addrs);
+ /* dependency on type itself already exists via function */
+ add_type_component_addresses(funcexpr->funcresulttype, context->addrs);
+
/* fall through to examine arguments */
}
else if (IsA(node, OpExpr))
add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
context->addrs);
+ /* dependency on type itself already exists via function */
+ add_type_component_addresses(opexpr->opresulttype, context->addrs);
+
/* fall through to examine arguments */
}
else if (IsA(node, DistinctExpr))
add_object_address(OCLASS_OPERATOR, distinctexpr->opno, 0,
context->addrs);
+ /*
+ * Dependency on type itself already exists via function. Be paranoid
+ * and add deps to return type components (unlikely to matter due to
+ * return type, but ...)
+ */
+ add_type_component_addresses(distinctexpr->opresulttype,
+ context->addrs);
/* fall through to examine arguments */
}
else if (IsA(node, NullIfExpr))
add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
context->addrs);
+ /* can't add new type dependencies */
/* fall through to examine arguments */
}
else if (IsA(node, ScalarArrayOpExpr))
add_object_address(OCLASS_PROC, aggref->aggfnoid, 0,
context->addrs);
+ add_type_component_addresses(aggref->aggtype, context->addrs);
/* fall through to examine arguments */
}
else if (IsA(node, WindowFunc))
add_object_address(OCLASS_PROC, wfunc->winfnoid, 0,
context->addrs);
+ add_type_component_addresses(wfunc->wintype, context->addrs);
/* fall through to examine arguments */
}
else if (IsA(node, SubPlan))
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);
+ add_type_addresses(relab->resulttype, false, context->addrs);
+
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(relab->resultcollid) &&
relab->resultcollid != DEFAULT_COLLATION_OID)
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);
+ add_type_addresses(iocoerce->resulttype, true, context->addrs);
}
else if (IsA(node, ArrayCoerceExpr))
{
if (OidIsValid(acoerce->elemfuncid))
add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0,
context->addrs);
- add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
- context->addrs);
+ add_type_addresses(acoerce->resulttype, true, context->addrs);
/* fall through to examine arguments */
}
else 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);
+ add_type_addresses(cvt->resulttype, true, context->addrs);
}
else if (IsA(node, CollateExpr))
{
{
RowExpr *rowexpr = (RowExpr *) node;
- add_object_address(OCLASS_TYPE, rowexpr->row_typeid, 0,
- context->addrs);
+ add_type_addresses(rowexpr->row_typeid, true, context->addrs);
}
else if (IsA(node, RowCompareExpr))
{
{
CoerceToDomain *cd = (CoerceToDomain *) node;
- add_object_address(OCLASS_TYPE, cd->resulttype, 0,
- context->addrs);
+ add_type_addresses(cd->resulttype, true, context->addrs);
}
else if (IsA(node, OnConflictExpr))
{
/*
* Add refs for any datatypes and collations used in a column
- * definition list for a RECORD function. (For other cases, it should
- * be enough to depend on the function itself.)
+ * definition list for a RECORD function. Additional dependencies
+ * will possibly be added when recursing to the contained function
+ * expression.
*/
foreach(ct, rtfunc->funccoltypes)
{
- add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0,
- context->addrs);
+ add_type_addresses(lfirst_oid(ct), true, context->addrs);
}
foreach(ct, rtfunc->funccolcollations)
{
return result;
}
+/*
+ * Add ObjectAddresses entry for typeid and, if include_components = true and
+ * and the type is a composite type, for it's columns.
+ */
+static void
+add_type_addresses(Oid typeid, bool include_components, ObjectAddresses *addrs)
+{
+ add_object_address(OCLASS_TYPE, typeid, 0, addrs);
+
+ if (include_components)
+ {
+ add_type_component_addresses(typeid, addrs);
+ }
+}
+
+/*
+ * Add ObjectAddresses entry for the type's columns if it's a composite type.
+ *
+ * Besides being a helper for add_type_addresses it can make sense to add
+ * dependencies on the components if, as e.g. the case for a function return
+ * type, there already exists a dependency on the type, but not the typ's
+ * components.
+ */
+static void
+add_type_component_addresses(Oid typeid, ObjectAddresses *addrs)
+{
+ Oid typerelid = get_typ_typrelid(typeid);
+
+ if (typerelid != InvalidOid)
+ {
+ add_class_component_addresses(typerelid, addrs);
+ }
+}
+
+/*
+ * Add ObjectAddresses entries for the relation's columns.
+ */
+static void
+add_class_component_addresses(Oid relid, ObjectAddresses *addrs)
+{
+ Relation rel = relation_open(relid, NoLock);
+ TupleDesc tupDesc = RelationGetDescr(rel);
+ int i;
+
+ for (i = 0; i < tupDesc->natts; i++)
+ {
+ Form_pg_attribute att = tupDesc->attrs[i];
+
+ if (att->attisdropped)
+ continue;
+
+ add_object_address(OCLASS_CLASS, relid, att->attnum, addrs);
+ }
+ relation_close(rel, NoLock);
+}
+
/*
* Similar to above, except we search an ObjectAddressStack.
*/
id2 | 2 | email2 | 12 | t | 11 | 2
(2 rows)
--- check that we can cope with post-parsing changes in rowtypes
create temp view usersview as
SELECT * FROM ROWS FROM(get_users(), generate_series(10,11)) WITH ORDINALITY;
+create temp view usersview2 as
+SELECT * FROM (SELECT get_users()) f;
+create temp view usersview3 as
+SELECT * FROM (SELECT users FROM users) f;
+-- check that rowtypes referenced in views can't be dropped / changed
select * from usersview;
userid | seq | email | moredrop | enabled | generate_series | ordinality
--------+-----+--------+----------+---------+-----------------+------------
id2 | 2 | email2 | 12 | t | 11 | 2
(2 rows)
+select * from usersview2;
+ get_users
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
alter table users drop column moredrop;
+ERROR: cannot drop table users column moredrop because other objects depend on it
+DETAIL: view usersview depends on table users column moredrop
+view usersview2 depends on table users column moredrop
+view usersview3 depends on table users column moredrop
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+alter table users alter column seq type numeric;
+ERROR: cannot alter type of a column used by a view or rule
+DETAIL: rule _RETURN on view usersview3 depends on column "seq"
select * from usersview;
userid | seq | email | moredrop | enabled | generate_series | ordinality
--------+-----+--------+----------+---------+-----------------+------------
- id | 1 | email | | t | 10 | 1
- id2 | 2 | email2 | | t | 11 | 2
+ id | 1 | email | 11 | t | 10 | 1
+ id2 | 2 | email2 | 12 | t | 11 | 2
+(2 rows)
+
+select * from usersview2;
+ get_users
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
(2 rows)
+select * from usersview3;
+ users
+---------------------
+ (id,1,email,11,t)
+ (id2,2,email2,12,t)
+(2 rows)
+
+-- check that column additions are handled properly
alter table users add column junk text;
select * from usersview;
userid | seq | email | moredrop | enabled | generate_series | ordinality
--------+-----+--------+----------+---------+-----------------+------------
- id | 1 | email | | t | 10 | 1
- id2 | 2 | email2 | | t | 11 | 2
+ id | 1 | email | 11 | t | 10 | 1
+ id2 | 2 | email2 | 12 | t | 11 | 2
(2 rows)
-alter table users alter column seq type numeric;
-select * from usersview; -- expect clean failure
-ERROR: attribute 2 has wrong type
-DETAIL: Table has type numeric, but query expects integer.
-drop view usersview;
+select * from usersview2;
+ get_users
+----------------------
+ (id,1,email,11,t,)
+ (id2,2,email2,12,t,)
+(2 rows)
+
+select * from usersview3;
+ users
+----------------------
+ (id,1,email,11,t,)
+ (id2,2,email2,12,t,)
+(2 rows)
+
+-- check that cascade is handled properly
+alter table users drop column moredrop CASCADE;
+NOTICE: drop cascades to 3 other objects
+DETAIL: drop cascades to view usersview
+drop cascades to view usersview2
+drop cascades to view usersview3
+-- should all be gone now
+\dv usersview*
+ List of relations
+ Schema | Name | Type | Owner
+--------+------+------+-------
+(0 rows)
+
drop function get_first_user();
drop function get_users();
drop table users;