LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_constraint.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 298 316 94.3 %
Date: 2017-09-29 13:40:31 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_constraint.c
       4             :  *    routines to support manipulation of the pg_constraint relation
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/catalog/pg_constraint.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/genam.h"
      18             : #include "access/heapam.h"
      19             : #include "access/htup_details.h"
      20             : #include "access/sysattr.h"
      21             : #include "catalog/dependency.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/objectaccess.h"
      24             : #include "catalog/pg_constraint.h"
      25             : #include "catalog/pg_constraint_fn.h"
      26             : #include "catalog/pg_operator.h"
      27             : #include "catalog/pg_type.h"
      28             : #include "commands/defrem.h"
      29             : #include "utils/array.h"
      30             : #include "utils/builtins.h"
      31             : #include "utils/fmgroids.h"
      32             : #include "utils/lsyscache.h"
      33             : #include "utils/rel.h"
      34             : #include "utils/syscache.h"
      35             : #include "utils/tqual.h"
      36             : 
      37             : 
      38             : /*
      39             :  * CreateConstraintEntry
      40             :  *  Create a constraint table entry.
      41             :  *
      42             :  * Subsidiary records (such as triggers or indexes to implement the
      43             :  * constraint) are *not* created here.  But we do make dependency links
      44             :  * from the constraint to the things it depends on.
      45             :  *
      46             :  * The new constraint's OID is returned.
      47             :  */
      48             : Oid
      49         627 : CreateConstraintEntry(const char *constraintName,
      50             :                       Oid constraintNamespace,
      51             :                       char constraintType,
      52             :                       bool isDeferrable,
      53             :                       bool isDeferred,
      54             :                       bool isValidated,
      55             :                       Oid relId,
      56             :                       const int16 *constraintKey,
      57             :                       int constraintNKeys,
      58             :                       Oid domainId,
      59             :                       Oid indexRelId,
      60             :                       Oid foreignRelId,
      61             :                       const int16 *foreignKey,
      62             :                       const Oid *pfEqOp,
      63             :                       const Oid *ppEqOp,
      64             :                       const Oid *ffEqOp,
      65             :                       int foreignNKeys,
      66             :                       char foreignUpdateType,
      67             :                       char foreignDeleteType,
      68             :                       char foreignMatchType,
      69             :                       const Oid *exclOp,
      70             :                       Node *conExpr,
      71             :                       const char *conBin,
      72             :                       const char *conSrc,
      73             :                       bool conIsLocal,
      74             :                       int conInhCount,
      75             :                       bool conNoInherit,
      76             :                       bool is_internal)
      77             : {
      78             :     Relation    conDesc;
      79             :     Oid         conOid;
      80             :     HeapTuple   tup;
      81             :     bool        nulls[Natts_pg_constraint];
      82             :     Datum       values[Natts_pg_constraint];
      83             :     ArrayType  *conkeyArray;
      84             :     ArrayType  *confkeyArray;
      85             :     ArrayType  *conpfeqopArray;
      86             :     ArrayType  *conppeqopArray;
      87             :     ArrayType  *conffeqopArray;
      88             :     ArrayType  *conexclopArray;
      89             :     NameData    cname;
      90             :     int         i;
      91             :     ObjectAddress conobject;
      92             : 
      93         627 :     conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
      94             : 
      95         627 :     Assert(constraintName);
      96         627 :     namestrcpy(&cname, constraintName);
      97             : 
      98             :     /*
      99             :      * Convert C arrays into Postgres arrays.
     100             :      */
     101         627 :     if (constraintNKeys > 0)
     102             :     {
     103             :         Datum      *conkey;
     104             : 
     105         586 :         conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
     106        1285 :         for (i = 0; i < constraintNKeys; i++)
     107         699 :             conkey[i] = Int16GetDatum(constraintKey[i]);
     108         586 :         conkeyArray = construct_array(conkey, constraintNKeys,
     109             :                                       INT2OID, 2, true, 's');
     110             :     }
     111             :     else
     112          41 :         conkeyArray = NULL;
     113             : 
     114         627 :     if (foreignNKeys > 0)
     115             :     {
     116             :         Datum      *fkdatums;
     117             : 
     118          93 :         fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
     119         213 :         for (i = 0; i < foreignNKeys; i++)
     120         120 :             fkdatums[i] = Int16GetDatum(foreignKey[i]);
     121          93 :         confkeyArray = construct_array(fkdatums, foreignNKeys,
     122             :                                        INT2OID, 2, true, 's');
     123         213 :         for (i = 0; i < foreignNKeys; i++)
     124         120 :             fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
     125          93 :         conpfeqopArray = construct_array(fkdatums, foreignNKeys,
     126             :                                          OIDOID, sizeof(Oid), true, 'i');
     127         213 :         for (i = 0; i < foreignNKeys; i++)
     128         120 :             fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
     129          93 :         conppeqopArray = construct_array(fkdatums, foreignNKeys,
     130             :                                          OIDOID, sizeof(Oid), true, 'i');
     131         213 :         for (i = 0; i < foreignNKeys; i++)
     132         120 :             fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
     133          93 :         conffeqopArray = construct_array(fkdatums, foreignNKeys,
     134             :                                          OIDOID, sizeof(Oid), true, 'i');
     135             :     }
     136             :     else
     137             :     {
     138         534 :         confkeyArray = NULL;
     139         534 :         conpfeqopArray = NULL;
     140         534 :         conppeqopArray = NULL;
     141         534 :         conffeqopArray = NULL;
     142             :     }
     143             : 
     144         627 :     if (exclOp != NULL)
     145             :     {
     146             :         Datum      *opdatums;
     147             : 
     148           8 :         opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
     149          20 :         for (i = 0; i < constraintNKeys; i++)
     150          12 :             opdatums[i] = ObjectIdGetDatum(exclOp[i]);
     151           8 :         conexclopArray = construct_array(opdatums, constraintNKeys,
     152             :                                          OIDOID, sizeof(Oid), true, 'i');
     153             :     }
     154             :     else
     155         619 :         conexclopArray = NULL;
     156             : 
     157             :     /* initialize nulls and values */
     158       15675 :     for (i = 0; i < Natts_pg_constraint; i++)
     159             :     {
     160       15048 :         nulls[i] = false;
     161       15048 :         values[i] = (Datum) NULL;
     162             :     }
     163             : 
     164         627 :     values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
     165         627 :     values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
     166         627 :     values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
     167         627 :     values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
     168         627 :     values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
     169         627 :     values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
     170         627 :     values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
     171         627 :     values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
     172         627 :     values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
     173         627 :     values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
     174         627 :     values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
     175         627 :     values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
     176         627 :     values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
     177         627 :     values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
     178         627 :     values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
     179         627 :     values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
     180             : 
     181         627 :     if (conkeyArray)
     182         586 :         values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
     183             :     else
     184          41 :         nulls[Anum_pg_constraint_conkey - 1] = true;
     185             : 
     186         627 :     if (confkeyArray)
     187          93 :         values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
     188             :     else
     189         534 :         nulls[Anum_pg_constraint_confkey - 1] = true;
     190             : 
     191         627 :     if (conpfeqopArray)
     192          93 :         values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
     193             :     else
     194         534 :         nulls[Anum_pg_constraint_conpfeqop - 1] = true;
     195             : 
     196         627 :     if (conppeqopArray)
     197          93 :         values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
     198             :     else
     199         534 :         nulls[Anum_pg_constraint_conppeqop - 1] = true;
     200             : 
     201         627 :     if (conffeqopArray)
     202          93 :         values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
     203             :     else
     204         534 :         nulls[Anum_pg_constraint_conffeqop - 1] = true;
     205             : 
     206         627 :     if (conexclopArray)
     207           8 :         values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
     208             :     else
     209         619 :         nulls[Anum_pg_constraint_conexclop - 1] = true;
     210             : 
     211             :     /*
     212             :      * initialize the binary form of the check constraint.
     213             :      */
     214         627 :     if (conBin)
     215         237 :         values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
     216             :     else
     217         390 :         nulls[Anum_pg_constraint_conbin - 1] = true;
     218             : 
     219             :     /*
     220             :      * initialize the text form of the check constraint
     221             :      */
     222         627 :     if (conSrc)
     223         237 :         values[Anum_pg_constraint_consrc - 1] = CStringGetTextDatum(conSrc);
     224             :     else
     225         390 :         nulls[Anum_pg_constraint_consrc - 1] = true;
     226             : 
     227         627 :     tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
     228             : 
     229         627 :     conOid = CatalogTupleInsert(conDesc, tup);
     230             : 
     231         627 :     conobject.classId = ConstraintRelationId;
     232         627 :     conobject.objectId = conOid;
     233         627 :     conobject.objectSubId = 0;
     234             : 
     235         627 :     heap_close(conDesc, RowExclusiveLock);
     236             : 
     237         627 :     if (OidIsValid(relId))
     238             :     {
     239             :         /*
     240             :          * Register auto dependency from constraint to owning relation, or to
     241             :          * specific column(s) if any are mentioned.
     242             :          */
     243             :         ObjectAddress relobject;
     244             : 
     245         587 :         relobject.classId = RelationRelationId;
     246         587 :         relobject.objectId = relId;
     247         587 :         if (constraintNKeys > 0)
     248             :         {
     249        1285 :             for (i = 0; i < constraintNKeys; i++)
     250             :             {
     251         699 :                 relobject.objectSubId = constraintKey[i];
     252             : 
     253         699 :                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
     254             :             }
     255             :         }
     256             :         else
     257             :         {
     258           1 :             relobject.objectSubId = 0;
     259             : 
     260           1 :             recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
     261             :         }
     262             :     }
     263             : 
     264         627 :     if (OidIsValid(domainId))
     265             :     {
     266             :         /*
     267             :          * Register auto dependency from constraint to owning domain
     268             :          */
     269             :         ObjectAddress domobject;
     270             : 
     271          40 :         domobject.classId = TypeRelationId;
     272          40 :         domobject.objectId = domainId;
     273          40 :         domobject.objectSubId = 0;
     274             : 
     275          40 :         recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
     276             :     }
     277             : 
     278         627 :     if (OidIsValid(foreignRelId))
     279             :     {
     280             :         /*
     281             :          * Register normal dependency from constraint to foreign relation, or
     282             :          * to specific column(s) if any are mentioned.
     283             :          */
     284             :         ObjectAddress relobject;
     285             : 
     286          93 :         relobject.classId = RelationRelationId;
     287          93 :         relobject.objectId = foreignRelId;
     288          93 :         if (foreignNKeys > 0)
     289             :         {
     290         213 :             for (i = 0; i < foreignNKeys; i++)
     291             :             {
     292         120 :                 relobject.objectSubId = foreignKey[i];
     293             : 
     294         120 :                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
     295             :             }
     296             :         }
     297             :         else
     298             :         {
     299           0 :             relobject.objectSubId = 0;
     300             : 
     301           0 :             recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
     302             :         }
     303             :     }
     304             : 
     305         627 :     if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN)
     306             :     {
     307             :         /*
     308             :          * Register normal dependency on the unique index that supports a
     309             :          * foreign-key constraint.  (Note: for indexes associated with unique
     310             :          * or primary-key constraints, the dependency runs the other way, and
     311             :          * is not made here.)
     312             :          */
     313             :         ObjectAddress relobject;
     314             : 
     315          93 :         relobject.classId = RelationRelationId;
     316          93 :         relobject.objectId = indexRelId;
     317          93 :         relobject.objectSubId = 0;
     318             : 
     319          93 :         recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
     320             :     }
     321             : 
     322         627 :     if (foreignNKeys > 0)
     323             :     {
     324             :         /*
     325             :          * Register normal dependencies on the equality operators that support
     326             :          * a foreign-key constraint.  If the PK and FK types are the same then
     327             :          * all three operators for a column are the same; otherwise they are
     328             :          * different.
     329             :          */
     330             :         ObjectAddress oprobject;
     331             : 
     332          93 :         oprobject.classId = OperatorRelationId;
     333          93 :         oprobject.objectSubId = 0;
     334             : 
     335         213 :         for (i = 0; i < foreignNKeys; i++)
     336             :         {
     337         120 :             oprobject.objectId = pfEqOp[i];
     338         120 :             recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
     339         120 :             if (ppEqOp[i] != pfEqOp[i])
     340             :             {
     341           5 :                 oprobject.objectId = ppEqOp[i];
     342           5 :                 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
     343             :             }
     344         120 :             if (ffEqOp[i] != pfEqOp[i])
     345             :             {
     346           5 :                 oprobject.objectId = ffEqOp[i];
     347           5 :                 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
     348             :             }
     349             :         }
     350             :     }
     351             : 
     352             :     /*
     353             :      * We don't bother to register dependencies on the exclusion operators of
     354             :      * an exclusion constraint.  We assume they are members of the opclass
     355             :      * supporting the index, so there's an indirect dependency via that. (This
     356             :      * would be pretty dicey for cross-type operators, but exclusion operators
     357             :      * can never be cross-type.)
     358             :      */
     359             : 
     360         627 :     if (conExpr != NULL)
     361             :     {
     362             :         /*
     363             :          * Register dependencies from constraint to objects mentioned in CHECK
     364             :          * expression.
     365             :          */
     366         237 :         recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
     367             :                                         DEPENDENCY_NORMAL,
     368             :                                         DEPENDENCY_NORMAL, false);
     369             :     }
     370             : 
     371             :     /* Post creation hook for new constraint */
     372         627 :     InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
     373             :                                   is_internal);
     374             : 
     375         627 :     return conOid;
     376             : }
     377             : 
     378             : 
     379             : /*
     380             :  * Test whether given name is currently used as a constraint name
     381             :  * for the given object (relation or domain).
     382             :  *
     383             :  * This is used to decide whether to accept a user-specified constraint name.
     384             :  * It is deliberately not the same test as ChooseConstraintName uses to decide
     385             :  * whether an auto-generated name is OK: here, we will allow it unless there
     386             :  * is an identical constraint name in use *on the same object*.
     387             :  *
     388             :  * NB: Caller should hold exclusive lock on the given object, else
     389             :  * this test can be fooled by concurrent additions.
     390             :  */
     391             : bool
     392          72 : ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
     393             :                      Oid objNamespace, const char *conname)
     394             : {
     395             :     bool        found;
     396             :     Relation    conDesc;
     397             :     SysScanDesc conscan;
     398             :     ScanKeyData skey[2];
     399             :     HeapTuple   tup;
     400             : 
     401          72 :     conDesc = heap_open(ConstraintRelationId, AccessShareLock);
     402             : 
     403          72 :     found = false;
     404             : 
     405          72 :     ScanKeyInit(&skey[0],
     406             :                 Anum_pg_constraint_conname,
     407             :                 BTEqualStrategyNumber, F_NAMEEQ,
     408             :                 CStringGetDatum(conname));
     409             : 
     410          72 :     ScanKeyInit(&skey[1],
     411             :                 Anum_pg_constraint_connamespace,
     412             :                 BTEqualStrategyNumber, F_OIDEQ,
     413             :                 ObjectIdGetDatum(objNamespace));
     414             : 
     415          72 :     conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
     416             :                                  NULL, 2, skey);
     417             : 
     418          72 :     while (HeapTupleIsValid(tup = systable_getnext(conscan)))
     419             :     {
     420           2 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
     421             : 
     422           2 :         if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
     423             :         {
     424           0 :             found = true;
     425           0 :             break;
     426             :         }
     427           2 :         else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
     428             :         {
     429           0 :             found = true;
     430           0 :             break;
     431             :         }
     432             :     }
     433             : 
     434          72 :     systable_endscan(conscan);
     435          72 :     heap_close(conDesc, AccessShareLock);
     436             : 
     437          72 :     return found;
     438             : }
     439             : 
     440             : /*
     441             :  * Select a nonconflicting name for a new constraint.
     442             :  *
     443             :  * The objective here is to choose a name that is unique within the
     444             :  * specified namespace.  Postgres does not require this, but the SQL
     445             :  * spec does, and some apps depend on it.  Therefore we avoid choosing
     446             :  * default names that so conflict.
     447             :  *
     448             :  * name1, name2, and label are used the same way as for makeObjectName(),
     449             :  * except that the label can't be NULL; digits will be appended to the label
     450             :  * if needed to create a name that is unique within the specified namespace.
     451             :  *
     452             :  * 'others' can be a list of string names already chosen within the current
     453             :  * command (but not yet reflected into the catalogs); we will not choose
     454             :  * a duplicate of one of these either.
     455             :  *
     456             :  * Note: it is theoretically possible to get a collision anyway, if someone
     457             :  * else chooses the same name concurrently.  This is fairly unlikely to be
     458             :  * a problem in practice, especially if one is holding an exclusive lock on
     459             :  * the relation identified by name1.
     460             :  *
     461             :  * Returns a palloc'd string.
     462             :  */
     463             : char *
     464         161 : ChooseConstraintName(const char *name1, const char *name2,
     465             :                      const char *label, Oid namespaceid,
     466             :                      List *others)
     467             : {
     468         161 :     int         pass = 0;
     469         161 :     char       *conname = NULL;
     470             :     char        modlabel[NAMEDATALEN];
     471             :     Relation    conDesc;
     472             :     SysScanDesc conscan;
     473             :     ScanKeyData skey[2];
     474             :     bool        found;
     475             :     ListCell   *l;
     476             : 
     477         161 :     conDesc = heap_open(ConstraintRelationId, AccessShareLock);
     478             : 
     479             :     /* try the unmodified label first */
     480         161 :     StrNCpy(modlabel, label, sizeof(modlabel));
     481             : 
     482             :     for (;;)
     483             :     {
     484         163 :         conname = makeObjectName(name1, name2, modlabel);
     485             : 
     486         163 :         found = false;
     487             : 
     488         169 :         foreach(l, others)
     489             :         {
     490           7 :             if (strcmp((char *) lfirst(l), conname) == 0)
     491             :             {
     492           1 :                 found = true;
     493           1 :                 break;
     494             :             }
     495             :         }
     496             : 
     497         163 :         if (!found)
     498             :         {
     499         162 :             ScanKeyInit(&skey[0],
     500             :                         Anum_pg_constraint_conname,
     501             :                         BTEqualStrategyNumber, F_NAMEEQ,
     502             :                         CStringGetDatum(conname));
     503             : 
     504         162 :             ScanKeyInit(&skey[1],
     505             :                         Anum_pg_constraint_connamespace,
     506             :                         BTEqualStrategyNumber, F_OIDEQ,
     507             :                         ObjectIdGetDatum(namespaceid));
     508             : 
     509         162 :             conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
     510             :                                          NULL, 2, skey);
     511             : 
     512         162 :             found = (HeapTupleIsValid(systable_getnext(conscan)));
     513             : 
     514         162 :             systable_endscan(conscan);
     515             :         }
     516             : 
     517         163 :         if (!found)
     518         161 :             break;
     519             : 
     520             :         /* found a conflict, so try a new name component */
     521           2 :         pfree(conname);
     522           2 :         snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
     523           2 :     }
     524             : 
     525         161 :     heap_close(conDesc, AccessShareLock);
     526             : 
     527         161 :     return conname;
     528             : }
     529             : 
     530             : /*
     531             :  * Delete a single constraint record.
     532             :  */
     533             : void
     534         507 : RemoveConstraintById(Oid conId)
     535             : {
     536             :     Relation    conDesc;
     537             :     HeapTuple   tup;
     538             :     Form_pg_constraint con;
     539             : 
     540         507 :     conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
     541             : 
     542         507 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
     543         507 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     544           0 :         elog(ERROR, "cache lookup failed for constraint %u", conId);
     545         507 :     con = (Form_pg_constraint) GETSTRUCT(tup);
     546             : 
     547             :     /*
     548             :      * Special processing depending on what the constraint is for.
     549             :      */
     550         507 :     if (OidIsValid(con->conrelid))
     551             :     {
     552             :         Relation    rel;
     553             : 
     554             :         /*
     555             :          * If the constraint is for a relation, open and exclusive-lock the
     556             :          * relation it's for.
     557             :          */
     558         485 :         rel = heap_open(con->conrelid, AccessExclusiveLock);
     559             : 
     560             :         /*
     561             :          * We need to update the relcheck count if it is a check constraint
     562             :          * being dropped.  This update will force backends to rebuild relcache
     563             :          * entries when we commit.
     564             :          */
     565         485 :         if (con->contype == CONSTRAINT_CHECK)
     566             :         {
     567             :             Relation    pgrel;
     568             :             HeapTuple   relTup;
     569             :             Form_pg_class classForm;
     570             : 
     571         154 :             pgrel = heap_open(RelationRelationId, RowExclusiveLock);
     572         154 :             relTup = SearchSysCacheCopy1(RELOID,
     573             :                                          ObjectIdGetDatum(con->conrelid));
     574         154 :             if (!HeapTupleIsValid(relTup))
     575           0 :                 elog(ERROR, "cache lookup failed for relation %u",
     576             :                      con->conrelid);
     577         154 :             classForm = (Form_pg_class) GETSTRUCT(relTup);
     578             : 
     579         154 :             if (classForm->relchecks == 0)   /* should not happen */
     580           0 :                 elog(ERROR, "relation \"%s\" has relchecks = 0",
     581             :                      RelationGetRelationName(rel));
     582         154 :             classForm->relchecks--;
     583             : 
     584         154 :             CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
     585             : 
     586         154 :             heap_freetuple(relTup);
     587             : 
     588         154 :             heap_close(pgrel, RowExclusiveLock);
     589             :         }
     590             : 
     591             :         /* Keep lock on constraint's rel until end of xact */
     592         485 :         heap_close(rel, NoLock);
     593             :     }
     594          22 :     else if (OidIsValid(con->contypid))
     595             :     {
     596             :         /*
     597             :          * XXX for now, do nothing special when dropping a domain constraint
     598             :          *
     599             :          * Probably there should be some form of locking on the domain type,
     600             :          * but we have no such concept at the moment.
     601             :          */
     602             :     }
     603             :     else
     604           0 :         elog(ERROR, "constraint %u is not of a known type", conId);
     605             : 
     606             :     /* Fry the constraint itself */
     607         507 :     CatalogTupleDelete(conDesc, &tup->t_self);
     608             : 
     609             :     /* Clean up */
     610         507 :     ReleaseSysCache(tup);
     611         507 :     heap_close(conDesc, RowExclusiveLock);
     612         507 : }
     613             : 
     614             : /*
     615             :  * RenameConstraintById
     616             :  *      Rename a constraint.
     617             :  *
     618             :  * Note: this isn't intended to be a user-exposed function; it doesn't check
     619             :  * permissions etc.  Currently this is only invoked when renaming an index
     620             :  * that is associated with a constraint, but it's made a little more general
     621             :  * than that with the expectation of someday having ALTER TABLE RENAME
     622             :  * CONSTRAINT.
     623             :  */
     624             : void
     625          11 : RenameConstraintById(Oid conId, const char *newname)
     626             : {
     627             :     Relation    conDesc;
     628             :     HeapTuple   tuple;
     629             :     Form_pg_constraint con;
     630             : 
     631          11 :     conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
     632             : 
     633          11 :     tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
     634          11 :     if (!HeapTupleIsValid(tuple))
     635           0 :         elog(ERROR, "cache lookup failed for constraint %u", conId);
     636          11 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
     637             : 
     638             :     /*
     639             :      * We need to check whether the name is already in use --- note that there
     640             :      * currently is not a unique index that would catch this.
     641             :      */
     642          21 :     if (OidIsValid(con->conrelid) &&
     643          10 :         ConstraintNameIsUsed(CONSTRAINT_RELATION,
     644             :                              con->conrelid,
     645             :                              con->connamespace,
     646             :                              newname))
     647           0 :         ereport(ERROR,
     648             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     649             :                  errmsg("constraint \"%s\" for relation \"%s\" already exists",
     650             :                         newname, get_rel_name(con->conrelid))));
     651          12 :     if (OidIsValid(con->contypid) &&
     652           1 :         ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
     653             :                              con->contypid,
     654             :                              con->connamespace,
     655             :                              newname))
     656           0 :         ereport(ERROR,
     657             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     658             :                  errmsg("constraint \"%s\" for domain %s already exists",
     659             :                         newname, format_type_be(con->contypid))));
     660             : 
     661             :     /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
     662          11 :     namestrcpy(&(con->conname), newname);
     663             : 
     664          11 :     CatalogTupleUpdate(conDesc, &tuple->t_self, tuple);
     665             : 
     666          11 :     InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
     667             : 
     668          11 :     heap_freetuple(tuple);
     669          11 :     heap_close(conDesc, RowExclusiveLock);
     670          11 : }
     671             : 
     672             : /*
     673             :  * AlterConstraintNamespaces
     674             :  *      Find any constraints belonging to the specified object,
     675             :  *      and move them to the specified new namespace.
     676             :  *
     677             :  * isType indicates whether the owning object is a type or a relation.
     678             :  */
     679             : void
     680          11 : AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
     681             :                           Oid newNspId, bool isType, ObjectAddresses *objsMoved)
     682             : {
     683             :     Relation    conRel;
     684             :     ScanKeyData key[1];
     685             :     SysScanDesc scan;
     686             :     HeapTuple   tup;
     687             : 
     688          11 :     conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
     689             : 
     690          11 :     if (isType)
     691             :     {
     692           1 :         ScanKeyInit(&key[0],
     693             :                     Anum_pg_constraint_contypid,
     694             :                     BTEqualStrategyNumber, F_OIDEQ,
     695             :                     ObjectIdGetDatum(ownerId));
     696             : 
     697           1 :         scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
     698             :                                   NULL, 1, key);
     699             :     }
     700             :     else
     701             :     {
     702          10 :         ScanKeyInit(&key[0],
     703             :                     Anum_pg_constraint_conrelid,
     704             :                     BTEqualStrategyNumber, F_OIDEQ,
     705             :                     ObjectIdGetDatum(ownerId));
     706             : 
     707          10 :         scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
     708             :                                   NULL, 1, key);
     709             :     }
     710             : 
     711          31 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     712             :     {
     713           9 :         Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
     714             :         ObjectAddress thisobj;
     715             : 
     716           9 :         thisobj.classId = ConstraintRelationId;
     717           9 :         thisobj.objectId = HeapTupleGetOid(tup);
     718           9 :         thisobj.objectSubId = 0;
     719             : 
     720           9 :         if (object_address_present(&thisobj, objsMoved))
     721           0 :             continue;
     722             : 
     723             :         /* Don't update if the object is already part of the namespace */
     724           9 :         if (conform->connamespace == oldNspId && oldNspId != newNspId)
     725             :         {
     726           6 :             tup = heap_copytuple(tup);
     727           6 :             conform = (Form_pg_constraint) GETSTRUCT(tup);
     728             : 
     729           6 :             conform->connamespace = newNspId;
     730             : 
     731           6 :             CatalogTupleUpdate(conRel, &tup->t_self, tup);
     732             : 
     733             :             /*
     734             :              * Note: currently, the constraint will not have its own
     735             :              * dependency on the namespace, so we don't need to do
     736             :              * changeDependencyFor().
     737             :              */
     738             :         }
     739             : 
     740           9 :         InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
     741             : 
     742           9 :         add_exact_object_address(&thisobj, objsMoved);
     743             :     }
     744             : 
     745          11 :     systable_endscan(scan);
     746             : 
     747          11 :     heap_close(conRel, RowExclusiveLock);
     748          11 : }
     749             : 
     750             : /*
     751             :  * get_relation_constraint_oid
     752             :  *      Find a constraint on the specified relation with the specified name.
     753             :  *      Returns constraint's OID.
     754             :  */
     755             : Oid
     756          42 : get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
     757             : {
     758             :     Relation    pg_constraint;
     759             :     HeapTuple   tuple;
     760             :     SysScanDesc scan;
     761             :     ScanKeyData skey[1];
     762          42 :     Oid         conOid = InvalidOid;
     763             : 
     764             :     /*
     765             :      * Fetch the constraint tuple from pg_constraint.  There may be more than
     766             :      * one match, because constraints are not required to have unique names;
     767             :      * if so, error out.
     768             :      */
     769          42 :     pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
     770             : 
     771          42 :     ScanKeyInit(&skey[0],
     772             :                 Anum_pg_constraint_conrelid,
     773             :                 BTEqualStrategyNumber, F_OIDEQ,
     774             :                 ObjectIdGetDatum(relid));
     775             : 
     776          42 :     scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
     777             :                               NULL, 1, skey);
     778             : 
     779         148 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     780             :     {
     781          64 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
     782             : 
     783          64 :         if (strcmp(NameStr(con->conname), conname) == 0)
     784             :         {
     785          40 :             if (OidIsValid(conOid))
     786           0 :                 ereport(ERROR,
     787             :                         (errcode(ERRCODE_DUPLICATE_OBJECT),
     788             :                          errmsg("table \"%s\" has multiple constraints named \"%s\"",
     789             :                                 get_rel_name(relid), conname)));
     790          40 :             conOid = HeapTupleGetOid(tuple);
     791             :         }
     792             :     }
     793             : 
     794          42 :     systable_endscan(scan);
     795             : 
     796             :     /* If no such constraint exists, complain */
     797          42 :     if (!OidIsValid(conOid) && !missing_ok)
     798           2 :         ereport(ERROR,
     799             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     800             :                  errmsg("constraint \"%s\" for table \"%s\" does not exist",
     801             :                         conname, get_rel_name(relid))));
     802             : 
     803          40 :     heap_close(pg_constraint, AccessShareLock);
     804             : 
     805          40 :     return conOid;
     806             : }
     807             : 
     808             : /*
     809             :  * get_domain_constraint_oid
     810             :  *      Find a constraint on the specified domain with the specified name.
     811             :  *      Returns constraint's OID.
     812             :  */
     813             : Oid
     814           6 : get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
     815             : {
     816             :     Relation    pg_constraint;
     817             :     HeapTuple   tuple;
     818             :     SysScanDesc scan;
     819             :     ScanKeyData skey[1];
     820           6 :     Oid         conOid = InvalidOid;
     821             : 
     822             :     /*
     823             :      * Fetch the constraint tuple from pg_constraint.  There may be more than
     824             :      * one match, because constraints are not required to have unique names;
     825             :      * if so, error out.
     826             :      */
     827           6 :     pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
     828             : 
     829           6 :     ScanKeyInit(&skey[0],
     830             :                 Anum_pg_constraint_contypid,
     831             :                 BTEqualStrategyNumber, F_OIDEQ,
     832             :                 ObjectIdGetDatum(typid));
     833             : 
     834           6 :     scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
     835             :                               NULL, 1, skey);
     836             : 
     837          18 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     838             :     {
     839           6 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
     840             : 
     841           6 :         if (strcmp(NameStr(con->conname), conname) == 0)
     842             :         {
     843           5 :             if (OidIsValid(conOid))
     844           0 :                 ereport(ERROR,
     845             :                         (errcode(ERRCODE_DUPLICATE_OBJECT),
     846             :                          errmsg("domain %s has multiple constraints named \"%s\"",
     847             :                                 format_type_be(typid), conname)));
     848           5 :             conOid = HeapTupleGetOid(tuple);
     849             :         }
     850             :     }
     851             : 
     852           6 :     systable_endscan(scan);
     853             : 
     854             :     /* If no such constraint exists, complain */
     855           6 :     if (!OidIsValid(conOid) && !missing_ok)
     856           1 :         ereport(ERROR,
     857             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     858             :                  errmsg("constraint \"%s\" for domain %s does not exist",
     859             :                         conname, format_type_be(typid))));
     860             : 
     861           5 :     heap_close(pg_constraint, AccessShareLock);
     862             : 
     863           5 :     return conOid;
     864             : }
     865             : 
     866             : /*
     867             :  * get_primary_key_attnos
     868             :  *      Identify the columns in a relation's primary key, if any.
     869             :  *
     870             :  * Returns a Bitmapset of the column attnos of the primary key's columns,
     871             :  * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
     872             :  * system columns can be represented.
     873             :  *
     874             :  * If there is no primary key, return NULL.  We also return NULL if the pkey
     875             :  * constraint is deferrable and deferrableOk is false.
     876             :  *
     877             :  * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
     878             :  * on failure.
     879             :  */
     880             : Bitmapset *
     881          85 : get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
     882             : {
     883          85 :     Bitmapset  *pkattnos = NULL;
     884             :     Relation    pg_constraint;
     885             :     HeapTuple   tuple;
     886             :     SysScanDesc scan;
     887             :     ScanKeyData skey[1];
     888             : 
     889             :     /* Set *constraintOid, to avoid complaints about uninitialized vars */
     890          85 :     *constraintOid = InvalidOid;
     891             : 
     892             :     /* Scan pg_constraint for constraints of the target rel */
     893          85 :     pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
     894             : 
     895          85 :     ScanKeyInit(&skey[0],
     896             :                 Anum_pg_constraint_conrelid,
     897             :                 BTEqualStrategyNumber, F_OIDEQ,
     898             :                 ObjectIdGetDatum(relid));
     899             : 
     900          85 :     scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
     901             :                               NULL, 1, skey);
     902             : 
     903          85 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     904             :     {
     905          67 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
     906             :         Datum       adatum;
     907             :         bool        isNull;
     908             :         ArrayType  *arr;
     909             :         int16      *attnums;
     910             :         int         numkeys;
     911             :         int         i;
     912             : 
     913             :         /* Skip constraints that are not PRIMARY KEYs */
     914          67 :         if (con->contype != CONSTRAINT_PRIMARY)
     915          34 :             continue;
     916             : 
     917             :         /*
     918             :          * If the primary key is deferrable, but we've been instructed to
     919             :          * ignore deferrable constraints, then we might as well give up
     920             :          * searching, since there can only be a single primary key on a table.
     921             :          */
     922          33 :         if (con->condeferrable && !deferrableOk)
     923          34 :             break;
     924             : 
     925             :         /* Extract the conkey array, ie, attnums of PK's columns */
     926          32 :         adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
     927             :                               RelationGetDescr(pg_constraint), &isNull);
     928          32 :         if (isNull)
     929           0 :             elog(ERROR, "null conkey for constraint %u",
     930             :                  HeapTupleGetOid(tuple));
     931          32 :         arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
     932          32 :         numkeys = ARR_DIMS(arr)[0];
     933          32 :         if (ARR_NDIM(arr) != 1 ||
     934          32 :             numkeys < 0 ||
     935          64 :             ARR_HASNULL(arr) ||
     936          32 :             ARR_ELEMTYPE(arr) != INT2OID)
     937           0 :             elog(ERROR, "conkey is not a 1-D smallint array");
     938          32 :         attnums = (int16 *) ARR_DATA_PTR(arr);
     939             : 
     940             :         /* Construct the result value */
     941          74 :         for (i = 0; i < numkeys; i++)
     942             :         {
     943          42 :             pkattnos = bms_add_member(pkattnos,
     944          42 :                                       attnums[i] - FirstLowInvalidHeapAttributeNumber);
     945             :         }
     946          32 :         *constraintOid = HeapTupleGetOid(tuple);
     947             : 
     948             :         /* No need to search further */
     949          32 :         break;
     950             :     }
     951             : 
     952          85 :     systable_endscan(scan);
     953             : 
     954          85 :     heap_close(pg_constraint, AccessShareLock);
     955             : 
     956          85 :     return pkattnos;
     957             : }
     958             : 
     959             : /*
     960             :  * Determine whether a relation can be proven functionally dependent on
     961             :  * a set of grouping columns.  If so, return TRUE and add the pg_constraint
     962             :  * OIDs of the constraints needed for the proof to the *constraintDeps list.
     963             :  *
     964             :  * grouping_columns is a list of grouping expressions, in which columns of
     965             :  * the rel of interest are Vars with the indicated varno/varlevelsup.
     966             :  *
     967             :  * Currently we only check to see if the rel has a primary key that is a
     968             :  * subset of the grouping_columns.  We could also use plain unique constraints
     969             :  * if all their columns are known not null, but there's a problem: we need
     970             :  * to be able to represent the not-null-ness as part of the constraints added
     971             :  * to *constraintDeps.  FIXME whenever not-null constraints get represented
     972             :  * in pg_constraint.
     973             :  */
     974             : bool
     975          29 : check_functional_grouping(Oid relid,
     976             :                           Index varno, Index varlevelsup,
     977             :                           List *grouping_columns,
     978             :                           List **constraintDeps)
     979             : {
     980             :     Bitmapset  *pkattnos;
     981             :     Bitmapset  *groupbyattnos;
     982             :     Oid         constraintOid;
     983             :     ListCell   *gl;
     984             : 
     985             :     /* If the rel has no PK, then we can't prove functional dependency */
     986          29 :     pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
     987          29 :     if (pkattnos == NULL)
     988           6 :         return false;
     989             : 
     990             :     /* Identify all the rel's columns that appear in grouping_columns */
     991          23 :     groupbyattnos = NULL;
     992          53 :     foreach(gl, grouping_columns)
     993             :     {
     994          30 :         Var        *gvar = (Var *) lfirst(gl);
     995             : 
     996          60 :         if (IsA(gvar, Var) &&
     997          53 :             gvar->varno == varno &&
     998          23 :             gvar->varlevelsup == varlevelsup)
     999          23 :             groupbyattnos = bms_add_member(groupbyattnos,
    1000          23 :                                            gvar->varattno - FirstLowInvalidHeapAttributeNumber);
    1001             :     }
    1002             : 
    1003          23 :     if (bms_is_subset(pkattnos, groupbyattnos))
    1004             :     {
    1005             :         /* The PK is a subset of grouping_columns, so we win */
    1006          16 :         *constraintDeps = lappend_oid(*constraintDeps, constraintOid);
    1007          16 :         return true;
    1008             :     }
    1009             : 
    1010           7 :     return false;
    1011             : }

Generated by: LCOV version 1.11