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 "access/xact.h"
23 : #include "catalog/catalog.h"
24 : #include "catalog/dependency.h"
25 : #include "catalog/heap.h"
26 : #include "catalog/indexing.h"
27 : #include "catalog/objectaccess.h"
28 : #include "catalog/pg_constraint.h"
29 : #include "catalog/pg_operator.h"
30 : #include "catalog/pg_type.h"
31 : #include "commands/defrem.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 32956 : 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 : int 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 32956 : conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
103 :
104 : Assert(constraintName);
105 32956 : namestrcpy(&cname, constraintName);
106 :
107 : /*
108 : * Convert C arrays into Postgres arrays.
109 : */
110 32956 : if (constraintNKeys > 0)
111 : {
112 : Datum *conkey;
113 :
114 32072 : conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
115 72280 : for (i = 0; i < constraintNKeys; i++)
116 40208 : conkey[i] = Int16GetDatum(constraintKey[i]);
117 32072 : conkeyArray = construct_array_builtin(conkey, constraintNKeys, INT2OID);
118 : }
119 : else
120 884 : conkeyArray = NULL;
121 :
122 32956 : if (foreignNKeys > 0)
123 : {
124 : Datum *fkdatums;
125 :
126 3466 : fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
127 7864 : for (i = 0; i < foreignNKeys; i++)
128 4398 : fkdatums[i] = Int16GetDatum(foreignKey[i]);
129 3466 : confkeyArray = construct_array_builtin(fkdatums, foreignNKeys, INT2OID);
130 7864 : for (i = 0; i < foreignNKeys; i++)
131 4398 : fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
132 3466 : conpfeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
133 7864 : for (i = 0; i < foreignNKeys; i++)
134 4398 : fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
135 3466 : conppeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
136 7864 : for (i = 0; i < foreignNKeys; i++)
137 4398 : fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
138 3466 : conffeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
139 :
140 3466 : 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 3406 : confdelsetcolsArray = NULL;
148 : }
149 : else
150 : {
151 29490 : confkeyArray = NULL;
152 29490 : conpfeqopArray = NULL;
153 29490 : conppeqopArray = NULL;
154 29490 : conffeqopArray = NULL;
155 29490 : confdelsetcolsArray = NULL;
156 : }
157 :
158 32956 : if (exclOp != NULL)
159 : {
160 : Datum *opdatums;
161 :
162 604 : opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
163 1736 : for (i = 0; i < constraintNKeys; i++)
164 1132 : opdatums[i] = ObjectIdGetDatum(exclOp[i]);
165 604 : conexclopArray = construct_array_builtin(opdatums, constraintNKeys, OIDOID);
166 : }
167 : else
168 32352 : conexclopArray = NULL;
169 :
170 : /* initialize nulls and values */
171 922768 : for (i = 0; i < Natts_pg_constraint; i++)
172 : {
173 889812 : nulls[i] = false;
174 889812 : values[i] = (Datum) NULL;
175 : }
176 :
177 32956 : conOid = GetNewOidWithIndex(conDesc, ConstraintOidIndexId,
178 : Anum_pg_constraint_oid);
179 32956 : values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(conOid);
180 32956 : values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
181 32956 : values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
182 32956 : values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
183 32956 : values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
184 32956 : values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
185 32956 : values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
186 32956 : values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
187 32956 : values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
188 32956 : values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
189 32956 : values[Anum_pg_constraint_conparentid - 1] = ObjectIdGetDatum(parentConstrId);
190 32956 : values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
191 32956 : values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
192 32956 : values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
193 32956 : values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
194 32956 : values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
195 32956 : values[Anum_pg_constraint_coninhcount - 1] = Int16GetDatum(conInhCount);
196 32956 : values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
197 32956 : values[Anum_pg_constraint_conperiod - 1] = BoolGetDatum(conPeriod);
198 :
199 32956 : if (conkeyArray)
200 32072 : values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
201 : else
202 884 : nulls[Anum_pg_constraint_conkey - 1] = true;
203 :
204 32956 : if (confkeyArray)
205 3466 : values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
206 : else
207 29490 : nulls[Anum_pg_constraint_confkey - 1] = true;
208 :
209 32956 : if (conpfeqopArray)
210 3466 : values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
211 : else
212 29490 : nulls[Anum_pg_constraint_conpfeqop - 1] = true;
213 :
214 32956 : if (conppeqopArray)
215 3466 : values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
216 : else
217 29490 : nulls[Anum_pg_constraint_conppeqop - 1] = true;
218 :
219 32956 : if (conffeqopArray)
220 3466 : values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
221 : else
222 29490 : nulls[Anum_pg_constraint_conffeqop - 1] = true;
223 :
224 32956 : if (confdelsetcolsArray)
225 60 : values[Anum_pg_constraint_confdelsetcols - 1] = PointerGetDatum(confdelsetcolsArray);
226 : else
227 32896 : nulls[Anum_pg_constraint_confdelsetcols - 1] = true;
228 :
229 32956 : if (conexclopArray)
230 604 : values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
231 : else
232 32352 : nulls[Anum_pg_constraint_conexclop - 1] = true;
233 :
234 32956 : if (conBin)
235 2730 : values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
236 : else
237 30226 : nulls[Anum_pg_constraint_conbin - 1] = true;
238 :
239 32956 : tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
240 :
241 32956 : CatalogTupleInsert(conDesc, tup);
242 :
243 32956 : ObjectAddressSet(conobject, ConstraintRelationId, conOid);
244 :
245 32956 : table_close(conDesc, RowExclusiveLock);
246 :
247 : /* Handle set of auto dependencies */
248 32956 : addrs_auto = new_object_addresses();
249 :
250 32956 : 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 32236 : if (constraintNTotalKeys > 0)
259 : {
260 72588 : for (i = 0; i < constraintNTotalKeys; i++)
261 : {
262 40516 : ObjectAddressSubSet(relobject, RelationRelationId, relId,
263 : constraintKey[i]);
264 40516 : 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 32956 : if (OidIsValid(domainId))
275 : {
276 : /*
277 : * Register auto dependency from constraint to owning domain
278 : */
279 : ObjectAddress domobject;
280 :
281 720 : ObjectAddressSet(domobject, TypeRelationId, domainId);
282 720 : add_exact_object_address(&domobject, addrs_auto);
283 : }
284 :
285 32956 : record_object_address_dependencies(&conobject, addrs_auto,
286 : DEPENDENCY_AUTO);
287 32956 : free_object_addresses(addrs_auto);
288 :
289 : /* Handle set of normal dependencies */
290 32956 : addrs_normal = new_object_addresses();
291 :
292 32956 : 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 3466 : if (foreignNKeys > 0)
301 : {
302 7864 : for (i = 0; i < foreignNKeys; i++)
303 : {
304 4398 : ObjectAddressSubSet(relobject, RelationRelationId,
305 : foreignRelId, foreignKey[i]);
306 4398 : 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 32956 : 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 3466 : ObjectAddressSet(relobject, RelationRelationId, indexRelId);
327 3466 : add_exact_object_address(&relobject, addrs_normal);
328 : }
329 :
330 32956 : 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 3466 : oprobject.classId = OperatorRelationId;
341 3466 : oprobject.objectSubId = 0;
342 :
343 7864 : for (i = 0; i < foreignNKeys; i++)
344 : {
345 4398 : oprobject.objectId = pfEqOp[i];
346 4398 : add_exact_object_address(&oprobject, addrs_normal);
347 4398 : if (ppEqOp[i] != pfEqOp[i])
348 : {
349 56 : oprobject.objectId = ppEqOp[i];
350 56 : add_exact_object_address(&oprobject, addrs_normal);
351 : }
352 4398 : 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 32956 : record_object_address_dependencies(&conobject, addrs_normal,
361 : DEPENDENCY_NORMAL);
362 32956 : 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 32956 : if (conExpr != NULL)
373 : {
374 : /*
375 : * Register dependencies from constraint to objects mentioned in CHECK
376 : * expression.
377 : */
378 2730 : recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
379 : DEPENDENCY_NORMAL,
380 : DEPENDENCY_NORMAL, false);
381 : }
382 :
383 : /* Post creation hook for new constraint */
384 32956 : InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
385 : is_internal);
386 :
387 32956 : 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 15322 : 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 15322 : conDesc = table_open(ConstraintRelationId, AccessShareLock);
412 :
413 15322 : ScanKeyInit(&skey[0],
414 : Anum_pg_constraint_conrelid,
415 : BTEqualStrategyNumber, F_OIDEQ,
416 : ObjectIdGetDatum((conCat == CONSTRAINT_RELATION)
417 : ? objId : InvalidOid));
418 15322 : ScanKeyInit(&skey[1],
419 : Anum_pg_constraint_contypid,
420 : BTEqualStrategyNumber, F_OIDEQ,
421 : ObjectIdGetDatum((conCat == CONSTRAINT_DOMAIN)
422 : ? objId : InvalidOid));
423 15322 : ScanKeyInit(&skey[2],
424 : Anum_pg_constraint_conname,
425 : BTEqualStrategyNumber, F_NAMEEQ,
426 : CStringGetDatum(conname));
427 :
428 15322 : conscan = systable_beginscan(conDesc, ConstraintRelidTypidNameIndexId,
429 : true, NULL, 3, skey);
430 :
431 : /* There can be at most one matching row */
432 15322 : found = (HeapTupleIsValid(systable_getnext(conscan)));
433 :
434 15322 : systable_endscan(conscan);
435 15322 : table_close(conDesc, AccessShareLock);
436 :
437 15322 : 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 8390 : ConstraintNameExists(const char *conname, Oid namespaceid)
449 : {
450 : bool found;
451 : Relation conDesc;
452 : SysScanDesc conscan;
453 : ScanKeyData skey[2];
454 :
455 8390 : conDesc = table_open(ConstraintRelationId, AccessShareLock);
456 :
457 8390 : ScanKeyInit(&skey[0],
458 : Anum_pg_constraint_conname,
459 : BTEqualStrategyNumber, F_NAMEEQ,
460 : CStringGetDatum(conname));
461 :
462 8390 : ScanKeyInit(&skey[1],
463 : Anum_pg_constraint_connamespace,
464 : BTEqualStrategyNumber, F_OIDEQ,
465 : ObjectIdGetDatum(namespaceid));
466 :
467 8390 : conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
468 : NULL, 2, skey);
469 :
470 8390 : found = (HeapTupleIsValid(systable_getnext(conscan)));
471 :
472 8390 : systable_endscan(conscan);
473 8390 : table_close(conDesc, AccessShareLock);
474 :
475 8390 : 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 10130 : ChooseConstraintName(const char *name1, const char *name2,
503 : const char *label, Oid namespaceid,
504 : List *others)
505 : {
506 10130 : int pass = 0;
507 10130 : 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 10130 : conDesc = table_open(ConstraintRelationId, AccessShareLock);
516 :
517 : /* try the unmodified label first */
518 10130 : strlcpy(modlabel, label, sizeof(modlabel));
519 :
520 : for (;;)
521 : {
522 11752 : conname = makeObjectName(name1, name2, modlabel);
523 :
524 11752 : found = false;
525 :
526 14254 : foreach(l, others)
527 : {
528 2520 : if (strcmp((char *) lfirst(l), conname) == 0)
529 : {
530 18 : found = true;
531 18 : break;
532 : }
533 : }
534 :
535 11752 : if (!found)
536 : {
537 11734 : ScanKeyInit(&skey[0],
538 : Anum_pg_constraint_conname,
539 : BTEqualStrategyNumber, F_NAMEEQ,
540 : CStringGetDatum(conname));
541 :
542 11734 : ScanKeyInit(&skey[1],
543 : Anum_pg_constraint_connamespace,
544 : BTEqualStrategyNumber, F_OIDEQ,
545 : ObjectIdGetDatum(namespaceid));
546 :
547 11734 : conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
548 : NULL, 2, skey);
549 :
550 11734 : found = (HeapTupleIsValid(systable_getnext(conscan)));
551 :
552 11734 : systable_endscan(conscan);
553 : }
554 :
555 11752 : if (!found)
556 10130 : break;
557 :
558 : /* found a conflict, so try a new name component */
559 1622 : pfree(conname);
560 1622 : snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
561 : }
562 :
563 10130 : table_close(conDesc, AccessShareLock);
564 :
565 10130 : 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 : *
572 : * XXX This would be easier if we had pg_attribute.notnullconstr with the OID
573 : * of the constraint that implements the not-null constraint for that column.
574 : * I'm not sure it's worth the catalog bloat and de-normalization, however.
575 : */
576 : HeapTuple
577 16740 : findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
578 : {
579 : Relation pg_constraint;
580 : HeapTuple conTup,
581 16740 : retval = NULL;
582 : SysScanDesc scan;
583 : ScanKeyData key;
584 :
585 16740 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
586 16740 : ScanKeyInit(&key,
587 : Anum_pg_constraint_conrelid,
588 : BTEqualStrategyNumber, F_OIDEQ,
589 : ObjectIdGetDatum(relid));
590 16740 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId,
591 : true, NULL, 1, &key);
592 :
593 24090 : while (HeapTupleIsValid(conTup = systable_getnext(scan)))
594 : {
595 9776 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(conTup);
596 : AttrNumber conkey;
597 :
598 : /*
599 : * We're looking for a NOTNULL constraint that's marked validated,
600 : * with the column we're looking for as the sole element in conkey.
601 : */
602 9776 : if (con->contype != CONSTRAINT_NOTNULL)
603 1636 : continue;
604 8140 : if (!con->convalidated)
605 0 : continue;
606 :
607 8140 : conkey = extractNotNullColumn(conTup);
608 8140 : if (conkey != attnum)
609 5714 : continue;
610 :
611 : /* Found it */
612 2426 : retval = heap_copytuple(conTup);
613 2426 : break;
614 : }
615 :
616 16740 : systable_endscan(scan);
617 16740 : table_close(pg_constraint, AccessShareLock);
618 :
619 16740 : return retval;
620 : }
621 :
622 : /*
623 : * Find and return the pg_constraint tuple that implements a validated
624 : * not-null constraint for the given column of the given relation.
625 : */
626 : HeapTuple
627 308 : findNotNullConstraint(Oid relid, const char *colname)
628 : {
629 308 : AttrNumber attnum = get_attnum(relid, colname);
630 :
631 308 : return findNotNullConstraintAttnum(relid, attnum);
632 : }
633 :
634 : /*
635 : * Find and return the pg_constraint tuple that implements a validated
636 : * not-null constraint for the given domain.
637 : */
638 : HeapTuple
639 12 : findDomainNotNullConstraint(Oid typid)
640 : {
641 : Relation pg_constraint;
642 : HeapTuple conTup,
643 12 : retval = NULL;
644 : SysScanDesc scan;
645 : ScanKeyData key;
646 :
647 12 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
648 12 : ScanKeyInit(&key,
649 : Anum_pg_constraint_contypid,
650 : BTEqualStrategyNumber, F_OIDEQ,
651 : ObjectIdGetDatum(typid));
652 12 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId,
653 : true, NULL, 1, &key);
654 :
655 12 : while (HeapTupleIsValid(conTup = systable_getnext(scan)))
656 : {
657 12 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(conTup);
658 :
659 : /*
660 : * We're looking for a NOTNULL constraint that's marked validated.
661 : */
662 12 : if (con->contype != CONSTRAINT_NOTNULL)
663 0 : continue;
664 12 : if (!con->convalidated)
665 0 : continue;
666 :
667 : /* Found it */
668 12 : retval = heap_copytuple(conTup);
669 12 : break;
670 : }
671 :
672 12 : systable_endscan(scan);
673 12 : table_close(pg_constraint, AccessShareLock);
674 :
675 12 : return retval;
676 : }
677 :
678 : /*
679 : * Given a pg_constraint tuple for a not-null constraint, return the column
680 : * number it is for.
681 : */
682 : AttrNumber
683 18910 : extractNotNullColumn(HeapTuple constrTup)
684 : {
685 : AttrNumber colnum;
686 : Datum adatum;
687 : ArrayType *arr;
688 :
689 : /* only tuples for not-null constraints should be given */
690 : Assert(((Form_pg_constraint) GETSTRUCT(constrTup))->contype == CONSTRAINT_NOTNULL);
691 :
692 18910 : adatum = SysCacheGetAttrNotNull(CONSTROID, constrTup,
693 : Anum_pg_constraint_conkey);
694 18910 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
695 18910 : if (ARR_NDIM(arr) != 1 ||
696 18910 : ARR_HASNULL(arr) ||
697 18910 : ARR_ELEMTYPE(arr) != INT2OID ||
698 18910 : ARR_DIMS(arr)[0] != 1)
699 0 : elog(ERROR, "conkey is not a 1-D smallint array");
700 :
701 18910 : memcpy(&colnum, ARR_DATA_PTR(arr), sizeof(AttrNumber));
702 :
703 18910 : if ((Pointer) arr != DatumGetPointer(adatum))
704 18910 : pfree(arr); /* free de-toasted copy, if any */
705 :
706 18910 : return colnum;
707 : }
708 :
709 : /*
710 : * AdjustNotNullInheritance1
711 : * Adjust inheritance count for a single not-null constraint
712 : *
713 : * If no not-null constraint is found for the column, return 0.
714 : * Caller can create one.
715 : * If the constraint does exist and it's inheritable, adjust its
716 : * inheritance count (and possibly islocal status) and return 1.
717 : * No further action needs to be taken.
718 : * If the constraint exists but is marked NO INHERIT, adjust it as above
719 : * and reset connoinherit to false, and return -1. Caller is
720 : * responsible for adding the same constraint to the children, if any.
721 : */
722 : int
723 1248 : AdjustNotNullInheritance1(Oid relid, AttrNumber attnum, int count,
724 : bool is_no_inherit, bool allow_noinherit_change)
725 : {
726 : HeapTuple tup;
727 :
728 : Assert(count >= 0);
729 :
730 1248 : tup = findNotNullConstraintAttnum(relid, attnum);
731 1248 : if (HeapTupleIsValid(tup))
732 : {
733 : Relation pg_constraint;
734 : Form_pg_constraint conform;
735 104 : int retval = 1;
736 :
737 104 : pg_constraint = table_open(ConstraintRelationId, RowExclusiveLock);
738 104 : conform = (Form_pg_constraint) GETSTRUCT(tup);
739 :
740 : /*
741 : * If we're asked for a NO INHERIT constraint and this relation
742 : * already has an inheritable one, throw an error.
743 : */
744 104 : if (is_no_inherit && !conform->connoinherit)
745 6 : ereport(ERROR,
746 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
747 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
748 : NameStr(conform->conname), get_rel_name(relid)));
749 :
750 : /*
751 : * If the constraint already exists in this relation but it's marked
752 : * NO INHERIT, we can just remove that flag (provided caller allows
753 : * such a change), and instruct caller to recurse to add the
754 : * constraint to children.
755 : */
756 98 : if (!is_no_inherit && conform->connoinherit)
757 : {
758 30 : if (!allow_noinherit_change)
759 6 : ereport(ERROR,
760 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
761 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" in relation \"%s\"",
762 : NameStr(conform->conname), get_rel_name(relid)));
763 :
764 24 : conform->connoinherit = false;
765 24 : retval = -1; /* caller must add constraint on child rels */
766 : }
767 :
768 92 : if (count > 0)
769 86 : conform->coninhcount += count;
770 :
771 : /* sanity check */
772 92 : if (conform->coninhcount < 0)
773 0 : elog(ERROR, "invalid inhcount %d for constraint \"%s\" on relation \"%s\"",
774 : conform->coninhcount, NameStr(conform->conname),
775 : get_rel_name(relid));
776 :
777 : /*
778 : * If the constraint is no longer inherited, mark it local. It's
779 : * arguable that we should drop it instead, but it's hard to see that
780 : * being better. The user can drop it manually later.
781 : */
782 92 : if (conform->coninhcount == 0)
783 6 : conform->conislocal = true;
784 :
785 92 : CatalogTupleUpdate(pg_constraint, &tup->t_self, tup);
786 :
787 92 : table_close(pg_constraint, RowExclusiveLock);
788 :
789 92 : return retval;
790 : }
791 :
792 1144 : return 0;
793 : }
794 :
795 : /*
796 : * AdjustNotNullInheritance
797 : * Adjust not-null constraints' inhcount/islocal for
798 : * ALTER TABLE [NO] INHERITS
799 : *
800 : * Mark the NOT NULL constraints for the given relation columns as
801 : * inherited, so that they can't be dropped.
802 : *
803 : * Caller must have checked beforehand that attnotnull was set for all
804 : * columns. However, some of those could be set because of a primary
805 : * key, so throw a proper user-visible error if one is not found.
806 : */
807 : void
808 36 : AdjustNotNullInheritance(Oid relid, Bitmapset *columns, int count)
809 : {
810 : Relation pg_constraint;
811 : int attnum;
812 :
813 36 : pg_constraint = table_open(ConstraintRelationId, RowExclusiveLock);
814 :
815 : /*
816 : * Scan the set of columns and bump inhcount for each.
817 : */
818 36 : attnum = -1;
819 66 : while ((attnum = bms_next_member(columns, attnum)) >= 0)
820 : {
821 : HeapTuple tup;
822 : Form_pg_constraint conform;
823 :
824 36 : tup = findNotNullConstraintAttnum(relid, attnum);
825 36 : if (!HeapTupleIsValid(tup))
826 6 : ereport(ERROR,
827 : errcode(ERRCODE_DATATYPE_MISMATCH),
828 : errmsg("column \"%s\" in child table must be marked NOT NULL",
829 : get_attname(relid, attnum,
830 : false)));
831 :
832 30 : conform = (Form_pg_constraint) GETSTRUCT(tup);
833 30 : conform->coninhcount += count;
834 30 : if (conform->coninhcount < 0)
835 0 : elog(ERROR, "invalid inhcount %d for constraint \"%s\" on relation \"%s\"",
836 : conform->coninhcount, NameStr(conform->conname),
837 : get_rel_name(relid));
838 :
839 : /*
840 : * If the constraints are no longer inherited, mark them local. It's
841 : * arguable that we should drop them instead, but it's hard to see
842 : * that being better. The user can drop it manually later.
843 : */
844 30 : if (conform->coninhcount == 0)
845 0 : conform->conislocal = true;
846 :
847 30 : CatalogTupleUpdate(pg_constraint, &tup->t_self, tup);
848 : }
849 :
850 30 : table_close(pg_constraint, RowExclusiveLock);
851 30 : }
852 :
853 : /*
854 : * RelationGetNotNullConstraints
855 : * Return the list of not-null constraints for the given rel
856 : *
857 : * Caller can request cooked constraints, or raw.
858 : *
859 : * This is seldom needed, so we just scan pg_constraint each time.
860 : *
861 : * XXX This is only used to create derived tables, so NO INHERIT constraints
862 : * are always skipped.
863 : */
864 : List *
865 12386 : RelationGetNotNullConstraints(Oid relid, bool cooked)
866 : {
867 12386 : List *notnulls = NIL;
868 : Relation constrRel;
869 : HeapTuple htup;
870 : SysScanDesc conscan;
871 : ScanKeyData skey;
872 :
873 12386 : constrRel = table_open(ConstraintRelationId, AccessShareLock);
874 12386 : ScanKeyInit(&skey,
875 : Anum_pg_constraint_conrelid,
876 : BTEqualStrategyNumber, F_OIDEQ,
877 : ObjectIdGetDatum(relid));
878 12386 : conscan = systable_beginscan(constrRel, ConstraintRelidTypidNameIndexId, true,
879 : NULL, 1, &skey);
880 :
881 16908 : while (HeapTupleIsValid(htup = systable_getnext(conscan)))
882 : {
883 4522 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(htup);
884 : AttrNumber colnum;
885 :
886 4522 : if (conForm->contype != CONSTRAINT_NOTNULL)
887 3196 : continue;
888 1326 : if (conForm->connoinherit)
889 60 : continue;
890 :
891 1266 : colnum = extractNotNullColumn(htup);
892 :
893 1266 : if (cooked)
894 : {
895 : CookedConstraint *cooked;
896 :
897 1052 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
898 :
899 1052 : cooked->contype = CONSTR_NOTNULL;
900 1052 : cooked->name = pstrdup(NameStr(conForm->conname));
901 1052 : cooked->attnum = colnum;
902 1052 : cooked->expr = NULL;
903 1052 : cooked->skip_validation = false;
904 1052 : cooked->is_local = true;
905 1052 : cooked->inhcount = 0;
906 1052 : cooked->is_no_inherit = conForm->connoinherit;
907 :
908 1052 : notnulls = lappend(notnulls, cooked);
909 : }
910 : else
911 : {
912 : Constraint *constr;
913 :
914 214 : constr = makeNode(Constraint);
915 214 : constr->contype = CONSTR_NOTNULL;
916 214 : constr->conname = pstrdup(NameStr(conForm->conname));
917 214 : constr->deferrable = false;
918 214 : constr->initdeferred = false;
919 214 : constr->location = -1;
920 214 : constr->keys = list_make1(makeString(get_attname(relid, colnum,
921 : false)));
922 214 : constr->skip_validation = false;
923 214 : constr->initially_valid = true;
924 214 : notnulls = lappend(notnulls, constr);
925 : }
926 : }
927 :
928 12386 : systable_endscan(conscan);
929 12386 : table_close(constrRel, AccessShareLock);
930 :
931 12386 : return notnulls;
932 : }
933 :
934 :
935 : /*
936 : * Delete a single constraint record.
937 : */
938 : void
939 19994 : RemoveConstraintById(Oid conId)
940 : {
941 : Relation conDesc;
942 : HeapTuple tup;
943 : Form_pg_constraint con;
944 19994 : bool dropping_pk = false;
945 19994 : List *unconstrained_cols = NIL;
946 :
947 19994 : conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
948 :
949 19994 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
950 19994 : if (!HeapTupleIsValid(tup)) /* should not happen */
951 0 : elog(ERROR, "cache lookup failed for constraint %u", conId);
952 19994 : con = (Form_pg_constraint) GETSTRUCT(tup);
953 :
954 : /*
955 : * Special processing depending on what the constraint is for.
956 : */
957 19994 : if (OidIsValid(con->conrelid))
958 : {
959 : Relation rel;
960 :
961 : /*
962 : * If the constraint is for a relation, open and exclusive-lock the
963 : * relation it's for.
964 : */
965 19660 : rel = table_open(con->conrelid, AccessExclusiveLock);
966 :
967 : /*
968 : * We need to update the relchecks count if it is a check constraint
969 : * being dropped. This update will force backends to rebuild relcache
970 : * entries when we commit. For not-null and primary key constraints,
971 : * obtain the list of columns affected, so that we can reset their
972 : * attnotnull flags below.
973 : */
974 19658 : if (con->contype == CONSTRAINT_CHECK)
975 : {
976 : Relation pgrel;
977 : HeapTuple relTup;
978 : Form_pg_class classForm;
979 :
980 1696 : pgrel = table_open(RelationRelationId, RowExclusiveLock);
981 1696 : relTup = SearchSysCacheCopy1(RELOID,
982 : ObjectIdGetDatum(con->conrelid));
983 1696 : if (!HeapTupleIsValid(relTup))
984 0 : elog(ERROR, "cache lookup failed for relation %u",
985 : con->conrelid);
986 1696 : classForm = (Form_pg_class) GETSTRUCT(relTup);
987 :
988 1696 : if (classForm->relchecks == 0) /* should not happen */
989 0 : elog(ERROR, "relation \"%s\" has relchecks = 0",
990 : RelationGetRelationName(rel));
991 1696 : classForm->relchecks--;
992 :
993 1696 : CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
994 :
995 1696 : heap_freetuple(relTup);
996 :
997 1696 : table_close(pgrel, RowExclusiveLock);
998 : }
999 17962 : else if (con->contype == CONSTRAINT_NOTNULL)
1000 : {
1001 7290 : unconstrained_cols = list_make1_int(extractNotNullColumn(tup));
1002 : }
1003 10672 : else if (con->contype == CONSTRAINT_PRIMARY)
1004 : {
1005 : Datum adatum;
1006 : ArrayType *arr;
1007 : int numkeys;
1008 : bool isNull;
1009 : int16 *attnums;
1010 :
1011 6698 : dropping_pk = true;
1012 :
1013 6698 : adatum = heap_getattr(tup, Anum_pg_constraint_conkey,
1014 : RelationGetDescr(conDesc), &isNull);
1015 6698 : if (isNull)
1016 0 : elog(ERROR, "null conkey for constraint %u", con->oid);
1017 6698 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1018 6698 : numkeys = ARR_DIMS(arr)[0];
1019 6698 : if (ARR_NDIM(arr) != 1 ||
1020 6698 : numkeys < 0 ||
1021 6698 : ARR_HASNULL(arr) ||
1022 6698 : ARR_ELEMTYPE(arr) != INT2OID)
1023 0 : elog(ERROR, "conkey is not a 1-D smallint array");
1024 6698 : attnums = (int16 *) ARR_DATA_PTR(arr);
1025 :
1026 14176 : for (int i = 0; i < numkeys; i++)
1027 7478 : unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
1028 : }
1029 :
1030 : /* Keep lock on constraint's rel until end of xact */
1031 19658 : table_close(rel, NoLock);
1032 : }
1033 334 : else if (OidIsValid(con->contypid))
1034 : {
1035 : /*
1036 : * XXX for now, do nothing special when dropping a domain constraint
1037 : *
1038 : * Probably there should be some form of locking on the domain type,
1039 : * but we have no such concept at the moment.
1040 : */
1041 : }
1042 : else
1043 0 : elog(ERROR, "constraint %u is not of a known type", conId);
1044 :
1045 : /* Fry the constraint itself */
1046 19992 : CatalogTupleDelete(conDesc, &tup->t_self);
1047 :
1048 : /*
1049 : * If this was a NOT NULL or the primary key, the constrained columns must
1050 : * have had pg_attribute.attnotnull set. See if we need to reset it, and
1051 : * do so.
1052 : */
1053 19992 : if (unconstrained_cols != NIL)
1054 : {
1055 : Relation tablerel;
1056 : Relation attrel;
1057 : Bitmapset *pkcols;
1058 : ListCell *lc;
1059 :
1060 : /* Make the above deletion visible */
1061 13988 : CommandCounterIncrement();
1062 :
1063 13988 : tablerel = table_open(con->conrelid, NoLock); /* already have lock */
1064 13988 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
1065 :
1066 : /*
1067 : * We want to test columns for their presence in the primary key, but
1068 : * only if we're not dropping it.
1069 : */
1070 13988 : pkcols = dropping_pk ? NULL :
1071 7290 : RelationGetIndexAttrBitmap(tablerel,
1072 : INDEX_ATTR_BITMAP_PRIMARY_KEY);
1073 :
1074 28756 : foreach(lc, unconstrained_cols)
1075 : {
1076 14768 : AttrNumber attnum = lfirst_int(lc);
1077 : HeapTuple atttup;
1078 : HeapTuple contup;
1079 : Bitmapset *ircols;
1080 : Form_pg_attribute attForm;
1081 :
1082 : /*
1083 : * Obtain pg_attribute tuple and verify conditions on it. We use
1084 : * a copy we can scribble on.
1085 : */
1086 14768 : atttup = SearchSysCacheCopyAttNum(con->conrelid, attnum);
1087 14768 : if (!HeapTupleIsValid(atttup))
1088 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
1089 : attnum, con->conrelid);
1090 14768 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
1091 :
1092 : /*
1093 : * Since the above deletion has been made visible, we can now
1094 : * search for any remaining constraints setting this column as
1095 : * not-nullable; if we find any, no need to reset attnotnull.
1096 : */
1097 14768 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
1098 : pkcols))
1099 216 : continue;
1100 14552 : contup = findNotNullConstraintAttnum(con->conrelid, attnum);
1101 14552 : if (contup)
1102 1976 : continue;
1103 :
1104 : /*
1105 : * Also no reset if the column is in the replica identity or it's
1106 : * a generated column
1107 : */
1108 12576 : if (attForm->attidentity != '\0')
1109 222 : continue;
1110 12354 : ircols = RelationGetIndexAttrBitmap(tablerel,
1111 : INDEX_ATTR_BITMAP_IDENTITY_KEY);
1112 12354 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
1113 : ircols))
1114 72 : continue;
1115 :
1116 : /* Reset attnotnull */
1117 12282 : if (attForm->attnotnull)
1118 : {
1119 12282 : attForm->attnotnull = false;
1120 12282 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
1121 : }
1122 : }
1123 :
1124 13988 : table_close(attrel, RowExclusiveLock);
1125 13988 : table_close(tablerel, NoLock);
1126 : }
1127 :
1128 : /* Clean up */
1129 19992 : ReleaseSysCache(tup);
1130 19992 : table_close(conDesc, RowExclusiveLock);
1131 19992 : }
1132 :
1133 : /*
1134 : * RenameConstraintById
1135 : * Rename a constraint.
1136 : *
1137 : * Note: this isn't intended to be a user-exposed function; it doesn't check
1138 : * permissions etc. Currently this is only invoked when renaming an index
1139 : * that is associated with a constraint, but it's made a little more general
1140 : * than that with the expectation of someday having ALTER TABLE RENAME
1141 : * CONSTRAINT.
1142 : */
1143 : void
1144 90 : RenameConstraintById(Oid conId, const char *newname)
1145 : {
1146 : Relation conDesc;
1147 : HeapTuple tuple;
1148 : Form_pg_constraint con;
1149 :
1150 90 : conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
1151 :
1152 90 : tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
1153 90 : if (!HeapTupleIsValid(tuple))
1154 0 : elog(ERROR, "cache lookup failed for constraint %u", conId);
1155 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
1156 :
1157 : /*
1158 : * For user-friendliness, check whether the name is already in use.
1159 : */
1160 174 : if (OidIsValid(con->conrelid) &&
1161 84 : ConstraintNameIsUsed(CONSTRAINT_RELATION,
1162 : con->conrelid,
1163 : newname))
1164 0 : ereport(ERROR,
1165 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1166 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
1167 : newname, get_rel_name(con->conrelid))));
1168 96 : if (OidIsValid(con->contypid) &&
1169 6 : ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
1170 : con->contypid,
1171 : newname))
1172 0 : ereport(ERROR,
1173 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1174 : errmsg("constraint \"%s\" for domain %s already exists",
1175 : newname, format_type_be(con->contypid))));
1176 :
1177 : /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
1178 90 : namestrcpy(&(con->conname), newname);
1179 :
1180 90 : CatalogTupleUpdate(conDesc, &tuple->t_self, tuple);
1181 :
1182 90 : InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
1183 :
1184 90 : heap_freetuple(tuple);
1185 90 : table_close(conDesc, RowExclusiveLock);
1186 90 : }
1187 :
1188 : /*
1189 : * AlterConstraintNamespaces
1190 : * Find any constraints belonging to the specified object,
1191 : * and move them to the specified new namespace.
1192 : *
1193 : * isType indicates whether the owning object is a type or a relation.
1194 : */
1195 : void
1196 102 : AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
1197 : Oid newNspId, bool isType, ObjectAddresses *objsMoved)
1198 : {
1199 : Relation conRel;
1200 : ScanKeyData key[2];
1201 : SysScanDesc scan;
1202 : HeapTuple tup;
1203 :
1204 102 : conRel = table_open(ConstraintRelationId, RowExclusiveLock);
1205 :
1206 102 : ScanKeyInit(&key[0],
1207 : Anum_pg_constraint_conrelid,
1208 : BTEqualStrategyNumber, F_OIDEQ,
1209 : ObjectIdGetDatum(isType ? InvalidOid : ownerId));
1210 102 : ScanKeyInit(&key[1],
1211 : Anum_pg_constraint_contypid,
1212 : BTEqualStrategyNumber, F_OIDEQ,
1213 : ObjectIdGetDatum(isType ? ownerId : InvalidOid));
1214 :
1215 102 : scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
1216 : NULL, 2, key);
1217 :
1218 236 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
1219 : {
1220 134 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
1221 : ObjectAddress thisobj;
1222 :
1223 134 : ObjectAddressSet(thisobj, ConstraintRelationId, conform->oid);
1224 :
1225 134 : if (object_address_present(&thisobj, objsMoved))
1226 0 : continue;
1227 :
1228 : /* Don't update if the object is already part of the namespace */
1229 134 : if (conform->connamespace == oldNspId && oldNspId != newNspId)
1230 : {
1231 104 : tup = heap_copytuple(tup);
1232 104 : conform = (Form_pg_constraint) GETSTRUCT(tup);
1233 :
1234 104 : conform->connamespace = newNspId;
1235 :
1236 104 : CatalogTupleUpdate(conRel, &tup->t_self, tup);
1237 :
1238 : /*
1239 : * Note: currently, the constraint will not have its own
1240 : * dependency on the namespace, so we don't need to do
1241 : * changeDependencyFor().
1242 : */
1243 : }
1244 :
1245 134 : InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
1246 :
1247 134 : add_exact_object_address(&thisobj, objsMoved);
1248 : }
1249 :
1250 102 : systable_endscan(scan);
1251 :
1252 102 : table_close(conRel, RowExclusiveLock);
1253 102 : }
1254 :
1255 : /*
1256 : * ConstraintSetParentConstraint
1257 : * Set a partition's constraint as child of its parent constraint,
1258 : * or remove the linkage if parentConstrId is InvalidOid.
1259 : *
1260 : * This updates the constraint's pg_constraint row to show it as inherited, and
1261 : * adds PARTITION dependencies to prevent the constraint from being deleted
1262 : * on its own. Alternatively, reverse that.
1263 : */
1264 : void
1265 552 : ConstraintSetParentConstraint(Oid childConstrId,
1266 : Oid parentConstrId,
1267 : Oid childTableId)
1268 : {
1269 : Relation constrRel;
1270 : Form_pg_constraint constrForm;
1271 : HeapTuple tuple,
1272 : newtup;
1273 : ObjectAddress depender;
1274 : ObjectAddress referenced;
1275 :
1276 552 : constrRel = table_open(ConstraintRelationId, RowExclusiveLock);
1277 552 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(childConstrId));
1278 552 : if (!HeapTupleIsValid(tuple))
1279 0 : elog(ERROR, "cache lookup failed for constraint %u", childConstrId);
1280 552 : newtup = heap_copytuple(tuple);
1281 552 : constrForm = (Form_pg_constraint) GETSTRUCT(newtup);
1282 552 : if (OidIsValid(parentConstrId))
1283 : {
1284 : /* don't allow setting parent for a constraint that already has one */
1285 : Assert(constrForm->coninhcount == 0);
1286 310 : if (constrForm->conparentid != InvalidOid)
1287 0 : elog(ERROR, "constraint %u already has a parent constraint",
1288 : childConstrId);
1289 :
1290 310 : constrForm->conislocal = false;
1291 310 : constrForm->coninhcount++;
1292 310 : if (constrForm->coninhcount < 0)
1293 0 : ereport(ERROR,
1294 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1295 : errmsg("too many inheritance parents"));
1296 310 : constrForm->conparentid = parentConstrId;
1297 :
1298 310 : CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
1299 :
1300 310 : ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
1301 :
1302 310 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
1303 310 : recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
1304 :
1305 310 : ObjectAddressSet(referenced, RelationRelationId, childTableId);
1306 310 : recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
1307 : }
1308 : else
1309 : {
1310 242 : constrForm->coninhcount--;
1311 242 : constrForm->conislocal = true;
1312 242 : constrForm->conparentid = InvalidOid;
1313 :
1314 : /* Make sure there's no further inheritance. */
1315 : Assert(constrForm->coninhcount == 0);
1316 :
1317 242 : CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
1318 :
1319 242 : deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
1320 : ConstraintRelationId,
1321 : DEPENDENCY_PARTITION_PRI);
1322 242 : deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
1323 : RelationRelationId,
1324 : DEPENDENCY_PARTITION_SEC);
1325 : }
1326 :
1327 552 : ReleaseSysCache(tuple);
1328 552 : table_close(constrRel, RowExclusiveLock);
1329 552 : }
1330 :
1331 :
1332 : /*
1333 : * get_relation_constraint_oid
1334 : * Find a constraint on the specified relation with the specified name.
1335 : * Returns constraint's OID.
1336 : */
1337 : Oid
1338 338 : get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
1339 : {
1340 : Relation pg_constraint;
1341 : HeapTuple tuple;
1342 : SysScanDesc scan;
1343 : ScanKeyData skey[3];
1344 338 : Oid conOid = InvalidOid;
1345 :
1346 338 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1347 :
1348 338 : ScanKeyInit(&skey[0],
1349 : Anum_pg_constraint_conrelid,
1350 : BTEqualStrategyNumber, F_OIDEQ,
1351 : ObjectIdGetDatum(relid));
1352 338 : ScanKeyInit(&skey[1],
1353 : Anum_pg_constraint_contypid,
1354 : BTEqualStrategyNumber, F_OIDEQ,
1355 : ObjectIdGetDatum(InvalidOid));
1356 338 : ScanKeyInit(&skey[2],
1357 : Anum_pg_constraint_conname,
1358 : BTEqualStrategyNumber, F_NAMEEQ,
1359 : CStringGetDatum(conname));
1360 :
1361 338 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1362 : NULL, 3, skey);
1363 :
1364 : /* There can be at most one matching row */
1365 338 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1366 326 : conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1367 :
1368 338 : systable_endscan(scan);
1369 :
1370 : /* If no such constraint exists, complain */
1371 338 : if (!OidIsValid(conOid) && !missing_ok)
1372 12 : ereport(ERROR,
1373 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1374 : errmsg("constraint \"%s\" for table \"%s\" does not exist",
1375 : conname, get_rel_name(relid))));
1376 :
1377 326 : table_close(pg_constraint, AccessShareLock);
1378 :
1379 326 : return conOid;
1380 : }
1381 :
1382 : /*
1383 : * get_relation_constraint_attnos
1384 : * Find a constraint on the specified relation with the specified name
1385 : * and return the constrained columns.
1386 : *
1387 : * Returns a Bitmapset of the column attnos of the constrained columns, with
1388 : * attnos being offset by FirstLowInvalidHeapAttributeNumber so that system
1389 : * columns can be represented.
1390 : *
1391 : * *constraintOid is set to the OID of the constraint, or InvalidOid on
1392 : * failure.
1393 : */
1394 : Bitmapset *
1395 48 : get_relation_constraint_attnos(Oid relid, const char *conname,
1396 : bool missing_ok, Oid *constraintOid)
1397 : {
1398 48 : Bitmapset *conattnos = NULL;
1399 : Relation pg_constraint;
1400 : HeapTuple tuple;
1401 : SysScanDesc scan;
1402 : ScanKeyData skey[3];
1403 :
1404 : /* Set *constraintOid, to avoid complaints about uninitialized vars */
1405 48 : *constraintOid = InvalidOid;
1406 :
1407 48 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1408 :
1409 48 : ScanKeyInit(&skey[0],
1410 : Anum_pg_constraint_conrelid,
1411 : BTEqualStrategyNumber, F_OIDEQ,
1412 : ObjectIdGetDatum(relid));
1413 48 : ScanKeyInit(&skey[1],
1414 : Anum_pg_constraint_contypid,
1415 : BTEqualStrategyNumber, F_OIDEQ,
1416 : ObjectIdGetDatum(InvalidOid));
1417 48 : ScanKeyInit(&skey[2],
1418 : Anum_pg_constraint_conname,
1419 : BTEqualStrategyNumber, F_NAMEEQ,
1420 : CStringGetDatum(conname));
1421 :
1422 48 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1423 : NULL, 3, skey);
1424 :
1425 : /* There can be at most one matching row */
1426 48 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1427 : {
1428 : Datum adatum;
1429 : bool isNull;
1430 :
1431 48 : *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1432 :
1433 : /* Extract the conkey array, ie, attnums of constrained columns */
1434 48 : adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
1435 : RelationGetDescr(pg_constraint), &isNull);
1436 48 : if (!isNull)
1437 : {
1438 : ArrayType *arr;
1439 : int numcols;
1440 : int16 *attnums;
1441 : int i;
1442 :
1443 48 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1444 48 : numcols = ARR_DIMS(arr)[0];
1445 48 : if (ARR_NDIM(arr) != 1 ||
1446 48 : numcols < 0 ||
1447 48 : ARR_HASNULL(arr) ||
1448 48 : ARR_ELEMTYPE(arr) != INT2OID)
1449 0 : elog(ERROR, "conkey is not a 1-D smallint array");
1450 48 : attnums = (int16 *) ARR_DATA_PTR(arr);
1451 :
1452 : /* Construct the result value */
1453 108 : for (i = 0; i < numcols; i++)
1454 : {
1455 60 : conattnos = bms_add_member(conattnos,
1456 60 : attnums[i] - FirstLowInvalidHeapAttributeNumber);
1457 : }
1458 : }
1459 : }
1460 :
1461 48 : systable_endscan(scan);
1462 :
1463 : /* If no such constraint exists, complain */
1464 48 : if (!OidIsValid(*constraintOid) && !missing_ok)
1465 0 : ereport(ERROR,
1466 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1467 : errmsg("constraint \"%s\" for table \"%s\" does not exist",
1468 : conname, get_rel_name(relid))));
1469 :
1470 48 : table_close(pg_constraint, AccessShareLock);
1471 :
1472 48 : return conattnos;
1473 : }
1474 :
1475 : /*
1476 : * Return the OID of the constraint enforced by the given index in the
1477 : * given relation; or InvalidOid if no such index is cataloged.
1478 : *
1479 : * Much like get_constraint_index, this function is concerned only with the
1480 : * one constraint that "owns" the given index. Therefore, constraints of
1481 : * types other than unique, primary-key, and exclusion are ignored.
1482 : */
1483 : Oid
1484 1526 : get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
1485 : {
1486 : Relation pg_constraint;
1487 : SysScanDesc scan;
1488 : ScanKeyData key;
1489 : HeapTuple tuple;
1490 1526 : Oid constraintId = InvalidOid;
1491 :
1492 1526 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1493 :
1494 1526 : ScanKeyInit(&key,
1495 : Anum_pg_constraint_conrelid,
1496 : BTEqualStrategyNumber,
1497 : F_OIDEQ,
1498 : ObjectIdGetDatum(relationId));
1499 1526 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId,
1500 : true, NULL, 1, &key);
1501 1856 : while ((tuple = systable_getnext(scan)) != NULL)
1502 : {
1503 : Form_pg_constraint constrForm;
1504 :
1505 1170 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
1506 :
1507 : /* See above */
1508 1170 : if (constrForm->contype != CONSTRAINT_PRIMARY &&
1509 396 : constrForm->contype != CONSTRAINT_UNIQUE &&
1510 306 : constrForm->contype != CONSTRAINT_EXCLUSION)
1511 306 : continue;
1512 :
1513 864 : if (constrForm->conindid == indexId)
1514 : {
1515 840 : constraintId = constrForm->oid;
1516 840 : break;
1517 : }
1518 : }
1519 1526 : systable_endscan(scan);
1520 :
1521 1526 : table_close(pg_constraint, AccessShareLock);
1522 1526 : return constraintId;
1523 : }
1524 :
1525 : /*
1526 : * get_domain_constraint_oid
1527 : * Find a constraint on the specified domain with the specified name.
1528 : * Returns constraint's OID.
1529 : */
1530 : Oid
1531 60 : get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
1532 : {
1533 : Relation pg_constraint;
1534 : HeapTuple tuple;
1535 : SysScanDesc scan;
1536 : ScanKeyData skey[3];
1537 60 : Oid conOid = InvalidOid;
1538 :
1539 60 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1540 :
1541 60 : ScanKeyInit(&skey[0],
1542 : Anum_pg_constraint_conrelid,
1543 : BTEqualStrategyNumber, F_OIDEQ,
1544 : ObjectIdGetDatum(InvalidOid));
1545 60 : ScanKeyInit(&skey[1],
1546 : Anum_pg_constraint_contypid,
1547 : BTEqualStrategyNumber, F_OIDEQ,
1548 : ObjectIdGetDatum(typid));
1549 60 : ScanKeyInit(&skey[2],
1550 : Anum_pg_constraint_conname,
1551 : BTEqualStrategyNumber, F_NAMEEQ,
1552 : CStringGetDatum(conname));
1553 :
1554 60 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1555 : NULL, 3, skey);
1556 :
1557 : /* There can be at most one matching row */
1558 60 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1559 54 : conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1560 :
1561 60 : systable_endscan(scan);
1562 :
1563 : /* If no such constraint exists, complain */
1564 60 : if (!OidIsValid(conOid) && !missing_ok)
1565 6 : ereport(ERROR,
1566 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1567 : errmsg("constraint \"%s\" for domain %s does not exist",
1568 : conname, format_type_be(typid))));
1569 :
1570 54 : table_close(pg_constraint, AccessShareLock);
1571 :
1572 54 : return conOid;
1573 : }
1574 :
1575 : /*
1576 : * get_primary_key_attnos
1577 : * Identify the columns in a relation's primary key, if any.
1578 : *
1579 : * Returns a Bitmapset of the column attnos of the primary key's columns,
1580 : * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
1581 : * system columns can be represented.
1582 : *
1583 : * If there is no primary key, return NULL. We also return NULL if the pkey
1584 : * constraint is deferrable and deferrableOk is false.
1585 : *
1586 : * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
1587 : * on failure.
1588 : */
1589 : Bitmapset *
1590 966 : get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
1591 : {
1592 966 : Bitmapset *pkattnos = NULL;
1593 : Relation pg_constraint;
1594 : HeapTuple tuple;
1595 : SysScanDesc scan;
1596 : ScanKeyData skey[1];
1597 :
1598 : /* Set *constraintOid, to avoid complaints about uninitialized vars */
1599 966 : *constraintOid = InvalidOid;
1600 :
1601 : /* Scan pg_constraint for constraints of the target rel */
1602 966 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1603 :
1604 966 : ScanKeyInit(&skey[0],
1605 : Anum_pg_constraint_conrelid,
1606 : BTEqualStrategyNumber, F_OIDEQ,
1607 : ObjectIdGetDatum(relid));
1608 :
1609 966 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1610 : NULL, 1, skey);
1611 :
1612 1128 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1613 : {
1614 514 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
1615 : Datum adatum;
1616 : bool isNull;
1617 : ArrayType *arr;
1618 : int16 *attnums;
1619 : int numkeys;
1620 : int i;
1621 :
1622 : /* Skip constraints that are not PRIMARY KEYs */
1623 514 : if (con->contype != CONSTRAINT_PRIMARY)
1624 162 : continue;
1625 :
1626 : /*
1627 : * If the primary key is deferrable, but we've been instructed to
1628 : * ignore deferrable constraints, then we might as well give up
1629 : * searching, since there can only be a single primary key on a table.
1630 : */
1631 352 : if (con->condeferrable && !deferrableOk)
1632 352 : break;
1633 :
1634 : /* Extract the conkey array, ie, attnums of PK's columns */
1635 346 : adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
1636 : RelationGetDescr(pg_constraint), &isNull);
1637 346 : if (isNull)
1638 0 : elog(ERROR, "null conkey for constraint %u",
1639 : ((Form_pg_constraint) GETSTRUCT(tuple))->oid);
1640 346 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1641 346 : numkeys = ARR_DIMS(arr)[0];
1642 346 : if (ARR_NDIM(arr) != 1 ||
1643 346 : numkeys < 0 ||
1644 346 : ARR_HASNULL(arr) ||
1645 346 : ARR_ELEMTYPE(arr) != INT2OID)
1646 0 : elog(ERROR, "conkey is not a 1-D smallint array");
1647 346 : attnums = (int16 *) ARR_DATA_PTR(arr);
1648 :
1649 : /* Construct the result value */
1650 764 : for (i = 0; i < numkeys; i++)
1651 : {
1652 418 : pkattnos = bms_add_member(pkattnos,
1653 418 : attnums[i] - FirstLowInvalidHeapAttributeNumber);
1654 : }
1655 346 : *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1656 :
1657 : /* No need to search further */
1658 346 : break;
1659 : }
1660 :
1661 966 : systable_endscan(scan);
1662 :
1663 966 : table_close(pg_constraint, AccessShareLock);
1664 :
1665 966 : return pkattnos;
1666 : }
1667 :
1668 : /*
1669 : * Extract data from the pg_constraint tuple of a foreign-key constraint.
1670 : *
1671 : * All arguments save the first are output arguments. All output arguments
1672 : * other than numfks, conkey and confkey can be passed as NULL if caller
1673 : * doesn't need them.
1674 : */
1675 : void
1676 7412 : DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
1677 : AttrNumber *conkey, AttrNumber *confkey,
1678 : Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs,
1679 : int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
1680 : {
1681 : Datum adatum;
1682 : bool isNull;
1683 : ArrayType *arr;
1684 : int numkeys;
1685 :
1686 : /*
1687 : * We expect the arrays to be 1-D arrays of the right types; verify that.
1688 : * We don't need to use deconstruct_array() since the array data is just
1689 : * going to look like a C array of values.
1690 : */
1691 7412 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1692 : Anum_pg_constraint_conkey);
1693 7412 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1694 7412 : if (ARR_NDIM(arr) != 1 ||
1695 7412 : ARR_HASNULL(arr) ||
1696 7412 : ARR_ELEMTYPE(arr) != INT2OID)
1697 0 : elog(ERROR, "conkey is not a 1-D smallint array");
1698 7412 : numkeys = ARR_DIMS(arr)[0];
1699 7412 : if (numkeys <= 0 || numkeys > INDEX_MAX_KEYS)
1700 0 : elog(ERROR, "foreign key constraint cannot have %d columns", numkeys);
1701 7412 : memcpy(conkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
1702 7412 : if ((Pointer) arr != DatumGetPointer(adatum))
1703 7412 : pfree(arr); /* free de-toasted copy, if any */
1704 :
1705 7412 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1706 : Anum_pg_constraint_confkey);
1707 7412 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1708 7412 : if (ARR_NDIM(arr) != 1 ||
1709 7412 : ARR_DIMS(arr)[0] != numkeys ||
1710 7412 : ARR_HASNULL(arr) ||
1711 7412 : ARR_ELEMTYPE(arr) != INT2OID)
1712 0 : elog(ERROR, "confkey is not a 1-D smallint array");
1713 7412 : memcpy(confkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
1714 7412 : if ((Pointer) arr != DatumGetPointer(adatum))
1715 7412 : pfree(arr); /* free de-toasted copy, if any */
1716 :
1717 7412 : if (pf_eq_oprs)
1718 : {
1719 7412 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1720 : Anum_pg_constraint_conpfeqop);
1721 7412 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1722 : /* see TryReuseForeignKey if you change the test below */
1723 7412 : if (ARR_NDIM(arr) != 1 ||
1724 7412 : ARR_DIMS(arr)[0] != numkeys ||
1725 7412 : ARR_HASNULL(arr) ||
1726 7412 : ARR_ELEMTYPE(arr) != OIDOID)
1727 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
1728 7412 : memcpy(pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1729 7412 : if ((Pointer) arr != DatumGetPointer(adatum))
1730 7412 : pfree(arr); /* free de-toasted copy, if any */
1731 : }
1732 :
1733 7412 : if (pp_eq_oprs)
1734 : {
1735 4706 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1736 : Anum_pg_constraint_conppeqop);
1737 4706 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1738 4706 : if (ARR_NDIM(arr) != 1 ||
1739 4706 : ARR_DIMS(arr)[0] != numkeys ||
1740 4706 : ARR_HASNULL(arr) ||
1741 4706 : ARR_ELEMTYPE(arr) != OIDOID)
1742 0 : elog(ERROR, "conppeqop is not a 1-D Oid array");
1743 4706 : memcpy(pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1744 4706 : if ((Pointer) arr != DatumGetPointer(adatum))
1745 4706 : pfree(arr); /* free de-toasted copy, if any */
1746 : }
1747 :
1748 7412 : if (ff_eq_oprs)
1749 : {
1750 4706 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1751 : Anum_pg_constraint_conffeqop);
1752 4706 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1753 4706 : if (ARR_NDIM(arr) != 1 ||
1754 4706 : ARR_DIMS(arr)[0] != numkeys ||
1755 4706 : ARR_HASNULL(arr) ||
1756 4706 : ARR_ELEMTYPE(arr) != OIDOID)
1757 0 : elog(ERROR, "conffeqop is not a 1-D Oid array");
1758 4706 : memcpy(ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1759 4706 : if ((Pointer) arr != DatumGetPointer(adatum))
1760 4706 : pfree(arr); /* free de-toasted copy, if any */
1761 : }
1762 :
1763 7412 : if (fk_del_set_cols)
1764 : {
1765 4706 : adatum = SysCacheGetAttr(CONSTROID, tuple,
1766 : Anum_pg_constraint_confdelsetcols, &isNull);
1767 4706 : if (isNull)
1768 : {
1769 4628 : *num_fk_del_set_cols = 0;
1770 : }
1771 : else
1772 : {
1773 : int num_delete_cols;
1774 :
1775 78 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1776 78 : if (ARR_NDIM(arr) != 1 ||
1777 78 : ARR_HASNULL(arr) ||
1778 78 : ARR_ELEMTYPE(arr) != INT2OID)
1779 0 : elog(ERROR, "confdelsetcols is not a 1-D smallint array");
1780 78 : num_delete_cols = ARR_DIMS(arr)[0];
1781 78 : memcpy(fk_del_set_cols, ARR_DATA_PTR(arr), num_delete_cols * sizeof(int16));
1782 78 : if ((Pointer) arr != DatumGetPointer(adatum))
1783 78 : pfree(arr); /* free de-toasted copy, if any */
1784 :
1785 78 : *num_fk_del_set_cols = num_delete_cols;
1786 : }
1787 : }
1788 :
1789 7412 : *numfks = numkeys;
1790 7412 : }
1791 :
1792 : /*
1793 : * FindFKPeriodOpers -
1794 : *
1795 : * Looks up the operator oids used for the PERIOD part of a temporal foreign key.
1796 : * The opclass should be the opclass of that PERIOD element.
1797 : * Everything else is an output: containedbyoperoid is the ContainedBy operator for
1798 : * types matching the PERIOD element.
1799 : * aggedcontainedbyoperoid is also a ContainedBy operator,
1800 : * but one whose rhs is a multirange.
1801 : * That way foreign keys can compare fkattr <@ range_agg(pkattr).
1802 : */
1803 : void
1804 408 : FindFKPeriodOpers(Oid opclass,
1805 : Oid *containedbyoperoid,
1806 : Oid *aggedcontainedbyoperoid)
1807 : {
1808 408 : Oid opfamily = InvalidOid;
1809 408 : Oid opcintype = InvalidOid;
1810 : StrategyNumber strat;
1811 :
1812 : /* Make sure we have a range or multirange. */
1813 408 : if (get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1814 : {
1815 408 : if (opcintype != ANYRANGEOID && opcintype != ANYMULTIRANGEOID)
1816 6 : ereport(ERROR,
1817 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1818 : errmsg("invalid type for PERIOD part of foreign key"),
1819 : errdetail("Only range and multirange are supported."));
1820 :
1821 : }
1822 : else
1823 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
1824 :
1825 : /*
1826 : * Look up the ContainedBy operator whose lhs and rhs are the opclass's
1827 : * type. We use this to optimize RI checks: if the new value includes all
1828 : * of the old value, then we can treat the attribute as if it didn't
1829 : * change, and skip the RI check.
1830 : */
1831 402 : strat = RTContainedByStrategyNumber;
1832 402 : GetOperatorFromWellKnownStrategy(opclass,
1833 : InvalidOid,
1834 : containedbyoperoid,
1835 : &strat);
1836 :
1837 : /*
1838 : * Now look up the ContainedBy operator. Its left arg must be the type of
1839 : * the column (or rather of the opclass). Its right arg must match the
1840 : * return type of the support proc.
1841 : */
1842 402 : strat = RTContainedByStrategyNumber;
1843 402 : GetOperatorFromWellKnownStrategy(opclass,
1844 : ANYMULTIRANGEOID,
1845 : aggedcontainedbyoperoid,
1846 : &strat);
1847 402 : }
1848 :
1849 : /*
1850 : * Determine whether a relation can be proven functionally dependent on
1851 : * a set of grouping columns. If so, return true and add the pg_constraint
1852 : * OIDs of the constraints needed for the proof to the *constraintDeps list.
1853 : *
1854 : * grouping_columns is a list of grouping expressions, in which columns of
1855 : * the rel of interest are Vars with the indicated varno/varlevelsup.
1856 : *
1857 : * Currently we only check to see if the rel has a primary key that is a
1858 : * subset of the grouping_columns. We could also use plain unique constraints
1859 : * if all their columns are known not null, but there's a problem: we need
1860 : * to be able to represent the not-null-ness as part of the constraints added
1861 : * to *constraintDeps. FIXME whenever not-null constraints get represented
1862 : * in pg_constraint.
1863 : */
1864 : bool
1865 206 : check_functional_grouping(Oid relid,
1866 : Index varno, Index varlevelsup,
1867 : List *grouping_columns,
1868 : List **constraintDeps)
1869 : {
1870 : Bitmapset *pkattnos;
1871 : Bitmapset *groupbyattnos;
1872 : Oid constraintOid;
1873 : ListCell *gl;
1874 :
1875 : /* If the rel has no PK, then we can't prove functional dependency */
1876 206 : pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
1877 206 : if (pkattnos == NULL)
1878 42 : return false;
1879 :
1880 : /* Identify all the rel's columns that appear in grouping_columns */
1881 164 : groupbyattnos = NULL;
1882 370 : foreach(gl, grouping_columns)
1883 : {
1884 206 : Var *gvar = (Var *) lfirst(gl);
1885 :
1886 206 : if (IsA(gvar, Var) &&
1887 206 : gvar->varno == varno &&
1888 164 : gvar->varlevelsup == varlevelsup)
1889 164 : groupbyattnos = bms_add_member(groupbyattnos,
1890 164 : gvar->varattno - FirstLowInvalidHeapAttributeNumber);
1891 : }
1892 :
1893 164 : if (bms_is_subset(pkattnos, groupbyattnos))
1894 : {
1895 : /* The PK is a subset of grouping_columns, so we win */
1896 122 : *constraintDeps = lappend_oid(*constraintDeps, constraintOid);
1897 122 : return true;
1898 : }
1899 :
1900 42 : return false;
1901 : }
|