aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-07-12 18:43:19 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-07-12 18:43:19 +0000
commit7c6df91dda27accab3097390ef0d21d93028c7a1 (patch)
tree5705b975e8de4edf82252e6df28e0bd57c83cb95 /src/backend/commands
parent791a40f943e2a9353c5823fb4f2bd446ec623d38 (diff)
downloadpostgresql-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.c51
-rw-r--r--src/backend/commands/cluster.c14
-rw-r--r--src/backend/commands/comment.c41
-rw-r--r--src/backend/commands/dbcommands.c11
-rw-r--r--src/backend/commands/functioncmds.c82
-rw-r--r--src/backend/commands/indexcmds.c13
-rw-r--r--src/backend/commands/operatorcmds.c48
-rw-r--r--src/backend/commands/proclang.c46
-rw-r--r--src/backend/commands/tablecmds.c703
-rw-r--r--src/backend/commands/trigger.c328
-rw-r--r--src/backend/commands/typecmds.c117
-rw-r--r--src/backend/commands/view.c18
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);
}