]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_constraint.c
Update CVS HEAD for 2007 copyright. Back branches are typically not
[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-2007, 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.34 2007/01/05 22:19:25 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/pg_constraint.h"
22 #include "catalog/pg_depend.h"
23 #include "catalog/pg_trigger.h"
24 #include "catalog/pg_type.h"
25 #include "commands/defrem.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_open(ConstraintRelationId, 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 = ConstraintRelationId;
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 to
170                  * specific column(s) if any are mentioned.
171                  */
172                 ObjectAddress relobject;
173
174                 relobject.classId = RelationRelationId;
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 = TypeRelationId;
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, or
211                  * to specific column(s) if any are mentioned.
212                  */
213                 ObjectAddress relobject;
214
215                 relobject.classId = RelationRelationId;
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 a
238                  * foreign-key constraint.
239                  */
240                 ObjectAddress relobject;
241
242                 relobject.classId = RelationRelationId;
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 CHECK
253                  * expression.
254                  */
255                 recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
256                                                                                 DEPENDENCY_NORMAL,
257                                                                                 DEPENDENCY_NORMAL);
258         }
259
260         return conOid;
261 }
262
263
264 /*
265  * Test whether given name is currently used as a constraint name
266  * for the given object (relation or domain).
267  *
268  * This is used to decide whether to accept a user-specified constraint name.
269  * It is deliberately not the same test as ChooseConstraintName uses to decide
270  * whether an auto-generated name is OK: here, we will allow it unless there
271  * is an identical constraint name in use *on the same object*.
272  *
273  * NB: Caller should hold exclusive lock on the given object, else
274  * this test can be fooled by concurrent additions.
275  */
276 bool
277 ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
278                                          Oid objNamespace, const char *conname)
279 {
280         bool            found;
281         Relation        conDesc;
282         SysScanDesc conscan;
283         ScanKeyData skey[2];
284         HeapTuple       tup;
285
286         conDesc = heap_open(ConstraintRelationId, AccessShareLock);
287
288         found = false;
289
290         ScanKeyInit(&skey[0],
291                                 Anum_pg_constraint_conname,
292                                 BTEqualStrategyNumber, F_NAMEEQ,
293                                 CStringGetDatum(conname));
294
295         ScanKeyInit(&skey[1],
296                                 Anum_pg_constraint_connamespace,
297                                 BTEqualStrategyNumber, F_OIDEQ,
298                                 ObjectIdGetDatum(objNamespace));
299
300         conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
301                                                                  SnapshotNow, 2, skey);
302
303         while (HeapTupleIsValid(tup = systable_getnext(conscan)))
304         {
305                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
306
307                 if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
308                 {
309                         found = true;
310                         break;
311                 }
312                 else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
313                 {
314                         found = true;
315                         break;
316                 }
317         }
318
319         systable_endscan(conscan);
320         heap_close(conDesc, AccessShareLock);
321
322         return found;
323 }
324
325 /*
326  * Select a nonconflicting name for a new constraint.
327  *
328  * The objective here is to choose a name that is unique within the
329  * specified namespace.  Postgres does not require this, but the SQL
330  * spec does, and some apps depend on it.  Therefore we avoid choosing
331  * default names that so conflict.
332  *
333  * name1, name2, and label are used the same way as for makeObjectName(),
334  * except that the label can't be NULL; digits will be appended to the label
335  * if needed to create a name that is unique within the specified namespace.
336  *
337  * 'others' can be a list of string names already chosen within the current
338  * command (but not yet reflected into the catalogs); we will not choose
339  * a duplicate of one of these either.
340  *
341  * Note: it is theoretically possible to get a collision anyway, if someone
342  * else chooses the same name concurrently.  This is fairly unlikely to be
343  * a problem in practice, especially if one is holding an exclusive lock on
344  * the relation identified by name1.
345  *
346  * Returns a palloc'd string.
347  */
348 char *
349 ChooseConstraintName(const char *name1, const char *name2,
350                                          const char *label, Oid namespace,
351                                          List *others)
352 {
353         int                     pass = 0;
354         char       *conname = NULL;
355         char            modlabel[NAMEDATALEN];
356         Relation        conDesc;
357         SysScanDesc conscan;
358         ScanKeyData skey[2];
359         bool            found;
360         ListCell   *l;
361
362         conDesc = heap_open(ConstraintRelationId, AccessShareLock);
363
364         /* try the unmodified label first */
365         StrNCpy(modlabel, label, sizeof(modlabel));
366
367         for (;;)
368         {
369                 conname = makeObjectName(name1, name2, modlabel);
370
371                 found = false;
372
373                 foreach(l, others)
374                 {
375                         if (strcmp((char *) lfirst(l), conname) == 0)
376                         {
377                                 found = true;
378                                 break;
379                         }
380                 }
381
382                 if (!found)
383                 {
384                         ScanKeyInit(&skey[0],
385                                                 Anum_pg_constraint_conname,
386                                                 BTEqualStrategyNumber, F_NAMEEQ,
387                                                 CStringGetDatum(conname));
388
389                         ScanKeyInit(&skey[1],
390                                                 Anum_pg_constraint_connamespace,
391                                                 BTEqualStrategyNumber, F_OIDEQ,
392                                                 ObjectIdGetDatum(namespace));
393
394                         conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
395                                                                                  SnapshotNow, 2, skey);
396
397                         found = (HeapTupleIsValid(systable_getnext(conscan)));
398
399                         systable_endscan(conscan);
400                 }
401
402                 if (!found)
403                         break;
404
405                 /* found a conflict, so try a new name component */
406                 pfree(conname);
407                 snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
408         }
409
410         heap_close(conDesc, AccessShareLock);
411
412         return conname;
413 }
414
415 /*
416  * Delete a single constraint record.
417  */
418 void
419 RemoveConstraintById(Oid conId)
420 {
421         Relation        conDesc;
422         ScanKeyData skey[1];
423         SysScanDesc conscan;
424         HeapTuple       tup;
425         Form_pg_constraint con;
426
427         conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
428
429         ScanKeyInit(&skey[0],
430                                 ObjectIdAttributeNumber,
431                                 BTEqualStrategyNumber, F_OIDEQ,
432                                 ObjectIdGetDatum(conId));
433
434         conscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
435                                                                  SnapshotNow, 1, skey);
436
437         tup = systable_getnext(conscan);
438         if (!HeapTupleIsValid(tup))
439                 elog(ERROR, "could not find tuple for constraint %u", conId);
440         con = (Form_pg_constraint) GETSTRUCT(tup);
441
442         /*
443          * Special processing depending on what the constraint is for.
444          */
445         if (OidIsValid(con->conrelid))
446         {
447                 Relation        rel;
448
449                 /*
450                  * If the constraint is for a relation, open and exclusive-lock the
451                  * relation it's for.
452                  */
453                 rel = heap_open(con->conrelid, AccessExclusiveLock);
454
455                 /*
456                  * We need to update the relcheck count if it is a check constraint
457                  * being dropped.  This update will force backends to rebuild relcache
458                  * entries when we commit.
459                  */
460                 if (con->contype == CONSTRAINT_CHECK)
461                 {
462                         Relation        pgrel;
463                         HeapTuple       relTup;
464                         Form_pg_class classForm;
465
466                         pgrel = heap_open(RelationRelationId, RowExclusiveLock);
467                         relTup = SearchSysCacheCopy(RELOID,
468                                                                                 ObjectIdGetDatum(con->conrelid),
469                                                                                 0, 0, 0);
470                         if (!HeapTupleIsValid(relTup))
471                                 elog(ERROR, "cache lookup failed for relation %u",
472                                          con->conrelid);
473                         classForm = (Form_pg_class) GETSTRUCT(relTup);
474
475                         if (classForm->relchecks == 0)          /* should not happen */
476                                 elog(ERROR, "relation \"%s\" has relchecks = 0",
477                                          RelationGetRelationName(rel));
478                         classForm->relchecks--;
479
480                         simple_heap_update(pgrel, &relTup->t_self, relTup);
481
482                         CatalogUpdateIndexes(pgrel, relTup);
483
484                         heap_freetuple(relTup);
485
486                         heap_close(pgrel, RowExclusiveLock);
487                 }
488
489                 /* Keep lock on constraint's rel until end of xact */
490                 heap_close(rel, NoLock);
491         }
492         else if (OidIsValid(con->contypid))
493         {
494                 /*
495                  * XXX for now, do nothing special when dropping a domain constraint
496                  *
497                  * Probably there should be some form of locking on the domain type,
498                  * but we have no such concept at the moment.
499                  */
500         }
501         else
502                 elog(ERROR, "constraint %u is not of a known type", conId);
503
504         /* Fry the constraint itself */
505         simple_heap_delete(conDesc, &tup->t_self);
506
507         /* Clean up */
508         systable_endscan(conscan);
509         heap_close(conDesc, RowExclusiveLock);
510 }
511
512 /*
513  * GetConstraintNameForTrigger
514  *              Get the name of the constraint owning a trigger, if any
515  *
516  * Returns a palloc'd string, or NULL if no constraint can be found
517  */
518 char *
519 GetConstraintNameForTrigger(Oid triggerId)
520 {
521         char       *result;
522         Oid                     constraintId = InvalidOid;
523         Relation        depRel;
524         Relation        conRel;
525         ScanKeyData key[2];
526         SysScanDesc scan;
527         HeapTuple       tup;
528
529         /*
530          * We must grovel through pg_depend to find the owning constraint. Perhaps
531          * pg_trigger should have a column for the owning constraint ... but right
532          * now this is not performance-critical code.
533          */
534         depRel = heap_open(DependRelationId, AccessShareLock);
535
536         ScanKeyInit(&key[0],
537                                 Anum_pg_depend_classid,
538                                 BTEqualStrategyNumber, F_OIDEQ,
539                                 ObjectIdGetDatum(TriggerRelationId));
540         ScanKeyInit(&key[1],
541                                 Anum_pg_depend_objid,
542                                 BTEqualStrategyNumber, F_OIDEQ,
543                                 ObjectIdGetDatum(triggerId));
544         /* assume we can ignore objsubid for a trigger */
545
546         scan = systable_beginscan(depRel, DependDependerIndexId, true,
547                                                           SnapshotNow, 2, key);
548
549         while (HeapTupleIsValid(tup = systable_getnext(scan)))
550         {
551                 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
552
553                 if (foundDep->refclassid == ConstraintRelationId &&
554                         foundDep->deptype == DEPENDENCY_INTERNAL)
555                 {
556                         constraintId = foundDep->refobjid;
557                         break;
558                 }
559         }
560
561         systable_endscan(scan);
562
563         heap_close(depRel, AccessShareLock);
564
565         if (!OidIsValid(constraintId))
566                 return NULL;                    /* no owning constraint found */
567
568         conRel = heap_open(ConstraintRelationId, AccessShareLock);
569
570         ScanKeyInit(&key[0],
571                                 ObjectIdAttributeNumber,
572                                 BTEqualStrategyNumber, F_OIDEQ,
573                                 ObjectIdGetDatum(constraintId));
574
575         scan = systable_beginscan(conRel, ConstraintOidIndexId, true,
576                                                           SnapshotNow, 1, key);
577
578         tup = systable_getnext(scan);
579
580         if (HeapTupleIsValid(tup))
581         {
582                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
583
584                 result = pstrdup(NameStr(con->conname));
585         }
586         else
587         {
588                 /* This arguably should be an error, but we'll just return NULL */
589                 result = NULL;
590         }
591
592         systable_endscan(scan);
593
594         heap_close(conRel, AccessShareLock);
595
596         return result;
597 }
598
599 /*
600  * AlterConstraintNamespaces
601  *              Find any constraints belonging to the specified object,
602  *              and move them to the specified new namespace.
603  *
604  * isType indicates whether the owning object is a type or a relation.
605  */
606 void
607 AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
608                                                   Oid newNspId, bool isType)
609 {
610         Relation        conRel;
611         ScanKeyData key[1];
612         SysScanDesc scan;
613         HeapTuple       tup;
614
615         conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
616
617         if (isType)
618         {
619                 ScanKeyInit(&key[0],
620                                         Anum_pg_constraint_contypid,
621                                         BTEqualStrategyNumber, F_OIDEQ,
622                                         ObjectIdGetDatum(ownerId));
623
624                 scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
625                                                                   SnapshotNow, 1, key);
626         }
627         else
628         {
629                 ScanKeyInit(&key[0],
630                                         Anum_pg_constraint_conrelid,
631                                         BTEqualStrategyNumber, F_OIDEQ,
632                                         ObjectIdGetDatum(ownerId));
633
634                 scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
635                                                                   SnapshotNow, 1, key);
636         }
637
638         while (HeapTupleIsValid((tup = systable_getnext(scan))))
639         {
640                 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
641
642                 if (conform->connamespace == oldNspId)
643                 {
644                         tup = heap_copytuple(tup);
645                         conform = (Form_pg_constraint) GETSTRUCT(tup);
646
647                         conform->connamespace = newNspId;
648
649                         simple_heap_update(conRel, &tup->t_self, tup);
650                         CatalogUpdateIndexes(conRel, tup);
651
652                         /*
653                          * Note: currently, the constraint will not have its own
654                          * dependency on the namespace, so we don't need to do
655                          * changeDependencyFor().
656                          */
657                 }
658         }
659
660         systable_endscan(scan);
661
662         heap_close(conRel, RowExclusiveLock);
663 }