Dependency / Constraint patch - Mailing list pgsql-patches

From Rod Taylor
Subject Dependency / Constraint patch
Date
Msg-id 3D0B9112.6010201@zort.ca
Whole thread Raw
Responses Re: Dependency / Constraint patch  (Bruce Momjian <pgman@candle.pha.pa.us>)
List pgsql-patches
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
doingwork 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

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: libpq++ fixes
Next
From: "Rod Taylor"
Date:
Subject: Re: Dependency / Constraint patch