1 /*-------------------------------------------------------------------------
2 *
3 * pg_constraint.c
4 * routines to support manipulation of the pg_constraint relation
5 *
6 * Portions Copyright (c) 1996-2016, 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/heapam.h"
19 #include "access/htup_details.h"
20 #include "access/sysattr.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/objectaccess.h"
24 #include "catalog/pg_constraint.h"
25 #include "catalog/pg_constraint_fn.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 #include "utils/tqual.h"
36
37
38 /*
39 * CreateConstraintEntry
40 * Create a constraint table entry.
41 *
42 * Subsidiary records (such as triggers or indexes to implement the
43 * constraint) are *not* created here. But we do make dependency links
44 * from the constraint to the things it depends on.
45 *
46 * The new constraint's OID is returned.
47 */
48 Oid
CreateConstraintEntry(const char * constraintName,Oid constraintNamespace,char constraintType,bool isDeferrable,bool isDeferred,bool isValidated,Oid relId,const int16 * constraintKey,int constraintNKeys,Oid domainId,Oid indexRelId,Oid foreignRelId,const int16 * foreignKey,const Oid * pfEqOp,const Oid * ppEqOp,const Oid * ffEqOp,int foreignNKeys,char foreignUpdateType,char foreignDeleteType,char foreignMatchType,const Oid * exclOp,Node * conExpr,const char * conBin,const char * conSrc,bool conIsLocal,int conInhCount,bool conNoInherit,bool is_internal)49 CreateConstraintEntry(const char *constraintName,
50 Oid constraintNamespace,
51 char constraintType,
52 bool isDeferrable,
53 bool isDeferred,
54 bool isValidated,
55 Oid relId,
56 const int16 *constraintKey,
57 int constraintNKeys,
58 Oid domainId,
59 Oid indexRelId,
60 Oid foreignRelId,
61 const int16 *foreignKey,
62 const Oid *pfEqOp,
63 const Oid *ppEqOp,
64 const Oid *ffEqOp,
65 int foreignNKeys,
66 char foreignUpdateType,
67 char foreignDeleteType,
68 char foreignMatchType,
69 const Oid *exclOp,
70 Node *conExpr,
71 const char *conBin,
72 const char *conSrc,
73 bool conIsLocal,
74 int conInhCount,
75 bool conNoInherit,
76 bool is_internal)
77 {
78 Relation conDesc;
79 Oid conOid;
80 HeapTuple tup;
81 bool nulls[Natts_pg_constraint];
82 Datum values[Natts_pg_constraint];
83 ArrayType *conkeyArray;
84 ArrayType *confkeyArray;
85 ArrayType *conpfeqopArray;
86 ArrayType *conppeqopArray;
87 ArrayType *conffeqopArray;
88 ArrayType *conexclopArray;
89 NameData cname;
90 int i;
91 ObjectAddress conobject;
92
93 conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
94
95 Assert(constraintName);
96 namestrcpy(&cname, constraintName);
97
98 /*
99 * Convert C arrays into Postgres arrays.
100 */
101 if (constraintNKeys > 0)
102 {
103 Datum *conkey;
104
105 conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
106 for (i = 0; i < constraintNKeys; i++)
107 conkey[i] = Int16GetDatum(constraintKey[i]);
108 conkeyArray = construct_array(conkey, constraintNKeys,
109 INT2OID, 2, true, 's');
110 }
111 else
112 conkeyArray = NULL;
113
114 if (foreignNKeys > 0)
115 {
116 Datum *fkdatums;
117
118 fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
119 for (i = 0; i < foreignNKeys; i++)
120 fkdatums[i] = Int16GetDatum(foreignKey[i]);
121 confkeyArray = construct_array(fkdatums, foreignNKeys,
122 INT2OID, 2, true, 's');
123 for (i = 0; i < foreignNKeys; i++)
124 fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
125 conpfeqopArray = construct_array(fkdatums, foreignNKeys,
126 OIDOID, sizeof(Oid), true, 'i');
127 for (i = 0; i < foreignNKeys; i++)
128 fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
129 conppeqopArray = construct_array(fkdatums, foreignNKeys,
130 OIDOID, sizeof(Oid), true, 'i');
131 for (i = 0; i < foreignNKeys; i++)
132 fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
133 conffeqopArray = construct_array(fkdatums, foreignNKeys,
134 OIDOID, sizeof(Oid), true, 'i');
135 }
136 else
137 {
138 confkeyArray = NULL;
139 conpfeqopArray = NULL;
140 conppeqopArray = NULL;
141 conffeqopArray = NULL;
142 }
143
144 if (exclOp != NULL)
145 {
146 Datum *opdatums;
147
148 opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
149 for (i = 0; i < constraintNKeys; i++)
150 opdatums[i] = ObjectIdGetDatum(exclOp[i]);
151 conexclopArray = construct_array(opdatums, constraintNKeys,
152 OIDOID, sizeof(Oid), true, 'i');
153 }
154 else
155 conexclopArray = NULL;
156
157 /* initialize nulls and values */
158 for (i = 0; i < Natts_pg_constraint; i++)
159 {
160 nulls[i] = false;
161 values[i] = (Datum) NULL;
162 }
163
164 values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
165 values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
166 values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
167 values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
168 values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
169 values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
170 values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
171 values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
172 values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
173 values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
174 values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
175 values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
176 values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
177 values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
178 values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
179 values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
180
181 if (conkeyArray)
182 values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
183 else
184 nulls[Anum_pg_constraint_conkey - 1] = true;
185
186 if (confkeyArray)
187 values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
188 else
189 nulls[Anum_pg_constraint_confkey - 1] = true;
190
191 if (conpfeqopArray)
192 values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
193 else
194 nulls[Anum_pg_constraint_conpfeqop - 1] = true;
195
196 if (conppeqopArray)
197 values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
198 else
199 nulls[Anum_pg_constraint_conppeqop - 1] = true;
200
201 if (conffeqopArray)
202 values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
203 else
204 nulls[Anum_pg_constraint_conffeqop - 1] = true;
205
206 if (conexclopArray)
207 values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
208 else
209 nulls[Anum_pg_constraint_conexclop - 1] = true;
210
211 /*
212 * initialize the binary form of the check constraint.
213 */
214 if (conBin)
215 values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
216 else
217 nulls[Anum_pg_constraint_conbin - 1] = true;
218
219 /*
220 * initialize the text form of the check constraint
221 */
222 if (conSrc)
223 values[Anum_pg_constraint_consrc - 1] = CStringGetTextDatum(conSrc);
224 else
225 nulls[Anum_pg_constraint_consrc - 1] = true;
226
227 tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
228
229 conOid = simple_heap_insert(conDesc, tup);
230
231 /* update catalog indexes */
232 CatalogUpdateIndexes(conDesc, tup);
233
234 conobject.classId = ConstraintRelationId;
235 conobject.objectId = conOid;
236 conobject.objectSubId = 0;
237
238 heap_close(conDesc, RowExclusiveLock);
239
240 if (OidIsValid(relId))
241 {
242 /*
243 * Register auto dependency from constraint to owning relation, or to
244 * specific column(s) if any are mentioned.
245 */
246 ObjectAddress relobject;
247
248 relobject.classId = RelationRelationId;
249 relobject.objectId = relId;
250 if (constraintNKeys > 0)
251 {
252 for (i = 0; i < constraintNKeys; i++)
253 {
254 relobject.objectSubId = constraintKey[i];
255
256 recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
257 }
258 }
259 else
260 {
261 relobject.objectSubId = 0;
262
263 recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
264 }
265 }
266
267 if (OidIsValid(domainId))
268 {
269 /*
270 * Register auto dependency from constraint to owning domain
271 */
272 ObjectAddress domobject;
273
274 domobject.classId = TypeRelationId;
275 domobject.objectId = domainId;
276 domobject.objectSubId = 0;
277
278 recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
279 }
280
281 if (OidIsValid(foreignRelId))
282 {
283 /*
284 * Register normal dependency from constraint to foreign relation, or
285 * to specific column(s) if any are mentioned.
286 */
287 ObjectAddress relobject;
288
289 relobject.classId = RelationRelationId;
290 relobject.objectId = foreignRelId;
291 if (foreignNKeys > 0)
292 {
293 for (i = 0; i < foreignNKeys; i++)
294 {
295 relobject.objectSubId = foreignKey[i];
296
297 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
298 }
299 }
300 else
301 {
302 relobject.objectSubId = 0;
303
304 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
305 }
306 }
307
308 if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN)
309 {
310 /*
311 * Register normal dependency on the unique index that supports a
312 * foreign-key constraint. (Note: for indexes associated with unique
313 * or primary-key constraints, the dependency runs the other way, and
314 * is not made here.)
315 */
316 ObjectAddress relobject;
317
318 relobject.classId = RelationRelationId;
319 relobject.objectId = indexRelId;
320 relobject.objectSubId = 0;
321
322 recordDependencyOn(&conobject, &relobject, DEPENDENCY_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 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
342 if (ppEqOp[i] != pfEqOp[i])
343 {
344 oprobject.objectId = ppEqOp[i];
345 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
346 }
347 if (ffEqOp[i] != pfEqOp[i])
348 {
349 oprobject.objectId = ffEqOp[i];
350 recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
351 }
352 }
353 }
354
355 /*
356 * We don't bother to register dependencies on the exclusion operators of
357 * an exclusion constraint. We assume they are members of the opclass
358 * supporting the index, so there's an indirect dependency via that. (This
359 * would be pretty dicey for cross-type operators, but exclusion operators
360 * can never be cross-type.)
361 */
362
363 if (conExpr != NULL)
364 {
365 /*
366 * Register dependencies from constraint to objects mentioned in CHECK
367 * expression.
368 */
369 recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
370 DEPENDENCY_NORMAL,
371 DEPENDENCY_NORMAL);
372 }
373
374 /* Post creation hook for new constraint */
375 InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
376 is_internal);
377
378 return conOid;
379 }
380
381
382 /*
383 * Test whether given name is currently used as a constraint name
384 * for the given object (relation or domain).
385 *
386 * This is used to decide whether to accept a user-specified constraint name.
387 * It is deliberately not the same test as ChooseConstraintName uses to decide
388 * whether an auto-generated name is OK: here, we will allow it unless there
389 * is an identical constraint name in use *on the same object*.
390 *
391 * NB: Caller should hold exclusive lock on the given object, else
392 * this test can be fooled by concurrent additions.
393 */
394 bool
ConstraintNameIsUsed(ConstraintCategory conCat,Oid objId,Oid objNamespace,const char * conname)395 ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
396 Oid objNamespace, const char *conname)
397 {
398 bool found;
399 Relation conDesc;
400 SysScanDesc conscan;
401 ScanKeyData skey[2];
402 HeapTuple tup;
403
404 conDesc = heap_open(ConstraintRelationId, AccessShareLock);
405
406 found = false;
407
408 ScanKeyInit(&skey[0],
409 Anum_pg_constraint_conname,
410 BTEqualStrategyNumber, F_NAMEEQ,
411 CStringGetDatum(conname));
412
413 ScanKeyInit(&skey[1],
414 Anum_pg_constraint_connamespace,
415 BTEqualStrategyNumber, F_OIDEQ,
416 ObjectIdGetDatum(objNamespace));
417
418 conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
419 NULL, 2, skey);
420
421 while (HeapTupleIsValid(tup = systable_getnext(conscan)))
422 {
423 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
424
425 if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
426 {
427 found = true;
428 break;
429 }
430 else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
431 {
432 found = true;
433 break;
434 }
435 }
436
437 systable_endscan(conscan);
438 heap_close(conDesc, AccessShareLock);
439
440 return found;
441 }
442
443 /*
444 * Select a nonconflicting name for a new constraint.
445 *
446 * The objective here is to choose a name that is unique within the
447 * specified namespace. Postgres does not require this, but the SQL
448 * spec does, and some apps depend on it. Therefore we avoid choosing
449 * default names that so conflict.
450 *
451 * name1, name2, and label are used the same way as for makeObjectName(),
452 * except that the label can't be NULL; digits will be appended to the label
453 * if needed to create a name that is unique within the specified namespace.
454 *
455 * 'others' can be a list of string names already chosen within the current
456 * command (but not yet reflected into the catalogs); we will not choose
457 * a duplicate of one of these either.
458 *
459 * Note: it is theoretically possible to get a collision anyway, if someone
460 * else chooses the same name concurrently. This is fairly unlikely to be
461 * a problem in practice, especially if one is holding an exclusive lock on
462 * the relation identified by name1.
463 *
464 * Returns a palloc'd string.
465 */
466 char *
ChooseConstraintName(const char * name1,const char * name2,const char * label,Oid namespaceid,List * others)467 ChooseConstraintName(const char *name1, const char *name2,
468 const char *label, Oid namespaceid,
469 List *others)
470 {
471 int pass = 0;
472 char *conname = NULL;
473 char modlabel[NAMEDATALEN];
474 Relation conDesc;
475 SysScanDesc conscan;
476 ScanKeyData skey[2];
477 bool found;
478 ListCell *l;
479
480 conDesc = heap_open(ConstraintRelationId, AccessShareLock);
481
482 /* try the unmodified label first */
483 StrNCpy(modlabel, label, sizeof(modlabel));
484
485 for (;;)
486 {
487 conname = makeObjectName(name1, name2, modlabel);
488
489 found = false;
490
491 foreach(l, others)
492 {
493 if (strcmp((char *) lfirst(l), conname) == 0)
494 {
495 found = true;
496 break;
497 }
498 }
499
500 if (!found)
501 {
502 ScanKeyInit(&skey[0],
503 Anum_pg_constraint_conname,
504 BTEqualStrategyNumber, F_NAMEEQ,
505 CStringGetDatum(conname));
506
507 ScanKeyInit(&skey[1],
508 Anum_pg_constraint_connamespace,
509 BTEqualStrategyNumber, F_OIDEQ,
510 ObjectIdGetDatum(namespaceid));
511
512 conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
513 NULL, 2, skey);
514
515 found = (HeapTupleIsValid(systable_getnext(conscan)));
516
517 systable_endscan(conscan);
518 }
519
520 if (!found)
521 break;
522
523 /* found a conflict, so try a new name component */
524 pfree(conname);
525 snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
526 }
527
528 heap_close(conDesc, AccessShareLock);
529
530 return conname;
531 }
532
533 /*
534 * Delete a single constraint record.
535 */
536 void
RemoveConstraintById(Oid conId)537 RemoveConstraintById(Oid conId)
538 {
539 Relation conDesc;
540 HeapTuple tup;
541 Form_pg_constraint con;
542
543 conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
544
545 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
546 if (!HeapTupleIsValid(tup)) /* should not happen */
547 elog(ERROR, "cache lookup failed for constraint %u", conId);
548 con = (Form_pg_constraint) GETSTRUCT(tup);
549
550 /*
551 * Special processing depending on what the constraint is for.
552 */
553 if (OidIsValid(con->conrelid))
554 {
555 Relation rel;
556
557 /*
558 * If the constraint is for a relation, open and exclusive-lock the
559 * relation it's for.
560 */
561 rel = heap_open(con->conrelid, AccessExclusiveLock);
562
563 /*
564 * We need to update the relcheck count if it is a check constraint
565 * being dropped. This update will force backends to rebuild relcache
566 * entries when we commit.
567 */
568 if (con->contype == CONSTRAINT_CHECK)
569 {
570 Relation pgrel;
571 HeapTuple relTup;
572 Form_pg_class classForm;
573
574 pgrel = heap_open(RelationRelationId, RowExclusiveLock);
575 relTup = SearchSysCacheCopy1(RELOID,
576 ObjectIdGetDatum(con->conrelid));
577 if (!HeapTupleIsValid(relTup))
578 elog(ERROR, "cache lookup failed for relation %u",
579 con->conrelid);
580 classForm = (Form_pg_class) GETSTRUCT(relTup);
581
582 if (classForm->relchecks == 0) /* should not happen */
583 elog(ERROR, "relation \"%s\" has relchecks = 0",
584 RelationGetRelationName(rel));
585 classForm->relchecks--;
586
587 simple_heap_update(pgrel, &relTup->t_self, relTup);
588
589 CatalogUpdateIndexes(pgrel, relTup);
590
591 heap_freetuple(relTup);
592
593 heap_close(pgrel, RowExclusiveLock);
594 }
595
596 /* Keep lock on constraint's rel until end of xact */
597 heap_close(rel, NoLock);
598 }
599 else if (OidIsValid(con->contypid))
600 {
601 /*
602 * XXX for now, do nothing special when dropping a domain constraint
603 *
604 * Probably there should be some form of locking on the domain type,
605 * but we have no such concept at the moment.
606 */
607 }
608 else
609 elog(ERROR, "constraint %u is not of a known type", conId);
610
611 /* Fry the constraint itself */
612 simple_heap_delete(conDesc, &tup->t_self);
613
614 /* Clean up */
615 ReleaseSysCache(tup);
616 heap_close(conDesc, RowExclusiveLock);
617 }
618
619 /*
620 * RenameConstraintById
621 * Rename a constraint.
622 *
623 * Note: this isn't intended to be a user-exposed function; it doesn't check
624 * permissions etc. Currently this is only invoked when renaming an index
625 * that is associated with a constraint, but it's made a little more general
626 * than that with the expectation of someday having ALTER TABLE RENAME
627 * CONSTRAINT.
628 */
629 void
RenameConstraintById(Oid conId,const char * newname)630 RenameConstraintById(Oid conId, const char *newname)
631 {
632 Relation conDesc;
633 HeapTuple tuple;
634 Form_pg_constraint con;
635
636 conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
637
638 tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
639 if (!HeapTupleIsValid(tuple))
640 elog(ERROR, "cache lookup failed for constraint %u", conId);
641 con = (Form_pg_constraint) GETSTRUCT(tuple);
642
643 /*
644 * We need to check whether the name is already in use --- note that there
645 * currently is not a unique index that would catch this.
646 */
647 if (OidIsValid(con->conrelid) &&
648 ConstraintNameIsUsed(CONSTRAINT_RELATION,
649 con->conrelid,
650 con->connamespace,
651 newname))
652 ereport(ERROR,
653 (errcode(ERRCODE_DUPLICATE_OBJECT),
654 errmsg("constraint \"%s\" for relation \"%s\" already exists",
655 newname, get_rel_name(con->conrelid))));
656 if (OidIsValid(con->contypid) &&
657 ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
658 con->contypid,
659 con->connamespace,
660 newname))
661 ereport(ERROR,
662 (errcode(ERRCODE_DUPLICATE_OBJECT),
663 errmsg("constraint \"%s\" for domain %s already exists",
664 newname, format_type_be(con->contypid))));
665
666 /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
667 namestrcpy(&(con->conname), newname);
668
669 simple_heap_update(conDesc, &tuple->t_self, tuple);
670
671 /* update the system catalog indexes */
672 CatalogUpdateIndexes(conDesc, tuple);
673
674 InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
675
676 heap_freetuple(tuple);
677 heap_close(conDesc, RowExclusiveLock);
678 }
679
680 /*
681 * AlterConstraintNamespaces
682 * Find any constraints belonging to the specified object,
683 * and move them to the specified new namespace.
684 *
685 * isType indicates whether the owning object is a type or a relation.
686 */
687 void
AlterConstraintNamespaces(Oid ownerId,Oid oldNspId,Oid newNspId,bool isType,ObjectAddresses * objsMoved)688 AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
689 Oid newNspId, bool isType, ObjectAddresses *objsMoved)
690 {
691 Relation conRel;
692 ScanKeyData key[1];
693 SysScanDesc scan;
694 HeapTuple tup;
695
696 conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
697
698 if (isType)
699 {
700 ScanKeyInit(&key[0],
701 Anum_pg_constraint_contypid,
702 BTEqualStrategyNumber, F_OIDEQ,
703 ObjectIdGetDatum(ownerId));
704
705 scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
706 NULL, 1, key);
707 }
708 else
709 {
710 ScanKeyInit(&key[0],
711 Anum_pg_constraint_conrelid,
712 BTEqualStrategyNumber, F_OIDEQ,
713 ObjectIdGetDatum(ownerId));
714
715 scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
716 NULL, 1, key);
717 }
718
719 while (HeapTupleIsValid((tup = systable_getnext(scan))))
720 {
721 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
722 ObjectAddress thisobj;
723
724 thisobj.classId = ConstraintRelationId;
725 thisobj.objectId = HeapTupleGetOid(tup);
726 thisobj.objectSubId = 0;
727
728 if (object_address_present(&thisobj, objsMoved))
729 continue;
730
731 /* Don't update if the object is already part of the namespace */
732 if (conform->connamespace == oldNspId && oldNspId != newNspId)
733 {
734 tup = heap_copytuple(tup);
735 conform = (Form_pg_constraint) GETSTRUCT(tup);
736
737 conform->connamespace = newNspId;
738
739 simple_heap_update(conRel, &tup->t_self, tup);
740 CatalogUpdateIndexes(conRel, tup);
741
742 /*
743 * Note: currently, the constraint will not have its own
744 * dependency on the namespace, so we don't need to do
745 * changeDependencyFor().
746 */
747 }
748
749 InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
750
751 add_exact_object_address(&thisobj, objsMoved);
752 }
753
754 systable_endscan(scan);
755
756 heap_close(conRel, RowExclusiveLock);
757 }
758
759 /*
760 * get_relation_constraint_oid
761 * Find a constraint on the specified relation with the specified name.
762 * Returns constraint's OID.
763 */
764 Oid
get_relation_constraint_oid(Oid relid,const char * conname,bool missing_ok)765 get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
766 {
767 Relation pg_constraint;
768 HeapTuple tuple;
769 SysScanDesc scan;
770 ScanKeyData skey[1];
771 Oid conOid = InvalidOid;
772
773 /*
774 * Fetch the constraint tuple from pg_constraint. There may be more than
775 * one match, because constraints are not required to have unique names;
776 * if so, error out.
777 */
778 pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
779
780 ScanKeyInit(&skey[0],
781 Anum_pg_constraint_conrelid,
782 BTEqualStrategyNumber, F_OIDEQ,
783 ObjectIdGetDatum(relid));
784
785 scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
786 NULL, 1, skey);
787
788 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
789 {
790 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
791
792 if (strcmp(NameStr(con->conname), conname) == 0)
793 {
794 if (OidIsValid(conOid))
795 ereport(ERROR,
796 (errcode(ERRCODE_DUPLICATE_OBJECT),
797 errmsg("table \"%s\" has multiple constraints named \"%s\"",
798 get_rel_name(relid), conname)));
799 conOid = HeapTupleGetOid(tuple);
800 }
801 }
802
803 systable_endscan(scan);
804
805 /* If no such constraint exists, complain */
806 if (!OidIsValid(conOid) && !missing_ok)
807 ereport(ERROR,
808 (errcode(ERRCODE_UNDEFINED_OBJECT),
809 errmsg("constraint \"%s\" for table \"%s\" does not exist",
810 conname, get_rel_name(relid))));
811
812 heap_close(pg_constraint, AccessShareLock);
813
814 return conOid;
815 }
816
817 /*
818 * get_relation_constraint_attnos
819 * Find a constraint on the specified relation with the specified name
820 * and return the constrained columns.
821 *
822 * Returns a Bitmapset of the column attnos of the constrained columns, with
823 * attnos being offset by FirstLowInvalidHeapAttributeNumber so that system
824 * columns can be represented.
825 *
826 * *constraintOid is set to the OID of the constraint, or InvalidOid on
827 * failure.
828 */
829 Bitmapset *
get_relation_constraint_attnos(Oid relid,const char * conname,bool missing_ok,Oid * constraintOid)830 get_relation_constraint_attnos(Oid relid, const char *conname,
831 bool missing_ok, Oid *constraintOid)
832 {
833 Bitmapset *conattnos = NULL;
834 Relation pg_constraint;
835 HeapTuple tuple;
836 SysScanDesc scan;
837 ScanKeyData skey[1];
838
839 /* Set *constraintOid, to avoid complaints about uninitialized vars */
840 *constraintOid = InvalidOid;
841
842 /*
843 * Fetch the constraint tuple from pg_constraint. There may be more than
844 * one match, because constraints are not required to have unique names;
845 * if so, error out.
846 */
847 pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
848
849 ScanKeyInit(&skey[0],
850 Anum_pg_constraint_conrelid,
851 BTEqualStrategyNumber, F_OIDEQ,
852 ObjectIdGetDatum(relid));
853
854 scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
855 NULL, 1, skey);
856
857 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
858 {
859 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
860 Datum adatum;
861 bool isNull;
862 ArrayType *arr;
863 int16 *attnums;
864 int numcols;
865 int i;
866
867 /* Check the constraint name */
868 if (strcmp(NameStr(con->conname), conname) != 0)
869 continue;
870 if (OidIsValid(*constraintOid))
871 ereport(ERROR,
872 (errcode(ERRCODE_DUPLICATE_OBJECT),
873 errmsg("table \"%s\" has multiple constraints named \"%s\"",
874 get_rel_name(relid), conname)));
875
876 *constraintOid = HeapTupleGetOid(tuple);
877
878 /* Extract the conkey array, ie, attnums of constrained columns */
879 adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
880 RelationGetDescr(pg_constraint), &isNull);
881 if (isNull)
882 continue; /* no constrained columns */
883
884 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
885 numcols = ARR_DIMS(arr)[0];
886 if (ARR_NDIM(arr) != 1 ||
887 numcols < 0 ||
888 ARR_HASNULL(arr) ||
889 ARR_ELEMTYPE(arr) != INT2OID)
890 elog(ERROR, "conkey is not a 1-D smallint array");
891 attnums = (int16 *) ARR_DATA_PTR(arr);
892
893 /* Construct the result value */
894 for (i = 0; i < numcols; i++)
895 {
896 conattnos = bms_add_member(conattnos,
897 attnums[i] - FirstLowInvalidHeapAttributeNumber);
898 }
899 }
900
901 systable_endscan(scan);
902
903 /* If no such constraint exists, complain */
904 if (!OidIsValid(*constraintOid) && !missing_ok)
905 ereport(ERROR,
906 (errcode(ERRCODE_UNDEFINED_OBJECT),
907 errmsg("constraint \"%s\" for table \"%s\" does not exist",
908 conname, get_rel_name(relid))));
909
910 heap_close(pg_constraint, AccessShareLock);
911
912 return conattnos;
913 }
914
915 /*
916 * get_domain_constraint_oid
917 * Find a constraint on the specified domain with the specified name.
918 * Returns constraint's OID.
919 */
920 Oid
get_domain_constraint_oid(Oid typid,const char * conname,bool missing_ok)921 get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
922 {
923 Relation pg_constraint;
924 HeapTuple tuple;
925 SysScanDesc scan;
926 ScanKeyData skey[1];
927 Oid conOid = InvalidOid;
928
929 /*
930 * Fetch the constraint tuple from pg_constraint. There may be more than
931 * one match, because constraints are not required to have unique names;
932 * if so, error out.
933 */
934 pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
935
936 ScanKeyInit(&skey[0],
937 Anum_pg_constraint_contypid,
938 BTEqualStrategyNumber, F_OIDEQ,
939 ObjectIdGetDatum(typid));
940
941 scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
942 NULL, 1, skey);
943
944 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
945 {
946 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
947
948 if (strcmp(NameStr(con->conname), conname) == 0)
949 {
950 if (OidIsValid(conOid))
951 ereport(ERROR,
952 (errcode(ERRCODE_DUPLICATE_OBJECT),
953 errmsg("domain \"%s\" has multiple constraints named \"%s\"",
954 format_type_be(typid), conname)));
955 conOid = HeapTupleGetOid(tuple);
956 }
957 }
958
959 systable_endscan(scan);
960
961 /* If no such constraint exists, complain */
962 if (!OidIsValid(conOid) && !missing_ok)
963 ereport(ERROR,
964 (errcode(ERRCODE_UNDEFINED_OBJECT),
965 errmsg("constraint \"%s\" for domain \"%s\" does not exist",
966 conname, format_type_be(typid))));
967
968 heap_close(pg_constraint, AccessShareLock);
969
970 return conOid;
971 }
972
973 /*
974 * get_primary_key_attnos
975 * Identify the columns in a relation's primary key, if any.
976 *
977 * Returns a Bitmapset of the column attnos of the primary key's columns,
978 * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
979 * system columns can be represented.
980 *
981 * If there is no primary key, return NULL. We also return NULL if the pkey
982 * constraint is deferrable and deferrableOk is false.
983 *
984 * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
985 * on failure.
986 */
987 Bitmapset *
get_primary_key_attnos(Oid relid,bool deferrableOk,Oid * constraintOid)988 get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
989 {
990 Bitmapset *pkattnos = NULL;
991 Relation pg_constraint;
992 HeapTuple tuple;
993 SysScanDesc scan;
994 ScanKeyData skey[1];
995
996 /* Set *constraintOid, to avoid complaints about uninitialized vars */
997 *constraintOid = InvalidOid;
998
999 /* Scan pg_constraint for constraints of the target rel */
1000 pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
1001
1002 ScanKeyInit(&skey[0],
1003 Anum_pg_constraint_conrelid,
1004 BTEqualStrategyNumber, F_OIDEQ,
1005 ObjectIdGetDatum(relid));
1006
1007 scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
1008 NULL, 1, skey);
1009
1010 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1011 {
1012 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
1013 Datum adatum;
1014 bool isNull;
1015 ArrayType *arr;
1016 int16 *attnums;
1017 int numkeys;
1018 int i;
1019
1020 /* Skip constraints that are not PRIMARY KEYs */
1021 if (con->contype != CONSTRAINT_PRIMARY)
1022 continue;
1023
1024 /*
1025 * If the primary key is deferrable, but we've been instructed to
1026 * ignore deferrable constraints, then we might as well give up
1027 * searching, since there can only be a single primary key on a table.
1028 */
1029 if (con->condeferrable && !deferrableOk)
1030 break;
1031
1032 /* Extract the conkey array, ie, attnums of PK's columns */
1033 adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
1034 RelationGetDescr(pg_constraint), &isNull);
1035 if (isNull)
1036 elog(ERROR, "null conkey for constraint %u",
1037 HeapTupleGetOid(tuple));
1038 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1039 numkeys = ARR_DIMS(arr)[0];
1040 if (ARR_NDIM(arr) != 1 ||
1041 numkeys < 0 ||
1042 ARR_HASNULL(arr) ||
1043 ARR_ELEMTYPE(arr) != INT2OID)
1044 elog(ERROR, "conkey is not a 1-D smallint array");
1045 attnums = (int16 *) ARR_DATA_PTR(arr);
1046
1047 /* Construct the result value */
1048 for (i = 0; i < numkeys; i++)
1049 {
1050 pkattnos = bms_add_member(pkattnos,
1051 attnums[i] - FirstLowInvalidHeapAttributeNumber);
1052 }
1053 *constraintOid = HeapTupleGetOid(tuple);
1054
1055 /* No need to search further */
1056 break;
1057 }
1058
1059 systable_endscan(scan);
1060
1061 heap_close(pg_constraint, AccessShareLock);
1062
1063 return pkattnos;
1064 }
1065
1066 /*
1067 * Determine whether a relation can be proven functionally dependent on
1068 * a set of grouping columns. If so, return TRUE and add the pg_constraint
1069 * OIDs of the constraints needed for the proof to the *constraintDeps list.
1070 *
1071 * grouping_columns is a list of grouping expressions, in which columns of
1072 * the rel of interest are Vars with the indicated varno/varlevelsup.
1073 *
1074 * Currently we only check to see if the rel has a primary key that is a
1075 * subset of the grouping_columns. We could also use plain unique constraints
1076 * if all their columns are known not null, but there's a problem: we need
1077 * to be able to represent the not-null-ness as part of the constraints added
1078 * to *constraintDeps. FIXME whenever not-null constraints get represented
1079 * in pg_constraint.
1080 */
1081 bool
check_functional_grouping(Oid relid,Index varno,Index varlevelsup,List * grouping_columns,List ** constraintDeps)1082 check_functional_grouping(Oid relid,
1083 Index varno, Index varlevelsup,
1084 List *grouping_columns,
1085 List **constraintDeps)
1086 {
1087 Bitmapset *pkattnos;
1088 Bitmapset *groupbyattnos;
1089 Oid constraintOid;
1090 ListCell *gl;
1091
1092 /* If the rel has no PK, then we can't prove functional dependency */
1093 pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
1094 if (pkattnos == NULL)
1095 return false;
1096
1097 /* Identify all the rel's columns that appear in grouping_columns */
1098 groupbyattnos = NULL;
1099 foreach(gl, grouping_columns)
1100 {
1101 Var *gvar = (Var *) lfirst(gl);
1102
1103 if (IsA(gvar, Var) &&
1104 gvar->varno == varno &&
1105 gvar->varlevelsup == varlevelsup)
1106 groupbyattnos = bms_add_member(groupbyattnos,
1107 gvar->varattno - FirstLowInvalidHeapAttributeNumber);
1108 }
1109
1110 if (bms_is_subset(pkattnos, groupbyattnos))
1111 {
1112 /* The PK is a subset of grouping_columns, so we win */
1113 *constraintDeps = lappend_oid(*constraintDeps, constraintOid);
1114 return true;
1115 }
1116
1117 return false;
1118 }
1119