PostgreSQL Source Code  git master
pg_constraint.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_constraint.c
4  * routines to support manipulation of the pg_constraint relation
5  *
6  * Portions Copyright (c) 1996-2024, 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 "catalog/catalog.h"
22 #include "catalog/dependency.h"
23 #include "catalog/heap.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 "utils/array.h"
31 #include "utils/builtins.h"
32 #include "utils/fmgroids.h"
33 #include "utils/lsyscache.h"
34 #include "utils/rel.h"
35 #include "utils/syscache.h"
36 
37 
38 /*
39  * CreateConstraintEntry
40  * Create a constraint table entry.
41  *
42  * Subsidiary records (such as triggers or indexes to implement the
43  * constraint) are *not* created here. But we do make dependency links
44  * from the constraint to the things it depends on.
45  *
46  * The new constraint's OID is returned.
47  */
48 Oid
49 CreateConstraintEntry(const char *constraintName,
50  Oid constraintNamespace,
51  char constraintType,
52  bool isDeferrable,
53  bool isDeferred,
54  bool isValidated,
55  Oid parentConstrId,
56  Oid relId,
57  const int16 *constraintKey,
58  int constraintNKeys,
59  int constraintNTotalKeys,
60  Oid domainId,
61  Oid indexRelId,
62  Oid foreignRelId,
63  const int16 *foreignKey,
64  const Oid *pfEqOp,
65  const Oid *ppEqOp,
66  const Oid *ffEqOp,
67  int foreignNKeys,
68  char foreignUpdateType,
69  char foreignDeleteType,
70  const int16 *fkDeleteSetCols,
71  int numFkDeleteSetCols,
72  char foreignMatchType,
73  const Oid *exclOp,
74  Node *conExpr,
75  const char *conBin,
76  bool conIsLocal,
77  int conInhCount,
78  bool conNoInherit,
79  bool conPeriod,
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;
95  int i;
96  ObjectAddress conobject;
97  ObjectAddresses *addrs_auto;
98  ObjectAddresses *addrs_normal;
99 
100  conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
101 
102  Assert(constraintName);
103  namestrcpy(&cname, constraintName);
104 
105  /*
106  * Convert C arrays into Postgres arrays.
107  */
108  if (constraintNKeys > 0)
109  {
110  Datum *conkey;
111 
112  conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
113  for (i = 0; i < constraintNKeys; i++)
114  conkey[i] = Int16GetDatum(constraintKey[i]);
115  conkeyArray = construct_array_builtin(conkey, constraintNKeys, INT2OID);
116  }
117  else
118  conkeyArray = NULL;
119 
120  if (foreignNKeys > 0)
121  {
122  Datum *fkdatums;
123 
124  fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
125  for (i = 0; i < foreignNKeys; i++)
126  fkdatums[i] = Int16GetDatum(foreignKey[i]);
127  confkeyArray = construct_array_builtin(fkdatums, foreignNKeys, INT2OID);
128  for (i = 0; i < foreignNKeys; i++)
129  fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
130  conpfeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
131  for (i = 0; i < foreignNKeys; i++)
132  fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
133  conppeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
134  for (i = 0; i < foreignNKeys; i++)
135  fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
136  conffeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
137 
138  if (numFkDeleteSetCols > 0)
139  {
140  for (i = 0; i < numFkDeleteSetCols; i++)
141  fkdatums[i] = Int16GetDatum(fkDeleteSetCols[i]);
142  confdelsetcolsArray = construct_array_builtin(fkdatums, numFkDeleteSetCols, INT2OID);
143  }
144  else
145  confdelsetcolsArray = NULL;
146  }
147  else
148  {
149  confkeyArray = NULL;
150  conpfeqopArray = NULL;
151  conppeqopArray = NULL;
152  conffeqopArray = NULL;
153  confdelsetcolsArray = NULL;
154  }
155 
156  if (exclOp != NULL)
157  {
158  Datum *opdatums;
159 
160  opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
161  for (i = 0; i < constraintNKeys; i++)
162  opdatums[i] = ObjectIdGetDatum(exclOp[i]);
163  conexclopArray = construct_array_builtin(opdatums, constraintNKeys, OIDOID);
164  }
165  else
166  conexclopArray = NULL;
167 
168  /* initialize nulls and values */
169  for (i = 0; i < Natts_pg_constraint; i++)
170  {
171  nulls[i] = false;
172  values[i] = (Datum) NULL;
173  }
174 
175  conOid = GetNewOidWithIndex(conDesc, ConstraintOidIndexId,
176  Anum_pg_constraint_oid);
177  values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(conOid);
178  values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
179  values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
180  values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
181  values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
182  values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
183  values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
184  values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
185  values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
186  values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
187  values[Anum_pg_constraint_conparentid - 1] = ObjectIdGetDatum(parentConstrId);
188  values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
189  values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
190  values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
191  values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
192  values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
193  values[Anum_pg_constraint_coninhcount - 1] = Int16GetDatum(conInhCount);
194  values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
195  values[Anum_pg_constraint_conperiod - 1] = BoolGetDatum(conPeriod);
196 
197  if (conkeyArray)
198  values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
199  else
200  nulls[Anum_pg_constraint_conkey - 1] = true;
201 
202  if (confkeyArray)
203  values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
204  else
205  nulls[Anum_pg_constraint_confkey - 1] = true;
206 
207  if (conpfeqopArray)
208  values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
209  else
210  nulls[Anum_pg_constraint_conpfeqop - 1] = true;
211 
212  if (conppeqopArray)
213  values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
214  else
215  nulls[Anum_pg_constraint_conppeqop - 1] = true;
216 
217  if (conffeqopArray)
218  values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
219  else
220  nulls[Anum_pg_constraint_conffeqop - 1] = true;
221 
222  if (confdelsetcolsArray)
223  values[Anum_pg_constraint_confdelsetcols - 1] = PointerGetDatum(confdelsetcolsArray);
224  else
225  nulls[Anum_pg_constraint_confdelsetcols - 1] = true;
226 
227  if (conexclopArray)
228  values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
229  else
230  nulls[Anum_pg_constraint_conexclop - 1] = true;
231 
232  if (conBin)
233  values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
234  else
235  nulls[Anum_pg_constraint_conbin - 1] = true;
236 
237  tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
238 
239  CatalogTupleInsert(conDesc, tup);
240 
241  ObjectAddressSet(conobject, ConstraintRelationId, conOid);
242 
243  table_close(conDesc, RowExclusiveLock);
244 
245  /* Handle set of auto dependencies */
246  addrs_auto = new_object_addresses();
247 
248  if (OidIsValid(relId))
249  {
250  /*
251  * Register auto dependency from constraint to owning relation, or to
252  * specific column(s) if any are mentioned.
253  */
254  ObjectAddress relobject;
255 
256  if (constraintNTotalKeys > 0)
257  {
258  for (i = 0; i < constraintNTotalKeys; i++)
259  {
260  ObjectAddressSubSet(relobject, RelationRelationId, relId,
261  constraintKey[i]);
262  add_exact_object_address(&relobject, addrs_auto);
263  }
264  }
265  else
266  {
267  ObjectAddressSet(relobject, RelationRelationId, relId);
268  add_exact_object_address(&relobject, addrs_auto);
269  }
270  }
271 
272  if (OidIsValid(domainId))
273  {
274  /*
275  * Register auto dependency from constraint to owning domain
276  */
277  ObjectAddress domobject;
278 
279  ObjectAddressSet(domobject, TypeRelationId, domainId);
280  add_exact_object_address(&domobject, addrs_auto);
281  }
282 
283  record_object_address_dependencies(&conobject, addrs_auto,
285  free_object_addresses(addrs_auto);
286 
287  /* Handle set of normal dependencies */
288  addrs_normal = new_object_addresses();
289 
290  if (OidIsValid(foreignRelId))
291  {
292  /*
293  * Register normal dependency from constraint to foreign relation, or
294  * to specific column(s) if any are mentioned.
295  */
296  ObjectAddress relobject;
297 
298  if (foreignNKeys > 0)
299  {
300  for (i = 0; i < foreignNKeys; i++)
301  {
302  ObjectAddressSubSet(relobject, RelationRelationId,
303  foreignRelId, foreignKey[i]);
304  add_exact_object_address(&relobject, addrs_normal);
305  }
306  }
307  else
308  {
309  ObjectAddressSet(relobject, RelationRelationId, foreignRelId);
310  add_exact_object_address(&relobject, addrs_normal);
311  }
312  }
313 
314  if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN)
315  {
316  /*
317  * Register normal dependency on the unique index that supports a
318  * foreign-key constraint. (Note: for indexes associated with unique
319  * or primary-key constraints, the dependency runs the other way, and
320  * is not made here.)
321  */
322  ObjectAddress relobject;
323 
324  ObjectAddressSet(relobject, RelationRelationId, indexRelId);
325  add_exact_object_address(&relobject, addrs_normal);
326  }
327 
328  if (foreignNKeys > 0)
329  {
330  /*
331  * Register normal dependencies on the equality operators that support
332  * a foreign-key constraint. If the PK and FK types are the same then
333  * all three operators for a column are the same; otherwise they are
334  * different.
335  */
336  ObjectAddress oprobject;
337 
338  oprobject.classId = OperatorRelationId;
339  oprobject.objectSubId = 0;
340 
341  for (i = 0; i < foreignNKeys; i++)
342  {
343  oprobject.objectId = pfEqOp[i];
344  add_exact_object_address(&oprobject, addrs_normal);
345  if (ppEqOp[i] != pfEqOp[i])
346  {
347  oprobject.objectId = ppEqOp[i];
348  add_exact_object_address(&oprobject, addrs_normal);
349  }
350  if (ffEqOp[i] != pfEqOp[i])
351  {
352  oprobject.objectId = ffEqOp[i];
353  add_exact_object_address(&oprobject, addrs_normal);
354  }
355  }
356  }
357 
358  record_object_address_dependencies(&conobject, addrs_normal,
360  free_object_addresses(addrs_normal);
361 
362  /*
363  * We don't bother to register dependencies on the exclusion operators of
364  * an exclusion constraint. We assume they are members of the opclass
365  * supporting the index, so there's an indirect dependency via that. (This
366  * would be pretty dicey for cross-type operators, but exclusion operators
367  * can never be cross-type.)
368  */
369 
370  if (conExpr != NULL)
371  {
372  /*
373  * Register dependencies from constraint to objects mentioned in CHECK
374  * expression.
375  */
376  recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
378  DEPENDENCY_NORMAL, false);
379  }
380 
381  /* Post creation hook for new constraint */
382  InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
383  is_internal);
384 
385  return conOid;
386 }
387 
388 /*
389  * Test whether given name is currently used as a constraint name
390  * for the given object (relation or domain).
391  *
392  * This is used to decide whether to accept a user-specified constraint name.
393  * It is deliberately not the same test as ChooseConstraintName uses to decide
394  * whether an auto-generated name is OK: here, we will allow it unless there
395  * is an identical constraint name in use *on the same object*.
396  *
397  * NB: Caller should hold exclusive lock on the given object, else
398  * this test can be fooled by concurrent additions.
399  */
400 bool
402  const char *conname)
403 {
404  bool found;
405  Relation conDesc;
406  SysScanDesc conscan;
407  ScanKeyData skey[3];
408 
409  conDesc = table_open(ConstraintRelationId, AccessShareLock);
410 
411  ScanKeyInit(&skey[0],
412  Anum_pg_constraint_conrelid,
413  BTEqualStrategyNumber, F_OIDEQ,
415  ? objId : InvalidOid));
416  ScanKeyInit(&skey[1],
417  Anum_pg_constraint_contypid,
418  BTEqualStrategyNumber, F_OIDEQ,
420  ? objId : InvalidOid));
421  ScanKeyInit(&skey[2],
422  Anum_pg_constraint_conname,
423  BTEqualStrategyNumber, F_NAMEEQ,
424  CStringGetDatum(conname));
425 
426  conscan = systable_beginscan(conDesc, ConstraintRelidTypidNameIndexId,
427  true, NULL, 3, skey);
428 
429  /* There can be at most one matching row */
430  found = (HeapTupleIsValid(systable_getnext(conscan)));
431 
432  systable_endscan(conscan);
433  table_close(conDesc, AccessShareLock);
434 
435  return found;
436 }
437 
438 /*
439  * Does any constraint of the given name exist in the given namespace?
440  *
441  * This is used for code that wants to match ChooseConstraintName's rule
442  * that we should avoid autogenerating duplicate constraint names within a
443  * namespace.
444  */
445 bool
446 ConstraintNameExists(const char *conname, Oid namespaceid)
447 {
448  bool found;
449  Relation conDesc;
450  SysScanDesc conscan;
451  ScanKeyData skey[2];
452 
453  conDesc = table_open(ConstraintRelationId, AccessShareLock);
454 
455  ScanKeyInit(&skey[0],
456  Anum_pg_constraint_conname,
457  BTEqualStrategyNumber, F_NAMEEQ,
458  CStringGetDatum(conname));
459 
460  ScanKeyInit(&skey[1],
461  Anum_pg_constraint_connamespace,
462  BTEqualStrategyNumber, F_OIDEQ,
463  ObjectIdGetDatum(namespaceid));
464 
465  conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
466  NULL, 2, skey);
467 
468  found = (HeapTupleIsValid(systable_getnext(conscan)));
469 
470  systable_endscan(conscan);
471  table_close(conDesc, AccessShareLock);
472 
473  return found;
474 }
475 
476 /*
477  * Select a nonconflicting name for a new constraint.
478  *
479  * The objective here is to choose a name that is unique within the
480  * specified namespace. Postgres does not require this, but the SQL
481  * spec does, and some apps depend on it. Therefore we avoid choosing
482  * default names that so conflict.
483  *
484  * name1, name2, and label are used the same way as for makeObjectName(),
485  * except that the label can't be NULL; digits will be appended to the label
486  * if needed to create a name that is unique within the specified namespace.
487  *
488  * 'others' can be a list of string names already chosen within the current
489  * command (but not yet reflected into the catalogs); we will not choose
490  * a duplicate of one of these either.
491  *
492  * Note: it is theoretically possible to get a collision anyway, if someone
493  * else chooses the same name concurrently. This is fairly unlikely to be
494  * a problem in practice, especially if one is holding an exclusive lock on
495  * the relation identified by name1.
496  *
497  * Returns a palloc'd string.
498  */
499 char *
500 ChooseConstraintName(const char *name1, const char *name2,
501  const char *label, Oid namespaceid,
502  List *others)
503 {
504  int pass = 0;
505  char *conname = NULL;
506  char modlabel[NAMEDATALEN];
507  Relation conDesc;
508  SysScanDesc conscan;
509  ScanKeyData skey[2];
510  bool found;
511  ListCell *l;
512 
513  conDesc = table_open(ConstraintRelationId, AccessShareLock);
514 
515  /* try the unmodified label first */
516  strlcpy(modlabel, label, sizeof(modlabel));
517 
518  for (;;)
519  {
520  conname = makeObjectName(name1, name2, modlabel);
521 
522  found = false;
523 
524  foreach(l, others)
525  {
526  if (strcmp((char *) lfirst(l), conname) == 0)
527  {
528  found = true;
529  break;
530  }
531  }
532 
533  if (!found)
534  {
535  ScanKeyInit(&skey[0],
536  Anum_pg_constraint_conname,
537  BTEqualStrategyNumber, F_NAMEEQ,
538  CStringGetDatum(conname));
539 
540  ScanKeyInit(&skey[1],
541  Anum_pg_constraint_connamespace,
542  BTEqualStrategyNumber, F_OIDEQ,
543  ObjectIdGetDatum(namespaceid));
544 
545  conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
546  NULL, 2, skey);
547 
548  found = (HeapTupleIsValid(systable_getnext(conscan)));
549 
550  systable_endscan(conscan);
551  }
552 
553  if (!found)
554  break;
555 
556  /* found a conflict, so try a new name component */
557  pfree(conname);
558  snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
559  }
560 
561  table_close(conDesc, AccessShareLock);
562 
563  return conname;
564 }
565 
566 /*
567  * Find and return the pg_constraint tuple that implements a validated
568  * not-null constraint for the given column of the given relation.
569  *
570  * XXX This would be easier if we had pg_attribute.notnullconstr with the OID
571  * of the constraint that implements the not-null constraint for that column.
572  * I'm not sure it's worth the catalog bloat and de-normalization, however.
573  */
574 HeapTuple
576 {
577  Relation pg_constraint;
578  HeapTuple conTup,
579  retval = NULL;
580  SysScanDesc scan;
582 
583  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
584  ScanKeyInit(&key,
585  Anum_pg_constraint_conrelid,
586  BTEqualStrategyNumber, F_OIDEQ,
587  ObjectIdGetDatum(relid));
588  scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId,
589  true, NULL, 1, &key);
590 
591  while (HeapTupleIsValid(conTup = systable_getnext(scan)))
592  {
594  AttrNumber conkey;
595 
596  /*
597  * We're looking for a NOTNULL constraint that's marked validated,
598  * with the column we're looking for as the sole element in conkey.
599  */
600  if (con->contype != CONSTRAINT_NOTNULL)
601  continue;
602  if (!con->convalidated)
603  continue;
604 
605  conkey = extractNotNullColumn(conTup);
606  if (conkey != attnum)
607  continue;
608 
609  /* Found it */
610  retval = heap_copytuple(conTup);
611  break;
612  }
613 
614  systable_endscan(scan);
615  table_close(pg_constraint, AccessShareLock);
616 
617  return retval;
618 }
619 
620 /*
621  * Find and return the pg_constraint tuple that implements a validated
622  * not-null constraint for the given column of the given relation.
623  */
624 HeapTuple
625 findNotNullConstraint(Oid relid, const char *colname)
626 {
627  AttrNumber attnum = get_attnum(relid, colname);
628 
629  return findNotNullConstraintAttnum(relid, attnum);
630 }
631 
632 /*
633  * Given a pg_constraint tuple for a not-null constraint, return the column
634  * number it is for.
635  */
638 {
639  AttrNumber colnum;
640  Datum adatum;
641  ArrayType *arr;
642 
643  /* only tuples for not-null constraints should be given */
644  Assert(((Form_pg_constraint) GETSTRUCT(constrTup))->contype == CONSTRAINT_NOTNULL);
645 
646  adatum = SysCacheGetAttrNotNull(CONSTROID, constrTup,
647  Anum_pg_constraint_conkey);
648  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
649  if (ARR_NDIM(arr) != 1 ||
650  ARR_HASNULL(arr) ||
651  ARR_ELEMTYPE(arr) != INT2OID ||
652  ARR_DIMS(arr)[0] != 1)
653  elog(ERROR, "conkey is not a 1-D smallint array");
654 
655  memcpy(&colnum, ARR_DATA_PTR(arr), sizeof(AttrNumber));
656 
657  if ((Pointer) arr != DatumGetPointer(adatum))
658  pfree(arr); /* free de-toasted copy, if any */
659 
660  return colnum;
661 }
662 
663 /*
664  * AdjustNotNullInheritance1
665  * Adjust inheritance count for a single not-null constraint
666  *
667  * Adjust inheritance count, and possibly islocal status, for the not-null
668  * constraint row of the given column, if it exists, and return true.
669  * If no not-null constraint is found for the column, return false.
670  */
671 bool
673  bool is_no_inherit)
674 {
675  HeapTuple tup;
676 
677  tup = findNotNullConstraintAttnum(relid, attnum);
678  if (HeapTupleIsValid(tup))
679  {
680  Relation pg_constraint;
681  Form_pg_constraint conform;
682 
683  pg_constraint = table_open(ConstraintRelationId, RowExclusiveLock);
684  conform = (Form_pg_constraint) GETSTRUCT(tup);
685 
686  /*
687  * Don't let the NO INHERIT status change (but don't complain
688  * unnecessarily.) In the future it might be useful to let an
689  * inheritable constraint replace a non-inheritable one, but we'd need
690  * to recurse to children to get it added there.
691  */
692  if (is_no_inherit != conform->connoinherit)
693  ereport(ERROR,
694  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
695  errmsg("cannot change NO INHERIT status of inherited NOT NULL constraint \"%s\" on relation \"%s\"",
696  NameStr(conform->conname), get_rel_name(relid)));
697 
698  if (count > 0)
699  conform->coninhcount += count;
700 
701  /* sanity check */
702  if (conform->coninhcount < 0)
703  elog(ERROR, "invalid inhcount %d for constraint \"%s\" on relation \"%s\"",
704  conform->coninhcount, NameStr(conform->conname),
705  get_rel_name(relid));
706 
707  /*
708  * If the constraint is no longer inherited, mark it local. It's
709  * arguable that we should drop it instead, but it's hard to see that
710  * being better. The user can drop it manually later.
711  */
712  if (conform->coninhcount == 0)
713  conform->conislocal = true;
714 
715  CatalogTupleUpdate(pg_constraint, &tup->t_self, tup);
716 
717  table_close(pg_constraint, RowExclusiveLock);
718 
719  return true;
720  }
721 
722  return false;
723 }
724 
725 /*
726  * AdjustNotNullInheritance
727  * Adjust not-null constraints' inhcount/islocal for
728  * ALTER TABLE [NO] INHERITS
729  *
730  * Mark the NOT NULL constraints for the given relation columns as
731  * inherited, so that they can't be dropped.
732  *
733  * Caller must have checked beforehand that attnotnull was set for all
734  * columns. However, some of those could be set because of a primary
735  * key, so throw a proper user-visible error if one is not found.
736  */
737 void
738 AdjustNotNullInheritance(Oid relid, Bitmapset *columns, int count)
739 {
740  Relation pg_constraint;
741  int attnum;
742 
743  pg_constraint = table_open(ConstraintRelationId, RowExclusiveLock);
744 
745  /*
746  * Scan the set of columns and bump inhcount for each.
747  */
748  attnum = -1;
749  while ((attnum = bms_next_member(columns, attnum)) >= 0)
750  {
751  HeapTuple tup;
752  Form_pg_constraint conform;
753 
754  tup = findNotNullConstraintAttnum(relid, attnum);
755  if (!HeapTupleIsValid(tup))
756  ereport(ERROR,
757  errcode(ERRCODE_DATATYPE_MISMATCH),
758  errmsg("column \"%s\" in child table must be marked NOT NULL",
759  get_attname(relid, attnum,
760  false)));
761 
762  conform = (Form_pg_constraint) GETSTRUCT(tup);
763  conform->coninhcount += count;
764  if (conform->coninhcount < 0)
765  elog(ERROR, "invalid inhcount %d for constraint \"%s\" on relation \"%s\"",
766  conform->coninhcount, NameStr(conform->conname),
767  get_rel_name(relid));
768 
769  /*
770  * If the constraints are no longer inherited, mark them local. It's
771  * arguable that we should drop them instead, but it's hard to see
772  * that being better. The user can drop it manually later.
773  */
774  if (conform->coninhcount == 0)
775  conform->conislocal = true;
776 
777  CatalogTupleUpdate(pg_constraint, &tup->t_self, tup);
778  }
779 
780  table_close(pg_constraint, RowExclusiveLock);
781 }
782 
783 /*
784  * RelationGetNotNullConstraints
785  * Return the list of not-null constraints for the given rel
786  *
787  * Caller can request cooked constraints, or raw.
788  *
789  * This is seldom needed, so we just scan pg_constraint each time.
790  *
791  * XXX This is only used to create derived tables, so NO INHERIT constraints
792  * are always skipped.
793  */
794 List *
796 {
797  List *notnulls = NIL;
798  Relation constrRel;
799  HeapTuple htup;
800  SysScanDesc conscan;
801  ScanKeyData skey;
802 
803  constrRel = table_open(ConstraintRelationId, AccessShareLock);
804  ScanKeyInit(&skey,
805  Anum_pg_constraint_conrelid,
806  BTEqualStrategyNumber, F_OIDEQ,
807  ObjectIdGetDatum(relid));
808  conscan = systable_beginscan(constrRel, ConstraintRelidTypidNameIndexId, true,
809  NULL, 1, &skey);
810 
811  while (HeapTupleIsValid(htup = systable_getnext(conscan)))
812  {
814  AttrNumber colnum;
815 
816  if (conForm->contype != CONSTRAINT_NOTNULL)
817  continue;
818  if (conForm->connoinherit)
819  continue;
820 
821  colnum = extractNotNullColumn(htup);
822 
823  if (cooked)
824  {
825  CookedConstraint *cooked;
826 
827  cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
828 
829  cooked->contype = CONSTR_NOTNULL;
830  cooked->name = pstrdup(NameStr(conForm->conname));
831  cooked->attnum = colnum;
832  cooked->expr = NULL;
833  cooked->skip_validation = false;
834  cooked->is_local = true;
835  cooked->inhcount = 0;
836  cooked->is_no_inherit = conForm->connoinherit;
837 
838  notnulls = lappend(notnulls, cooked);
839  }
840  else
841  {
842  Constraint *constr;
843 
844  constr = makeNode(Constraint);
845  constr->contype = CONSTR_NOTNULL;
846  constr->conname = pstrdup(NameStr(conForm->conname));
847  constr->deferrable = false;
848  constr->initdeferred = false;
849  constr->location = -1;
850  constr->keys = list_make1(makeString(get_attname(relid, colnum,
851  false)));
852  constr->skip_validation = false;
853  constr->initially_valid = true;
854  notnulls = lappend(notnulls, constr);
855  }
856  }
857 
858  systable_endscan(conscan);
859  table_close(constrRel, AccessShareLock);
860 
861  return notnulls;
862 }
863 
864 
865 /*
866  * Delete a single constraint record.
867  */
868 void
870 {
871  Relation conDesc;
872  HeapTuple tup;
873  Form_pg_constraint con;
874 
875  conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
876 
877  tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
878  if (!HeapTupleIsValid(tup)) /* should not happen */
879  elog(ERROR, "cache lookup failed for constraint %u", conId);
880  con = (Form_pg_constraint) GETSTRUCT(tup);
881 
882  /*
883  * Special processing depending on what the constraint is for.
884  */
885  if (OidIsValid(con->conrelid))
886  {
887  Relation rel;
888 
889  /*
890  * If the constraint is for a relation, open and exclusive-lock the
891  * relation it's for.
892  */
893  rel = table_open(con->conrelid, AccessExclusiveLock);
894 
895  /*
896  * We need to update the relchecks count if it is a check constraint
897  * being dropped. This update will force backends to rebuild relcache
898  * entries when we commit.
899  */
900  if (con->contype == CONSTRAINT_CHECK)
901  {
902  Relation pgrel;
903  HeapTuple relTup;
904  Form_pg_class classForm;
905 
906  pgrel = table_open(RelationRelationId, RowExclusiveLock);
907  relTup = SearchSysCacheCopy1(RELOID,
908  ObjectIdGetDatum(con->conrelid));
909  if (!HeapTupleIsValid(relTup))
910  elog(ERROR, "cache lookup failed for relation %u",
911  con->conrelid);
912  classForm = (Form_pg_class) GETSTRUCT(relTup);
913 
914  if (classForm->relchecks == 0) /* should not happen */
915  elog(ERROR, "relation \"%s\" has relchecks = 0",
917  classForm->relchecks--;
918 
919  CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
920 
921  heap_freetuple(relTup);
922 
924  }
925 
926  /* Keep lock on constraint's rel until end of xact */
927  table_close(rel, NoLock);
928  }
929  else if (OidIsValid(con->contypid))
930  {
931  /*
932  * XXX for now, do nothing special when dropping a domain constraint
933  *
934  * Probably there should be some form of locking on the domain type,
935  * but we have no such concept at the moment.
936  */
937  }
938  else
939  elog(ERROR, "constraint %u is not of a known type", conId);
940 
941  /* Fry the constraint itself */
942  CatalogTupleDelete(conDesc, &tup->t_self);
943 
944  /* Clean up */
945  ReleaseSysCache(tup);
946  table_close(conDesc, RowExclusiveLock);
947 }
948 
949 /*
950  * RenameConstraintById
951  * Rename a constraint.
952  *
953  * Note: this isn't intended to be a user-exposed function; it doesn't check
954  * permissions etc. Currently this is only invoked when renaming an index
955  * that is associated with a constraint, but it's made a little more general
956  * than that with the expectation of someday having ALTER TABLE RENAME
957  * CONSTRAINT.
958  */
959 void
960 RenameConstraintById(Oid conId, const char *newname)
961 {
962  Relation conDesc;
963  HeapTuple tuple;
964  Form_pg_constraint con;
965 
966  conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
967 
968  tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
969  if (!HeapTupleIsValid(tuple))
970  elog(ERROR, "cache lookup failed for constraint %u", conId);
971  con = (Form_pg_constraint) GETSTRUCT(tuple);
972 
973  /*
974  * For user-friendliness, check whether the name is already in use.
975  */
976  if (OidIsValid(con->conrelid) &&
978  con->conrelid,
979  newname))
980  ereport(ERROR,
982  errmsg("constraint \"%s\" for relation \"%s\" already exists",
983  newname, get_rel_name(con->conrelid))));
984  if (OidIsValid(con->contypid) &&
986  con->contypid,
987  newname))
988  ereport(ERROR,
990  errmsg("constraint \"%s\" for domain %s already exists",
991  newname, format_type_be(con->contypid))));
992 
993  /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
994  namestrcpy(&(con->conname), newname);
995 
996  CatalogTupleUpdate(conDesc, &tuple->t_self, tuple);
997 
998  InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
999 
1000  heap_freetuple(tuple);
1001  table_close(conDesc, RowExclusiveLock);
1002 }
1003 
1004 /*
1005  * AlterConstraintNamespaces
1006  * Find any constraints belonging to the specified object,
1007  * and move them to the specified new namespace.
1008  *
1009  * isType indicates whether the owning object is a type or a relation.
1010  */
1011 void
1013  Oid newNspId, bool isType, ObjectAddresses *objsMoved)
1014 {
1015  Relation conRel;
1016  ScanKeyData key[2];
1017  SysScanDesc scan;
1018  HeapTuple tup;
1019 
1020  conRel = table_open(ConstraintRelationId, RowExclusiveLock);
1021 
1022  ScanKeyInit(&key[0],
1023  Anum_pg_constraint_conrelid,
1024  BTEqualStrategyNumber, F_OIDEQ,
1025  ObjectIdGetDatum(isType ? InvalidOid : ownerId));
1026  ScanKeyInit(&key[1],
1027  Anum_pg_constraint_contypid,
1028  BTEqualStrategyNumber, F_OIDEQ,
1029  ObjectIdGetDatum(isType ? ownerId : InvalidOid));
1030 
1031  scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
1032  NULL, 2, key);
1033 
1034  while (HeapTupleIsValid((tup = systable_getnext(scan))))
1035  {
1037  ObjectAddress thisobj;
1038 
1039  ObjectAddressSet(thisobj, ConstraintRelationId, conform->oid);
1040 
1041  if (object_address_present(&thisobj, objsMoved))
1042  continue;
1043 
1044  /* Don't update if the object is already part of the namespace */
1045  if (conform->connamespace == oldNspId && oldNspId != newNspId)
1046  {
1047  tup = heap_copytuple(tup);
1048  conform = (Form_pg_constraint) GETSTRUCT(tup);
1049 
1050  conform->connamespace = newNspId;
1051 
1052  CatalogTupleUpdate(conRel, &tup->t_self, tup);
1053 
1054  /*
1055  * Note: currently, the constraint will not have its own
1056  * dependency on the namespace, so we don't need to do
1057  * changeDependencyFor().
1058  */
1059  }
1060 
1061  InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
1062 
1063  add_exact_object_address(&thisobj, objsMoved);
1064  }
1065 
1066  systable_endscan(scan);
1067 
1068  table_close(conRel, RowExclusiveLock);
1069 }
1070 
1071 /*
1072  * ConstraintSetParentConstraint
1073  * Set a partition's constraint as child of its parent constraint,
1074  * or remove the linkage if parentConstrId is InvalidOid.
1075  *
1076  * This updates the constraint's pg_constraint row to show it as inherited, and
1077  * adds PARTITION dependencies to prevent the constraint from being deleted
1078  * on its own. Alternatively, reverse that.
1079  */
1080 void
1082  Oid parentConstrId,
1083  Oid childTableId)
1084 {
1085  Relation constrRel;
1086  Form_pg_constraint constrForm;
1087  HeapTuple tuple,
1088  newtup;
1089  ObjectAddress depender;
1090  ObjectAddress referenced;
1091 
1092  constrRel = table_open(ConstraintRelationId, RowExclusiveLock);
1093  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(childConstrId));
1094  if (!HeapTupleIsValid(tuple))
1095  elog(ERROR, "cache lookup failed for constraint %u", childConstrId);
1096  newtup = heap_copytuple(tuple);
1097  constrForm = (Form_pg_constraint) GETSTRUCT(newtup);
1098  if (OidIsValid(parentConstrId))
1099  {
1100  /* don't allow setting parent for a constraint that already has one */
1101  Assert(constrForm->coninhcount == 0);
1102  if (constrForm->conparentid != InvalidOid)
1103  elog(ERROR, "constraint %u already has a parent constraint",
1104  childConstrId);
1105 
1106  constrForm->conislocal = false;
1107  constrForm->coninhcount++;
1108  if (constrForm->coninhcount < 0)
1109  ereport(ERROR,
1110  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1111  errmsg("too many inheritance parents"));
1112  constrForm->conparentid = parentConstrId;
1113 
1114  CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
1115 
1116  ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
1117 
1118  ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
1119  recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
1120 
1121  ObjectAddressSet(referenced, RelationRelationId, childTableId);
1122  recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
1123  }
1124  else
1125  {
1126  constrForm->coninhcount--;
1127  constrForm->conislocal = true;
1128  constrForm->conparentid = InvalidOid;
1129 
1130  /* Make sure there's no further inheritance. */
1131  Assert(constrForm->coninhcount == 0);
1132 
1133  CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
1134 
1135  deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
1136  ConstraintRelationId,
1138  deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
1139  RelationRelationId,
1141  }
1142 
1143  ReleaseSysCache(tuple);
1144  table_close(constrRel, RowExclusiveLock);
1145 }
1146 
1147 
1148 /*
1149  * get_relation_constraint_oid
1150  * Find a constraint on the specified relation with the specified name.
1151  * Returns constraint's OID.
1152  */
1153 Oid
1154 get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
1155 {
1156  Relation pg_constraint;
1157  HeapTuple tuple;
1158  SysScanDesc scan;
1159  ScanKeyData skey[3];
1160  Oid conOid = InvalidOid;
1161 
1162  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1163 
1164  ScanKeyInit(&skey[0],
1165  Anum_pg_constraint_conrelid,
1166  BTEqualStrategyNumber, F_OIDEQ,
1167  ObjectIdGetDatum(relid));
1168  ScanKeyInit(&skey[1],
1169  Anum_pg_constraint_contypid,
1170  BTEqualStrategyNumber, F_OIDEQ,
1172  ScanKeyInit(&skey[2],
1173  Anum_pg_constraint_conname,
1174  BTEqualStrategyNumber, F_NAMEEQ,
1175  CStringGetDatum(conname));
1176 
1177  scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1178  NULL, 3, skey);
1179 
1180  /* There can be at most one matching row */
1181  if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1182  conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1183 
1184  systable_endscan(scan);
1185 
1186  /* If no such constraint exists, complain */
1187  if (!OidIsValid(conOid) && !missing_ok)
1188  ereport(ERROR,
1189  (errcode(ERRCODE_UNDEFINED_OBJECT),
1190  errmsg("constraint \"%s\" for table \"%s\" does not exist",
1191  conname, get_rel_name(relid))));
1192 
1193  table_close(pg_constraint, AccessShareLock);
1194 
1195  return conOid;
1196 }
1197 
1198 /*
1199  * get_relation_constraint_attnos
1200  * Find a constraint on the specified relation with the specified name
1201  * and return the constrained columns.
1202  *
1203  * Returns a Bitmapset of the column attnos of the constrained columns, with
1204  * attnos being offset by FirstLowInvalidHeapAttributeNumber so that system
1205  * columns can be represented.
1206  *
1207  * *constraintOid is set to the OID of the constraint, or InvalidOid on
1208  * failure.
1209  */
1210 Bitmapset *
1211 get_relation_constraint_attnos(Oid relid, const char *conname,
1212  bool missing_ok, Oid *constraintOid)
1213 {
1214  Bitmapset *conattnos = NULL;
1215  Relation pg_constraint;
1216  HeapTuple tuple;
1217  SysScanDesc scan;
1218  ScanKeyData skey[3];
1219 
1220  /* Set *constraintOid, to avoid complaints about uninitialized vars */
1221  *constraintOid = InvalidOid;
1222 
1223  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1224 
1225  ScanKeyInit(&skey[0],
1226  Anum_pg_constraint_conrelid,
1227  BTEqualStrategyNumber, F_OIDEQ,
1228  ObjectIdGetDatum(relid));
1229  ScanKeyInit(&skey[1],
1230  Anum_pg_constraint_contypid,
1231  BTEqualStrategyNumber, F_OIDEQ,
1233  ScanKeyInit(&skey[2],
1234  Anum_pg_constraint_conname,
1235  BTEqualStrategyNumber, F_NAMEEQ,
1236  CStringGetDatum(conname));
1237 
1238  scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1239  NULL, 3, skey);
1240 
1241  /* There can be at most one matching row */
1242  if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1243  {
1244  Datum adatum;
1245  bool isNull;
1246 
1247  *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1248 
1249  /* Extract the conkey array, ie, attnums of constrained columns */
1250  adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
1251  RelationGetDescr(pg_constraint), &isNull);
1252  if (!isNull)
1253  {
1254  ArrayType *arr;
1255  int numcols;
1256  int16 *attnums;
1257  int i;
1258 
1259  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1260  numcols = ARR_DIMS(arr)[0];
1261  if (ARR_NDIM(arr) != 1 ||
1262  numcols < 0 ||
1263  ARR_HASNULL(arr) ||
1264  ARR_ELEMTYPE(arr) != INT2OID)
1265  elog(ERROR, "conkey is not a 1-D smallint array");
1266  attnums = (int16 *) ARR_DATA_PTR(arr);
1267 
1268  /* Construct the result value */
1269  for (i = 0; i < numcols; i++)
1270  {
1271  conattnos = bms_add_member(conattnos,
1273  }
1274  }
1275  }
1276 
1277  systable_endscan(scan);
1278 
1279  /* If no such constraint exists, complain */
1280  if (!OidIsValid(*constraintOid) && !missing_ok)
1281  ereport(ERROR,
1282  (errcode(ERRCODE_UNDEFINED_OBJECT),
1283  errmsg("constraint \"%s\" for table \"%s\" does not exist",
1284  conname, get_rel_name(relid))));
1285 
1286  table_close(pg_constraint, AccessShareLock);
1287 
1288  return conattnos;
1289 }
1290 
1291 /*
1292  * Return the OID of the constraint enforced by the given index in the
1293  * given relation; or InvalidOid if no such index is cataloged.
1294  *
1295  * Much like get_constraint_index, this function is concerned only with the
1296  * one constraint that "owns" the given index. Therefore, constraints of
1297  * types other than unique, primary-key, and exclusion are ignored.
1298  */
1299 Oid
1301 {
1302  Relation pg_constraint;
1303  SysScanDesc scan;
1304  ScanKeyData key;
1305  HeapTuple tuple;
1306  Oid constraintId = InvalidOid;
1307 
1308  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1309 
1310  ScanKeyInit(&key,
1311  Anum_pg_constraint_conrelid,
1313  F_OIDEQ,
1314  ObjectIdGetDatum(relationId));
1315  scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId,
1316  true, NULL, 1, &key);
1317  while ((tuple = systable_getnext(scan)) != NULL)
1318  {
1319  Form_pg_constraint constrForm;
1320 
1321  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
1322 
1323  /* See above */
1324  if (constrForm->contype != CONSTRAINT_PRIMARY &&
1325  constrForm->contype != CONSTRAINT_UNIQUE &&
1326  constrForm->contype != CONSTRAINT_EXCLUSION)
1327  continue;
1328 
1329  if (constrForm->conindid == indexId)
1330  {
1331  constraintId = constrForm->oid;
1332  break;
1333  }
1334  }
1335  systable_endscan(scan);
1336 
1337  table_close(pg_constraint, AccessShareLock);
1338  return constraintId;
1339 }
1340 
1341 /*
1342  * get_domain_constraint_oid
1343  * Find a constraint on the specified domain with the specified name.
1344  * Returns constraint's OID.
1345  */
1346 Oid
1347 get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
1348 {
1349  Relation pg_constraint;
1350  HeapTuple tuple;
1351  SysScanDesc scan;
1352  ScanKeyData skey[3];
1353  Oid conOid = InvalidOid;
1354 
1355  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1356 
1357  ScanKeyInit(&skey[0],
1358  Anum_pg_constraint_conrelid,
1359  BTEqualStrategyNumber, F_OIDEQ,
1361  ScanKeyInit(&skey[1],
1362  Anum_pg_constraint_contypid,
1363  BTEqualStrategyNumber, F_OIDEQ,
1364  ObjectIdGetDatum(typid));
1365  ScanKeyInit(&skey[2],
1366  Anum_pg_constraint_conname,
1367  BTEqualStrategyNumber, F_NAMEEQ,
1368  CStringGetDatum(conname));
1369 
1370  scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1371  NULL, 3, skey);
1372 
1373  /* There can be at most one matching row */
1374  if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1375  conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1376 
1377  systable_endscan(scan);
1378 
1379  /* If no such constraint exists, complain */
1380  if (!OidIsValid(conOid) && !missing_ok)
1381  ereport(ERROR,
1382  (errcode(ERRCODE_UNDEFINED_OBJECT),
1383  errmsg("constraint \"%s\" for domain %s does not exist",
1384  conname, format_type_be(typid))));
1385 
1386  table_close(pg_constraint, AccessShareLock);
1387 
1388  return conOid;
1389 }
1390 
1391 /*
1392  * get_primary_key_attnos
1393  * Identify the columns in a relation's primary key, if any.
1394  *
1395  * Returns a Bitmapset of the column attnos of the primary key's columns,
1396  * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
1397  * system columns can be represented.
1398  *
1399  * If there is no primary key, return NULL. We also return NULL if the pkey
1400  * constraint is deferrable and deferrableOk is false.
1401  *
1402  * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
1403  * on failure.
1404  */
1405 Bitmapset *
1406 get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
1407 {
1408  Bitmapset *pkattnos = NULL;
1409  Relation pg_constraint;
1410  HeapTuple tuple;
1411  SysScanDesc scan;
1412  ScanKeyData skey[1];
1413 
1414  /* Set *constraintOid, to avoid complaints about uninitialized vars */
1415  *constraintOid = InvalidOid;
1416 
1417  /* Scan pg_constraint for constraints of the target rel */
1418  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1419 
1420  ScanKeyInit(&skey[0],
1421  Anum_pg_constraint_conrelid,
1422  BTEqualStrategyNumber, F_OIDEQ,
1423  ObjectIdGetDatum(relid));
1424 
1425  scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1426  NULL, 1, skey);
1427 
1428  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1429  {
1431  Datum adatum;
1432  bool isNull;
1433  ArrayType *arr;
1434  int16 *attnums;
1435  int numkeys;
1436  int i;
1437 
1438  /* Skip constraints that are not PRIMARY KEYs */
1439  if (con->contype != CONSTRAINT_PRIMARY)
1440  continue;
1441 
1442  /*
1443  * If the primary key is deferrable, but we've been instructed to
1444  * ignore deferrable constraints, then we might as well give up
1445  * searching, since there can only be a single primary key on a table.
1446  */
1447  if (con->condeferrable && !deferrableOk)
1448  break;
1449 
1450  /* Extract the conkey array, ie, attnums of PK's columns */
1451  adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
1452  RelationGetDescr(pg_constraint), &isNull);
1453  if (isNull)
1454  elog(ERROR, "null conkey for constraint %u",
1455  ((Form_pg_constraint) GETSTRUCT(tuple))->oid);
1456  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1457  numkeys = ARR_DIMS(arr)[0];
1458  if (ARR_NDIM(arr) != 1 ||
1459  numkeys < 0 ||
1460  ARR_HASNULL(arr) ||
1461  ARR_ELEMTYPE(arr) != INT2OID)
1462  elog(ERROR, "conkey is not a 1-D smallint array");
1463  attnums = (int16 *) ARR_DATA_PTR(arr);
1464 
1465  /* Construct the result value */
1466  for (i = 0; i < numkeys; i++)
1467  {
1468  pkattnos = bms_add_member(pkattnos,
1470  }
1471  *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1472 
1473  /* No need to search further */
1474  break;
1475  }
1476 
1477  systable_endscan(scan);
1478 
1479  table_close(pg_constraint, AccessShareLock);
1480 
1481  return pkattnos;
1482 }
1483 
1484 /*
1485  * Extract data from the pg_constraint tuple of a foreign-key constraint.
1486  *
1487  * All arguments save the first are output arguments. All output arguments
1488  * other than numfks, conkey and confkey can be passed as NULL if caller
1489  * doesn't need them.
1490  */
1491 void
1493  AttrNumber *conkey, AttrNumber *confkey,
1494  Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs,
1495  int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
1496 {
1497  Datum adatum;
1498  bool isNull;
1499  ArrayType *arr;
1500  int numkeys;
1501 
1502  /*
1503  * We expect the arrays to be 1-D arrays of the right types; verify that.
1504  * We don't need to use deconstruct_array() since the array data is just
1505  * going to look like a C array of values.
1506  */
1507  adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1508  Anum_pg_constraint_conkey);
1509  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1510  if (ARR_NDIM(arr) != 1 ||
1511  ARR_HASNULL(arr) ||
1512  ARR_ELEMTYPE(arr) != INT2OID)
1513  elog(ERROR, "conkey is not a 1-D smallint array");
1514  numkeys = ARR_DIMS(arr)[0];
1515  if (numkeys <= 0 || numkeys > INDEX_MAX_KEYS)
1516  elog(ERROR, "foreign key constraint cannot have %d columns", numkeys);
1517  memcpy(conkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
1518  if ((Pointer) arr != DatumGetPointer(adatum))
1519  pfree(arr); /* free de-toasted copy, if any */
1520 
1521  adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1522  Anum_pg_constraint_confkey);
1523  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1524  if (ARR_NDIM(arr) != 1 ||
1525  ARR_DIMS(arr)[0] != numkeys ||
1526  ARR_HASNULL(arr) ||
1527  ARR_ELEMTYPE(arr) != INT2OID)
1528  elog(ERROR, "confkey is not a 1-D smallint array");
1529  memcpy(confkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
1530  if ((Pointer) arr != DatumGetPointer(adatum))
1531  pfree(arr); /* free de-toasted copy, if any */
1532 
1533  if (pf_eq_oprs)
1534  {
1535  adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1536  Anum_pg_constraint_conpfeqop);
1537  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1538  /* see TryReuseForeignKey if you change the test below */
1539  if (ARR_NDIM(arr) != 1 ||
1540  ARR_DIMS(arr)[0] != numkeys ||
1541  ARR_HASNULL(arr) ||
1542  ARR_ELEMTYPE(arr) != OIDOID)
1543  elog(ERROR, "conpfeqop is not a 1-D Oid array");
1544  memcpy(pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1545  if ((Pointer) arr != DatumGetPointer(adatum))
1546  pfree(arr); /* free de-toasted copy, if any */
1547  }
1548 
1549  if (pp_eq_oprs)
1550  {
1551  adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1552  Anum_pg_constraint_conppeqop);
1553  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1554  if (ARR_NDIM(arr) != 1 ||
1555  ARR_DIMS(arr)[0] != numkeys ||
1556  ARR_HASNULL(arr) ||
1557  ARR_ELEMTYPE(arr) != OIDOID)
1558  elog(ERROR, "conppeqop is not a 1-D Oid array");
1559  memcpy(pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1560  if ((Pointer) arr != DatumGetPointer(adatum))
1561  pfree(arr); /* free de-toasted copy, if any */
1562  }
1563 
1564  if (ff_eq_oprs)
1565  {
1566  adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1567  Anum_pg_constraint_conffeqop);
1568  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1569  if (ARR_NDIM(arr) != 1 ||
1570  ARR_DIMS(arr)[0] != numkeys ||
1571  ARR_HASNULL(arr) ||
1572  ARR_ELEMTYPE(arr) != OIDOID)
1573  elog(ERROR, "conffeqop is not a 1-D Oid array");
1574  memcpy(ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1575  if ((Pointer) arr != DatumGetPointer(adatum))
1576  pfree(arr); /* free de-toasted copy, if any */
1577  }
1578 
1579  if (fk_del_set_cols)
1580  {
1581  adatum = SysCacheGetAttr(CONSTROID, tuple,
1582  Anum_pg_constraint_confdelsetcols, &isNull);
1583  if (isNull)
1584  {
1585  *num_fk_del_set_cols = 0;
1586  }
1587  else
1588  {
1589  int num_delete_cols;
1590 
1591  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1592  if (ARR_NDIM(arr) != 1 ||
1593  ARR_HASNULL(arr) ||
1594  ARR_ELEMTYPE(arr) != INT2OID)
1595  elog(ERROR, "confdelsetcols is not a 1-D smallint array");
1596  num_delete_cols = ARR_DIMS(arr)[0];
1597  memcpy(fk_del_set_cols, ARR_DATA_PTR(arr), num_delete_cols * sizeof(int16));
1598  if ((Pointer) arr != DatumGetPointer(adatum))
1599  pfree(arr); /* free de-toasted copy, if any */
1600 
1601  *num_fk_del_set_cols = num_delete_cols;
1602  }
1603  }
1604 
1605  *numfks = numkeys;
1606 }
1607 
1608 /*
1609  * Determine whether a relation can be proven functionally dependent on
1610  * a set of grouping columns. If so, return true and add the pg_constraint
1611  * OIDs of the constraints needed for the proof to the *constraintDeps list.
1612  *
1613  * grouping_columns is a list of grouping expressions, in which columns of
1614  * the rel of interest are Vars with the indicated varno/varlevelsup.
1615  *
1616  * Currently we only check to see if the rel has a primary key that is a
1617  * subset of the grouping_columns. We could also use plain unique constraints
1618  * if all their columns are known not null, but there's a problem: we need
1619  * to be able to represent the not-null-ness as part of the constraints added
1620  * to *constraintDeps. FIXME whenever not-null constraints get represented
1621  * in pg_constraint.
1622  */
1623 bool
1625  Index varno, Index varlevelsup,
1626  List *grouping_columns,
1627  List **constraintDeps)
1628 {
1629  Bitmapset *pkattnos;
1630  Bitmapset *groupbyattnos;
1631  Oid constraintOid;
1632  ListCell *gl;
1633 
1634  /* If the rel has no PK, then we can't prove functional dependency */
1635  pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
1636  if (pkattnos == NULL)
1637  return false;
1638 
1639  /* Identify all the rel's columns that appear in grouping_columns */
1640  groupbyattnos = NULL;
1641  foreach(gl, grouping_columns)
1642  {
1643  Var *gvar = (Var *) lfirst(gl);
1644 
1645  if (IsA(gvar, Var) &&
1646  gvar->varno == varno &&
1647  gvar->varlevelsup == varlevelsup)
1648  groupbyattnos = bms_add_member(groupbyattnos,
1650  }
1651 
1652  if (bms_is_subset(pkattnos, groupbyattnos))
1653  {
1654  /* The PK is a subset of grouping_columns, so we win */
1655  *constraintDeps = lappend_oid(*constraintDeps, constraintOid);
1656  return true;
1657  }
1658 
1659  return false;
1660 }
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3374
int16 AttrNumber
Definition: attnum.h:21
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:412
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
static Datum values[MAXATTR]
Definition: bootstrap.c:152
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define NameStr(name)
Definition: c.h:733
signed short int16
Definition: c.h:480
char * Pointer
Definition: c.h:470
unsigned int Index
Definition: c.h:601
#define OidIsValid(objectId)
Definition: c.h:762
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:391
void record_object_address_dependencies(const ObjectAddress *depender, ObjectAddresses *referenced, DependencyType behavior)
Definition: dependency.c:2738
void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, bool reverse_self)
Definition: dependency.c:1596
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2589
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2483
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2529
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2769
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:776
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:792
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
char * makeObjectName(const char *name1, const char *name2, const char *label)
Definition: indexcmds.c:2524
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:339
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
#define NoLock
Definition: lockdefs.h:34
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:858
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1906
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:827
char * pstrdup(const char *in)
Definition: mcxt.c:1683
void pfree(void *pointer)
Definition: mcxt.c:1508
void * palloc(Size size)
Definition: mcxt.c:1304
void namestrcpy(Name name, const char *str)
Definition: name.c:233
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define makeNode(_type_)
Definition: nodes.h:155
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
#define InvokeObjectPostCreateHookArg(classId, objectId, subId, is_internal)
Definition: objectaccess.h:175
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
@ CONSTR_NOTNULL
Definition: parsenodes.h:2553
int16 attnum
Definition: pg_attribute.h:74
static char * label
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#define INDEX_MAX_KEYS
#define NAMEDATALEN
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:49
void RemoveConstraintById(Oid conId)
void RenameConstraintById(Oid conId, const char *newname)
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
List * RelationGetNotNullConstraints(Oid relid, bool cooked)
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
void AdjustNotNullInheritance(Oid relid, Bitmapset *columns, int count)
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
bool ConstraintNameExists(const char *conname, Oid namespaceid)
bool check_functional_grouping(Oid relid, Index varno, Index varlevelsup, List *grouping_columns, List **constraintDeps)
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
bool AdjustNotNullInheritance1(Oid relid, AttrNumber attnum, int count, bool is_no_inherit)
Bitmapset * get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
Bitmapset * get_relation_constraint_attnos(Oid relid, const char *conname, bool missing_ok, Oid *constraintOid)
FormData_pg_constraint * Form_pg_constraint
ConstraintCategory
@ CONSTRAINT_DOMAIN
@ CONSTRAINT_RELATION
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:350
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
#define snprintf
Definition: port.h:238
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:373
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static Datum CharGetDatum(char X)
Definition: postgres.h:122
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
bool initdeferred
Definition: parsenodes.h:2586
List * keys
Definition: parsenodes.h:2598
ConstrType contype
Definition: parsenodes.h:2583
bool initially_valid
Definition: parsenodes.h:2588
bool skip_validation
Definition: parsenodes.h:2587
bool deferrable
Definition: parsenodes.h:2585
char * conname
Definition: parsenodes.h:2584
char * name
Definition: heap.h:40
AttrNumber attnum
Definition: heap.h:41
bool skip_validation
Definition: heap.h:43
bool is_no_inherit
Definition: heap.h:46
int inhcount
Definition: heap.h:45
bool is_local
Definition: heap.h:44
ConstrType contype
Definition: heap.h:37
Node * expr
Definition: heap.h:42
ItemPointerData t_self
Definition: htup.h:65
Definition: pg_list.h:54
Definition: nodes.h:129
Definition: primnodes.h:234
AttrNumber varattno
Definition: primnodes.h:246
int varno
Definition: primnodes.h:241
Index varlevelsup
Definition: primnodes.h:266
Definition: c.h:728
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:479
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:510
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:86
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
String * makeString(char *str)
Definition: value.c:63