Commit 17194f41 authored by Tom Lane's avatar Tom Lane

Partial code review for ALTER DOMAIN patch. Incorporates Rod Taylor's

patches of 9-Dec (permissions fix) and 13-Dec (performance) as well as
a partial fix for locking issues: concurrent DROP COLUMN should not
create trouble anymore.  But concurrent DROP TABLE is still a risk, and
there is no protection at all against creating a column of a domain while
we are altering the domain.
parent 150ffb2d
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.25 2002/12/15 16:17:43 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.26 2003/01/04 00:46:08 tgl Exp $
* *
* DESCRIPTION * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
...@@ -59,20 +60,25 @@ ...@@ -59,20 +60,25 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
/* result structure for get_rels_with_domain() */
typedef struct
{
Relation rel; /* opened and locked relation */
int natts; /* number of attributes of interest */
int *atts; /* attribute numbers */
/* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
} RelToCheck;
static Oid findTypeIOFunction(List *procname, Oid typeOid, bool isOutput); static Oid findTypeIOFunction(List *procname, Oid typeOid, bool isOutput);
static List *get_rels_with_domain(Oid domainOid); static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void domainPermissionCheck(HeapTuple tup, TypeName *typename); static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace, static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid, Oid baseTypeOid,
int typMod, Constraint *constr, int typMod, Constraint *constr,
int *counter, char *domainName); int *counter, char *domainName);
typedef struct
{
Oid relOid;
int natts;
int *atts;
} relToCheck;
/* /*
* DefineType * DefineType
...@@ -942,7 +948,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) ...@@ -942,7 +948,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
TypeNameToString(typename)); TypeNameToString(typename));
/* Doesn't return if user isn't allowed to alter the domain */ /* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename); domainOwnerCheck(tup, typename);
/* Setup new tuple */ /* Setup new tuple */
MemSet(new_record, (Datum) 0, sizeof(new_record)); MemSet(new_record, (Datum) 0, sizeof(new_record));
...@@ -1029,12 +1035,8 @@ AlterDomainNotNull(List *names, bool notNull) ...@@ -1029,12 +1035,8 @@ AlterDomainNotNull(List *names, bool notNull)
{ {
TypeName *typename; TypeName *typename;
Oid domainoid; Oid domainoid;
Relation typrel;
HeapTuple tup; HeapTuple tup;
Relation rel;
Datum new_record[Natts_pg_type];
char new_record_nulls[Natts_pg_type];
char new_record_repl[Natts_pg_type];
HeapTuple newtuple;
Form_pg_type typTup; Form_pg_type typTup;
/* Make a TypeName so we can use standard type lookup machinery */ /* Make a TypeName so we can use standard type lookup machinery */
...@@ -1044,9 +1046,9 @@ AlterDomainNotNull(List *names, bool notNull) ...@@ -1044,9 +1046,9 @@ AlterDomainNotNull(List *names, bool notNull)
typename->arrayBounds = NIL; typename->arrayBounds = NIL;
/* Lock the type table */ /* Lock the type table */
rel = heap_openr(TypeRelationName, RowExclusiveLock); typrel = heap_openr(TypeRelationName, RowExclusiveLock);
/* Use LookupTypeName here so that shell types can be removed. */ /* Use LookupTypeName here so that shell types can be found (why?). */
domainoid = LookupTypeName(typename); domainoid = LookupTypeName(typename);
if (!OidIsValid(domainoid)) if (!OidIsValid(domainoid))
elog(ERROR, "Type \"%s\" does not exist", elog(ERROR, "Type \"%s\" does not exist",
...@@ -1055,93 +1057,84 @@ AlterDomainNotNull(List *names, bool notNull) ...@@ -1055,93 +1057,84 @@ AlterDomainNotNull(List *names, bool notNull)
tup = SearchSysCacheCopy(TYPEOID, tup = SearchSysCacheCopy(TYPEOID,
ObjectIdGetDatum(domainoid), ObjectIdGetDatum(domainoid),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
elog(ERROR, "AlterDomain: type \"%s\" does not exist", elog(ERROR, "AlterDomain: type \"%s\" does not exist",
TypeNameToString(typename)); TypeNameToString(typename));
typTup = (Form_pg_type) GETSTRUCT(tup);
/* Doesn't return if user isn't allowed to alter the domain */ /* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename); domainOwnerCheck(tup, typename);
typTup = (Form_pg_type) GETSTRUCT(tup); /* Is the domain already set to the desired constraint? */
/* Is the domain already set to the destination constraint? */
if (typTup->typnotnull == notNull) if (typTup->typnotnull == notNull)
elog(ERROR, "AlterDomain: %s is already set to %s", {
elog(NOTICE, "AlterDomain: %s is already set to %s",
TypeNameToString(typename), TypeNameToString(typename),
notNull ? "NOT NULL" : "NULL"); notNull ? "NOT NULL" : "NULL");
heap_close(typrel, RowExclusiveLock);
return;
}
/* Adding a NOT NULL constraint requires checking current domains */ /* Adding a NOT NULL constraint requires checking existing columns */
if (notNull) if (notNull)
{ {
List *rels; List *rels;
List *rt; List *rt;
/* Fetch relation list with attributes based on this domain */ /* Fetch relation list with attributes based on this domain */
rels = get_rels_with_domain(domainoid); /* ShareLock is sufficient to prevent concurrent data changes */
rels = get_rels_with_domain(domainoid, ShareLock);
foreach (rt, rels) foreach (rt, rels)
{ {
Relation typrel; RelToCheck *rtc = (RelToCheck *) lfirst(rt);
HeapTuple tuple; Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
HeapScanDesc scan; HeapScanDesc scan;
TupleDesc tupdesc; HeapTuple tuple;
relToCheck *rtc = (relToCheck *) lfirst(rt);
/* Lock relation */
typrel = heap_open(rtc->relOid, ExclusiveLock);
tupdesc = RelationGetDescr(typrel);
/* Fetch tuples sequentially */ /* Scan all tuples in this relation */
scan = heap_beginscan(typrel, SnapshotNow, 0, NULL); scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{ {
int i; int i;
/* Test attributes */ /* Test attributes that are of the domain */
for (i = 0; i < rtc->natts; i++) for (i = 0; i < rtc->natts; i++)
{ {
int attnum = rtc->atts[i];
Datum d; Datum d;
bool isNull; bool isNull;
d = heap_getattr(tuple, rtc->atts[i], tupdesc, &isNull); d = heap_getattr(tuple, attnum, tupdesc, &isNull);
if (isNull) if (isNull)
elog(ERROR, "ALTER DOMAIN: Relation \"%s\" Attribute \"%s\" " elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains NULL values",
"contains NULL values", RelationGetRelationName(testrel),
RelationGetRelationName(typrel), NameStr(tupdesc->attrs[attnum - 1]->attname));
NameStr(*attnumAttName(typrel, rtc->atts[i])));
} }
} }
heap_endscan(scan); heap_endscan(scan);
/* Release lock */ /* Close each rel after processing, but keep lock */
heap_close(typrel, NoLock); heap_close(testrel, NoLock);
} }
} }
/*
* Okay to update pg_type row. We can scribble on typTup because it's
* a copy.
*/
typTup->typnotnull = notNull;
/* Setup new tuple */ simple_heap_update(typrel, &tup->t_self, tup);
MemSet(new_record, (Datum) 0, sizeof(new_record));
MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
MemSet(new_record_repl, ' ', sizeof(new_record_repl));
new_record[Anum_pg_type_typnotnull - 1] = BoolGetDatum(notNull);
new_record_repl[Anum_pg_type_typnotnull - 1] = 'r';
/* Build the new tuple */
newtuple = heap_modifytuple(tup, rel,
new_record, new_record_nulls, new_record_repl);
simple_heap_update(rel, &tup->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple); CatalogUpdateIndexes(typrel, tup);
/* Clean up */ /* Clean up */
heap_close(rel, NoLock); heap_freetuple(tup);
heap_freetuple(newtuple); heap_close(typrel, RowExclusiveLock);
} }
/* /*
...@@ -1186,7 +1179,7 @@ AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior beha ...@@ -1186,7 +1179,7 @@ AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior beha
TypeNameToString(typename)); TypeNameToString(typename));
/* Doesn't return if user isn't allowed to alter the domain */ /* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename); domainOwnerCheck(tup, typename);
/* Grab an appropriate lock on the pg_constraint relation */ /* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_openr(ConstraintRelationName, RowExclusiveLock); conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
...@@ -1236,11 +1229,11 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) ...@@ -1236,11 +1229,11 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
{ {
TypeName *typename; TypeName *typename;
Oid domainoid; Oid domainoid;
Relation typrel;
HeapTuple tup; HeapTuple tup;
Relation rel; Form_pg_type typTup;
List *rels; List *rels;
List *rt; List *rt;
Form_pg_type typTup;
EState *estate; EState *estate;
ExprContext *econtext; ExprContext *econtext;
char *ccbin; char *ccbin;
...@@ -1256,9 +1249,9 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) ...@@ -1256,9 +1249,9 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
typename->arrayBounds = NIL; typename->arrayBounds = NIL;
/* Lock the type table */ /* Lock the type table */
rel = heap_openr(TypeRelationName, RowExclusiveLock); typrel = heap_openr(TypeRelationName, RowExclusiveLock);
/* Use LookupTypeName here so that shell types can be found. */ /* Use LookupTypeName here so that shell types can be found (why?). */
domainoid = LookupTypeName(typename); domainoid = LookupTypeName(typename);
if (!OidIsValid(domainoid)) if (!OidIsValid(domainoid))
elog(ERROR, "Type \"%s\" does not exist", elog(ERROR, "Type \"%s\" does not exist",
...@@ -1267,15 +1260,13 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) ...@@ -1267,15 +1260,13 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
tup = SearchSysCacheCopy(TYPEOID, tup = SearchSysCacheCopy(TYPEOID,
ObjectIdGetDatum(domainoid), ObjectIdGetDatum(domainoid),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
elog(ERROR, "AlterDomain: type \"%s\" does not exist", elog(ERROR, "AlterDomain: type \"%s\" does not exist",
TypeNameToString(typename)); TypeNameToString(typename));
typTup = (Form_pg_type) GETSTRUCT(tup); typTup = (Form_pg_type) GETSTRUCT(tup);
/* Doesn't return if user isn't allowed to alter the domain */ /* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename); domainOwnerCheck(tup, typename);
/* Check for unsupported constraint types */ /* Check for unsupported constraint types */
if (IsA(newConstraint, FkConstraint)) if (IsA(newConstraint, FkConstraint))
...@@ -1346,35 +1337,34 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) ...@@ -1346,35 +1337,34 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
/* build execution state for expr */ /* build execution state for expr */
exprstate = ExecPrepareExpr(expr, estate); exprstate = ExecPrepareExpr(expr, estate);
rels = get_rels_with_domain(domainoid); /* Fetch relation list with attributes based on this domain */
/* ShareLock is sufficient to prevent concurrent data changes */
rels = get_rels_with_domain(domainoid, ShareLock);
foreach (rt, rels) foreach (rt, rels)
{ {
relToCheck *rtc = (relToCheck *) lfirst(rt); RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel; Relation testrel = rtc->rel;
TupleDesc tupdesc; TupleDesc tupdesc = RelationGetDescr(testrel);
HeapTuple tuple;
HeapScanDesc scan; HeapScanDesc scan;
HeapTuple tuple;
/* Lock relation against changes */ /* Scan all tuples in this relation */
testrel = heap_open(rtc->relOid, ShareLock);
tupdesc = RelationGetDescr(testrel);
/* Scan through table */
scan = heap_beginscan(testrel, SnapshotNow, 0, NULL); scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{ {
int i; int i;
/* Loop through each attribute of the tuple with a domain */ /* Test attributes that are of the domain */
for (i = 0; i < rtc->natts; i++) for (i = 0; i < rtc->natts; i++)
{ {
int attnum = rtc->atts[i];
Datum d; Datum d;
bool isNull; bool isNull;
Datum conResult; Datum conResult;
d = heap_getattr(tuple, rtc->atts[i], tupdesc, &isNull); d = heap_getattr(tuple, attnum, tupdesc, &isNull);
econtext->domainValue_datum = d; econtext->domainValue_datum = d;
econtext->domainValue_isNull = isNull; econtext->domainValue_isNull = isNull;
...@@ -1384,13 +1374,13 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) ...@@ -1384,13 +1374,13 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
&isNull, NULL); &isNull, NULL);
if (!isNull && !DatumGetBool(conResult)) if (!isNull && !DatumGetBool(conResult))
elog(ERROR, "AlterDomainAddConstraint: Domain %s constraint %s failed", elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains values that fail the new constraint",
NameStr(typTup->typname), constr->name); RelationGetRelationName(testrel),
NameStr(tupdesc->attrs[attnum - 1]->attname));
} }
ResetExprContext(econtext); ResetExprContext(econtext);
} }
heap_endscan(scan); heap_endscan(scan);
/* Hold relation lock till commit (XXX bad for concurrency) */ /* Hold relation lock till commit (XXX bad for concurrency) */
...@@ -1400,114 +1390,158 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) ...@@ -1400,114 +1390,158 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
FreeExecutorState(estate); FreeExecutorState(estate);
/* Clean up */ /* Clean up */
heap_close(rel, NoLock); heap_close(typrel, RowExclusiveLock);
} }
/* /*
* get_rels_with_domain * get_rels_with_domain
* *
* Fetch all relations / attributes which are using the domain * Fetch all relations / attributes which are using the domain
* while maintaining a RowExclusiveLock on the pg_attribute *
* entries. * The result is a list of RelToCheck structs, one for each distinct
* relation, each containing one or more attribute numbers that are of
* the domain type. We have opened each rel and acquired the specified lock
* type on it.
*
* XXX this is completely broken because there is no way to lock the domain
* to prevent columns from being added or dropped while our command runs.
* We can partially protect against column drops by locking relations as we
* come across them, but there is still a race condition (the window between
* seeing a pg_depend entry and acquiring lock on the relation it references).
* Also, holding locks on all these relations simultaneously creates a non-
* trivial risk of deadlock. We can minimize but not eliminate the deadlock
* risk by using the weakest suitable lock (ShareLock for most callers).
*
* XXX to support domains over domains, we'd need to make this smarter,
* or make its callers smarter, so that we could find columns of derived
* domains. Arrays of domains would be a problem too.
* *
* Generally used for retrieving a list of tests when adding * Generally used for retrieving a list of tests when adding
* new constraints to a domain. * new constraints to a domain.
*/ */
List * static List *
get_rels_with_domain(Oid domainOid) get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
{ {
Relation classRel; List *result = NIL;
HeapTuple classTup; Relation depRel;
Relation attRel; ScanKeyData key[2];
HeapScanDesc classScan; SysScanDesc depScan;
List *rels = NIL; HeapTuple depTup;
/* /*
* We need to lock the domain rows for the length of the transaction, * We scan pg_depend to find those things that depend on the domain.
* but once all of the tables and the appropriate attributes are * (We assume we can ignore refobjsubid for a domain.)
* found we can relese the relation lock.
*/ */
classRel = relation_openr(RelationRelationName, ExclusiveLock); depRel = relation_openr(DependRelationName, AccessShareLock);
attRel = relation_openr(AttributeRelationName, RowExclusiveLock);
classScan = heap_beginscan(classRel, SnapshotNow, 0, NULL); ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_depend_refclassid, F_OIDEQ,
ObjectIdGetDatum(RelOid_pg_type));
ScanKeyEntryInitialize(&key[1], 0x0,
Anum_pg_depend_refobjid, F_OIDEQ,
ObjectIdGetDatum(domainOid));
depScan = systable_beginscan(depRel, DependReferenceIndex, true,
SnapshotNow, 2, key);
/* Scan through pg_class for tables */ while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
while ((classTup = heap_getnext(classScan, ForwardScanDirection)) != NULL)
{ {
relToCheck *rtc = NULL; Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
int nkeys = 0; RelToCheck *rtc = NULL;
HeapTuple attTup; List *rellist;
HeapScanDesc attScan; Form_pg_attribute pg_att;
ScanKeyData attKey[2]; int ptr;
Form_pg_class pg_class;
/* Ignore dependees that aren't user columns of tables */
/* (we assume system columns are never of domain types) */
if (pg_depend->classid != RelOid_pg_class ||
pg_depend->objsubid <= 0)
continue;
/* See if we already have an entry for this relation */
foreach(rellist, result)
{
RelToCheck *rt = (RelToCheck *) lfirst(rellist);
/* Get our pg_class struct */ if (RelationGetRelid(rt->rel) == pg_depend->objid)
pg_class = (Form_pg_class) GETSTRUCT(classTup); {
rtc = rt;
break;
}
}
/* Fetch attributes from pg_attribute for the relation of the type domainOid */ if (rtc == NULL)
ScanKeyEntryInitialize(&attKey[nkeys++], 0, Anum_pg_attribute_attrelid, {
F_OIDEQ, ObjectIdGetDatum(HeapTupleGetOid(classTup))); /* First attribute found for this relation */
Relation rel;
/* Acquire requested lock on relation */
rel = heap_open(pg_depend->objid, lockmode);
/* Build the RelToCheck entry with enough space for all atts */
rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
rtc->rel = rel;
rtc->natts = 0;
rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
result = lcons(rtc, result);
}
ScanKeyEntryInitialize(&attKey[nkeys++], 0, Anum_pg_attribute_atttypid, /*
F_OIDEQ, ObjectIdGetDatum(domainOid)); * Confirm column has not been dropped, and is of the expected type.
* This defends against an ALTER DROP COLUMN occuring just before
* we acquired lock ... but if the whole table were dropped, we'd
* still have a problem.
*/
if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
continue;
pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];
if (pg_att->attisdropped || pg_att->atttypid != domainOid)
continue;
/* Setup to scan pg_attribute */ /*
attScan = heap_beginscan(attRel, SnapshotNow, nkeys, attKey); * Okay, add column to result. We store the columns in column-number
* order; this is just a hack to improve predictability of regression
* test output ...
*/
Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
/* Scan through pg_attribute for attributes based on the domain */ ptr = rtc->natts++;
while ((attTup = heap_getnext(attScan, ForwardScanDirection)) != NULL) while (ptr > 0 && rtc->atts[ptr-1] > pg_depend->objsubid)
{ {
if (rtc == NULL) rtc->atts[ptr] = rtc->atts[ptr-1];
{ ptr--;
/* First one found for this rel */
rtc = (relToCheck *)palloc(sizeof(relToCheck));
rtc->atts = (int *)palloc(sizeof(int) * pg_class->relnatts);
rtc->relOid = HeapTupleGetOid(classTup);
rtc->natts = 0;
rels = lcons((void *)rtc, rels);
}
/* Now add the attribute */
rtc->atts[rtc->natts++] = ((Form_pg_attribute) GETSTRUCT(attTup))->attnum;
} }
rtc->atts[ptr] = pg_depend->objsubid;
heap_endscan(attScan);
} }
heap_endscan(classScan); systable_endscan(depScan);
/* Release pg_class, hold pg_attribute for further processing */ relation_close(depRel, AccessShareLock);
relation_close(classRel, ExclusiveLock);
relation_close(attRel, NoLock);
return rels; return result;
} }
/* /*
* domainPermissionCheck * domainOwnerCheck
* *
* Throw an error if the current user doesn't have permission to modify * Throw an error if the current user doesn't have permission to modify
* the domain in an ALTER DOMAIN statement, or if the type isn't actually * the domain in an ALTER DOMAIN statement, or if the type isn't actually
* a domain. * a domain.
*/ */
void static void
domainPermissionCheck(HeapTuple tup, TypeName *typename) domainOwnerCheck(HeapTuple tup, TypeName *typename)
{ {
Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup); Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
/* Permission check: must own type or its namespace */
if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()) &&
!pg_namespace_ownercheck(typTup->typnamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
/* Check that this is actually a domain */ /* Check that this is actually a domain */
if (typTup->typtype != 'd') if (typTup->typtype != 'd')
elog(ERROR, "%s is not a domain", elog(ERROR, "%s is not a domain",
TypeNameToString(typename)); TypeNameToString(typename));
}
/* Permission check: must own type */
if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
}
/* /*
* domainAddConstraint - code shared between CREATE and ALTER DOMAIN * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
......
...@@ -201,19 +201,19 @@ create table domnotnull ...@@ -201,19 +201,19 @@ create table domnotnull
); );
insert into domnotnull default values; insert into domnotnull default values;
alter domain dnotnulltest set not null; -- fails alter domain dnotnulltest set not null; -- fails
ERROR: ALTER DOMAIN: Relation "domnotnull" Attribute "col1" contains NULL values ERROR: ALTER DOMAIN: Relation "domnotnull" attribute "col1" contains NULL values
update domnotnull set col1 = 5; update domnotnull set col1 = 5;
alter domain dnotnulltest set not null; -- fails alter domain dnotnulltest set not null; -- fails
ERROR: ALTER DOMAIN: Relation "domnotnull" Attribute "col2" contains NULL values ERROR: ALTER DOMAIN: Relation "domnotnull" attribute "col2" contains NULL values
update domnotnull set col2 = 6; update domnotnull set col2 = 6;
alter domain dnotnulltest set not null; alter domain dnotnulltest set not null;
alter domain dnotnulltest set not null; -- fails alter domain dnotnulltest set not null; -- fails
ERROR: AlterDomain: dnotnulltest is already set to NOT NULL NOTICE: AlterDomain: dnotnulltest is already set to NOT NULL
update domnotnull set col1 = null; -- fails update domnotnull set col1 = null; -- fails
ERROR: Domain dnotnulltest does not allow NULL values ERROR: Domain dnotnulltest does not allow NULL values
alter domain dnotnulltest drop not null; alter domain dnotnulltest drop not null;
alter domain dnotnulltest drop not null; -- fails alter domain dnotnulltest drop not null; -- fails
ERROR: AlterDomain: dnotnulltest is already set to NULL NOTICE: AlterDomain: dnotnulltest is already set to NULL
update domnotnull set col1 = null; update domnotnull set col1 = null;
drop domain dnotnulltest cascade; drop domain dnotnulltest cascade;
NOTICE: Drop cascades to table domnotnull column col2 NOTICE: Drop cascades to table domnotnull column col2
...@@ -253,7 +253,7 @@ create table domcontest (col1 con); ...@@ -253,7 +253,7 @@ create table domcontest (col1 con);
insert into domcontest values (1); insert into domcontest values (1);
insert into domcontest values (2); insert into domcontest values (2);
alter domain con add constraint t check (VALUE < 1); -- fails alter domain con add constraint t check (VALUE < 1); -- fails
ERROR: AlterDomainAddConstraint: Domain con constraint t failed ERROR: ALTER DOMAIN: Relation "domcontest" attribute "col1" contains values that fail the new constraint
alter domain con add constraint t check (VALUE < 34); alter domain con add constraint t check (VALUE < 34);
alter domain con add check (VALUE > 0); alter domain con add check (VALUE > 0);
insert into domcontest values (-5); -- fails insert into domcontest values (-5); -- fails
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment