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