]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/command.c
added ALTER TABLE DROP COLUMN, early version
[postgresql] / src / backend / commands / command.c
index 93322d7a7f71d8b6e7a7786f657fe9f098411dae..5c29dd692acf1de791e2f386b3ed97dd6ffefa2a 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.63 2000/01/16 20:04:55 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.64 2000/01/22 14:20:45 petere Exp $
  *
  * NOTES
  *       The PortalExecutorHeapMemory crap needs to be eliminated
@@ -30,6 +30,7 @@
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_type.h"
 #include "commands/command.h"
+#include "commands/rename.h"
 #include "executor/execdefs.h"
 #include "executor/executor.h"
 #include "catalog/heap.h"
@@ -299,7 +300,7 @@ AlterTableAddColumn(const char *relationName,
        Relation        idescs[Num_pg_attr_indices];
        Relation        ridescs[Num_pg_class_indices];
        bool            hasindex;
-    List       *rawDefaults = NIL;
+//    List       *rawDefaults = NIL;
 
        /*
         * permissions checking.  this would normally be done in utility.c,
@@ -319,7 +320,7 @@ AlterTableAddColumn(const char *relationName,
         * Grab an exclusive lock on the target table, which we will NOT release
         * until end of transaction.
         */
-       rel = heap_openr((char *)relationName, AccessExclusiveLock);
+       rel = heap_openr(relationName, AccessExclusiveLock);
        myrelid = RelationGetRelid(rel);
        heap_close(rel, NoLock);        /* close rel but keep lock! */
 
@@ -519,8 +520,7 @@ AlterTableAlterColumn(const char *relationName,
                elog(ERROR, "ALTER TABLE: permission denied");
 #endif
 
-    /* XXX should heap_openr take const char * ? */
-    rel = heap_openr((char *)relationName, AccessExclusiveLock);
+    rel = heap_openr(relationName, AccessExclusiveLock);
     myrelid = RelationGetRelid(rel);
     heap_close(rel, NoLock);
 
@@ -626,7 +626,7 @@ AlterTableAlterColumn(const char *relationName,
             /* keep the system catalog indices current */
             CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
             CatalogIndexInsert(irelations, Num_pg_attr_indices, attr_rel, newtuple);
-            CatalogCloseIndices(Num_pg_class_indices, irelations);
+            CatalogCloseIndices(Num_pg_attrdef_indices, irelations);
 
             /* get rid of actual default definition */
             drop_default(myrelid, attnum);
@@ -672,31 +672,234 @@ drop_default(Oid relid, int16 attnum)
 
 /*
  * ALTER TABLE DROP COLUMN
+ *
+ * Strategy:
+ * - permission/sanity checks
+ * - create a new table _ATDC<name> with all attributes minus the desired one
+ * - copy over all the data
+ * - make the column defaults point to the new table
+ * - kill the old table
+ * - rename the intermediate table back
  */
 void
 AlterTableDropColumn(const char *relationName,
                      bool inh, const char *colName,
                      int behavior)
 {
-    elog(NOTICE, "ALTER TABLE / DROP COLUMN is not implemented");
+    Relation       oldrel, newrel, defrel;
+    HeapTuple      tuple;
+    TupleDesc      olddesc, newdesc, defdsc;
+    int16          dropattnum, oldnumatts;
+    Oid            oldrel_oid, newrel_oid;
+    char           tmpname[NAMEDATALEN];
+    int16          i;
+    HeapScanDesc   scan;
+    ScanKeyData    scankey;
+
+       if (!allowSystemTableMods && IsSystemRelationName(relationName))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        relationName);
+#ifndef NO_SECURITY
+       if (!pg_ownercheck(UserName, relationName, RELNAME))
+               elog(ERROR, "ALTER TABLE: permission denied");
+#endif
+
+    oldrel = heap_openr(relationName, AccessExclusiveLock);
+    if (oldrel->rd_rel->relkind != RELKIND_RELATION)
+    {
+        heap_close(oldrel, AccessExclusiveLock);
+        elog(ERROR, "ALTER TABLE: relation %s is not a table", relationName);
+    }
+
+    oldrel_oid = ObjectIdGetDatum(RelationGetRelid(oldrel));
+    oldnumatts = RelationGetNumberOfAttributes(oldrel);
+
+    if (oldnumatts==1)
+    {
+        heap_close(oldrel, AccessExclusiveLock);
+        elog(ERROR, "ALTER TABLE: relation %s only has one column", relationName);
+    }
+
+/* What to do here? */
+/*
+    if (length(find_all_inheritors(RelationGetRelid(oldrel)))>0)
+        elog(ERROR, "ALTER TABLE: cannot drop a column on table that is inherited from");
+*/
+    /*
+     * get the number of the attribute
+     */
+    tuple = SearchSysCacheTuple(ATTNAME, oldrel_oid, NameGetDatum(namein(colName)), 0, 0);
+    if (!HeapTupleIsValid(tuple))
+    {
+        heap_close(oldrel, AccessExclusiveLock);
+        elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+             relationName, colName);
+    }
+
+    dropattnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
+
+    if (snprintf(tmpname, NAMEDATALEN, "_ATDC%s", relationName)==-1)
+    {
+        heap_close(oldrel, AccessExclusiveLock);
+        elog(ERROR, "AlterTableDropColumn: relation name too long");
+    }
+
+    /*
+     * Build descriptor for new relation
+     */
+    olddesc = RelationGetDescr(oldrel);
+
+    newdesc = CreateTemplateTupleDesc(oldnumatts-1);
+    for(i = 1; i < dropattnum; i++)
+    {
+        Form_pg_attribute att = olddesc->attrs[i-1];
+        TupleDescInitEntry(newdesc, i, nameout(&(att->attname)),
+                           att->atttypid, att->atttypmod,
+                           att->attnelems, att->attisset);
+        /* the above function doesn't take care of these two */
+        newdesc->attrs[i-1]->attnotnull = att->attnotnull;
+        newdesc->attrs[i-1]->atthasdef = att->atthasdef;
+    }
+
+    for(i = dropattnum; i <= oldnumatts-1; i++)
+    {
+        Form_pg_attribute att = olddesc->attrs[i];
+        TupleDescInitEntry(newdesc, i, nameout(&(att->attname)),
+                           att->atttypid, att->atttypmod,
+                           att->attnelems, att->attisset);
+        /* the above function doesn't take care of these two */
+        newdesc->attrs[i-1]->attnotnull = att->attnotnull;
+        newdesc->attrs[i-1]->atthasdef = att->atthasdef;
+    }
+
+    /* Create the new table */
+    newrel_oid = heap_create_with_catalog(tmpname, newdesc, RELKIND_RELATION, false);
+    if (newrel_oid == InvalidOid)
+    {
+        heap_close(oldrel, AccessExclusiveLock);
+        elog(ERROR, "ALTER TABLE: something went wrong");
+    }
+
+    /* Make the new table visible */
+    CommandCounterIncrement();
+
+    /*
+     * Copy over the data
+     */
+    newrel = heap_open(newrel_oid, AccessExclusiveLock);
+
+    scan = heap_beginscan(oldrel, false, SnapshotNow, 0, NULL);
+    while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+    {
+        bool       isnull;
+        Datum     *new_record;
+        bool      *new_record_nulls;
+        HeapTuple  new_tuple;
+
+        new_record = palloc((oldnumatts-1) * sizeof(*new_record));
+        new_record_nulls = palloc((oldnumatts-1) * sizeof(*new_record_nulls));
+
+        for(i = 1; i < dropattnum; i++)
+        {
+            new_record[i-1] = heap_getattr(tuple, i, olddesc, &isnull);
+            new_record_nulls[i-1] = isnull ? 'n' : ' ';
+        }
+        for(i = dropattnum+1; i <= oldnumatts; i++)
+        {
+            new_record[i-2] = heap_getattr(tuple, i, olddesc, &isnull);
+            new_record_nulls[i-2] = isnull ? 'n' : ' ';
+        }
+
+        new_tuple = heap_formtuple(newdesc, new_record, new_record_nulls);
+        Assert(new_tuple);
+
+        if (heap_insert(newrel, new_tuple) == InvalidOid)
+            elog(ERROR, "AlterTableDropColumn: heap_insert failed");
+
+        pfree(new_record);
+        pfree(new_record_nulls);
+    }
+    heap_endscan(scan);
+
+    heap_close(newrel, NoLock);
+    heap_close(oldrel, NoLock);
+
+    /*
+     * Move defaults over to the new table
+     */
+    defrel = heap_openr(AttrDefaultRelationName, AccessExclusiveLock);
+    defdsc = RelationGetDescr(defrel);
+
+    /* look for all entries referencing the old table */
+    ScanKeyEntryInitialize(&scankey, 0x0, Anum_pg_attrdef_adrelid, F_OIDEQ,
+                           ObjectIdGetDatum(oldrel_oid));
+    scan = heap_beginscan(defrel, false, SnapshotNow, 1, &scankey);
+    while(HeapTupleIsValid(tuple = heap_getnext(scan, false)))
+    {
+        HeapTuple   newtuple;
+        int2        attrnum;
+        Relation       irelations[Num_pg_attrdef_indices];
+
+        attrnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
+
+        /* remove the entry about the dropped column */
+        if (attrnum == dropattnum)
+        {
+            heap_delete(defrel, &tuple->t_self, NULL);
+            continue;
+        }
+
+        newtuple = heap_copytuple(tuple);
+
+        if (attrnum > dropattnum)
+            ((Form_pg_attrdef) GETSTRUCT(newtuple))->adnum--;
+
+        /* make it point to the new table */
+        ((Form_pg_attrdef) GETSTRUCT(newtuple))->adrelid = newrel_oid;
+        heap_update(defrel, &tuple->t_self, newtuple, NULL);
+
+        /* keep the system catalog indices current */
+        CatalogOpenIndices(Num_pg_attrdef_indices, Name_pg_attrdef_indices, irelations);
+        CatalogIndexInsert(irelations, Num_pg_attrdef_indices, defrel, newtuple);
+        CatalogCloseIndices(Num_pg_attrdef_indices, irelations);
+    }
+    heap_endscan(scan);
+    heap_close(defrel, NoLock);
+
+    CommandCounterIncrement();
+
+    /* make the old table disappear */
+    heap_drop_with_catalog(relationName);
+    CommandCounterIncrement();
+
+    /* set back original name */
+    TypeRename(tmpname, relationName);
+    renamerel(tmpname, relationName);
 }
 
 
 
+/*
+ * ALTER TABLE ADD CONSTRAINT
+ */
 void
 AlterTableAddConstraint(const char *relationName,
                         bool inh, Node *newConstraint)
 {
-    elog(NOTICE, "ALTER TABLE / ADD CONSTRAINT is not implemented");
+    elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented");
 }
 
 
 
-void AlterTableDropConstraint(const char *relationName,
-                              bool inh, const char *constrName,
-                              int behavior)
+/*
+ * ALTER TABLE DROP CONSTRAINT
+ */
+void
+AlterTableDropConstraint(const char *relationName,
+                         bool inh, const char *constrName,
+                         int behavior)
 {
-    elog(NOTICE, "ALTER TABLE / DROP CONSTRAINT is not implemented");
+    elog(ERROR, "ALTER TABLE / DROP CONSTRAINT is not implemented");
 }