1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-12 05:01:15 +03:00

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.
This commit is contained in:
Tom Lane
2002-07-12 18:43:19 +00:00
parent 791a40f943
commit 7c6df91dda
77 changed files with 4074 additions and 1987 deletions

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.48 2002/06/20 20:29:25 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.49 2002/07/12 18:43:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -240,7 +240,8 @@ Boot_DeclareIndexStmt:
DefineIndex(makeRangeVar(NULL, LexIDStr($5)),
LexIDStr($3),
LexIDStr($7),
$9, false, false, NULL, NIL);
$9,
false, false, false, NULL, NIL);
do_end();
}
;
@@ -253,7 +254,8 @@ Boot_DeclareUniqueIndexStmt:
DefineIndex(makeRangeVar(NULL, LexIDStr($6)),
LexIDStr($4),
LexIDStr($8),
$10, true, false, NULL, NIL);
$10,
true, false, false, NULL, NIL);
do_end();
}
;

View File

@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.40 2002/07/11 07:39:27 ishii Exp $
# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.41 2002/07/12 18:43:13 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -10,9 +10,9 @@ subdir = src/backend/catalog
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = catalog.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_type.o pg_conversion.o
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o
BKIFILES = postgres.bki postgres.description
@@ -27,12 +27,12 @@ SUBSYS.o: $(OBJS)
POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
pg_attrdef.h pg_relcheck.h pg_inherits.h pg_index.h \
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h \
pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \
pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \
indexing.h \
pg_depend.h indexing.h \
)
pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)

View File

