diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-07-12 18:43:19 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-07-12 18:43:19 +0000 |
commit | 7c6df91dda27accab3097390ef0d21d93028c7a1 (patch) | |
tree | 5705b975e8de4edf82252e6df28e0bd57c83cb95 /src/backend/commands | |
parent | 791a40f943e2a9353c5823fb4f2bd446ec623d38 (diff) | |
download | postgresql-7c6df91dda27accab3097390ef0d21d93028c7a1.tar.gz postgresql-7c6df91dda27accab3097390ef0d21d93028c7a1.zip |
Second phase of committing Rod Taylor's pg_depend/pg_constraint patch.
pg_relcheck is gone; CHECK, UNIQUE, PRIMARY KEY, and FOREIGN KEY
constraints all have real live entries in pg_constraint. pg_depend
exists, and RESTRICT/CASCADE options work on most kinds of DROP;
however, pg_depend is not yet very well populated with dependencies.
(Most of the ones that are present at this point just replace formerly
hardwired associations, such as the implicit drop of a relation's pg_type
entry when the relation is dropped.) Need to add more logic to create
dependency entries, improve pg_dump to dump constraints in place of
indexes and triggers, and add some regression tests.
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/aggregatecmds.c | 51 | ||||
-rw-r--r-- | src/backend/commands/cluster.c | 14 | ||||
-rw-r--r-- | src/backend/commands/comment.c | 41 | ||||
-rw-r--r-- | src/backend/commands/dbcommands.c | 11 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 82 | ||||
-rw-r--r-- | src/backend/commands/indexcmds.c | 13 | ||||
-rw-r--r-- | src/backend/commands/operatorcmds.c | 48 | ||||
-rw-r--r-- | src/backend/commands/proclang.c | 46 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 703 | ||||
-rw-r--r-- | src/backend/commands/trigger.c | 328 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 117 | ||||
-rw-r--r-- | src/backend/commands/view.c | 18 |
12 files changed, 1031 insertions, 441 deletions
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index c3c1ed16dfc..1b83f03f481 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.2 2002/04/27 03:45:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.3 2002/07/12 18:43:15 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -24,10 +24,10 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_aggregate.h" #include "catalog/pg_proc.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "parser/parse_func.h" @@ -141,13 +141,19 @@ DefineAggregate(List *names, List *parameters) } +/* + * RemoveAggregate + * Deletes an aggregate. + */ void -RemoveAggregate(List *aggName, TypeName *aggType) +RemoveAggregate(RemoveAggrStmt *stmt) { - Relation relation; - HeapTuple tup; + List *aggName = stmt->aggname; + TypeName *aggType = stmt->aggtype; Oid basetypeID; Oid procOid; + HeapTuple tup; + ObjectAddress object; /* * if a basetype is passed in, then attempt to find an aggregate for @@ -164,8 +170,9 @@ RemoveAggregate(List *aggName, TypeName *aggType) procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID); - relation = heap_openr(ProcedureRelationName, RowExclusiveLock); - + /* + * Find the function tuple, do permissions and validity checks + */ tup = SearchSysCache(PROCOID, ObjectIdGetDatum(procOid), 0, 0, 0); @@ -179,30 +186,16 @@ RemoveAggregate(List *aggName, TypeName *aggType) GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName)); - /* Delete any comments associated with this function */ - DeleteComments(procOid, RelationGetRelid(relation)); - - /* Remove the pg_proc tuple */ - simple_heap_delete(relation, &tup->t_self); + /* find_aggregate_func already checked it is an aggregate */ ReleaseSysCache(tup); - heap_close(relation, RowExclusiveLock); - - /* Remove the pg_aggregate tuple */ - - relation = heap_openr(AggregateRelationName, RowExclusiveLock); - - tup = SearchSysCache(AGGFNOID, - ObjectIdGetDatum(procOid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s", - NameListToString(aggName)); - - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); + /* + * Do the deletion + */ + object.classId = RelOid_pg_proc; + object.objectId = procOid; + object.objectSubId = 0; - heap_close(relation, RowExclusiveLock); + performDeletion(&object, stmt->behavior); } diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 3306943fb04..837390744fb 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.82 2002/06/20 20:29:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.83 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "access/genam.h" #include "access/heapam.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/pg_index.h" @@ -64,6 +65,7 @@ cluster(RangeVar *oldrelation, char *oldindexname) OldIndex; char NewHeapName[NAMEDATALEN]; char NewIndexName[NAMEDATALEN]; + ObjectAddress object; /* * We grab exclusive access to the target rel and index for the @@ -119,9 +121,14 @@ cluster(RangeVar *oldrelation, char *oldindexname) CommandCounterIncrement(); /* Destroy old heap (along with its index) and rename new. */ - heap_drop_with_catalog(OIDOldHeap, allowSystemTableMods); + object.classId = RelOid_pg_class; + object.objectId = OIDOldHeap; + object.objectSubId = 0; - CommandCounterIncrement(); + /* XXX better to use DROP_CASCADE here? */ + performDeletion(&object, DROP_RESTRICT); + + /* performDeletion does CommandCounterIncrement at end */ renamerel(OIDNewHeap, oldrelation->relname); @@ -198,6 +205,7 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName) OldIndex->rd_rel->relam, OldIndex->rd_index->indclass, OldIndex->rd_index->indisprimary, + false, /* XXX losing constraint status */ allowSystemTableMods); setRelhasindex(OIDNewHeap, true, diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 160a52246a1..b1ce1b2ce65 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -7,7 +7,7 @@ * Copyright (c) 1996-2001, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.49 2002/06/20 20:51:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.50 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -225,38 +225,45 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment) } /* - * DeleteComments -- + * DeleteComments -- remove comments for an object * - * This routine is used to purge all comments associated with an object, - * regardless of their objsubid. It is called, for example, when a relation - * is destroyed. + * If subid is nonzero then only comments matching it will be removed. + * If subid is zero, all comments matching the oid/classoid will be removed + * (this corresponds to deleting a whole object). */ void -DeleteComments(Oid oid, Oid classoid) +DeleteComments(Oid oid, Oid classoid, int32 subid) { Relation description; - ScanKeyData skey[2]; + ScanKeyData skey[3]; + int nkeys; SysScanDesc sd; HeapTuple oldtuple; /* Use the index to search for all matching old tuples */ - ScanKeyEntryInitialize(&skey[0], - (bits16) 0x0, - (AttrNumber) 1, - (RegProcedure) F_OIDEQ, + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_description_objoid, F_OIDEQ, ObjectIdGetDatum(oid)); - ScanKeyEntryInitialize(&skey[1], - (bits16) 0x0, - (AttrNumber) 2, - (RegProcedure) F_OIDEQ, + ScanKeyEntryInitialize(&skey[1], 0x0, + Anum_pg_description_classoid, F_OIDEQ, ObjectIdGetDatum(classoid)); + if (subid != 0) + { + ScanKeyEntryInitialize(&skey[2], 0x0, + Anum_pg_description_objsubid, F_INT4EQ, + Int32GetDatum(subid)); + nkeys = 3; + } + else + nkeys = 2; + description = heap_openr(DescriptionRelationName, RowExclusiveLock); sd = systable_beginscan(description, DescriptionObjIndex, true, - SnapshotNow, 2, skey); + SnapshotNow, nkeys, skey); while ((oldtuple = systable_getnext(sd)) != NULL) { @@ -266,7 +273,7 @@ DeleteComments(Oid oid, Oid classoid) /* Done */ systable_endscan(sd); - heap_close(description, NoLock); + heap_close(description, RowExclusiveLock); } /* diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 3526b91b997..c6bbb371860 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.95 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.96 2002/07/12 18:43:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -456,8 +456,13 @@ dropdb(const char *dbname) heap_endscan(pgdbscan); - /* Delete any comments associated with the database */ - DeleteComments(db_id, RelationGetRelid(pgdbrel)); + /* + * Delete any comments associated with the database + * + * NOTE: this is probably dead code since any such comments should have + * been in that database, not mine. + */ + DeleteComments(db_id, RelationGetRelid(pgdbrel), 0); /* * Close pg_database, but keep exclusive lock till commit to ensure diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 638fd19a8eb..9a33810b073 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.7 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.8 2002/07/12 18:43:16 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -33,11 +33,11 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "optimizer/cost.h" @@ -532,25 +532,22 @@ CreateFunction(CreateFunctionStmt *stmt) /* * RemoveFunction * Deletes a function. - * - * Exceptions: - * BadArg if name is invalid. - * "ERROR" if function nonexistent. - * ... */ void -RemoveFunction(List *functionName, /* function name to be removed */ - List *argTypes) /* list of TypeName nodes */ +RemoveFunction(RemoveFuncStmt *stmt) { + List *functionName = stmt->funcname; + List *argTypes = stmt->args; /* list of TypeName nodes */ Oid funcOid; - Relation relation; HeapTuple tup; + ObjectAddress object; + /* + * Find the function, do permissions and validity checks + */ funcOid = LookupFuncNameTypeNames(functionName, argTypes, true, "RemoveFunction"); - relation = heap_openr(ProcedureRelationName, RowExclusiveLock); - tup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcOid), 0, 0, 0); @@ -576,12 +573,69 @@ RemoveFunction(List *functionName, /* function name to be removed */ NameListToString(functionName)); } - /* Delete any comments associated with this function */ - DeleteComments(funcOid, RelationGetRelid(relation)); + ReleaseSysCache(tup); + + /* + * Do the deletion + */ + object.classId = RelOid_pg_proc; + object.objectId = funcOid; + object.objectSubId = 0; + + performDeletion(&object, stmt->behavior); +} + +/* + * Guts of function deletion. + * + * Note: this is also used for aggregate deletion, since the OIDs of + * both functions and aggregates point to pg_proc. + */ +void +RemoveFunctionById(Oid funcOid) +{ + Relation relation; + HeapTuple tup; + bool isagg; + + /* + * Delete the pg_proc tuple. + */ + relation = heap_openr(ProcedureRelationName, RowExclusiveLock); + + tup = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveFunctionById: couldn't find tuple for function %u", + funcOid); + + isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg; simple_heap_delete(relation, &tup->t_self); ReleaseSysCache(tup); heap_close(relation, RowExclusiveLock); + + /* + * If there's a pg_aggregate tuple, delete that too. + */ + if (isagg) + { + relation = heap_openr(AggregateRelationName, RowExclusiveLock); + + tup = SearchSysCache(AGGFNOID, + ObjectIdGetDatum(funcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveFunctionById: couldn't find pg_aggregate tuple for %u", + funcOid); + + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(relation, RowExclusiveLock); + } } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 1338f16bb59..5cf03bd11aa 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.76 2002/07/01 15:27:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.77 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_opclass.h" @@ -68,6 +69,7 @@ DefineIndex(RangeVar *heapRelation, List *attributeList, bool unique, bool primary, + bool isconstraint, Expr *predicate, List *rangetable) { @@ -208,7 +210,7 @@ DefineIndex(RangeVar *heapRelation, index_create(relationId, indexRelationName, indexInfo, accessMethodId, classObjectId, - primary, allowSystemTableMods); + primary, isconstraint, allowSystemTableMods); /* * We update the relation's pg_class tuple even if it already has @@ -566,6 +568,7 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior) { Oid indOid; HeapTuple tuple; + ObjectAddress object; indOid = RangeVarGetRelid(relation, false); tuple = SearchSysCache(RELOID, @@ -580,7 +583,11 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior) ReleaseSysCache(tuple); - index_drop(indOid); + object.classId = RelOid_pg_class; + object.objectId = indOid; + object.objectSubId = 0; + + performDeletion(&object, behavior); } /* diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index fcf96c5e9c1..1c4e5f3bee3 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.4 2002/07/01 15:27:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -36,9 +36,9 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_operator.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "parser/parse_oper.h" @@ -217,17 +217,15 @@ RemoveOperator(RemoveOperStmt *stmt) TypeName *typeName1 = (TypeName *) lfirst(stmt->args); TypeName *typeName2 = (TypeName *) lsecond(stmt->args); Oid operOid; - Relation relation; HeapTuple tup; + ObjectAddress object; operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2, "RemoveOperator"); - relation = heap_openr(OperatorRelationName, RowExclusiveLock); - - tup = SearchSysCacheCopy(OPEROID, - ObjectIdGetDatum(operOid), - 0, 0, 0); + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operOid), + 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'", NameListToString(operatorName)); @@ -238,11 +236,39 @@ RemoveOperator(RemoveOperStmt *stmt) GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName)); - /* Delete any comments associated with this operator */ - DeleteComments(operOid, RelationGetRelid(relation)); + ReleaseSysCache(tup); + + /* + * Do the deletion + */ + object.classId = get_system_catalog_relid(OperatorRelationName); + object.objectId = operOid; + object.objectSubId = 0; + + performDeletion(&object, stmt->behavior); +} + +/* + * Guts of operator deletion. + */ +void +RemoveOperatorById(Oid operOid) +{ + Relation relation; + HeapTuple tup; + + relation = heap_openr(OperatorRelationName, RowExclusiveLock); + + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveOperatorById: failed to find tuple for operator %u", + operOid); simple_heap_delete(relation, &tup->t_self); - heap_freetuple(tup); + ReleaseSysCache(tup); + heap_close(relation, RowExclusiveLock); } diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 158927067f1..56dc320e2ef 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.34 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.35 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_language.h" @@ -140,7 +141,7 @@ DropProceduralLanguage(DropPLangStmt *stmt) { char languageName[NAMEDATALEN]; HeapTuple langTup; - Relation rel; + ObjectAddress object; /* * Check permission @@ -155,11 +156,9 @@ DropProceduralLanguage(DropPLangStmt *stmt) */ case_translate_language_name(stmt->plname, languageName); - rel = heap_openr(LanguageRelationName, RowExclusiveLock); - - langTup = SearchSysCacheCopy(LANGNAME, - PointerGetDatum(languageName), - 0, 0, 0); + langTup = SearchSysCache(LANGNAME, + CStringGetDatum(languageName), + 0, 0, 0); if (!HeapTupleIsValid(langTup)) elog(ERROR, "Language %s doesn't exist", languageName); @@ -167,8 +166,39 @@ DropProceduralLanguage(DropPLangStmt *stmt) elog(ERROR, "Language %s isn't a created procedural language", languageName); + object.classId = get_system_catalog_relid(LanguageRelationName); + object.objectId = langTup->t_data->t_oid; + object.objectSubId = 0; + + ReleaseSysCache(langTup); + + /* + * Do the deletion + */ + performDeletion(&object, stmt->behavior); +} + +/* + * Guts of language dropping. + */ +void +DropProceduralLanguageById(Oid langOid) +{ + Relation rel; + HeapTuple langTup; + + rel = heap_openr(LanguageRelationName, RowExclusiveLock); + + langTup = SearchSysCache(LANGOID, + ObjectIdGetDatum(langOid), + 0, 0, 0); + if (!HeapTupleIsValid(langTup)) + elog(ERROR, "DropProceduralLanguageById: language %u not found", + langOid); + simple_heap_delete(rel, &langTup->t_self); - heap_freetuple(langTup); + ReleaseSysCache(langTup); + heap_close(rel, RowExclusiveLock); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 901090c70ac..0d4d277bae4 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.19 2002/07/06 20:16:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.20 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,11 +18,13 @@ #include "access/tuptoaster.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_attrdef.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" @@ -36,6 +38,7 @@ #include "optimizer/clauses.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" +#include "parser/gramparse.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" @@ -57,6 +60,13 @@ static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass); static void drop_default(Oid relid, int16 attnum); static void CheckTupleType(Form_pg_class tuple_class); static bool needs_toast_table(Relation rel); +static void validateForeignKeyConstraint(FkConstraint *fkconstraint, + Relation rel, Relation pkrel); +static Oid createForeignKeyConstraint(Relation rel, Relation pkrel, + FkConstraint *fkconstraint); +static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, + Oid constrOid); +static char *fkMatchTypeToString(char match_type); /* Used by attribute and relation renaming routines: */ @@ -147,6 +157,7 @@ DefineRelation(CreateStmt *stmt, char relkind) ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) * sizeof(ConstrCheck)); int ncheck = 0; + int constr_name_ctr = 0; foreach(listptr, old_constraints) { @@ -167,8 +178,16 @@ DefineRelation(CreateStmt *stmt, char relkind) } else { + /* + * Generate a constraint name. NB: this should match the + * form of names that GenerateConstraintName() may produce + * for names added later. We are assured that there is + * no name conflict, because MergeAttributes() did not pass + * back any names of this form. + */ check[ncheck].ccname = (char *) palloc(NAMEDATALEN); - snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1); + snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", + ++constr_name_ctr); } Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); check[ncheck].ccbin = pstrdup(cdef->cooked_expr); @@ -262,21 +281,20 @@ DefineRelation(CreateStmt *stmt, char relkind) /* * RemoveRelation * Deletes a relation. - * - * Exceptions: - * BadArg if name is invalid. - * - * Note: - * If the relation has indices defined on it, then the index relations - * themselves will be destroyed, too. */ void RemoveRelation(const RangeVar *relation, DropBehavior behavior) { Oid relOid; + ObjectAddress object; relOid = RangeVarGetRelid(relation, false); - heap_drop_with_catalog(relOid, allowSystemTableMods); + + object.classId = RelOid_pg_class; + object.objectId = relOid; + object.objectSubId = 0; + + performDeletion(&object, behavior); } /* @@ -580,7 +598,13 @@ MergeAttributes(List *schema, List *supers, bool istemp, Node *expr; cdef->contype = CONSTR_CHECK; - if (check[i].ccname[0] == '$') + /* + * Do not inherit generated constraint names, since they + * might conflict across multiple inheritance parents. + * (But conflicts between user-assigned names will cause + * an error.) + */ + if (ConstraintNameIsGenerated(check[i].ccname)) cdef->name = NULL; else cdef->name = pstrdup(check[i].ccname); @@ -684,7 +708,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, /* * complementary static functions for MergeAttributes(). * - * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit + * Varattnos of pg_constraint.conbin must be rewritten when subclasses inherit * constraints from parent classes, since the inherited attributes could * be given different column numbers in multiple-inheritance cases. * @@ -747,7 +771,8 @@ StoreCatalogInheritance(Oid relationId, List *supers) return; /* - * Catalog INHERITS information using direct ancestors only. + * Store INHERITS information in pg_inherits using direct ancestors only. + * Also enter dependencies on the direct ancestors. */ relation = heap_openr(InheritsRelationName, RowExclusiveLock); desc = RelationGetDescr(relation); @@ -758,6 +783,8 @@ StoreCatalogInheritance(Oid relationId, List *supers) Oid entryOid = lfirsti(entry); Datum datum[Natts_pg_inherits]; char nullarr[Natts_pg_inherits]; + ObjectAddress childobject, + parentobject; datum[0] = ObjectIdGetDatum(relationId); /* inhrel */ datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */ @@ -782,6 +809,18 @@ StoreCatalogInheritance(Oid relationId, List *supers) heap_freetuple(tuple); + /* + * Store a dependency too + */ + parentobject.classId = RelOid_pg_class; + parentobject.objectId = entryOid; + parentobject.objectSubId = 0; + childobject.classId = RelOid_pg_class; + childobject.objectId = relationId; + childobject.objectSubId = 0; + + recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL); + seqNumber += 1; } @@ -2299,6 +2338,7 @@ AlterTableAddConstraint(Oid myrelid, { Relation rel; List *listptr; + int counter = 0; /* * Grab an exclusive lock on the target table, which we will NOT @@ -2343,7 +2383,12 @@ AlterTableAddConstraint(Oid myrelid, foreach(listptr, newConstraints) { - Node *newConstraint = lfirst(listptr); + /* + * copy is because we may destructively alter the node below + * by inserting a generated name; this name is not necessarily + * correct for children or parents. + */ + Node *newConstraint = copyObject(lfirst(listptr)); switch (nodeTag(newConstraint)) { @@ -2370,12 +2415,23 @@ AlterTableAddConstraint(Oid myrelid, RangeTblEntry *rte; List *qual; Node *expr; - char *name; + /* + * Assign or validate constraint name + */ if (constr->name) - name = constr->name; + { + if (ConstraintNameIsUsed(RelationGetRelid(rel), + RelationGetNamespace(rel), + constr->name)) + elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", + constr->name, + RelationGetRelationName(rel)); + } else - name = "<unnamed>"; + constr->name = GenerateConstraintName(RelationGetRelid(rel), + RelationGetNamespace(rel), + &counter); /* * We need to make a parse state and range @@ -2458,7 +2514,8 @@ AlterTableAddConstraint(Oid myrelid, pfree(slot); if (!successful) - elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name); + elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", + constr->name); /* * Call AddRelationRawConstraints to do @@ -2481,17 +2538,32 @@ AlterTableAddConstraint(Oid myrelid, { FkConstraint *fkconstraint = (FkConstraint *) newConstraint; Relation pkrel; - HeapScanDesc scan; - HeapTuple tuple; - Trigger trig; - List *list; - int count; + Oid constrOid; + + /* + * Assign or validate constraint name + */ + if (fkconstraint->constr_name) + { + if (ConstraintNameIsUsed(RelationGetRelid(rel), + RelationGetNamespace(rel), + fkconstraint->constr_name)) + elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", + fkconstraint->constr_name, + RelationGetRelationName(rel)); + } + else + fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel), + RelationGetNamespace(rel), + &counter); /* * Grab an exclusive lock on the pk table, so that * someone doesn't delete rows out from under us. - * - * XXX wouldn't a lesser lock be sufficient? + * (Although a lesser lock would do for that purpose, + * we'll need exclusive lock anyway to add triggers + * to the pk table; trying to start with a lesser lock + * will just create a risk of deadlock.) */ pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); @@ -2500,119 +2572,471 @@ AlterTableAddConstraint(Oid myrelid, * Validity checks */ if (pkrel->rd_rel->relkind != RELKIND_RELATION) - elog(ERROR, "referenced table \"%s\" not a relation", - fkconstraint->pktable->relname); + elog(ERROR, "referenced relation \"%s\" is not a table", + RelationGetRelationName(pkrel)); + + if (!allowSystemTableMods + && IsSystemRelation(pkrel)) + elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog", + RelationGetRelationName(pkrel)); + + /* XXX shouldn't there be a permission check too? */ if (isTempNamespace(RelationGetNamespace(pkrel)) && !isTempNamespace(RelationGetNamespace(rel))) - elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint."); + elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint"); /* - * First we check for limited correctness of the - * constraint. + * Check that the constraint is satisfied by existing + * rows (we can skip this during table creation). * * NOTE: we assume parser has already checked for * existence of an appropriate unique index on the * referenced relation, and that the column datatypes * are comparable. - * - * Scan through each tuple, calling RI_FKey_check_ins - * (insert trigger) as if that tuple had just been - * inserted. If any of those fail, it should - * elog(ERROR) and that's that. */ - MemSet(&trig, 0, sizeof(trig)); - trig.tgoid = InvalidOid; - if (fkconstraint->constr_name) - trig.tgname = fkconstraint->constr_name; - else - trig.tgname = "<unknown>"; - trig.tgenabled = TRUE; - trig.tgisconstraint = TRUE; - trig.tgconstrrelid = RelationGetRelid(pkrel); - trig.tgdeferrable = FALSE; - trig.tginitdeferred = FALSE; - - trig.tgargs = (char **) palloc( - sizeof(char *) * (4 + length(fkconstraint->fk_attrs) - + length(fkconstraint->pk_attrs))); - - trig.tgargs[0] = trig.tgname; - trig.tgargs[1] = RelationGetRelationName(rel); - trig.tgargs[2] = RelationGetRelationName(pkrel); - trig.tgargs[3] = fkconstraint->match_type; - count = 4; - foreach(list, fkconstraint->fk_attrs) - { - Ident *fk_at = lfirst(list); + if (!fkconstraint->skip_validation) + validateForeignKeyConstraint(fkconstraint, rel, pkrel); - trig.tgargs[count] = fk_at->name; - count += 2; - } - count = 5; - foreach(list, fkconstraint->pk_attrs) - { - Ident *pk_at = lfirst(list); + /* + * Record the FK constraint in pg_constraint. + */ + constrOid = createForeignKeyConstraint(rel, pkrel, + fkconstraint); - trig.tgargs[count] = pk_at->name; - count += 2; - } - trig.tgnargs = count - 1; + /* + * Create the triggers that will enforce the constraint. + */ + createForeignKeyTriggers(rel, fkconstraint, constrOid); - scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + /* + * Close pk table, but keep lock until we've committed. + */ + heap_close(pkrel, NoLock); - while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - /* Make a call to the check function */ + break; + } + default: + elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed"); + } + } - /* - * No parameters are passed, but we do set a - * context - */ - FunctionCallInfoData fcinfo; - TriggerData trigdata; + /* Close rel, but keep lock till commit */ + heap_close(rel, NoLock); +} - MemSet(&fcinfo, 0, sizeof(fcinfo)); +/* + * Scan the existing rows in a table to verify they meet a proposed FK + * constraint. + * + * Caller must have opened and locked both relations. + */ +static void +validateForeignKeyConstraint(FkConstraint *fkconstraint, + Relation rel, + Relation pkrel) +{ + HeapScanDesc scan; + HeapTuple tuple; + Trigger trig; + List *list; + int count; + + /* + * Scan through each tuple, calling RI_FKey_check_ins + * (insert trigger) as if that tuple had just been + * inserted. If any of those fail, it should + * elog(ERROR) and that's that. + */ + MemSet(&trig, 0, sizeof(trig)); + trig.tgoid = InvalidOid; + trig.tgname = fkconstraint->constr_name; + trig.tgenabled = TRUE; + trig.tgisconstraint = TRUE; + trig.tgconstrrelid = RelationGetRelid(pkrel); + trig.tgdeferrable = FALSE; + trig.tginitdeferred = FALSE; + + trig.tgargs = (char **) palloc(sizeof(char *) * + (4 + length(fkconstraint->fk_attrs) + + length(fkconstraint->pk_attrs))); + + trig.tgargs[0] = trig.tgname; + trig.tgargs[1] = RelationGetRelationName(rel); + trig.tgargs[2] = RelationGetRelationName(pkrel); + trig.tgargs[3] = fkMatchTypeToString(fkconstraint->fk_matchtype); + count = 4; + foreach(list, fkconstraint->fk_attrs) + { + Ident *fk_at = lfirst(list); - /* - * We assume RI_FKey_check_ins won't look at - * flinfo... - */ + trig.tgargs[count] = fk_at->name; + count += 2; + } + count = 5; + foreach(list, fkconstraint->pk_attrs) + { + Ident *pk_at = lfirst(list); - trigdata.type = T_TriggerData; - trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; - trigdata.tg_relation = rel; - trigdata.tg_trigtuple = tuple; - trigdata.tg_newtuple = NULL; - trigdata.tg_trigger = &trig; + trig.tgargs[count] = pk_at->name; + count += 2; + } + trig.tgnargs = count - 1; - fcinfo.context = (Node *) &trigdata; + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); - RI_FKey_check_ins(&fcinfo); - } - heap_endscan(scan); + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + FunctionCallInfoData fcinfo; + TriggerData trigdata; - pfree(trig.tgargs); + /* + * Make a call to the trigger function + * + * No parameters are passed, but we do set a context + */ + MemSet(&fcinfo, 0, sizeof(fcinfo)); - heap_close(pkrel, NoLock); + /* + * We assume RI_FKey_check_ins won't look at flinfo... + */ + trigdata.type = T_TriggerData; + trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; + trigdata.tg_relation = rel; + trigdata.tg_trigtuple = tuple; + trigdata.tg_newtuple = NULL; + trigdata.tg_trigger = &trig; - break; - } - default: - elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed"); - } + fcinfo.context = (Node *) &trigdata; + + RI_FKey_check_ins(&fcinfo); } - /* Close rel, but keep lock till commit */ - heap_close(rel, NoLock); + heap_endscan(scan); + + pfree(trig.tgargs); } +/* + * Record an FK constraint in pg_constraint. + */ +static Oid +createForeignKeyConstraint(Relation rel, Relation pkrel, + FkConstraint *fkconstraint) +{ + int16 *fkattr; + int16 *pkattr; + int fkcount; + int pkcount; + List *l; + int i; + + /* Convert foreign-key attr names to attr number array */ + fkcount = length(fkconstraint->fk_attrs); + fkattr = (int16 *) palloc(fkcount * sizeof(int16)); + i = 0; + foreach(l, fkconstraint->fk_attrs) + { + Ident *id = (Ident *) lfirst(l); + + fkattr[i++] = get_attnum(RelationGetRelid(rel), id->name); + } + + /* The same for the referenced primary key attrs */ + pkcount = length(fkconstraint->pk_attrs); + pkattr = (int16 *) palloc(pkcount * sizeof(int16)); + i = 0; + foreach(l, fkconstraint->pk_attrs) + { + Ident *id = (Ident *) lfirst(l); + + pkattr[i++] = get_attnum(RelationGetRelid(pkrel), id->name); + } + + /* Now we can make the pg_constraint entry */ + return CreateConstraintEntry(fkconstraint->constr_name, + RelationGetNamespace(rel), + CONSTRAINT_FOREIGN, + fkconstraint->deferrable, + fkconstraint->initdeferred, + RelationGetRelid(rel), + fkattr, + fkcount, + InvalidOid, /* not a domain constraint */ + RelationGetRelid(pkrel), + pkattr, + pkcount, + fkconstraint->fk_upd_action, + fkconstraint->fk_del_action, + fkconstraint->fk_matchtype, + NULL, + NULL); +} + +/* + * Create the triggers that implement an FK constraint. + */ +static void +createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, + Oid constrOid) +{ + RangeVar *myRel; + CreateTrigStmt *fk_trigger; + List *fk_attr; + List *pk_attr; + Ident *id; + ObjectAddress trigobj, + constrobj; + + /* + * Reconstruct a RangeVar for my relation (not passed in, unfortunately). + */ + myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)), + RelationGetRelationName(rel)); + + /* + * Preset objectAddress fields + */ + constrobj.classId = get_system_catalog_relid(ConstraintRelationName); + constrobj.objectId = constrOid; + constrobj.objectSubId = 0; + trigobj.classId = get_system_catalog_relid(TriggerRelationName); + trigobj.objectSubId = 0; + + /* Make changes-so-far visible */ + CommandCounterIncrement(); + + /* + * Build and execute a CREATE CONSTRAINT TRIGGER statement for the + * CHECK action. + */ + fk_trigger = makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relation = myRel; + fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins"); + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'i'; + fk_trigger->actions[1] = 'u'; + fk_trigger->actions[2] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrel = fkconstraint->pktable; + + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->constr_name)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(myRel->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->pktable->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + if (length(fk_attr) != length(pk_attr)) + elog(ERROR, "number of key attributes in referenced table must be equal to foreign key" + "\n\tIllegal FOREIGN KEY definition references \"%s\"", + fkconstraint->pktable->relname); + + while (fk_attr != NIL) + { + id = (Ident *) lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + id = (Ident *) lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); + } + + trigobj.objectId = CreateTrigger(fk_trigger, true); + + /* Register dependency from trigger to constraint */ + recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); + + /* Make changes-so-far visible */ + CommandCounterIncrement(); + + /* + * Build and execute a CREATE CONSTRAINT TRIGGER statement for the + * ON DELETE action on the referenced table. + */ + fk_trigger = makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relation = fkconstraint->pktable; + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'd'; + fk_trigger->actions[1] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrel = myRel; + switch (fkconstraint->fk_del_action) + { + case FKCONSTR_ACTION_NOACTION: + fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del"); + break; + case FKCONSTR_ACTION_RESTRICT: + fk_trigger->deferrable = false; + fk_trigger->initdeferred = false; + fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del"); + break; + case FKCONSTR_ACTION_CASCADE: + fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del"); + break; + case FKCONSTR_ACTION_SETNULL: + fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del"); + break; + case FKCONSTR_ACTION_SETDEFAULT: + fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del"); + break; + default: + elog(ERROR, "Unrecognized ON DELETE action for FOREIGN KEY constraint"); + break; + } + + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->constr_name)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(myRel->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->pktable->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + while (fk_attr != NIL) + { + id = (Ident *) lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + id = (Ident *) lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); + } + + trigobj.objectId = CreateTrigger(fk_trigger, true); + + /* Register dependency from trigger to constraint */ + recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); + + /* Make changes-so-far visible */ + CommandCounterIncrement(); + + /* + * Build and execute a CREATE CONSTRAINT TRIGGER statement for the + * ON UPDATE action on the referenced table. + */ + fk_trigger = makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relation = fkconstraint->pktable; + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'u'; + fk_trigger->actions[1] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrel = myRel; + switch (fkconstraint->fk_upd_action) + { + case FKCONSTR_ACTION_NOACTION: + fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd"); + break; + case FKCONSTR_ACTION_RESTRICT: + fk_trigger->deferrable = false; + fk_trigger->initdeferred = false; + fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd"); + break; + case FKCONSTR_ACTION_CASCADE: + fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd"); + break; + case FKCONSTR_ACTION_SETNULL: + fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd"); + break; + case FKCONSTR_ACTION_SETDEFAULT: + fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd"); + break; + default: + elog(ERROR, "Unrecognized ON UPDATE action for FOREIGN KEY constraint"); + break; + } + + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->constr_name)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(myRel->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkconstraint->pktable->relname)); + fk_trigger->args = lappend(fk_trigger->args, + makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + while (fk_attr != NIL) + { + id = (Ident *) lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + id = (Ident *) lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, makeString(id->name)); + + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); + } + + trigobj.objectId = CreateTrigger(fk_trigger, true); + + /* Register dependency from trigger to constraint */ + recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); +} + +/* + * fkMatchTypeToString - + * convert FKCONSTR_MATCH_xxx code to string to use in trigger args + */ +static char * +fkMatchTypeToString(char match_type) +{ + switch (match_type) + { + case FKCONSTR_MATCH_FULL: + return pstrdup("FULL"); + case FKCONSTR_MATCH_PARTIAL: + return pstrdup("PARTIAL"); + case FKCONSTR_MATCH_UNSPECIFIED: + return pstrdup("UNSPECIFIED"); + default: + elog(ERROR, "fkMatchTypeToString: Unknown MATCH TYPE '%c'", + match_type); + } + return NULL; /* can't get here */ +} /* * ALTER TABLE DROP CONSTRAINT - * Note: It is legal to remove a constraint with name "" as it is possible - * to add a constraint with name "". - * Christopher Kings-Lynne */ void AlterTableDropConstraint(Oid myrelid, @@ -2620,14 +3044,7 @@ AlterTableDropConstraint(Oid myrelid, DropBehavior behavior) { Relation rel; - int deleted; - - /* - * We don't support CASCADE yet - in fact, RESTRICT doesn't work to - * the spec either! - */ - if (behavior == DROP_CASCADE) - elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword"); + int deleted = 0; /* * Acquire an exclusive lock on the target relation for the duration @@ -2649,26 +3066,39 @@ AlterTableDropConstraint(Oid myrelid, aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* - * Since all we have is the name of the constraint, we have to look - * through all catalogs that could possibly contain a constraint for - * this relation. We also keep a count of the number of constraints - * removed. + * Process child tables if requested. */ + if (inh) + { + List *child, + *children; - deleted = 0; + /* This routine is actually in the planner */ + children = find_all_inheritors(myrelid); - /* - * First, we remove all CHECK constraints with the given name - */ + /* + * find_all_inheritors does the recursive search of the + * inheritance hierarchy, so all we have to do is process all of + * the relids in the list that it returns. + */ + foreach(child, children) + { + Oid childrelid = lfirsti(child); + Relation inhrel; - deleted += RemoveCheckConstraint(rel, constrName, inh); + if (childrelid == myrelid) + continue; + inhrel = heap_open(childrelid, AccessExclusiveLock); + /* do NOT count child constraints in deleted. */ + RemoveRelConstraints(inhrel, constrName, behavior); + heap_close(inhrel, NoLock); + } + } /* - * Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY - * constraints. - * - * Unimplemented. + * Now do the thing on this relation. */ + deleted += RemoveRelConstraints(rel, constrName, behavior); /* Close the target relation */ heap_close(rel, NoLock); @@ -2797,6 +3227,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent) char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid classObjectId[2]; + ObjectAddress baseobject, + toastobject; /* * Grab an exclusive lock on the target table, which we will NOT @@ -2957,7 +3389,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent) toast_idxid = index_create(toast_relid, toast_idxname, indexInfo, BTREE_AM_OID, classObjectId, - true, true); + true, false, true); /* * Update toast rel's pg_class entry to show that it has an index. The @@ -2982,6 +3414,19 @@ AlterTableCreateToastTable(Oid relOid, bool silent) heap_freetuple(reltup); /* + * Register dependency from the toast table to the master, so that + * the toast table will be deleted if the master is. + */ + baseobject.classId = RelOid_pg_class; + baseobject.objectId = relOid; + baseobject.objectSubId = 0; + toastobject.classId = RelOid_pg_class; + toastobject.objectId = toast_relid; + toastobject.objectSubId = 0; + + recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); + + /* * Close relations and make changes visible */ heap_close(class_rel, NoLock); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 18484372ed5..b27dd0f4380 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.120 2002/06/20 20:29:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.121 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,12 +17,12 @@ #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" -#include "commands/comment.h" #include "commands/trigger.h" #include "executor/executor.h" #include "miscadmin.h" @@ -50,8 +50,8 @@ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno, MemoryContext per_tuple_context); -void -CreateTrigger(CreateTrigStmt *stmt) +Oid +CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) { int16 tgtype; int16 tgattr[FUNC_MAX_ARGS]; @@ -69,11 +69,15 @@ CreateTrigger(CreateTrigStmt *stmt) Oid fargtypes[FUNC_MAX_ARGS]; Oid funcoid; Oid funclang; + Oid trigoid; int found = 0; int i; char constrtrigname[NAMEDATALEN]; - char *constrname = ""; - Oid constrrelid = InvalidOid; + char *trigname; + char *constrname; + Oid constrrelid; + ObjectAddress myself, + referenced; rel = heap_openrv(stmt->relation, AccessExclusiveLock); @@ -91,21 +95,28 @@ CreateTrigger(CreateTrigStmt *stmt) aclcheck_error(aclresult, RelationGetRelationName(rel)); /* - * If trigger is an RI constraint, use trigger name as constraint name - * and build a unique trigger name instead. + * If trigger is an RI constraint, use specified trigger name as + * constraint name and build a unique trigger name instead. + * This is mainly for backwards compatibility with CREATE CONSTRAINT + * TRIGGER commands. */ if (stmt->isconstraint) { - constrname = stmt->trigname; snprintf(constrtrigname, sizeof(constrtrigname), "RI_ConstraintTrigger_%u", newoid()); - stmt->trigname = constrtrigname; - - if (stmt->constrrel != NULL) - constrrelid = RangeVarGetRelid(stmt->constrrel, false); - else - constrrelid = InvalidOid; + trigname = constrtrigname; + constrname = stmt->trigname; } + else + { + trigname = stmt->trigname; + constrname = ""; + } + + if (stmt->constrrel != NULL) + constrrelid = RangeVarGetRelid(stmt->constrrel, false); + else + constrrelid = InvalidOid; TRIGGER_CLEAR_TYPE(tgtype); if (stmt->before) @@ -160,9 +171,9 @@ CreateTrigger(CreateTrigStmt *stmt) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); - if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) + if (namestrcmp(&(pg_trigger->tgname), trigname) == 0) elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s", - stmt->trigname, stmt->relation->relname); + trigname, stmt->relation->relname); found++; } systable_endscan(tgscan); @@ -209,12 +220,13 @@ CreateTrigger(CreateTrigStmt *stmt) values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel)); values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein, - CStringGetDatum(stmt->trigname)); + CStringGetDatum(trigname)); values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid); values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true); values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint); - values[Anum_pg_trigger_tgconstrname - 1] = PointerGetDatum(constrname); + values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein, + CStringGetDatum(constrname)); values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid); values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable); values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred); @@ -270,10 +282,16 @@ CreateTrigger(CreateTrigStmt *stmt) /* * Insert tuple into pg_trigger. */ - simple_heap_insert(tgrel, tuple); + trigoid = simple_heap_insert(tgrel, tuple); + CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs); CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple); CatalogCloseIndices(Num_pg_trigger_indices, idescs); + + myself.classId = RelationGetRelid(tgrel); + myself.objectId = trigoid; + myself.objectSubId = 0; + heap_freetuple(tuple); heap_close(tgrel, RowExclusiveLock); @@ -294,10 +312,13 @@ CreateTrigger(CreateTrigStmt *stmt) stmt->relation->relname); ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; + simple_heap_update(pgrel, &tuple->t_self, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); + heap_freetuple(tuple); heap_close(pgrel, RowExclusiveLock); @@ -307,25 +328,129 @@ CreateTrigger(CreateTrigStmt *stmt) * upcoming CommandCounterIncrement... */ + /* + * Record dependencies for trigger. Always place a normal dependency + * on the function. If we are doing this in response to an explicit + * CREATE TRIGGER command, also make trigger be auto-dropped if its + * relation is dropped or if the FK relation is dropped. (Auto drop + * is compatible with our pre-7.3 behavior.) If the trigger is being + * made for a constraint, we can skip the relation links; the dependency + * on the constraint will indirectly depend on the relations. + */ + referenced.classId = RelOid_pg_proc; + referenced.objectId = funcoid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (!forConstraint) + { + referenced.classId = RelOid_pg_class; + referenced.objectId = RelationGetRelid(rel); + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + if (constrrelid != InvalidOid) + { + referenced.classId = RelOid_pg_class; + referenced.objectId = constrrelid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + } + } + /* Keep lock on target rel until end of xact */ heap_close(rel, NoLock); + + return trigoid; } /* * DropTrigger - drop an individual trigger by name */ void -DropTrigger(Oid relid, const char *trigname) +DropTrigger(Oid relid, const char *trigname, DropBehavior behavior) +{ + Relation tgrel; + ScanKeyData skey[2]; + SysScanDesc tgscan; + HeapTuple tup; + ObjectAddress object; + + /* + * Find the trigger, verify permissions, set up object address + */ + tgrel = heap_openr(TriggerRelationName, AccessShareLock); + + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_trigger_tgrelid, F_OIDEQ, + ObjectIdGetDatum(relid)); + + ScanKeyEntryInitialize(&skey[1], 0x0, + Anum_pg_trigger_tgname, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, + SnapshotNow, 2, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + elog(ERROR, "DropTrigger: there is no trigger %s on relation %s", + trigname, get_rel_name(relid)); + + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, get_rel_name(relid)); + + object.classId = RelationGetRelid(tgrel); + object.objectId = tup->t_data->t_oid; + object.objectSubId = 0; + + systable_endscan(tgscan); + heap_close(tgrel, AccessShareLock); + + /* + * Do the deletion + */ + performDeletion(&object, behavior); +} + +/* + * Guts of trigger deletion. + */ +void +RemoveTriggerById(Oid trigOid) { - Relation rel; Relation tgrel; SysScanDesc tgscan; - ScanKeyData key; + ScanKeyData skey[1]; + HeapTuple tup; + Oid relid; + Relation rel; Relation pgrel; HeapTuple tuple; + Form_pg_class classForm; Relation ridescs[Num_pg_class_indices]; - int remaining = 0; - int found = 0; + + tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); + + /* + * Find the trigger to delete. + */ + ScanKeyEntryInitialize(&skey[0], 0x0, + ObjectIdAttributeNumber, F_OIDEQ, + ObjectIdGetDatum(trigOid)); + + tgscan = systable_beginscan(tgrel, TriggerOidIndex, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "RemoveTriggerById: Trigger %u does not exist", + trigOid); + + /* + * Open and exclusive-lock the relation the trigger belongs to. + */ + relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid; rel = heap_open(relid, AccessExclusiveLock); @@ -337,55 +462,22 @@ DropTrigger(Oid relid, const char *trigname) elog(ERROR, "DropTrigger: can't drop trigger for system relation %s", RelationGetRelationName(rel)); - if (!pg_class_ownercheck(relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); - /* - * Search pg_trigger, delete target trigger, count remaining triggers - * for relation. (Although we could fetch and delete the target - * trigger directly, we'd still have to scan the remaining triggers, - * so we may as well do both in one indexscan.) - * - * Note this is OK only because we have AccessExclusiveLock on the rel, - * so no one else is creating/deleting triggers on this rel at the same - * time. + * Delete the pg_trigger tuple. */ - tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); - ScanKeyEntryInitialize(&key, 0, - Anum_pg_trigger_tgrelid, - F_OIDEQ, - ObjectIdGetDatum(relid)); - tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, - SnapshotNow, 1, &key); - while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + simple_heap_delete(tgrel, &tup->t_self); - if (namestrcmp(&(pg_trigger->tgname), trigname) == 0) - { - /* Delete any comments associated with this trigger */ - DeleteComments(tuple->t_data->t_oid, RelationGetRelid(tgrel)); - - simple_heap_delete(tgrel, &tuple->t_self); - found++; - } - else - remaining++; - } systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); - if (found == 0) - elog(ERROR, "DropTrigger: there is no trigger %s on relation %s", - trigname, RelationGetRelationName(rel)); - if (found > 1) /* shouldn't happen */ - elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s", - found, trigname, RelationGetRelationName(rel)); - /* * Update relation's pg_class entry. Crucial side-effect: other * backends (and this one too!) are sent SI message to make them * rebuild relcache entries. + * + * Note this is OK only because we have AccessExclusiveLock on the rel, + * so no one else is creating/deleting triggers on this rel at the same + * time. */ pgrel = heap_openr(RelationRelationName, RowExclusiveLock); tuple = SearchSysCacheCopy(RELOID, @@ -394,116 +486,28 @@ DropTrigger(Oid relid, const char *trigname) if (!HeapTupleIsValid(tuple)) elog(ERROR, "DropTrigger: relation %s not found in pg_class", RelationGetRelationName(rel)); + classForm = (Form_pg_class) GETSTRUCT(tuple); + + if (classForm->reltriggers == 0) + elog(ERROR, "DropTrigger: relation %s has reltriggers = 0", + RelationGetRelationName(rel)); + classForm->reltriggers--; - ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = remaining; simple_heap_update(pgrel, &tuple->t_self, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); + heap_freetuple(tuple); + heap_close(pgrel, RowExclusiveLock); - /* Keep lock on target rel until end of xact */ + /* Keep lock on trigger's rel until end of xact */ heap_close(rel, NoLock); } /* - * Remove all triggers for a relation that's being deleted. - */ -void -RelationRemoveTriggers(Relation rel) -{ - Relation tgrel; - SysScanDesc tgscan; - ScanKeyData key; - HeapTuple tup; - bool found = false; - - tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); - ScanKeyEntryInitialize(&key, 0, - Anum_pg_trigger_tgrelid, - F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(rel))); - tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, - SnapshotNow, 1, &key); - - while (HeapTupleIsValid(tup = systable_getnext(tgscan))) - { - /* Delete any comments associated with this trigger */ - DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel)); - - simple_heap_delete(tgrel, &tup->t_self); - - found = true; - } - - systable_endscan(tgscan); - - /* - * If we deleted any triggers, must update pg_class entry and advance - * command counter to make the updated entry visible. This is fairly - * annoying, since we'e just going to drop the durn thing later, but - * it's necessary to have a consistent state in case we do - * CommandCounterIncrement() below --- if RelationBuildTriggers() - * runs, it will complain otherwise. Perhaps RelationBuildTriggers() - * shouldn't be so picky... - */ - if (found) - { - Relation pgrel; - Relation ridescs[Num_pg_class_indices]; - - pgrel = heap_openr(RelationRelationName, RowExclusiveLock); - tup = SearchSysCacheCopy(RELOID, - ObjectIdGetDatum(RelationGetRelid(rel)), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "RelationRemoveTriggers: relation %u not found in pg_class", - RelationGetRelid(rel)); - - ((Form_pg_class) GETSTRUCT(tup))->reltriggers = 0; - simple_heap_update(pgrel, &tup->t_self, tup); - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tup); - CatalogCloseIndices(Num_pg_class_indices, ridescs); - heap_freetuple(tup); - heap_close(pgrel, RowExclusiveLock); - CommandCounterIncrement(); - } - - /* - * Also drop all constraint triggers referencing this relation - */ - ScanKeyEntryInitialize(&key, 0, - Anum_pg_trigger_tgconstrrelid, - F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(rel))); - tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true, - SnapshotNow, 1, &key); - - while (HeapTupleIsValid(tup = systable_getnext(tgscan))) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tup); - - elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"", - get_rel_name(pg_trigger->tgrelid)); - - DropTrigger(pg_trigger->tgrelid, NameStr(pg_trigger->tgname)); - - /* - * Need to do a command counter increment here to show up new - * pg_class.reltriggers in the next loop iteration (in case there - * are multiple referential integrity action triggers for the same - * FK table defined on the PK table). - */ - CommandCounterIncrement(); - } - systable_endscan(tgscan); - - heap_close(tgrel, RowExclusiveLock); -} - -/* * renametrig - changes the name of a trigger on a relation * * trigger name is changed in trigger catalog. diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index a9b46855810..f148ff65891 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.4 2002/07/01 15:27:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -33,10 +33,10 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" -#include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" #include "parser/parse_func.h" @@ -262,17 +262,14 @@ DefineType(List *names, List *parameters) /* * RemoveType * Removes a datatype. - * - * NOTE: since this tries to remove the associated array type too, it'll - * only work on scalar types. */ void RemoveType(List *names, DropBehavior behavior) { TypeName *typename; - Relation relation; Oid typeoid; HeapTuple tup; + ObjectAddress object; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeNode(TypeName); @@ -280,8 +277,6 @@ RemoveType(List *names, DropBehavior behavior) typename->typmod = -1; typename->arrayBounds = NIL; - relation = heap_openr(TypeRelationName, RowExclusiveLock); - /* Use LookupTypeName here so that shell types can be removed. */ typeoid = LookupTypeName(typename); if (!OidIsValid(typeoid)) @@ -301,30 +296,36 @@ RemoveType(List *names, DropBehavior behavior) GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename)); - /* Delete any comments associated with this type */ - DeleteComments(typeoid, RelationGetRelid(relation)); + ReleaseSysCache(tup); - /* Remove the type tuple from pg_type */ - simple_heap_delete(relation, &tup->t_self); + /* + * Do the deletion + */ + object.classId = RelOid_pg_type; + object.objectId = typeoid; + object.objectSubId = 0; - ReleaseSysCache(tup); + performDeletion(&object, behavior); +} - /* Now, delete the "array of" that type */ - typename->arrayBounds = makeList1(makeInteger(1)); - typeoid = LookupTypeName(typename); - if (!OidIsValid(typeoid)) - elog(ERROR, "Type \"%s\" does not exist", - TypeNameToString(typename)); +/* + * Guts of type deletion. + */ +void +RemoveTypeById(Oid typeOid) +{ + Relation relation; + HeapTuple tup; + + relation = heap_openr(TypeRelationName, RowExclusiveLock); tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typeoid), + ObjectIdGetDatum(typeOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "Type \"%s\" does not exist", - TypeNameToString(typename)); - - DeleteComments(typeoid, RelationGetRelid(relation)); + elog(ERROR, "RemoveTypeById: type %u not found", + typeOid); simple_heap_delete(relation, &tup->t_self); @@ -365,6 +366,8 @@ DefineDomain(CreateDomainStmt *stmt) HeapTuple typeTup; List *schema = stmt->constraints; List *listptr; + Oid basetypeoid; + Form_pg_type baseType; /* Convert list of names to a name and namespace */ domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, @@ -389,40 +392,43 @@ DefineDomain(CreateDomainStmt *stmt) */ typeTup = typenameType(stmt->typename); + baseType = (Form_pg_type) GETSTRUCT(typeTup); + basetypeoid = typeTup->t_data->t_oid; + /* * What we really don't want is domains of domains. This could cause all sorts * of neat issues if we allow that. * * With testing, we may determine complex types should be allowed */ - typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype; + typtype = baseType->typtype; if (typtype != 'b') elog(ERROR, "DefineDomain: %s is not a basetype", TypeNameToString(stmt->typename)); /* passed by value */ - byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval; + byValue = baseType->typbyval; /* Required Alignment */ - alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign; + alignment = baseType->typalign; /* TOAST Strategy */ - storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage; + storage = baseType->typstorage; /* Storage Length */ - internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen; + internalLength = baseType->typlen; /* External Length (unused) */ - externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen; + externalLength = baseType->typprtlen; /* Array element Delimiter */ - delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim; + delimiter = baseType->typdelim; /* I/O Functions */ - inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput; - outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput; - receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive; - sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend; + inputProcedure = baseType->typinput; + outputProcedure = baseType->typoutput; + receiveProcedure = baseType->typreceive; + sendProcedure = baseType->typsend; /* Inherited default value */ datum = SysCacheGetAttr(TYPEOID, typeTup, @@ -441,7 +447,7 @@ DefineDomain(CreateDomainStmt *stmt) * * This is what enables us to make a domain of an array */ - basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; + basetypelem = baseType->typelem; /* * Run through constraints manually to avoid the additional @@ -474,7 +480,7 @@ DefineDomain(CreateDomainStmt *stmt) * Note: Name is strictly for error message */ expr = cookDefault(pstate, colDef->raw_expr, - typeTup->t_data->t_oid, + basetypeoid, stmt->typename->typmod, domainName); /* @@ -540,7 +546,7 @@ DefineDomain(CreateDomainStmt *stmt) */ TypeCreate(domainName, /* type name */ domainNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ + InvalidOid, /* preassigned type oid (none here) */ InvalidOid, /* relation oid (n/a here) */ internalLength, /* internal size */ externalLength, /* external size */ @@ -551,7 +557,7 @@ DefineDomain(CreateDomainStmt *stmt) receiveProcedure, /* receive procedure */ sendProcedure, /* send procedure */ basetypelem, /* element type ID */ - typeTup->t_data->t_oid, /* base type ID */ + basetypeoid, /* base type ID */ defaultValue, /* default type value (text) */ defaultValueBin, /* default type value (binary) */ byValue, /* passed by value */ @@ -571,19 +577,17 @@ DefineDomain(CreateDomainStmt *stmt) /* * RemoveDomain * Removes a domain. + * + * This is identical to RemoveType except we insist it be a domain. */ void RemoveDomain(List *names, DropBehavior behavior) { TypeName *typename; - Relation relation; Oid typeoid; HeapTuple tup; char typtype; - - /* CASCADE unsupported */ - if (behavior == DROP_CASCADE) - elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword"); + ObjectAddress object; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeNode(TypeName); @@ -591,15 +595,17 @@ RemoveDomain(List *names, DropBehavior behavior) typename->typmod = -1; typename->arrayBounds = NIL; - relation = heap_openr(TypeRelationName, RowExclusiveLock); - - typeoid = typenameTypeId(typename); + /* Use LookupTypeName here so that shell types can be removed. */ + typeoid = LookupTypeName(typename); + if (!OidIsValid(typeoid)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(typename)); tup = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "RemoveDomain: type '%s' does not exist", + elog(ERROR, "RemoveDomain: type \"%s\" does not exist", TypeNameToString(typename)); /* Permission check: must own type or its namespace */ @@ -615,17 +621,16 @@ RemoveDomain(List *names, DropBehavior behavior) elog(ERROR, "%s is not a domain", TypeNameToString(typename)); - /* Delete any comments associated with this type */ - DeleteComments(typeoid, RelationGetRelid(relation)); - - /* Remove the type tuple from pg_type */ - simple_heap_delete(relation, &tup->t_self); - ReleaseSysCache(tup); - /* At present, domains don't have associated array types */ + /* + * Do the deletion + */ + object.classId = RelOid_pg_type; + object.objectId = typeoid; + object.objectSubId = 0; - heap_close(relation, RowExclusiveLock); + performDeletion(&object, behavior); } diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index d27350fd467..519df157184 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -6,13 +6,14 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: view.c,v 1.65 2002/07/01 15:27:49 tgl Exp $ + * $Id: view.c,v 1.66 2002/07/12 18:43:16 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/xact.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/namespace.h" #include "commands/tablecmds.h" @@ -252,16 +253,21 @@ DefineView(const RangeVar *view, Query *viewParse) * RemoveView * * Remove a view given its name + * + * We just have to drop the relation; the associated rules will be + * cleaned up automatically. */ void RemoveView(const RangeVar *view, DropBehavior behavior) { Oid viewOid; + ObjectAddress object; viewOid = RangeVarGetRelid(view, false); - /* - * We just have to drop the relation; the associated rules will be - * cleaned up automatically. - */ - heap_drop_with_catalog(viewOid, allowSystemTableMods); + + object.classId = RelOid_pg_class; + object.objectId = viewOid; + object.objectSubId = 0; + + performDeletion(&object, behavior); } |