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

Generated by: LCOV version 1.14