@@ -0,0 +1,731 @@
/*-------------------------------------------------------------------------
*
* dependency.c
* Routines to support inter-object dependencies.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.1 2002/07/12 18:43:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_language.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "commands/trigger.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "rewrite/rewriteRemove.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/* This enum covers all system catalogs whose OIDs can appear in classid. */
typedef enum ObjectClasses
{
OCLASS_CLASS, /* pg_class */
OCLASS_PROC, /* pg_proc */
OCLASS_TYPE, /* pg_type */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_LANGUAGE, /* pg_language */
OCLASS_OPERATOR, /* pg_operator */
OCLASS_REWRITE, /* pg_rewrite */
OCLASS_TRIGGER /* pg_trigger */
} ObjectClasses;
static bool recursiveDeletion(const ObjectAddress *object,
DropBehavior behavior,
int recursionLevel,
Relation depRel);
static void doDeletion(const ObjectAddress *object);
static ObjectClasses getObjectClass(const ObjectAddress *object);
static char *getObjectDescription(const ObjectAddress *object);
/*
* performDeletion: attempt to drop the specified object. If CASCADE
* behavior is specified, also drop any dependent objects (recursively).
* If RESTRICT behavior is specified, error out if there are any dependent
* objects, except for those that should be implicitly dropped anyway
* according to the dependency type.
*
* This is the outer control routine for all forms of DROP that drop objects
* that can participate in dependencies.
*/
void
performDeletion(const ObjectAddress *object,
DropBehavior behavior)
{
char *objDescription;
Relation depRel;
/*
* Get object description for possible use in failure message.
* Must do this before deleting it ...
*/
objDescription = getObjectDescription(object);
/*
* We save some cycles by opening pg_depend just once and passing the
* Relation pointer down to all the recursive deletion steps.
*/
depRel = heap_openr(DependRelationName, RowExclusiveLock);
if (!recursiveDeletion(object, behavior, 0, depRel))
elog(ERROR, "Cannot drop %s because other objects depend on it"
"\n\tUse DROP ... CASCADE to drop the dependent objects too",
objDescription);
heap_close(depRel, RowExclusiveLock);
pfree(objDescription);
}
/*
* recursiveDeletion: delete a single object for performDeletion.
*
* Returns TRUE if successful, FALSE if not. recursionLevel is 0 at the
* outer level, >0 when deleting a dependent object.
*
* In RESTRICT mode, we perform all the deletions anyway, but elog a NOTICE
* and return FALSE if we find a restriction violation. performDeletion
* will then abort the transaction to nullify the deletions. We have to
* do it this way to (a) report all the direct and indirect dependencies
* while (b) not going into infinite recursion if there's a cycle.
*/
static bool
recursiveDeletion(const ObjectAddress *object,
DropBehavior behavior,
int recursionLevel,
Relation depRel)
{
bool ok = true;
char *objDescription;
ScanKeyData key[3];
int nkeys;
SysScanDesc scan;
HeapTuple tup;
ObjectAddress otherObject;
/*
* Get object description for possible use in messages. Must do this
* before deleting it ...
*/
objDescription = getObjectDescription(object);
/*
* Step 1: find and remove pg_depend records that link from this
* object to others. We have to do this anyway, and doing it first
* ensures that we avoid infinite recursion in the case of cycles.
* Also, some dependency types require an error here.
*
* When dropping a whole object (subId = 0), remove all pg_depend
* records for its sub-objects too.
*/
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_depend_classid, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyEntryInitialize(&key[1], 0x0,
Anum_pg_depend_objid, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
if (object->objectSubId != 0)
{
ScanKeyEntryInitialize(&key[2], 0x0,
Anum_pg_depend_objsubid, F_INT4EQ,
Int32GetDatum(object->objectSubId));
nkeys = 3;
}
else
nkeys = 2;
scan = systable_beginscan(depRel, DependDependerIndex, true,
SnapshotNow, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
otherObject.classId = foundDep->refclassid;
otherObject.objectId = foundDep->refobjid;
otherObject.objectSubId = foundDep->refobjsubid;
switch (foundDep->deptype)
{
case DEPENDENCY_NORMAL:
case DEPENDENCY_AUTO:
/* no problem */
break;
case DEPENDENCY_INTERNAL:
/*
* Disallow direct DROP of an object that is part of the
* implementation of another object. (We just elog here,
* rather than issuing a notice and continuing, since
* no other dependencies are likely to be interesting.)
*/
if (recursionLevel == 0)
elog(ERROR, "Cannot drop %s because %s requires it"
"\n\tYou may DROP the other object instead",
objDescription,
getObjectDescription(&otherObject));
break;
case DEPENDENCY_PIN:
/*
* Should not happen; PIN dependencies should have zeroes
* in the depender fields...
*/
elog(ERROR, "recursiveDeletion: incorrect use of PIN dependency with %s",
objDescription);
break;
default:
elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
foundDep->deptype, objDescription);
break;
}
simple_heap_delete(depRel, &tup->t_self);
}
systable_endscan(scan);
/*
* CommandCounterIncrement here to ensure that preceding changes
* are all visible; in particular, that the above deletions of pg_depend
* entries are visible. That prevents infinite recursion in case of
* a dependency loop (which is perfectly legal).
*/
CommandCounterIncrement();
/*
* Step 2: scan pg_depend records that link to this object, showing
* the things that depend on it. Recursively delete those things.
* (We don't delete the pg_depend records here, as the recursive call
* will do that.) Note it's important to delete the dependent objects
* before the referenced one, since the deletion routines might do
* things like try to update the pg_class record when deleting a
* check constraint.
*
* Again, when dropping a whole object (subId = 0), find pg_depend
* records for its sub-objects too.
*
* NOTE: because we are using SnapshotNow, if a recursive call deletes
* any pg_depend tuples that our scan hasn't yet visited, we will not see
* them as good when we do visit them. This is essential for correct
* behavior if there are multiple dependency paths between two objects
* --- else we might try to delete an already-deleted object.
*/
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_depend_refclassid, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyEntryInitialize(&key[1], 0x0,
Anum_pg_depend_refobjid, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
if (object->objectSubId != 0)
{
ScanKeyEntryInitialize(&key[2], 0x0,
Anum_pg_depend_refobjsubid, F_INT4EQ,
Int32GetDatum(object->objectSubId));
nkeys = 3;
}
else
nkeys = 2;
scan = systable_beginscan(depRel, DependReferenceIndex, true,
SnapshotNow, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
otherObject.classId = foundDep->classid;
otherObject.objectId = foundDep->objid;
otherObject.objectSubId = foundDep->objsubid;
switch (foundDep->deptype)
{
case DEPENDENCY_NORMAL:
if (behavior == DROP_RESTRICT)
{
elog(NOTICE, "%s depends on %s",
getObjectDescription(&otherObject),
objDescription);
ok = false;
}
else
elog(NOTICE, "Drop cascades to %s",
getObjectDescription(&otherObject));
if (!recursiveDeletion(&otherObject, behavior,
recursionLevel + 1, depRel))
ok = false;
break;
case DEPENDENCY_AUTO:
case DEPENDENCY_INTERNAL:
/*
* We propagate the DROP without complaint even in the
* RESTRICT case. (However, normal dependencies on the
* component object could still cause failure.)
*/
elog(DEBUG1, "Drop internally cascades to %s",
getObjectDescription(&otherObject));
if (!recursiveDeletion(&otherObject, behavior,
recursionLevel + 1, depRel))
ok = false;
break;
case DEPENDENCY_PIN:
/*
* For a PIN dependency we just elog immediately; there
* won't be any others to report.
*/
elog(ERROR, "Cannot drop %s because it is required by the database system",
objDescription);
break;
default:
elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
foundDep->deptype, objDescription);
break;
}
}
systable_endscan(scan);
/*
* We do not need CommandCounterIncrement here, since if step 2 did
* anything then each recursive call will have ended with one.
*/
/*
* Step 3: delete the object itself.
*/
doDeletion(object);
/*
* Delete any comments associated with this object. (This is a convenient
* place to do it instead of having every object type know to do it.)
*/
DeleteComments(object->objectId, object->classId, object->objectSubId);
/*
* CommandCounterIncrement here to ensure that preceding changes
* are all visible.
*/
CommandCounterIncrement();
/*
* And we're done!
*/
pfree(objDescription);
return ok;
}
/*
* doDeletion: actually delete a single object
*/
static void
doDeletion(const ObjectAddress *object)
{
switch (getObjectClass(object))
{
case OCLASS_CLASS:
{
HeapTuple relTup;
char relKind;
/*
* Need the relkind to figure out how to drop.
*/
relTup = SearchSysCache(RELOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(relTup))
elog(ERROR, "doDeletion: Relation %u does not exist",
object->objectId);
relKind = ((Form_pg_class) GETSTRUCT(relTup))->relkind;
ReleaseSysCache(relTup);
if (relKind == RELKIND_INDEX)
{
Assert(object->objectSubId == 0);
index_drop(object->objectId);
}
else
{
if (object->objectSubId != 0)
elog(ERROR, "DROP COLUMN not implemented yet");
else
heap_drop_with_catalog(object->objectId);
}
break;
}
case OCLASS_PROC:
RemoveFunctionById(object->objectId);
break;
case OCLASS_TYPE:
RemoveTypeById(object->objectId);
break;
case OCLASS_CONSTRAINT:
RemoveConstraintById(object->objectId);
break;
case OCLASS_LANGUAGE:
DropProceduralLanguageById(object->objectId);
break;
case OCLASS_OPERATOR:
RemoveOperatorById(object->objectId);
break;
case OCLASS_REWRITE:
RemoveRewriteRuleById(object->objectId);
break;
case OCLASS_TRIGGER:
RemoveTriggerById(object->objectId);
break;
default:
elog(ERROR, "doDeletion: Unsupported object class %u",
object->classId);
}
}
/*
* Determine the class of a given object identified by objectAddress.
*
* This function is needed just because some of the system catalogs do
* not have hardwired-at-compile-time OIDs.
*/
static ObjectClasses
getObjectClass(const ObjectAddress *object)
{
static bool reloids_initialized = false;
static Oid reloid_pg_constraint;
static Oid reloid_pg_language;
static Oid reloid_pg_operator;
static Oid reloid_pg_rewrite;
static Oid reloid_pg_trigger;
/* Easy for the bootstrapped catalogs... */
switch (object->classId)
{
case RelOid_pg_class:
/* caller must check objectSubId */
return OCLASS_CLASS;
case RelOid_pg_proc:
Assert(object->objectSubId == 0);
return OCLASS_PROC;
case RelOid_pg_type:
Assert(object->objectSubId == 0);
return OCLASS_TYPE;
}
/*
* Handle cases where catalog's OID is not hardwired.
*
* Although these OIDs aren't compile-time constants, they surely
* shouldn't change during a backend's run. So, look them up the
* first time through and then cache them.
*/
if (!reloids_initialized)
{
reloid_pg_constraint = get_system_catalog_relid(ConstraintRelationName);
reloid_pg_language = get_system_catalog_relid(LanguageRelationName);
reloid_pg_operator = get_system_catalog_relid(OperatorRelationName);
reloid_pg_rewrite = get_system_catalog_relid(RewriteRelationName);
reloid_pg_trigger = get_system_catalog_relid(TriggerRelationName);
reloids_initialized = true;
}
if (object->classId == reloid_pg_constraint)
{
Assert(object->objectSubId == 0);
return OCLASS_CONSTRAINT;
}
if (object->classId == reloid_pg_language)
{
Assert(object->objectSubId == 0);
return OCLASS_LANGUAGE;
}
if (object->classId == reloid_pg_operator)
{
Assert(object->objectSubId == 0);
return OCLASS_OPERATOR;
}
if (object->classId == reloid_pg_rewrite)
{
Assert(object->objectSubId == 0);
return OCLASS_REWRITE;
}
if (object->classId == reloid_pg_trigger)
{
Assert(object->objectSubId == 0);
return OCLASS_TRIGGER;
}
elog(ERROR, "getObjectClass: Unknown object class %u",
object->classId);
return OCLASS_CLASS; /* keep compiler quiet */
}
/*
* getObjectDescription: build an object description for messages
*
* The result is a palloc'd string.
*/
static char *
getObjectDescription(const ObjectAddress *object)
{
StringInfoData buffer;
initStringInfo(&buffer);
switch (getObjectClass(object))
{
case OCLASS_CLASS:
{
HeapTuple relTup;
Form_pg_class relForm;
relTup = SearchSysCache(RELOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(relTup))
elog(ERROR, "getObjectDescription: Relation %u does not exist",
object->objectId);
relForm = (Form_pg_class) GETSTRUCT(relTup);
switch (relForm->relkind)
{
case RELKIND_RELATION:
appendStringInfo(&buffer, "table %s",
NameStr(relForm->relname));
break;
case RELKIND_INDEX:
appendStringInfo(&buffer, "index %s",
NameStr(relForm->relname));
break;
case RELKIND_SPECIAL:
appendStringInfo(&buffer, "special system relation %s",
NameStr(relForm->relname));
break;
case RELKIND_SEQUENCE:
appendStringInfo(&buffer, "sequence %s",
NameStr(relForm->relname));
break;
case RELKIND_UNCATALOGED:
appendStringInfo(&buffer, "uncataloged table %s",
NameStr(relForm->relname));
break;
case RELKIND_TOASTVALUE:
appendStringInfo(&buffer, "toast table %s",
NameStr(relForm->relname));
break;
case RELKIND_VIEW:
appendStringInfo(&buffer, "view %s",
NameStr(relForm->relname));
break;
default:
/* shouldn't get here */
appendStringInfo(&buffer, "relation %s",
NameStr(relForm->relname));
break;
}
if (object->objectSubId != 0)
appendStringInfo(&buffer, " column %s",
get_attname(object->objectId,
object->objectSubId));
ReleaseSysCache(relTup);
break;
}
case OCLASS_PROC:
/* XXX could improve on this */
appendStringInfo(&buffer, "function %s",
get_func_name(object->objectId));
break;
case OCLASS_TYPE:
{
HeapTuple typeTup;
typeTup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "getObjectDescription: Type %u does not exist",
object->objectId);
appendStringInfo(&buffer, "type %s",
NameStr(((Form_pg_type) GETSTRUCT(typeTup))->typname));
ReleaseSysCache(typeTup);
break;
}
case OCLASS_CONSTRAINT:
{
Relation conDesc;
ScanKeyData skey[1];
SysScanDesc rcscan;
HeapTuple tup;
Form_pg_constraint con;
conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(rcscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getObjectDescription: Constraint %u does not exist",
object->objectId);
con = (Form_pg_constraint) GETSTRUCT(tup);
appendStringInfo(&buffer, "constraint %s",
NameStr(con->conname));
if (OidIsValid(con->conrelid))
appendStringInfo(&buffer, " on table %s",
get_rel_name(con->conrelid));
systable_endscan(rcscan);
heap_close(conDesc, AccessShareLock);
break;
}
case OCLASS_LANGUAGE:
{
HeapTuple langTup;
langTup = SearchSysCache(LANGOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(langTup))
elog(ERROR, "getObjectDescription: Language %u does not exist",
object->objectId);
appendStringInfo(&buffer, "language %s",
NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname));
ReleaseSysCache(langTup);
break;
}
case OCLASS_OPERATOR:
/* XXX could improve on this */
appendStringInfo(&buffer, "operator %s",
get_opname(object->objectId));
break;
case OCLASS_REWRITE:
{
Relation ruleDesc;
ScanKeyData skey[1];
SysScanDesc rcscan;
HeapTuple tup;
Form_pg_rewrite rule;
ruleDesc = heap_openr(RewriteRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(ruleDesc, RewriteOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(rcscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getObjectDescription: Rule %u does not exist",
object->objectId);
rule = (Form_pg_rewrite) GETSTRUCT(tup);
appendStringInfo(&buffer, "rule %s",
NameStr(rule->rulename));
if (OidIsValid(rule->ev_class))
appendStringInfo(&buffer, " on table %s",
get_rel_name(rule->ev_class));
systable_endscan(rcscan);
heap_close(ruleDesc, AccessShareLock);
break;
}
case OCLASS_TRIGGER:
{
Relation trigDesc;
ScanKeyData skey[1];
SysScanDesc tgscan;
HeapTuple tup;
Form_pg_trigger trig;
trigDesc = heap_openr(TriggerRelationName, AccessShareLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
tgscan = systable_beginscan(trigDesc, TriggerOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(tgscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getObjectDescription: Trigger %u does not exist",
object->objectId);
trig = (Form_pg_trigger) GETSTRUCT(tup);
appendStringInfo(&buffer, "trigger %s",
NameStr(trig->tgname));
if (OidIsValid(trig->tgrelid))
appendStringInfo(&buffer, " on table %s",
get_rel_name(trig->tgrelid));
systable_endscan(tgscan);
heap_close(trigDesc, AccessShareLock);
break;
}
default:
appendStringInfo(&buffer, "unknown object %u %u %d",
object->classId,
object->objectId,
object->objectSubId);
break;
}
return buffer.data;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.204 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.205 2002/07/12 18:43:13 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -33,21 +33,20 @@
#include "access/genam.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/pg_attrdef.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_relcheck.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/trigger.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
@@ -69,8 +68,6 @@ static void AddNewRelationTuple(Relation pg_class_desc,
char relkind, bool relhasoids);
static void DeleteAttributeTuples(Relation rel);
static void DeleteRelationTuple(Relation rel);
static void DeleteTypeTuple(Relation rel);
static void RelationRemoveIndexes(Relation relation);
static void RelationRemoveInheritance(Relation relation);
static void AddNewRelationType(const char *typeName,
Oid typeNamespace,
@@ -80,7 +77,7 @@ static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
static void StoreConstraints(Relation rel, TupleDesc tupdesc);
static void SetRelationNumChecks(Relation rel, int numchecks);
static void RemoveConstraints(Relation rel);
static void RemoveDefaults(Relation rel);
static void RemoveStatistics(Relation rel);
@@ -760,106 +757,42 @@ heap_create_with_catalog(const char *relname,
}
/* --------------------------------
/*
* RelationRemoveInheritance
*
* Note: for now, we cause an exception if relation is a
* superclass. Someday, we may want to allow this and merge
* the type info into subclass procedures.... this seems like
* lots of work.
* --------------------------------
* Formerly, this routine checked for child relations and aborted the
* deletion if any were found. Now we rely on the dependency mechanism
* to check for or delete child relations. By the time we get here,
* there are no children and we need only remove the pg_inherits rows.
*/
static void
RelationRemoveInheritance(Relation relation)
{
Relation catalogRelation;
HeapTuple tuple;
HeapScanDesc scan;
SysScanDesc scan;
ScanKeyData entry;
bool found = false;
/*
* open pg_inherits
*/
catalogRelation = heap_openr(InheritsRelationName, RowExclusiveLock);
/*
* form a scan key for the subclasses of this class and begin scanning
*/
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent,
F_OIDEQ,
ScanKeyEntryInitialize(&entry, 0x0,
Anum_pg_inherits_inhrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
scan = heap_beginscan(catalogRelation,
SnapshotNow,
1,
&entry);
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndex, true,
SnapshotNow, 1, &entry);
/*
* if any subclasses exist, then we disallow the deletion.
*/
if ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid subclass = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
char *subclassname;
subclassname = get_rel_name(subclass);
/* Just in case get_rel_name fails... */
if (subclassname)
elog(ERROR, "Relation \"%s\" inherits from \"%s\"",
subclassname, RelationGetRelationName(relation));
else
elog(ERROR, "Relation %u inherits from \"%s\"",
subclass, RelationGetRelationName(relation));
}
heap_endscan(scan);
/*
* If we get here, it means the relation has no subclasses so we can
* trash it. First we remove dead INHERITS tuples.
*/
entry.sk_attno = Anum_pg_inherits_inhrelid;
scan = heap_beginscan(catalogRelation,
SnapshotNow,
1,
&entry);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
simple_heap_delete(catalogRelation, &tuple->t_self);
found = true;
}
heap_endscan(scan);
systable_endscan(scan);
heap_close(catalogRelation, RowExclusiveLock);
}
/*
* RelationRemoveIndexes
*/
static void
RelationRemoveIndexes(Relation relation)
{
List *indexoidlist,
*indexoidscan;
indexoidlist = RelationGetIndexList(relation);
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirsti(indexoidscan);
index_drop(indexoid);
}
freeList(indexoidlist);
}
/* --------------------------------
* DeleteRelationTuple
*
* --------------------------------
*/
static void
DeleteRelationTuple(Relation rel)
@@ -1049,163 +982,34 @@ DeleteAttributeTuples(Relation rel)
heap_close(pg_attribute_desc, RowExclusiveLock);
}
/* --------------------------------
* DeleteTypeTuple
*
* If the user attempts to destroy a relation and there
* exists attributes in other relations of type
* "relation we are deleting", then we have to do something
* special. presently we disallow the destroy.
* --------------------------------
*/
static void
DeleteTypeTuple(Relation rel)
{
Relation pg_type_desc;
HeapScanDesc pg_type_scan;
Relation pg_attribute_desc;
HeapScanDesc pg_attribute_scan;
ScanKeyData key;
ScanKeyData attkey;
HeapTuple tup;
HeapTuple atttup;
Oid typoid;
/*
* open pg_type
*/
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
/*
* create a scan key to locate the type tuple corresponding to this
* relation.
*/
ScanKeyEntryInitialize(&key, 0,
Anum_pg_type_typrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
pg_type_scan = heap_beginscan(pg_type_desc,
SnapshotNow,
1,
&key);
/*
* use heap_getnext() to fetch the pg_type tuple. If this tuple is
* not valid then something's wrong.
*/
tup = heap_getnext(pg_type_scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
{
heap_endscan(pg_type_scan);
heap_close(pg_type_desc, RowExclusiveLock);
elog(ERROR, "DeleteTypeTuple: type \"%s\" does not exist",
RelationGetRelationName(rel));
}
/*
* now scan pg_attribute. if any other relations have attributes of
* the type of the relation we are deleteing then we have to disallow
* the deletion. should talk to stonebraker about this. -cim 6/19/90
*/
typoid = tup->t_data->t_oid;
pg_attribute_desc = heap_openr(AttributeRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&attkey,
0,
Anum_pg_attribute_atttypid,
F_OIDEQ,
ObjectIdGetDatum(typoid));
pg_attribute_scan = heap_beginscan(pg_attribute_desc,
SnapshotNow,
1,
&attkey);
/*
* try and get a pg_attribute tuple. if we succeed it means we can't
* delete the relation because something depends on the schema.
*/
atttup = heap_getnext(pg_attribute_scan, ForwardScanDirection);
if (HeapTupleIsValid(atttup))
{
Oid relid = ((Form_pg_attribute) GETSTRUCT(atttup))->attrelid;
heap_endscan(pg_attribute_scan);
heap_close(pg_attribute_desc, RowExclusiveLock);
heap_endscan(pg_type_scan);
heap_close(pg_type_desc, RowExclusiveLock);
elog(ERROR, "DeleteTypeTuple: column of type %s exists in relation %u",
RelationGetRelationName(rel), relid);
}
heap_endscan(pg_attribute_scan);
heap_close(pg_attribute_desc, RowExclusiveLock);
/*
* Ok, it's safe so we delete the relation tuple from pg_type and
* finish up.
*/
simple_heap_delete(pg_type_desc, &tup->t_self);
heap_endscan(pg_type_scan);
heap_close(pg_type_desc, RowExclusiveLock);
}
/* ----------------------------------------------------------------
* heap_drop_with_catalog - removes all record of named relation from catalogs
* heap_drop_with_catalog - removes specified relation from catalogs
*
* 1) open relation, check for existence, etc.
* 2) remove inheritance information
* 3) remove indexes
* 4) remove pg_class tuple
* 5) remove pg_attribute tuples and related descriptions
* 6) remove pg_description tuples
* 7) remove pg_type tuples
* 8) RemoveConstraints ()
* 9) unlink relation
* 1) open relation, acquire exclusive lock.
* 2) flush relation buffers from bufmgr
* 3) remove inheritance information
* 4) remove pg_statistic tuples
* 5) remove pg_attribute tuples and related items
* 6) remove pg_class tuple
* 7) unlink relation file
*
* old comments
* Except for vital relations, removes relation from
* relation catalog, and related attributes from
* attribute catalog (needed?). (Anything else?)
*
* get proper relation from relation catalog (if not arg)
* scan attribute catalog deleting attributes of reldesc
* (necessary?)
* delete relation from relation catalog
* (How are the tuples of the relation discarded?)
*
* XXX Must fix to work with indexes.
* There may be a better order for doing things.
* Problems with destroying a deleted database--cannot create
* a struct reldesc without having an open file descriptor.
* Note that this routine is not responsible for dropping objects that are
* linked to the pg_class entry via dependencies (for example, indexes and
* constraints). Those are deleted by the dependency-tracing logic in
* dependency.c before control gets here. In general, therefore, this routine
* should never be called directly; go through performDeletion() instead.
* ----------------------------------------------------------------
*/
void
heap_drop_with_catalog(Oid rid,
bool allow_system_table_mods)
heap_drop_with_catalog(Oid rid)
{
Relation rel;
Oid toasttableOid;
int i;
/*
* Open and lock the relation.
*/
rel = heap_open(rid, AccessExclusiveLock);
toasttableOid = rel->rd_rel->reltoastrelid;
/*
* prevent deletion of system relations
*/
if (!allow_system_table_mods &&
IsSystemRelation(rel))
elog(ERROR, "System relation \"%s\" may not be dropped",
RelationGetRelationName(rel));
/*
* Release all buffers that belong to this relation, after writing any
@@ -1216,43 +1020,22 @@ heap_drop_with_catalog(Oid rid,
elog(ERROR, "heap_drop_with_catalog: FlushRelationBuffers returned %d",
i);
/*
* remove rules if necessary
*/
if (rel->rd_rules != NULL)
RelationRemoveRules(rid);
/* triggers */
RelationRemoveTriggers(rel);
/*
* remove inheritance information
*/
RelationRemoveInheritance(rel);
/*
* remove indexes if necessary
* delete statistics
*/
RelationRemoveIndexes(rel);
RemoveStatistics(rel);
/*
* delete attribute tuples
* delete attribute tuples and associated defaults
*/
DeleteAttributeTuples(rel);
/*
* delete comments, statistics, and constraints
*/
DeleteComments(rid, RelOid_pg_class);
RemoveStatistics(rel);
RemoveConstraints(rel);
/*
* delete type tuple
*/
DeleteTypeTuple(rel);
RemoveDefaults(rel);
/*
* delete relation tuple
@@ -1276,10 +1059,6 @@ heap_drop_with_catalog(Oid rid,
* flush the relation from the relcache
*/
RelationForgetRelation(rid);
/* If it has a toast table, recurse to get rid of that too */
if (OidIsValid(toasttableOid))
heap_drop_with_catalog(toasttableOid, true);
}
@@ -1374,11 +1153,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
{
Node *expr;
char *ccsrc;
Relation rcrel;
Relation idescs[Num_pg_relcheck_indices];
HeapTuple tuple;
Datum values[4];
static char nulls[4] = {' ', ' ', ' ', ' '};
List *varList;
int keycount;
int16 *attNos;
/*
* Convert condition to a normal boolean expression tree.
@@ -1394,26 +1171,55 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
RelationGetRelid(rel)),
false);
values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel);
values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein,
CStringGetDatum(ccname));
values[Anum_pg_relcheck_rcbin - 1] = DirectFunctionCall1(textin,
CStringGetDatum(ccbin));
values[Anum_pg_relcheck_rcsrc - 1] = DirectFunctionCall1(textin,
CStringGetDatum(ccsrc));
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
tuple = heap_formtuple(rcrel->rd_att, values, nulls);
simple_heap_insert(rcrel, tuple);
CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices,
idescs);
CatalogIndexInsert(idescs, Num_pg_relcheck_indices, rcrel, tuple);
CatalogCloseIndices(Num_pg_relcheck_indices, idescs);
heap_close(rcrel, RowExclusiveLock);
/*
* Find columns of rel that are used in ccbin
*/
varList = pull_var_clause(expr, false);
keycount = length(varList);
if (keycount > 0)
{
List *vl;
int i = 0;
attNos = (int16 *) palloc(keycount * sizeof(int16));
foreach(vl, varList)
{
Var *var = (Var *) lfirst(vl);
int j;
for (j = 0; j < i; j++)
if (attNos[j] == var->varattno)
break;
if (j == i)
attNos[i++] = var->varattno;
}
keycount = i;
}
else
attNos = NULL;
/*
* Create the Check Constraint
*/
CreateConstraintEntry(ccname, /* Constraint Name */
RelationGetNamespace(rel), /* namespace */
CONSTRAINT_CHECK, /* Constraint Type */
false, /* Is Deferrable */
false, /* Is Deferred */
RelationGetRelid(rel), /* relation */
attNos, /* List of attributes in the constraint */
keycount, /* # attributes in the constraint */
InvalidOid, /* not a domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
0,
' ',
' ',
' ',
ccbin, /* Binary form check constraint */
ccsrc); /* Source form check constraint */
pfree(DatumGetPointer(values[Anum_pg_relcheck_rcname - 1]));
pfree(DatumGetPointer(values[Anum_pg_relcheck_rcbin - 1]));
pfree(DatumGetPointer(values[Anum_pg_relcheck_rcsrc - 1]));
heap_freetuple(tuple);
pfree(ccsrc);
}
@@ -1488,6 +1294,7 @@ AddRelationRawConstraints(Relation rel,
ParseState *pstate;
RangeTblEntry *rte;
int numchecks;
int constr_name_ctr = 0;
List *listptr;
Node *expr;
@@ -1549,18 +1356,17 @@ AddRelationRawConstraints(Relation rel,
/* Check name uniqueness, or generate a new name */
if (cdef->name != NULL)
{
int i;
List *listptr2;
ccname = cdef->name;
/* Check against old constraints */
for (i = 0; i < numoldchecks; i++)
{
if (strcmp(oldchecks[i].ccname, ccname) == 0)
elog(ERROR, "Duplicate CHECK constraint name: '%s'",
ccname);
}
/* Check against pre-existing constraints */
if (ConstraintNameIsUsed(RelationGetRelid(rel),
RelationGetNamespace(rel),
ccname))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
ccname, RelationGetRelationName(rel));
/* Check against other new constraints */
/* Needed because we don't do CommandCounterIncrement in loop */
foreach(listptr2, rawConstraints)
{
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
@@ -1577,55 +1383,40 @@ AddRelationRawConstraints(Relation rel,
}
else
{
int i;
int j;
bool success;
List *listptr2;
ccname = (char *) palloc(NAMEDATALEN);
/* Loop until we find a non-conflicting constraint name */
/* What happens if this loops forever? */
j = numchecks + 1;
do
{
success = true;
snprintf(ccname, NAMEDATALEN, "$%d", j);
List *listptr2;
/* Check against old constraints */
for (i = 0; i < numoldchecks; i++)
/*
* Generate a name that does not conflict with pre-existing
* constraints, nor with any auto-generated names so far.
*/
ccname = GenerateConstraintName(RelationGetRelid(rel),
RelationGetNamespace(rel),
&constr_name_ctr);
/*
* Check against other new constraints, in case the user
* has specified a name that looks like an auto-generated
* name.
*/
success = true;
foreach(listptr2, rawConstraints)
{
if (strcmp(oldchecks[i].ccname, ccname) == 0)
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
if (cdef2 == cdef ||
cdef2->contype != CONSTR_CHECK ||
cdef2->raw_expr == NULL ||
cdef2->name == NULL)
continue;
if (strcmp(cdef2->name, ccname) == 0)
{
success = false;
break;
}
}
/*
* Check against other new constraints, if the check
* hasn't already failed
*/
if (success)
{
foreach(listptr2, rawConstraints)
{
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
if (cdef2 == cdef ||
cdef2->contype != CONSTR_CHECK ||
cdef2->raw_expr == NULL ||
cdef2->name == NULL)
continue;
if (strcmp(cdef2->name, ccname) == 0)
{
success = false;
break;
}
}
}
++j;
} while (!success);
}
@@ -1852,157 +1643,74 @@ RemoveAttrDefaults(Relation rel)
heap_close(adrel, RowExclusiveLock);
}
static void
RemoveRelChecks(Relation rel)
{
Relation rcrel;
HeapScanDesc rcscan;
ScanKeyData key;
HeapTuple tup;
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&key, 0, Anum_pg_relcheck_rcrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
rcscan = heap_beginscan(rcrel, SnapshotNow, 1, &key);
while ((tup = heap_getnext(rcscan, ForwardScanDirection)) != NULL)
simple_heap_delete(rcrel, &tup->t_self);
heap_endscan(rcscan);
heap_close(rcrel, RowExclusiveLock);
}
/*
* Removes all CHECK constraints on a relation that match the given name.
* It is the responsibility of the calling function to acquire a lock on
* the relation.
* Returns: The number of CHECK constraints removed.
* Removes all constraints on a relation that match the given name.
*
* It is the responsibility of the calling function to acquire a suitable
* lock on the relation.
*
* Returns: The number of constraints removed.
*/
int
RemoveCheckConstraint(Relation rel, const char *constrName, bool inh)
RemoveRelConstraints(Relation rel, const char *constrName,
DropBehavior behavior)
{
Oid relid;
Relation rcrel;
TupleDesc tupleDesc;
TupleConstr *oldconstr;
int numoldchecks;
int numchecks;
HeapScanDesc rcscan;
ScanKeyData key[2];
HeapTuple rctup;
int rel_deleted = 0;
int all_deleted = 0;
int ndeleted = 0;
Relation conrel;
SysScanDesc conscan;
ScanKeyData key[1];
HeapTuple contup;
/* Find id of the relation */
relid = RelationGetRelid(rel);
/* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
/* Use the index to scan only constraints of the target relation */
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_constraint_conrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
SnapshotNow, 1, key);
/*
* Process child tables and remove constraints of the same name.
* Scan over the result set, removing any matching entries.
*/
if (inh)
while ((contup = systable_getnext(conscan)) != NULL)
{
List *child,
*children;
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
/* This routine is actually in the planner */
children = find_all_inheritors(relid);
/*
* 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)
if (strcmp(NameStr(con->conname), constrName) == 0)
{
Oid childrelid = lfirsti(child);
Relation inhrel;
ObjectAddress conobj;
if (childrelid == relid)
continue;
inhrel = heap_open(childrelid, AccessExclusiveLock);
all_deleted += RemoveCheckConstraint(inhrel, constrName, false);
heap_close(inhrel, NoLock);
conobj.classId = RelationGetRelid(conrel);
conobj.objectId = contup->t_data->t_oid;
conobj.objectSubId = 0;
performDeletion(&conobj, behavior);
ndeleted++;
}
}
/*
* Get number of existing constraints.
*/
tupleDesc = RelationGetDescr(rel);
oldconstr = tupleDesc->constr;
if (oldconstr)
numoldchecks = oldconstr->num_check;
else
numoldchecks = 0;
/* Grab an appropriate lock on the pg_relcheck relation */
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
/*
* Create two scan keys. We need to match on the oid of the table the
* CHECK is in and also we need to match the name of the CHECK
* constraint.
*/
ScanKeyEntryInitialize(&key[0], 0, Anum_pg_relcheck_rcrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
ScanKeyEntryInitialize(&key[1], 0, Anum_pg_relcheck_rcname,
F_NAMEEQ,
PointerGetDatum(constrName));
/* Begin scanning the heap */
rcscan = heap_beginscan(rcrel, SnapshotNow, 2, key);
/*
* Scan over the result set, removing any matching entries. Note that
* this has the side-effect of removing ALL CHECK constraints that
* share the specified constraint name.
*/
while ((rctup = heap_getnext(rcscan, ForwardScanDirection)) != NULL)
{
simple_heap_delete(rcrel, &rctup->t_self);
++rel_deleted;
++all_deleted;
}
/* Clean up after the scan */
heap_endscan(rcscan);
heap_close(rcrel, RowExclusiveLock);
systable_endscan(conscan);
heap_close(conrel, RowExclusiveLock);
if (rel_deleted)
{
/*
* Update the count of constraints in the relation's pg_class tuple.
*/
numchecks = numoldchecks - rel_deleted;
if (numchecks < 0)
elog(ERROR, "check count became negative");
SetRelationNumChecks(rel, numchecks);
}
/* Return the number of tuples deleted, including all children */
return all_deleted;
return ndeleted;
}
static void
RemoveConstraints(Relation rel)
RemoveDefaults(Relation rel)
{
TupleConstr *constr = rel->rd_att->constr;
if (!constr)
return;
if (constr->num_defval > 0)
/*
* We can skip looking at pg_attrdef if there are no defaults recorded
* in the Relation.
*/
if (constr && constr->num_defval > 0)
RemoveAttrDefaults(rel);
if (constr->num_check > 0)
RemoveRelChecks(rel);
}
static void

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.181 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.182 2002/07/12 18:43:13 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -29,14 +29,15 @@
#include "bootstrap/bootstrap.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/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
@@ -535,6 +536,7 @@ index_create(Oid heapRelationId,
Oid accessMethodObjectId,
Oid *classObjectId,
bool primary,
bool isconstraint,
bool allow_system_table_mods)
{
Relation heapRelation;
@@ -543,6 +545,7 @@ index_create(Oid heapRelationId,
bool shared_relation;
Oid namespaceId;
Oid indexoid;
int i;
SetReindexProcessing(false);
@@ -660,7 +663,89 @@ index_create(Oid heapRelationId,
classObjectId, primary);
/*
* fill in the index strategy structure with information from the
* Register constraint and dependencies for the index.
*
* If the index is from a CONSTRAINT clause, construct a pg_constraint
* entry. The index is then linked to the constraint, which in turn is
* linked to the table. If it's not a CONSTRAINT, make the dependency
* directly on the table.
*
* During bootstrap we can't register any dependencies, and we don't
* try to make a constraint either.
*/
if (!IsBootstrapProcessingMode())
{
ObjectAddress myself,
referenced;
myself.classId = RelOid_pg_class;
myself.objectId = indexoid;
myself.objectSubId = 0;
if (isconstraint)
{
char constraintType;
Oid conOid;
if (primary)
constraintType = CONSTRAINT_PRIMARY;
else if (indexInfo->ii_Unique)
constraintType = CONSTRAINT_UNIQUE;
else
{
elog(ERROR, "index_create: constraint must be PRIMARY or UNIQUE");
constraintType = 0; /* keep compiler quiet */
}
conOid = CreateConstraintEntry(indexRelationName,
namespaceId,
constraintType,
false, /* isDeferrable */
false, /* isDeferred */
heapRelationId,
indexInfo->ii_KeyAttrNumbers,
indexInfo->ii_NumIndexAttrs,
InvalidOid, /* no domain */
InvalidOid, /* no foreign key */
NULL,
0,
' ',
' ',
' ',
NULL, /* Constraint Bin & Src */
NULL);
referenced.classId = get_system_catalog_relid(ConstraintRelationName);
referenced.objectId = conOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
else
{
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
referenced.classId = RelOid_pg_class;
referenced.objectId = heapRelationId;
referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i];
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
}
/* Store the dependency on the function (if appropriate) */
if (OidIsValid(indexInfo->ii_FuncOid))
{
referenced.classId = RelOid_pg_proc;
referenced.objectId = indexInfo->ii_FuncOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
}
/*
* Fill in the index strategy structure with information from the
* catalogs. First we must advance the command counter so that we
* will see the newly-entered index catalog tuples.
*/
@@ -691,11 +776,11 @@ index_create(Oid heapRelationId,
return indexoid;
}
/* ----------------------------------------------------------------
*
/*
* index_drop
*
* ----------------------------------------------------------------
* NOTE: this routine should now only be called through performDeletion(),
* else associated dependencies won't be cleaned up.
*/
void
index_drop(Oid indexId)
@@ -730,17 +815,6 @@ index_drop(Oid indexId)
userIndexRelation = index_open(indexId);
LockRelation(userIndexRelation, AccessExclusiveLock);
/*
* Note: unlike heap_drop_with_catalog, we do not need to prevent
* deletion of system indexes here; that's checked for upstream. If we
* did check it here, deletion of TOAST tables would fail...
*/
/*
* fix DESCRIPTION relation
*/
DeleteComments(indexId, RelOid_pg_class);
/*
* fix RELATION relation
*/

View File

@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.95 2002/07/11 07:39:27 ishii Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.96 2002/07/12 18:43:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -45,10 +45,14 @@ char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] =
{AttrDefaultIndex};
char *Name_pg_class_indices[Num_pg_class_indices] =
{ClassNameNspIndex, ClassOidIndex};
char *Name_pg_constraint_indices[Num_pg_constraint_indices] =
{ConstraintNameNspIndex, ConstraintOidIndex, ConstraintRelidIndex};
char *Name_pg_conversion_indices[Num_pg_conversion_indices] =
{ConversionNameNspIndex, ConversionDefaultIndex};
char *Name_pg_database_indices[Num_pg_database_indices] =
{DatabaseNameIndex, DatabaseOidIndex};
char *Name_pg_depend_indices[Num_pg_depend_indices] =
{DependDependerIndex, DependReferenceIndex};
char *Name_pg_group_indices[Num_pg_group_indices] =
{GroupNameIndex, GroupSysidIndex};
char *Name_pg_index_indices[Num_pg_index_indices] =
@@ -67,8 +71,6 @@ char *Name_pg_operator_indices[Num_pg_operator_indices] =
{OperatorOidIndex, OperatorNameNspIndex};
char *Name_pg_proc_indices[Num_pg_proc_indices] =
{ProcedureOidIndex, ProcedureNameNspIndex};
char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] =
{RelCheckIndex};
char *Name_pg_rewrite_indices[Num_pg_rewrite_indices] =
{RewriteOidIndex, RewriteRelRulenameIndex};
char *Name_pg_shadow_indices[Num_pg_shadow_indices] =

View File

@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.23 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.24 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,7 @@
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_inherits.h"
@@ -128,25 +129,10 @@ static Oid mySpecialNamespace = InvalidOid;
char *namespace_search_path = NULL;
/*
* Deletion ordering constraint item.
*/
typedef struct DelConstraint
{
Oid referencer; /* table to delete first */
Oid referencee; /* table to delete second */
int pred; /* workspace for TopoSortRels */
struct DelConstraint *link; /* workspace for TopoSortRels */
} DelConstraint;
/* Local functions */
static void recomputeNamespacePath(void);
static void InitTempTableNamespace(void);
static void RemoveTempRelations(Oid tempNamespaceId);
static List *FindTempRelations(Oid tempNamespaceId);
static List *FindDeletionConstraints(List *relOids);
static List *TopoSortRels(List *relOids, List *constraintList);
static void RemoveTempRelationsCallback(void);
static void NamespaceCallback(Datum arg, Oid relid);
@@ -1531,56 +1517,22 @@ AtEOXact_Namespace(bool isCommit)
static void
RemoveTempRelations(Oid tempNamespaceId)
{
List *tempRelList;
List *constraintList;
List *lptr;
/* Get a list of relations to delete */
tempRelList = FindTempRelations(tempNamespaceId);
if (tempRelList == NIL)
return; /* nothing to do */
/* If more than one, sort them to respect any deletion-order constraints */
if (length(tempRelList) > 1)
{
constraintList = FindDeletionConstraints(tempRelList);
if (constraintList != NIL)
tempRelList = TopoSortRels(tempRelList, constraintList);
}
/* Scan the list and delete all entries */
foreach(lptr, tempRelList)
{
Oid reloid = (Oid) lfirsti(lptr);
heap_drop_with_catalog(reloid, true);
/*
* Advance cmd counter to make catalog changes visible, in case
* a later entry depends on this one.
*/
CommandCounterIncrement();
}
}
/*
* Find all relations in the specified temp namespace.
*
* Returns a list of relation OIDs.
*/
static List *
FindTempRelations(Oid tempNamespaceId)
{
List *tempRelList = NIL;
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
ScanKeyData key;
ObjectAddress object;
/*
* Scan pg_class to find all the relations in the target namespace.
* Ignore indexes, though, on the assumption that they'll go away
* when their tables are deleted.
*
* NOTE: if there are deletion constraints between temp relations,
* then our CASCADE delete call may cause as-yet-unvisited objects
* to go away. This is okay because we are using SnapshotNow; when
* the scan does reach those pg_class tuples, they'll be ignored as
* already deleted.
*/
ScanKeyEntryInitialize(&key, 0x0,
Anum_pg_class_relnamespace,
@@ -1597,7 +1549,10 @@ FindTempRelations(Oid tempNamespaceId)
case RELKIND_RELATION:
case RELKIND_SEQUENCE:
case RELKIND_VIEW:
tempRelList = lconsi(tuple->t_data->t_oid, tempRelList);
object.classId = RelOid_pg_class;
object.objectId = tuple->t_data->t_oid;
object.objectSubId = 0;
performDeletion(&object, DROP_CASCADE);
break;
default:
break;
@@ -1606,164 +1561,6 @@ FindTempRelations(Oid tempNamespaceId)
heap_endscan(scan);
heap_close(pgclass, AccessShareLock);
return tempRelList;
}
/*
* Find deletion-order constraints involving the given relation OIDs.
*
* Returns a list of DelConstraint objects.
*/
static List *
FindDeletionConstraints(List *relOids)
{
List *constraintList = NIL;
Relation inheritsrel;
HeapScanDesc scan;
HeapTuple tuple;
/*
* Scan pg_inherits to find parents and children that are in the list.
*/
inheritsrel = heap_openr(InheritsRelationName, AccessShareLock);
scan = heap_beginscan(inheritsrel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid inhrelid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
Oid inhparent = ((Form_pg_inherits) GETSTRUCT(tuple))->inhparent;
if (intMember(inhrelid, relOids) && intMember(inhparent, relOids))
{
DelConstraint *item;
item = (DelConstraint *) palloc(sizeof(DelConstraint));
item->referencer = inhrelid;
item->referencee = inhparent;
constraintList = lcons(item, constraintList);
}
}
heap_endscan(scan);
heap_close(inheritsrel, AccessShareLock);
return constraintList;
}
/*
* TopoSortRels -- topological sort of a list of rels to delete
*
* This is a lot simpler and slower than, for example, the topological sort
* algorithm shown in Knuth's Volume 1. However, we are not likely to be
* working with more than a few constraints, so the apparent slowness of the
* algorithm won't really matter.
*/
static List *
TopoSortRels(List *relOids, List *constraintList)
{
int queue_size = length(relOids);
Oid *rels;
int *beforeConstraints;
DelConstraint **afterConstraints;
List *resultList = NIL;
List *lptr;
int i,
j,
k,
last;
/* Allocate workspace */
rels = (Oid *) palloc(queue_size * sizeof(Oid));
beforeConstraints = (int *) palloc(queue_size * sizeof(int));
afterConstraints = (DelConstraint **)
palloc(queue_size * sizeof(DelConstraint*));
/* Build an array of the target relation OIDs */
i = 0;
foreach(lptr, relOids)
{
rels[i++] = (Oid) lfirsti(lptr);
}
/*
* Scan the constraints, and for each rel in the array, generate a
* count of the number of constraints that say it must be before
* something else, plus a list of the constraints that say it must be
* after something else. The count for the j'th rel is stored in
* beforeConstraints[j], and the head of its list in
* afterConstraints[j]. Each constraint stores its list link in
* its link field (note any constraint will be in just one list).
* The array index for the before-rel of each constraint is
* remembered in the constraint's pred field.
*/
MemSet(beforeConstraints, 0, queue_size * sizeof(int));
MemSet(afterConstraints, 0, queue_size * sizeof(DelConstraint*));
foreach(lptr, constraintList)
{
DelConstraint *constraint = (DelConstraint *) lfirst(lptr);
Oid rel;
/* Find the referencer rel in the array */
rel = constraint->referencer;
for (j = queue_size; --j >= 0;)
{
if (rels[j] == rel)
break;
}
Assert(j >= 0); /* should have found a match */
/* Find the referencee rel in the array */
rel = constraint->referencee;
for (k = queue_size; --k >= 0;)
{
if (rels[k] == rel)
break;
}
Assert(k >= 0); /* should have found a match */
beforeConstraints[j]++; /* referencer must come before */
/* add this constraint to list of after-constraints for referencee */
constraint->pred = j;
constraint->link = afterConstraints[k];
afterConstraints[k] = constraint;
}
/*--------------------
* Now scan the rels array backwards. At each step, output the
* last rel that has no remaining before-constraints, and decrease
* the beforeConstraints count of each of the rels it was constrained
* against. (This is the right order since we are building the result
* list back-to-front.)
* i = counter for number of rels left to output
* j = search index for rels[]
* dc = temp for scanning constraint list for rel j
* last = last valid index in rels (avoid redundant searches)
*--------------------
*/
last = queue_size - 1;
for (i = queue_size; --i >= 0;)
{
DelConstraint *dc;
/* Find next candidate to output */
while (rels[last] == InvalidOid)
last--;
for (j = last; j >= 0; j--)
{
if (rels[j] != InvalidOid && beforeConstraints[j] == 0)
break;
}
/* If no available candidate, topological sort fails */
if (j < 0)
elog(ERROR, "TopoSortRels: failed to find a workable deletion ordering");
/* Output candidate, and mark it done by zeroing rels[] entry */
resultList = lconsi(rels[j], resultList);
rels[j] = InvalidOid;
/* Update beforeConstraints counts of its predecessors */
for (dc = afterConstraints[j]; dc; dc = dc->link)
beforeConstraints[dc->pred]--;
}
/* Done */
return resultList;
}
/*

View File

@@ -0,0 +1,453 @@
/*-------------------------------------------------------------------------
*
* pg_constraint.c
* routines to support manipulation of the pg_constraint relation
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "access/genam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
/*
* CreateConstraintEntry
* Create a constraint table entry.
*
* Subsidiary records (such as triggers or indexes to implement the
* constraint) are *not* created here. But we do make dependency links
* from the constraint to the things it depends on.
*/
Oid
CreateConstraintEntry(const char *constraintName,
Oid constraintNamespace,
char constraintType,
bool isDeferrable,
bool isDeferred,
Oid relId,
const int16 *constraintKey,
int constraintNKeys,
Oid domainId,
Oid foreignRelId,
const int16 *foreignKey,
int foreignNKeys,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
const char *conBin,
const char *conSrc)
{
Relation conDesc;
Oid conOid;
HeapTuple tup;
char nulls[Natts_pg_constraint];
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
NameData cname;
int i;
ObjectAddress conobject;
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
Assert(constraintName);
namestrcpy(&cname, constraintName);
/*
* Convert C arrays into Postgres arrays.
*/
if (constraintNKeys > 0)
{
Datum *conkey;
conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
for (i = 0; i < constraintNKeys; i++)
conkey[i] = Int16GetDatum(constraintKey[i]);
conkeyArray = construct_array(conkey, constraintNKeys,
true, 2, 's');
}
else
conkeyArray = NULL;
if (foreignNKeys > 0)
{
Datum *confkey;
confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum));
for (i = 0; i < foreignNKeys; i++)
confkey[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(confkey, foreignNKeys,
true, 2, 's');
}
else
confkeyArray = NULL;
/* initialize nulls and values */
for (i = 0; i < Natts_pg_constraint; i++)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
}
values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
else
nulls[Anum_pg_constraint_conkey - 1] = 'n';
if (confkeyArray)
values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
else
nulls[Anum_pg_constraint_confkey - 1] = 'n';
/*
* initialize the binary form of the check constraint.
*/
if (conBin)
values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin,
CStringGetDatum(conBin));
else
nulls[Anum_pg_constraint_conbin - 1] = 'n';
/*
* initialize the text form of the check constraint
*/
if (conSrc)
values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin,
CStringGetDatum(conSrc));
else
nulls[Anum_pg_constraint_consrc - 1] = 'n';
tup = heap_formtuple(RelationGetDescr(conDesc), values, nulls);
conOid = simple_heap_insert(conDesc, tup);
/* Handle Indices */
if (RelationGetForm(conDesc)->relhasindex)
{
Relation idescs[Num_pg_constraint_indices];
CatalogOpenIndices(Num_pg_constraint_indices, Name_pg_constraint_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_constraint_indices, conDesc, tup);
CatalogCloseIndices(Num_pg_constraint_indices, idescs);
}
conobject.classId = RelationGetRelid(conDesc);
conobject.objectId = conOid;
conobject.objectSubId = 0;
heap_close(conDesc, RowExclusiveLock);
if (OidIsValid(relId))
{
/*
* Register auto dependency from constraint to owning relation,
* or to specific column(s) if any are mentioned.
*/
ObjectAddress relobject;
relobject.classId = RelOid_pg_class;
relobject.objectId = relId;
if (constraintNKeys > 0)
{
for (i = 0; i < constraintNKeys; i++)
{
relobject.objectSubId = constraintKey[i];
recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
}
}
else
{
relobject.objectSubId = 0;
recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
}
}
if (OidIsValid(foreignRelId))
{
/*
* Register dependency from constraint to foreign relation,
* or to specific column(s) if any are mentioned.
*
* In normal case of two separate relations, make this a NORMAL
* dependency (so dropping the FK table would require CASCADE).
* However, for a self-reference just make it AUTO.
*/
DependencyType deptype;
ObjectAddress relobject;
deptype = (foreignRelId == relId) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL;
relobject.classId = RelOid_pg_class;
relobject.objectId = foreignRelId;
if (foreignNKeys > 0)
{
for (i = 0; i < foreignNKeys; i++)
{
relobject.objectSubId = foreignKey[i];
recordDependencyOn(&conobject, &relobject, deptype);
}
}
else
{
relobject.objectSubId = 0;
recordDependencyOn(&conobject, &relobject, deptype);
}
}
return conOid;
}
/*
* Test whether given name is currently used as a constraint name
* for the given relation.
*
* NB: Caller should hold exclusive lock on the given relation, else
* this test is not very meaningful.
*/
bool
ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
{
bool found;
Relation conDesc;
SysScanDesc conscan;
ScanKeyData skey[2];
HeapTuple tup;
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
found = false;
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_constraint_conname, F_NAMEEQ,
CStringGetDatum(cname));
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (con->conrelid == relId)
{
found = true;
break;
}
}
systable_endscan(conscan);
heap_close(conDesc, RowExclusiveLock);
return found;
}
/*
* Generate a currently-unused constraint name for the given relation.
*
* The passed counter should be initialized to 0 the first time through.
* If multiple constraint names are to be generated in a single command,
* pass the new counter value to each successive call, else the same
* name will be generated each time.
*
* NB: Caller should hold exclusive lock on the given relation, else
* someone else might choose the same name concurrently!
*/
char *
GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
{
bool found;
Relation conDesc;
char *cname;
cname = (char *) palloc(NAMEDATALEN * sizeof(char));
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
/* Loop until we find a non-conflicting constraint name */
/* We assume there will be one eventually ... */
do
{
SysScanDesc conscan;
ScanKeyData skey[2];
HeapTuple tup;
++(*counter);
snprintf(cname, NAMEDATALEN, "$%d", *counter);
/*
* This duplicates ConstraintNameIsUsed() so that we can avoid
* re-opening pg_constraint for each iteration.
*/
found = false;
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_constraint_conname, F_NAMEEQ,
CStringGetDatum(cname));
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (con->conrelid == relId)
{
found = true;
break;
}
}
systable_endscan(conscan);
} while (found);
heap_close(conDesc, RowExclusiveLock);
return cname;
}
/*
* Does the given name look like a generated constraint name?
*
* This is a test on the form of the name, *not* on whether it has
* actually been assigned.
*/
bool
ConstraintNameIsGenerated(const char *cname)
{
if (cname[0] != '$')
return false;
if (strspn(cname+1, "0123456789") != strlen(cname+1))
return false;
return true;
}
/*
* Delete a single constraint record.
*/
void
RemoveConstraintById(Oid conId)
{
Relation conDesc;
ScanKeyData skey[1];
SysScanDesc conscan;
HeapTuple tup;
Form_pg_constraint con;
conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(conId));
conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
SnapshotNow, 1, skey);
tup = systable_getnext(conscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "RemoveConstraintById: constraint %u not found",
conId);
con = (Form_pg_constraint) GETSTRUCT(tup);
/*
* If the constraint is for a relation, open and exclusive-lock
* the relation it's for.
*
* XXX not clear what we should lock, if anything, for other constraints.
*/
if (OidIsValid(con->conrelid))
{
Relation rel;
rel = heap_open(con->conrelid, AccessExclusiveLock);
/*
* We need to update the relcheck count if it is a check constraint
* being dropped. This update will force backends to rebuild
* relcache entries when we commit.
*/
if (con->contype == CONSTRAINT_CHECK)
{
Relation pgrel;
HeapTuple relTup;
Form_pg_class classForm;
Relation ridescs[Num_pg_class_indices];
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
relTup = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(con->conrelid),
0, 0, 0);
if (!HeapTupleIsValid(relTup))
elog(ERROR, "cache lookup of relation %u failed",
con->conrelid);
classForm = (Form_pg_class) GETSTRUCT(relTup);
if (classForm->relchecks == 0)
elog(ERROR, "RemoveConstraintById: relation %s has relchecks = 0",
RelationGetRelationName(rel));
classForm->relchecks--;
simple_heap_update(pgrel, &relTup->t_self, relTup);
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, relTup);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
heap_freetuple(relTup);
heap_close(pgrel, RowExclusiveLock);
}
/* Keep lock on constraint's rel until end of xact */
heap_close(rel, NoLock);
}
/* Fry the constraint itself */
simple_heap_delete(conDesc, &tup->t_self);
/* Clean up */
systable_endscan(conscan);
heap_close(conDesc, RowExclusiveLock);
}

