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