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