View File

@@ -0,0 +1,147 @@
/*-------------------------------------------------------------------------
*
* pg_depend.c
* routines to support manipulation of the pg_depend relation
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/dependency.h"
#include "catalog/pg_depend.h"
#include "miscadmin.h"
#include "utils/fmgroids.h"
static bool isObjectPinned(const ObjectAddress *object, Relation rel);
/*
* Record a dependency between 2 objects via their respective objectAddress.
* The first argument is the dependent object, the second the one it
* references.
*
* This simply creates an entry in pg_depend, without any other processing.
*/
void
recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior)
{
Relation dependDesc;
HeapTuple tup;
int i;
char nulls[Natts_pg_depend];
Datum values[Natts_pg_depend];
Relation idescs[Num_pg_depend_indices];
/*
* During bootstrap, do nothing since pg_depend may not exist yet.
* initdb will fill in appropriate pg_depend entries after bootstrap.
*/
if (IsBootstrapProcessingMode())
return;
dependDesc = heap_openr(DependRelationName, RowExclusiveLock);
/*
* If the referenced object is pinned by the system, there's no real
* need to record dependencies on it. This saves lots of space in
* pg_depend, so it's worth the time taken to check.
*/
if (!isObjectPinned(referenced, dependDesc))
{
/*
* Record the Dependency. Note we don't bother to check for
* duplicate dependencies; there's no harm in them.
*/
for (i = 0; i < Natts_pg_depend; ++i)
{
nulls[i] = ' ';
values[i] = (Datum) 0;
}
values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
values[Anum_pg_depend_deptype -1] = CharGetDatum((char) behavior);
tup = heap_formtuple(dependDesc->rd_att, values, nulls);
simple_heap_insert(dependDesc, tup);
/*
* Keep indices current
*/
CatalogOpenIndices(Num_pg_depend_indices, Name_pg_depend_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_depend_indices, dependDesc, tup);
CatalogCloseIndices(Num_pg_depend_indices, idescs);
}
heap_close(dependDesc, RowExclusiveLock);
}
/*
* isObjectPinned()
*
* Test if an object is required for basic database functionality.
* Caller must already have opened pg_depend.
*
* The passed subId, if any, is ignored; we assume that only whole objects
* are pinned (and that this implies pinning their components).
*/
static bool
isObjectPinned(const ObjectAddress *object, Relation rel)
{
bool ret = false;
SysScanDesc scan;
HeapTuple tup;
ScanKeyData key[2];
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_depend_refclassid, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyEntryInitialize(&key[1], 0x0,
Anum_pg_depend_refobjid, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
scan = systable_beginscan(rel, DependReferenceIndex, true,
SnapshotNow, 2, key);
/*
* Since we won't generate additional pg_depend entries for pinned
* objects, there can be at most one entry referencing a pinned
* object. Hence, it's sufficient to look at the first returned
* tuple; we don't need to loop.
*/
tup = systable_getnext(scan);
if (HeapTupleIsValid(tup))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
if (foundDep->deptype == DEPENDENCY_PIN)
ret = true;
}
systable_endscan(scan);
return ret;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.72 2002/06/20 20:29:26 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.73 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -166,6 +167,8 @@ TypeCreate(const char *typeName,
NameData name;
TupleDesc tupDesc;
int i;
ObjectAddress myself,
referenced;
/*
* validate size specifications: either positive (fixed-length) or -1
@@ -298,6 +301,77 @@ TypeCreate(const char *typeName,
CatalogCloseIndices(Num_pg_type_indices, idescs);
}
/*
* Create dependencies
*/
myself.classId = RelOid_pg_type;
myself.objectId = typeObjectId;
myself.objectSubId = 0;
/* Normal dependencies on the I/O functions */
referenced.classId = RelOid_pg_proc;
referenced.objectId = inputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
referenced.classId = RelOid_pg_proc;
referenced.objectId = outputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (receiveProcedure != inputProcedure)
{
referenced.classId = RelOid_pg_proc;
referenced.objectId = receiveProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
if (sendProcedure != outputProcedure)
{
referenced.classId = RelOid_pg_proc;
referenced.objectId = sendProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/*
* If the type is a rowtype for a relation, mark it as internally
* dependent on the relation. This allows it to be auto-dropped
* when the relation is, and not otherwise.
*/
if (OidIsValid(relationOid))
{
referenced.classId = RelOid_pg_class;
referenced.objectId = relationOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
/*
* If the type is an array type, mark it auto-dependent on the
* base type. (This is a compromise between the typical case where the
* array type is automatically generated and the case where it is manually
* created: we'd prefer INTERNAL for the former case and NORMAL for the
* latter.)
*/
if (OidIsValid(elementType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = elementType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
/* Normal dependency from a domain to its base type. */
if (OidIsValid(baseType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = baseType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/*
* finish up
*/

View File

@@ -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);
/*
* Do the deletion
*/
object.classId = RelOid_pg_proc;
object.objectId = procOid;
object.objectSubId = 0;
/* 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);
heap_close(relation, RowExclusiveLock);
performDeletion(&object, stmt->behavior);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,100 +2572,46 @@ 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;
if (!fkconstraint->skip_validation)
validateForeignKeyConstraint(fkconstraint, rel, pkrel);
trig.tgargs = (char **) palloc(
sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
+ length(fkconstraint->pk_attrs)));
/*
* Record the FK constraint in pg_constraint.
*/
constrOid = createForeignKeyConstraint(rel, pkrel,
fkconstraint);
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);
trig.tgargs[count] = fk_at->name;
count += 2;
}
count = 5;
foreach(list, fkconstraint->pk_attrs)
{
Ident *pk_at = lfirst(list);
trig.tgargs[count] = pk_at->name;
count += 2;
}
trig.tgnargs = count - 1;
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
/* Make a call to the check function */
/*
* No parameters are passed, but we do set a
* context
*/
FunctionCallInfoData fcinfo;
TriggerData trigdata;
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* 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;
fcinfo.context = (Node *) &trigdata;
RI_FKey_check_ins(&fcinfo);
}
heap_endscan(scan);
pfree(trig.tgargs);
/*
* Create the triggers that will enforce the constraint.
*/
createForeignKeyTriggers(rel, fkconstraint, constrOid);
/*
* Close pk table, but keep lock until we've committed.
*/
heap_close(pkrel, NoLock);
break;
@@ -2607,12 +2625,418 @@ AlterTableAddConstraint(Oid myrelid,
heap_close(rel, NoLock);
}
/*
* 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);
trig.tgargs[count] = fk_at->name;
count += 2;
}
count = 5;
foreach(list, fkconstraint->pk_attrs)
{
Ident *pk_at = lfirst(list);
trig.tgargs[count] = pk_at->name;
count += 2;
}
trig.tgnargs = count - 1;
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
/*
* Make a call to the trigger function
*
* No parameters are passed, but we do set a context
*/
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* 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;
fcinfo.context = (Node *) &trigdata;
RI_FKey_check_ins(&fcinfo);
}
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);
/*
* 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;
if (childrelid == myrelid)
continue;
inhrel = heap_open(childrelid, AccessExclusiveLock);
/* do NOT count child constraints in deleted. */
RemoveRelConstraints(inhrel, constrName, behavior);
heap_close(inhrel, NoLock);
}
}
/*
* First, we remove all CHECK constraints with the given name
*/
deleted += RemoveCheckConstraint(rel, constrName, inh);
/*
* 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
@@ -2981,6 +3413,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
*/

View File

@@ -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,115 +486,27 @@ 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
*

View File

@@ -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));
/* Remove the type tuple from pg_type */
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
/* Now, delete the "array of" that type */
typename->arrayBounds = makeList1(makeInteger(1));
/*
* Do the deletion
*/
object.classId = RelOid_pg_type;
object.objectId = typeoid;
object.objectSubId = 0;
typeoid = LookupTypeName(typename);
if (!OidIsValid(typeoid))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
performDeletion(&object, behavior);
}
/*
* 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);
}

View File

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

View File

@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.192 2002/07/01 15:27:51 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.193 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1505,11 +1505,12 @@ _copyFkConstraint(FkConstraint *from)
Node_Copy(from, newnode, pktable);
Node_Copy(from, newnode, fk_attrs);
Node_Copy(from, newnode, pk_attrs);
if (from->match_type)
newnode->match_type = pstrdup(from->match_type);
newnode->actions = from->actions;
newnode->fk_matchtype = from->fk_matchtype;
newnode->fk_upd_action = from->fk_upd_action;
newnode->fk_del_action = from->fk_del_action;
newnode->deferrable = from->deferrable;
newnode->initdeferred = from->initdeferred;
newnode->skip_validation = from->skip_validation;
return newnode;
}
@@ -2089,6 +2090,7 @@ _copyIndexStmt(IndexStmt *from)
Node_Copy(from, newnode, rangetable);
newnode->unique = from->unique;
newnode->primary = from->primary;
newnode->isconstraint = from->isconstraint;
return newnode;
}

View File

@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.139 2002/07/01 15:27:52 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.140 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -912,6 +912,8 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
return false;
if (a->primary != b->primary)
return false;
if (a->isconstraint != b->isconstraint)
return false;
return true;
}
@@ -1734,14 +1736,18 @@ _equalFkConstraint(FkConstraint *a, FkConstraint *b)
return false;
if (!equal(a->pk_attrs, b->pk_attrs))
return false;
if (!equalstr(a->match_type, b->match_type))
if (a->fk_matchtype != b->fk_matchtype)
return false;
if (a->actions != b->actions)
if (a->fk_upd_action != b->fk_upd_action)
return false;
if (a->fk_del_action != b->fk_del_action)
return false;
if (a->deferrable != b->deferrable)
return false;
if (a->initdeferred != b->initdeferred)
return false;
if (a->skip_validation != b->skip_validation)
return false;
return true;
}

View File

@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.161 2002/07/04 15:23:53 thomas Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.162 2002/07/12 18:43:16 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -136,9 +136,10 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
_outNode(str, node->whereClause);
appendStringInfo(str, " :rangetable ");
_outNode(str, node->rangetable);
appendStringInfo(str, " :unique %s :primary %s ",
appendStringInfo(str, " :unique %s :primary %s :isconstraint %s ",
booltostr(node->unique),
booltostr(node->primary));
booltostr(node->primary),
booltostr(node->isconstraint));
}
static void
@@ -1447,12 +1448,13 @@ _outFkConstraint(StringInfo str, FkConstraint *node)
_outNode(str, node->fk_attrs);
appendStringInfo(str, " :pk_attrs ");
_outNode(str, node->pk_attrs);
appendStringInfo(str, " :match_type ");
_outToken(str, node->match_type);
appendStringInfo(str, " :actions %d :deferrable %s :initdeferred %s",
node->actions,
appendStringInfo(str, " :fk_matchtype %c :fk_upd_action %c :fk_del_action %c :deferrable %s :initdeferred %s :skip_validation %s",
node->fk_matchtype,
node->fk_upd_action,
node->fk_del_action,
booltostr(node->deferrable),
booltostr(node->initdeferred));
booltostr(node->initdeferred),
booltostr(node->skip_validation));
}
static void

View File

@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.237 2002/06/20 20:29:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.238 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,7 +47,7 @@
/* State shared by transformCreateSchemaStmt and its subroutines */
typedef struct
{
const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
const char *stmtType; /* "CREATE SCHEMA" or "ALTER SCHEMA" */
char *schemaname; /* name of schema */
char *authid; /* owner of schema */
List *tables; /* CREATE TABLE items */
@@ -1066,6 +1066,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
cxt->stmtType, (cxt->relation)->relname);
cxt->pkey = index;
}
index->isconstraint = true;
if (constraint->name != NULL)
index->idxname = pstrdup(constraint->name);
@@ -1304,15 +1305,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
static void
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
{
CreateTrigStmt *fk_trigger;
List *fkactions = NIL;
List *fkclist;
List *fk_attr;
List *pk_attr;
Ident *id;
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
int i;
if (cxt->fkconstraints == NIL)
return;
@@ -1323,15 +1317,12 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
foreach(fkclist, cxt->fkconstraints)
{
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
int i;
int attnum;
List *fkattrs;
/*
* If the constraint has no name, set it to <unnamed>
*/
if (fkconstraint->constr_name == NULL)
fkconstraint->constr_name = "<unnamed>";
for (attnum = 0; attnum < INDEX_MAX_KEYS; attnum++)
pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
@@ -1473,203 +1464,24 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
}
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
* action.
* For ALTER TABLE ADD CONSTRAINT, we're done. For CREATE TABLE,
* gin up an ALTER TABLE ADD CONSTRAINT command to execute after
* the basic CREATE TABLE is complete.
*/
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
fk_trigger->relation = cxt->relation;
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((cxt->relation)->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
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)
if (strcmp(cxt->stmtType, "CREATE TABLE") == 0)
{
id = (Ident *) lfirst(fk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
id = (Ident *) lfirst(pk_attr);
fk_trigger->args = lappend(fk_trigger->args,
makeString(id->name));
alterstmt->subtype = 'c'; /* preprocessed add constraint */
alterstmt->relation = cxt->relation;
alterstmt->name = NULL;
alterstmt->def = (Node *) makeList1(fkconstraint);
fk_attr = lnext(fk_attr);
pk_attr = lnext(pk_attr);
/* Don't need to scan the table contents in this case */
fkconstraint->skip_validation = true;
fkactions = lappend(fkactions, (Node *) alterstmt);
}
fkactions = lappend(fkactions, (Node *) fk_trigger);
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the ON DELETE
* action fired on the PK table !!!
*/
fk_trigger = (CreateTrigStmt *) 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 = cxt->relation;
switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
>> FKCONSTR_ON_DELETE_SHIFT)
{
case FKCONSTR_ON_KEY_NOACTION:
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
break;
case FKCONSTR_ON_KEY_RESTRICT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
break;
case FKCONSTR_ON_KEY_CASCADE:
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
break;
case FKCONSTR_ON_KEY_SETNULL:
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
break;
case FKCONSTR_ON_KEY_SETDEFAULT:
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
break;
default:
elog(ERROR, "Only one ON DELETE action can be specified 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((cxt->relation)->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
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);
}
fkactions = lappend(fkactions, (Node *) fk_trigger);
/*
* Build a CREATE CONSTRAINT TRIGGER statement for the ON UPDATE
* action fired on the PK table !!!
*/
fk_trigger = (CreateTrigStmt *) 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 = cxt->relation;
switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
>> FKCONSTR_ON_UPDATE_SHIFT)
{
case FKCONSTR_ON_KEY_NOACTION:
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
break;
case FKCONSTR_ON_KEY_RESTRICT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
break;
case FKCONSTR_ON_KEY_CASCADE:
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
break;
case FKCONSTR_ON_KEY_SETNULL:
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
break;
case FKCONSTR_ON_KEY_SETDEFAULT:
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
break;
default:
elog(ERROR, "Only one ON UPDATE action can be specified 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((cxt->relation)->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
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);
}
fkactions = lappend(fkactions, (Node *) fk_trigger);
}
/*
@@ -2642,6 +2454,14 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
*extras_after = nconc(cxt.alist, *extras_after);
break;
case 'c':
/*
* Already-transformed ADD CONSTRAINT, so just make it look
* like the standard case.
*/
stmt->subtype = 'C';
break;
default:
break;
}

