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