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