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