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