Attached is my patch that adds DROP CONSTRAINT support to PostgreSQL. I
authorBruce Momjian <[email protected]>
Wed, 30 May 2001 12:57:36 +0000 (12:57 +0000)
committerBruce Momjian <[email protected]>
Wed, 30 May 2001 12:57:36 +0000 (12:57 +0000)
basically want your guys feedback.  I have sprinkled some of my q's thru
the text delimited with the @@ symbol.  It seems to work perfectly.

[ Removed @@ comments because patch was reviewed. ]

At the moment it does CHECK constraints only, with inheritance.  However,
due to the problem mentioned before with the mismatching between inherited
constraints it may be wise to disable the inheritance feature for a while.
it is written in an extensible fashion to support future dropping of other
types of constraint, and is well documented.

Please send me your comments, check my use of locking, updating of
indices, use of ERROR and NOTICE, etc. and I will rework the patch based
on feedback until everyone
is happy with it...

Christopher Kings

src/backend/catalog/heap.c
src/backend/commands/command.c
src/include/catalog/heap.h

index 4191047214637fde4b589c5beb4f6172d4cddacc..0675cd766a5f1e17373bc7f3be20e925aed964bd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.165 2001/05/14 20:30:19 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.166 2001/05/30 12:57:36 momjian Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -48,6 +48,7 @@
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
+#include "optimizer/prep.h"
 #include "optimizer/var.h"
 #include "nodes/makefuncs.h"
 #include "parser/parse_clause.h"
@@ -1975,6 +1976,150 @@ RemoveRelCheck(Relation rel)
 
        heap_endscan(rcscan);
        heap_close(rcrel, RowExclusiveLock);
+
+}
+
+/*
+ * Removes all CHECK constraints on a relation that match the given name.
+ * It is the responsibility of the calling function to acquire a lock on
+ * the relation.
+ * Returns: The number of CHECK constraints removed.
+ */
+int
+RemoveCheckConstraint(Relation rel, const char *constrName, bool inh)
+{
+   Oid            relid;
+       Relation                        rcrel;
+       Relation                        relrel;
+       Relation                        inhrel;
+       Relation                        relidescs[Num_pg_class_indices];
+       TupleDesc               tupleDesc;
+       TupleConstr             *oldconstr;
+       int                             numoldchecks;
+       int                             numchecks;
+       HeapScanDesc    rcscan;
+       ScanKeyData             key[2];
+       HeapTuple               rctup;
+       HeapTuple               reltup;
+       Form_pg_class   relStruct;
+       int                             rel_deleted = 0;
+   int            all_deleted = 0;
+
+   /* Find id of the relation */
+   relid = RelationGetRelid(rel);
+
+   /* Process child tables and remove constraints of the
+      same name. */
+   if (inh)
+   {
+      List  *child,
+            *children;
+
+      /* This routine is actually in the planner */
+      children = find_all_inheritors(relid);
+
+      /*
+       * find_all_inheritors does the recursive search of the
+       * inheritance hierarchy, so all we have to do is process all
+       * of the relids in the list that it returns.
+       */
+      foreach(child, children)
+      {
+         Oid   childrelid = lfirsti(child);
+
+         if (childrelid == relid)
+            continue;
+         inhrel = heap_open(childrelid, AccessExclusiveLock);
+         all_deleted += RemoveCheckConstraint(inhrel, constrName, false);
+         heap_close(inhrel, NoLock);
+      }
+   }
+
+       /* Grab an exclusive lock on the pg_relcheck relation */
+       rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
+
+       /*
+        * Create two scan keys.  We need to match on the oid of the table
+        * the CHECK is in and also we need to match the name of the CHECK
+        * constraint.
+        */
+       ScanKeyEntryInitialize(&key[0], 0, Anum_pg_relcheck_rcrelid,
+                                                  F_OIDEQ, RelationGetRelid(rel));
+
+       ScanKeyEntryInitialize(&key[1], 0, Anum_pg_relcheck_rcname,
+                                                  F_NAMEEQ, PointerGetDatum(constrName));
+
+       /* Begin scanning the heap */
+       rcscan = heap_beginscan(rcrel, 0, SnapshotNow, 2, key);
+
+       /*
+        * Scan over the result set, removing any matching entries.  Note
+        * that this has the side-effect of removing ALL CHECK constraints
+        * that share the specified constraint name.
+        */
+       while (HeapTupleIsValid(rctup = heap_getnext(rcscan, 0))) {
+               simple_heap_delete(rcrel, &rctup->t_self);
+               ++rel_deleted;
+      ++all_deleted;
+       }
+
+       /* Clean up after the scan */
+       heap_endscan(rcscan);
+
+       /*
+        * Update the count of constraints in the relation's pg_class tuple.
+        * We do this even if there was no change, in order to ensure that an
+        * SI update message is sent out for the pg_class tuple, which will
+        * force other backends to rebuild their relcache entries for the rel.
+        * (Of course, for a newly created rel there is no need for an SI
+        * message, but for ALTER TABLE ADD ATTRIBUTE this'd be important.)
+        */
+
+       /*
+        * Get number of existing constraints.
+        */
+
+       tupleDesc = RelationGetDescr(rel);
+       oldconstr = tupleDesc->constr;
+       if (oldconstr)
+               numoldchecks = oldconstr->num_check;
+       else
+               numoldchecks = 0;
+
+       /* Calculate the new number of checks in the table, fail if negative */
+       numchecks = numoldchecks - rel_deleted;
+
+       if (numchecks < 0)
+               elog(ERROR, "check count became negative");
+
+       relrel = heap_openr(RelationRelationName, RowExclusiveLock);
+       reltup = SearchSysCacheCopy(RELOID,
+               ObjectIdGetDatum(RelationGetRelid(rel)), 0, 0, 0);
+
+       if (!HeapTupleIsValid(reltup))
+               elog(ERROR, "cache lookup of relation %u failed",
+                        RelationGetRelid(rel));
+       relStruct = (Form_pg_class) GETSTRUCT(reltup);
+
+       relStruct->relchecks = numchecks;
+
+       simple_heap_update(relrel, &reltup->t_self, reltup);
+
+       /* Keep catalog indices current */
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
+                                          relidescs);
+       CatalogIndexInsert(relidescs, Num_pg_class_indices, relrel, reltup);
+       CatalogCloseIndices(Num_pg_class_indices, relidescs);
+
+       /* Clean up after the scan */
+       heap_freetuple(reltup);
+       heap_close(relrel, RowExclusiveLock);
+
+       /* Close the heap relation */
+       heap_close(rcrel, RowExclusiveLock);
+
+       /* Return the number of tuples deleted */
+       return all_deleted;
 }
 
 static void
