]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_constraint.c
Update copyright to 2004.
[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-2004, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.21 2004/08/29 04:12:28 momjian 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 "commands/defrem.h"
26 #include "miscadmin.h"
27 #include "utils/array.h"
28 #include "utils/builtins.h"
29 #include "utils/fmgroids.h"
30 #include "utils/syscache.h"
31
32
33 /*
34  * CreateConstraintEntry
35  *      Create a constraint table entry.
36  *
37  * Subsidiary records (such as triggers or indexes to implement the
38  * constraint) are *not* created here.  But we do make dependency links
39  * from the constraint to the things it depends on.
40  */
41 Oid
42 CreateConstraintEntry(const char *constraintName,
43                                           Oid constraintNamespace,
44                                           char constraintType,
45                                           bool isDeferrable,
46                                           bool isDeferred,
47                                           Oid relId,
48                                           const int16 *constraintKey,
49                                           int constraintNKeys,
50                                           Oid domainId,
51                                           Oid foreignRelId,
52                                           const int16 *foreignKey,
53                                           int foreignNKeys,
54                                           char foreignUpdateType,
55                                           char foreignDeleteType,
56                                           char foreignMatchType,
57                                           Oid indexRelId,
58                                           Node *conExpr,
59                                           const char *conBin,
60                                           const char *conSrc)
61 {
62         Relation        conDesc;
63         Oid                     conOid;
64         HeapTuple       tup;
65         char            nulls[Natts_pg_constraint];
66         Datum           values[Natts_pg_constraint];
67         ArrayType  *conkeyArray;
68         ArrayType  *confkeyArray;
69         NameData        cname;
70         int                     i;
71         ObjectAddress conobject;
72
73         conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
74
75         Assert(constraintName);
76         namestrcpy(&cname, constraintName);
77
78         /*
79          * Convert C arrays into Postgres arrays.
80          */
81         if (constraintNKeys > 0)
82         {
83                 Datum      *conkey;
84
85                 conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
86                 for (i = 0; i < constraintNKeys; i++)
87                         conkey[i] = Int16GetDatum(constraintKey[i]);
88                 conkeyArray = construct_array(conkey, constraintNKeys,
89                                                                           INT2OID, 2, true, 's');
90         }
91         else
92                 conkeyArray = NULL;
93
94         if (foreignNKeys > 0)
95         {
96                 Datum      *confkey;
97
98                 confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum));
99                 for (i = 0; i < foreignNKeys; i++)
100                         confkey[i] = Int16GetDatum(foreignKey[i]);
101                 confkeyArray = construct_array(confkey, foreignNKeys,
102                                                                            INT2OID, 2, true, 's');
103         }
104         else
105                 confkeyArray = NULL;
106
107         /* initialize nulls and values */
108         for (i = 0; i < Natts_pg_constraint; i++)
109         {
110                 nulls[i] = ' ';
111                 values[i] = (Datum) NULL;
112         }
113
114         values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
115         values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
116         values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
117         values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
118         values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
119         values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
120         values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
121         values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
122         values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
123         values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
124         values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
125
126         if (conkeyArray)
127                 values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
128         else
129                 nulls[Anum_pg_constraint_conkey - 1] = 'n';
130
131         if (confkeyArray)
132                 values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
133         else
134                 nulls[Anum_pg_constraint_confkey - 1] = 'n';
135
136         /*
137          * initialize the binary form of the check constraint.
138          */
139         if (conBin)
140                 values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin,
141                                                                                                 CStringGetDatum(conBin));
142         else
143                 nulls[Anum_pg_constraint_conbin - 1] = 'n';
144
145         /*
146          * initialize the text form of the check constraint
147          */
148         if (conSrc)
149                 values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin,
150                                                                                                 CStringGetDatum(conSrc));
151         else
152                 nulls[Anum_pg_constraint_consrc - 1] = 'n';
153
154         tup = heap_formtuple(RelationGetDescr(conDesc), values, nulls);
155
156         conOid = simple_heap_insert(conDesc, tup);
157
158         /* update catalog indexes */
159         CatalogUpdateIndexes(conDesc, tup);
160
161         conobject.classId = RelationGetRelid(conDesc);
162         conobject.objectId = conOid;
163         conobject.objectSubId = 0;
164
165         heap_close(conDesc, RowExclusiveLock);
166
167         if (OidIsValid(relId))
168         {
169                 /*
170                  * Register auto dependency from constraint to owning relation, or
171                  * to specific column(s) if any are mentioned.
172                  */
173                 ObjectAddress relobject;
174
175                 relobject.classId = RelOid_pg_class;
176                 relobject.objectId = relId;
177                 if (constraintNKeys > 0)
178                 {
179                         for (i = 0; i < constraintNKeys; i++)
180                         {
181                                 relobject.objectSubId = constraintKey[i];
182
183                                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
184                         }
185                 }
186                 else
187                 {
188                         relobject.objectSubId = 0;
189
190                         recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
191                 }
192         }
193
194         if (OidIsValid(domainId))
195         {
196                 /*
197                  * Register auto dependency from constraint to owning domain
198                  */
199                 ObjectAddress domobject;
200
201                 domobject.classId = RelOid_pg_type;
202                 domobject.objectId = domainId;
203                 domobject.objectSubId = 0;
204
205                 recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
206         }
207
208         if (OidIsValid(foreignRelId))
209         {
210                 /*
211                  * Register normal dependency from constraint to foreign relation,
212                  * or to specific column(s) if any are mentioned.
213                  */
214                 ObjectAddress relobject;
215
216                 relobject.classId = RelOid_pg_class;
217                 relobject.objectId = foreignRelId;
218                 if (foreignNKeys > 0)
219                 {
220                         for (i = 0; i < foreignNKeys; i++)
221                         {
222                                 relobject.objectSubId = foreignKey[i];
223
224                                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
225                         }
226                 }
227                 else
228                 {
229                         relobject.objectSubId = 0;
230
231                         recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
232                 }
233         }
234
235         if (OidIsValid(indexRelId))
236         {
237                 /*
238                  * Register normal dependency on the unique index that supports a
239                  * foreign-key constraint.
240                  */
241                 ObjectAddress relobject;
242
243                 relobject.classId = RelOid_pg_class;
244                 relobject.objectId = indexRelId;
245                 relobject.objectSubId = 0;
246
247                 recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
248         }
249
250         if (conExpr != NULL)
251         {
252                 /*
253                  * Register dependencies from constraint to objects mentioned in
254                  * CHECK expression.
255                  */
256                 recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
257                                                                                 DEPENDENCY_NORMAL,
258                                                                                 DEPENDENCY_NORMAL);
259         }
260
261         return conOid;
262 }
263
264
265 /*
266  * Test whether given name is currently used as a constraint name
267  * for the given object (relation or domain).
268  *
269  * This is used to decide whether to accept a user-specified constraint name.
270  * It is deliberately not the same test as ChooseConstraintName uses to decide
271  * whether an auto-generated name is OK: here, we will allow it unless there
272  * is an identical constraint name in use *on the same object*.
273  *
274  * NB: Caller should hold exclusive lock on the given object, else
275  * this test can be fooled by concurrent additions.
276  */
277 bool
278 ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
279                                          Oid objNamespace, const char *conname)
280 {
281         bool            found;
282         Relation        conDesc;
283         SysScanDesc conscan;
284         ScanKeyData skey[2];
285         HeapTuple       tup;
286
287         conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
288
289         found = false;
290
291         ScanKeyInit(&skey[0],
292                                 Anum_pg_constraint_conname,
293                                 BTEqualStrategyNumber, F_NAMEEQ,
294                                 CStringGetDatum(conname));
295
296         ScanKeyInit(&skey[1],
297                                 Anum_pg_constraint_connamespace,
298                                 BTEqualStrategyNumber, F_OIDEQ,
299                                 ObjectIdGetDatum(objNamespace));
300
301         conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
302                                                                  SnapshotNow, 2, skey);
303
304         while (HeapTupleIsValid(tup = systable_getnext(conscan)))
305         {
306                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
307
308                 if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
309                 {
310                         found = true;
311                         break;
312                 }
313                 else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
314                 {
315                         found = true;
316                         break;
317                 }
318         }
319
320         systable_endscan(conscan);
321         heap_close(conDesc, AccessShareLock);
322
323         return found;
324 }
325
326 /*
327  * Select a nonconflicting name for a new constraint.
328  *
329  * The objective here is to choose a name that is unique within the
330  * specified namespace.  Postgres does not require this, but the SQL
331  * spec does, and some apps depend on it.  Therefore we avoid choosing
332  * default names that so conflict.
333  *
334  * name1, name2, and label are used the same way as for makeObjectName(),
335  * except that the label can't be NULL; digits will be appended to the label
336  * if needed to create a name that is unique within the specified namespace.
337  *
338  * 'others' can be a list of string names already chosen within the current
339  * command (but not yet reflected into the catalogs); we will not choose
340  * a duplicate of one of these either.
341  *
342  * Note: it is theoretically possible to get a collision anyway, if someone
343  * else chooses the same name concurrently.  This is fairly unlikely to be
344  * a problem in practice, especially if one is holding an exclusive lock on
345  * the relation identified by name1.
346  *
347  * Returns a palloc'd string.
348  */
349 char *
350 ChooseConstraintName(const char *name1, const char *name2,
351                                          const char *label, Oid namespace,
352                                          List *others)
353 {
354         int                     pass = 0;
355         char       *conname = NULL;
356         char            modlabel[NAMEDATALEN];
357         Relation        conDesc;
358         SysScanDesc conscan;
359         ScanKeyData skey[2];
360         bool            found;
361         ListCell   *l;
362
363         conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
364
365         /* try the unmodified label first */
366         StrNCpy(modlabel, label, sizeof(modlabel));
367
368         for (;;)
369         {
370                 conname = makeObjectName(name1, name2, modlabel);
371
372                 found = false;
373
374                 foreach(l, others)
375                 {
376                         if (strcmp((char *) lfirst(l), conname) == 0)
377                         {
378                                 found = true;
379                                 break;
380                         }
381                 }
382
383                 if (!found)
384                 {
385                         ScanKeyInit(&skey[0],
386                                                 Anum_pg_constraint_conname,
387                                                 BTEqualStrategyNumber, F_NAMEEQ,
388                                                 CStringGetDatum(conname));
389
390                         ScanKeyInit(&skey[1],
391                                                 Anum_pg_constraint_connamespace,
392                                                 BTEqualStrategyNumber, F_OIDEQ,
393                                                 ObjectIdGetDatum(namespace));
394
395                         conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
396                                                                                  SnapshotNow, 2, skey);
397
398                         found = (HeapTupleIsValid(systable_getnext(conscan)));
399
400                         systable_endscan(conscan);
401                 }
402
403                 if (!found)
404                         break;
405
406                 /* found a conflict, so try a new name component */
407                 pfree(conname);
408                 snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
409         }
410
411         heap_close(conDesc, AccessShareLock);
412
413         return conname;
414 }
415
416 /*
417  * Delete a single constraint record.
418  */
419 void
420 RemoveConstraintById(Oid conId)
421 {
422         Relation        conDesc;
423         ScanKeyData skey[1];
424         SysScanDesc conscan;
425         HeapTuple       tup;
426         Form_pg_constraint con;
427
428         conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
429
430         ScanKeyInit(&skey[0],
431                                 ObjectIdAttributeNumber,
432                                 BTEqualStrategyNumber, F_OIDEQ,
433                                 ObjectIdGetDatum(conId));
434
435         conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
436                                                                  SnapshotNow, 1, skey);
437
438         tup = systable_getnext(conscan);
439         if (!HeapTupleIsValid(tup))
440                 elog(ERROR, "could not find tuple for constraint %u", conId);
441         con = (Form_pg_constraint) GETSTRUCT(tup);
442
443         /*
444          * Special processing depending on what the constraint is for.
445          */
446         if (OidIsValid(con->conrelid))
447         {
448                 Relation        rel;
449
450                 /*
451                  * If the constraint is for a relation, open and exclusive-lock
452                  * the relation it's for.
453                  */
454                 rel = heap_open(con->conrelid, AccessExclusiveLock);
455
456                 /*
457                  * We need to update the relcheck count if it is a check
458                  * constraint being dropped.  This update will force backends to
459                  * rebuild relcache entries when we commit.
460                  */
461                 if (con->contype == CONSTRAINT_CHECK)
462                 {
463                         Relation        pgrel;
464                         HeapTuple       relTup;
465                         Form_pg_class classForm;
466
467                         pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
468                         relTup = SearchSysCacheCopy(RELOID,
469                                                                                 ObjectIdGetDatum(con->conrelid),
470                                                                                 0, 0, 0);
471                         if (!HeapTupleIsValid(relTup))
472                                 elog(ERROR, "cache lookup failed for relation %u",
473                                          con->conrelid);
474                         classForm = (Form_pg_class) GETSTRUCT(relTup);
475
476                         if (classForm->relchecks == 0)          /* should not happen */
477                                 elog(ERROR, "relation \"%s\" has relchecks = 0",
478                                          RelationGetRelationName(rel));
479                         classForm->relchecks--;
480
481                         simple_heap_update(pgrel, &relTup->t_self, relTup);
482
483                         CatalogUpdateIndexes(pgrel, relTup);
484
485                         heap_freetuple(relTup);
486
487                         heap_close(pgrel, RowExclusiveLock);
488                 }
489
490                 /* Keep lock on constraint's rel until end of xact */
491                 heap_close(rel, NoLock);
492         }
493         else if (OidIsValid(con->contypid))
494         {
495                 /*
496                  * XXX for now, do nothing special when dropping a domain
497                  * constraint
498                  *
499                  * Probably there should be some form of locking on the domain type,
500                  * but we have no such concept at the moment.
501                  */
502         }
503         else
504                 elog(ERROR, "constraint %u is not of a known type", conId);
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 }