LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_constraint.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 422 449 94.0 %
Date: 2019-06-19 16:07:09 Functions: 15 15 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-2019, 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/htup_details.h"
      19             : #include "access/sysattr.h"
      20             : #include "access/table.h"
      21             : #include "access/tupconvert.h"
      22             : #include "access/xact.h"
      23             : #include "catalog/catalog.h"
      24             : #include "catalog/dependency.h"
      25             : #include "catalog/indexing.h"
      26             : #include "catalog/objectaccess.h"
      27             : #include "catalog/pg_constraint.h"
      28             : #include "catalog/pg_operator.h"
      29             : #include "catalog/pg_type.h"
      30             : #include "commands/defrem.h"
      31             : #include "commands/tablecmds.h"
      32             : #include "utils/array.h"
      33             : #include "utils/builtins.h"
      34             : #include "utils/fmgroids.h"
      35             : #include "utils/lsyscache.h"
      36             : #include "utils/rel.h"
      37             : #include "utils/syscache.h"
      38             : 
      39             : 
      40             : /*
      41             :  * CreateConstraintEntry
      42             :  *  Create a constraint table entry.
      43             :  *
      44             :  * Subsidiary records (such as triggers or indexes to implement the
      45             :  * constraint) are *not* created here.  But we do make dependency links
      46             :  * from the constraint to the things it depends on.
      47             :  *
      48             :  * The new constraint's OID is returned.
      49             :  */
      50             : Oid
      51        8838 : CreateConstraintEntry(const char *constraintName,
      52             :                       Oid constraintNamespace,
      53             :                       char constraintType,
      54             :                       bool isDeferrable,
      55             :                       bool isDeferred,
      56             :                       bool isValidated,
      57             :                       Oid parentConstrId,
      58             :                       Oid relId,
      59             :                       const int16 *constraintKey,
      60             :                       int constraintNKeys,
      61             :                       int constraintNTotalKeys,
      62             :                       Oid domainId,
      63             :                       Oid indexRelId,
      64             :                       Oid foreignRelId,
      65             :                       const int16 *foreignKey,
      66             :                       const Oid *pfEqOp,
      67             :                       const Oid *ppEqOp,
      68             :                       const Oid *ffEqOp,
      69             :                       int foreignNKeys,
      70             :                       char foreignUpdateType,
      71             :                       char foreignDeleteType,
      72             :                       char foreignMatchType,
      73             :                       const Oid *exclOp,
      74             :                       Node *conExpr,
      75             :                       const char *conBin,
      76             :                       bool conIsLocal,
      77             :                       int conInhCount,
      78             :                       bool conNoInherit,
      79             :                       bool is_internal)
      80             : {
      81             :     Relation    conDesc;
      82             :     Oid         conOid;
      83             :     HeapTuple   tup;
      84             :     bool        nulls[Natts_pg_constraint];
      85             :     Datum       values[Natts_pg_constraint];
      86             :     ArrayType  *conkeyArray;
      87             :     ArrayType  *confkeyArray;
      88             :     ArrayType  *conpfeqopArray;
      89             :     ArrayType  *conppeqopArray;
      90             :     ArrayType  *conffeqopArray;
      91             :     ArrayType  *conexclopArray;
      92             :     NameData    cname;
      93             :     int         i;
      94             :     ObjectAddress conobject;
      95             : 
      96        8838 :     conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
      97             : 
      98             :     Assert(constraintName);
      99        8838 :     namestrcpy(&cname, constraintName);
     100             : 
     101             :     /*
     102             :      * Convert C arrays into Postgres arrays.
     103             :      */
     104        8838 :     if (constraintNKeys > 0)
     105             :     {
     106             :         Datum      *conkey;
     107             : 
     108        7886 :         conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
     109       16862 :         for (i = 0; i < constraintNKeys; i++)
     110        8976 :             conkey[i] = Int16GetDatum(constraintKey[i]);
     111        7886 :         conkeyArray = construct_array(conkey, constraintNKeys,
     112             :                                       INT2OID, 2, true, 's');
     113             :     }
     114             :     else
     115         952 :         conkeyArray = NULL;
     116             : 
     117        8838 :     if (foreignNKeys > 0)
     118             :     {
     119             :         Datum      *fkdatums;
     120             : 
     121        1696 :         fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
     122        3746 :         for (i = 0; i < foreignNKeys; i++)
     123        2050 :             fkdatums[i] = Int16GetDatum(foreignKey[i]);
     124        1696 :         confkeyArray = construct_array(fkdatums, foreignNKeys,
     125             :                                        INT2OID, 2, true, 's');
     126        3746 :         for (i = 0; i < foreignNKeys; i++)
     127        2050 :             fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
     128        1696 :         conpfeqopArray = construct_array(fkdatums, foreignNKeys,
     129             :                                          OIDOID, sizeof(Oid), true, 'i');
     130        3746 :         for (i = 0; i < foreignNKeys; i++)
     131        2050 :             fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
     132        1696 :         conppeqopArray = construct_array(fkdatums, foreignNKeys,
     133             :                                          OIDOID, sizeof(Oid), true, 'i');
     134        3746 :         for (i = 0; i < foreignNKeys; i++)
     135        2050 :             fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
     136        1696 :         conffeqopArray = construct_array(fkdatums, foreignNKeys,
     137             :                                          OIDOID, sizeof(Oid), true, 'i');
     138             :     }
     139             :     else
     140             :     {
     141        7142 :         confkeyArray = NULL;
     142        7142 :         conpfeqopArray = NULL;
     143        7142 :         conppeqopArray = NULL;
     144        7142 :         conffeqopArray = NULL;
     145             :     }
     146             : 
     147        8838 :     if (exclOp != NULL)
     148             :     {
     149             :         Datum      *opdatums;
     150             : 
     151          70 :         opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
     152         162 :         for (i = 0; i < constraintNKeys; i++)
     153          92 :             opdatums[i] = ObjectIdGetDatum(exclOp[i]);
     154          70 :         conexclopArray = construct_array(opdatums, constraintNKeys,
     155             :                                          OIDOID, sizeof(Oid), true, 'i');
     156             :     }
     157             :     else
     158        8768 :         conexclopArray = NULL;
     159             : 
     160             :     /* initialize nulls and values */
     161      229788 :     for (i = 0; i < Natts_pg_constraint; i++)
     162             :     {
     163      220950 :         nulls[i] = false;
     164      220950 :         values[i] = (Datum) NULL;
     165             :     }
     166             : 
     167        8838 :     conOid = GetNewOidWithIndex(conDesc, ConstraintOidIndexId,
     168             :                                 Anum_pg_constraint_oid);
     169        8838 :     values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(conOid);
     170        8838 :     values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
     171        8838 :     values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
     172        8838 :     values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
     173        8838 :     values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
     174        8838 :     values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
     175        8838 :     values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
     176        8838 :     values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
     177        8838 :     values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
     178        8838 :     values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
     179        8838 :     values[Anum_pg_constraint_conparentid - 1] = ObjectIdGetDatum(parentConstrId);
     180        8838 :     values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
     181        8838 :     values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
     182        8838 :     values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
     183        8838 :     values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
     184        8838 :     values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
     185        8838 :     values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
     186        8838 :     values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
     187             : 
     188        8838 :     if (conkeyArray)
     189        7886 :         values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
     190             :     else
     191         952 :         nulls[Anum_pg_constraint_conkey - 1] = true;
     192             : 
     193        8838 :     if (confkeyArray)
     194        1696 :         values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
     195             :     else
     196        7142 :         nulls[Anum_pg_constraint_confkey - 1] = true;
     197             : 
     198        8838 :     if (conpfeqopArray)
     199        1696 :         values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
     200             :     else
     201        7142 :         nulls[Anum_pg_constraint_conpfeqop - 1] = true;
     202             : 
     203        8838 :     if (conppeqopArray)
     204        1696 :         values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
     205             :     else
     206        7142 :         nulls[Anum_pg_constraint_conppeqop - 1] = true;
     207             : 
     208        8838 :     if (conffeqopArray)
     209        1696 :         values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
     210             :     else
     211        7142 :         nulls[Anum_pg_constraint_conffeqop - 1] = true;
     212             : 
     213        8838 :     if (conexclopArray)
     214          70 :         values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
     215             :     else
     216        8768 :         nulls[Anum_pg_constraint_conexclop - 1] = true;
     217             : 
     218        8838 :     if (conBin)
     219        1966 :         values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
     220             :     else
     221        6872 :         nulls[Anum_pg_constraint_conbin - 1] = true;
     222             : 
     223        8838 :     tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
     224             : 
     225        8838 :     CatalogTupleInsert(conDesc, tup);
     226             : 
     227        8838 :     conobject.classId = ConstraintRelationId;
     228        8838 :     conobject.objectId = conOid;
     229        8838 :     conobject.objectSubId = 0;
     230             : 
     231        8838 :     table_close(conDesc, RowExclusiveLock);
     232             : 
     233        8838 :     if (OidIsValid(relId))
     234             :     {
     235             :         /*
     236             :          * Register auto dependency from constraint to owning relation, or to
     237             :          * specific column(s) if any are mentioned.
     238             :          */
     239             :         ObjectAddress relobject;
     240             : 
     241        7940 :         relobject.classId = RelationRelationId;
     242        7940 :         relobject.objectId = relId;
     243        7940 :         if (constraintNTotalKeys > 0)
     244             :         {
     245       17094 :             for (i = 0; i < constraintNTotalKeys; i++)
     246             :             {
     247        9208 :                 relobject.objectSubId = constraintKey[i];
     248             : 
     249        9208 :                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
     250             :             }
     251             :         }
     252             :         else
     253             :         {
     254          54 :             relobject.objectSubId = 0;
     255             : 
     256          54 :             recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
     257             :         }
     258             :     }
     259             : 
     260        8838 :     if (OidIsValid(domainId))
     261             :     {
     262             :         /*
     263             :          * Register auto dependency from constraint to owning domain
     264             :          */
     265             :         ObjectAddress domobject;
     266             : 
     267         898 :         domobject.classId = TypeRelationId;
     268         898 :         domobject.objectId = domainId;
     269         898 :         domobject.objectSubId = 0;
     270             : 
     271         898 :         recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
     272             :     }
     273             : 
     274        8838 :     if (OidIsValid(foreignRelId))
     275             :     {
     276             :         /*
     277             :          * Register normal dependency from constraint to foreign relation, or
     278             :          * to specific column(s) if any are mentioned.
     279             :          */
     280             :         ObjectAddress relobject;
     281             : 
     282        1696 :         relobject.classId = RelationRelationId;
     283        1696 :         relobject.objectId = foreignRelId;
     284        1696 :         if (foreignNKeys > 0)
     285             :         {
     286        3746 :             for (i = 0; i < foreignNKeys; i++)
     287             :             {
     288        2050 :                 relobject.objectSubId = foreignKey[i];
     289             : 
     290        2050 :                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
     291             :             }
     292             :         }
     293             :         else
     294             :         {
     295           0 :             relobject.objectSubId = 0;
     296             : 
     297           0 :             recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
     298             :         }
     299             :     }
     300             : 
     301        8838 :     if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN)
     302             :     {
     303             :         /*
     304             :          * Register normal dependency on the unique index that supports a
     305             :          * foreign-key constraint.  (Note: for indexes associated with unique
     306             :          * or primary-key constraints, the dependency runs the other way, and
     307             :          * is not made here.)
     308             :          */
     309             :         ObjectAddress relobject;
     310             : 
     311        1696 :         relobject.classId = RelationRelationId;
     312        1696 :         relobject.objectId = indexRelId;
     313        1696 :         relobject.objectSubId = 0;
     314             : 
     315        1696 :         recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
     316             :     }
     317             : 
     318        8838 :     if (foreignNKeys > 0)
     319             :     {
     320             :         /*
     321             :          * Register normal dependencies on the equality operators that support
     322             :          * a foreign-key constraint.  If the PK and FK types are the same then
     323             :          * all three operators for a column are the same; otherwise they are
     324             :          * different.
     325             :          */
     326             :         ObjectAddress oprobject;
     327             : 
     328        1696 :         oprobject.classId = OperatorRelationId;
     329        1696 :         oprobject.objectSubId = 0;
     330             : 
     331        3746 :         for (i = 0; i < foreignNKeys; i++)
     332             :         {
     333        2050 :             oprobject.objectId = pfEqOp[i];
     334        2050 :             recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
     335        2050 :             if (ppEqOp[i] != pfEqOp[i])
     336             :             {
     337          30 :                 oprobject.objectId = ppEqOp[i];
     338          30 :                 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
     339             :             }
     340        2050 :             if (ffEqOp[i] != pfEqOp[i])
     341             :             {
     342          30 :                 oprobject.objectId = ffEqOp[i];
     343          30 :                 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
     344             :             }
     345             :         }
     346             :     }
     347             : 
     348             :     /*
     349             :      * We don't bother to register dependencies on the exclusion operators of
     350             :      * an exclusion constraint.  We assume they are members of the opclass
     351             :      * supporting the index, so there's an indirect dependency via that. (This
     352             :      * would be pretty dicey for cross-type operators, but exclusion operators
     353             :      * can never be cross-type.)
     354             :      */
     355             : 
     356        8838 :     if (conExpr != NULL)
     357             :     {
     358             :         /*
     359             :          * Register dependencies from constraint to objects mentioned in CHECK
     360             :          * expression.
     361             :          */
     362        1966 :         recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
     363             :                                         DEPENDENCY_NORMAL,
     364             :                                         DEPENDENCY_NORMAL, false);
     365             :     }
     366             : 
     367             :     /* Post creation hook for new constraint */
     368        8838 :     InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
     369             :                                   is_internal);
     370             : 
     371        8838 :     return conOid;
     372             : }
     373             : 
     374             : /*
     375             :  * Test whether given name is currently used as a constraint name
     376             :  * for the given object (relation or domain).
     377             :  *
     378             :  * This is used to decide whether to accept a user-specified constraint name.
     379             :  * It is deliberately not the same test as ChooseConstraintName uses to decide
     380             :  * whether an auto-generated name is OK: here, we will allow it unless there
     381             :  * is an identical constraint name in use *on the same object*.
     382             :  *
     383             :  * NB: Caller should hold exclusive lock on the given object, else
     384             :  * this test can be fooled by concurrent additions.
     385             :  */
     386             : bool
     387        8226 : ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
     388             :                      const char *conname)
     389             : {
     390             :     bool        found;
     391             :     Relation    conDesc;
     392             :     SysScanDesc conscan;
     393             :     ScanKeyData skey[3];
     394             : 
     395        8226 :     conDesc = table_open(ConstraintRelationId, AccessShareLock);
     396             : 
     397        8226 :     ScanKeyInit(&skey[0],
     398             :                 Anum_pg_constraint_conrelid,
     399             :                 BTEqualStrategyNumber, F_OIDEQ,
     400             :                 ObjectIdGetDatum((conCat == CONSTRAINT_RELATION)
     401             :                                  ? objId : InvalidOid));
     402        8226 :     ScanKeyInit(&skey[1],
     403             :                 Anum_pg_constraint_contypid,
     404             :                 BTEqualStrategyNumber, F_OIDEQ,
     405             :                 ObjectIdGetDatum((conCat == CONSTRAINT_DOMAIN)
     406             :                                  ? objId : InvalidOid));
     407        8226 :     ScanKeyInit(&skey[2],
     408             :                 Anum_pg_constraint_conname,
     409             :                 BTEqualStrategyNumber, F_NAMEEQ,
     410             :                 CStringGetDatum(conname));
     411             : 
     412        8226 :     conscan = systable_beginscan(conDesc, ConstraintRelidTypidNameIndexId,
     413             :                                  true, NULL, 3, skey);
     414             : 
     415             :     /* There can be at most one matching row */
     416        8226 :     found = (HeapTupleIsValid(systable_getnext(conscan)));
     417             : 
     418        8226 :     systable_endscan(conscan);
     419        8226 :     table_close(conDesc, AccessShareLock);
     420             : 
     421        8226 :     return found;
     422             : }
     423             : 
     424             : /*
     425             :  * Does any constraint of the given name exist in the given namespace?
     426             :  *
     427             :  * This is used for code that wants to match ChooseConstraintName's rule
     428             :  * that we should avoid autogenerating duplicate constraint names within a
     429             :  * namespace.
     430             :  */
     431             : bool
     432        4648 : ConstraintNameExists(const char *conname, Oid namespaceid)
     433             : {
     434             :     bool        found;
     435             :     Relation    conDesc;
     436             :     SysScanDesc conscan;
     437             :     ScanKeyData skey[2];
     438             : 
     439        4648 :     conDesc = table_open(ConstraintRelationId, AccessShareLock);
     440             : 
     441        4648 :     ScanKeyInit(&skey[0],
     442             :                 Anum_pg_constraint_conname,
     443             :                 BTEqualStrategyNumber, F_NAMEEQ,
     444             :                 CStringGetDatum(conname));
     445             : 
     446        4648 :     ScanKeyInit(&skey[1],
     447             :                 Anum_pg_constraint_connamespace,
     448             :                 BTEqualStrategyNumber, F_OIDEQ,
     449             :                 ObjectIdGetDatum(namespaceid));
     450             : 
     451        4648 :     conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
     452             :                                  NULL, 2, skey);
     453             : 
     454        4648 :     found = (HeapTupleIsValid(systable_getnext(conscan)));
     455             : 
     456        4648 :     systable_endscan(conscan);
     457        4648 :     table_close(conDesc, AccessShareLock);
     458             : 
     459        4648 :     return found;
     460             : }
     461             : 
     462             : /*
     463             :  * Select a nonconflicting name for a new constraint.
     464             :  *
     465             :  * The objective here is to choose a name that is unique within the
     466             :  * specified namespace.  Postgres does not require this, but the SQL
     467             :  * spec does, and some apps depend on it.  Therefore we avoid choosing
     468             :  * default names that so conflict.
     469             :  *
     470             :  * name1, name2, and label are used the same way as for makeObjectName(),
     471             :  * except that the label can't be NULL; digits will be appended to the label
     472             :  * if needed to create a name that is unique within the specified namespace.
     473             :  *
     474             :  * 'others' can be a list of string names already chosen within the current
     475             :  * command (but not yet reflected into the catalogs); we will not choose
     476             :  * a duplicate of one of these either.
     477             :  *
     478             :  * Note: it is theoretically possible to get a collision anyway, if someone
     479             :  * else chooses the same name concurrently.  This is fairly unlikely to be
     480             :  * a problem in practice, especially if one is holding an exclusive lock on
     481             :  * the relation identified by name1.
     482             :  *
     483             :  * Returns a palloc'd string.
     484             :  */
     485             : char *
     486        1336 : ChooseConstraintName(const char *name1, const char *name2,
     487             :                      const char *label, Oid namespaceid,
     488             :                      List *others)
     489             : {
     490        1336 :     int         pass = 0;
     491        1336 :     char       *conname = NULL;
     492             :     char        modlabel[NAMEDATALEN];
     493             :     Relation    conDesc;
     494             :     SysScanDesc conscan;
     495             :     ScanKeyData skey[2];
     496             :     bool        found;
     497             :     ListCell   *l;
     498             : 
     499        1336 :     conDesc = table_open(ConstraintRelationId, AccessShareLock);
     500             : 
     501             :     /* try the unmodified label first */
     502        1336 :     StrNCpy(modlabel, label, sizeof(modlabel));
     503             : 
     504             :     for (;;)
     505             :     {
     506        2412 :         conname = makeObjectName(name1, name2, modlabel);
     507             : 
     508        1874 :         found = false;
     509             : 
     510        1900 :         foreach(l, others)
     511             :         {
     512          30 :             if (strcmp((char *) lfirst(l), conname) == 0)
     513             :             {
     514           4 :                 found = true;
     515           4 :                 break;
     516             :             }
     517             :         }
     518             : 
     519        1874 :         if (!found)
     520             :         {
     521        1870 :             ScanKeyInit(&skey[0],
     522             :                         Anum_pg_constraint_conname,
     523             :                         BTEqualStrategyNumber, F_NAMEEQ,
     524             :                         CStringGetDatum(conname));
     525             : 
     526        1870 :             ScanKeyInit(&skey[1],
     527             :                         Anum_pg_constraint_connamespace,
     528             :                         BTEqualStrategyNumber, F_OIDEQ,
     529             :                         ObjectIdGetDatum(namespaceid));
     530             : 
     531        1870 :             conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
     532             :                                          NULL, 2, skey);
     533             : 
     534        1870 :             found = (HeapTupleIsValid(systable_getnext(conscan)));
     535             : 
     536        1870 :             systable_endscan(conscan);
     537             :         }
     538             : 
     539        1874 :         if (!found)
     540        1336 :             break;
     541             : 
     542             :         /* found a conflict, so try a new name component */
     543         538 :         pfree(conname);
     544         538 :         snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
     545             :     }
     546             : 
     547        1336 :     table_close(conDesc, AccessShareLock);
     548             : 
     549        1336 :     return conname;
     550             : }
     551             : 
     552             : /*
     553             :  * Delete a single constraint record.
     554             :  */
     555             : void
     556        6584 : RemoveConstraintById(Oid conId)
     557             : {
     558             :     Relation    conDesc;
     559             :     HeapTuple   tup;
     560             :     Form_pg_constraint con;
     561             : 
     562        6584 :     conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
     563             : 
     564        6584 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
     565        6584 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     566           0 :         elog(ERROR, "cache lookup failed for constraint %u", conId);
     567        6584 :     con = (Form_pg_constraint) GETSTRUCT(tup);
     568             : 
     569             :     /*
     570             :      * Special processing depending on what the constraint is for.
     571             :      */
     572        6584 :     if (OidIsValid(con->conrelid))
     573             :     {
     574             :         Relation    rel;
     575             : 
     576             :         /*
     577             :          * If the constraint is for a relation, open and exclusive-lock the
     578             :          * relation it's for.
     579             :          */
     580        6454 :         rel = table_open(con->conrelid, AccessExclusiveLock);
     581             : 
     582             :         /*
     583             :          * We need to update the relcheck count if it is a check constraint
     584             :          * being dropped.  This update will force backends to rebuild relcache
     585             :          * entries when we commit.
     586             :          */
     587        6454 :         if (con->contype == CONSTRAINT_CHECK)
     588             :         {
     589             :             Relation    pgrel;
     590             :             HeapTuple   relTup;
     591             :             Form_pg_class classForm;
     592             : 
     593         832 :             pgrel = table_open(RelationRelationId, RowExclusiveLock);
     594         832 :             relTup = SearchSysCacheCopy1(RELOID,
     595             :                                          ObjectIdGetDatum(con->conrelid));
     596         832 :             if (!HeapTupleIsValid(relTup))
     597           0 :                 elog(ERROR, "cache lookup failed for relation %u",
     598             :                      con->conrelid);
     599         832 :             classForm = (Form_pg_class) GETSTRUCT(relTup);
     600             : 
     601         832 :             if (classForm->relchecks == 0)   /* should not happen */
     602           0 :                 elog(ERROR, "relation \"%s\" has relchecks = 0",
     603             :                      RelationGetRelationName(rel));
     604         832 :             classForm->relchecks--;
     605             : 
     606         832 :             CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
     607             : 
     608         832 :             heap_freetuple(relTup);
     609             : 
     610         832 :             table_close(pgrel, RowExclusiveLock);
     611             :         }
     612             : 
     613             :         /* Keep lock on constraint's rel until end of xact */
     614        6454 :         table_close(rel, NoLock);
     615             :     }
     616         130 :     else if (OidIsValid(con->contypid))
     617             :     {
     618             :         /*
     619             :          * XXX for now, do nothing special when dropping a domain constraint
     620             :          *
     621             :          * Probably there should be some form of locking on the domain type,
     622             :          * but we have no such concept at the moment.
     623             :          */
     624             :     }
     625             :     else
     626           0 :         elog(ERROR, "constraint %u is not of a known type", conId);
     627             : 
     628             :     /* Fry the constraint itself */
     629        6584 :     CatalogTupleDelete(conDesc, &tup->t_self);
     630             : 
     631             :     /* Clean up */
     632        6584 :     ReleaseSysCache(tup);
     633        6584 :     table_close(conDesc, RowExclusiveLock);
     634        6584 : }
     635             : 
     636             : /*
     637             :  * RenameConstraintById
     638             :  *      Rename a constraint.
     639             :  *
     640             :  * Note: this isn't intended to be a user-exposed function; it doesn't check
     641             :  * permissions etc.  Currently this is only invoked when renaming an index
     642             :  * that is associated with a constraint, but it's made a little more general
     643             :  * than that with the expectation of someday having ALTER TABLE RENAME
     644             :  * CONSTRAINT.
     645             :  */
     646             : void
     647          60 : RenameConstraintById(Oid conId, const char *newname)
     648             : {
     649             :     Relation    conDesc;
     650             :     HeapTuple   tuple;
     651             :     Form_pg_constraint con;
     652             : 
     653          60 :     conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
     654             : 
     655          60 :     tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
     656          60 :     if (!HeapTupleIsValid(tuple))
     657           0 :         elog(ERROR, "cache lookup failed for constraint %u", conId);
     658          60 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
     659             : 
     660             :     /*
     661             :      * For user-friendliness, check whether the name is already in use.
     662             :      */
     663         116 :     if (OidIsValid(con->conrelid) &&
     664          56 :         ConstraintNameIsUsed(CONSTRAINT_RELATION,
     665             :                              con->conrelid,
     666             :                              newname))
     667           0 :         ereport(ERROR,
     668             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     669             :                  errmsg("constraint \"%s\" for relation \"%s\" already exists",
     670             :                         newname, get_rel_name(con->conrelid))));
     671          64 :     if (OidIsValid(con->contypid) &&
     672           4 :         ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
     673             :                              con->contypid,
     674             :                              newname))
     675           0 :         ereport(ERROR,
     676             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     677             :                  errmsg("constraint \"%s\" for domain %s already exists",
     678             :                         newname, format_type_be(con->contypid))));
     679             : 
     680             :     /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
     681          60 :     namestrcpy(&(con->conname), newname);
     682             : 
     683          60 :     CatalogTupleUpdate(conDesc, &tuple->t_self, tuple);
     684             : 
     685          60 :     InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
     686             : 
     687          60 :     heap_freetuple(tuple);
     688          60 :     table_close(conDesc, RowExclusiveLock);
     689          60 : }
     690             : 
     691             : /*
     692             :  * AlterConstraintNamespaces
     693             :  *      Find any constraints belonging to the specified object,
     694             :  *      and move them to the specified new namespace.
     695             :  *
     696             :  * isType indicates whether the owning object is a type or a relation.
     697             :  */
     698             : void
     699          52 : AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
     700             :                           Oid newNspId, bool isType, ObjectAddresses *objsMoved)
     701             : {
     702             :     Relation    conRel;
     703             :     ScanKeyData key[2];
     704             :     SysScanDesc scan;
     705             :     HeapTuple   tup;
     706             : 
     707          52 :     conRel = table_open(ConstraintRelationId, RowExclusiveLock);
     708             : 
     709          52 :     ScanKeyInit(&key[0],
     710             :                 Anum_pg_constraint_conrelid,
     711             :                 BTEqualStrategyNumber, F_OIDEQ,
     712          52 :                 ObjectIdGetDatum(isType ? InvalidOid : ownerId));
     713          52 :     ScanKeyInit(&key[1],
     714             :                 Anum_pg_constraint_contypid,
     715             :                 BTEqualStrategyNumber, F_OIDEQ,
     716             :                 ObjectIdGetDatum(isType ? ownerId : InvalidOid));
     717             : 
     718          52 :     scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
     719             :                               NULL, 2, key);
     720             : 
     721         148 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
     722             :     {
     723          44 :         Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
     724             :         ObjectAddress thisobj;
     725             : 
     726          44 :         thisobj.classId = ConstraintRelationId;
     727          44 :         thisobj.objectId = conform->oid;
     728          44 :         thisobj.objectSubId = 0;
     729             : 
     730          44 :         if (object_address_present(&thisobj, objsMoved))
     731           0 :             continue;
     732             : 
     733             :         /* Don't update if the object is already part of the namespace */
     734          44 :         if (conform->connamespace == oldNspId && oldNspId != newNspId)
     735             :         {
     736          32 :             tup = heap_copytuple(tup);
     737          32 :             conform = (Form_pg_constraint) GETSTRUCT(tup);
     738             : 
     739          32 :             conform->connamespace = newNspId;
     740             : 
     741          32 :             CatalogTupleUpdate(conRel, &tup->t_self, tup);
     742             : 
     743             :             /*
     744             :              * Note: currently, the constraint will not have its own
     745             :              * dependency on the namespace, so we don't need to do
     746             :              * changeDependencyFor().
     747             :              */
     748             :         }
     749             : 
     750          44 :         InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
     751             : 
     752          44 :         add_exact_object_address(&thisobj, objsMoved);
     753             :     }
     754             : 
     755          52 :     systable_endscan(scan);
     756             : 
     757          52 :     table_close(conRel, RowExclusiveLock);
     758          52 : }
     759             : 
     760             : /*
     761             :  * ConstraintSetParentConstraint
     762             :  *      Set a partition's constraint as child of its parent constraint,
     763             :  *      or remove the linkage if parentConstrId is InvalidOid.
     764             :  *
     765             :  * This updates the constraint's pg_constraint row to show it as inherited, and
     766             :  * adds PARTITION dependencies to prevent the constraint from being deleted
     767             :  * on its own.  Alternatively, reverse that.
     768             :  */
     769             : void
     770         164 : ConstraintSetParentConstraint(Oid childConstrId,
     771             :                               Oid parentConstrId,
     772             :                               Oid childTableId)
     773             : {
     774             :     Relation    constrRel;
     775             :     Form_pg_constraint constrForm;
     776             :     HeapTuple   tuple,
     777             :                 newtup;
     778             :     ObjectAddress depender;
     779             :     ObjectAddress referenced;
     780             : 
     781         164 :     constrRel = table_open(ConstraintRelationId, RowExclusiveLock);
     782         164 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(childConstrId));
     783         164 :     if (!HeapTupleIsValid(tuple))
     784           0 :         elog(ERROR, "cache lookup failed for constraint %u", childConstrId);
     785         164 :     newtup = heap_copytuple(tuple);
     786         164 :     constrForm = (Form_pg_constraint) GETSTRUCT(newtup);
     787         164 :     if (OidIsValid(parentConstrId))
     788             :     {
     789             :         /* don't allow setting parent for a constraint that already has one */
     790             :         Assert(constrForm->coninhcount == 0);
     791         116 :         if (constrForm->conparentid != InvalidOid)
     792           0 :             elog(ERROR, "constraint %u already has a parent constraint",
     793             :                  childConstrId);
     794             : 
     795         116 :         constrForm->conislocal = false;
     796         116 :         constrForm->coninhcount++;
     797         116 :         constrForm->conparentid = parentConstrId;
     798             : 
     799         116 :         CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
     800             : 
     801         116 :         ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
     802             : 
     803         116 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
     804         116 :         recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
     805             : 
     806         116 :         ObjectAddressSet(referenced, RelationRelationId, childTableId);
     807         116 :         recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
     808             :     }
     809             :     else
     810             :     {
     811          48 :         constrForm->coninhcount--;
     812          48 :         constrForm->conislocal = true;
     813          48 :         constrForm->conparentid = InvalidOid;
     814             : 
     815             :         /* Make sure there's no further inheritance. */
     816             :         Assert(constrForm->coninhcount == 0);
     817             : 
     818          48 :         CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
     819             : 
     820          48 :         deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
     821             :                                         ConstraintRelationId,
     822             :                                         DEPENDENCY_PARTITION_PRI);
     823          48 :         deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
     824             :                                         RelationRelationId,
     825             :                                         DEPENDENCY_PARTITION_SEC);
     826             :     }
     827             : 
     828         164 :     ReleaseSysCache(tuple);
     829         164 :     table_close(constrRel, RowExclusiveLock);
     830         164 : }
     831             : 
     832             : 
     833             : /*
     834             :  * get_relation_constraint_oid
     835             :  *      Find a constraint on the specified relation with the specified name.
     836             :  *      Returns constraint's OID.
     837             :  */
     838             : Oid
     839         186 : get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
     840             : {
     841             :     Relation    pg_constraint;
     842             :     HeapTuple   tuple;
     843             :     SysScanDesc scan;
     844             :     ScanKeyData skey[3];
     845         186 :     Oid         conOid = InvalidOid;
     846             : 
     847         186 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
     848             : 
     849         186 :     ScanKeyInit(&skey[0],
     850             :                 Anum_pg_constraint_conrelid,
     851             :                 BTEqualStrategyNumber, F_OIDEQ,
     852             :                 ObjectIdGetDatum(relid));
     853         186 :     ScanKeyInit(&skey[1],
     854             :                 Anum_pg_constraint_contypid,
     855             :                 BTEqualStrategyNumber, F_OIDEQ,
     856             :                 ObjectIdGetDatum(InvalidOid));
     857         186 :     ScanKeyInit(&skey[2],
     858             :                 Anum_pg_constraint_conname,
     859             :                 BTEqualStrategyNumber, F_NAMEEQ,
     860             :                 CStringGetDatum(conname));
     861             : 
     862         186 :     scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
     863             :                               NULL, 3, skey);
     864             : 
     865             :     /* There can be at most one matching row */
     866         186 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
     867         178 :         conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
     868             : 
     869         186 :     systable_endscan(scan);
     870             : 
     871             :     /* If no such constraint exists, complain */
     872         186 :     if (!OidIsValid(conOid) && !missing_ok)
     873           8 :         ereport(ERROR,
     874             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     875             :                  errmsg("constraint \"%s\" for table \"%s\" does not exist",
     876             :                         conname, get_rel_name(relid))));
     877             : 
     878         178 :     table_close(pg_constraint, AccessShareLock);
     879             : 
     880         178 :     return conOid;
     881             : }
     882             : 
     883             : /*
     884             :  * get_relation_constraint_attnos
     885             :  *      Find a constraint on the specified relation with the specified name
     886             :  *      and return the constrained columns.
     887             :  *
     888             :  * Returns a Bitmapset of the column attnos of the constrained columns, with
     889             :  * attnos being offset by FirstLowInvalidHeapAttributeNumber so that system
     890             :  * columns can be represented.
     891             :  *
     892             :  * *constraintOid is set to the OID of the constraint, or InvalidOid on
     893             :  * failure.
     894             :  */
     895             : Bitmapset *
     896          32 : get_relation_constraint_attnos(Oid relid, const char *conname,
     897             :                                bool missing_ok, Oid *constraintOid)
     898             : {
     899          32 :     Bitmapset  *conattnos = NULL;
     900             :     Relation    pg_constraint;
     901             :     HeapTuple   tuple;
     902             :     SysScanDesc scan;
     903             :     ScanKeyData skey[3];
     904             : 
     905             :     /* Set *constraintOid, to avoid complaints about uninitialized vars */
     906          32 :     *constraintOid = InvalidOid;
     907             : 
     908          32 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
     909             : 
     910          32 :     ScanKeyInit(&skey[0],
     911             :                 Anum_pg_constraint_conrelid,
     912             :                 BTEqualStrategyNumber, F_OIDEQ,
     913             :                 ObjectIdGetDatum(relid));
     914          32 :     ScanKeyInit(&skey[1],
     915             :                 Anum_pg_constraint_contypid,
     916             :                 BTEqualStrategyNumber, F_OIDEQ,
     917             :                 ObjectIdGetDatum(InvalidOid));
     918          32 :     ScanKeyInit(&skey[2],
     919             :                 Anum_pg_constraint_conname,
     920             :                 BTEqualStrategyNumber, F_NAMEEQ,
     921             :                 CStringGetDatum(conname));
     922             : 
     923          32 :     scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
     924             :                               NULL, 3, skey);
     925             : 
     926             :     /* There can be at most one matching row */
     927          32 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
     928             :     {
     929             :         Datum       adatum;
     930             :         bool        isNull;
     931             : 
     932          32 :         *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
     933             : 
     934             :         /* Extract the conkey array, ie, attnums of constrained columns */
     935          32 :         adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
     936             :                               RelationGetDescr(pg_constraint), &isNull);
     937          32 :         if (!isNull)
     938             :         {
     939             :             ArrayType  *arr;
     940             :             int         numcols;
     941             :             int16      *attnums;
     942             :             int         i;
     943             : 
     944          32 :             arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
     945          32 :             numcols = ARR_DIMS(arr)[0];
     946          32 :             if (ARR_NDIM(arr) != 1 ||
     947          32 :                 numcols < 0 ||
     948          64 :                 ARR_HASNULL(arr) ||
     949          32 :                 ARR_ELEMTYPE(arr) != INT2OID)
     950           0 :                 elog(ERROR, "conkey is not a 1-D smallint array");
     951          32 :             attnums = (int16 *) ARR_DATA_PTR(arr);
     952             : 
     953             :             /* Construct the result value */
     954          72 :             for (i = 0; i < numcols; i++)
     955             :             {
     956          40 :                 conattnos = bms_add_member(conattnos,
     957          40 :                                            attnums[i] - FirstLowInvalidHeapAttributeNumber);
     958             :             }
     959             :         }
     960             :     }
     961             : 
     962          32 :     systable_endscan(scan);
     963             : 
     964             :     /* If no such constraint exists, complain */
     965          32 :     if (!OidIsValid(*constraintOid) && !missing_ok)
     966           0 :         ereport(ERROR,
     967             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     968             :                  errmsg("constraint \"%s\" for table \"%s\" does not exist",
     969             :                         conname, get_rel_name(relid))));
     970             : 
     971          32 :     table_close(pg_constraint, AccessShareLock);
     972             : 
     973          32 :     return conattnos;
     974             : }
     975             : 
     976             : /*
     977             :  * Return the OID of the constraint associated with the given index in the
     978             :  * given relation; or InvalidOid if no such index is catalogued.
     979             :  */
     980             : Oid
     981         440 : get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
     982             : {
     983             :     Relation    pg_constraint;
     984             :     SysScanDesc scan;
     985             :     ScanKeyData key;
     986             :     HeapTuple   tuple;
     987         440 :     Oid         constraintId = InvalidOid;
     988             : 
     989         440 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
     990             : 
     991         440 :     ScanKeyInit(&key,
     992             :                 Anum_pg_constraint_conrelid,
     993             :                 BTEqualStrategyNumber,
     994             :                 F_OIDEQ,
     995             :                 ObjectIdGetDatum(relationId));
     996         440 :     scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId,
     997             :                               true, NULL, 1, &key);
     998         440 :     while ((tuple = systable_getnext(scan)) != NULL)
     999             :     {
    1000             :         Form_pg_constraint constrForm;
    1001             : 
    1002         208 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
    1003         208 :         if (constrForm->conindid == indexId)
    1004             :         {
    1005         204 :             constraintId = constrForm->oid;
    1006         204 :             break;
    1007             :         }
    1008             :     }
    1009         440 :     systable_endscan(scan);
    1010             : 
    1011         440 :     table_close(pg_constraint, AccessShareLock);
    1012         440 :     return constraintId;
    1013             : }
    1014             : 
    1015             : /*
    1016             :  * get_domain_constraint_oid
    1017             :  *      Find a constraint on the specified domain with the specified name.
    1018             :  *      Returns constraint's OID.
    1019             :  */
    1020             : Oid
    1021          38 : get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
    1022             : {
    1023             :     Relation    pg_constraint;
    1024             :     HeapTuple   tuple;
    1025             :     SysScanDesc scan;
    1026             :     ScanKeyData skey[3];
    1027          38 :     Oid         conOid = InvalidOid;
    1028             : 
    1029          38 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
    1030             : 
    1031          38 :     ScanKeyInit(&skey[0],
    1032             :                 Anum_pg_constraint_conrelid,
    1033             :                 BTEqualStrategyNumber, F_OIDEQ,
    1034             :                 ObjectIdGetDatum(InvalidOid));
    1035          38 :     ScanKeyInit(&skey[1],
    1036             :                 Anum_pg_constraint_contypid,
    1037             :                 BTEqualStrategyNumber, F_OIDEQ,
    1038             :                 ObjectIdGetDatum(typid));
    1039          38 :     ScanKeyInit(&skey[2],
    1040             :                 Anum_pg_constraint_conname,
    1041             :                 BTEqualStrategyNumber, F_NAMEEQ,
    1042             :                 CStringGetDatum(conname));
    1043             : 
    1044          38 :     scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
    1045             :                               NULL, 3, skey);
    1046             : 
    1047             :     /* There can be at most one matching row */
    1048          38 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1049          34 :         conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
    1050             : 
    1051          38 :     systable_endscan(scan);
    1052             : 
    1053             :     /* If no such constraint exists, complain */
    1054          38 :     if (!OidIsValid(conOid) && !missing_ok)
    1055           4 :         ereport(ERROR,
    1056             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1057             :                  errmsg("constraint \"%s\" for domain %s does not exist",
    1058             :                         conname, format_type_be(typid))));
    1059             : 
    1060          34 :     table_close(pg_constraint, AccessShareLock);
    1061             : 
    1062          34 :     return conOid;
    1063             : }
    1064             : 
    1065             : /*
    1066             :  * get_primary_key_attnos
    1067             :  *      Identify the columns in a relation's primary key, if any.
    1068             :  *
    1069             :  * Returns a Bitmapset of the column attnos of the primary key's columns,
    1070             :  * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
    1071             :  * system columns can be represented.
    1072             :  *
    1073             :  * If there is no primary key, return NULL.  We also return NULL if the pkey
    1074             :  * constraint is deferrable and deferrableOk is false.
    1075             :  *
    1076             :  * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
    1077             :  * on failure.
    1078             :  */
    1079             : Bitmapset *
    1080         414 : get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
    1081             : {
    1082         414 :     Bitmapset  *pkattnos = NULL;
    1083             :     Relation    pg_constraint;
    1084             :     HeapTuple   tuple;
    1085             :     SysScanDesc scan;
    1086             :     ScanKeyData skey[1];
    1087             : 
    1088             :     /* Set *constraintOid, to avoid complaints about uninitialized vars */
    1089         414 :     *constraintOid = InvalidOid;
    1090             : 
    1091             :     /* Scan pg_constraint for constraints of the target rel */
    1092         414 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
    1093             : 
    1094         414 :     ScanKeyInit(&skey[0],
    1095             :                 Anum_pg_constraint_conrelid,
    1096             :                 BTEqualStrategyNumber, F_OIDEQ,
    1097             :                 ObjectIdGetDatum(relid));
    1098             : 
    1099         414 :     scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
    1100             :                               NULL, 1, skey);
    1101             : 
    1102         414 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1103             :     {
    1104         226 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
    1105             :         Datum       adatum;
    1106             :         bool        isNull;
    1107             :         ArrayType  *arr;
    1108             :         int16      *attnums;
    1109             :         int         numkeys;
    1110             :         int         i;
    1111             : 
    1112             :         /* Skip constraints that are not PRIMARY KEYs */
    1113         226 :         if (con->contype != CONSTRAINT_PRIMARY)
    1114          78 :             continue;
    1115             : 
    1116             :         /*
    1117             :          * If the primary key is deferrable, but we've been instructed to
    1118             :          * ignore deferrable constraints, then we might as well give up
    1119             :          * searching, since there can only be a single primary key on a table.
    1120             :          */
    1121         148 :         if (con->condeferrable && !deferrableOk)
    1122         152 :             break;
    1123             : 
    1124             :         /* Extract the conkey array, ie, attnums of PK's columns */
    1125         144 :         adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
    1126             :                               RelationGetDescr(pg_constraint), &isNull);
    1127         144 :         if (isNull)
    1128           0 :             elog(ERROR, "null conkey for constraint %u",
    1129             :                  ((Form_pg_constraint) GETSTRUCT(tuple))->oid);
    1130         144 :         arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    1131         144 :         numkeys = ARR_DIMS(arr)[0];
    1132         144 :         if (ARR_NDIM(arr) != 1 ||
    1133         144 :             numkeys < 0 ||
    1134         288 :             ARR_HASNULL(arr) ||
    1135         144 :             ARR_ELEMTYPE(arr) != INT2OID)
    1136           0 :             elog(ERROR, "conkey is not a 1-D smallint array");
    1137         144 :         attnums = (int16 *) ARR_DATA_PTR(arr);
    1138             : 
    1139             :         /* Construct the result value */
    1140         328 :         for (i = 0; i < numkeys; i++)
    1141             :         {
    1142         184 :             pkattnos = bms_add_member(pkattnos,
    1143         184 :                                       attnums[i] - FirstLowInvalidHeapAttributeNumber);
    1144             :         }
    1145         144 :         *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
    1146             : 
    1147             :         /* No need to search further */
    1148         144 :         break;
    1149             :     }
    1150             : 
    1151         414 :     systable_endscan(scan);
    1152             : 
    1153         414 :     table_close(pg_constraint, AccessShareLock);
    1154             : 
    1155         414 :     return pkattnos;
    1156             : }
    1157             : 
    1158             : /*
    1159             :  * Extract data from the pg_constraint tuple of a foreign-key constraint.
    1160             :  *
    1161             :  * All arguments save the first are output arguments; the last three of them
    1162             :  * can be passed as NULL if caller doesn't need them.
    1163             :  */
    1164             : void
    1165        4332 : DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
    1166             :                            AttrNumber *conkey, AttrNumber *confkey,
    1167             :                            Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs)
    1168             : {
    1169             :     Oid         constrId;
    1170             :     Datum       adatum;
    1171             :     bool        isNull;
    1172             :     ArrayType  *arr;
    1173             :     int         numkeys;
    1174             : 
    1175        4332 :     constrId = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
    1176             : 
    1177             :     /*
    1178             :      * We expect the arrays to be 1-D arrays of the right types; verify that.
    1179             :      * We don't need to use deconstruct_array() since the array data is just
    1180             :      * going to look like a C array of values.
    1181             :      */
    1182        4332 :     adatum = SysCacheGetAttr(CONSTROID, tuple,
    1183             :                              Anum_pg_constraint_conkey, &isNull);
    1184        4332 :     if (isNull)
    1185           0 :         elog(ERROR, "null conkey for constraint %u", constrId);
    1186        4332 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    1187        8664 :     if (ARR_NDIM(arr) != 1 ||
    1188        8664 :         ARR_HASNULL(arr) ||
    1189        4332 :         ARR_ELEMTYPE(arr) != INT2OID)
    1190           0 :         elog(ERROR, "conkey is not a 1-D smallint array");
    1191        4332 :     numkeys = ARR_DIMS(arr)[0];
    1192        4332 :     if (numkeys <= 0 || numkeys > INDEX_MAX_KEYS)
    1193           0 :         elog(ERROR, "foreign key constraint cannot have %d columns", numkeys);
    1194        4332 :     memcpy(conkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
    1195        4332 :     if ((Pointer) arr != DatumGetPointer(adatum))
    1196        4332 :         pfree(arr);             /* free de-toasted copy, if any */
    1197             : 
    1198        4332 :     adatum = SysCacheGetAttr(CONSTROID, tuple,
    1199             :                              Anum_pg_constraint_confkey, &isNull);
    1200        4332 :     if (isNull)
    1201           0 :         elog(ERROR, "null confkey for constraint %u", constrId);
    1202        4332 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    1203        8664 :     if (ARR_NDIM(arr) != 1 ||
    1204        8664 :         ARR_DIMS(arr)[0] != numkeys ||
    1205        8664 :         ARR_HASNULL(arr) ||
    1206        4332 :         ARR_ELEMTYPE(arr) != INT2OID)
    1207           0 :         elog(ERROR, "confkey is not a 1-D smallint array");
    1208        4332 :     memcpy(confkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
    1209        4332 :     if ((Pointer) arr != DatumGetPointer(adatum))
    1210        4332 :         pfree(arr);             /* free de-toasted copy, if any */
    1211             : 
    1212        4332 :     if (pf_eq_oprs)
    1213             :     {
    1214        4332 :         adatum = SysCacheGetAttr(CONSTROID, tuple,
    1215             :                                  Anum_pg_constraint_conpfeqop, &isNull);
    1216        4332 :         if (isNull)
    1217           0 :             elog(ERROR, "null conpfeqop for constraint %u", constrId);
    1218        4332 :         arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    1219             :         /* see TryReuseForeignKey if you change the test below */
    1220        8664 :         if (ARR_NDIM(arr) != 1 ||
    1221        8664 :             ARR_DIMS(arr)[0] != numkeys ||
    1222        8664 :             ARR_HASNULL(arr) ||
    1223        4332 :             ARR_ELEMTYPE(arr) != OIDOID)
    1224           0 :             elog(ERROR, "conpfeqop is not a 1-D Oid array");
    1225        4332 :         memcpy(pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
    1226        4332 :         if ((Pointer) arr != DatumGetPointer(adatum))
    1227        4332 :             pfree(arr);         /* free de-toasted copy, if any */
    1228             :     }
    1229             : 
    1230        4332 :     if (pp_eq_oprs)
    1231             :     {
    1232        2808 :         adatum = SysCacheGetAttr(CONSTROID, tuple,
    1233             :                                  Anum_pg_constraint_conppeqop, &isNull);
    1234        2808 :         if (isNull)
    1235           0 :             elog(ERROR, "null conppeqop for constraint %u", constrId);
    1236        2808 :         arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    1237        5616 :         if (ARR_NDIM(arr) != 1 ||
    1238        5616 :             ARR_DIMS(arr)[0] != numkeys ||
    1239        5616 :             ARR_HASNULL(arr) ||
    1240        2808 :             ARR_ELEMTYPE(arr) != OIDOID)
    1241           0 :             elog(ERROR, "conppeqop is not a 1-D Oid array");
    1242        2808 :         memcpy(pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
    1243        2808 :         if ((Pointer) arr != DatumGetPointer(adatum))
    1244        2808 :             pfree(arr);         /* free de-toasted copy, if any */
    1245             :     }
    1246             : 
    1247        4332 :     if (ff_eq_oprs)
    1248             :     {
    1249        2808 :         adatum = SysCacheGetAttr(CONSTROID, tuple,
    1250             :                                  Anum_pg_constraint_conffeqop, &isNull);
    1251        2808 :         if (isNull)
    1252           0 :             elog(ERROR, "null conffeqop for constraint %u", constrId);
    1253        2808 :         arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    1254        5616 :         if (ARR_NDIM(arr) != 1 ||
    1255        5616 :             ARR_DIMS(arr)[0] != numkeys ||
    1256        5616 :             ARR_HASNULL(arr) ||
    1257        2808 :             ARR_ELEMTYPE(arr) != OIDOID)
    1258           0 :             elog(ERROR, "conffeqop is not a 1-D Oid array");
    1259        2808 :         memcpy(ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
    1260        2808 :         if ((Pointer) arr != DatumGetPointer(adatum))
    1261        2808 :             pfree(arr);         /* free de-toasted copy, if any */
    1262             :     }
    1263             : 
    1264        4332 :     *numfks = numkeys;
    1265        4332 : }
    1266             : 
    1267             : /*
    1268             :  * Determine whether a relation can be proven functionally dependent on
    1269             :  * a set of grouping columns.  If so, return true and add the pg_constraint
    1270             :  * OIDs of the constraints needed for the proof to the *constraintDeps list.
    1271             :  *
    1272             :  * grouping_columns is a list of grouping expressions, in which columns of
    1273             :  * the rel of interest are Vars with the indicated varno/varlevelsup.
    1274             :  *
    1275             :  * Currently we only check to see if the rel has a primary key that is a
    1276             :  * subset of the grouping_columns.  We could also use plain unique constraints
    1277             :  * if all their columns are known not null, but there's a problem: we need
    1278             :  * to be able to represent the not-null-ness as part of the constraints added
    1279             :  * to *constraintDeps.  FIXME whenever not-null constraints get represented
    1280             :  * in pg_constraint.
    1281             :  */
    1282             : bool
    1283         130 : check_functional_grouping(Oid relid,
    1284             :                           Index varno, Index varlevelsup,
    1285             :                           List *grouping_columns,
    1286             :                           List **constraintDeps)
    1287             : {
    1288             :     Bitmapset  *pkattnos;
    1289             :     Bitmapset  *groupbyattnos;
    1290             :     Oid         constraintOid;
    1291             :     ListCell   *gl;
    1292             : 
    1293             :     /* If the rel has no PK, then we can't prove functional dependency */
    1294         130 :     pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
    1295         130 :     if (pkattnos == NULL)
    1296          24 :         return false;
    1297             : 
    1298             :     /* Identify all the rel's columns that appear in grouping_columns */
    1299         106 :     groupbyattnos = NULL;
    1300         240 :     foreach(gl, grouping_columns)
    1301             :     {
    1302         134 :         Var        *gvar = (Var *) lfirst(gl);
    1303             : 
    1304         268 :         if (IsA(gvar, Var) &&
    1305         240 :             gvar->varno == varno &&
    1306         106 :             gvar->varlevelsup == varlevelsup)
    1307         106 :             groupbyattnos = bms_add_member(groupbyattnos,
    1308         106 :                                            gvar->varattno - FirstLowInvalidHeapAttributeNumber);
    1309             :     }
    1310             : 
    1311         106 :     if (bms_is_subset(pkattnos, groupbyattnos))
    1312             :     {
    1313             :         /* The PK is a subset of grouping_columns, so we win */
    1314          78 :         *constraintDeps = lappend_oid(*constraintDeps, constraintOid);
    1315          78 :         return true;
    1316             :     }
    1317             : 
    1318          28 :     return false;
    1319             : }

Generated by: LCOV version 1.13