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