Line data Source code
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
49 627 : 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 627 : conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
94 :
95 627 : Assert(constraintName);
96 627 : namestrcpy(&cname, constraintName);
97 :
98 : /*
99 : * Convert C arrays into Postgres arrays.
100 : */
101 627 : if (constraintNKeys > 0)
102 : {
103 : Datum *conkey;
104 :
105 586 : conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
106 1285 : for (i = 0; i < constraintNKeys; i++)
107 699 : conkey[i] = Int16GetDatum(constraintKey[i]);
108 586 : conkeyArray = construct_array(conkey, constraintNKeys,
109 : INT2OID, 2, true, 's');
110 : }
111 : else
112 41 : conkeyArray = NULL;
113 :
114 627 : if (foreignNKeys > 0)
115 : {
116 : Datum *fkdatums;
117 :
118 93 : fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
119 213 : for (i = 0; i < foreignNKeys; i++)
120 120 : fkdatums[i] = Int16GetDatum(foreignKey[i]);
121 93 : confkeyArray = construct_array(fkdatums, foreignNKeys,
122 : INT2OID, 2, true, 's');
123 213 : for (i = 0; i < foreignNKeys; i++)
124 120 : fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
125 93 : conpfeqopArray = construct_array(fkdatums, foreignNKeys,
126 : OIDOID, sizeof(Oid), true, 'i');
127 213 : for (i = 0; i < foreignNKeys; i++)
128 120 : fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
129 93 : conppeqopArray = construct_array(fkdatums, foreignNKeys,
130 : OIDOID, sizeof(Oid), true, 'i');
131 213 : for (i = 0; i < foreignNKeys; i++)
132 120 : fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
133 93 : conffeqopArray = construct_array(fkdatums, foreignNKeys,
134 : OIDOID, sizeof(Oid), true, 'i');
135 : }
136 : else
137 : {
138 534 : confkeyArray = NULL;
139 534 : conpfeqopArray = NULL;
140 534 : conppeqopArray = NULL;
141 534 : conffeqopArray = NULL;
142 : }
143 :
144 627 : if (exclOp != NULL)
145 : {
146 : Datum *opdatums;
147 :
148 8 : opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
149 20 : for (i = 0; i < constraintNKeys; i++)
150 12 : opdatums[i] = ObjectIdGetDatum(exclOp[i]);
151 8 : conexclopArray = construct_array(opdatums, constraintNKeys,
152 : OIDOID, sizeof(Oid), true, 'i');
153 : }
154 : else
155 619 : conexclopArray = NULL;
156 :
157 : /* initialize nulls and values */
158 15675 : for (i = 0; i < Natts_pg_constraint; i++)
159 : {
160 15048 : nulls[i] = false;
161 15048 : values[i] = (Datum) NULL;
162 : }
163 :
164 627 : values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
165 627 : values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
166 627 : values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
167 627 : values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
168 627 : values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
169 627 : values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
170 627 : values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
171 627 : values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
172 627 : values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
173 627 : values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
174 627 : values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
175 627 : values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
176 627 : values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
177 627 : values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
178 627 : values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
179 627 : values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
180 :
181 627 : if (conkeyArray)
182 586 : values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
183 : else
184 41 : nulls[Anum_pg_constraint_conkey - 1] = true;
185 :
186 627 : if (confkeyArray)
187 93 : values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
188 : else
189 534 : nulls[Anum_pg_constraint_confkey - 1] = true;
190 :
191 627 : if (conpfeqopArray)
192 93 : values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
193 : else
194 534 : nulls[Anum_pg_constraint_conpfeqop - 1] = true;
195 :
196 627 : if (conppeqopArray)
197 93 : values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
198 : else
199 534 : nulls[Anum_pg_constraint_conppeqop - 1] = true;
200 :
201 627 : if (conffeqopArray)
202 93 : values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
203 : else
204 534 : nulls[Anum_pg_constraint_conffeqop - 1] = true;
205 :
206 627 : if (conexclopArray)
207 8 : values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
208 : else
209 619 : nulls[Anum_pg_constraint_conexclop - 1] = true;
210 :
211 : /*
212 : * initialize the binary form of the check constraint.
213 : */
214 627 : if (conBin)
215 237 : values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
216 : else
217 390 : nulls[Anum_pg_constraint_conbin - 1] = true;
218 :
219 : /*
220 : * initialize the text form of the check constraint
221 : */
222 627 : if (conSrc)
223 237 : values[Anum_pg_constraint_consrc - 1] = CStringGetTextDatum(conSrc);
224 : else
225 390 : nulls[Anum_pg_constraint_consrc - 1] = true;
226 :
227 627 : tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
228 :
229 627 : conOid = CatalogTupleInsert(conDesc, tup);
230 :
231 627 : conobject.classId = ConstraintRelationId;
232 627 : conobject.objectId = conOid;
233 627 : conobject.objectSubId = 0;
234 :
235 627 : heap_close(conDesc, RowExclusiveLock);
236 :
237 627 : 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 587 : relobject.classId = RelationRelationId;
246 587 : relobject.objectId = relId;
247 587 : if (constraintNKeys > 0)
248 : {
249 1285 : for (i = 0; i < constraintNKeys; i++)
250 : {
251 699 : relobject.objectSubId = constraintKey[i];
252 :
253 699 : recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
254 : }
255 : }
256 : else
257 : {
258 1 : relobject.objectSubId = 0;
259 :
260 1 : recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
261 : }
262 : }
263 :
264 627 : if (OidIsValid(domainId))
265 : {
266 : /*
267 : * Register auto dependency from constraint to owning domain
268 : */
269 : ObjectAddress domobject;
270 :
271 40 : domobject.classId = TypeRelationId;
272 40 : domobject.objectId = domainId;
273 40 : domobject.objectSubId = 0;
274 :
275 40 : recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
276 : }
277 :
278 627 : 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 93 : relobject.classId = RelationRelationId;
287 93 : relobject.objectId = foreignRelId;
288 93 : if (foreignNKeys > 0)
289 : {
290 213 : for (i = 0; i < foreignNKeys; i++)
291 : {
292 120 : relobject.objectSubId = foreignKey[i];
293 :
294 120 : recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
295 : }
296 : }
297 : else
298 : {
299 0 : relobject.objectSubId = 0;
300 :
301 0 : recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
302 : }
303 : }
304 :
305 627 : 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 93 : relobject.classId = RelationRelationId;
316 93 : relobject.objectId = indexRelId;
317 93 : relobject.objectSubId = 0;
318 :
319 93 : recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
320 : }
321 :
322 627 : 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 93 : oprobject.classId = OperatorRelationId;
333 93 : oprobject.objectSubId = 0;
334 :
335 213 : for (i = 0; i < foreignNKeys; i++)
336 : {
337 120 : oprobject.objectId = pfEqOp[i];
338 120 : recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
339 120 : if (ppEqOp[i] != pfEqOp[i])
340 : {
341 5 : oprobject.objectId = ppEqOp[i];
342 5 : recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
343 : }
344 120 : if (ffEqOp[i] != pfEqOp[i])
345 : {
346 5 : oprobject.objectId = ffEqOp[i];
347 5 : 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 627 : if (conExpr != NULL)
361 : {
362 : /*
363 : * Register dependencies from constraint to objects mentioned in CHECK
364 : * expression.
365 : */
366 237 : recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
367 : DEPENDENCY_NORMAL,
368 : DEPENDENCY_NORMAL, false);
369 : }
370 :
371 : /* Post creation hook for new constraint */
372 627 : InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
373 : is_internal);
374 :
375 627 : 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
392 72 : 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 72 : conDesc = heap_open(ConstraintRelationId, AccessShareLock);
402 :
403 72 : found = false;
404 :
405 72 : ScanKeyInit(&skey[0],
406 : Anum_pg_constraint_conname,
407 : BTEqualStrategyNumber, F_NAMEEQ,
408 : CStringGetDatum(conname));
409 :
410 72 : ScanKeyInit(&skey[1],
411 : Anum_pg_constraint_connamespace,
412 : BTEqualStrategyNumber, F_OIDEQ,
413 : ObjectIdGetDatum(objNamespace));
414 :
415 72 : conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
416 : NULL, 2, skey);
417 :
418 72 : while (HeapTupleIsValid(tup = systable_getnext(conscan)))
419 : {
420 2 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
421 :
422 2 : if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
423 : {
424 0 : found = true;
425 0 : break;
426 : }
427 2 : else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
428 : {
429 0 : found = true;
430 0 : break;
431 : }
432 : }
433 :
434 72 : systable_endscan(conscan);
435 72 : heap_close(conDesc, AccessShareLock);
436 :
437 72 : 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 *
464 161 : ChooseConstraintName(const char *name1, const char *name2,
465 : const char *label, Oid namespaceid,
466 : List *others)
467 : {
468 161 : int pass = 0;
469 161 : 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 161 : conDesc = heap_open(ConstraintRelationId, AccessShareLock);
478 :
479 : /* try the unmodified label first */
480 161 : StrNCpy(modlabel, label, sizeof(modlabel));
481 :
482 : for (;;)
483 : {
484 163 : conname = makeObjectName(name1, name2, modlabel);
485 :
486 163 : found = false;
487 :
488 169 : foreach(l, others)
489 : {
490 7 : if (strcmp((char *) lfirst(l), conname) == 0)
491 : {
492 1 : found = true;
493 1 : break;
494 : }
495 : }
496 :
497 163 : if (!found)
498 : {
499 162 : ScanKeyInit(&skey[0],
500 : Anum_pg_constraint_conname,
501 : BTEqualStrategyNumber, F_NAMEEQ,
502 : CStringGetDatum(conname));
503 :
504 162 : ScanKeyInit(&skey[1],
505 : Anum_pg_constraint_connamespace,
506 : BTEqualStrategyNumber, F_OIDEQ,
507 : ObjectIdGetDatum(namespaceid));
508 :
509 162 : conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
510 : NULL, 2, skey);
511 :
512 162 : found = (HeapTupleIsValid(systable_getnext(conscan)));
513 :
514 162 : systable_endscan(conscan);
515 : }
516 :
517 163 : if (!found)
518 161 : break;
519 :
520 : /* found a conflict, so try a new name component */
521 2 : pfree(conname);
522 2 : snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
523 2 : }
524 :
525 161 : heap_close(conDesc, AccessShareLock);
526 :
527 161 : return conname;
528 : }
529 :
530 : /*
531 : * Delete a single constraint record.
532 : */
533 : void
534 507 : RemoveConstraintById(Oid conId)
535 : {
536 : Relation conDesc;
537 : HeapTuple tup;
538 : Form_pg_constraint con;
539 :
540 507 : conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
541 :
542 507 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
543 507 : if (!HeapTupleIsValid(tup)) /* should not happen */
544 0 : elog(ERROR, "cache lookup failed for constraint %u", conId);
545 507 : con = (Form_pg_constraint) GETSTRUCT(tup);
546 :
547 : /*
548 : * Special processing depending on what the constraint is for.
549 : */
550 507 : 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 485 : 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 485 : if (con->contype == CONSTRAINT_CHECK)
566 : {
567 : Relation pgrel;
568 : HeapTuple relTup;
569 : Form_pg_class classForm;
570 :
571 154 : pgrel = heap_open(RelationRelationId, RowExclusiveLock);
572 154 : relTup = SearchSysCacheCopy1(RELOID,
573 : ObjectIdGetDatum(con->conrelid));
574 154 : if (!HeapTupleIsValid(relTup))
575 0 : elog(ERROR, "cache lookup failed for relation %u",
576 : con->conrelid);
577 154 : classForm = (Form_pg_class) GETSTRUCT(relTup);
578 :
579 154 : if (classForm->relchecks == 0) /* should not happen */
580 0 : elog(ERROR, "relation \"%s\" has relchecks = 0",
581 : RelationGetRelationName(rel));
582 154 : classForm->relchecks--;
583 :
584 154 : CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
585 :
586 154 : heap_freetuple(relTup);
587 :
588 154 : heap_close(pgrel, RowExclusiveLock);
589 : }
590 :
591 : /* Keep lock on constraint's rel until end of xact */
592 485 : heap_close(rel, NoLock);
593 : }
594 22 : 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 0 : elog(ERROR, "constraint %u is not of a known type", conId);
605 :
606 : /* Fry the constraint itself */
607 507 : CatalogTupleDelete(conDesc, &tup->t_self);
608 :
609 : /* Clean up */
610 507 : ReleaseSysCache(tup);
611 507 : heap_close(conDesc, RowExclusiveLock);
612 507 : }
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
625 11 : RenameConstraintById(Oid conId, const char *newname)
626 : {
627 : Relation conDesc;
628 : HeapTuple tuple;
629 : Form_pg_constraint con;
630 :
631 11 : conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
632 :
633 11 : tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
634 11 : if (!HeapTupleIsValid(tuple))
635 0 : elog(ERROR, "cache lookup failed for constraint %u", conId);
636 11 : 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 21 : if (OidIsValid(con->conrelid) &&
643 10 : ConstraintNameIsUsed(CONSTRAINT_RELATION,
644 : con->conrelid,
645 : con->connamespace,
646 : newname))
647 0 : ereport(ERROR,
648 : (errcode(ERRCODE_DUPLICATE_OBJECT),
649 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
650 : newname, get_rel_name(con->conrelid))));
651 12 : if (OidIsValid(con->contypid) &&
652 1 : ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
653 : con->contypid,
654 : con->connamespace,
655 : newname))
656 0 : 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 11 : namestrcpy(&(con->conname), newname);
663 :
664 11 : CatalogTupleUpdate(conDesc, &tuple->t_self, tuple);
665 :
666 11 : InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
667 :
668 11 : heap_freetuple(tuple);
669 11 : heap_close(conDesc, RowExclusiveLock);
670 11 : }
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
680 11 : 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 11 : conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
689 :
690 11 : if (isType)
691 : {
692 1 : ScanKeyInit(&key[0],
693 : Anum_pg_constraint_contypid,
694 : BTEqualStrategyNumber, F_OIDEQ,
695 : ObjectIdGetDatum(ownerId));
696 :
697 1 : scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
698 : NULL, 1, key);
699 : }
700 : else
701 : {
702 10 : ScanKeyInit(&key[0],
703 : Anum_pg_constraint_conrelid,
704 : BTEqualStrategyNumber, F_OIDEQ,
705 : ObjectIdGetDatum(ownerId));
706 :
707 10 : scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
708 : NULL, 1, key);
709 : }
710 :
711 31 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
712 : {
713 9 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
714 : ObjectAddress thisobj;
715 :
716 9 : thisobj.classId = ConstraintRelationId;
717 9 : thisobj.objectId = HeapTupleGetOid(tup);
718 9 : thisobj.objectSubId = 0;
719 :
720 9 : if (object_address_present(&thisobj, objsMoved))
721 0 : continue;
722 :
723 : /* Don't update if the object is already part of the namespace */
724 9 : if (conform->connamespace == oldNspId && oldNspId != newNspId)
725 : {
726 6 : tup = heap_copytuple(tup);
727 6 : conform = (Form_pg_constraint) GETSTRUCT(tup);
728 :
729 6 : conform->connamespace = newNspId;
730 :
731 6 : 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 9 : InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
741 :
742 9 : add_exact_object_address(&thisobj, objsMoved);
743 : }
744 :
745 11 : systable_endscan(scan);
746 :
747 11 : heap_close(conRel, RowExclusiveLock);
748 11 : }
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
756 42 : 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 42 : 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 42 : pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
770 :
771 42 : ScanKeyInit(&skey[0],
772 : Anum_pg_constraint_conrelid,
773 : BTEqualStrategyNumber, F_OIDEQ,
774 : ObjectIdGetDatum(relid));
775 :
776 42 : scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
777 : NULL, 1, skey);
778 :
779 148 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
780 : {
781 64 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
782 :
783 64 : if (strcmp(NameStr(con->conname), conname) == 0)
784 : {
785 40 : if (OidIsValid(conOid))
786 0 : ereport(ERROR,
787 : (errcode(ERRCODE_DUPLICATE_OBJECT),
788 : errmsg("table \"%s\" has multiple constraints named \"%s\"",
789 : get_rel_name(relid), conname)));
790 40 : conOid = HeapTupleGetOid(tuple);
791 : }
792 : }
793 :
794 42 : systable_endscan(scan);
795 :
796 : /* If no such constraint exists, complain */
797 42 : if (!OidIsValid(conOid) && !missing_ok)
798 2 : 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 40 : heap_close(pg_constraint, AccessShareLock);
804 :
805 40 : return conOid;
806 : }
807 :
808 : /*
809 : * get_domain_constraint_oid
810 : * Find a constraint on the specified domain with the specified name.
811 : * Returns constraint's OID.
812 : */
813 : Oid
814 6 : get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
815 : {
816 : Relation pg_constraint;
817 : HeapTuple tuple;
818 : SysScanDesc scan;
819 : ScanKeyData skey[1];
820 6 : Oid conOid = InvalidOid;
821 :
822 : /*
823 : * Fetch the constraint tuple from pg_constraint. There may be more than
824 : * one match, because constraints are not required to have unique names;
825 : * if so, error out.
826 : */
827 6 : pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
828 :
829 6 : ScanKeyInit(&skey[0],
830 : Anum_pg_constraint_contypid,
831 : BTEqualStrategyNumber, F_OIDEQ,
832 : ObjectIdGetDatum(typid));
833 :
834 6 : scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
835 : NULL, 1, skey);
836 :
837 18 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
838 : {
839 6 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
840 :
841 6 : if (strcmp(NameStr(con->conname), conname) == 0)
842 : {
843 5 : if (OidIsValid(conOid))
844 0 : ereport(ERROR,
845 : (errcode(ERRCODE_DUPLICATE_OBJECT),
846 : errmsg("domain %s has multiple constraints named \"%s\"",
847 : format_type_be(typid), conname)));
848 5 : conOid = HeapTupleGetOid(tuple);
849 : }
850 : }
851 :
852 6 : systable_endscan(scan);
853 :
854 : /* If no such constraint exists, complain */
855 6 : if (!OidIsValid(conOid) && !missing_ok)
856 1 : ereport(ERROR,
857 : (errcode(ERRCODE_UNDEFINED_OBJECT),
858 : errmsg("constraint \"%s\" for domain %s does not exist",
859 : conname, format_type_be(typid))));
860 :
861 5 : heap_close(pg_constraint, AccessShareLock);
862 :
863 5 : return conOid;
864 : }
865 :
866 : /*
867 : * get_primary_key_attnos
868 : * Identify the columns in a relation's primary key, if any.
869 : *
870 : * Returns a Bitmapset of the column attnos of the primary key's columns,
871 : * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
872 : * system columns can be represented.
873 : *
874 : * If there is no primary key, return NULL. We also return NULL if the pkey
875 : * constraint is deferrable and deferrableOk is false.
876 : *
877 : * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
878 : * on failure.
879 : */
880 : Bitmapset *
881 85 : get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
882 : {
883 85 : Bitmapset *pkattnos = NULL;
884 : Relation pg_constraint;
885 : HeapTuple tuple;
886 : SysScanDesc scan;
887 : ScanKeyData skey[1];
888 :
889 : /* Set *constraintOid, to avoid complaints about uninitialized vars */
890 85 : *constraintOid = InvalidOid;
891 :
892 : /* Scan pg_constraint for constraints of the target rel */
893 85 : pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
894 :
895 85 : ScanKeyInit(&skey[0],
896 : Anum_pg_constraint_conrelid,
897 : BTEqualStrategyNumber, F_OIDEQ,
898 : ObjectIdGetDatum(relid));
899 :
900 85 : scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
901 : NULL, 1, skey);
902 :
903 85 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
904 : {
905 67 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
906 : Datum adatum;
907 : bool isNull;
908 : ArrayType *arr;
909 : int16 *attnums;
910 : int numkeys;
911 : int i;
912 :
913 : /* Skip constraints that are not PRIMARY KEYs */
914 67 : if (con->contype != CONSTRAINT_PRIMARY)
915 34 : continue;
916 :
917 : /*
918 : * If the primary key is deferrable, but we've been instructed to
919 : * ignore deferrable constraints, then we might as well give up
920 : * searching, since there can only be a single primary key on a table.
921 : */
922 33 : if (con->condeferrable && !deferrableOk)
923 34 : break;
924 :
925 : /* Extract the conkey array, ie, attnums of PK's columns */
926 32 : adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
927 : RelationGetDescr(pg_constraint), &isNull);
928 32 : if (isNull)
929 0 : elog(ERROR, "null conkey for constraint %u",
930 : HeapTupleGetOid(tuple));
931 32 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
932 32 : numkeys = ARR_DIMS(arr)[0];
933 32 : if (ARR_NDIM(arr) != 1 ||
934 32 : numkeys < 0 ||
935 64 : ARR_HASNULL(arr) ||
936 32 : ARR_ELEMTYPE(arr) != INT2OID)
937 0 : elog(ERROR, "conkey is not a 1-D smallint array");
938 32 : attnums = (int16 *) ARR_DATA_PTR(arr);
939 :
940 : /* Construct the result value */
941 74 : for (i = 0; i < numkeys; i++)
942 : {
943 42 : pkattnos = bms_add_member(pkattnos,
944 42 : attnums[i] - FirstLowInvalidHeapAttributeNumber);
945 : }
946 32 : *constraintOid = HeapTupleGetOid(tuple);
947 :
948 : /* No need to search further */
949 32 : break;
950 : }
951 :
952 85 : systable_endscan(scan);
953 :
954 85 : heap_close(pg_constraint, AccessShareLock);
955 :
956 85 : return pkattnos;
957 : }
958 :
959 : /*
960 : * Determine whether a relation can be proven functionally dependent on
961 : * a set of grouping columns. If so, return TRUE and add the pg_constraint
962 : * OIDs of the constraints needed for the proof to the *constraintDeps list.
963 : *
964 : * grouping_columns is a list of grouping expressions, in which columns of
965 : * the rel of interest are Vars with the indicated varno/varlevelsup.
966 : *
967 : * Currently we only check to see if the rel has a primary key that is a
968 : * subset of the grouping_columns. We could also use plain unique constraints
969 : * if all their columns are known not null, but there's a problem: we need
970 : * to be able to represent the not-null-ness as part of the constraints added
971 : * to *constraintDeps. FIXME whenever not-null constraints get represented
972 : * in pg_constraint.
973 : */
974 : bool
975 29 : check_functional_grouping(Oid relid,
976 : Index varno, Index varlevelsup,
977 : List *grouping_columns,
978 : List **constraintDeps)
979 : {
980 : Bitmapset *pkattnos;
981 : Bitmapset *groupbyattnos;
982 : Oid constraintOid;
983 : ListCell *gl;
984 :
985 : /* If the rel has no PK, then we can't prove functional dependency */
986 29 : pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
987 29 : if (pkattnos == NULL)
988 6 : return false;
989 :
990 : /* Identify all the rel's columns that appear in grouping_columns */
991 23 : groupbyattnos = NULL;
992 53 : foreach(gl, grouping_columns)
993 : {
994 30 : Var *gvar = (Var *) lfirst(gl);
995 :
996 60 : if (IsA(gvar, Var) &&
997 53 : gvar->varno == varno &&
998 23 : gvar->varlevelsup == varlevelsup)
999 23 : groupbyattnos = bms_add_member(groupbyattnos,
1000 23 : gvar->varattno - FirstLowInvalidHeapAttributeNumber);
1001 : }
1002 :
1003 23 : if (bms_is_subset(pkattnos, groupbyattnos))
1004 : {
1005 : /* The PK is a subset of grouping_columns, so we win */
1006 16 : *constraintDeps = lappend_oid(*constraintDeps, constraintOid);
1007 16 : return true;
1008 : }
1009 :
1010 7 : return false;
1011 : }
|