1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Check for USAGE privilege on types used by stored expressions.

This omission allowed roles without USAGE on a type to create
stored expressions that depend on it, which could prevent the owner
from changing the type later.

The checks deliberately live in the command paths rather than the
dependency-recording routines.  Those routines also run whenever
the server re-derives an existing expression, and re-checking there
would break routine maintenance for an owner who has since lost
USAGE on a type its objects already reference.  (Checking in the
dependency-recording routines would also require additional
CommandCounterIncrement() calls to avoid spurious errors.)

The addition of a parameter to AlterDomainAddConstraint() breaks
ABI compatibility, but we are unaware of any impacted third-party
code.

Reported-by: Noah Misch <noah@leadboat.com>
Author: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Noah Misch <noah@leadboat.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Security: CVE-2026-6470
Backpatch-through: 14
This commit is contained in:
Nathan Bossart
2026-08-10 06:38:06 -07:00
committed by Noah Misch
parent efdb260728
commit 424fb7160b
22 changed files with 318 additions and 9 deletions

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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.
*/

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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).

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -1370,7 +1370,8 @@ ProcessUtilitySlow(ParseState *pstate,
address =
AlterDomainAddConstraint(stmt->typeName,
stmt->def,
&secondaryObject);
&secondaryObject,
false);
break;
case AD_DropConstraint:
address =

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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