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