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