2002-07-12 20:43:19 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_constraint.c
|
|
|
|
* routines to support manipulation of the pg_constraint relation
|
|
|
|
*
|
2004-12-31 23:04:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
2002-07-12 20:43:19 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2006-02-12 20:11:01 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.30 2006/02/12 19:11:01 momjian Exp $
|
2002-07-12 20:43:19 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "access/genam.h"
|
|
|
|
#include "catalog/catalog.h"
|
|
|
|
#include "catalog/dependency.h"
|
|
|
|
#include "catalog/indexing.h"
|
|
|
|
#include "catalog/pg_constraint.h"
|
2005-03-25 22:58:00 +01:00
|
|
|
#include "catalog/pg_depend.h"
|
2005-04-14 22:03:27 +02:00
|
|
|
#include "catalog/pg_trigger.h"
|
2002-08-26 19:54:02 +02:00
|
|
|
#include "catalog/pg_type.h"
|
2004-06-10 19:56:03 +02:00
|
|
|
#include "commands/defrem.h"
|
2002-07-12 20:43:19 +02:00
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "utils/array.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/fmgroids.h"
|
2005-03-25 22:58:00 +01:00
|
|
|
#include "utils/lsyscache.h"
|
2002-07-12 20:43:19 +02:00
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CreateConstraintEntry
|
|
|
|
* Create a constraint table entry.
|
|
|
|
*
|
|
|
|
* Subsidiary records (such as triggers or indexes to implement the
|
2002-09-04 22:31:48 +02:00
|
|
|
* constraint) are *not* created here. But we do make dependency links
|
2002-07-12 20:43:19 +02:00
|
|
|
* 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,
|
2002-09-22 02:37:09 +02:00
|
|
|
Oid indexRelId,
|
2002-07-16 07:53:34 +02:00
|
|
|
Node *conExpr,
|
2002-07-12 20:43:19 +02:00
|
|
|
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;
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
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,
|
2002-08-26 19:54:02 +02:00
|
|
|
INT2OID, 2, true, 's');
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
|
|
|
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,
|
2002-08-26 19:54:02 +02:00
|
|
|
INT2OID, 2, true, 's');
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
|
|
|
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,
|
2005-10-15 04:49:52 +02:00
|
|
|
CStringGetDatum(conBin));
|
2002-07-12 20:43:19 +02:00
|
|
|
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,
|
2005-10-15 04:49:52 +02:00
|
|
|
CStringGetDatum(conSrc));
|
2002-07-12 20:43:19 +02:00
|
|
|
else
|
|
|
|
nulls[Anum_pg_constraint_consrc - 1] = 'n';
|
|
|
|
|
|
|
|
tup = heap_formtuple(RelationGetDescr(conDesc), values, nulls);
|
|
|
|
|
|
|
|
conOid = simple_heap_insert(conDesc, tup);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* update catalog indexes */
|
|
|
|
CatalogUpdateIndexes(conDesc, tup);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conobject.classId = ConstraintRelationId;
|
2002-07-12 20:43:19 +02:00
|
|
|
conobject.objectId = conOid;
|
|
|
|
conobject.objectSubId = 0;
|
|
|
|
|
|
|
|
heap_close(conDesc, RowExclusiveLock);
|
|
|
|
|
|
|
|
if (OidIsValid(relId))
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Register auto dependency from constraint to owning relation, or to
|
|
|
|
* specific column(s) if any are mentioned.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
ObjectAddress relobject;
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 03:38:22 +02:00
|
|
|
relobject.classId = RelationRelationId;
|
2002-07-12 20:43:19 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-15 03:50:21 +01:00
|
|
|
if (OidIsValid(domainId))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Register auto dependency from constraint to owning domain
|
|
|
|
*/
|
2003-08-04 02:43:34 +02:00
|
|
|
ObjectAddress domobject;
|
2002-11-15 03:50:21 +01:00
|
|
|
|
2005-04-14 03:38:22 +02:00
|
|
|
domobject.classId = TypeRelationId;
|
2002-11-15 03:50:21 +01:00
|
|
|
domobject.objectId = domainId;
|
2002-12-06 06:00:34 +01:00
|
|
|
domobject.objectSubId = 0;
|
2002-11-15 03:50:21 +01:00
|
|
|
|
|
|
|
recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
|
|
|
|
}
|
|
|
|
|
2002-07-12 20:43:19 +02:00
|
|
|
if (OidIsValid(foreignRelId))
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Register normal dependency from constraint to foreign relation, or
|
|
|
|
* to specific column(s) if any are mentioned.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
ObjectAddress relobject;
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 03:38:22 +02:00
|
|
|
relobject.classId = RelationRelationId;
|
2002-07-12 20:43:19 +02:00
|
|
|
relobject.objectId = foreignRelId;
|
|
|
|
if (foreignNKeys > 0)
|
|
|
|
{
|
|
|
|
for (i = 0; i < foreignNKeys; i++)
|
|
|
|
{
|
|
|
|
relobject.objectSubId = foreignKey[i];
|
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
relobject.objectSubId = 0;
|
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-22 02:37:09 +02:00
|
|
|
if (OidIsValid(indexRelId))
|
|
|
|
{
|
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Register normal dependency on the unique index that supports a
|
|
|
|
* foreign-key constraint.
|
2002-09-22 02:37:09 +02:00
|
|
|
*/
|
|
|
|
ObjectAddress relobject;
|
|
|
|
|
2005-04-14 03:38:22 +02:00
|
|
|
relobject.classId = RelationRelationId;
|
2002-09-22 02:37:09 +02:00
|
|
|
relobject.objectId = indexRelId;
|
|
|
|
relobject.objectSubId = 0;
|
|
|
|
|
|
|
|
recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
2002-07-16 07:53:34 +02:00
|
|
|
if (conExpr != NULL)
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Register dependencies from constraint to objects mentioned in CHECK
|
|
|
|
* expression.
|
2002-07-16 07:53:34 +02:00
|
|
|
*/
|
2003-05-28 18:04:02 +02:00
|
|
|
recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
|
|
|
|
DEPENDENCY_NORMAL,
|
|
|
|
DEPENDENCY_NORMAL);
|
2002-07-16 07:53:34 +02:00
|
|
|
}
|
|
|
|
|
2002-07-12 20:43:19 +02:00
|
|
|
return conOid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test whether given name is currently used as a constraint name
|
2004-06-10 19:56:03 +02:00
|
|
|
* for the given object (relation or domain).
|
2002-07-12 20:43:19 +02:00
|
|
|
*
|
2004-06-10 19:56:03 +02:00
|
|
|
* This is used to decide whether to accept a user-specified constraint name.
|
|
|
|
* It is deliberately not the same test as ChooseConstraintName uses to decide
|
|
|
|
* whether an auto-generated name is OK: here, we will allow it unless there
|
|
|
|
* is an identical constraint name in use *on the same object*.
|
|
|
|
*
|
|
|
|
* NB: Caller should hold exclusive lock on the given object, else
|
|
|
|
* this test can be fooled by concurrent additions.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
|
|
|
bool
|
2004-06-10 19:56:03 +02:00
|
|
|
ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
|
|
|
|
Oid objNamespace, const char *conname)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
|
|
|
bool found;
|
|
|
|
Relation conDesc;
|
|
|
|
SysScanDesc conscan;
|
|
|
|
ScanKeyData skey[2];
|
|
|
|
HeapTuple tup;
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conDesc = heap_open(ConstraintRelationId, AccessShareLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
found = false;
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
Anum_pg_constraint_conname,
|
|
|
|
BTEqualStrategyNumber, F_NAMEEQ,
|
2004-06-10 19:56:03 +02:00
|
|
|
CStringGetDatum(conname));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[1],
|
|
|
|
Anum_pg_constraint_connamespace,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objNamespace));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
|
2002-07-12 20:43:19 +02:00
|
|
|
SnapshotNow, 2, skey);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2002-11-15 03:50:21 +01:00
|
|
|
if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(conscan);
|
2004-06-10 19:56:03 +02:00
|
|
|
heap_close(conDesc, AccessShareLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2004-06-10 19:56:03 +02:00
|
|
|
* Select a nonconflicting name for a new constraint.
|
|
|
|
*
|
|
|
|
* The objective here is to choose a name that is unique within the
|
|
|
|
* specified namespace. Postgres does not require this, but the SQL
|
|
|
|
* spec does, and some apps depend on it. Therefore we avoid choosing
|
|
|
|
* default names that so conflict.
|
|
|
|
*
|
|
|
|
* name1, name2, and label are used the same way as for makeObjectName(),
|
|
|
|
* except that the label can't be NULL; digits will be appended to the label
|
|
|
|
* if needed to create a name that is unique within the specified namespace.
|
2002-07-12 20:43:19 +02:00
|
|
|
*
|
2004-06-10 19:56:03 +02:00
|
|
|
* 'others' can be a list of string names already chosen within the current
|
|
|
|
* command (but not yet reflected into the catalogs); we will not choose
|
|
|
|
* a duplicate of one of these either.
|
2002-07-12 20:43:19 +02:00
|
|
|
*
|
2004-06-10 19:56:03 +02:00
|
|
|
* Note: it is theoretically possible to get a collision anyway, if someone
|
|
|
|
* else chooses the same name concurrently. This is fairly unlikely to be
|
|
|
|
* a problem in practice, especially if one is holding an exclusive lock on
|
|
|
|
* the relation identified by name1.
|
|
|
|
*
|
|
|
|
* Returns a palloc'd string.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
|
|
|
char *
|
2004-06-10 19:56:03 +02:00
|
|
|
ChooseConstraintName(const char *name1, const char *name2,
|
|
|
|
const char *label, Oid namespace,
|
|
|
|
List *others)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
2004-06-10 19:56:03 +02:00
|
|
|
int pass = 0;
|
|
|
|
char *conname = NULL;
|
|
|
|
char modlabel[NAMEDATALEN];
|
2002-07-12 20:43:19 +02:00
|
|
|
Relation conDesc;
|
2004-06-10 19:56:03 +02:00
|
|
|
SysScanDesc conscan;
|
|
|
|
ScanKeyData skey[2];
|
|
|
|
bool found;
|
|
|
|
ListCell *l;
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conDesc = heap_open(ConstraintRelationId, AccessShareLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2004-06-10 19:56:03 +02:00
|
|
|
/* try the unmodified label first */
|
|
|
|
StrNCpy(modlabel, label, sizeof(modlabel));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2004-06-10 19:56:03 +02:00
|
|
|
for (;;)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
2004-06-10 19:56:03 +02:00
|
|
|
conname = makeObjectName(name1, name2, modlabel);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
found = false;
|
|
|
|
|
2004-06-10 19:56:03 +02:00
|
|
|
foreach(l, others)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
2004-06-10 19:56:03 +02:00
|
|
|
if (strcmp((char *) lfirst(l), conname) == 0)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-10 19:56:03 +02:00
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
Anum_pg_constraint_conname,
|
|
|
|
BTEqualStrategyNumber, F_NAMEEQ,
|
|
|
|
CStringGetDatum(conname));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2004-06-10 19:56:03 +02:00
|
|
|
ScanKeyInit(&skey[1],
|
|
|
|
Anum_pg_constraint_connamespace,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(namespace));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
|
2004-06-10 19:56:03 +02:00
|
|
|
SnapshotNow, 2, skey);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2004-06-10 19:56:03 +02:00
|
|
|
found = (HeapTupleIsValid(systable_getnext(conscan)));
|
|
|
|
|
|
|
|
systable_endscan(conscan);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* found a conflict, so try a new name component */
|
|
|
|
pfree(conname);
|
|
|
|
snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_close(conDesc, AccessShareLock);
|
|
|
|
|
|
|
|
return conname;
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a single constraint record.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
RemoveConstraintById(Oid conId)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Relation conDesc;
|
|
|
|
ScanKeyData skey[1];
|
|
|
|
SysScanDesc conscan;
|
|
|
|
HeapTuple tup;
|
|
|
|
Form_pg_constraint con;
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(conId));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
|
2002-07-12 20:43:19 +02:00
|
|
|
SnapshotNow, 1, skey);
|
|
|
|
|
|
|
|
tup = systable_getnext(conscan);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "could not find tuple for constraint %u", conId);
|
2002-07-12 20:43:19 +02:00
|
|
|
con = (Form_pg_constraint) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
/*
|
2002-12-12 21:35:16 +01:00
|
|
|
* Special processing depending on what the constraint is for.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
|
|
|
if (OidIsValid(con->conrelid))
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
|
2002-12-12 21:35:16 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If the constraint is for a relation, open and exclusive-lock the
|
|
|
|
* relation it's for.
|
2002-12-12 21:35:16 +01:00
|
|
|
*/
|
2002-07-12 20:43:19 +02:00
|
|
|
rel = heap_open(con->conrelid, AccessExclusiveLock);
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
|
|
|
if (con->contype == CONSTRAINT_CHECK)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Relation pgrel;
|
|
|
|
HeapTuple relTup;
|
|
|
|
Form_pg_class classForm;
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
pgrel = heap_open(RelationRelationId, RowExclusiveLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
relTup = SearchSysCacheCopy(RELOID,
|
|
|
|
ObjectIdGetDatum(con->conrelid),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(relTup))
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "cache lookup failed for relation %u",
|
2002-07-12 20:43:19 +02:00
|
|
|
con->conrelid);
|
|
|
|
classForm = (Form_pg_class) GETSTRUCT(relTup);
|
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
if (classForm->relchecks == 0) /* should not happen */
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "relation \"%s\" has relchecks = 0",
|
2002-07-12 20:43:19 +02:00
|
|
|
RelationGetRelationName(rel));
|
|
|
|
classForm->relchecks--;
|
|
|
|
|
|
|
|
simple_heap_update(pgrel, &relTup->t_self, relTup);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(pgrel, relTup);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
heap_freetuple(relTup);
|
|
|
|
|
|
|
|
heap_close(pgrel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keep lock on constraint's rel until end of xact */
|
|
|
|
heap_close(rel, NoLock);
|
|
|
|
}
|
2002-11-15 03:50:21 +01:00
|
|
|
else if (OidIsValid(con->contypid))
|
|
|
|
{
|
2002-12-12 21:35:16 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* XXX for now, do nothing special when dropping a domain constraint
|
2002-12-12 21:35:16 +01:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* Probably there should be some form of locking on the domain type,
|
|
|
|
* but we have no such concept at the moment.
|
2002-12-12 21:35:16 +01:00
|
|
|
*/
|
2002-11-15 03:50:21 +01:00
|
|
|
}
|
2002-12-06 06:00:34 +01:00
|
|
|
else
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "constraint %u is not of a known type", conId);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
/* Fry the constraint itself */
|
|
|
|
simple_heap_delete(conDesc, &tup->t_self);
|
|
|
|
|
|
|
|
/* Clean up */
|
|
|
|
systable_endscan(conscan);
|
|
|
|
heap_close(conDesc, RowExclusiveLock);
|
|
|
|
}
|
2005-03-25 22:58:00 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* GetConstraintNameForTrigger
|
|
|
|
* Get the name of the constraint owning a trigger, if any
|
|
|
|
*
|
|
|
|
* Returns a palloc'd string, or NULL if no constraint can be found
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
GetConstraintNameForTrigger(Oid triggerId)
|
|
|
|
{
|
|
|
|
char *result;
|
|
|
|
Oid constraintId = InvalidOid;
|
|
|
|
Relation depRel;
|
|
|
|
Relation conRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We must grovel through pg_depend to find the owning constraint. Perhaps
|
|
|
|
* pg_trigger should have a column for the owning constraint ... but right
|
|
|
|
* now this is not performance-critical code.
|
2005-03-25 22:58:00 +01:00
|
|
|
*/
|
2005-04-14 22:03:27 +02:00
|
|
|
depRel = heap_open(DependRelationId, AccessShareLock);
|
2005-03-25 22:58:00 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
2005-04-14 22:03:27 +02:00
|
|
|
ObjectIdGetDatum(TriggerRelationId));
|
2005-03-25 22:58:00 +01:00
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(triggerId));
|
|
|
|
/* assume we can ignore objsubid for a trigger */
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, true,
|
2005-03-25 22:58:00 +01:00
|
|
|
SnapshotNow, 2, key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
if (foundDep->refclassid == ConstraintRelationId &&
|
2005-03-25 22:58:00 +01:00
|
|
|
foundDep->deptype == DEPENDENCY_INTERNAL)
|
|
|
|
{
|
|
|
|
constraintId = foundDep->refobjid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(depRel, AccessShareLock);
|
|
|
|
|
|
|
|
if (!OidIsValid(constraintId))
|
2005-10-15 04:49:52 +02:00
|
|
|
return NULL; /* no owning constraint found */
|
2005-03-25 22:58:00 +01:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
conRel = heap_open(ConstraintRelationId, AccessShareLock);
|
2005-03-25 22:58:00 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(constraintId));
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
scan = systable_beginscan(conRel, ConstraintOidIndexId, true,
|
2005-03-25 22:58:00 +01:00
|
|
|
SnapshotNow, 1, key);
|
|
|
|
|
|
|
|
tup = systable_getnext(scan);
|
|
|
|
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
|
|
|
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
result = pstrdup(NameStr(con->conname));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* This arguably should be an error, but we'll just return NULL */
|
|
|
|
result = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(conRel, AccessShareLock);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* AlterConstraintNamespaces
|
|
|
|
* Find any constraints belonging to the specified object,
|
|
|
|
* and move them to the specified new namespace.
|
|
|
|
*
|
|
|
|
* isType indicates whether the owning object is a type or a relation.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
|
|
|
|
Oid newNspId, bool isType)
|
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
Relation conRel;
|
|
|
|
ScanKeyData key[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
if (isType)
|
|
|
|
{
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_constraint_contypid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(ownerId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
|
|
|
|
SnapshotNow, 1, key);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_constraint_conrelid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(ownerId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
|
|
|
|
SnapshotNow, 1, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
|
|
|
{
|
|
|
|
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (conform->connamespace == oldNspId)
|
|
|
|
{
|
|
|
|
tup = heap_copytuple(tup);
|
|
|
|
conform = (Form_pg_constraint) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
conform->connamespace = newNspId;
|
|
|
|
|
|
|
|
simple_heap_update(conRel, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(conRel, tup);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: currently, the constraint will not have its own
|
|
|
|
* dependency on the namespace, so we don't need to do
|
|
|
|
* changeDependencyFor().
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(conRel, RowExclusiveLock);
|
|
|
|
}
|