View File

@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.338 2002/07/11 07:39:25 ishii Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.339 2002/07/12 18:43:17 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -309,8 +309,7 @@ static void doNegateFloat(Value *v);
%type <node> TableConstraint, TableLikeClause
%type <list> ColQualList
%type <node> ColConstraint, ColConstraintElem, ConstraintAttr
%type <ival> key_actions, key_delete, key_update, key_reference
%type <str> key_match
%type <ival> key_actions, key_delete, key_match, key_update, key_action
%type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
ConstraintTimeSpec
@@ -1594,8 +1593,9 @@ ColConstraintElem:
n->pktable = $2;
n->fk_attrs = NIL;
n->pk_attrs = $3;
n->match_type = $4;
n->actions = $5;
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
n->deferrable = FALSE;
n->initdeferred = FALSE;
$$ = (Node *)n;
@@ -1714,16 +1714,16 @@ ConstraintElem:
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
opt_column_list
key_match key_actions ConstraintAttributeSpec
opt_column_list key_match key_actions ConstraintAttributeSpec
{
FkConstraint *n = makeNode(FkConstraint);
n->constr_name = NULL;
n->pktable = $7;
n->fk_attrs = $4;
n->pk_attrs = $8;
n->match_type = $9;
n->actions = $10;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
n->fk_del_action = (char) ($10 & 0xFF);
n->deferrable = ($11 & 1) != 0;
n->initdeferred = ($11 & 2) != 0;
$$ = (Node *)n;
@@ -1750,45 +1750,54 @@ columnElem: ColId
key_match: MATCH FULL
{
$$ = "FULL";
$$ = FKCONSTR_MATCH_FULL;
}
| MATCH PARTIAL
{
elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented");
$$ = "PARTIAL";
$$ = FKCONSTR_MATCH_PARTIAL;
}
| MATCH SIMPLE
{
$$ = "UNSPECIFIED";
$$ = FKCONSTR_MATCH_UNSPECIFIED;
}
| /*EMPTY*/
{
$$ = "UNSPECIFIED";
$$ = FKCONSTR_MATCH_UNSPECIFIED;
}
;
/*
* We combine the update and delete actions into one value temporarily
* for simplicity of parsing, and then break them down again in the
* calling production. update is in the left 8 bits, delete in the right.
* Note that NOACTION is the default.
*/
key_actions:
key_delete { $$ = $1; }
| key_update { $$ = $1; }
| key_delete key_update { $$ = $1 | $2; }
| key_update key_delete { $$ = $1 | $2; }
| /*EMPTY*/ { $$ = 0; }
key_update
{ $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
| key_delete
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); }
| key_update key_delete
{ $$ = ($1 << 8) | ($2 & 0xFF); }
| key_delete key_update
{ $$ = ($2 << 8) | ($1 & 0xFF); }
| /*EMPTY*/
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
;
key_delete: ON DELETE_P key_reference
{ $$ = $3 << FKCONSTR_ON_DELETE_SHIFT; }
key_update: ON UPDATE key_action { $$ = $3; }
;
key_update: ON UPDATE key_reference
{ $$ = $3 << FKCONSTR_ON_UPDATE_SHIFT; }
key_delete: ON DELETE_P key_action { $$ = $3; }
;
key_reference:
NO ACTION { $$ = FKCONSTR_ON_KEY_NOACTION; }
| RESTRICT { $$ = FKCONSTR_ON_KEY_RESTRICT; }
| CASCADE { $$ = FKCONSTR_ON_KEY_CASCADE; }
| SET NULL_P { $$ = FKCONSTR_ON_KEY_SETNULL; }
| SET DEFAULT { $$ = FKCONSTR_ON_KEY_SETDEFAULT; }
key_action:
NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; }
| RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; }
| CASCADE { $$ = FKCONSTR_ACTION_CASCADE; }
| SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; }
| SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; }
;
OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
@@ -2300,7 +2309,7 @@ drop_type: TABLE { $$ = DROP_TABLE; }
| INDEX { $$ = DROP_INDEX; }
| TYPE_P { $$ = DROP_TYPE; }
| DOMAIN_P { $$ = DROP_DOMAIN; }
| CONVERSION_P { $$ = DROP_CONVERSION; }
| CONVERSION_P { $$ = DROP_CONVERSION; }
;
any_name_list:
@@ -6673,6 +6682,7 @@ unreserved_keyword:
| COMMIT
| COMMITTED
| CONSTRAINTS
| CONVERSION_P
| COPY
| CREATEDB
| CREATEUSER
@@ -6857,6 +6867,7 @@ col_name_keyword:
| SUBSTRING
| TIME
| TIMESTAMP
| TREAT
| TRIM
| VARCHAR
;
@@ -6963,7 +6974,6 @@ reserved_keyword:
| THEN
| TO
| TRAILING
| TREAT
| TRUE_P
| UNION
| UNIQUE

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.73 2002/06/20 20:29:33 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.74 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_rewrite.h"
#include "commands/view.h"
@@ -57,6 +58,8 @@ InsertRule(char *rulname,
TupleDesc tupDesc;
HeapTuple tup;
Oid rewriteObjectId;
ObjectAddress myself,
referenced;
if (IsDefinedRewriteRule(eventrel_oid, rulname))
elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
@@ -103,6 +106,23 @@ InsertRule(char *rulname,
heap_freetuple(tup);
/*
* Install dependency on rule's relation to ensure it will go away
* on relation deletion. If the rule is ON SELECT, make the dependency
* implicit --- this prevents deleting a view's SELECT rule. Other
* kinds of rules can be AUTO.
*/
myself.classId = RelationGetRelid(pg_rewrite_desc);
myself.objectId = rewriteObjectId;
myself.objectSubId = 0;
referenced.classId = RelOid_pg_class;
referenced.objectId = eventrel_oid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced,
(evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
heap_close(pg_rewrite_desc, RowExclusiveLock);
return rewriteObjectId;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.50 2002/06/20 20:29:34 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.51 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,14 +17,15 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_rewrite.h"
#include "commands/comment.h"
#include "miscadmin.h"
#include "rewrite/rewriteRemove.h"
#include "rewrite/rewriteSupport.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -34,15 +35,62 @@
* Delete a rule given its name.
*/
void
RemoveRewriteRule(Oid owningRel, const char *ruleName)
RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior)
{
HeapTuple tuple;
Oid eventRelationOid;
AclResult aclresult;
ObjectAddress object;
/*
* Find the tuple for the target rule.
*/
tuple = SearchSysCache(RULERELNAME,
ObjectIdGetDatum(owningRel),
PointerGetDatum(ruleName),
0, 0);
/*
* complain if no rule with such name exists
*/
if (!HeapTupleIsValid(tuple))
elog(ERROR, "Rule \"%s\" not found", ruleName);
/*
* Verify user has appropriate permissions.
*/
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
Assert(eventRelationOid == owningRel);
aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, get_rel_name(eventRelationOid));
/*
* Do the deletion
*/
object.classId = get_system_catalog_relid(RewriteRelationName);
object.objectId = tuple->t_data->t_oid;
object.objectSubId = 0;
ReleaseSysCache(tuple);
performDeletion(&object, behavior);
}
/*
* Guts of rule deletion.
*/
void
RemoveRewriteRuleById(Oid ruleOid)
{
Relation RewriteRelation;
ScanKeyData skey[1];
SysScanDesc rcscan;
Relation event_relation;
HeapTuple tuple;
Oid ruleId;
Oid eventRelationOid;
bool hasMoreRules;
AclResult aclresult;
/*
* Open the pg_rewrite relation.
@@ -52,24 +100,18 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
/*
* Find the tuple for the target rule.
*/
tuple = SearchSysCacheCopy(RULERELNAME,
ObjectIdGetDatum(owningRel),
PointerGetDatum(ruleName),
0, 0);
ScanKeyEntryInitialize(&skey[0], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(ruleOid));
rcscan = systable_beginscan(RewriteRelation, RewriteOidIndex, true,
SnapshotNow, 1, skey);
tuple = systable_getnext(rcscan);
/*
* complain if no rule with such name existed
*/
if (!HeapTupleIsValid(tuple))
elog(ERROR, "Rule \"%s\" not found", ruleName);
/*
* Save the OID of the rule (i.e. the tuple's OID) and the event
* relation's OID
*/
ruleId = tuple->t_data->t_oid;
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
Assert(eventRelationOid == owningRel);
elog(ERROR, "RemoveRewriteRuleById: Rule %u does not exist",
ruleOid);
/*
* We had better grab AccessExclusiveLock so that we know no other
@@ -77,34 +119,18 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
* cannot set relhasrules correctly. Besides, we don't want to be
* changing the ruleset while queries are executing on the rel.
*/
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
/*
* Verify user has appropriate permissions.
*/
aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, RelationGetRelationName(event_relation));
/* do not allow the removal of a view's SELECT rule */
if (event_relation->rd_rel->relkind == RELKIND_VIEW &&
((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1')
elog(ERROR, "Cannot remove a view's SELECT rule");
hasMoreRules = event_relation->rd_rules != NULL &&
event_relation->rd_rules->numLocks > 1;
/*
* Delete any comments associated with this rule
*/
DeleteComments(ruleId, RelationGetRelid(RewriteRelation));
/*
* Now delete the pg_rewrite tuple for the rule
*/
simple_heap_delete(RewriteRelation, &tuple->t_self);
heap_freetuple(tuple);
systable_endscan(rcscan);
heap_close(RewriteRelation, RowExclusiveLock);
@@ -120,49 +146,3 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);
}
/*
* RelationRemoveRules -
* removes all rules associated with the relation when the relation is
* being removed.
*/
void
RelationRemoveRules(Oid relid)
{
Relation RewriteRelation;
SysScanDesc scanDesc;
ScanKeyData scanKeyData;
HeapTuple tuple;
/*
* Open the pg_rewrite relation.
*/
RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock);
/*
* Scan pg_rewrite for all the tuples that have the same ev_class
* as relid (the relation to be removed).
*/
ScanKeyEntryInitialize(&scanKeyData,
0,
Anum_pg_rewrite_ev_class,
F_OIDEQ,
ObjectIdGetDatum(relid));
scanDesc = systable_beginscan(RewriteRelation,
RewriteRelRulenameIndex,
true, SnapshotNow,
1, &scanKeyData);
while (HeapTupleIsValid(tuple = systable_getnext(scanDesc)))
{
/* Delete any comments associated with this rule */
DeleteComments(tuple->t_data->t_oid, RelationGetRelid(RewriteRelation));
simple_heap_delete(RewriteRelation, &tuple->t_self);
}
systable_endscan(scanDesc);
heap_close(RewriteRelation, RowExclusiveLock);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.52 2002/06/20 20:29:34 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.53 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,6 +18,7 @@
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "rewrite/rewriteSupport.h"
#include "utils/inval.h"
#include "utils/syscache.h"
@@ -44,9 +45,8 @@ IsDefinedRewriteRule(Oid owningRel, const char *ruleName)
* NOTE: an important side-effect of this operation is that an SI invalidation
* message is sent out to all backends --- including me --- causing relcache
* entries to be flushed or updated with the new set of rules for the table.
* Therefore, we execute the update even if relhasrules has the right value
* already. Possible future improvement: skip the disk update and just send
* an SI message in that case.
* This must happen even if we find that no change is needed in the pg_class
* row.
*/
void
SetRelationRuleStatus(Oid relationId, bool relHasRules,
@@ -54,6 +54,7 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
{
Relation relationRelation;
HeapTuple tuple;
Form_pg_class classForm;
Relation idescs[Num_pg_class_indices];
/*
@@ -66,18 +67,28 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "SetRelationRuleStatus: cache lookup failed for relation %u", relationId);
classForm = (Form_pg_class) GETSTRUCT(tuple);
/* Do the update */
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules;
if (relIsBecomingView)
((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
if (classForm->relhasrules != relHasRules ||
(relIsBecomingView && classForm->relkind != RELKIND_VIEW))
{
/* Do the update */
classForm->relhasrules = relHasRules;
if (relIsBecomingView)
classForm->relkind = RELKIND_VIEW;
simple_heap_update(relationRelation, &tuple->t_self, tuple);
simple_heap_update(relationRelation, &tuple->t_self, tuple);
/* Keep the catalog indices up to date */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
CatalogCloseIndices(Num_pg_class_indices, idescs);
/* Keep the catalog indices up to date */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
CatalogCloseIndices(Num_pg_class_indices, idescs);
}
else
{
/* no need to change tuple, but force relcache rebuild anyway */
CacheInvalidateRelcache(relationId);
}
heap_freetuple(tuple);
heap_close(relationRelation, RowExclusiveLock);

View File

@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.161 2002/07/11 07:39:26 ishii Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.162 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -582,6 +582,7 @@ ProcessUtility(Node *parsetree,
stmt->indexParams, /* parameters */
stmt->unique,
stmt->primary,
stmt->isconstraint,
(Expr *) stmt->whereClause,
stmt->rangetable);
}
@@ -596,19 +597,11 @@ ProcessUtility(Node *parsetree,
break;
case T_RemoveAggrStmt:
{
RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
RemoveAggregate(stmt->aggname, stmt->aggtype);
}
RemoveAggregate((RemoveAggrStmt *) parsetree);
break;
case T_RemoveFuncStmt:
{
RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
RemoveFunction(stmt->funcname, stmt->args);
}
RemoveFunction((RemoveFuncStmt *) parsetree);
break;
case T_RemoveOperStmt:
@@ -719,7 +712,7 @@ ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
CreateTrigger((CreateTrigStmt *) parsetree);
CreateTrigger((CreateTrigStmt *) parsetree, false);
break;
case T_DropPropertyStmt:
@@ -733,11 +726,13 @@ ProcessUtility(Node *parsetree,
{
case DROP_RULE:
/* RemoveRewriteRule checks permissions */
RemoveRewriteRule(relId, stmt->property);
RemoveRewriteRule(relId, stmt->property,
stmt->behavior);
break;
case DROP_TRIGGER:
/* DropTrigger checks permissions */
DropTrigger(relId, stmt->property);
DropTrigger(relId, stmt->property,
stmt->behavior);
break;
}
}

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.75 2002/07/06 20:16:36 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.76 2002/07/12 18:43:18 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -671,6 +671,25 @@ get_relname_relid(const char *relname, Oid relnamespace)
0, 0);
}
/*
* get_system_catalog_relid
* Get the OID of a system catalog identified by name.
*/
Oid
get_system_catalog_relid(const char *catname)
{
Oid relid;
relid = GetSysCacheOid(RELNAMENSP,
PointerGetDatum(catname),
ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
0, 0);
if (!OidIsValid(relid))
elog(ERROR, "get_system_catalog_relid: cannot find %s", catname);
return relid;
}
#ifdef NOT_USED
/*
* get_relnatts
@@ -1060,7 +1079,7 @@ getBaseType(Oid typid)
/*
* getBaseTypeTypeMod
* If the given type is a domain, return its base type;
* otherwise return the type's own OID.
* otherwise return the type's own OID. Also return base typmod.
*/
Oid
getBaseTypeTypeMod(Oid typid, int32 *typmod)
@@ -1077,7 +1096,7 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
ObjectIdGetDatum(typid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "getBaseType: failed to lookup type %u", typid);
elog(ERROR, "getBaseTypeTypeMod: failed to lookup type %u", typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
if (typTup->typtype != 'd')
{

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.165 2002/06/20 20:29:39 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.166 2002/07/12 18:43:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,11 +43,11 @@
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_relcheck.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
@@ -296,7 +296,7 @@ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
Relation oldrelation);
static void AttrDefaultFetch(Relation relation);
static void RelCheckFetch(Relation relation);
static void CheckConstraintFetch(Relation relation);
static List *insert_ordered_oid(List *list, Oid datum);
static void IndexSupportInitialize(Form_pg_index iform,
IndexStrategy indexStrategy,
@@ -451,7 +451,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
* RelationBuildTupleDesc
*
* Form the relation's tuple descriptor from information in
* the pg_attribute, pg_attrdef & pg_relcheck system catalogs.
* the pg_attribute, pg_attrdef & pg_constraint system catalogs.
*/
static void
RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
@@ -603,7 +603,7 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
MemoryContextAlloc(CacheMemoryContext,
constr->num_check * sizeof(ConstrCheck));
MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
RelCheckFetch(relation);
CheckConstraintFetch(relation);
}
else
constr->num_check = 0;
@@ -2483,62 +2483,60 @@ AttrDefaultFetch(Relation relation)
}
static void
RelCheckFetch(Relation relation)
CheckConstraintFetch(Relation relation)
{
ConstrCheck *check = relation->rd_att->constr->check;
int ncheck = relation->rd_att->constr->num_check;
Relation rcrel;
SysScanDesc rcscan;
ScanKeyData skey;
Relation conrel;
SysScanDesc conscan;
ScanKeyData skey[1];
HeapTuple htup;
Name rcname;
Datum val;
bool isnull;
int found;
int found = 0;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) Anum_pg_relcheck_rcrelid,
(RegProcedure) F_OIDEQ,
ScanKeyEntryInitialize(&skey[0], 0x0,
Anum_pg_constraint_conrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
rcrel = heap_openr(RelCheckRelationName, AccessShareLock);
rcscan = systable_beginscan(rcrel, RelCheckIndex, true,
SnapshotNow,
1, &skey);
found = 0;
conrel = heap_openr(ConstraintRelationName, AccessShareLock);
conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
SnapshotNow, 1, skey);
while (HeapTupleIsValid(htup = systable_getnext(rcscan)))
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
{
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
/* We want check constraints only */
if (conform->contype != CONSTRAINT_CHECK)
continue;
if (found == ncheck)
elog(ERROR, "RelCheckFetch: unexpected record found for rel %s",
elog(ERROR, "CheckConstraintFetch: unexpected record found for rel %s",
RelationGetRelationName(relation));
rcname = (Name) fastgetattr(htup,
Anum_pg_relcheck_rcname,
rcrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "RelCheckFetch: rcname IS NULL for rel %s",
RelationGetRelationName(relation));
check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
NameStr(*rcname));
NameStr(conform->conname));
/* Grab and test conbin is actually set */
val = fastgetattr(htup,
Anum_pg_relcheck_rcbin,
rcrel->rd_att, &isnull);
Anum_pg_constraint_conbin,
conrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
elog(ERROR, "CheckConstraintFetch: conbin IS NULL for rel %s",
RelationGetRelationName(relation));
check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
DatumGetCString(DirectFunctionCall1(textout,
val)));
found++;
}
systable_endscan(rcscan);
heap_close(rcrel, AccessShareLock);
systable_endscan(conscan);
heap_close(conrel, AccessShareLock);
if (found != ncheck)
elog(ERROR, "RelCheckFetch: %d record(s) not found for rel %s",
elog(ERROR, "CheckConstraintFetch: %d record(s) not found for rel %s",
ncheck - found, RelationGetRelationName(relation));
}