]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_constraint.c
Preliminary code review for domain CHECK constraints patch: add documentation,
[postgresql] / src / backend / catalog / pg_constraint.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_constraint.c
4  *        routines to support manipulation of the pg_constraint relation
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.12 2002/12/12 20:35:11 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "access/genam.h"
19 #include "catalog/catalog.h"
20 #include "catalog/catname.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/pg_constraint.h"
24 #include "catalog/pg_type.h"
25 #include "miscadmin.h"
26 #include "utils/array.h"
27 #include "utils/builtins.h"
28 #include "utils/fmgroids.h"
29 #include "utils/syscache.h"
30
31
32 /*
33  * CreateConstraintEntry
34  *      Create a constraint table entry.
35  *
36  * Subsidiary records (such as triggers or indexes to implement the
37  * constraint) are *not* created here.  But we do make dependency links
38  * from the constraint to the things it depends on.
39  */
40 Oid
41 CreateConstraintEntry(const char *constraintName,
42                                           Oid constraintNamespace,
43                                           char constraintType,
44                                           bool isDeferrable,
45                                           bool isDeferred,
46                                           Oid relId,
47                                           const int16 *constraintKey,
48                                           int constraintNKeys,
49                                           Oid domainId,
50                                           Oid foreignRelId,
51                                           const int16 *foreignKey,
52                                           int foreignNKeys,
53                                           char foreignUpdateType,
54                                           char foreignDeleteType,
55                                           char foreignMatchType,
56                                           Oid indexRelId,
57                                           Node *conExpr,
58                                           const char *conBin,
59                                           const char *conSrc)
60 {
61         Relation        conDesc;
62         Oid                     conOid;
63         HeapTuple       tup;
64         char            nulls[Natts_pg_constraint];
65         Datum           values[Natts_pg_constraint];
66         ArrayType  *conkeyArray;
67         ArrayType  *confkeyArray;
68         NameData        cname;
69         int                     i;
70         ObjectAddress conobject;
71
72         conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
73
74         Assert(constraintName);
75         namestrcpy(&cname, constraintName);
76
77         /*
78          * Convert C arrays into Postgres arrays.
79          */
80         if (constraintNKeys > 0)
81         {
82                 Datum      *conkey;
83
84                 conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
85                 for (i = 0; i < constraintNKeys; i++)
86                         conkey[i] = Int16GetDatum(constraintKey[i]);
87                 conkeyArray = construct_array(conkey, constraintNKeys,
88                                                                           INT2OID, 2, true, 's');
89         }
90         else
91                 conkeyArray = NULL;
92
93         if (foreignNKeys > 0)
94         {
95                 Datum      *confkey;
96
97                 confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum));
98                 for (i = 0; i < foreignNKeys; i++)
99                         confkey[i] = Int16GetDatum(foreignKey[i]);
100                 confkeyArray = construct_array(confkey, foreignNKeys,
101                                                                            INT2OID, 2, true, 's');
102         }
103         else
104                 confkeyArray = NULL;
105
106         /* initialize nulls and values */
107         for (i = 0; i < Natts_pg_constraint; i++)
108         {
109                 nulls[i] = ' ';
110                 values[i] = (Datum) NULL;
111         }
112
113         values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
114         values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
115         values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
116         values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
117         values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
118         values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
119         values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
120         values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
121         values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
122         values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
123         values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
124
125         if (conkeyArray)
126                 values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
127         else
128                 nulls[Anum_pg_constraint_conkey - 1] = 'n';
129
130         if (confkeyArray)
131                 values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
132         else
133                 nulls[Anum_pg_constraint_confkey - 1] = 'n';
134
135         /*
136          * initialize the binary form of the check constraint.
137          */
138         if (conBin)
139                 values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin,
140                                                                                                 CStringGetDatum(conBin));
141         else
142                 nulls[Anum_pg_constraint_conbin - 1] = 'n';
143
144         /*
145          * initialize the text form of the check constraint
146          */
147         if (conSrc)
148                 values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin,
149                                                                                                 CStringGetDatum(conSrc));
150         else
151                 nulls[Anum_pg_constraint_consrc - 1] = 'n';
152
153         tup = heap_formtuple(RelationGetDescr(conDesc), values, nulls);
154
155         conOid = simple_heap_insert(conDesc, tup);
156
157         /* update catalog indexes */
158         CatalogUpdateIndexes(conDesc, tup);
159
160         conobject.classId = RelationGetRelid(conDesc);
161         conobject.objectId = conOid;
162         conobject.objectSubId = 0;
163
164         heap_close(conDesc, RowExclusiveLock);
165
166         if (OidIsValid(relId))
167         {
168                 /*
169                  * Register auto dependency from constraint to owning relation, or
170                  * to specific column(s) if any are mentioned.
171                  */
172                 ObjectAddress relobject;
173
174                 relobject.classId = RelOid_pg_class;
175                 relobject.objectId = relId;
176                 if (constraintNKeys > 0)
177                 {
178                         for (i = 0; i < constraintNKeys; i++)
179                         {
180                                 relobject.objectSubId = constraintKey[i];
181
182                                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
183                         }
184                 }
185                 else
186                 {
187                         relobject.objectSubId = 0;
188
189                         recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
190                 }
191         }
192
193         if (OidIsValid(domainId))
194         {
195                 /*
196                  * Register auto dependency from constraint to owning domain
197                  */
198                 ObjectAddress   domobject;
199
200                 domobject.classId = RelOid_pg_type;
201                 domobject.objectId = domainId;
202                 domobject.objectSubId = 0;
203
204                 recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
205         }
206
207         if (OidIsValid(foreignRelId))
208         {
209                 /*
210                  * Register normal dependency from constraint to foreign relation,
211                  * or to specific column(s) if any are mentioned.
212                  */
213                 ObjectAddress relobject;
214
215                 relobject.classId = RelOid_pg_class;
216                 relobject.objectId = foreignRelId;
217                 if (foreignNKeys > 0)
218                 {
219                         for (i = 0; i < foreignNKeys; i++)
220                         {
221                                 relobject.objectSubId = foreignKey[i];
222
223                                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
224                         }
225                 }
226                 else
227                 {
228                         relobject.objectSubId = 0;
229
230                         recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
231                 }
232         }
233
234         if (OidIsValid(indexRelId))
235         {
236                 /*
237                  * Register normal dependency on the unique index that supports
238                  * a foreign-key constraint.
239                  */
240                 ObjectAddress relobject;
241
242                 relobject.classId = RelOid_pg_class;
243                 relobject.objectId = indexRelId;
244                 relobject.objectSubId = 0;
245
246                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
247         }
248
249         if (conExpr != NULL)
250         {
251                 /*
252                  * Register dependencies from constraint to objects mentioned in
253                  * CHECK expression.  We gin up a rather bogus rangetable list to
254                  * handle any Vars in the constraint.
255                  */
256                 RangeTblEntry rte;
257
258                 MemSet(&rte, 0, sizeof(rte));
259                 rte.type = T_RangeTblEntry;
260                 rte.rtekind = RTE_RELATION;
261                 rte.relid = relId;
262
263                 recordDependencyOnExpr(&conobject, conExpr, makeList1(&rte),
264                                                            DEPENDENCY_NORMAL);
265         }
266
267         return conOid;
268 }
269
270
271 /*
272  * Test whether given name is currently used as a constraint name
273  * for the given relation.
274  *
275  * NB: Caller should hold exclusive lock on the given relation, else
276  * this test is not very meaningful.
277  */
278 bool
279 ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, const char *cname)
280 {
281         bool            found;
282         Relation        conDesc;
283         SysScanDesc conscan;
284         ScanKeyData skey[2];
285         HeapTuple       tup;
286
287         conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
288
289         found = false;
290
291         ScanKeyEntryInitialize(&skey[0], 0x0,
292                                                    Anum_pg_constraint_conname, F_NAMEEQ,
293                                                    CStringGetDatum(cname));
294
295         ScanKeyEntryInitialize(&skey[1], 0x0,
296                                                    Anum_pg_constraint_connamespace, F_OIDEQ,
297                                                    ObjectIdGetDatum(objNamespace));
298
299         conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
300                                                                  SnapshotNow, 2, skey);
301
302         while (HeapTupleIsValid(tup = systable_getnext(conscan)))
303         {
304                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
305
306                 if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
307                 {
308                         found = true;
309                         break;
310                 }
311                 else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
312                 {
313                         found = true;
314                         break;
315                 }
316         }
317
318         systable_endscan(conscan);
319         heap_close(conDesc, RowExclusiveLock);
320
321         return found;
322 }
323
324 /*
325  * Generate a currently-unused constraint name for the given relation.
326  *
327  * The passed counter should be initialized to 0 the first time through.
328  * If multiple constraint names are to be generated in a single command,
329  * pass the new counter value to each successive call, else the same
330  * name will be generated each time.
331  *
332  * NB: Caller should hold exclusive lock on the given relation, else
333  * someone else might choose the same name concurrently!
334  */
335 char *
336 GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, int *counter)
337 {
338         bool            found;
339         Relation        conDesc;
340         char       *cname;
341
342         cname = (char *) palloc(NAMEDATALEN * sizeof(char));
343
344         conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
345
346         /* Loop until we find a non-conflicting constraint name */
347         /* We assume there will be one eventually ... */
348         do
349         {
350                 SysScanDesc conscan;
351                 ScanKeyData skey[2];
352                 HeapTuple       tup;
353
354                 ++(*counter);
355                 snprintf(cname, NAMEDATALEN, "$%d", *counter);
356
357                 /*
358                  * This duplicates ConstraintNameIsUsed() so that we can avoid
359                  * re-opening pg_constraint for each iteration.
360                  */
361                 found = false;
362
363                 ScanKeyEntryInitialize(&skey[0], 0x0,
364                                                            Anum_pg_constraint_conname, F_NAMEEQ,
365                                                            CStringGetDatum(cname));
366
367                 ScanKeyEntryInitialize(&skey[1], 0x0,
368                                                            Anum_pg_constraint_connamespace, F_OIDEQ,
369                                                            ObjectIdGetDatum(objNamespace));
370
371                 conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
372                                                                          SnapshotNow, 2, skey);
373
374                 while (HeapTupleIsValid(tup = systable_getnext(conscan)))
375                 {
376                         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
377
378                         if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
379                         {
380                                 found = true;
381                                 break;
382                         }
383                         else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
384                         {
385                                 found = true;
386                                 break;
387                         }
388                 }
389
390                 systable_endscan(conscan);
391         } while (found);
392
393         heap_close(conDesc, RowExclusiveLock);
394
395         return cname;
396 }
397
398 /*
399  * Does the given name look like a generated constraint name?
400  *
401  * This is a test on the form of the name, *not* on whether it has
402  * actually been assigned.
403  */
404 bool
405 ConstraintNameIsGenerated(const char *cname)
406 {
407         if (cname[0] != '$')
408                 return false;
409         if (strspn(cname + 1, "0123456789") != strlen(cname + 1))
410                 return false;
411         return true;
412 }
413
414 /*
415  * Delete a single constraint record.
416  */
417 void
418 RemoveConstraintById(Oid conId)
419 {
420         Relation        conDesc;
421         ScanKeyData skey[1];
422         SysScanDesc conscan;
423         HeapTuple       tup;
424         Form_pg_constraint con;
425
426         conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
427
428         ScanKeyEntryInitialize(&skey[0], 0x0,
429                                                    ObjectIdAttributeNumber, F_OIDEQ,
430                                                    ObjectIdGetDatum(conId));
431
432         conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
433                                                                  SnapshotNow, 1, skey);
434
435         tup = systable_getnext(conscan);
436         if (!HeapTupleIsValid(tup))
437                 elog(ERROR, "RemoveConstraintById: constraint %u not found",
438                          conId);
439         con = (Form_pg_constraint) GETSTRUCT(tup);
440
441         /*
442          * Special processing depending on what the constraint is for.
443          */
444         if (OidIsValid(con->conrelid))
445         {
446                 Relation        rel;
447
448                 /*
449                  * If the constraint is for a relation, open and exclusive-lock the
450                  * relation it's for.
451                  */
452                 rel = heap_open(con->conrelid, AccessExclusiveLock);
453
454                 /*
455                  * We need to update the relcheck count if it is a check
456                  * constraint being dropped.  This update will force backends to
457                  * rebuild relcache entries when we commit.
458                  */
459                 if (con->contype == CONSTRAINT_CHECK)
460                 {
461                         Relation        pgrel;
462                         HeapTuple       relTup;
463                         Form_pg_class classForm;
464
465                         pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
466                         relTup = SearchSysCacheCopy(RELOID,
467                                                                                 ObjectIdGetDatum(con->conrelid),
468                                                                                 0, 0, 0);
469                         if (!HeapTupleIsValid(relTup))
470                                 elog(ERROR, "cache lookup of relation %u failed",
471                                          con->conrelid);
472                         classForm = (Form_pg_class) GETSTRUCT(relTup);
473
474                         if (classForm->relchecks == 0)
475                                 elog(ERROR, "RemoveConstraintById: relation %s has relchecks = 0",
476                                          RelationGetRelationName(rel));
477                         classForm->relchecks--;
478
479                         simple_heap_update(pgrel, &relTup->t_self, relTup);
480
481                         CatalogUpdateIndexes(pgrel, relTup);
482
483                         heap_freetuple(relTup);
484
485                         heap_close(pgrel, RowExclusiveLock);
486                 }
487
488                 /* Keep lock on constraint's rel until end of xact */
489                 heap_close(rel, NoLock);
490         }
491         else if (OidIsValid(con->contypid))
492         {
493                 /*
494                  * XXX for now, do nothing special when dropping a domain constraint
495                  *
496                  * Probably there should be some form of locking on the domain type,
497                  * but we have no such concept at the moment.
498                  */
499         }
500         else
501         {
502                 elog(ERROR, "RemoveConstraintById: Constraint %u is not a known type",
503                          conId);
504         }
505
506         /* Fry the constraint itself */
507         simple_heap_delete(conDesc, &tup->t_self);
508
509         /* Clean up */
510         systable_endscan(conscan);
511         heap_close(conDesc, RowExclusiveLock);
512 }