Re: Dependency / Constraint patch - Mailing list pgsql-patches
From | Rod Taylor |
---|---|
Subject | Re: Dependency / Constraint patch |
Date | |
Msg-id | 006e01c214a0$b9fd8e00$fe01a8c0@jester Whole thread Raw |
In response to | Dependency / Constraint patch (Rod Taylor <rbt@zort.ca>) |
Responses |
Re: Dependency / Constraint patch
|
List | pgsql-patches |
All that, and I forgot the actual patch. Attached. -- Rod ----- Original Message ----- From: "Rod Taylor" <rbt@zort.ca> To: <pgsql-patches@postgresql.org> Sent: Saturday, June 15, 2002 3:10 PM Subject: [PATCHES] Dependency / Constraint patch > Differences from previous version: > - Fully functional ALTER TABLE / DROP CONSTRAINT > - pg_dump uses ALTER TABLE / ADD FOREIGN KEY > - psql displays foreign keys (\d output) > - Foreign key triggers are autonamed based on the constraint name > - Namespace dependencies were quickly added. Unable to test them very > well (DROP SCHEMA required) > > > Postgresql TODO items completed (or very close): > # Add ALTER TABLE DROP non-CHECK CONSTRAINT > # Allow psql \d to show foreign keys > * Add pg_depend table for dependency recording; use sysrelid, oid, > depend_sysrelid, depend_oid, name > # Auto-destroy sequence on DROP of table with SERIAL > # Prevent column dropping if column is used by foreign key > # Automatically drop constraints/functions when object is dropped > # Make constraints clearer in dump file > # Make foreign keys easier to identify > > > > The locking of relations may not be as strong as it should be. I was > unable to cause failure -- and can't see what it would be missing but it > has been bothering me. > > I've not touched pg_dump for SERIAL stuff. I may do it later. > > Basic documentation updates included. I'll scour for examples or notes > which may no longer apply in a couple of weeks. > > > Attached files: > > TODO.depend - A short list of items I completed, notes, any assumptions, > and possible outstanding items > > src/backend/catalog/pg_constraint.c > src/backend/catalog/pg_depend.c > src/include/catalog/pg_constraint.h > src/include/catalog/pg_depend.h > src/test/regress/expected/drop.out > > Remove: > src/backend/catalog/pg_relcheck.c > src/include/catalog/pg_relcheck.h > > > ---------------------------------------------------------------------- ---------- > > CHANGES > ------- > initdb process has been changed in an attempt to automatically pin some basic types. > pg_type and pg_proc currently. > > pg_relcheck replaced with more generic pg_constraint. > > Nearly all objects have an enforced RESTRICT / CASCADE except for in 'compiled' expressions. > Ie. Views, function contents, default expressions, > > > > [-] completed > [*] yet to do > - Create Type > - Create View > * Create View (on precompile set deps in source) > - Create Trigger > - Create Table (Columns on Types) > * Create Table / Column Defaults (on precompile set deps in source) > - Create Sequence (currval, nextval, setval are PINNED) > - Create Rule (always cascade) > - Create Operator > - Create Language > - Create Index > - Create Function > * Create Function (on precompile set additional deps in source) > - Create Aggregate > > - Drop Type (regress tested) > - Drop View > - Drop Trigger > - Drop Table > - Drop Sequence > - Drop Rule > - Drop Operator > - Drop Language > - Drop Index > - Drop Function (regress tested) > - Drop Aggregate > > - Alter Table / Primary Key > - Alter Table / unique index > * Alter Table / Default (Compiled default depends on functions / types within) > - Alter Table / Add Column > - Alter table / add column which creates toast table > - Alter Table / Drop Constraint (Fixed to function as expected) > > > - Drop pg_relcheck > - Create pg_constraint > > - Insert check constraints into pg_constraint > > - Insert unique constraints into pg_constraint > - Have unique key indicies depend on pg_constraint (implicit cascade) > - Insert primary key constraints into pg_constraint > - Have primary key indicies depend on pg_constraint (implicit cascade) > - Insert foreign key constraints into pg_constraint > - Have foreign key triggers depend on pg_constraint > - Have pg_constraint depend on the table columns (not the table itself) > - heap.c - Drop RemoveRelChecks() > - pg_constraint - ConstraintCreate dependencies > > - Base type dependency on array type managed by pg_depend (always cascaded) > - Table drop foreign key triggers as managed by pg_depend (always cascaded) > - Toast tables depend on relation (always cascade) > - Enable opt_behaviour for most items in gram.y > - Disallow base functionality (types, procedures, and catalogs required for operation) to be dropped ever > - Implicit drop of a SERIALs sequence (regress tested) > > - Alter Table / Drop Constraint (tablecmds->AlterTableDropConstraint) > > - Enable psql to view check and foreign key constraints (\d) on table definition > - Have pg_dump use ALTER TABLE commands for Foreign Keys > - Move Foreign Key constraint trigger creation to constraintCreate() from analyze.c. > - Name triggers after the constraints -- but append a number and guarentee uniqueness > > > OTHER NOTES > ----------- > > CREATE TABLE tab (col1 int4 DEFAULT nextval('seq')); > - DROP FUNCTION nextval(text) CASCADE; > - Drop the column (col1) or set the default to NULL? > > Do objects depend on users (ownership)? ie. DROP USER CASCADE to dump everything they own when they're removed? > > Unique constraints for indicies have unique names across BOTH pg_constraint and pg_index. pg_constraint > is unique to relid and index name so it's not that bad. > > One can drop a unique index without the constraint being dropped (DOH!). Attempts to fix cause circular dependency. > > > ALTER TABLE DROP COLUMN should allow foreign key relations to only drop the foreign column, not the whole relation. > > CASCADEd drops should occur regardless of ownership of objects other than the one specifically specified (parent of cascade) > > Change foreign keys to work through a set of constraint functions using definitions from pg_constraint rather than doing work in the parser. > ---------------------------------------------------------------------- ---------- > /*-------------------------------------------------------------------- ----- > * > * pg_constraint.c > * routines to support manipulation of the pg_namespace relation > * > * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group > * Portions Copyright (c) 1994, Regents of the University of California > * > * > * IDENTIFICATION > * $Header$ > * > *--------------------------------------------------------------------- ---- > */ > #include "postgres.h" > > #include "access/heapam.h" > #include "access/genam.h" > #include "catalog/catname.h" > #include "catalog/indexing.h" > #include "catalog/pg_constraint.h" > #include "catalog/pg_namespace.h" > #include "catalog/pg_depend.h" > #include "commands/trigger.h" > #include "nodes/makefuncs.h" > #include "nodes/parsenodes.h" > #include "parser/gramparse.h" > #include "utils/array.h" > #include "utils/builtins.h" > #include "utils/fmgroids.h" > #include "utils/lsyscache.h" > #include "utils/syscache.h" > > /* > * ConstraintCreate > * Create the constraint table portion as well as any dependencies. > */ > Oid > constraintCreate(Oid relId, > const char *constraintName, > char constraintType, > bool isDeferrable, > bool isDeferred, > const AttrNumber *constraintKey, > int constraintNKeys, > Oid foreignRelId, > const AttrNumber *foreignKey, > int foreignNKeys, > char foreignUpdateType, > char foreignDeleteType, > char foreignMatchType, > char *conBin, > char *conSrc) > { > Relation conDesc; > HeapTuple tup; > char nulls[Natts_pg_constraint]; > Datum values[Natts_pg_constraint]; > int i = 0; > Oid conOid; > Datum conkey[constraintNKeys]; > Datum confkey[foreignNKeys]; > SysScanDesc rcscan; > ScanKeyData skey[2]; > ObjectAddress myself, > dependee; > NameData cname; > > conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock); > > /* sanity checks */ > if (!constraintName) > { > namestrcpy(&cname, getConstraintName(relId)); > } > else > { > namestrcpy(&cname, constraintName); > > /* make sure there is no existing constraints of the same name */ > ScanKeyEntryInitialize(&skey[i++], > 0, Anum_pg_constraint_conrelid, F_OIDEQ, > ObjectIdGetDatum(relId)); > > ScanKeyEntryInitialize(&skey[i++], > 0, Anum_pg_constraint_conname, F_NAMEEQ, > NameGetDatum(&cname)); > > rcscan = systable_beginscan(conDesc, ConstraintRelidNameIndex, true, > SnapshotNow, > i, skey); > > tup = systable_getnext(rcscan); > if (HeapTupleIsValid(tup)) > elog(ERROR, "constraint \"%s\" already exists", NameStr(cname)); > > systable_endscan(rcscan); > } > > > /* Build Datum array for ConstraintKey */ > for (i = 0; i < constraintNKeys; i++) > conkey[i] = Int16GetDatum(constraintKey[i]); > > /* > * Build Datum array for foreignKey. Use a > * placeholder entry if otherwise NULL. > */ > if (foreignNKeys && foreignNKeys > 0) > for (i = 0; i < foreignNKeys; i++) > confkey[i] = Int16GetDatum(foreignKey[i]); > else > confkey[0] = Int16GetDatum(0); > > > /* initialize nulls and values */ > for (i = 0; i < Natts_pg_constraint; i++) > { > nulls[i] = ' '; > values[i] = (Datum) NULL; > } > > values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId); > values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname); > 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_conkey - 1] = PointerGetDatum(construct_array(conkey, > constraintNKeys, > true, 2, 'i')); > > /* Record what we were given, or a placeholder if NULL */ > if (foreignRelId) > values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId); > else > values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(InvalidOid); > > /* Record what we were given, or a placeholder if NULL */ > values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(construct_array(confkey, > foreignNKeys > 0 ? foreignNKeys : 1, > true, 2, 'i')); > > /* Record what we were given, or a placeholder if NULL */ > if (foreignUpdateType) > values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType); > else > values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(CONSTRAINT_FKEY_RESTRICT); > > /* Record what we were given, or a placeholder if NULL */ > if (foreignDeleteType) > values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType); > else > values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(CONSTRAINT_FKEY_RESTRICT); > > /* Record what we were given, or a placeholder if NULL */ > if (foreignMatchType) > values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType); > else > values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(CONSTRAINT_FKEY_FULL); > > /* > * 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); > if (!HeapTupleIsValid(tup)) > elog(ERROR, "ConstraintCreate: heap_formtuple failed"); > > conOid = simple_heap_insert(conDesc, tup); > if (!OidIsValid(conOid)) > elog(ERROR, "ConstraintCreate: heap_insert failed"); > > /* Handle Indicies */ > 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); > } > > /* > * Handle Dependencies > */ > myself.classId = RelationGetRelid(conDesc); > myself.objectId = conOid; > myself.objectSubId = 0; > > /* The constraint depends on the relation */ > dependee.classId = RelOid_pg_class; > dependee.objectId = relId; > dependee.objectSubId = 0; > dependCreate(&myself, &dependee, true); > > > /* > * The constraint depends on the foreign relation columns > * > * Relation dependencies are skipped if we depend > * directly on ourselves > */ > if (foreignNKeys && foreignNKeys > 0 > && relId != foreignRelId) > { > Assert(foreignNKeys = constraintNKeys); > > for (i = 0; i < foreignNKeys; i++) > { > ObjectAddress depender; > depender.classId = RelOid_pg_class; > depender.objectId = relId; > depender.objectSubId = conkey[i]; > > dependee.classId = RelOid_pg_class; > dependee.objectId = foreignRelId; > dependee.objectSubId = foreignKey[i]; > dependCreate(&depender, &dependee, false); > > dependCreate(&myself, &dependee, true); > } > } > > /* > * Create the required triggers to enforce the requested > * foreign key constraint. Record dependencies of the > * trigger to the FK Constraint. > */ > if (foreignNKeys && foreignNKeys > 0) > { > CreateTrigStmt *fk_trigger; > Oid trigId; > RangeVar *foreignRel; > RangeVar *localRel; > char *foreignNameSpace; > char *localNameSpace; > char *foreignRelation; > char *localRelation; > char *mType = "UNSPECIFIED"; > > /* Pull relation names */ > foreignNameSpace = get_namespace_name(get_rel_namespace(foreignRelId)); > localNameSpace = get_namespace_name(get_rel_namespace(relId)); > > foreignRelation = get_rel_name(foreignRelId); > localRelation = get_rel_name(relId); > > localRel = makeRangeVar(localNameSpace, localRelation); > foreignRel = makeRangeVar(foreignNameSpace, foreignRelation); > > /* Find the trigger name for match types */ > switch (foreignMatchType) > { > case CONSTRAINT_FKEY_FULL: > mType = "FULL"; > break; > case CONSTRAINT_FKEY_PARTIAL: > mType = "PARTIAL"; > break; > case CONSTRAINT_FKEY_UNSPECIFIED: > mType = "UNSPECIFIED"; > break; > default: > elog(ERROR, "constraintCreate: Unknown MATCH TYPE"); > } > > /* Double check keys align */ > Assert(foreignNKeys = constraintNKeys); > > /* > * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK > * action. > */ > fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt); > fk_trigger->trigname = getTriggerName(relId, NameStr(cname)); > fk_trigger->relation = localRel; > 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 = isDeferrable; > fk_trigger->initdeferred = isDeferred; > fk_trigger->constrrel = foreignRel; > > fk_trigger->args = NIL; > fk_trigger->args = lappend(fk_trigger->args, > makeString(NameStr(cname))); > fk_trigger->args = lappend(fk_trigger->args, > makeString(localRel->relname)); > fk_trigger->args = lappend(fk_trigger->args, > makeString(foreignRel->relname)); > fk_trigger->args = lappend(fk_trigger->args, > makeString(mType)); > for (i = 0; i < foreignNKeys; i++) > { > fk_trigger->args = lappend(fk_trigger->args, > makeString(get_attname(relId, constraintKey[i]))); > fk_trigger->args = lappend(fk_trigger->args, > makeString(get_attname(foreignRelId, foreignKey[i]))); > } > > trigId = CreateTrigger(fk_trigger); > > /* The trigger depends on the constraint */ > dependee.classId = get_relname_relid(TriggerRelationName, PG_CATALOG_NAMESPACE);; > dependee.objectId = trigId; > dependee.objectSubId = 0; > dependCreate(&dependee, &myself, true); > > /* > * Bump the command counter to prevent the next trigger > * from attempting to use the same name as the previous > */ > CommandCounterIncrement(); > > /* > * Build a CREATE CONSTRAINT TRIGGER statement for the ON DELETE > * action fired on the PK table !!! > */ > fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt); > fk_trigger->trigname = getTriggerName(foreignRelId, NameStr(cname)); > fk_trigger->relation = foreignRel; > 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 = isDeferrable; > fk_trigger->initdeferred = isDeferred; > fk_trigger->constrrel = localRel; > switch (foreignDeleteType) > { > case CONSTRAINT_FKEY_NOACTION: > fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del"); > break; > case CONSTRAINT_FKEY_RESTRICT: > fk_trigger->deferrable = false; > fk_trigger->initdeferred = false; > fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del"); > break; > case CONSTRAINT_FKEY_CASCADE: > fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del"); > break; > case CONSTRAINT_FKEY_NULL: > fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del"); > break; > case CONSTRAINT_FKEY_DEFAULT: > fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del"); > break; > } > > fk_trigger->args = NIL; > fk_trigger->args = lappend(fk_trigger->args, > makeString(NameStr(cname))); > fk_trigger->args = lappend(fk_trigger->args, > makeString(localRel->relname)); > fk_trigger->args = lappend(fk_trigger->args, > makeString(foreignRel->relname)); > fk_trigger->args = lappend(fk_trigger->args, > makeString(mType)); > for (i = 0; i < foreignNKeys; i++) > { > fk_trigger->args = lappend(fk_trigger->args, > makeString(get_attname(relId, constraintKey[i]))); > fk_trigger->args = lappend(fk_trigger->args, > makeString(get_attname(foreignRelId, foreignKey[i]))); > } > > trigId = CreateTrigger(fk_trigger); > > /* The trigger depends on the constraint */ > dependee.classId = get_relname_relid(TriggerRelationName, PG_CATALOG_NAMESPACE);; > dependee.objectId = trigId; > dependee.objectSubId = 0; > dependCreate(&dependee, &myself, true); > > /* > * Bump the command counter to prevent the next trigger > * from attempting to use the same name as the previous > */ > CommandCounterIncrement(); > > > /* > * Build a CREATE CONSTRAINT TRIGGER statement for the ON UPDATE > * action fired on the PK table !!! > */ > fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt); > fk_trigger->trigname = getTriggerName(foreignRelId, NameStr(cname)); > fk_trigger->relation = foreignRel; > 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 = isDeferrable; > fk_trigger->initdeferred = isDeferred; > fk_trigger->constrrel = localRel; > switch (foreignUpdateType) > { > case CONSTRAINT_FKEY_NOACTION: > fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd"); > break; > case CONSTRAINT_FKEY_RESTRICT: > fk_trigger->deferrable = false; > fk_trigger->initdeferred = false; > fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd"); > break; > case CONSTRAINT_FKEY_CASCADE: > fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd"); > break; > case CONSTRAINT_FKEY_NULL: > fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd"); > break; > case CONSTRAINT_FKEY_DEFAULT: > fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd"); > break; > } > > fk_trigger->args = NIL; > fk_trigger->args = lappend(fk_trigger->args, > makeString(NameStr(cname))); > fk_trigger->args = lappend(fk_trigger->args, > makeString(localRel->relname)); > fk_trigger->args = lappend(fk_trigger->args, > makeString(foreignRel->relname)); > fk_trigger->args = lappend(fk_trigger->args, > makeString(mType)); > for (i = 0; i < foreignNKeys; i++) > { > fk_trigger->args = lappend(fk_trigger->args, > makeString(get_attname(relId, constraintKey[i]))); > fk_trigger->args = lappend(fk_trigger->args, > makeString(get_attname(foreignRelId, foreignKey[i]))); > } > > trigId = CreateTrigger(fk_trigger); > > /* The trigger depends on the constraint */ > dependee.classId = get_relname_relid(TriggerRelationName, PG_CATALOG_NAMESPACE);; > dependee.objectId = trigId; > dependee.objectSubId = 0; > dependCreate(&dependee, &myself, true); > } > > /* Cleanup, but keep lock */ > heap_close(conDesc, NoLock); > > return conOid; > } > > > char * > getConstraintName(Oid relId) > { > int j = 1; > bool success; > Relation conDesc; > HeapTuple tup; > char *cname; > > cname = palloc(NAMEDATALEN * sizeof(char)); > > conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock); > > /* Loop until we find a non-conflicting constraint name */ > /* What happens if this loops forever? */ > do > { > int i = 0; > SysScanDesc rcscan; > ScanKeyData skey[2]; > > success = false; > > snprintf(cname, NAMEDATALEN, "constraint_%d", j); > > /* make sure there is no existing constraints of the same name */ > ScanKeyEntryInitialize(&skey[i++], > 0, Anum_pg_constraint_conrelid, F_OIDEQ, > ObjectIdGetDatum(relId)); > > ScanKeyEntryInitialize(&skey[i++], > 0, Anum_pg_constraint_conname, F_NAMEEQ, > NameGetDatum(cname)); > > rcscan = systable_beginscan(conDesc, ConstraintRelidNameIndex, true, > SnapshotNow, > i, skey); > > tup = systable_getnext(rcscan); > if (!HeapTupleIsValid(tup)) > success = true; > > systable_endscan(rcscan); > ++j; > } while (!success); > > /* Cleanup, but keep lock */ > heap_close(conDesc, NoLock); > > return cname; > } > > void > DropConstraintById(Oid conId, int behavior) > { > ObjectAddress myself; > Relation conDesc; > HeapTuple tup; > ScanKeyData skey[1]; > SysScanDesc rcscan; > int i = 0; > Relation ridescs[Num_pg_class_indices]; > Form_pg_constraint con; > > /* Better be a valid Id */ > Assert(OidIsValid(conId)); > > /* CONSTRAINT */ > ScanKeyEntryInitialize(&skey[i++], > 0, ObjectIdAttributeNumber, F_OIDEQ, > ObjectIdGetDatum(conId)); > > conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock); > rcscan = systable_beginscan(conDesc, ConstraintOidIndex, true, > SnapshotNow, > i, skey); > > tup = systable_getnext(rcscan); > > > /* > * Due to circular constraint dependencies we simply > * skip the drop when we don't find the constraint rather > * than the below: > * > * > */ > if (!HeapTupleIsValid(tup)) > elog(ERROR, "Constraint OID %d missing", conId); > > con = (Form_pg_constraint) GETSTRUCT(tup); > > /* > * Now we need to update the relcheck count > * if it was a check constraint being dropped > */ > if (con->contype == CONSTRAINT_CHECK) > { > Relation rel; > HeapTuple relTup; > > rel = heap_openr(RelationRelationName, RowExclusiveLock); > > relTup = SearchSysCache(RELOID, > ObjectIdGetDatum(con->conrelid), > 0, 0, 0); > if (!HeapTupleIsValid(relTup)) > elog(ERROR, "DropConstraintById: Relation Tuple non-existant"); > > ((Form_pg_class) GETSTRUCT(relTup))->relchecks -= 1; > > simple_heap_update(rel, &relTup->t_self, relTup); > CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); > CatalogIndexInsert(ridescs, Num_pg_class_indices, rel, relTup); > CatalogCloseIndices(Num_pg_class_indices, ridescs); > > ReleaseSysCache(relTup); > > heap_close(rel, RowExclusiveLock); > } > > /* Fry the constraint itself*/ > simple_heap_delete(conDesc, &tup->t_self); > > /* Clean up */ > systable_endscan(rcscan); > heap_close(conDesc, RowExclusiveLock); > > /* Deal with dependencies */ > myself.classId = get_relname_relid(ConstraintRelationName, PG_CATALOG_NAMESPACE); > myself.objectId = conId; > myself.objectSubId = 0; > dependDelete(&myself, behavior); > }; > > ---------------------------------------------------------------------- ---------- > /*-------------------------------------------------------------------- ----- > * > * depend.c > * random postgres portal and utility support code > * > * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group > * Portions Copyright (c) 1994, Regents of the University of California > * > * > * IDENTIFICATION > * $Header$ > * > * NOTES > * Manage dependencies between varying system objects. > * > *--------------------------------------------------------------------- ---- > */ > > #include "postgres.h" > #include "miscadmin.h" > #include "access/heapam.h" > #include "access/genam.h" > #include "catalog/catname.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_namespace.h" > #include "catalog/pg_operator.h" > #include "catalog/pg_proc.h" > #include "catalog/pg_type.h" > #include "commands/comment.h" > #include "commands/defrem.h" > #include "commands/tablecmds.h" > #include "commands/trigger.h" > #include "commands/view.h" > #include "nodes/parsenodes.h" > #include "nodes/pg_list.h" > #include "nodes/makefuncs.h" > #include "rewrite/rewriteRemove.h" > #include "utils/fmgroids.h" > #include "utils/lsyscache.h" > #include "utils/syscache.h" > > static char *getObjectName(const ObjectAddress *object); > static char *getObjectType(const ObjectAddress *object); > static bool isObjectPinned(const ObjectAddress *object, Relation rel); > static bool isStructureOfPin(const ObjectAddress *object); > > /* > * Records a requested dependency between 2 objects via their > * respective objectAddress. > * > * It makes the assumption that both objects currently (or will) > * exist before the end of the transaction. > * > * Behaviour, if true tells dependDelete to ignore RESTRICT as > * the issued behaviour at the time and cascade to the object > * anyway. The reason for this is sequences generated by SERIAL > * and array types. > */ > void > dependCreate(const ObjectAddress *depender, > const ObjectAddress *dependee, > bool behavior) > { > if (!IsBootstrapProcessingMode()) > { > Relation dependDesc; > TupleDesc tupDesc; > HeapTuple tup; > int i; > > char nulls[Natts_pg_depend]; > Datum values[Natts_pg_depend]; > > > for (i = 0; i < Natts_pg_depend; ++i) > { > nulls[i] = ' '; > values[i] = (Datum) 0; > } > > dependDesc = heap_openr(DependRelationName, RowExclusiveLock); > > /* Test to see if the object is pinned (permenant) */ > if (!isObjectPinned(dependee, dependDesc)) > { > Relation idescs[Num_pg_depend_indices]; > /* > * Record the Dependency. Assume it can be added, and > * doesn't previously exist. Some items (type creation) > * may add duplicates. > */ > 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_depclassid - 1] = ObjectIdGetDatum(dependee->classId); > values[Anum_pg_depend_depobjid - 1] = ObjectIdGetDatum(dependee->objectId); > values[Anum_pg_depend_depobjsubid - 1] = Int32GetDatum(dependee->objectSubId); > values[Anum_pg_depend_alwayscascade -1] = BoolGetDatum(behavior); > > tupDesc = dependDesc->rd_att; > > if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc, > values, > nulls))) > elog(ERROR, "DependCreate: heap_formtuple failed"); > > 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); /* Required? */ > } > } > > > /* > * Drops the interdependencies between the object and it's > * children depending on the behavior specified. RESTRICT or > * CASCADE types supported. > * > * RESTRICT will abort the transaction if other objects depend > * on this one. > * > * CASCADE will drop all objects which depend on the supplied > * object address. > */ > void > dependDelete(ObjectAddress *object, int behavior) > { > > Relation rel; > ScanKeyData dropkey[3]; > HeapTuple tup; > int dropnkeys = 0; > SysScanDesc scan; > > bool deprem = true; > ObjectAddress objectCopy; > > Assert(behavior == DEPEND_RESTRICT || behavior == DEPEND_CASCADE || behavior == DEPEND_IMPLICITONLY); > > /* > * A copy of the object passed in needs to be taken, else we risk > * it being wiped from memory mid way through the drops. > */ > objectCopy.classId = object->classId; > objectCopy.objectId = object->objectId; > objectCopy.objectSubId = object->objectSubId; > > /* Delete any comments associated with this object */ > DeleteComments(objectCopy.objectId, objectCopy.classId, objectCopy.objectSubId); > > /* > * Test whether object being dropped is a dependee > * or not. > */ > rel = heap_openr(DependRelationName, RowExclusiveLock); > > /* If object is pinned dissallow it's removal */ > if (isObjectPinned(&objectCopy, rel)) > elog(ERROR, "Drop Restricted as %s %s is an essential for the database to function", > getObjectType(&objectCopy), > getObjectName(&objectCopy)); > > while (deprem) > { > ScanKeyData key[3]; > > ObjectAddress foundObject; > Form_pg_depend foundTup; > int nkeys = 0; > > /* Class Oid */ > Assert(objectCopy.classId != InvalidOid); > ScanKeyEntryInitialize(&key[nkeys++], > 0, Anum_pg_depend_depclassid, F_OIDEQ, > ObjectIdGetDatum(objectCopy.classId)); > > /* Object Oid */ > Assert(objectCopy.objectId != InvalidOid); > ScanKeyEntryInitialize(&key[nkeys++], > 0, Anum_pg_depend_depobjid, F_OIDEQ, > ObjectIdGetDatum(objectCopy.objectId)); > > /* SubObject Id */ > ScanKeyEntryInitialize(&key[nkeys++], > 0, Anum_pg_depend_depobjsubid, F_INT4EQ, > Int32GetDatum(objectCopy.objectSubId)); > > scan = systable_beginscan(rel, DependDependeeIndex, true, > SnapshotNow, nkeys, key); > > /* > * If no type tuple exists for the given type name, then end the scan > * and return appropriate information. > */ > tup = systable_getnext(scan); > if (!HeapTupleIsValid(tup)) > { > deprem = false; > continue; > } > > foundTup = (Form_pg_depend) GETSTRUCT(tup); > > /* > * Lets load up and test the object which depends > * on the one we want to drop. > */ > foundObject.classId = foundTup->classid; > foundObject.objectId = foundTup->objid; > foundObject.objectSubId = foundTup->objsubid; > > systable_endscan(scan); > > /* > * If there are dependencies and behaviour is RESTRICT > * then drop them all. > */ > if (behavior == DEPEND_RESTRICT && !foundTup->alwayscascade) > elog(ERROR, "Drop Restricted as %s %s Depends on %s %s", > getObjectType(&foundObject), > getObjectName(&foundObject), > getObjectType(&objectCopy), > getObjectName(&objectCopy)); > > /* > * When IMPLICITONLY we don't want to cascade or restrict. > * Simply drop all items implicitly associated with this object. > */ > if (behavior == DEPEND_IMPLICITONLY && !foundTup->alwayscascade) > { > continue; > } > > /* Tell the user */ > if (foundTup->alwayscascade) > elog(DEBUG1, "Implicit drop of %s %s", > getObjectType(&foundObject), > getObjectName(&foundObject)); > else > elog(NOTICE, "Cascading drop to %s %s", > getObjectType(&foundObject), > getObjectName(&foundObject)); > > /* > * The below functions are expected to cascade back here by calling > * dependDelete(). If they don't, a partial cascade can occur leaving > * poor relations in place. > */ > switch (foundObject.classId) > { > case RelOid_pg_proc: > RemoveFunctionById(foundObject.objectId, behavior); > break; > > case RelOid_pg_class: > { > HeapTuple relTup; > char relKind; > char *relName; > char *schemaName; > > relTup = SearchSysCache(RELOID, > ObjectIdGetDatum(foundObject.objectId), > 0, 0, 0); > if (!HeapTupleIsValid(relTup)) { > elog(ERROR, "dependDelete: Relation %d does not exist", > foundObject.objectId); > } > > relKind = ((Form_pg_class) GETSTRUCT(relTup))->relkind; > relName = NameStr(((Form_pg_class) GETSTRUCT(relTup))->relname); > schemaName = get_namespace_name(((Form_pg_class) GETSTRUCT(relTup))->relnamespace); > > ReleaseSysCache(relTup); > > switch(relKind) > { > case RELKIND_INDEX: > /* Drop INDEX > * > * Future use will use below once indexes drops are > * corrected to be selfcontained. Messages of tuple > * updates occur if more than a single index is removed > * during a table drop. > */ > index_drop(foundObject.objectId, behavior); > break; > > case RELKIND_VIEW: > RemoveView(makeRangeVar(schemaName, relName), behavior); > break; > > case RELKIND_RELATION: > case RELKIND_SEQUENCE: > case RELKIND_TOASTVALUE: > case RELKIND_SPECIAL: > RemoveRelation(makeRangeVar(schemaName, relName), behavior); > break; > default: > elog(ERROR, "dependDelete: Unknown relkind %c", relKind); > } > break; > } > > case RelOid_pg_type: > { > TypeName *typename; > > /* Make a TypeName so we can use standard type lookup machinery */ > typename = makeNode(TypeName); > typename->names = NIL; > typename->typeid = foundObject.objectId; > typename->typmod = -1; > typename->arrayBounds = NIL; > > /* Drop the type */ > RemoveTypeByTypeName(typename, behavior); > > break; > } > case RelOid_pg_attribute: > elog(ERROR, "Removing Attribute"); > break; > > default: > /* Can't compare to a 'static' OID */ > if (foundObject.classId == get_relname_relid(AggregateRelationName, PG_CATALOG_NAMESPACE)) > elog(ERROR, "Removing Aggregate"); > > else if (foundObject.classId == get_relname_relid(ConstraintRelationName, PG_CATALOG_NAMESPACE)) > DropConstraintById(foundObject.objectId, behavior); > > else if (foundObject.classId == get_relname_relid(LanguageRelationName, PG_CATALOG_NAMESPACE)) > elog(ERROR, "PL Handler"); > > else if (foundObject.classId == get_relname_relid(OperatorRelationName, PG_CATALOG_NAMESPACE)) > RemoveOperatorById(foundObject.objectId, behavior); > > else if (foundObject.classId == get_relname_relid(RewriteRelationName, PG_CATALOG_NAMESPACE)) > RemoveRewriteRuleById(foundObject.objectId, behavior); > > else if (foundObject.classId == get_relname_relid(TriggerRelationName, PG_CATALOG_NAMESPACE)) > DropTriggerById(foundObject.objectId, behavior); > > else > elog(ERROR, "getObjectType: Unknown object class %d", foundObject.classId); > } > > /* > * We need to assume that cascaded items could potentially > * remove dependencies we want to as well. The simplest > * way to ovoid double deletions (and warnings about tuples > * being modified twice) is to rescan our list after > * bumping the command counter. > */ > CommandCounterIncrement(); > } > > /* > * Now go through the whole thing again looking for our object > * as the depender so we can drop those dependencies. > */ > > /* Class Oid */ > Assert(objectCopy.classId != InvalidOid); > ScanKeyEntryInitialize(&dropkey[dropnkeys++], > 0, Anum_pg_depend_classid, F_OIDEQ, > ObjectIdGetDatum(objectCopy.classId)); > /* Object Oid */ > Assert(objectCopy.objectId != InvalidOid); > ScanKeyEntryInitialize(&dropkey[dropnkeys++], > 0, Anum_pg_depend_objid, F_OIDEQ, > ObjectIdGetDatum(objectCopy.objectId)); > /* SubObject Id */ > ScanKeyEntryInitialize(&dropkey[dropnkeys++], > 0, Anum_pg_depend_objsubid, F_INT4EQ, > Int32GetDatum(objectCopy.objectSubId)); > > scan = systable_beginscan(rel, DependDependerIndex, true, > SnapshotNow, dropnkeys, dropkey); > > /* Drop dependencies found */ > while (HeapTupleIsValid(tup = systable_getnext(scan))) { > simple_heap_delete(rel, &tup->t_self); > } > > systable_endscan(scan); > > /* Cleanup and get out */ > heap_close(rel, RowExclusiveLock); > } > > /* Delete function to save time */ > void > dependDeleteTuple(const HeapTuple tup, const Relation relation, int behavior) > { > ObjectAddress myself; > > /* Collect the information and call the real delete function */ > myself.classId = RelationGetRelid(relation); > myself.objectId = tup->t_data->t_oid; > myself.objectSubId = 0; > > dependDelete(&myself, behavior); > } > > > /* Fetch the Object Name for display */ > static char * > getObjectName(const ObjectAddress *object) > { > char *name = "Unknown"; /* Unknown to Keep compiler quiet */ > > switch (object->classId) > { > case RelOid_pg_proc: > { > /* FUNCTION */ > HeapTuple procTup; > > procTup = SearchSysCache(PROCOID, > ObjectIdGetDatum(object->objectId), > 0, 0, 0); > name = NameStr(((Form_pg_proc) GETSTRUCT(procTup))->proname); > > ReleaseSysCache(procTup); > break; > } > case RelOid_pg_class: > { > /* RELATION */ > HeapTuple relTup; > > relTup = SearchSysCache(RELOID, > ObjectIdGetDatum(object->objectId), > 0, 0, 0); > name = NameStr(((Form_pg_class) GETSTRUCT(relTup))->relname); > > ReleaseSysCache(relTup); > break; > } > case RelOid_pg_type: > { > /* TYPE */ > HeapTuple typeTup; > > typeTup = SearchSysCache(TYPEOID, > ObjectIdGetDatum(object->objectId), > 0, 0, 0); > name = NameStr(((Form_pg_type) GETSTRUCT(typeTup))->typname); > > ReleaseSysCache(typeTup); > break; > } > case RelOid_pg_attribute: > /* ATTRIBUTE */ > name = "Unknown"; > break; > > default: > /* Can't compare to a 'static' OID */ > if (object->classId == get_relname_relid(AggregateRelationName, PG_CATALOG_NAMESPACE)) > /* AGGREGATE */ > name = "Unknown"; > > else if (object->classId == get_relname_relid(ConstraintRelationName, PG_CATALOG_NAMESPACE)) > { > HeapTuple tup; > Relation conDesc; > ScanKeyData skey[1]; > SysScanDesc rcscan; > int i = 0; > > /* CONSTRAINT */ > ScanKeyEntryInitialize(&skey[i++], > 0, ObjectIdAttributeNumber, F_OIDEQ, > ObjectIdGetDatum(object->objectId)); > > conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock); > rcscan = systable_beginscan(conDesc, ConstraintOidIndex, true, > SnapshotNow, > i, skey); > > tup = systable_getnext(rcscan); > > if (!HeapTupleIsValid(tup)) > /* > * elog(ERROR, "Constraint OID %d missing", object->objectId); > * > * Due to circular dependencies we simply say we don't know rather > * than the above line. This shouldn't happen in any other case > * than a the circular constraint dependency anyway. > */ > name = "<Unknown>"; > else > name = NameStr(((Form_pg_constraint) GETSTRUCT(tup))->conname); > > /* Clean up */ > systable_endscan(rcscan); > } > else if (object->classId == get_relname_relid(LanguageRelationName, PG_CATALOG_NAMESPACE)) > { > /* LANGUAGE */ > HeapTuple langTup; > > langTup = SearchSysCache(LANGOID, > ObjectIdGetDatum(object->objectId), > 0, 0, 0); > name = NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname); > > ReleaseSysCache(langTup); > } > else if (object->classId == get_relname_relid(OperatorRelationName, PG_CATALOG_NAMESPACE)) > { > /* OPERATOR */ > HeapTuple operTup; > > operTup = SearchSysCache(OPEROID, > ObjectIdGetDatum(object->objectId), > 0, 0, 0); > name = NameStr(((Form_pg_operator) GETSTRUCT(operTup))->oprname); > > ReleaseSysCache(operTup); > } > else if (object->classId == get_relname_relid(RewriteRelationName, PG_CATALOG_NAMESPACE)) > /* RULE */ > name = "Unknown"; > > else if (object->classId == get_relname_relid(TriggerRelationName, PG_CATALOG_NAMESPACE)) > /* TRIGGER */ > name = "Unknown"; > > else > elog(ERROR, "getObjectType: Unknown object class %d", object->classId); > } > > return name; > } > > > /* Fetch the Object Type for display */ > static char * > getObjectType(const ObjectAddress *object) > { > char *name = "Unknown"; /* Unknown to keep compiler quiet */ > > switch (object->classId) > { > case RelOid_pg_proc: > name = "Function"; > break; > > case RelOid_pg_class: > { > HeapTuple relTup; > char relKind; > > relTup = SearchSysCache(RELOID, > ObjectIdGetDatum(object->objectId), > 0, 0, 0); > if (!HeapTupleIsValid(relTup)) { > elog(ERROR, "getObjectType: Relation %d does not exist", > object->objectId); > } > > relKind = ((Form_pg_class) GETSTRUCT(relTup))->relkind; > ReleaseSysCache(relTup); > > switch(relKind) > { > case RELKIND_INDEX: > name = "Index"; > break; > case RELKIND_VIEW: > name = "View"; > break; > case RELKIND_RELATION: > name = "Table"; > break; > case RELKIND_TOASTVALUE: > name = "Toast Table"; > break; > case RELKIND_SEQUENCE: > name = "Sequence"; > break; > case RELKIND_SPECIAL: > name = "Special"; > break; > default: > elog(ERROR, "dependDelete: Unknown relkind %c", relKind); > } > break; > } > > case RelOid_pg_type: > name = "Type"; > break; > > case RelOid_pg_attribute: > name = "Table Attribute"; > break; > > default: > /* Can't compare to a 'static' OID */ > if (object->classId == get_relname_relid(AggregateRelationName, PG_CATALOG_NAMESPACE)) > name = "Aggregate"; > > else if (object->classId == get_relname_relid(ConstraintRelationName, PG_CATALOG_NAMESPACE)) > name = "Constraint"; > > else if (object->classId == get_relname_relid(LanguageRelationName, PG_CATALOG_NAMESPACE)) > name = "PL Hander"; > > else if (object->classId == get_relname_relid(OperatorRelationName, PG_CATALOG_NAMESPACE)) > name = "Operator"; > > else if (object->classId == get_relname_relid(RewriteRelationName, PG_CATALOG_NAMESPACE)) > name = "Rule"; > > else if (object->classId == get_relname_relid(TriggerRelationName, PG_CATALOG_NAMESPACE)) > name = "Trigger"; > > else > elog(ERROR, "getObjectType: Unknown object class %d", object->classId); > } > > return name; > } > > > > /* > * isPinnedDependee() > * > * Test if the dependee of a found object is a permenant requirement > * for basic database functionality. > */ > static bool > isStructureOfPin(const ObjectAddress *object) > { > bool ret = false; > > if (object->classId == InvalidOid > && object->objectId == InvalidOid > && object->objectSubId == 0) > ret = true; > > return(ret); > } > > > /* > * isObjectPinned() > * > * Test if an object is permenantly required for basic database functionality > */ > static bool > isObjectPinned(const ObjectAddress *object, Relation rel) > { > SysScanDesc scan; > HeapTuple tup; > bool ret = false; > ScanKeyData key[6]; > int nkeys = 0; > > /* Pinned in Depender Slot*/ > Assert(object->classId); > ScanKeyEntryInitialize(&key[nkeys++], > 0, Anum_pg_depend_classid, F_OIDEQ, > ObjectIdGetDatum(object->classId)); > > Assert(object->objectId); > ScanKeyEntryInitialize(&key[nkeys++], > 0, Anum_pg_depend_objid, F_OIDEQ, > ObjectIdGetDatum(object->objectId)); > > ScanKeyEntryInitialize(&key[nkeys++], > 0, Anum_pg_depend_objsubid, F_INT4EQ, > Int32GetDatum(object->objectSubId)); > > > scan = systable_beginscan(rel, DependDependerIndex, true, > SnapshotNow, nkeys, key); > > /* > * If we find a match, skip the entire process. > */ > tup = systable_getnext(scan); > if (HeapTupleIsValid(tup)) > { > ObjectAddress foundObject; > Form_pg_depend foundTup; > > /* > * Pinned objects have a dependee ObjectAddress of 0, 0, 0 > * and will only ever have one entry. > */ > foundTup = (Form_pg_depend) GETSTRUCT(tup); > > foundObject.classId = foundTup->depclassid; > foundObject.objectId = foundTup->depobjid; > foundObject.objectSubId = foundTup->depobjsubid; > > if (isStructureOfPin(&foundObject)) > ret = true; > } > > /* Cleanup and return */ > systable_endscan(scan); > > return(ret); > } > ---------------------------------------------------------------------- ---------- > /*-------------------------------------------------------------------- ----- > * > * pg_constraint.h > * > * > * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group > * Portions Copyright (c) 1994, Regents of the University of California > * > * NOTES > * the genbki.sh script reads this file and generates .bki > * information from the DATA() statements. > * > *--------------------------------------------------------------------- ---- > */ > #ifndef PG_CONSTRAINT_H > #define PG_CONSTRAINT_H > > /* ---------------- > * postgres.h contains the system type definintions and the > * CATALOG(), BOOTSTRAP and DATA() sugar words so this file > * can be read by both genbki.sh and the C compiler. > * ---------------- > */ > > /* ---------------- > * pg_constraint definition. cpp turns this into > * typedef struct FormData_pg_constraint > * ---------------- > */ > CATALOG(pg_constraint) > { > /* Oid of the relation this constraint constrains */ > Oid conrelid; > > /* Name of this constraint */ > NameData conname; > > /* > * contype is the Constraint Type. > * > * Includes 'p'rimary keys, 'u'nique keys, 'f'oreign keys > * and 'c'heck constraints. > */ > char contype; > > /* > * Can application of the constraint be deferred until > * transaction commit? > */ > bool condeferrable; > > /* > * Is the constraint deferred until transaction commit > * by default? > */ > bool condeferred; > > /* > * Foreign key'd relation > */ > Oid confrelid; > > /* > * confupdtype is the type of update action expected > */ > char confupdtype; > > /* > * confdeltype is the type of update action expected > */ > char confdeltype; > > /* > * confmatchtype the match type of the foreign key > * 'f'ull or 'p'artial > */ > char confmatchtype; > > /* > * Columns of conrelid that the constraint applies to > */ > int2 conkey[1]; > > /* > * Foreign key'd columns > */ > int2 confkey[1]; > > /* > * Source (text and binary) for check constraints > */ > text conbin; > text consrc; > } FormData_pg_constraint; > > /* ---------------- > * Form_pg_constraint corresponds to a pointer to a tuple with > * the format of pg_constraint relation. > * ---------------- > */ > typedef FormData_pg_constraint *Form_pg_constraint; > > /* ---------------- > * compiler constants for pg_constraint > * ---------------- > */ > #define Natts_pg_constraint 13 > #define Anum_pg_constraint_conrelid 1 > #define Anum_pg_constraint_conname 2 > #define Anum_pg_constraint_contype 3 > #define Anum_pg_constraint_condeferrable 4 > #define Anum_pg_constraint_condeferred 5 > #define Anum_pg_constraint_confrelid 6 > #define Anum_pg_constraint_confupdtype 7 > #define Anum_pg_constraint_confdeltype 8 > #define Anum_pg_constraint_confmatchtype 9 > #define Anum_pg_constraint_conkey 10 > #define Anum_pg_constraint_confkey 11 > #define Anum_pg_constraint_conbin 12 > #define Anum_pg_constraint_consrc 13 > > #define CONSTRAINT_FKEY_RESTRICT 'r' > #define CONSTRAINT_FKEY_CASCADE 'c' > #define CONSTRAINT_FKEY_NULL 'n' > #define CONSTRAINT_FKEY_DEFAULT 'd' > #define CONSTRAINT_FKEY_NOACTION 'a' > > #define CONSTRAINT_FKEY_PARTIAL 'p' > #define CONSTRAINT_FKEY_FULL 'f' > #define CONSTRAINT_FKEY_UNSPECIFIED 'u' > > #define CONSTRAINT_CHECK 'c' > #define CONSTRAINT_FOREIGN 'f' > #define CONSTRAINT_PRIMARY 'p' > #define CONSTRAINT_UNIQUE 'u' > > /* > * prototypes for functions in pg_constraint.c > */ > extern Oid constraintCreate(Oid relId, > const char *constraintName, > char constraintType, > bool isDeferrable, > bool isDeferred, > const AttrNumber *constraintKey, > int constraintNKeys, > Oid foreignRelId, > const AttrNumber *foreignKey, > int foreignNKeys, > char foreignUpdateType, > char foreignDeleteType, > char foreignMatchType, > char *conBin, > char *conSrc); > > extern void DropConstraintById(Oid conId, int behavior); > > extern char *getConstraintName(Oid relId); > > #endif /* PG_CONSTRAINT_H */ > ---------------------------------------------------------------------- ---------- > /*-------------------------------------------------------------------- ----- > * > * pg_depend.h > * definition of the system "depend" relation (pg_depend) > * along with the relation's initial contents. > * > * > * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group > * Portions Copyright (c) 1994, Regents of the University of California > * > * $Header$ > * > * NOTES > * the genbki.sh script reads this file and generates .bki > * information from theDATA() statements. > * > *--------------------------------------------------------------------- ---- > */ > #ifndef PG_DEPEND_H > #define PG_DEPEND_H > > /* ---------------- > * postgres.h contains the system type definitions and the > * CATALOG(), BOOTSTRAP andDATA() sugar words so this file > * can be read by both genbki.sh and the C compiler. > * ---------------- > */ > > /* ---------------- > * pg_depend definition. cpp turns this into > * typedef struct FormData_pg_depend > * ---------------- > */ > CATALOG(pg_depend) BKI_PINWRITE BKI_WITHOUT_OIDS > { > Oid classid; /* OID of table containing object */ > Oid objid; /* OID of object itself */ > int4 objsubid; /* column number, or 0 if not used */ > > Oid depclassid; /* OID of table containing dependee object */ > Oid depobjid; /* OID of dependee object itself */ > int4 depobjsubid; /* dependee column number, or 0 if not used */ > > /* > * Always cascade to the item, even when the RESTRICT behavior has been > * specified. Used primarily for SERIAL, and ARRAY types. > */ > bool alwayscascade; > } FormData_pg_depend; > > /* ---------------- > * Form_pg_depend corresponds to a pointer to a row with > * the format of pg_depend relation. > * ---------------- > */ > typedef FormData_pg_depend *Form_pg_depend; > > /* ---------------- > * compiler constants for pg_depend > * ---------------- > */ > #define Natts_pg_depend 7 > #define Anum_pg_depend_classid 1 > #define Anum_pg_depend_objid 2 > #define Anum_pg_depend_objsubid 3 > #define Anum_pg_depend_depclassid 4 > #define Anum_pg_depend_depobjid 5 > #define Anum_pg_depend_depobjsubid 6 > #define Anum_pg_depend_alwayscascade 7 > > #define DEPEND_RESTRICT 1 > #define DEPEND_CASCADE 2 > #define DEPEND_IMPLICITONLY 3 > > /* > * Dependencies are automatically discovered in the genbki.sh > * script by using tha TABLEOID variable located at the top of > * the table description files. > */ > > > typedef struct ObjectAddress > { > Oid classId; /* Class Id from pg_class */ > Oid objectId; /* OID of the object */ > int32 objectSubId; /* Subitem within the object (column of table) */ > } ObjectAddress; > > > extern void dependCreate(const ObjectAddress *depender, > const ObjectAddress *dependee, bool behavior); > extern void dependDelete(ObjectAddress *object, int behavior); > extern void dependDeleteTuple(const HeapTuple tup, > const Relation relation, > int behavior); > > #endif /* PG_DEPEND_H */ > ---------------------------------------------------------------------- ---------- > -- > -- Test RESTRICT and CASCADE keywords. > -- > -- Ensure system types cannot be removed > DROP TYPE int4 CASCADE; > ERROR: Drop Restricted as Type int4 is an essential for the database to function > DROP FUNCTION nextval(text) CASCADE; > ERROR: Drop Restricted as Function nextval is an essential for the database to function > DROP TABLE pg_type CASCADE; > ERROR: table "pg_type" is a system table > -- Function RESTRICT / CASCADE > DROP FUNCTION widget_in(opaque) RESTRICT; -- fail > ERROR: Drop Restricted as Type widget Depends on Function widget_in > DROP TYPE widget RESTRICT; -- fail > ERROR: Drop Restricted as Operator <% Depends on Type widget > DROP FUNCTION widget_in(opaque) CASCADE; > NOTICE: Cascading drop to Type widget > NOTICE: Cascading drop to Operator <% > NOTICE: Cascading drop to Function pt_in_widget > DROP TYPE widget RESTRICT; -- doesn't exist > ERROR: Type "widget" does not exist > -- Type RESTRICT / CASCADE > DROP TYPE city_budget RESTRICT; -- fail > ERROR: Drop Restricted as Table city Depends on Type city_budget > DROP TYPE city_budget CASCADE; > NOTICE: Cascading drop to Table city > DROP TABLE city RESTRICT; -- doesn't exist > ERROR: table "city" does not exist > -- Domain RESTRICT / CASCADE > DROP DOMAIN ddef1 RESTRICT; -- fail > ERROR: Drop Restricted as Table defaulttest Depends on Type ddef1 > DROP DOMAIN ddef1 CASCADE; > NOTICE: Cascading drop to Table defaulttest > DROP TABLE defaulttest RESTRICT; -- doesn't exist > ERROR: table "defaulttest" does not exist > -- Procedural languge RESTRICT / CASCADE > DROP LANGUAGE plpgsql RESTRICT; -- fail > ERROR: Drop Restricted as Function recursion_test Depends on PL Hander plpgsql > DROP LANGUAGE plpgsql CASCADE; > NOTICE: Cascading drop to Function recursion_test > NOTICE: Cascading drop to Function wslot_slotlink_view > NOTICE: Cascading drop to Function pslot_slotlink_view > NOTICE: Cascading drop to Function pslot_backlink_view > NOTICE: Cascading drop to Function tg_slotlink_unset > NOTICE: Cascading drop to Function tg_slotlink_set > NOTICE: Cascading drop to Function tg_slotlink_a > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_backlink_unset > NOTICE: Cascading drop to Function tg_backlink_set > NOTICE: Cascading drop to Function tg_backlink_a > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_phone_bu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_hslot_bu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_iface_bu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_pline_bu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_wslot_bu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_pslot_bu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_chkbacklink > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_chkslotlink > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_chkslotname > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_hslot_bd > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_hslot_biu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_hub_adjustslots > NOTICE: Cascading drop to Function tg_hub_a > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_iface_biu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_system_au > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_pslot_biu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_pfield_ad > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_pfield_au > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_wslot_biu > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_room_ad > NOTICE: Cascading drop to Trigger Unknown > NOTICE: Cascading drop to Function tg_room_au > NOTICE: Cascading drop to Trigger Unknown > SELECT recursion_test(2,3); -- doesn't exist > ERROR: Function 'recursion_test(integer, integer)' does not exist > Unable to identify a function that satisfies the given argument types > You may need to add explicit typecasts > -- Foreign Key RESTRICT / CASCADE > -- See alter table pktable and fktable tests > ---------------------------------------------------------------------- ---------- > > ---------------------------(end of broadcast)--------------------------- > TIP 6: Have you searched our list archives? > > http://archives.postgresql.org >
Attachment
pgsql-patches by date: