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

Generated by: LCOV version 1.13