Header And Logo

PostgreSQL
| The world's most advanced open source database.

pg_constraint.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pg_constraint.c
00004  *    routines to support manipulation of the pg_constraint relation
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/catalog/pg_constraint.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include "access/genam.h"
00018 #include "access/heapam.h"
00019 #include "access/htup_details.h"
00020 #include "catalog/dependency.h"
00021 #include "catalog/indexing.h"
00022 #include "catalog/objectaccess.h"
00023 #include "catalog/pg_constraint.h"
00024 #include "catalog/pg_operator.h"
00025 #include "catalog/pg_type.h"
00026 #include "commands/defrem.h"
00027 #include "utils/array.h"
00028 #include "utils/builtins.h"
00029 #include "utils/fmgroids.h"
00030 #include "utils/lsyscache.h"
00031 #include "utils/rel.h"
00032 #include "utils/syscache.h"
00033 #include "utils/tqual.h"
00034 
00035 
00036 /*
00037  * CreateConstraintEntry
00038  *  Create a constraint table entry.
00039  *
00040  * Subsidiary records (such as triggers or indexes to implement the
00041  * constraint) are *not* created here.  But we do make dependency links
00042  * from the constraint to the things it depends on.
00043  */
00044 Oid
00045 CreateConstraintEntry(const char *constraintName,
00046                       Oid constraintNamespace,
00047                       char constraintType,
00048                       bool isDeferrable,
00049                       bool isDeferred,
00050                       bool isValidated,
00051                       Oid relId,
00052                       const int16 *constraintKey,
00053                       int constraintNKeys,
00054                       Oid domainId,
00055                       Oid indexRelId,
00056                       Oid foreignRelId,
00057                       const int16 *foreignKey,
00058                       const Oid *pfEqOp,
00059                       const Oid *ppEqOp,
00060                       const Oid *ffEqOp,
00061                       int foreignNKeys,
00062                       char foreignUpdateType,
00063                       char foreignDeleteType,
00064                       char foreignMatchType,
00065                       const Oid *exclOp,
00066                       Node *conExpr,
00067                       const char *conBin,
00068                       const char *conSrc,
00069                       bool conIsLocal,
00070                       int conInhCount,
00071                       bool conNoInherit,
00072                       bool is_internal)
00073 {
00074     Relation    conDesc;
00075     Oid         conOid;
00076     HeapTuple   tup;
00077     bool        nulls[Natts_pg_constraint];
00078     Datum       values[Natts_pg_constraint];
00079     ArrayType  *conkeyArray;
00080     ArrayType  *confkeyArray;
00081     ArrayType  *conpfeqopArray;
00082     ArrayType  *conppeqopArray;
00083     ArrayType  *conffeqopArray;
00084     ArrayType  *conexclopArray;
00085     NameData    cname;
00086     int         i;
00087     ObjectAddress conobject;
00088 
00089     conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
00090 
00091     Assert(constraintName);
00092     namestrcpy(&cname, constraintName);
00093 
00094     /*
00095      * Convert C arrays into Postgres arrays.
00096      */
00097     if (constraintNKeys > 0)
00098     {
00099         Datum      *conkey;
00100 
00101         conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
00102         for (i = 0; i < constraintNKeys; i++)
00103             conkey[i] = Int16GetDatum(constraintKey[i]);
00104         conkeyArray = construct_array(conkey, constraintNKeys,
00105                                       INT2OID, 2, true, 's');
00106     }
00107     else
00108         conkeyArray = NULL;
00109 
00110     if (foreignNKeys > 0)
00111     {
00112         Datum      *fkdatums;
00113 
00114         fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
00115         for (i = 0; i < foreignNKeys; i++)
00116             fkdatums[i] = Int16GetDatum(foreignKey[i]);
00117         confkeyArray = construct_array(fkdatums, foreignNKeys,
00118                                        INT2OID, 2, true, 's');
00119         for (i = 0; i < foreignNKeys; i++)
00120             fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
00121         conpfeqopArray = construct_array(fkdatums, foreignNKeys,
00122                                          OIDOID, sizeof(Oid), true, 'i');
00123         for (i = 0; i < foreignNKeys; i++)
00124             fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
00125         conppeqopArray = construct_array(fkdatums, foreignNKeys,
00126                                          OIDOID, sizeof(Oid), true, 'i');
00127         for (i = 0; i < foreignNKeys; i++)
00128             fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
00129         conffeqopArray = construct_array(fkdatums, foreignNKeys,
00130                                          OIDOID, sizeof(Oid), true, 'i');
00131     }
00132     else
00133     {
00134         confkeyArray = NULL;
00135         conpfeqopArray = NULL;
00136         conppeqopArray = NULL;
00137         conffeqopArray = NULL;
00138     }
00139 
00140     if (exclOp != NULL)
00141     {
00142         Datum      *opdatums;
00143 
00144         opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
00145         for (i = 0; i < constraintNKeys; i++)
00146             opdatums[i] = ObjectIdGetDatum(exclOp[i]);
00147         conexclopArray = construct_array(opdatums, constraintNKeys,
00148                                          OIDOID, sizeof(Oid), true, 'i');
00149     }
00150     else
00151         conexclopArray = NULL;
00152 
00153     /* initialize nulls and values */
00154     for (i = 0; i < Natts_pg_constraint; i++)
00155     {
00156         nulls[i] = false;
00157         values[i] = (Datum) NULL;
00158     }
00159 
00160     values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
00161     values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
00162     values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
00163     values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
00164     values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
00165     values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
00166     values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
00167     values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
00168     values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
00169     values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
00170     values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
00171     values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
00172     values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
00173     values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
00174     values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
00175     values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
00176 
00177     if (conkeyArray)
00178         values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
00179     else
00180         nulls[Anum_pg_constraint_conkey - 1] = true;
00181 
00182     if (confkeyArray)
00183         values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
00184     else
00185         nulls[Anum_pg_constraint_confkey - 1] = true;
00186 
00187     if (conpfeqopArray)
00188         values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
00189     else
00190         nulls[Anum_pg_constraint_conpfeqop - 1] = true;
00191 
00192     if (conppeqopArray)
00193         values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
00194     else
00195         nulls[Anum_pg_constraint_conppeqop - 1] = true;
00196 
00197     if (conffeqopArray)
00198         values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
00199     else
00200         nulls[Anum_pg_constraint_conffeqop - 1] = true;
00201 
00202     if (conexclopArray)
00203         values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
00204     else
00205         nulls[Anum_pg_constraint_conexclop - 1] = true;
00206 
00207     /*
00208      * initialize the binary form of the check constraint.
00209      */
00210     if (conBin)
00211         values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
00212     else
00213         nulls[Anum_pg_constraint_conbin - 1] = true;
00214 
00215     /*
00216      * initialize the text form of the check constraint
00217      */
00218     if (conSrc)
00219         values[Anum_pg_constraint_consrc - 1] = CStringGetTextDatum(conSrc);
00220     else
00221         nulls[Anum_pg_constraint_consrc - 1] = true;
00222 
00223     tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
00224 
00225     conOid = simple_heap_insert(conDesc, tup);
00226 
00227     /* update catalog indexes */
00228     CatalogUpdateIndexes(conDesc, tup);
00229 
00230     conobject.classId = ConstraintRelationId;
00231     conobject.objectId = conOid;
00232     conobject.objectSubId = 0;
00233 
00234     heap_close(conDesc, RowExclusiveLock);
00235 
00236     if (OidIsValid(relId))
00237     {
00238         /*
00239          * Register auto dependency from constraint to owning relation, or to
00240          * specific column(s) if any are mentioned.
00241          */
00242         ObjectAddress relobject;
00243 
00244         relobject.classId = RelationRelationId;
00245         relobject.objectId = relId;
00246         if (constraintNKeys > 0)
00247         {
00248             for (i = 0; i < constraintNKeys; i++)
00249             {
00250                 relobject.objectSubId = constraintKey[i];
00251 
00252                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
00253             }
00254         }
00255         else
00256         {
00257             relobject.objectSubId = 0;
00258 
00259             recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
00260         }
00261     }
00262 
00263     if (OidIsValid(domainId))
00264     {
00265         /*
00266          * Register auto dependency from constraint to owning domain
00267          */
00268         ObjectAddress domobject;
00269 
00270         domobject.classId = TypeRelationId;
00271         domobject.objectId = domainId;
00272         domobject.objectSubId = 0;
00273 
00274         recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
00275     }
00276 
00277     if (OidIsValid(foreignRelId))
00278     {
00279         /*
00280          * Register normal dependency from constraint to foreign relation, or
00281          * to specific column(s) if any are mentioned.
00282          */
00283         ObjectAddress relobject;
00284 
00285         relobject.classId = RelationRelationId;
00286         relobject.objectId = foreignRelId;
00287         if (foreignNKeys > 0)
00288         {
00289             for (i = 0; i < foreignNKeys; i++)
00290             {
00291                 relobject.objectSubId = foreignKey[i];
00292 
00293                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
00294             }
00295         }
00296         else
00297         {
00298             relobject.objectSubId = 0;
00299 
00300             recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
00301         }
00302     }
00303 
00304     if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN)
00305     {
00306         /*
00307          * Register normal dependency on the unique index that supports a
00308          * foreign-key constraint.  (Note: for indexes associated with unique
00309          * or primary-key constraints, the dependency runs the other way, and
00310          * is not made here.)
00311          */
00312         ObjectAddress relobject;
00313 
00314         relobject.classId = RelationRelationId;
00315         relobject.objectId = indexRelId;
00316         relobject.objectSubId = 0;
00317 
00318         recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
00319     }
00320 
00321     if (foreignNKeys > 0)
00322     {
00323         /*
00324          * Register normal dependencies on the equality operators that support
00325          * a foreign-key constraint.  If the PK and FK types are the same then
00326          * all three operators for a column are the same; otherwise they are
00327          * different.
00328          */
00329         ObjectAddress oprobject;
00330 
00331         oprobject.classId = OperatorRelationId;
00332         oprobject.objectSubId = 0;
00333 
00334         for (i = 0; i < foreignNKeys; i++)
00335         {
00336             oprobject.objectId = pfEqOp[i];
00337             recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
00338             if (ppEqOp[i] != pfEqOp[i])
00339             {
00340                 oprobject.objectId = ppEqOp[i];
00341                 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
00342             }
00343             if (ffEqOp[i] != pfEqOp[i])
00344             {
00345                 oprobject.objectId = ffEqOp[i];
00346                 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
00347             }
00348         }
00349     }
00350 
00351     /*
00352      * We don't bother to register dependencies on the exclusion operators of
00353      * an exclusion constraint.  We assume they are members of the opclass
00354      * supporting the index, so there's an indirect dependency via that. (This
00355      * would be pretty dicey for cross-type operators, but exclusion operators
00356      * can never be cross-type.)
00357      */
00358 
00359     if (conExpr != NULL)
00360     {
00361         /*
00362          * Register dependencies from constraint to objects mentioned in CHECK
00363          * expression.
00364          */
00365         recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
00366                                         DEPENDENCY_NORMAL,
00367                                         DEPENDENCY_NORMAL);
00368     }
00369 
00370     /* Post creation hook for new constraint */
00371     InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
00372                                   is_internal);
00373 
00374     return conOid;
00375 }
00376 
00377 
00378 /*
00379  * Test whether given name is currently used as a constraint name
00380  * for the given object (relation or domain).
00381  *
00382  * This is used to decide whether to accept a user-specified constraint name.
00383  * It is deliberately not the same test as ChooseConstraintName uses to decide
00384  * whether an auto-generated name is OK: here, we will allow it unless there
00385  * is an identical constraint name in use *on the same object*.
00386  *
00387  * NB: Caller should hold exclusive lock on the given object, else
00388  * this test can be fooled by concurrent additions.
00389  */
00390 bool
00391 ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
00392                      Oid objNamespace, const char *conname)
00393 {
00394     bool        found;
00395     Relation    conDesc;
00396     SysScanDesc conscan;
00397     ScanKeyData skey[2];
00398     HeapTuple   tup;
00399 
00400     conDesc = heap_open(ConstraintRelationId, AccessShareLock);
00401 
00402     found = false;
00403 
00404     ScanKeyInit(&skey[0],
00405                 Anum_pg_constraint_conname,
00406                 BTEqualStrategyNumber, F_NAMEEQ,
00407                 CStringGetDatum(conname));
00408 
00409     ScanKeyInit(&skey[1],
00410                 Anum_pg_constraint_connamespace,
00411                 BTEqualStrategyNumber, F_OIDEQ,
00412                 ObjectIdGetDatum(objNamespace));
00413 
00414     conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
00415                                  SnapshotNow, 2, skey);
00416 
00417     while (HeapTupleIsValid(tup = systable_getnext(conscan)))
00418     {
00419         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
00420 
00421         if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
00422         {
00423             found = true;
00424             break;
00425         }
00426         else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
00427         {
00428             found = true;
00429             break;
00430         }
00431     }
00432 
00433     systable_endscan(conscan);
00434     heap_close(conDesc, AccessShareLock);
00435 
00436     return found;
00437 }
00438 
00439 /*
00440  * Select a nonconflicting name for a new constraint.
00441  *
00442  * The objective here is to choose a name that is unique within the
00443  * specified namespace.  Postgres does not require this, but the SQL
00444  * spec does, and some apps depend on it.  Therefore we avoid choosing
00445  * default names that so conflict.
00446  *
00447  * name1, name2, and label are used the same way as for makeObjectName(),
00448  * except that the label can't be NULL; digits will be appended to the label
00449  * if needed to create a name that is unique within the specified namespace.
00450  *
00451  * 'others' can be a list of string names already chosen within the current
00452  * command (but not yet reflected into the catalogs); we will not choose
00453  * a duplicate of one of these either.
00454  *
00455  * Note: it is theoretically possible to get a collision anyway, if someone
00456  * else chooses the same name concurrently.  This is fairly unlikely to be
00457  * a problem in practice, especially if one is holding an exclusive lock on
00458  * the relation identified by name1.
00459  *
00460  * Returns a palloc'd string.
00461  */
00462 char *
00463 ChooseConstraintName(const char *name1, const char *name2,
00464                      const char *label, Oid namespaceid,
00465                      List *others)
00466 {
00467     int         pass = 0;
00468     char       *conname = NULL;
00469     char        modlabel[NAMEDATALEN];
00470     Relation    conDesc;
00471     SysScanDesc conscan;
00472     ScanKeyData skey[2];
00473     bool        found;
00474     ListCell   *l;
00475 
00476     conDesc = heap_open(ConstraintRelationId, AccessShareLock);
00477 
00478     /* try the unmodified label first */
00479     StrNCpy(modlabel, label, sizeof(modlabel));
00480 
00481     for (;;)
00482     {
00483         conname = makeObjectName(name1, name2, modlabel);
00484 
00485         found = false;
00486 
00487         foreach(l, others)
00488         {
00489             if (strcmp((char *) lfirst(l), conname) == 0)
00490             {
00491                 found = true;
00492                 break;
00493             }
00494         }
00495 
00496         if (!found)
00497         {
00498             ScanKeyInit(&skey[0],
00499                         Anum_pg_constraint_conname,
00500                         BTEqualStrategyNumber, F_NAMEEQ,
00501                         CStringGetDatum(conname));
00502 
00503             ScanKeyInit(&skey[1],
00504                         Anum_pg_constraint_connamespace,
00505                         BTEqualStrategyNumber, F_OIDEQ,
00506                         ObjectIdGetDatum(namespaceid));
00507 
00508             conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
00509                                          SnapshotNow, 2, skey);
00510 
00511             found = (HeapTupleIsValid(systable_getnext(conscan)));
00512 
00513             systable_endscan(conscan);
00514         }
00515 
00516         if (!found)
00517             break;
00518 
00519         /* found a conflict, so try a new name component */
00520         pfree(conname);
00521         snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
00522     }
00523 
00524     heap_close(conDesc, AccessShareLock);
00525 
00526     return conname;
00527 }
00528 
00529 /*
00530  * Delete a single constraint record.
00531  */
00532 void
00533 RemoveConstraintById(Oid conId)
00534 {
00535     Relation    conDesc;
00536     HeapTuple   tup;
00537     Form_pg_constraint con;
00538 
00539     conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
00540 
00541     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
00542     if (!HeapTupleIsValid(tup)) /* should not happen */
00543         elog(ERROR, "cache lookup failed for constraint %u", conId);
00544     con = (Form_pg_constraint) GETSTRUCT(tup);
00545 
00546     /*
00547      * Special processing depending on what the constraint is for.
00548      */
00549     if (OidIsValid(con->conrelid))
00550     {
00551         Relation    rel;
00552 
00553         /*
00554          * If the constraint is for a relation, open and exclusive-lock the
00555          * relation it's for.
00556          */
00557         rel = heap_open(con->conrelid, AccessExclusiveLock);
00558 
00559         /*
00560          * We need to update the relcheck count if it is a check constraint
00561          * being dropped.  This update will force backends to rebuild relcache
00562          * entries when we commit.
00563          */
00564         if (con->contype == CONSTRAINT_CHECK)
00565         {
00566             Relation    pgrel;
00567             HeapTuple   relTup;
00568             Form_pg_class classForm;
00569 
00570             pgrel = heap_open(RelationRelationId, RowExclusiveLock);
00571             relTup = SearchSysCacheCopy1(RELOID,
00572                                          ObjectIdGetDatum(con->conrelid));
00573             if (!HeapTupleIsValid(relTup))
00574                 elog(ERROR, "cache lookup failed for relation %u",
00575                      con->conrelid);
00576             classForm = (Form_pg_class) GETSTRUCT(relTup);
00577 
00578             if (classForm->relchecks == 0)      /* should not happen */
00579                 elog(ERROR, "relation \"%s\" has relchecks = 0",
00580                      RelationGetRelationName(rel));
00581             classForm->relchecks--;
00582 
00583             simple_heap_update(pgrel, &relTup->t_self, relTup);
00584 
00585             CatalogUpdateIndexes(pgrel, relTup);
00586 
00587             heap_freetuple(relTup);
00588 
00589             heap_close(pgrel, RowExclusiveLock);
00590         }
00591 
00592         /* Keep lock on constraint's rel until end of xact */
00593         heap_close(rel, NoLock);
00594     }
00595     else if (OidIsValid(con->contypid))
00596     {
00597         /*
00598          * XXX for now, do nothing special when dropping a domain constraint
00599          *
00600          * Probably there should be some form of locking on the domain type,
00601          * but we have no such concept at the moment.
00602          */
00603     }
00604     else
00605         elog(ERROR, "constraint %u is not of a known type", conId);
00606 
00607     /* Fry the constraint itself */
00608     simple_heap_delete(conDesc, &tup->t_self);
00609 
00610     /* Clean up */
00611     ReleaseSysCache(tup);
00612     heap_close(conDesc, RowExclusiveLock);
00613 }
00614 
00615 /*
00616  * RenameConstraintById
00617  *      Rename a constraint.
00618  *
00619  * Note: this isn't intended to be a user-exposed function; it doesn't check
00620  * permissions etc.  Currently this is only invoked when renaming an index
00621  * that is associated with a constraint, but it's made a little more general
00622  * than that with the expectation of someday having ALTER TABLE RENAME
00623  * CONSTRAINT.
00624  */
00625 void
00626 RenameConstraintById(Oid conId, const char *newname)
00627 {
00628     Relation    conDesc;
00629     HeapTuple   tuple;
00630     Form_pg_constraint con;
00631 
00632     conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
00633 
00634     tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
00635     if (!HeapTupleIsValid(tuple))
00636         elog(ERROR, "cache lookup failed for constraint %u", conId);
00637     con = (Form_pg_constraint) GETSTRUCT(tuple);
00638 
00639     /*
00640      * We need to check whether the name is already in use --- note that there
00641      * currently is not a unique index that would catch this.
00642      */
00643     if (OidIsValid(con->conrelid) &&
00644         ConstraintNameIsUsed(CONSTRAINT_RELATION,
00645                              con->conrelid,
00646                              con->connamespace,
00647                              newname))
00648         ereport(ERROR,
00649                 (errcode(ERRCODE_DUPLICATE_OBJECT),
00650                errmsg("constraint \"%s\" for relation \"%s\" already exists",
00651                       newname, get_rel_name(con->conrelid))));
00652     if (OidIsValid(con->contypid) &&
00653         ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
00654                              con->contypid,
00655                              con->connamespace,
00656                              newname))
00657         ereport(ERROR,
00658                 (errcode(ERRCODE_DUPLICATE_OBJECT),
00659                  errmsg("constraint \"%s\" for domain %s already exists",
00660                         newname, format_type_be(con->contypid))));
00661 
00662     /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
00663     namestrcpy(&(con->conname), newname);
00664 
00665     simple_heap_update(conDesc, &tuple->t_self, tuple);
00666 
00667     /* update the system catalog indexes */
00668     CatalogUpdateIndexes(conDesc, tuple);
00669 
00670     InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
00671 
00672     heap_freetuple(tuple);
00673     heap_close(conDesc, RowExclusiveLock);
00674 }
00675 
00676 /*
00677  * AlterConstraintNamespaces
00678  *      Find any constraints belonging to the specified object,
00679  *      and move them to the specified new namespace.
00680  *
00681  * isType indicates whether the owning object is a type or a relation.
00682  */
00683 void
00684 AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
00685                           Oid newNspId, bool isType, ObjectAddresses *objsMoved)
00686 {
00687     Relation    conRel;
00688     ScanKeyData key[1];
00689     SysScanDesc scan;
00690     HeapTuple   tup;
00691 
00692     conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
00693 
00694     if (isType)
00695     {
00696         ScanKeyInit(&key[0],
00697                     Anum_pg_constraint_contypid,
00698                     BTEqualStrategyNumber, F_OIDEQ,
00699                     ObjectIdGetDatum(ownerId));
00700 
00701         scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
00702                                   SnapshotNow, 1, key);
00703     }
00704     else
00705     {
00706         ScanKeyInit(&key[0],
00707                     Anum_pg_constraint_conrelid,
00708                     BTEqualStrategyNumber, F_OIDEQ,
00709                     ObjectIdGetDatum(ownerId));
00710 
00711         scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
00712                                   SnapshotNow, 1, key);
00713     }
00714 
00715     while (HeapTupleIsValid((tup = systable_getnext(scan))))
00716     {
00717         Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
00718         ObjectAddress   thisobj;
00719 
00720         thisobj.classId = ConstraintRelationId;
00721         thisobj.objectId = HeapTupleGetOid(tup);
00722         thisobj.objectSubId = 0;
00723 
00724         if (object_address_present(&thisobj, objsMoved))
00725             continue;
00726 
00727         if (conform->connamespace == oldNspId)
00728         {
00729             tup = heap_copytuple(tup);
00730             conform = (Form_pg_constraint) GETSTRUCT(tup);
00731 
00732             conform->connamespace = newNspId;
00733 
00734             simple_heap_update(conRel, &tup->t_self, tup);
00735             CatalogUpdateIndexes(conRel, tup);
00736 
00737             /*
00738              * Note: currently, the constraint will not have its own
00739              * dependency on the namespace, so we don't need to do
00740              * changeDependencyFor().
00741              */
00742         }
00743 
00744         InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
00745 
00746         add_exact_object_address(&thisobj, objsMoved);
00747     }
00748 
00749     systable_endscan(scan);
00750 
00751     heap_close(conRel, RowExclusiveLock);
00752 }
00753 
00754 /*
00755  * get_relation_constraint_oid
00756  *      Find a constraint on the specified relation with the specified name.
00757  *      Returns constraint's OID.
00758  */
00759 Oid
00760 get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
00761 {
00762     Relation    pg_constraint;
00763     HeapTuple   tuple;
00764     SysScanDesc scan;
00765     ScanKeyData skey[1];
00766     Oid         conOid = InvalidOid;
00767 
00768     /*
00769      * Fetch the constraint tuple from pg_constraint.  There may be more than
00770      * one match, because constraints are not required to have unique names;
00771      * if so, error out.
00772      */
00773     pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
00774 
00775     ScanKeyInit(&skey[0],
00776                 Anum_pg_constraint_conrelid,
00777                 BTEqualStrategyNumber, F_OIDEQ,
00778                 ObjectIdGetDatum(relid));
00779 
00780     scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
00781                               SnapshotNow, 1, skey);
00782 
00783     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
00784     {
00785         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
00786 
00787         if (strcmp(NameStr(con->conname), conname) == 0)
00788         {
00789             if (OidIsValid(conOid))
00790                 ereport(ERROR,
00791                         (errcode(ERRCODE_DUPLICATE_OBJECT),
00792                  errmsg("table \"%s\" has multiple constraints named \"%s\"",
00793                         get_rel_name(relid), conname)));
00794             conOid = HeapTupleGetOid(tuple);
00795         }
00796     }
00797 
00798     systable_endscan(scan);
00799 
00800     /* If no such constraint exists, complain */
00801     if (!OidIsValid(conOid) && !missing_ok)
00802         ereport(ERROR,
00803                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00804                  errmsg("constraint \"%s\" for table \"%s\" does not exist",
00805                         conname, get_rel_name(relid))));
00806 
00807     heap_close(pg_constraint, AccessShareLock);
00808 
00809     return conOid;
00810 }
00811 
00812 /*
00813  * get_domain_constraint_oid
00814  *      Find a constraint on the specified domain with the specified name.
00815  *      Returns constraint's OID.
00816  */
00817 Oid
00818 get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
00819 {
00820     Relation    pg_constraint;
00821     HeapTuple   tuple;
00822     SysScanDesc scan;
00823     ScanKeyData skey[1];
00824     Oid         conOid = InvalidOid;
00825 
00826     /*
00827      * Fetch the constraint tuple from pg_constraint.  There may be more than
00828      * one match, because constraints are not required to have unique names;
00829      * if so, error out.
00830      */
00831     pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
00832 
00833     ScanKeyInit(&skey[0],
00834                 Anum_pg_constraint_contypid,
00835                 BTEqualStrategyNumber, F_OIDEQ,
00836                 ObjectIdGetDatum(typid));
00837 
00838     scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
00839                               SnapshotNow, 1, skey);
00840 
00841     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
00842     {
00843         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
00844 
00845         if (strcmp(NameStr(con->conname), conname) == 0)
00846         {
00847             if (OidIsValid(conOid))
00848                 ereport(ERROR,
00849                         (errcode(ERRCODE_DUPLICATE_OBJECT),
00850                 errmsg("domain \"%s\" has multiple constraints named \"%s\"",
00851                        format_type_be(typid), conname)));
00852             conOid = HeapTupleGetOid(tuple);
00853         }
00854     }
00855 
00856     systable_endscan(scan);
00857 
00858     /* If no such constraint exists, complain */
00859     if (!OidIsValid(conOid) && !missing_ok)
00860         ereport(ERROR,
00861                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00862                  errmsg("constraint \"%s\" for domain \"%s\" does not exist",
00863                         conname, format_type_be(typid))));
00864 
00865     heap_close(pg_constraint, AccessShareLock);
00866 
00867     return conOid;
00868 }
00869 
00870 /*
00871  * Determine whether a relation can be proven functionally dependent on
00872  * a set of grouping columns.  If so, return TRUE and add the pg_constraint
00873  * OIDs of the constraints needed for the proof to the *constraintDeps list.
00874  *
00875  * grouping_columns is a list of grouping expressions, in which columns of
00876  * the rel of interest are Vars with the indicated varno/varlevelsup.
00877  *
00878  * Currently we only check to see if the rel has a primary key that is a
00879  * subset of the grouping_columns.  We could also use plain unique constraints
00880  * if all their columns are known not null, but there's a problem: we need
00881  * to be able to represent the not-null-ness as part of the constraints added
00882  * to *constraintDeps.  FIXME whenever not-null constraints get represented
00883  * in pg_constraint.
00884  */
00885 bool
00886 check_functional_grouping(Oid relid,
00887                           Index varno, Index varlevelsup,
00888                           List *grouping_columns,
00889                           List **constraintDeps)
00890 {
00891     bool        result = false;
00892     Relation    pg_constraint;
00893     HeapTuple   tuple;
00894     SysScanDesc scan;
00895     ScanKeyData skey[1];
00896 
00897     /* Scan pg_constraint for constraints of the target rel */
00898     pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
00899 
00900     ScanKeyInit(&skey[0],
00901                 Anum_pg_constraint_conrelid,
00902                 BTEqualStrategyNumber, F_OIDEQ,
00903                 ObjectIdGetDatum(relid));
00904 
00905     scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
00906                               SnapshotNow, 1, skey);
00907 
00908     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
00909     {
00910         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
00911         Datum       adatum;
00912         bool        isNull;
00913         ArrayType  *arr;
00914         int16      *attnums;
00915         int         numkeys;
00916         int         i;
00917         bool        found_col;
00918 
00919         /* Only PK constraints are of interest for now, see comment above */
00920         if (con->contype != CONSTRAINT_PRIMARY)
00921             continue;
00922         /* Constraint must be non-deferrable */
00923         if (con->condeferrable)
00924             continue;
00925 
00926         /* Extract the conkey array, ie, attnums of PK's columns */
00927         adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
00928                               RelationGetDescr(pg_constraint), &isNull);
00929         if (isNull)
00930             elog(ERROR, "null conkey for constraint %u",
00931                  HeapTupleGetOid(tuple));
00932         arr = DatumGetArrayTypeP(adatum);       /* ensure not toasted */
00933         numkeys = ARR_DIMS(arr)[0];
00934         if (ARR_NDIM(arr) != 1 ||
00935             numkeys < 0 ||
00936             ARR_HASNULL(arr) ||
00937             ARR_ELEMTYPE(arr) != INT2OID)
00938             elog(ERROR, "conkey is not a 1-D smallint array");
00939         attnums = (int16 *) ARR_DATA_PTR(arr);
00940 
00941         found_col = false;
00942         for (i = 0; i < numkeys; i++)
00943         {
00944             AttrNumber  attnum = attnums[i];
00945             ListCell   *gl;
00946 
00947             found_col = false;
00948             foreach(gl, grouping_columns)
00949             {
00950                 Var        *gvar = (Var *) lfirst(gl);
00951 
00952                 if (IsA(gvar, Var) &&
00953                     gvar->varno == varno &&
00954                     gvar->varlevelsup == varlevelsup &&
00955                     gvar->varattno == attnum)
00956                 {
00957                     found_col = true;
00958                     break;
00959                 }
00960             }
00961             if (!found_col)
00962                 break;
00963         }
00964 
00965         if (found_col)
00966         {
00967             /* The PK is a subset of grouping_columns, so we win */
00968             *constraintDeps = lappend_oid(*constraintDeps,
00969                                           HeapTupleGetOid(tuple));
00970             result = true;
00971             break;
00972         }
00973     }
00974 
00975     systable_endscan(scan);
00976 
00977     heap_close(pg_constraint, AccessShareLock);
00978 
00979     return result;
00980 }