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