diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index c8dd78341eb..026b743275f 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1792,6 +1792,82 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, free_object_addresses(context.addrs); } +/* + * We require USAGE on a type to store a dependency on it. This helper + * function does the appropriate privilege checks. + * + * NB: Other objects have privileges of their own, but recording those + * dependencies doesn't require holding them. For example, an expression may + * reference a function for which the user lacks EXECUTE. Instead, EXECUTE is + * checked when the function is executed. + */ +static void +check_usage_on_types(ObjectAddresses *addrs, Oid roleid) +{ + for (int i = 0; i < addrs->numrefs; i++) + { + ObjectAddress *ref = &addrs->refs[i]; + AclResult aclresult; + + if (ref->classId != TypeRelationId) + continue; + + /* we don't record dependencies on pinned types */ + if (IsPinnedObject(ref->classId, ref->objectId)) + continue; + + aclresult = object_aclcheck(ref->classId, ref->objectId, + roleid, ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error_type(aclresult, ref->objectId); + } +} + +/* + * CheckUsageOnTypesInExpr - require USAGE on all types named by an expression + * + * rtable is the rangetable for interpreting Vars (or NIL if none are + * expected). roleid is the role whose USAGE is required. + */ +void +CheckUsageOnTypesInExpr(Node *expr, List *rtable, Oid roleid) +{ + ObjectAddresses *addrs = new_object_addresses(); + + collectDependenciesOfExpr(addrs, expr, rtable); + eliminate_duplicate_dependencies(addrs); + check_usage_on_types(addrs, roleid); + free_object_addresses(addrs); +} + +/* + * CheckUsageOnTypesInSingleRelExpr - as above, for a single-rel expression + * + * Like recordDependencyOnSingleRelExpr(), this handles expressions whose Vars + * all refer to one relation. roleid is the role whose USAGE is required. + */ +void +CheckUsageOnTypesInSingleRelExpr(Node *expr, Oid relId, Oid roleid) +{ + find_expr_references_context context; + RangeTblEntry rte = {0}; + + context.addrs = new_object_addresses(); + + /* We gin up a rather bogus rangetable list to handle Vars */ + rte.type = T_RangeTblEntry; + rte.rtekind = RTE_RELATION; + rte.relid = relId; + rte.relkind = RELKIND_RELATION; + rte.rellockmode = AccessShareLock; + context.rtables = list_make1(list_make1(&rte)); + + find_expr_references_walker(expr, &context); + eliminate_duplicate_dependencies(context.addrs); + check_usage_on_types(context.addrs, roleid); + free_object_addresses(context.addrs); +} + /* * Recursively search an expression tree for object references. * diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 88087654de9..0fbbf8bcf56 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -2162,6 +2162,9 @@ SetAttrMissing(Oid relid, char *attname, char *value) * in the pg_class entry for the relation. * * The OID of the new constraint is returned. + * + * NB: Caller is responsible for ensuring the user has USAGE on all types expr + * depends on. */ static Oid StoreRelCheck(Relation rel, const char *ccname, Node *expr, @@ -2477,6 +2480,13 @@ AddRelationNewConstraints(Relation rel, castNode(Const, expr)->constisnull)) continue; + /* + * The below call to StoreAttrDefault() adds the dependencies on + * types. We are responsible for checking USAGE. + */ + if (!is_internal) + CheckUsageOnTypesInSingleRelExpr(expr, RelationGetRelid(rel), GetUserId()); + defOid = StoreAttrDefault(rel, colDef->attnum, expr, is_internal); cooked = palloc_object(CookedConstraint); @@ -2517,6 +2527,14 @@ AddRelationNewConstraints(Relation rel, */ expr = cookConstraint(pstate, cdef->raw_expr, RelationGetRelationName(rel)); + + /* + * The below call to StoreRelCheck() calls + * CreateConstraintEntry(), which adds the dependencies on + * types. We are responsible for checking USAGE. + */ + if (!is_internal) + CheckUsageOnTypesInSingleRelExpr(expr, RelationGetRelid(rel), GetUserId()); } else { @@ -4020,12 +4038,16 @@ StorePartitionKey(Relation rel, * columns, i.e. they become internally dependent on the whole table. */ if (partexprs) + { + CheckUsageOnTypesInSingleRelExpr((Node *) partexprs, RelationGetRelid(rel), + GetUserId()); recordDependencyOnSingleRelExpr(&myself, (Node *) partexprs, RelationGetRelid(rel), DEPENDENCY_NORMAL, DEPENDENCY_INTERNAL, true /* reverse the self-deps */ ); + } /* * We must invalidate the relcache so that the next diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 86f22570b45..5d94dbb8334 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -728,6 +728,9 @@ UpdateIndexRelation(Oid indexoid, * constraintId: if not NULL, receives OID of created constraint * * Returns the OID of the created index. + * + * NB: Caller is responsible for ensuring the user has USAGE on all types + * indexInfo->ii_{Expressions,Predicate} depend on. */ Oid index_create(Relation heapRelation, diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c index 24815090d3d..60bd7a40759 100644 --- a/src/backend/catalog/pg_attrdef.c +++ b/src/backend/catalog/pg_attrdef.c @@ -32,6 +32,9 @@ * Store a default expression for column attnum of relation rel. * * Returns the OID of the new pg_attrdef tuple. + * + * NB: Caller is responsible for ensuring the user has USAGE on all types expr + * depends on. */ Oid StoreAttrDefault(Relation rel, AttrNumber attnum, diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index b12765ae691..8aba9cbbc57 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -46,6 +46,9 @@ * from the constraint to the things it depends on. * * The new constraint's OID is returned. + * + * NB: Caller is responsible for ensuring the user has USAGE on all types + * conExpr depends on. */ Oid CreateConstraintEntry(const char *constraintName, diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 5df4b3f7a91..2e854eb08e9 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -662,11 +662,17 @@ ProcedureCreate(const char *procedureName, /* dependencies appearing in new-style SQL routine body */ if (languageObjectId == SQLlanguageId && prosqlbody) + { + CheckUsageOnTypesInExpr(prosqlbody, NIL, GetUserId()); collectDependenciesOfExpr(addrs, prosqlbody, NIL); + } /* dependency on parameter default expressions */ if (parameterDefaults) + { + CheckUsageOnTypesInExpr((Node *) parameterDefaults, NIL, GetUserId()); collectDependenciesOfExpr(addrs, (Node *) parameterDefaults, NIL); + } /* * Now that we have all the normal dependencies, thumb through them and diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c index e6ebc1e2627..b5843dd422c 100644 --- a/src/backend/catalog/pg_publication.c +++ b/src/backend/catalog/pg_publication.c @@ -32,6 +32,7 @@ #include "catalog/pg_type.h" #include "commands/publicationcmds.h" #include "funcapi.h" +#include "miscadmin.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/catcache.h" @@ -609,9 +610,12 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri, /* Add dependency on the objects mentioned in the qualifications */ if (pri->whereClause) + { + CheckUsageOnTypesInSingleRelExpr(pri->whereClause, relid, GetUserId()); recordDependencyOnSingleRelExpr(&myself, pri->whereClause, relid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false); + } /* Add dependency on the columns, if any are listed */ i = -1; diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index fc369c35aa6..a3f5c56280c 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -189,6 +189,9 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) * Returns the ObjectAddress assigned to the new type. * If newTypeOid is zero (the normal case), a new OID is created; * otherwise we use exactly that OID. + * + * NB: Caller is responsible for ensuring the user has USAGE + * on all types defaultTypeBin depends on. * ---------------------------------------------------------------- */ ObjectAddress @@ -550,6 +553,9 @@ TypeCreate(Oid newTypeOid, * type already belongs to the current extension. That's the behavior we * want when replacing a shell type, which is the only case where both flags * are true. + * + * NB: Caller is responsible for ensuring the user has USAGE on all types + * defaultExpr depends on. */ void GenerateTypeDependencies(HeapTuple typeTuple, diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 2a73a55d1fc..c09852c9528 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -948,6 +948,22 @@ DefineIndex(ParseState *pstate, root_save_userid, root_save_sec_context, &root_save_nestlevel); + /* + * The below call to index_create() creates the dependencies on types. We + * are responsible for checking USAGE. + */ + if (check_rights) + { + if (indexInfo->ii_Expressions) + CheckUsageOnTypesInSingleRelExpr((Node *) indexInfo->ii_Expressions, + tableId, + root_save_userid); + if (indexInfo->ii_Predicate) + CheckUsageOnTypesInSingleRelExpr((Node *) indexInfo->ii_Predicate, + tableId, + root_save_userid); + } + /* * Extra checks when creating a PRIMARY KEY index. */ diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 21b8eebe32d..2d112b523e5 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -724,9 +724,12 @@ CreatePolicy(CreatePolicyStmt *stmt) recordDependencyOn(&myself, &target, DEPENDENCY_AUTO); + CheckUsageOnTypesInExpr(qual, qual_pstate->p_rtable, GetUserId()); recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable, DEPENDENCY_NORMAL); + CheckUsageOnTypesInExpr(with_check_qual, with_check_pstate->p_rtable, + GetUserId()); recordDependencyOnExpr(&myself, with_check_qual, with_check_pstate->p_rtable, DEPENDENCY_NORMAL); @@ -1055,8 +1058,13 @@ AlterPolicy(AlterPolicyStmt *stmt) recordDependencyOn(&myself, &target, DEPENDENCY_AUTO); + if (stmt->qual) + CheckUsageOnTypesInExpr(qual, qual_parse_rtable, GetUserId()); recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL); + if (stmt->with_check) + CheckUsageOnTypesInExpr(with_check_qual, with_check_parse_rtable, + GetUserId()); recordDependencyOnExpr(&myself, with_check_qual, with_check_parse_rtable, DEPENDENCY_NORMAL); diff --git a/src/backend/commands/propgraphcmds.c b/src/backend/commands/propgraphcmds.c index 9c9f4f2b299..076005226f2 100644 --- a/src/backend/commands/propgraphcmds.c +++ b/src/backend/commands/propgraphcmds.c @@ -31,12 +31,14 @@ #include "commands/defrem.h" #include "commands/propgraphcmds.h" #include "commands/tablecmds.h" +#include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" +#include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -959,6 +961,7 @@ insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *pr HeapTuple tup; ObjectAddress myself; ObjectAddress referenced; + AclResult aclresult; rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock); @@ -979,6 +982,9 @@ insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *pr ObjectAddressSet(referenced, RelationRelationId, graphid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + aclresult = object_aclcheck(TypeRelationId, exprtypid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error_type(aclresult, exprtypid); ObjectAddressSet(referenced, TypeRelationId, exprtypid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); if (OidIsValid(exprcollation) && exprcollation != DEFAULT_COLLATION_OID) @@ -1063,6 +1069,7 @@ insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *pr ObjectAddressSet(referenced, PropgraphElementLabelRelationId, ellabeloid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + CheckUsageOnTypesInSingleRelExpr((Node *) expr, pgerelid, GetUserId()); recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false); table_close(rel, NoLock); diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index 8e377ca445d..c5c1bf0bd10 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -613,11 +613,16 @@ CreateStatistics(List *relids, CreateStatsStmt *stmt, bool check_rights) * just like we do for index expressions. */ if (stxexprs) + { + if (check_rights) + CheckUsageOnTypesInSingleRelExpr((Node *) stxexprs, relid, GetUserId()); + recordDependencyOnSingleRelExpr(&myself, (Node *) stxexprs, relid, DEPENDENCY_NORMAL, DEPENDENCY_AUTO, false); + } /* * Also add dependencies on namespace and owner. These are required diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f8fedc2b249..5d8963be108 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1024,6 +1024,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, stmt->partbound != NULL, &old_constraints, &old_notnulls); + /* + * NB: The defaults and constraints we just inherited are already cooked, + * so they don't get the USAGE checks that AddRelationNewConstraints() + * applies to raw ones. That's intentional: the parent's own catalog + * entries already depend on those types, so copying them pins nothing + * new, and creating a child requires owning the parent, anyway. + */ + /* * Create a tuple descriptor from the relation schema. Note that this * deals with column names, types, and in-descriptor NOT NULL flags, but @@ -5556,7 +5564,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, address = AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName, ((AlterDomainStmt *) cmd->def)->def, - NULL); + NULL, true); break; case AT_ReAddComment: /* Re-add existing comment */ address = CommentObject((CommentStmt *) cmd->def); @@ -8308,7 +8316,14 @@ ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, { ObjectAddress address; - /* We assume no checking is required */ + /* + * This is used for a cooked default copied by CREATE TABLE ... LIKE, + * which adds new type dependencies. Such a default doesn't go through + * AddRelationNewConstraints(), and StoreAttrDefault() leaves the + * privilege checks to its caller, so we must check for USAGE on the types + * here. + */ + CheckUsageOnTypesInSingleRelExpr(newDefault, RelationGetRelid(rel), GetUserId()); /* * Remove any old default for the column. We use RESTRICT here for diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index e980f847ec3..8444332d44f 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -1132,8 +1132,13 @@ CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString, * expression (eg, functions, as well as any columns used). */ if (whenRtable != NIL) + { + if (!isInternal) + CheckUsageOnTypesInExpr(whenClause, whenRtable, GetUserId()); + recordDependencyOnExpr(&myself, whenClause, whenRtable, DEPENDENCY_NORMAL); + } /* Post creation hook for new trigger */ InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0, diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 1d74a6e9457..b19da409597 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -135,7 +135,8 @@ static void checkEnumOwner(HeapTuple tup); static char *domainAddCheckConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, int typMod, Constraint *constr, - const char *domainName, ObjectAddress *constrAddr); + const char *domainName, ObjectAddress *constrAddr, + bool is_readd); static Node *replace_domain_constraint_value(ParseState *pstate, ColumnRef *cref); static void domainAddNotNullConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, @@ -1050,6 +1051,13 @@ DefineDomain(ParseState *pstate, CreateDomainStmt *stmt) } } + /* + * The below call to TypeCreate() calls GenerateTypeDependencies(), which + * adds the dependencies on types. We are responsible for checking USAGE. + */ + if (defaultValueBin) + CheckUsageOnTypesInExpr(stringToNode(defaultValueBin), NIL, GetUserId()); + /* Allocate OID for array type */ domainArrayOid = AssignTypeArrayOid(); @@ -1147,7 +1155,7 @@ DefineDomain(ParseState *pstate, CreateDomainStmt *stmt) case CONSTR_CHECK: domainAddCheckConstraint(address.objectId, domainNamespace, basetypeoid, basetypeMod, - constr, domainName, NULL); + constr, domainName, NULL, false); break; case CONSTR_NOTNULL: @@ -2713,6 +2721,12 @@ AlterDomainDefault(List *names, Node *defaultRaw) } else { + /* + * The below call to GenerateTypeDependencies() creates the + * dependencies on types. We are responsible for checking USAGE. + */ + CheckUsageOnTypesInExpr(defaultExpr, NIL, GetUserId()); + /* * Expression must be stored as a nodeToString result, but we also * require a valid textual representation (mainly to make life @@ -2969,7 +2983,7 @@ AlterDomainDropConstraint(List *names, const char *constrName, */ ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, - ObjectAddress *constrAddr) + ObjectAddress *constrAddr, bool is_readd) { TypeName *typename; Oid domainoid; @@ -3013,7 +3027,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint, ccbin = domainAddCheckConstraint(domainoid, typTup->typnamespace, typTup->typbasetype, typTup->typtypmod, - constr, NameStr(typTup->typname), constrAddr); + constr, NameStr(typTup->typname), constrAddr, + is_readd); /* @@ -3549,7 +3564,8 @@ checkDomainOwner(HeapTuple tup) static char * domainAddCheckConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, int typMod, Constraint *constr, - const char *domainName, ObjectAddress *constrAddr) + const char *domainName, ObjectAddress *constrAddr, + bool is_readd) { Node *expr; char *ccbin; @@ -3612,6 +3628,13 @@ domainAddCheckConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, */ assign_expr_collations(pstate, expr); + /* + * The below call to CreateConstraintEntry() creates the dependencies on + * types. We are responsible for checking USAGE. + */ + if (!is_readd) + CheckUsageOnTypesInExpr(expr, NIL, GetUserId()); + /* * Domains don't allow variables (this is probably dead code now that * add_missing_from is history, but let's be sure). diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index a049cc67ed6..a17fe73cc35 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1484,6 +1484,16 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) ccname, RelationGetRelationName(relation)))); + /* + * Copying a CHECK constraint adds new references. Since the + * constraint arrives pre-cooked, it bypasses the checks in + * AddRelationNewConstraints(), so we must check for USAGE on + * types here. + */ + CheckUsageOnTypesInSingleRelExpr(stringToNode(ccbin), + RelationGetRelid(relation), + GetUserId()); + n = makeNode(Constraint); n->contype = CONSTR_CHECK; n->conname = pstrdup(ccname); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6361eeea20f..685a1fc4f58 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -161,6 +161,7 @@ InsertRule(const char *rulname, /* * Also install dependencies on objects referenced in action and qual. */ + CheckUsageOnTypesInExpr((Node *) action, NIL, GetUserId()); recordDependencyOnExpr(&myself, (Node *) action, NIL, DEPENDENCY_NORMAL); @@ -170,6 +171,7 @@ InsertRule(const char *rulname, Query *qry = linitial_node(Query, action); qry = getInsertSelectQuery(qry, NULL); + CheckUsageOnTypesInExpr(event_qual, qry->rtable, GetUserId()); recordDependencyOnExpr(&myself, event_qual, qry->rtable, DEPENDENCY_NORMAL); } diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 5f204addcd4..4d33fcb5e9d 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1370,7 +1370,8 @@ ProcessUtilitySlow(ParseState *pstate, address = AlterDomainAddConstraint(stmt->typeName, stmt->def, - &secondaryObject); + &secondaryObject, + false); break; case AD_DropConstraint: address = diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 2f3c1eae3c7..214ef1e7e2d 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -125,6 +125,10 @@ extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, DependencyType self_behavior, bool reverse_self); +extern void CheckUsageOnTypesInExpr(Node *expr, List *rtable, Oid roleid); + +extern void CheckUsageOnTypesInSingleRelExpr(Node *expr, Oid relId, Oid roleid); + extern bool find_temp_object(const ObjectAddresses *addrs, bool local_temp_okay, ObjectAddress *foundobj); diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index f0c5c111326..2112b4addd2 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -35,7 +35,8 @@ extern Oid AssignTypeMultirangeArrayOid(void); extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw); extern ObjectAddress AlterDomainNotNull(List *names, bool notNull); extern ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, - ObjectAddress *constrAddr); + ObjectAddress *constrAddr, + bool is_readd); extern ObjectAddress AlterDomainValidateConstraint(List *names, const char *constrName); extern ObjectAddress AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior, bool missing_ok); diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 5e3c9510490..3e5cc81f0eb 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -1438,10 +1438,14 @@ CREATE CAST (priv_testdomain1 AS priv_testdomain3a) WITH FUNCTION castfunc(int); ERROR: permission denied for type priv_testdomain1 DROP FUNCTION castfunc(int) CASCADE; DROP DOMAIN priv_testdomain3a; +CREATE DOMAIN priv_testdomain4a AS int CHECK (VALUE > ('(0,1)'::priv_testtype1).a); +ERROR: permission denied for type priv_testtype1 CREATE FUNCTION priv_testfunc5a(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; ERROR: permission denied for type priv_testdomain1 CREATE FUNCTION priv_testfunc6a(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; ERROR: permission denied for type priv_testdomain1 +CREATE FUNCTION priv_testfunc7a(a int DEFAULT ('(0,1)'::priv_testtype1).a) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; +ERROR: permission denied for type priv_testtype1 CREATE OPERATOR !+! (PROCEDURE = int4pl, LEFTARG = priv_testdomain1, RIGHTARG = priv_testdomain1); ERROR: permission denied for type priv_testdomain1 CREATE TABLE test5a (a int, b priv_testdomain1); @@ -1455,6 +1459,8 @@ ALTER TABLE test9a ADD COLUMN c priv_testdomain1; ERROR: permission denied for type priv_testdomain1 ALTER TABLE test9a ALTER COLUMN b TYPE priv_testdomain1; ERROR: permission denied for type priv_testdomain1 +CREATE INDEX ON test9a ((a::priv_testdomain1)); +ERROR: permission denied for type public.priv_testdomain1 CREATE TYPE test7a AS (a int, b priv_testdomain1); ERROR: permission denied for type priv_testdomain1 CREATE TYPE test8a AS (a int, b int); @@ -1462,10 +1468,22 @@ ALTER TYPE test8a ADD ATTRIBUTE c priv_testdomain1; ERROR: permission denied for type priv_testdomain1 ALTER TYPE test8a ALTER ATTRIBUTE b TYPE priv_testdomain1; ERROR: permission denied for type priv_testdomain1 +CREATE DOMAIN priv_testdomain5a AS test8a CHECK ((VALUE).a > 0 AND ('(0,1)'::priv_testtype1).a >= 0); +ERROR: permission denied for type priv_testtype1 CREATE TABLE test11a AS (SELECT 1::priv_testdomain1 AS a); ERROR: permission denied for type priv_testdomain1 +CREATE VIEW test16a AS SELECT ('(0,1)'::priv_testtype1).a; +ERROR: permission denied for type priv_testtype1 +CREATE TABLE test17a (a int) PARTITION BY RANGE ((a + ('(0,1)'::priv_testtype1).a)); +ERROR: permission denied for type priv_testtype1 +CREATE POLICY priv_testpolicy1a ON test9a USING (('(0,1)'::priv_testtype1).a > 0); +ERROR: permission denied for type priv_testtype1 REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; ERROR: permission denied for type priv_testtype1 +CREATE TABLE test12 (c int DEFAULT 0 CHECK (c > 0 AND '(0,1)'::priv_testtype1 IS NOT NULL)); +ERROR: permission denied for type priv_testtype1 +CREATE TABLE test13 (c int DEFAULT ('(0,1)'::priv_testtype1).a); +ERROR: permission denied for type priv_testtype1 SET SESSION AUTHORIZATION regress_priv_user2; -- commands that should succeed CREATE AGGREGATE priv_testagg1b(priv_testdomain1) (sfunc = int4_sum, stype = bigint); @@ -1474,8 +1492,10 @@ CREATE DOMAIN priv_testdomain3b AS int; CREATE FUNCTION castfunc(int) RETURNS priv_testdomain3b AS $$ SELECT $1::priv_testdomain3b $$ LANGUAGE SQL; CREATE CAST (priv_testdomain1 AS priv_testdomain3b) WITH FUNCTION castfunc(int); WARNING: cast will be ignored because the source data type is a domain +CREATE DOMAIN priv_testdomain4b AS int CHECK (VALUE > ('(0,1)'::priv_testtype1).a); CREATE FUNCTION priv_testfunc5b(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE FUNCTION priv_testfunc6b(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; +CREATE FUNCTION priv_testfunc7b(a int DEFAULT ('(0,1)'::priv_testtype1).a) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE OPERATOR !! (PROCEDURE = priv_testfunc5b, RIGHTARG = priv_testdomain1); CREATE TABLE test5b (a int, b priv_testdomain1); CREATE TABLE test6b OF priv_testtype1; @@ -1483,19 +1503,41 @@ CREATE TABLE test10b (a int[], b priv_testtype1[]); CREATE TABLE test9b (a int, b int); ALTER TABLE test9b ADD COLUMN c priv_testdomain1; ALTER TABLE test9b ALTER COLUMN b TYPE priv_testdomain1; +CREATE INDEX ON test9b ((a::priv_testdomain1)); CREATE TYPE test7b AS (a int, b priv_testdomain1); CREATE TYPE test8b AS (a int, b int); ALTER TYPE test8b ADD ATTRIBUTE c priv_testdomain1; ALTER TYPE test8b ALTER ATTRIBUTE b TYPE priv_testdomain1; +CREATE DOMAIN priv_testdomain5b AS test8b CHECK ((VALUE).a > 0 AND ('(0,1)'::priv_testtype1).a >= 0); CREATE TABLE test11b AS (SELECT 1::priv_testdomain1 AS a); +CREATE VIEW test16b AS SELECT ('(0,1)'::priv_testtype1).a; +CREATE TABLE test17b (a int) PARTITION BY RANGE ((a + ('(0,1)'::priv_testtype1).a)); +CREATE POLICY priv_testpolicy1b ON test9b USING (('(0,1)'::priv_testtype1).a > 0); REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; WARNING: no privileges could be revoked for "priv_testtype1" +CREATE TABLE test12 (c int DEFAULT 0 CHECK (c > 0 AND '(0,1)'::priv_testtype1 IS NOT NULL)); +CREATE TABLE test13 (c int DEFAULT ('(0,1)'::priv_testtype1).a); +-- new stored expressions require USAGE on types, rebuilds do not +\c - +REVOKE USAGE ON TYPE priv_testtype1 FROM regress_priv_user2; +SET SESSION AUTHORIZATION regress_priv_user2; +ALTER TABLE test12 ALTER COLUMN c TYPE bigint; +ALTER TYPE test8b ALTER ATTRIBUTE a TYPE bigint CASCADE; +ALTER TABLE test12 ALTER COLUMN c SET DEFAULT ('(0,1)'::priv_testtype1).a; +ERROR: permission denied for type priv_testtype1 +CREATE TABLE test14 (LIKE test12 INCLUDING CONSTRAINTS); +ERROR: permission denied for type priv_testtype1 +CREATE TABLE test15 (LIKE test13 INCLUDING DEFAULTS); +ERROR: permission denied for type priv_testtype1 \c - DROP AGGREGATE priv_testagg1b(priv_testdomain1); DROP DOMAIN priv_testdomain2b; +DROP DOMAIN priv_testdomain4b; +DROP DOMAIN priv_testdomain5b; DROP OPERATOR !! (NONE, priv_testdomain1); DROP FUNCTION priv_testfunc5b(a priv_testdomain1); DROP FUNCTION priv_testfunc6b(b int); +DROP FUNCTION priv_testfunc7b(a int); DROP TABLE test5b; DROP TABLE test6b; DROP TABLE test9b; @@ -1505,7 +1547,11 @@ DROP TYPE test8b; DROP CAST (priv_testdomain1 AS priv_testdomain3b); DROP FUNCTION castfunc(int) CASCADE; DROP DOMAIN priv_testdomain3b; +DROP VIEW test16b; DROP TABLE test11b; +DROP TABLE test17b; +DROP TABLE test12; +DROP TABLE test13; DROP TYPE priv_testtype1; -- ok DROP DOMAIN priv_testdomain1; -- ok -- truncate diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index d3e87fa617f..79322165655 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -994,8 +994,11 @@ CREATE CAST (priv_testdomain1 AS priv_testdomain3a) WITH FUNCTION castfunc(int); DROP FUNCTION castfunc(int) CASCADE; DROP DOMAIN priv_testdomain3a; +CREATE DOMAIN priv_testdomain4a AS int CHECK (VALUE > ('(0,1)'::priv_testtype1).a); + CREATE FUNCTION priv_testfunc5a(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE FUNCTION priv_testfunc6a(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; +CREATE FUNCTION priv_testfunc7a(a int DEFAULT ('(0,1)'::priv_testtype1).a) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE OPERATOR !+! (PROCEDURE = int4pl, LEFTARG = priv_testdomain1, RIGHTARG = priv_testdomain1); @@ -1006,6 +1009,7 @@ CREATE TABLE test10a (a int[], b priv_testtype1[]); CREATE TABLE test9a (a int, b int); ALTER TABLE test9a ADD COLUMN c priv_testdomain1; ALTER TABLE test9a ALTER COLUMN b TYPE priv_testdomain1; +CREATE INDEX ON test9a ((a::priv_testdomain1)); CREATE TYPE test7a AS (a int, b priv_testdomain1); @@ -1013,10 +1017,19 @@ CREATE TYPE test8a AS (a int, b int); ALTER TYPE test8a ADD ATTRIBUTE c priv_testdomain1; ALTER TYPE test8a ALTER ATTRIBUTE b TYPE priv_testdomain1; +CREATE DOMAIN priv_testdomain5a AS test8a CHECK ((VALUE).a > 0 AND ('(0,1)'::priv_testtype1).a >= 0); + CREATE TABLE test11a AS (SELECT 1::priv_testdomain1 AS a); +CREATE VIEW test16a AS SELECT ('(0,1)'::priv_testtype1).a; +CREATE TABLE test17a (a int) PARTITION BY RANGE ((a + ('(0,1)'::priv_testtype1).a)); +CREATE POLICY priv_testpolicy1a ON test9a USING (('(0,1)'::priv_testtype1).a > 0); + REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; +CREATE TABLE test12 (c int DEFAULT 0 CHECK (c > 0 AND '(0,1)'::priv_testtype1 IS NOT NULL)); +CREATE TABLE test13 (c int DEFAULT ('(0,1)'::priv_testtype1).a); + SET SESSION AUTHORIZATION regress_priv_user2; -- commands that should succeed @@ -1029,8 +1042,11 @@ CREATE DOMAIN priv_testdomain3b AS int; CREATE FUNCTION castfunc(int) RETURNS priv_testdomain3b AS $$ SELECT $1::priv_testdomain3b $$ LANGUAGE SQL; CREATE CAST (priv_testdomain1 AS priv_testdomain3b) WITH FUNCTION castfunc(int); +CREATE DOMAIN priv_testdomain4b AS int CHECK (VALUE > ('(0,1)'::priv_testtype1).a); + CREATE FUNCTION priv_testfunc5b(a priv_testdomain1) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE FUNCTION priv_testfunc6b(b int) RETURNS priv_testdomain1 LANGUAGE SQL AS $$ SELECT $1::priv_testdomain1 $$; +CREATE FUNCTION priv_testfunc7b(a int DEFAULT ('(0,1)'::priv_testtype1).a) RETURNS int LANGUAGE SQL AS $$ SELECT $1 $$; CREATE OPERATOR !! (PROCEDURE = priv_testfunc5b, RIGHTARG = priv_testdomain1); @@ -1041,6 +1057,7 @@ CREATE TABLE test10b (a int[], b priv_testtype1[]); CREATE TABLE test9b (a int, b int); ALTER TABLE test9b ADD COLUMN c priv_testdomain1; ALTER TABLE test9b ALTER COLUMN b TYPE priv_testdomain1; +CREATE INDEX ON test9b ((a::priv_testdomain1)); CREATE TYPE test7b AS (a int, b priv_testdomain1); @@ -1048,16 +1065,38 @@ CREATE TYPE test8b AS (a int, b int); ALTER TYPE test8b ADD ATTRIBUTE c priv_testdomain1; ALTER TYPE test8b ALTER ATTRIBUTE b TYPE priv_testdomain1; +CREATE DOMAIN priv_testdomain5b AS test8b CHECK ((VALUE).a > 0 AND ('(0,1)'::priv_testtype1).a >= 0); + CREATE TABLE test11b AS (SELECT 1::priv_testdomain1 AS a); +CREATE VIEW test16b AS SELECT ('(0,1)'::priv_testtype1).a; +CREATE TABLE test17b (a int) PARTITION BY RANGE ((a + ('(0,1)'::priv_testtype1).a)); +CREATE POLICY priv_testpolicy1b ON test9b USING (('(0,1)'::priv_testtype1).a > 0); + REVOKE ALL ON TYPE priv_testtype1 FROM PUBLIC; +CREATE TABLE test12 (c int DEFAULT 0 CHECK (c > 0 AND '(0,1)'::priv_testtype1 IS NOT NULL)); +CREATE TABLE test13 (c int DEFAULT ('(0,1)'::priv_testtype1).a); + +-- new stored expressions require USAGE on types, rebuilds do not +\c - +REVOKE USAGE ON TYPE priv_testtype1 FROM regress_priv_user2; +SET SESSION AUTHORIZATION regress_priv_user2; +ALTER TABLE test12 ALTER COLUMN c TYPE bigint; +ALTER TYPE test8b ALTER ATTRIBUTE a TYPE bigint CASCADE; +ALTER TABLE test12 ALTER COLUMN c SET DEFAULT ('(0,1)'::priv_testtype1).a; +CREATE TABLE test14 (LIKE test12 INCLUDING CONSTRAINTS); +CREATE TABLE test15 (LIKE test13 INCLUDING DEFAULTS); + \c - DROP AGGREGATE priv_testagg1b(priv_testdomain1); DROP DOMAIN priv_testdomain2b; +DROP DOMAIN priv_testdomain4b; +DROP DOMAIN priv_testdomain5b; DROP OPERATOR !! (NONE, priv_testdomain1); DROP FUNCTION priv_testfunc5b(a priv_testdomain1); DROP FUNCTION priv_testfunc6b(b int); +DROP FUNCTION priv_testfunc7b(a int); DROP TABLE test5b; DROP TABLE test6b; DROP TABLE test9b; @@ -1067,7 +1106,11 @@ DROP TYPE test8b; DROP CAST (priv_testdomain1 AS priv_testdomain3b); DROP FUNCTION castfunc(int) CASCADE; DROP DOMAIN priv_testdomain3b; +DROP VIEW test16b; DROP TABLE test11b; +DROP TABLE test17b; +DROP TABLE test12; +DROP TABLE test13; DROP TYPE priv_testtype1; -- ok DROP DOMAIN priv_testdomain1; -- ok