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/indexing.h"
25 : #include "catalog/objectaccess.h"
26 : #include "catalog/pg_constraint.h"
27 : #include "catalog/pg_operator.h"
28 : #include "catalog/pg_type.h"
29 : #include "commands/defrem.h"
30 : #include "commands/tablecmds.h"
31 : #include "utils/array.h"
32 : #include "utils/builtins.h"
33 : #include "utils/fmgroids.h"
34 : #include "utils/lsyscache.h"
35 : #include "utils/rel.h"
36 : #include "utils/syscache.h"
37 :
38 :
39 : /*
40 : * CreateConstraintEntry
41 : * Create a constraint table entry.
42 : *
43 : * Subsidiary records (such as triggers or indexes to implement the
44 : * constraint) are *not* created here. But we do make dependency links
45 : * from the constraint to the things it depends on.
46 : *
47 : * The new constraint's OID is returned.
48 : */
49 : Oid
50 81454 : CreateConstraintEntry(const char *constraintName,
51 : Oid constraintNamespace,
52 : char constraintType,
53 : bool isDeferrable,
54 : bool isDeferred,
55 : bool isValidated,
56 : Oid parentConstrId,
57 : Oid relId,
58 : const int16 *constraintKey,
59 : int constraintNKeys,
60 : int constraintNTotalKeys,
61 : Oid domainId,
62 : Oid indexRelId,
63 : Oid foreignRelId,
64 : const int16 *foreignKey,
65 : const Oid *pfEqOp,
66 : const Oid *ppEqOp,
67 : const Oid *ffEqOp,
68 : int foreignNKeys,
69 : char foreignUpdateType,
70 : char foreignDeleteType,
71 : const int16 *fkDeleteSetCols,
72 : int numFkDeleteSetCols,
73 : char foreignMatchType,
74 : const Oid *exclOp,
75 : Node *conExpr,
76 : const char *conBin,
77 : bool conIsLocal,
78 : int conInhCount,
79 : bool conNoInherit,
80 : bool is_internal)
81 : {
82 : Relation conDesc;
83 : Oid conOid;
84 : HeapTuple tup;
85 : bool nulls[Natts_pg_constraint];
86 : Datum values[Natts_pg_constraint];
87 : ArrayType *conkeyArray;
88 : ArrayType *confkeyArray;
89 : ArrayType *conpfeqopArray;
90 : ArrayType *conppeqopArray;
91 : ArrayType *conffeqopArray;
92 : ArrayType *conexclopArray;
93 : ArrayType *confdelsetcolsArray;
94 : NameData cname;
95 : int i;
96 : ObjectAddress conobject;
97 : ObjectAddresses *addrs_auto;
98 : ObjectAddresses *addrs_normal;
99 :
100 81454 : conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
101 :
102 : Assert(constraintName);
103 81454 : namestrcpy(&cname, constraintName);
104 :
105 : /*
106 : * Convert C arrays into Postgres arrays.
107 : */
108 81454 : if (constraintNKeys > 0)
109 : {
110 : Datum *conkey;
111 :
112 79690 : conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
113 204032 : for (i = 0; i < constraintNKeys; i++)
114 124342 : conkey[i] = Int16GetDatum(constraintKey[i]);
115 79690 : conkeyArray = construct_array_builtin(conkey, constraintNKeys, INT2OID);
116 : }
117 : else
118 1764 : conkeyArray = NULL;
119 :
120 81454 : if (foreignNKeys > 0)
121 : {
122 : Datum *fkdatums;
123 :
124 3106 : fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
125 6890 : for (i = 0; i < foreignNKeys; i++)
126 3784 : fkdatums[i] = Int16GetDatum(foreignKey[i]);
127 3106 : confkeyArray = construct_array_builtin(fkdatums, foreignNKeys, INT2OID);
128 6890 : for (i = 0; i < foreignNKeys; i++)
129 3784 : fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
130 3106 : conpfeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
131 6890 : for (i = 0; i < foreignNKeys; i++)
132 3784 : fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
133 3106 : conppeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
134 6890 : for (i = 0; i < foreignNKeys; i++)
135 3784 : fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
136 3106 : conffeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
137 :
138 3106 : if (numFkDeleteSetCols > 0)
139 : {
140 120 : for (i = 0; i < numFkDeleteSetCols; i++)
141 60 : fkdatums[i] = Int16GetDatum(fkDeleteSetCols[i]);
142 60 : confdelsetcolsArray = construct_array_builtin(fkdatums, numFkDeleteSetCols, INT2OID);
143 : }
144 : else
145 3046 : confdelsetcolsArray = NULL;
146 : }
147 : else
148 : {
149 78348 : confkeyArray = NULL;
150 78348 : conpfeqopArray = NULL;
151 78348 : conppeqopArray = NULL;
152 78348 : conffeqopArray = NULL;
153 78348 : confdelsetcolsArray = NULL;
154 : }
155 :
156 81454 : if (exclOp != NULL)
157 : {
158 : Datum *opdatums;
159 :
160 128 : opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
161 286 : for (i = 0; i < constraintNKeys; i++)
162 158 : opdatums[i] = ObjectIdGetDatum(exclOp[i]);
163 128 : conexclopArray = construct_array_builtin(opdatums, constraintNKeys, OIDOID);
164 : }
165 : else
166 81326 : conexclopArray = NULL;
167 :
168 : /* initialize nulls and values */
169 2199258 : for (i = 0; i < Natts_pg_constraint; i++)
170 : {
171 2117804 : nulls[i] = false;
172 2117804 : values[i] = (Datum) NULL;
173 : }
174 :
175 81454 : conOid = GetNewOidWithIndex(conDesc, ConstraintOidIndexId,
176 : Anum_pg_constraint_oid);
177 81454 : values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(conOid);
178 81454 : values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
179 81454 : values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
180 81454 : values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
181 81454 : values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
182 81454 : values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
183 81454 : values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
184 81454 : values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
185 81454 : values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
186 81454 : values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
187 81454 : values[Anum_pg_constraint_conparentid - 1] = ObjectIdGetDatum(parentConstrId);
188 81454 : values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
189 81454 : values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
190 81454 : values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
191 81454 : values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
192 81454 : values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
193 81454 : values[Anum_pg_constraint_coninhcount - 1] = Int16GetDatum(conInhCount);
194 81454 : values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
195 :
196 81454 : if (conkeyArray)
197 79690 : values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
198 : else
199 1764 : nulls[Anum_pg_constraint_conkey - 1] = true;
200 :
201 81454 : if (confkeyArray)
202 3106 : values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
203 : else
204 78348 : nulls[Anum_pg_constraint_confkey - 1] = true;
205 :
206 81454 : if (conpfeqopArray)
207 3106 : values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
208 : else
209 78348 : nulls[Anum_pg_constraint_conpfeqop - 1] = true;
210 :
211 81454 : if (conppeqopArray)
212 3106 : values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
213 : else
214 78348 : nulls[Anum_pg_constraint_conppeqop - 1] = true;
215 :
216 81454 : if (conffeqopArray)
217 3106 : values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
218 : else
219 78348 : nulls[Anum_pg_constraint_conffeqop - 1] = true;
220 :
221 81454 : if (confdelsetcolsArray)
222 60 : values[Anum_pg_constraint_confdelsetcols - 1] = PointerGetDatum(confdelsetcolsArray);
223 : else
224 81394 : nulls[Anum_pg_constraint_confdelsetcols - 1] = true;
225 :
226 81454 : if (conexclopArray)
227 128 : values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
228 : else
229 81326 : nulls[Anum_pg_constraint_conexclop - 1] = true;
230 :
231 81454 : if (conBin)
232 3544 : values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
233 : else
234 77910 : nulls[Anum_pg_constraint_conbin - 1] = true;
235 :
236 81454 : tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
237 :
238 81454 : CatalogTupleInsert(conDesc, tup);
239 :
240 81454 : ObjectAddressSet(conobject, ConstraintRelationId, conOid);
241 :
242 81454 : table_close(conDesc, RowExclusiveLock);
243 :
244 : /* Handle set of auto dependencies */
245 81454 : addrs_auto = new_object_addresses();
246 :
247 81454 : if (OidIsValid(relId))
248 : {
249 : /*
250 : * Register auto dependency from constraint to owning relation, or to
251 : * specific column(s) if any are mentioned.
252 : */
253 : ObjectAddress relobject;
254 :
255 79854 : if (constraintNTotalKeys > 0)
256 : {
257 204340 : for (i = 0; i < constraintNTotalKeys; i++)
258 : {
259 124650 : ObjectAddressSubSet(relobject, RelationRelationId, relId,
260 : constraintKey[i]);
261 124650 : add_exact_object_address(&relobject, addrs_auto);
262 : }
263 : }
264 : else
265 : {
266 164 : ObjectAddressSet(relobject, RelationRelationId, relId);
267 164 : add_exact_object_address(&relobject, addrs_auto);
268 : }
269 : }
270 :
271 81454 : if (OidIsValid(domainId))
272 : {
273 : /*
274 : * Register auto dependency from constraint to owning domain
275 : */
276 : ObjectAddress domobject;
277 :
278 1600 : ObjectAddressSet(domobject, TypeRelationId, domainId);
279 1600 : add_exact_object_address(&domobject, addrs_auto);
280 : }
281 :
282 81454 : record_object_address_dependencies(&conobject, addrs_auto,
283 : DEPENDENCY_AUTO);
284 81454 : free_object_addresses(addrs_auto);
285 :
286 : /* Handle set of normal dependencies */
287 81454 : addrs_normal = new_object_addresses();
288 :
289 81454 : if (OidIsValid(foreignRelId))
290 : {
291 : /*
292 : * Register normal dependency from constraint to foreign relation, or
293 : * to specific column(s) if any are mentioned.
294 : */
295 : ObjectAddress relobject;
296 :
297 3106 : if (foreignNKeys > 0)
298 : {
299 6890 : for (i = 0; i < foreignNKeys; i++)
300 : {
301 3784 : ObjectAddressSubSet(relobject, RelationRelationId,
302 : foreignRelId, foreignKey[i]);
303 3784 : add_exact_object_address(&relobject, addrs_normal);
304 : }
305 : }
306 : else
307 : {
308 0 : ObjectAddressSet(relobject, RelationRelationId, foreignRelId);
309 0 : add_exact_object_address(&relobject, addrs_normal);
310 : }
311 : }
312 :
313 81454 : if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN)
314 : {
315 : /*
316 : * Register normal dependency on the unique index that supports a
317 : * foreign-key constraint. (Note: for indexes associated with unique
318 : * or primary-key constraints, the dependency runs the other way, and
319 : * is not made here.)
320 : */
321 : ObjectAddress relobject;
322 :
323 3106 : ObjectAddressSet(relobject, RelationRelationId, indexRelId);
324 3106 : add_exact_object_address(&relobject, addrs_normal);
325 : }
326 :
327 81454 : if (foreignNKeys > 0)
328 : {
329 : /*
330 : * Register normal dependencies on the equality operators that support
331 : * a foreign-key constraint. If the PK and FK types are the same then
332 : * all three operators for a column are the same; otherwise they are
333 : * different.
334 : */
335 : ObjectAddress oprobject;
336 :
337 3106 : oprobject.classId = OperatorRelationId;
338 3106 : oprobject.objectSubId = 0;
339 :
340 6890 : for (i = 0; i < foreignNKeys; i++)
341 : {
342 3784 : oprobject.objectId = pfEqOp[i];
343 3784 : add_exact_object_address(&oprobject, addrs_normal);
344 3784 : if (ppEqOp[i] != pfEqOp[i])
345 : {
346 56 : oprobject.objectId = ppEqOp[i];
347 56 : add_exact_object_address(&oprobject, addrs_normal);
348 : }
349 3784 : if (ffEqOp[i] != pfEqOp[i])
350 : {
351 56 : oprobject.objectId = ffEqOp[i];
352 56 : add_exact_object_address(&oprobject, addrs_normal);
353 : }
354 : }
355 : }
356 :
357 81454 : record_object_address_dependencies(&conobject, addrs_normal,
358 : DEPENDENCY_NORMAL);
359 81454 : free_object_addresses(addrs_normal);
360 :
361 : /*
362 : * We don't bother to register dependencies on the exclusion operators of
363 : * an exclusion constraint. We assume they are members of the opclass
364 : * supporting the index, so there's an indirect dependency via that. (This
365 : * would be pretty dicey for cross-type operators, but exclusion operators
366 : * can never be cross-type.)
367 : */
368 :
369 81454 : if (conExpr != NULL)
370 : {
371 : /*
372 : * Register dependencies from constraint to objects mentioned in CHECK
373 : * expression.
374 : */
375 3544 : recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
376 : DEPENDENCY_NORMAL,
377 : DEPENDENCY_NORMAL, false);
378 : }
379 :
380 : /* Post creation hook for new constraint */
381 81454 : InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
382 : is_internal);
383 :
384 81454 : return conOid;
385 : }
386 :
387 : /*
388 : * Test whether given name is currently used as a constraint name
389 : * for the given object (relation or domain).
390 : *
391 : * This is used to decide whether to accept a user-specified constraint name.
392 : * It is deliberately not the same test as ChooseConstraintName uses to decide
393 : * whether an auto-generated name is OK: here, we will allow it unless there
394 : * is an identical constraint name in use *on the same object*.
395 : *
396 : * NB: Caller should hold exclusive lock on the given object, else
397 : * this test can be fooled by concurrent additions.
398 : */
399 : bool
400 13760 : ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
401 : const char *conname)
402 : {
403 : bool found;
404 : Relation conDesc;
405 : SysScanDesc conscan;
406 : ScanKeyData skey[3];
407 :
408 13760 : conDesc = table_open(ConstraintRelationId, AccessShareLock);
409 :
410 13760 : ScanKeyInit(&skey[0],
411 : Anum_pg_constraint_conrelid,
412 : BTEqualStrategyNumber, F_OIDEQ,
413 : ObjectIdGetDatum((conCat == CONSTRAINT_RELATION)
414 : ? objId : InvalidOid));
415 13760 : ScanKeyInit(&skey[1],
416 : Anum_pg_constraint_contypid,
417 : BTEqualStrategyNumber, F_OIDEQ,
418 : ObjectIdGetDatum((conCat == CONSTRAINT_DOMAIN)
419 : ? objId : InvalidOid));
420 13760 : ScanKeyInit(&skey[2],
421 : Anum_pg_constraint_conname,
422 : BTEqualStrategyNumber, F_NAMEEQ,
423 : CStringGetDatum(conname));
424 :
425 13760 : conscan = systable_beginscan(conDesc, ConstraintRelidTypidNameIndexId,
426 : true, NULL, 3, skey);
427 :
428 : /* There can be at most one matching row */
429 13760 : found = (HeapTupleIsValid(systable_getnext(conscan)));
430 :
431 13760 : systable_endscan(conscan);
432 13760 : table_close(conDesc, AccessShareLock);
433 :
434 13760 : return found;
435 : }
436 :
437 : /*
438 : * Does any constraint of the given name exist in the given namespace?
439 : *
440 : * This is used for code that wants to match ChooseConstraintName's rule
441 : * that we should avoid autogenerating duplicate constraint names within a
442 : * namespace.
443 : */
444 : bool
445 7696 : ConstraintNameExists(const char *conname, Oid namespaceid)
446 : {
447 : bool found;
448 : Relation conDesc;
449 : SysScanDesc conscan;
450 : ScanKeyData skey[2];
451 :
452 7696 : conDesc = table_open(ConstraintRelationId, AccessShareLock);
453 :
454 7696 : ScanKeyInit(&skey[0],
455 : Anum_pg_constraint_conname,
456 : BTEqualStrategyNumber, F_NAMEEQ,
457 : CStringGetDatum(conname));
458 :
459 7696 : ScanKeyInit(&skey[1],
460 : Anum_pg_constraint_connamespace,
461 : BTEqualStrategyNumber, F_OIDEQ,
462 : ObjectIdGetDatum(namespaceid));
463 :
464 7696 : conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
465 : NULL, 2, skey);
466 :
467 7696 : found = (HeapTupleIsValid(systable_getnext(conscan)));
468 :
469 7696 : systable_endscan(conscan);
470 7696 : table_close(conDesc, AccessShareLock);
471 :
472 7696 : return found;
473 : }
474 :
475 : /*
476 : * Select a nonconflicting name for a new constraint.
477 : *
478 : * The objective here is to choose a name that is unique within the
479 : * specified namespace. Postgres does not require this, but the SQL
480 : * spec does, and some apps depend on it. Therefore we avoid choosing
481 : * default names that so conflict.
482 : *
483 : * name1, name2, and label are used the same way as for makeObjectName(),
484 : * except that the label can't be NULL; digits will be appended to the label
485 : * if needed to create a name that is unique within the specified namespace.
486 : *
487 : * 'others' can be a list of string names already chosen within the current
488 : * command (but not yet reflected into the catalogs); we will not choose
489 : * a duplicate of one of these either.
490 : *
491 : * Note: it is theoretically possible to get a collision anyway, if someone
492 : * else chooses the same name concurrently. This is fairly unlikely to be
493 : * a problem in practice, especially if one is holding an exclusive lock on
494 : * the relation identified by name1.
495 : *
496 : * Returns a palloc'd string.
497 : */
498 : char *
499 2794 : ChooseConstraintName(const char *name1, const char *name2,
500 : const char *label, Oid namespaceid,
501 : List *others)
502 : {
503 2794 : int pass = 0;
504 2794 : char *conname = NULL;
505 : char modlabel[NAMEDATALEN];
506 : Relation conDesc;
507 : SysScanDesc conscan;
508 : ScanKeyData skey[2];
509 : bool found;
510 : ListCell *l;
511 :
512 2794 : conDesc = table_open(ConstraintRelationId, AccessShareLock);
513 :
514 : /* try the unmodified label first */
515 2794 : strlcpy(modlabel, label, sizeof(modlabel));
516 :
517 : for (;;)
518 : {
519 4320 : conname = makeObjectName(name1, name2, modlabel);
520 :
521 4320 : found = false;
522 :
523 4390 : foreach(l, others)
524 : {
525 88 : if (strcmp((char *) lfirst(l), conname) == 0)
526 : {
527 18 : found = true;
528 18 : break;
529 : }
530 : }
531 :
532 4320 : if (!found)
533 : {
534 4302 : ScanKeyInit(&skey[0],
535 : Anum_pg_constraint_conname,
536 : BTEqualStrategyNumber, F_NAMEEQ,
537 : CStringGetDatum(conname));
538 :
539 4302 : ScanKeyInit(&skey[1],
540 : Anum_pg_constraint_connamespace,
541 : BTEqualStrategyNumber, F_OIDEQ,
542 : ObjectIdGetDatum(namespaceid));
543 :
544 4302 : conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
545 : NULL, 2, skey);
546 :
547 4302 : found = (HeapTupleIsValid(systable_getnext(conscan)));
548 :
549 4302 : systable_endscan(conscan);
550 : }
551 :
552 4320 : if (!found)
553 2794 : break;
554 :
555 : /* found a conflict, so try a new name component */
556 1526 : pfree(conname);
557 1526 : snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
558 : }
559 :
560 2794 : table_close(conDesc, AccessShareLock);
561 :
562 2794 : return conname;
563 : }
564 :
565 : /*
566 : * Delete a single constraint record.
567 : */
568 : void
569 11246 : RemoveConstraintById(Oid conId)
570 : {
571 : Relation conDesc;
572 : HeapTuple tup;
573 : Form_pg_constraint con;
574 :
575 11246 : conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
576 :
577 11246 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
578 11246 : if (!HeapTupleIsValid(tup)) /* should not happen */
579 0 : elog(ERROR, "cache lookup failed for constraint %u", conId);
580 11246 : con = (Form_pg_constraint) GETSTRUCT(tup);
581 :
582 : /*
583 : * Special processing depending on what the constraint is for.
584 : */
585 11246 : if (OidIsValid(con->conrelid))
586 : {
587 : Relation rel;
588 :
589 : /*
590 : * If the constraint is for a relation, open and exclusive-lock the
591 : * relation it's for.
592 : */
593 11020 : rel = table_open(con->conrelid, AccessExclusiveLock);
594 :
595 : /*
596 : * We need to update the relchecks count if it is a check constraint
597 : * being dropped. This update will force backends to rebuild relcache
598 : * entries when we commit.
599 : */
600 11018 : if (con->contype == CONSTRAINT_CHECK)
601 : {
602 : Relation pgrel;
603 : HeapTuple relTup;
604 : Form_pg_class classForm;
605 :
606 1516 : pgrel = table_open(RelationRelationId, RowExclusiveLock);
607 1516 : relTup = SearchSysCacheCopy1(RELOID,
608 : ObjectIdGetDatum(con->conrelid));
609 1516 : if (!HeapTupleIsValid(relTup))
610 0 : elog(ERROR, "cache lookup failed for relation %u",
611 : con->conrelid);
612 1516 : classForm = (Form_pg_class) GETSTRUCT(relTup);
613 :
614 1516 : if (classForm->relchecks == 0) /* should not happen */
615 0 : elog(ERROR, "relation \"%s\" has relchecks = 0",
616 : RelationGetRelationName(rel));
617 1516 : classForm->relchecks--;
618 :
619 1516 : CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
620 :
621 1516 : heap_freetuple(relTup);
622 :
623 1516 : table_close(pgrel, RowExclusiveLock);
624 : }
625 :
626 : /* Keep lock on constraint's rel until end of xact */
627 11018 : table_close(rel, NoLock);
628 : }
629 226 : else if (OidIsValid(con->contypid))
630 : {
631 : /*
632 : * XXX for now, do nothing special when dropping a domain constraint
633 : *
634 : * Probably there should be some form of locking on the domain type,
635 : * but we have no such concept at the moment.
636 : */
637 : }
638 : else
639 0 : elog(ERROR, "constraint %u is not of a known type", conId);
640 :
641 : /* Fry the constraint itself */
642 11244 : CatalogTupleDelete(conDesc, &tup->t_self);
643 :
644 : /* Clean up */
645 11244 : ReleaseSysCache(tup);
646 11244 : table_close(conDesc, RowExclusiveLock);
647 11244 : }
648 :
649 : /*
650 : * RenameConstraintById
651 : * Rename a constraint.
652 : *
653 : * Note: this isn't intended to be a user-exposed function; it doesn't check
654 : * permissions etc. Currently this is only invoked when renaming an index
655 : * that is associated with a constraint, but it's made a little more general
656 : * than that with the expectation of someday having ALTER TABLE RENAME
657 : * CONSTRAINT.
658 : */
659 : void
660 90 : RenameConstraintById(Oid conId, const char *newname)
661 : {
662 : Relation conDesc;
663 : HeapTuple tuple;
664 : Form_pg_constraint con;
665 :
666 90 : conDesc = table_open(ConstraintRelationId, RowExclusiveLock);
667 :
668 90 : tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
669 90 : if (!HeapTupleIsValid(tuple))
670 0 : elog(ERROR, "cache lookup failed for constraint %u", conId);
671 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
672 :
673 : /*
674 : * For user-friendliness, check whether the name is already in use.
675 : */
676 174 : if (OidIsValid(con->conrelid) &&
677 84 : ConstraintNameIsUsed(CONSTRAINT_RELATION,
678 : con->conrelid,
679 : newname))
680 0 : ereport(ERROR,
681 : (errcode(ERRCODE_DUPLICATE_OBJECT),
682 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
683 : newname, get_rel_name(con->conrelid))));
684 96 : if (OidIsValid(con->contypid) &&
685 6 : ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
686 : con->contypid,
687 : newname))
688 0 : ereport(ERROR,
689 : (errcode(ERRCODE_DUPLICATE_OBJECT),
690 : errmsg("constraint \"%s\" for domain %s already exists",
691 : newname, format_type_be(con->contypid))));
692 :
693 : /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
694 90 : namestrcpy(&(con->conname), newname);
695 :
696 90 : CatalogTupleUpdate(conDesc, &tuple->t_self, tuple);
697 :
698 90 : InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
699 :
700 90 : heap_freetuple(tuple);
701 90 : table_close(conDesc, RowExclusiveLock);
702 90 : }
703 :
704 : /*
705 : * AlterConstraintNamespaces
706 : * Find any constraints belonging to the specified object,
707 : * and move them to the specified new namespace.
708 : *
709 : * isType indicates whether the owning object is a type or a relation.
710 : */
711 : void
712 88 : AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
713 : Oid newNspId, bool isType, ObjectAddresses *objsMoved)
714 : {
715 : Relation conRel;
716 : ScanKeyData key[2];
717 : SysScanDesc scan;
718 : HeapTuple tup;
719 :
720 88 : conRel = table_open(ConstraintRelationId, RowExclusiveLock);
721 :
722 88 : ScanKeyInit(&key[0],
723 : Anum_pg_constraint_conrelid,
724 : BTEqualStrategyNumber, F_OIDEQ,
725 : ObjectIdGetDatum(isType ? InvalidOid : ownerId));
726 88 : ScanKeyInit(&key[1],
727 : Anum_pg_constraint_contypid,
728 : BTEqualStrategyNumber, F_OIDEQ,
729 : ObjectIdGetDatum(isType ? ownerId : InvalidOid));
730 :
731 88 : scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
732 : NULL, 2, key);
733 :
734 156 : while (HeapTupleIsValid((tup = systable_getnext(scan))))
735 : {
736 68 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
737 : ObjectAddress thisobj;
738 :
739 68 : ObjectAddressSet(thisobj, ConstraintRelationId, conform->oid);
740 :
741 68 : if (object_address_present(&thisobj, objsMoved))
742 0 : continue;
743 :
744 : /* Don't update if the object is already part of the namespace */
745 68 : if (conform->connamespace == oldNspId && oldNspId != newNspId)
746 : {
747 50 : tup = heap_copytuple(tup);
748 50 : conform = (Form_pg_constraint) GETSTRUCT(tup);
749 :
750 50 : conform->connamespace = newNspId;
751 :
752 50 : CatalogTupleUpdate(conRel, &tup->t_self, tup);
753 :
754 : /*
755 : * Note: currently, the constraint will not have its own
756 : * dependency on the namespace, so we don't need to do
757 : * changeDependencyFor().
758 : */
759 : }
760 :
761 68 : InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
762 :
763 68 : add_exact_object_address(&thisobj, objsMoved);
764 : }
765 :
766 88 : systable_endscan(scan);
767 :
768 88 : table_close(conRel, RowExclusiveLock);
769 88 : }
770 :
771 : /*
772 : * ConstraintSetParentConstraint
773 : * Set a partition's constraint as child of its parent constraint,
774 : * or remove the linkage if parentConstrId is InvalidOid.
775 : *
776 : * This updates the constraint's pg_constraint row to show it as inherited, and
777 : * adds PARTITION dependencies to prevent the constraint from being deleted
778 : * on its own. Alternatively, reverse that.
779 : */
780 : void
781 438 : ConstraintSetParentConstraint(Oid childConstrId,
782 : Oid parentConstrId,
783 : Oid childTableId)
784 : {
785 : Relation constrRel;
786 : Form_pg_constraint constrForm;
787 : HeapTuple tuple,
788 : newtup;
789 : ObjectAddress depender;
790 : ObjectAddress referenced;
791 :
792 438 : constrRel = table_open(ConstraintRelationId, RowExclusiveLock);
793 438 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(childConstrId));
794 438 : if (!HeapTupleIsValid(tuple))
795 0 : elog(ERROR, "cache lookup failed for constraint %u", childConstrId);
796 438 : newtup = heap_copytuple(tuple);
797 438 : constrForm = (Form_pg_constraint) GETSTRUCT(newtup);
798 438 : if (OidIsValid(parentConstrId))
799 : {
800 : /* don't allow setting parent for a constraint that already has one */
801 : Assert(constrForm->coninhcount == 0);
802 280 : if (constrForm->conparentid != InvalidOid)
803 0 : elog(ERROR, "constraint %u already has a parent constraint",
804 : childConstrId);
805 :
806 280 : constrForm->conislocal = false;
807 280 : constrForm->coninhcount++;
808 280 : if (constrForm->coninhcount < 0)
809 0 : ereport(ERROR,
810 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
811 : errmsg("too many inheritance parents"));
812 280 : constrForm->conparentid = parentConstrId;
813 :
814 280 : CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
815 :
816 280 : ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
817 :
818 280 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
819 280 : recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
820 :
821 280 : ObjectAddressSet(referenced, RelationRelationId, childTableId);
822 280 : recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
823 : }
824 : else
825 : {
826 158 : constrForm->coninhcount--;
827 158 : constrForm->conislocal = true;
828 158 : constrForm->conparentid = InvalidOid;
829 :
830 : /* Make sure there's no further inheritance. */
831 : Assert(constrForm->coninhcount == 0);
832 :
833 158 : CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
834 :
835 158 : deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
836 : ConstraintRelationId,
837 : DEPENDENCY_PARTITION_PRI);
838 158 : deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
839 : RelationRelationId,
840 : DEPENDENCY_PARTITION_SEC);
841 : }
842 :
843 438 : ReleaseSysCache(tuple);
844 438 : table_close(constrRel, RowExclusiveLock);
845 438 : }
846 :
847 :
848 : /*
849 : * get_relation_constraint_oid
850 : * Find a constraint on the specified relation with the specified name.
851 : * Returns constraint's OID.
852 : */
853 : Oid
854 320 : get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
855 : {
856 : Relation pg_constraint;
857 : HeapTuple tuple;
858 : SysScanDesc scan;
859 : ScanKeyData skey[3];
860 320 : Oid conOid = InvalidOid;
861 :
862 320 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
863 :
864 320 : ScanKeyInit(&skey[0],
865 : Anum_pg_constraint_conrelid,
866 : BTEqualStrategyNumber, F_OIDEQ,
867 : ObjectIdGetDatum(relid));
868 320 : ScanKeyInit(&skey[1],
869 : Anum_pg_constraint_contypid,
870 : BTEqualStrategyNumber, F_OIDEQ,
871 : ObjectIdGetDatum(InvalidOid));
872 320 : ScanKeyInit(&skey[2],
873 : Anum_pg_constraint_conname,
874 : BTEqualStrategyNumber, F_NAMEEQ,
875 : CStringGetDatum(conname));
876 :
877 320 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
878 : NULL, 3, skey);
879 :
880 : /* There can be at most one matching row */
881 320 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
882 308 : conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
883 :
884 320 : systable_endscan(scan);
885 :
886 : /* If no such constraint exists, complain */
887 320 : if (!OidIsValid(conOid) && !missing_ok)
888 12 : ereport(ERROR,
889 : (errcode(ERRCODE_UNDEFINED_OBJECT),
890 : errmsg("constraint \"%s\" for table \"%s\" does not exist",
891 : conname, get_rel_name(relid))));
892 :
893 308 : table_close(pg_constraint, AccessShareLock);
894 :
895 308 : return conOid;
896 : }
897 :
898 : /*
899 : * get_relation_constraint_attnos
900 : * Find a constraint on the specified relation with the specified name
901 : * and return the constrained columns.
902 : *
903 : * Returns a Bitmapset of the column attnos of the constrained columns, with
904 : * attnos being offset by FirstLowInvalidHeapAttributeNumber so that system
905 : * columns can be represented.
906 : *
907 : * *constraintOid is set to the OID of the constraint, or InvalidOid on
908 : * failure.
909 : */
910 : Bitmapset *
911 48 : get_relation_constraint_attnos(Oid relid, const char *conname,
912 : bool missing_ok, Oid *constraintOid)
913 : {
914 48 : Bitmapset *conattnos = NULL;
915 : Relation pg_constraint;
916 : HeapTuple tuple;
917 : SysScanDesc scan;
918 : ScanKeyData skey[3];
919 :
920 : /* Set *constraintOid, to avoid complaints about uninitialized vars */
921 48 : *constraintOid = InvalidOid;
922 :
923 48 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
924 :
925 48 : ScanKeyInit(&skey[0],
926 : Anum_pg_constraint_conrelid,
927 : BTEqualStrategyNumber, F_OIDEQ,
928 : ObjectIdGetDatum(relid));
929 48 : ScanKeyInit(&skey[1],
930 : Anum_pg_constraint_contypid,
931 : BTEqualStrategyNumber, F_OIDEQ,
932 : ObjectIdGetDatum(InvalidOid));
933 48 : ScanKeyInit(&skey[2],
934 : Anum_pg_constraint_conname,
935 : BTEqualStrategyNumber, F_NAMEEQ,
936 : CStringGetDatum(conname));
937 :
938 48 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
939 : NULL, 3, skey);
940 :
941 : /* There can be at most one matching row */
942 48 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
943 : {
944 : Datum adatum;
945 : bool isNull;
946 :
947 48 : *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
948 :
949 : /* Extract the conkey array, ie, attnums of constrained columns */
950 48 : adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
951 : RelationGetDescr(pg_constraint), &isNull);
952 48 : if (!isNull)
953 : {
954 : ArrayType *arr;
955 : int numcols;
956 : int16 *attnums;
957 : int i;
958 :
959 48 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
960 48 : numcols = ARR_DIMS(arr)[0];
961 48 : if (ARR_NDIM(arr) != 1 ||
962 48 : numcols < 0 ||
963 48 : ARR_HASNULL(arr) ||
964 48 : ARR_ELEMTYPE(arr) != INT2OID)
965 0 : elog(ERROR, "conkey is not a 1-D smallint array");
966 48 : attnums = (int16 *) ARR_DATA_PTR(arr);
967 :
968 : /* Construct the result value */
969 108 : for (i = 0; i < numcols; i++)
970 : {
971 60 : conattnos = bms_add_member(conattnos,
972 60 : attnums[i] - FirstLowInvalidHeapAttributeNumber);
973 : }
974 : }
975 : }
976 :
977 48 : systable_endscan(scan);
978 :
979 : /* If no such constraint exists, complain */
980 48 : if (!OidIsValid(*constraintOid) && !missing_ok)
981 0 : ereport(ERROR,
982 : (errcode(ERRCODE_UNDEFINED_OBJECT),
983 : errmsg("constraint \"%s\" for table \"%s\" does not exist",
984 : conname, get_rel_name(relid))));
985 :
986 48 : table_close(pg_constraint, AccessShareLock);
987 :
988 48 : return conattnos;
989 : }
990 :
991 : /*
992 : * Return the OID of the constraint enforced by the given index in the
993 : * given relation; or InvalidOid if no such index is catalogued.
994 : *
995 : * Much like get_constraint_index, this function is concerned only with the
996 : * one constraint that "owns" the given index. Therefore, constraints of
997 : * types other than unique, primary-key, and exclusion are ignored.
998 : */
999 : Oid
1000 1010 : get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
1001 : {
1002 : Relation pg_constraint;
1003 : SysScanDesc scan;
1004 : ScanKeyData key;
1005 : HeapTuple tuple;
1006 1010 : Oid constraintId = InvalidOid;
1007 :
1008 1010 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1009 :
1010 1010 : ScanKeyInit(&key,
1011 : Anum_pg_constraint_conrelid,
1012 : BTEqualStrategyNumber,
1013 : F_OIDEQ,
1014 : ObjectIdGetDatum(relationId));
1015 1010 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId,
1016 : true, NULL, 1, &key);
1017 1082 : while ((tuple = systable_getnext(scan)) != NULL)
1018 : {
1019 : Form_pg_constraint constrForm;
1020 :
1021 654 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
1022 :
1023 : /* See above */
1024 654 : if (constrForm->contype != CONSTRAINT_PRIMARY &&
1025 138 : constrForm->contype != CONSTRAINT_UNIQUE &&
1026 66 : constrForm->contype != CONSTRAINT_EXCLUSION)
1027 66 : continue;
1028 :
1029 588 : if (constrForm->conindid == indexId)
1030 : {
1031 582 : constraintId = constrForm->oid;
1032 582 : break;
1033 : }
1034 : }
1035 1010 : systable_endscan(scan);
1036 :
1037 1010 : table_close(pg_constraint, AccessShareLock);
1038 1010 : return constraintId;
1039 : }
1040 :
1041 : /*
1042 : * get_domain_constraint_oid
1043 : * Find a constraint on the specified domain with the specified name.
1044 : * Returns constraint's OID.
1045 : */
1046 : Oid
1047 56 : get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
1048 : {
1049 : Relation pg_constraint;
1050 : HeapTuple tuple;
1051 : SysScanDesc scan;
1052 : ScanKeyData skey[3];
1053 56 : Oid conOid = InvalidOid;
1054 :
1055 56 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1056 :
1057 56 : ScanKeyInit(&skey[0],
1058 : Anum_pg_constraint_conrelid,
1059 : BTEqualStrategyNumber, F_OIDEQ,
1060 : ObjectIdGetDatum(InvalidOid));
1061 56 : ScanKeyInit(&skey[1],
1062 : Anum_pg_constraint_contypid,
1063 : BTEqualStrategyNumber, F_OIDEQ,
1064 : ObjectIdGetDatum(typid));
1065 56 : ScanKeyInit(&skey[2],
1066 : Anum_pg_constraint_conname,
1067 : BTEqualStrategyNumber, F_NAMEEQ,
1068 : CStringGetDatum(conname));
1069 :
1070 56 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1071 : NULL, 3, skey);
1072 :
1073 : /* There can be at most one matching row */
1074 56 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
1075 50 : conOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1076 :
1077 56 : systable_endscan(scan);
1078 :
1079 : /* If no such constraint exists, complain */
1080 56 : if (!OidIsValid(conOid) && !missing_ok)
1081 6 : ereport(ERROR,
1082 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1083 : errmsg("constraint \"%s\" for domain %s does not exist",
1084 : conname, format_type_be(typid))));
1085 :
1086 50 : table_close(pg_constraint, AccessShareLock);
1087 :
1088 50 : return conOid;
1089 : }
1090 :
1091 : /*
1092 : * get_primary_key_attnos
1093 : * Identify the columns in a relation's primary key, if any.
1094 : *
1095 : * Returns a Bitmapset of the column attnos of the primary key's columns,
1096 : * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
1097 : * system columns can be represented.
1098 : *
1099 : * If there is no primary key, return NULL. We also return NULL if the pkey
1100 : * constraint is deferrable and deferrableOk is false.
1101 : *
1102 : * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
1103 : * on failure.
1104 : */
1105 : Bitmapset *
1106 884 : get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
1107 : {
1108 884 : Bitmapset *pkattnos = NULL;
1109 : Relation pg_constraint;
1110 : HeapTuple tuple;
1111 : SysScanDesc scan;
1112 : ScanKeyData skey[1];
1113 :
1114 : /* Set *constraintOid, to avoid complaints about uninitialized vars */
1115 884 : *constraintOid = InvalidOid;
1116 :
1117 : /* Scan pg_constraint for constraints of the target rel */
1118 884 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
1119 :
1120 884 : ScanKeyInit(&skey[0],
1121 : Anum_pg_constraint_conrelid,
1122 : BTEqualStrategyNumber, F_OIDEQ,
1123 : ObjectIdGetDatum(relid));
1124 :
1125 884 : scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true,
1126 : NULL, 1, skey);
1127 :
1128 998 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1129 : {
1130 432 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
1131 : Datum adatum;
1132 : bool isNull;
1133 : ArrayType *arr;
1134 : int16 *attnums;
1135 : int numkeys;
1136 : int i;
1137 :
1138 : /* Skip constraints that are not PRIMARY KEYs */
1139 432 : if (con->contype != CONSTRAINT_PRIMARY)
1140 114 : continue;
1141 :
1142 : /*
1143 : * If the primary key is deferrable, but we've been instructed to
1144 : * ignore deferrable constraints, then we might as well give up
1145 : * searching, since there can only be a single primary key on a table.
1146 : */
1147 318 : if (con->condeferrable && !deferrableOk)
1148 318 : break;
1149 :
1150 : /* Extract the conkey array, ie, attnums of PK's columns */
1151 312 : adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
1152 : RelationGetDescr(pg_constraint), &isNull);
1153 312 : if (isNull)
1154 0 : elog(ERROR, "null conkey for constraint %u",
1155 : ((Form_pg_constraint) GETSTRUCT(tuple))->oid);
1156 312 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1157 312 : numkeys = ARR_DIMS(arr)[0];
1158 312 : if (ARR_NDIM(arr) != 1 ||
1159 312 : numkeys < 0 ||
1160 312 : ARR_HASNULL(arr) ||
1161 312 : ARR_ELEMTYPE(arr) != INT2OID)
1162 0 : elog(ERROR, "conkey is not a 1-D smallint array");
1163 312 : attnums = (int16 *) ARR_DATA_PTR(arr);
1164 :
1165 : /* Construct the result value */
1166 696 : for (i = 0; i < numkeys; i++)
1167 : {
1168 384 : pkattnos = bms_add_member(pkattnos,
1169 384 : attnums[i] - FirstLowInvalidHeapAttributeNumber);
1170 : }
1171 312 : *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid;
1172 :
1173 : /* No need to search further */
1174 312 : break;
1175 : }
1176 :
1177 884 : systable_endscan(scan);
1178 :
1179 884 : table_close(pg_constraint, AccessShareLock);
1180 :
1181 884 : return pkattnos;
1182 : }
1183 :
1184 : /*
1185 : * Extract data from the pg_constraint tuple of a foreign-key constraint.
1186 : *
1187 : * All arguments save the first are output arguments. All output arguments
1188 : * other than numfks, conkey and confkey can be passed as NULL if caller
1189 : * doesn't need them.
1190 : */
1191 : void
1192 6962 : DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
1193 : AttrNumber *conkey, AttrNumber *confkey,
1194 : Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs,
1195 : int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
1196 : {
1197 : Datum adatum;
1198 : bool isNull;
1199 : ArrayType *arr;
1200 : int numkeys;
1201 :
1202 : /*
1203 : * We expect the arrays to be 1-D arrays of the right types; verify that.
1204 : * We don't need to use deconstruct_array() since the array data is just
1205 : * going to look like a C array of values.
1206 : */
1207 6962 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1208 : Anum_pg_constraint_conkey);
1209 6962 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1210 6962 : if (ARR_NDIM(arr) != 1 ||
1211 6962 : ARR_HASNULL(arr) ||
1212 6962 : ARR_ELEMTYPE(arr) != INT2OID)
1213 0 : elog(ERROR, "conkey is not a 1-D smallint array");
1214 6962 : numkeys = ARR_DIMS(arr)[0];
1215 6962 : if (numkeys <= 0 || numkeys > INDEX_MAX_KEYS)
1216 0 : elog(ERROR, "foreign key constraint cannot have %d columns", numkeys);
1217 6962 : memcpy(conkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
1218 6962 : if ((Pointer) arr != DatumGetPointer(adatum))
1219 6962 : pfree(arr); /* free de-toasted copy, if any */
1220 :
1221 6962 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1222 : Anum_pg_constraint_confkey);
1223 6962 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1224 6962 : if (ARR_NDIM(arr) != 1 ||
1225 6962 : ARR_DIMS(arr)[0] != numkeys ||
1226 6962 : ARR_HASNULL(arr) ||
1227 6962 : ARR_ELEMTYPE(arr) != INT2OID)
1228 0 : elog(ERROR, "confkey is not a 1-D smallint array");
1229 6962 : memcpy(confkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
1230 6962 : if ((Pointer) arr != DatumGetPointer(adatum))
1231 6962 : pfree(arr); /* free de-toasted copy, if any */
1232 :
1233 6962 : if (pf_eq_oprs)
1234 : {
1235 6962 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1236 : Anum_pg_constraint_conpfeqop);
1237 6962 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1238 : /* see TryReuseForeignKey if you change the test below */
1239 6962 : if (ARR_NDIM(arr) != 1 ||
1240 6962 : ARR_DIMS(arr)[0] != numkeys ||
1241 6962 : ARR_HASNULL(arr) ||
1242 6962 : ARR_ELEMTYPE(arr) != OIDOID)
1243 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
1244 6962 : memcpy(pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1245 6962 : if ((Pointer) arr != DatumGetPointer(adatum))
1246 6962 : pfree(arr); /* free de-toasted copy, if any */
1247 : }
1248 :
1249 6962 : if (pp_eq_oprs)
1250 : {
1251 4346 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1252 : Anum_pg_constraint_conppeqop);
1253 4346 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1254 4346 : if (ARR_NDIM(arr) != 1 ||
1255 4346 : ARR_DIMS(arr)[0] != numkeys ||
1256 4346 : ARR_HASNULL(arr) ||
1257 4346 : ARR_ELEMTYPE(arr) != OIDOID)
1258 0 : elog(ERROR, "conppeqop is not a 1-D Oid array");
1259 4346 : memcpy(pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1260 4346 : if ((Pointer) arr != DatumGetPointer(adatum))
1261 4346 : pfree(arr); /* free de-toasted copy, if any */
1262 : }
1263 :
1264 6962 : if (ff_eq_oprs)
1265 : {
1266 4346 : adatum = SysCacheGetAttrNotNull(CONSTROID, tuple,
1267 : Anum_pg_constraint_conffeqop);
1268 4346 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1269 4346 : if (ARR_NDIM(arr) != 1 ||
1270 4346 : ARR_DIMS(arr)[0] != numkeys ||
1271 4346 : ARR_HASNULL(arr) ||
1272 4346 : ARR_ELEMTYPE(arr) != OIDOID)
1273 0 : elog(ERROR, "conffeqop is not a 1-D Oid array");
1274 4346 : memcpy(ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
1275 4346 : if ((Pointer) arr != DatumGetPointer(adatum))
1276 4346 : pfree(arr); /* free de-toasted copy, if any */
1277 : }
1278 :
1279 6962 : if (fk_del_set_cols)
1280 : {
1281 4346 : adatum = SysCacheGetAttr(CONSTROID, tuple,
1282 : Anum_pg_constraint_confdelsetcols, &isNull);
1283 4346 : if (isNull)
1284 : {
1285 4268 : *num_fk_del_set_cols = 0;
1286 : }
1287 : else
1288 : {
1289 : int num_delete_cols;
1290 :
1291 78 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
1292 78 : if (ARR_NDIM(arr) != 1 ||
1293 78 : ARR_HASNULL(arr) ||
1294 78 : ARR_ELEMTYPE(arr) != INT2OID)
1295 0 : elog(ERROR, "confdelsetcols is not a 1-D smallint array");
1296 78 : num_delete_cols = ARR_DIMS(arr)[0];
1297 78 : memcpy(fk_del_set_cols, ARR_DATA_PTR(arr), num_delete_cols * sizeof(int16));
1298 78 : if ((Pointer) arr != DatumGetPointer(adatum))
1299 78 : pfree(arr); /* free de-toasted copy, if any */
1300 :
1301 78 : *num_fk_del_set_cols = num_delete_cols;
1302 : }
1303 : }
1304 :
1305 6962 : *numfks = numkeys;
1306 6962 : }
1307 :
1308 : /*
1309 : * Determine whether a relation can be proven functionally dependent on
1310 : * a set of grouping columns. If so, return true and add the pg_constraint
1311 : * OIDs of the constraints needed for the proof to the *constraintDeps list.
1312 : *
1313 : * grouping_columns is a list of grouping expressions, in which columns of
1314 : * the rel of interest are Vars with the indicated varno/varlevelsup.
1315 : *
1316 : * Currently we only check to see if the rel has a primary key that is a
1317 : * subset of the grouping_columns. We could also use plain unique constraints
1318 : * if all their columns are known not null, but there's a problem: we need
1319 : * to be able to represent the not-null-ness as part of the constraints added
1320 : * to *constraintDeps. FIXME whenever not-null constraints get represented
1321 : * in pg_constraint.
1322 : */
1323 : bool
1324 196 : check_functional_grouping(Oid relid,
1325 : Index varno, Index varlevelsup,
1326 : List *grouping_columns,
1327 : List **constraintDeps)
1328 : {
1329 : Bitmapset *pkattnos;
1330 : Bitmapset *groupbyattnos;
1331 : Oid constraintOid;
1332 : ListCell *gl;
1333 :
1334 : /* If the rel has no PK, then we can't prove functional dependency */
1335 196 : pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
1336 196 : if (pkattnos == NULL)
1337 42 : return false;
1338 :
1339 : /* Identify all the rel's columns that appear in grouping_columns */
1340 154 : groupbyattnos = NULL;
1341 350 : foreach(gl, grouping_columns)
1342 : {
1343 196 : Var *gvar = (Var *) lfirst(gl);
1344 :
1345 196 : if (IsA(gvar, Var) &&
1346 196 : gvar->varno == varno &&
1347 154 : gvar->varlevelsup == varlevelsup)
1348 154 : groupbyattnos = bms_add_member(groupbyattnos,
1349 154 : gvar->varattno - FirstLowInvalidHeapAttributeNumber);
1350 : }
1351 :
1352 154 : if (bms_is_subset(pkattnos, groupbyattnos))
1353 : {
1354 : /* The PK is a subset of grouping_columns, so we win */
1355 112 : *constraintDeps = lappend_oid(*constraintDeps, constraintOid);
1356 112 : return true;
1357 : }
1358 :
1359 42 : return false;
1360 : }
|