index 90cfba50be535c32dfe371d2605a61a53a9cab2c..5f799199ed73ad8166c0f7cc9bcb475bebfd8938 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.129 2001/05/27 09:59:28 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.130 2001/05/30 12:57:36 momjian Exp $
  *
  * NOTES
  *       The PerformAddAttribute() code, like most of the relation
@@ -51,9 +51,7 @@
 #include "catalog/pg_shadow.h"
 #include "utils/relcache.h"
 
-#ifdef _DROP_COLUMN_HACK__
 #include "parser/parse.h"
-#endif  /* _DROP_COLUMN_HACK__ */
 #include "access/genam.h"
 
 
@@ -1322,6 +1320,11 @@ AlterTableAddConstraint(char *relationName,
                                                        heap_close(rel, NoLock);
                                                        pfree(constlist);
 
+                                                       break;
+                                               }
+                                       case CONSTR_PRIMARY:
+                                               {
+
                                                        break;
                                                }
                                        default:
@@ -1585,13 +1588,71 @@ AlterTableAddConstraint(char *relationName,
 
 /*
  * ALTER TABLE DROP CONSTRAINT
+ * Note: It is legal to remove a constraint with name "" as it is possible
+ * to add a constraint with name "".
+ * Christopher Kings-Lynne
  */
 void
 AlterTableDropConstraint(const char *relationName,
                                                 bool inh, const char *constrName,
                                                 int behavior)
 {
-       elog(ERROR, "ALTER TABLE / DROP CONSTRAINT is not implemented");
+       Relation                rel;
+       int                     deleted;
+
+#ifndef NO_SECURITY
+       if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
+               elog(ERROR, "ALTER TABLE: permission denied");
+#endif
+
+       /* We don't support CASCADE yet  - in fact, RESTRICT
+        * doesn't work to the spec either! */
+       if (behavior == CASCADE)
+               elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword");
+
+       /*
+        * Acquire an exclusive lock on the target relation for
+        * the duration of the operation.
+        */
+
+       rel = heap_openr(relationName, AccessExclusiveLock);
+
+       /* Disallow DROP CONSTRAINT on views, indexes, sequences, etc */
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE / DROP CONSTRAINT: %s is not a table",
+                        relationName);
+
+       /*
+        * Since all we have is the name of the constraint, we have to look through
+        * all catalogs that could possibly contain a constraint for this relation.
+        * We also keep a count of the number of constraints removed.
+        */
+
+       deleted = 0;
+
+       /*
+        * First, we remove all CHECK constraints with the given name
+        */
+
+       deleted += RemoveCheckConstraint(rel, constrName, inh);
+
+       /*
+        * Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY constraints.
+        *
+        * Unimplemented.
+        */
+
+       /* Close the target relation */
+       heap_close(rel, NoLock);
+
+       /* If zero constraints deleted, complain */
+       if (deleted == 0)
+               elog(ERROR, "ALTER TABLE / DROP CONSTRAINT: %s does not exist",
+                        constrName);
+       /* Otherwise if more than one constraint deleted, notify */
+       else if (deleted > 1)
+               elog(NOTICE, "Multiple constraints dropped");
+
 }
 
 
index 7ab04b05fb25b1dd765830e90ec1b717c6e2814e..5488e794e969c8dbcb4ca575a714532af14343d5 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: heap.h,v 1.35 2001/05/07 00:43:24 tgl Exp $
+ * $Id: heap.h,v 1.36 2001/05/30 12:57:36 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,6 +45,9 @@ extern void AddRelationRawConstraints(Relation rel,
                                                  List *rawColDefaults,
                                                  List *rawConstraints);
 
+extern int RemoveCheckConstraint(Relation rel, const char *constrName, bool inh);
+
+
 extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno);
 
 #endif  /* HEAP_H */