X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Fcatalog%2Fpg_constraint.c;h=81c35100c7b286463081f670d47175a8e040fe11;hb=c1b9ec24efb5d576800fb5163acab6bdefb4391c;hp=a383647a9380302a0f9b8292d7dfa2f84b3fbbf7;hpb=162bd08b3f2e6783d1d75ae79f86fc444d34a28d;p=postgresql diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index a383647a93..81c35100c7 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -3,33 +3,32 @@ * pg_constraint.c * routines to support manipulation of the pg_constraint relation * - * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.25 2005/04/14 20:03:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.47 2009/07/28 02:56:29 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/heapam.h" #include "access/genam.h" -#include "catalog/catalog.h" +#include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_constraint.h" -#include "catalog/pg_depend.h" -#include "catalog/pg_trigger.h" +#include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "commands/defrem.h" -#include "miscadmin.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/rel.h" #include "utils/syscache.h" +#include "utils/tqual.h" /* @@ -50,24 +49,32 @@ CreateConstraintEntry(const char *constraintName, const int16 *constraintKey, int constraintNKeys, Oid domainId, + Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, + const Oid *pfEqOp, + const Oid *ppEqOp, + const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, char foreignMatchType, - Oid indexRelId, Node *conExpr, const char *conBin, - const char *conSrc) + const char *conSrc, + bool conIsLocal, + int conInhCount) { Relation conDesc; Oid conOid; HeapTuple tup; - char nulls[Natts_pg_constraint]; + bool nulls[Natts_pg_constraint]; Datum values[Natts_pg_constraint]; ArrayType *conkeyArray; ArrayType *confkeyArray; + ArrayType *conpfeqopArray; + ArrayType *conppeqopArray; + ArrayType *conffeqopArray; NameData cname; int i; ObjectAddress conobject; @@ -95,21 +102,38 @@ CreateConstraintEntry(const char *constraintName, if (foreignNKeys > 0) { - Datum *confkey; + Datum *fkdatums; - confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum)); + fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum)); for (i = 0; i < foreignNKeys; i++) - confkey[i] = Int16GetDatum(foreignKey[i]); - confkeyArray = construct_array(confkey, foreignNKeys, + fkdatums[i] = Int16GetDatum(foreignKey[i]); + confkeyArray = construct_array(fkdatums, foreignNKeys, INT2OID, 2, true, 's'); + for (i = 0; i < foreignNKeys; i++) + fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]); + conpfeqopArray = construct_array(fkdatums, foreignNKeys, + OIDOID, sizeof(Oid), true, 'i'); + for (i = 0; i < foreignNKeys; i++) + fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]); + conppeqopArray = construct_array(fkdatums, foreignNKeys, + OIDOID, sizeof(Oid), true, 'i'); + for (i = 0; i < foreignNKeys; i++) + fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]); + conffeqopArray = construct_array(fkdatums, foreignNKeys, + OIDOID, sizeof(Oid), true, 'i'); } else + { confkeyArray = NULL; + conpfeqopArray = NULL; + conppeqopArray = NULL; + conffeqopArray = NULL; + } /* initialize nulls and values */ for (i = 0; i < Natts_pg_constraint; i++) { - nulls[i] = ' '; + nulls[i] = false; values[i] = (Datum) NULL; } @@ -120,40 +144,56 @@ CreateConstraintEntry(const char *constraintName, 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_conindid - 1] = ObjectIdGetDatum(indexRelId); 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); + values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal); + values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount); if (conkeyArray) values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray); else - nulls[Anum_pg_constraint_conkey - 1] = 'n'; + nulls[Anum_pg_constraint_conkey - 1] = true; if (confkeyArray) values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray); else - nulls[Anum_pg_constraint_confkey - 1] = 'n'; + nulls[Anum_pg_constraint_confkey - 1] = true; + + if (conpfeqopArray) + values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray); + else + nulls[Anum_pg_constraint_conpfeqop - 1] = true; + + if (conppeqopArray) + values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray); + else + nulls[Anum_pg_constraint_conppeqop - 1] = true; + + if (conffeqopArray) + values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray); + else + nulls[Anum_pg_constraint_conffeqop - 1] = true; /* * initialize the binary form of the check constraint. */ if (conBin) - values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin, - CStringGetDatum(conBin)); + values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin); else - nulls[Anum_pg_constraint_conbin - 1] = 'n'; + nulls[Anum_pg_constraint_conbin - 1] = true; /* * initialize the text form of the check constraint */ if (conSrc) - values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin, - CStringGetDatum(conSrc)); + values[Anum_pg_constraint_consrc - 1] = CStringGetTextDatum(conSrc); else - nulls[Anum_pg_constraint_consrc - 1] = 'n'; + nulls[Anum_pg_constraint_consrc - 1] = true; - tup = heap_formtuple(RelationGetDescr(conDesc), values, nulls); + tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls); conOid = simple_heap_insert(conDesc, tup); @@ -169,8 +209,8 @@ CreateConstraintEntry(const char *constraintName, if (OidIsValid(relId)) { /* - * Register auto dependency from constraint to owning relation, or - * to specific column(s) if any are mentioned. + * Register auto dependency from constraint to owning relation, or to + * specific column(s) if any are mentioned. */ ObjectAddress relobject; @@ -210,8 +250,8 @@ CreateConstraintEntry(const char *constraintName, if (OidIsValid(foreignRelId)) { /* - * Register normal dependency from constraint to foreign relation, - * or to specific column(s) if any are mentioned. + * Register normal dependency from constraint to foreign relation, or + * to specific column(s) if any are mentioned. */ ObjectAddress relobject; @@ -234,11 +274,13 @@ CreateConstraintEntry(const char *constraintName, } } - if (OidIsValid(indexRelId)) + if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN) { /* * Register normal dependency on the unique index that supports a - * foreign-key constraint. + * foreign-key constraint. (Note: for indexes associated with + * unique or primary-key constraints, the dependency runs the other + * way, and is not made here.) */ ObjectAddress relobject; @@ -249,11 +291,41 @@ CreateConstraintEntry(const char *constraintName, recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL); } + if (foreignNKeys > 0) + { + /* + * Register normal dependencies on the equality operators that support + * a foreign-key constraint. If the PK and FK types are the same then + * all three operators for a column are the same; otherwise they are + * different. + */ + ObjectAddress oprobject; + + oprobject.classId = OperatorRelationId; + oprobject.objectSubId = 0; + + for (i = 0; i < foreignNKeys; i++) + { + oprobject.objectId = pfEqOp[i]; + recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); + if (ppEqOp[i] != pfEqOp[i]) + { + oprobject.objectId = ppEqOp[i]; + recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); + } + if (ffEqOp[i] != pfEqOp[i]) + { + oprobject.objectId = ffEqOp[i]; + recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL); + } + } + } + if (conExpr != NULL) { /* - * Register dependencies from constraint to objects mentioned in - * CHECK expression. + * Register dependencies from constraint to objects mentioned in CHECK + * expression. */ recordDependencyOnSingleRelExpr(&conobject, conExpr, relId, DEPENDENCY_NORMAL, @@ -350,7 +422,7 @@ ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, */ char * ChooseConstraintName(const char *name1, const char *name2, - const char *label, Oid namespace, + const char *label, Oid namespaceid, List *others) { int pass = 0; @@ -392,7 +464,7 @@ ChooseConstraintName(const char *name1, const char *name2, ScanKeyInit(&skey[1], Anum_pg_constraint_connamespace, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(namespace)); + ObjectIdGetDatum(namespaceid)); conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true, SnapshotNow, 2, skey); @@ -422,24 +494,16 @@ void RemoveConstraintById(Oid conId) { Relation conDesc; - ScanKeyData skey[1]; - SysScanDesc conscan; HeapTuple tup; Form_pg_constraint con; conDesc = heap_open(ConstraintRelationId, RowExclusiveLock); - ScanKeyInit(&skey[0], - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(conId)); - - conscan = systable_beginscan(conDesc, ConstraintOidIndexId, true, - SnapshotNow, 1, skey); - - tup = systable_getnext(conscan); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "could not find tuple for constraint %u", conId); + tup = SearchSysCache(CONSTROID, + ObjectIdGetDatum(conId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for constraint %u", conId); con = (Form_pg_constraint) GETSTRUCT(tup); /* @@ -450,15 +514,15 @@ RemoveConstraintById(Oid conId) Relation rel; /* - * If the constraint is for a relation, open and exclusive-lock - * the relation it's for. + * If the constraint is for a relation, open and exclusive-lock the + * relation it's for. */ 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. + * 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) { @@ -495,8 +559,7 @@ RemoveConstraintById(Oid conId) else if (OidIsValid(con->contypid)) { /* - * XXX for now, do nothing special when dropping a domain - * constraint + * XXX for now, do nothing special when dropping a domain constraint * * Probably there should be some form of locking on the domain type, * but we have no such concept at the moment. @@ -509,93 +572,133 @@ RemoveConstraintById(Oid conId) simple_heap_delete(conDesc, &tup->t_self); /* Clean up */ - systable_endscan(conscan); + ReleaseSysCache(tup); heap_close(conDesc, RowExclusiveLock); } /* - * GetConstraintNameForTrigger - * Get the name of the constraint owning a trigger, if any + * RenameConstraintById + * Rename a constraint. * - * Returns a palloc'd string, or NULL if no constraint can be found + * Note: this isn't intended to be a user-exposed function; it doesn't check + * permissions etc. Currently this is only invoked when renaming an index + * that is associated with a constraint, but it's made a little more general + * than that with the expectation of someday having ALTER TABLE RENAME + * CONSTRAINT. */ -char * -GetConstraintNameForTrigger(Oid triggerId) +void +RenameConstraintById(Oid conId, const char *newname) { - char *result; - Oid constraintId = InvalidOid; - Relation depRel; - Relation conRel; - ScanKeyData key[2]; - SysScanDesc scan; - HeapTuple tup; + Relation conDesc; + HeapTuple tuple; + Form_pg_constraint con; + + conDesc = heap_open(ConstraintRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopy(CONSTROID, + ObjectIdGetDatum(conId), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for constraint %u", conId); + con = (Form_pg_constraint) GETSTRUCT(tuple); /* - * 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. + * We need to check whether the name is already in use --- note that there + * currently is not a unique index that would catch this. */ - depRel = heap_open(DependRelationId, AccessShareLock); + if (OidIsValid(con->conrelid) && + ConstraintNameIsUsed(CONSTRAINT_RELATION, + con->conrelid, + con->connamespace, + newname)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("constraint \"%s\" for relation \"%s\" already exists", + newname, get_rel_name(con->conrelid)))); + if (OidIsValid(con->contypid) && + ConstraintNameIsUsed(CONSTRAINT_DOMAIN, + con->contypid, + con->connamespace, + newname)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("constraint \"%s\" for domain \"%s\" already exists", + newname, format_type_be(con->contypid)))); + + /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ + namestrcpy(&(con->conname), newname); + + simple_heap_update(conDesc, &tuple->t_self, tuple); + + /* update the system catalog indexes */ + CatalogUpdateIndexes(conDesc, tuple); + + heap_freetuple(tuple); + heap_close(conDesc, RowExclusiveLock); +} - ScanKeyInit(&key[0], - Anum_pg_depend_classid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(TriggerRelationId)); - ScanKeyInit(&key[1], - Anum_pg_depend_objid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(triggerId)); - /* assume we can ignore objsubid for a trigger */ +/* + * 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) +{ + Relation conRel; + ScanKeyData key[1]; + SysScanDesc scan; + HeapTuple tup; - scan = systable_beginscan(depRel, DependDependerIndexId, true, - SnapshotNow, 2, key); + conRel = heap_open(ConstraintRelationId, RowExclusiveLock); - while (HeapTupleIsValid(tup = systable_getnext(scan))) + if (isType) { - Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); + ScanKeyInit(&key[0], + Anum_pg_constraint_contypid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ownerId)); - if (foundDep->refclassid == ConstraintRelationId && - foundDep->deptype == DEPENDENCY_INTERNAL) - { - constraintId = foundDep->refobjid; - break; - } + scan = systable_beginscan(conRel, ConstraintTypidIndexId, true, + SnapshotNow, 1, key); } + else + { + ScanKeyInit(&key[0], + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ownerId)); - systable_endscan(scan); - - heap_close(depRel, AccessShareLock); - - if (!OidIsValid(constraintId)) - return NULL; /* no owning constraint found */ - - conRel = heap_open(ConstraintRelationId, AccessShareLock); + scan = systable_beginscan(conRel, ConstraintRelidIndexId, true, + SnapshotNow, 1, key); + } - ScanKeyInit(&key[0], - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(constraintId)); + while (HeapTupleIsValid((tup = systable_getnext(scan)))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); - scan = systable_beginscan(conRel, ConstraintOidIndexId, true, - SnapshotNow, 1, key); + if (conform->connamespace == oldNspId) + { + tup = heap_copytuple(tup); + conform = (Form_pg_constraint) GETSTRUCT(tup); - tup = systable_getnext(scan); + conform->connamespace = newNspId; - if (HeapTupleIsValid(tup)) - { - Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup); + simple_heap_update(conRel, &tup->t_self, tup); + CatalogUpdateIndexes(conRel, tup); - result = pstrdup(NameStr(con->conname)); - } - else - { - /* This arguably should be an error, but we'll just return NULL */ - result = NULL; + /* + * 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, AccessShareLock); - - return result; + heap_close(conRel, RowExclusiveLock); }