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