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