ALTER TABLE SET/DROP NOT NULL, from Christopher Kings-Lynne.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Apr 2002 04:35:40 +0000 (04:35 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Apr 2002 04:35:40 +0000 (04:35 +0000)
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/release.sgml
src/backend/commands/command.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/include/commands/command.h
src/include/nodes/parsenodes.h
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index da6e91bf4d33b1109226c3e43df1a9c2513dc8c7..4423d8b2ed8b7609bf2b59825753b05686a71517 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.40 2002/03/06 20:42:38 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.41 2002/04/01 04:35:37 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -28,6 +28,8 @@ ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
 ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET DEFAULT <replaceable
     class="PARAMETER">value</replaceable> | DROP DEFAULT }
+ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
 ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
 ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
@@ -168,6 +170,9 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
    allow you to set or remove the default for the column. Note that defaults
    only apply to subsequent <command>INSERT</command> commands; they do not
    cause rows already in the table to change.
+   The <literal>ALTER COLUMN SET/DROP NOT NULL</literal> forms allow you to
+   change whether a column is marked to allow NULL values or to reject NULL
+   values.
    The <literal>ALTER COLUMN SET STATISTICS</literal> form allows you to
    set the statistics-gathering target for subsequent
    <xref linkend="sql-analyze" endterm="sql-analyze-title"> operations.
@@ -279,6 +284,17 @@ ALTER TABLE distributors RENAME TO suppliers;
    </programlisting>
   </para>
 
+  <para>
+   To add a NOT NULL constraint to a column:
+   <programlisting>
+ALTER TABLE distributors ALTER COLUMN street SET NOT NULL;
+   </programlisting>
+   To remove a NOT NULL constraint from a column:
+   <programlisting>
+ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;
+   </programlisting>
+  </para>
+
   <para> 
    To add a check constraint to a table:
    <programlisting>
index 5c2c859acb0f37b06114f01ffe53ec169d3237f8..c3e52dd948bfad776d115ff1a7dfef30ee219f2d 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.128 2002/03/25 21:24:08 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.129 2002/04/01 04:35:37 tgl Exp $
 -->
 
 <appendix id="release">
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+ALTER TABLE ALTER COLUMN SET/DROP NOT NULL
 EXPLAIN output comes out as a query result, not a NOTICE message
 DOMAINs (types that are constrained versions of base types)
 Access privileges on functions
index b79e65960f7b37fe8b80c7b1515c611e2fee7e7b..9bdeb3c7c7a0b812a5ae9d193f8c81b7e411c145 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.169 2002/03/31 06:26:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.170 2002/04/01 04:35:38 tgl Exp $
  *
  * NOTES
  *       The PerformAddAttribute() code, like most of the relation
@@ -403,7 +403,7 @@ AlterTableAddColumn(Oid myrelid,
 
        if (colDef->is_not_null)
                elog(ERROR, "Adding NOT NULL columns is not implemented."
-                        "\n\tAdd the column, then use ALTER TABLE ADD CONSTRAINT.");
+                        "\n\tAdd the column, then use ALTER TABLE ... SET NOT NULL.");
 
        pgclass = heap_openr(RelationRelationName, RowExclusiveLock);
 
@@ -527,6 +527,288 @@ AlterTableAddColumn(Oid myrelid,
        AlterTableCreateToastTable(myrelid, true);
 }
 
+/*
+ * ALTER TABLE ALTER COLUMN DROP NOT NULL
+ */
+void
+AlterTableAlterColumnDropNotNull(Oid myrelid,
+                                                                bool inh, const char *colName)
+{
+       Relation        rel;
+       HeapTuple       tuple;
+       AttrNumber      attnum;
+       Relation        attr_rel;
+       List            *indexoidlist;
+       List            *indexoidscan;
+
+       rel = heap_open(myrelid, AccessExclusiveLock);
+
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+                        RelationGetRelationName(rel));
+
+       if (!allowSystemTableMods
+               && IsSystemRelationName(RelationGetRelationName(rel)))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
+
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
+
+       /*
+        * Propagate to children if desired
+        */
+       if (inh)
+       {
+               List       *child,
+                                  *children;
+
+               /* this routine is actually in the planner */
+               children = find_all_inheritors(myrelid);
+
+               /*
+                * 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 == myrelid)
+                               continue;
+                       AlterTableAlterColumnDropNotNull(childrelid,
+                                                                                        false, colName);
+               }
+       }
+
+       /* -= now do the thing on this relation =- */
+
+       /*
+        * get the number of the attribute
+        */
+       tuple = SearchSysCache(ATTNAME,
+                                                  ObjectIdGetDatum(myrelid),
+                                                  PointerGetDatum(colName),
+                                                  0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
+
+       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
+       ReleaseSysCache(tuple);
+
+       /* Prevent them from altering a system attribute */
+       if (attnum < 0)
+               elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
+                        colName);
+
+       /* 
+        * Check that the attribute is not in a primary key
+        */
+               
+       /* Loop over all indices on the relation */
+       indexoidlist = RelationGetIndexList(rel);
+
+       foreach(indexoidscan, indexoidlist)
+       {
+               Oid             indexoid = lfirsti(indexoidscan);
+               HeapTuple       indexTuple;
+               Form_pg_index   indexStruct;
+               int             i;
+
+               indexTuple = SearchSysCache(INDEXRELID,
+                                                                       ObjectIdGetDatum(indexoid),
+                                                                       0, 0, 0);
+               if (!HeapTupleIsValid(indexTuple))
+                       elog(ERROR, "ALTER TABLE: Index %u not found",
+                                indexoid);
+               indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
+
+               /* If the index is not a primary key, skip the check */
+               if (indexStruct->indisprimary)
+               {
+                       /*
+                        * Loop over each attribute in the primary key and
+                        * see if it matches the to-be-altered attribute
+                        */
+                       for (i = 0; i < INDEX_MAX_KEYS &&
+                                        indexStruct->indkey[i] != InvalidAttrNumber; i++)
+                       {
+                               if (indexStruct->indkey[i] == attnum)
+                                       elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName);
+                       }
+               }
+
+               ReleaseSysCache(indexTuple);
+       }
+
+       freeList(indexoidlist);
+
+       /*
+        * Okay, actually perform the catalog change
+        */
+       attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy(ATTNAME,
+                                                          ObjectIdGetDatum(myrelid),
+                                                          PointerGetDatum(colName),
+                                                          0, 0);
+       if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
+
+       ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
+
+       simple_heap_update(attr_rel, &tuple->t_self, tuple);
+
+       /* keep the system catalog indices current */
+       if (RelationGetForm(attr_rel)->relhasindex)
+       {
+               Relation        idescs[Num_pg_attr_indices];
+
+               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+               CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
+               CatalogCloseIndices(Num_pg_attr_indices, idescs);
+       }
+
+       heap_close(attr_rel, RowExclusiveLock);
+
+       heap_close(rel, NoLock);
+}
+
+/*
+ * ALTER TABLE ALTER COLUMN SET NOT NULL
+ */
+void
+AlterTableAlterColumnSetNotNull(Oid myrelid,
+                                                               bool inh, const char *colName)
+{
+       Relation        rel;
+       HeapTuple       tuple;
+       AttrNumber      attnum;
+       Relation        attr_rel;
+       HeapScanDesc    scan;
+       TupleDesc       tupdesc;
+
+       rel = heap_open(myrelid, AccessExclusiveLock);
+
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+                        RelationGetRelationName(rel));
+
+       if (!allowSystemTableMods
+               && IsSystemRelationName(RelationGetRelationName(rel)))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
+
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
+
+       /*
+        * Propagate to children if desired
+        */
+       if (inh)
+       {
+               List       *child,
+                                  *children;
+
+               /* this routine is actually in the planner */
+               children = find_all_inheritors(myrelid);
+
+               /*
+                * 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 == myrelid)
+                               continue;
+                       AlterTableAlterColumnSetNotNull(childrelid,
+                                                                                       false, colName);
+               }
+       }
+
+       /* -= now do the thing on this relation =- */
+
+       /*
+        * get the number of the attribute
+        */
+       tuple = SearchSysCache(ATTNAME,
+                                                  ObjectIdGetDatum(myrelid),
+                                                  PointerGetDatum(colName),
+                                                  0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
+
+       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
+       ReleaseSysCache(tuple);
+
+       /* Prevent them from altering a system attribute */
+       if (attnum < 0)
+               elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
+                        colName);
+
+       /*
+        * Perform a scan to ensure that there are no NULL
+        * values already in the relation
+        */
+       tupdesc = RelationGetDescr(rel);
+
+       scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
+
+       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+       {
+               Datum           d;
+               bool            isnull;
+
+               d = heap_getattr(tuple, attnum, tupdesc, &isnull);
+
+               if (isnull)
+                       elog(ERROR, "ALTER TABLE: Attribute \"%s\" contains NULL values",
+                                colName);
+       }
+
+       heap_endscan(scan);
+
+       /*
+        * Okay, actually perform the catalog change
+        */
+       attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy(ATTNAME,
+                                                          ObjectIdGetDatum(myrelid),
+                                                          PointerGetDatum(colName),
+                                                          0, 0);
+       if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
+
+       ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
+
+       simple_heap_update(attr_rel, &tuple->t_self, tuple);
+
+       /* keep the system catalog indices current */
+       if (RelationGetForm(attr_rel)->relhasindex)
+       {
+               Relation        idescs[Num_pg_attr_indices];
+
+               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+               CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
+               CatalogCloseIndices(Num_pg_attr_indices, idescs);
+       }
+
+       heap_close(attr_rel, RowExclusiveLock);
+
+       heap_close(rel, NoLock);
+}
+
 
 /*
  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
@@ -538,7 +820,7 @@ AlterTableAlterColumnDefault(Oid myrelid,
 {
        Relation        rel;
        HeapTuple       tuple;
-       int16           attnum;
+       AttrNumber      attnum;
 
        rel = heap_open(myrelid, AccessExclusiveLock);
 
@@ -552,7 +834,7 @@ AlterTableAlterColumnDefault(Oid myrelid,
                         RelationGetRelationName(rel));
 
        if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\" permission denied",
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
                         RelationGetRelationName(rel));
 
        /*
@@ -620,44 +902,36 @@ AlterTableAlterColumnDefault(Oid myrelid,
        {
                /* DROP DEFAULT */
                Relation        attr_rel;
-               ScanKeyData scankeys[3];
-               HeapScanDesc scan;
 
+               /* Fix the pg_attribute row */
                attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
-               ScanKeyEntryInitialize(&scankeys[0], 0x0,
-                                                          Anum_pg_attribute_attrelid, F_OIDEQ,
-                                                          ObjectIdGetDatum(myrelid));
-               ScanKeyEntryInitialize(&scankeys[1], 0x0,
-                                                          Anum_pg_attribute_attnum, F_INT2EQ,
-                                                          Int16GetDatum(attnum));
-               ScanKeyEntryInitialize(&scankeys[2], 0x0,
-                                                          Anum_pg_attribute_atthasdef, F_BOOLEQ,
-                                                          BoolGetDatum(true));
-
-               scan = heap_beginscan(attr_rel, false, SnapshotNow, 3, scankeys);
-               AssertState(scan != NULL);
-
-               if (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
-               {
-                       HeapTuple       newtuple;
-                       Relation        irelations[Num_pg_attr_indices];
 
-                       /* update to false */
-                       newtuple = heap_copytuple(tuple);
-                       ((Form_pg_attribute) GETSTRUCT(newtuple))->atthasdef = FALSE;
-                       simple_heap_update(attr_rel, &tuple->t_self, newtuple);
+               tuple = SearchSysCacheCopy(ATTNAME,
+                                                                  ObjectIdGetDatum(myrelid),
+                                                                  PointerGetDatum(colName),
+                                                                  0, 0);
+               if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
+                       elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                                RelationGetRelationName(rel), colName);
 
-                       /* 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_attr_indices, irelations);
+               ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = FALSE;
 
-                       /* get rid of actual default definition */
-                       drop_default(myrelid, attnum);
+               simple_heap_update(attr_rel, &tuple->t_self, tuple);
+
+               /* keep the system catalog indices current */
+               if (RelationGetForm(attr_rel)->relhasindex)
+               {
+                       Relation        idescs[Num_pg_attr_indices];
+
+                       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+                       CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
+                       CatalogCloseIndices(Num_pg_attr_indices, idescs);
                }
 
-               heap_endscan(scan);
-               heap_close(attr_rel, NoLock);
+               heap_close(attr_rel, RowExclusiveLock);
+
+               /* get rid of actual default definition in pg_attrdef */
+               drop_default(myrelid, attnum);
        }
 
        heap_close(rel, NoLock);
@@ -722,7 +996,7 @@ AlterTableAlterColumnFlags(Oid myrelid,
                         RelationGetRelationName(rel));
 
        if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\" permission denied",
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
                         RelationGetRelationName(rel));
 
        /*
@@ -1011,7 +1285,7 @@ AlterTableDropColumn(Oid myrelid,
                         RelationGetRelationName(rel));
 
        if (!allowSystemTableMods
-               && IsSystemRelationName(RelationGetRelationName(rel))
+               && IsSystemRelationName(RelationGetRelationName(rel)))
                elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
                         RelationGetRelationName(rel));
 
@@ -1022,7 +1296,8 @@ AlterTableDropColumn(Oid myrelid,
         * normally, only the owner of a class can change its schema.
         */
        if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: permission denied");
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
 
        heap_close(rel, NoLock);        /* close rel but keep lock! */
 
@@ -1670,7 +1945,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
                         RelationGetRelationName(rel));
 
        if (!pg_class_ownercheck(relOid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: permission denied");
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
 
        /*
         * lock the pg_class tuple for update (is that really needed?)
index 64cc3d60ca7517b7fd4c19be2bcb2eec19002a09..4e2c2e70336b8163d672c0b8f5fcf86b8c54360d 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.298 2002/04/01 03:34:25 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.299 2002/04/01 04:35:38 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1122,6 +1122,24 @@ AlterTableStmt:
                                        n->def = $7;
                                        $$ = (Node *)n;
                                }
+/* ALTER TABLE <relation> ALTER [COLUMN] <colname> DROP NOT NULL */
+                | ALTER TABLE relation_expr ALTER opt_column ColId DROP NOT NULL_P
+                                {
+                                        AlterTableStmt *n = makeNode(AlterTableStmt);
+                                        n->subtype = 'N';
+                                        n->relation = $3;
+                                        n->name = $6;
+                                        $$ = (Node *)n;
+                                }
+/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET NOT NULL */
+                | ALTER TABLE relation_expr ALTER opt_column ColId SET NOT NULL_P
+                                {
+                                        AlterTableStmt *n = makeNode(AlterTableStmt);
+                                        n->subtype = 'O';
+                                        n->relation = $3;
+                                        n->name = $6;
+                                        $$ = (Node *)n;
+                                }
 /* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STATISTICS <Iconst> */
                | ALTER TABLE relation_expr ALTER opt_column ColId SET STATISTICS Iconst
                                {
index 6043177cb469a1313765f5f3363d27d80b232d76..bd5e31666ca9c70c8489fccbcd2b98303b551d95 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.144 2002/03/31 07:49:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.145 2002/04/01 04:35:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -437,6 +437,16 @@ ProcessUtility(Node *parsetree,
                                                                                                         stmt->name,
                                                                                                         stmt->def);
                                                break;
+                                       case 'N':       /* ALTER COLUMN DROP NOT NULL */
+                                               AlterTableAlterColumnDropNotNull(RangeVarGetRelid(stmt->relation, false),
+                                                                               interpretInhOption(stmt->relation->inhOpt),
+                                                                                                       stmt->name);
+                                               break;
+                                       case 'O':       /* ALTER COLUMN SET NOT NULL */
+                                               AlterTableAlterColumnSetNotNull(RangeVarGetRelid(stmt->relation, false),
+                                                                               interpretInhOption(stmt->relation->inhOpt),
+                                                                                                       stmt->name);
+                                               break;
                                        case 'S':       /* ALTER COLUMN STATISTICS */
                                        case 'M':   /* ALTER COLUMN STORAGE */
                                                /*
index 73c33ac3c7eb033121073a67850624120e7c2001..cfb8f20890eb7117e7aa0099ee9db56ff2ab66f9 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: command.h,v 1.36 2002/03/29 19:06:21 tgl Exp $
+ * $Id: command.h,v 1.37 2002/04/01 04:35:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,6 +45,12 @@ extern void AlterTableAddColumn(Oid myrelid, bool inherits, ColumnDef *colDef);
 extern void AlterTableAlterColumnDefault(Oid myrelid, bool inh,
                                                                                 const char *colName, Node *newDefault);
 
+extern void AlterTableAlterColumnDropNotNull(Oid myrelid,
+                                                        bool inh, const char *colName);
+
+extern void AlterTableAlterColumnSetNotNull(Oid myrelid,
+                                                        bool inh, const char *colName);
+
 extern void AlterTableAlterColumnFlags(Oid myrelid,
                                                                           bool inh, const char *colName,
                                                                           Node *flagValue, const char *flagType);
index 4e08940f7b5732b1d912501e056548a11c8506a9..4cccc6a7d2a7536aea0bfc080eff403849a09176 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: parsenodes.h,v 1.166 2002/03/29 19:06:23 tgl Exp $
+ * $Id: parsenodes.h,v 1.167 2002/04/01 04:35:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -728,6 +728,8 @@ typedef struct AlterTableStmt
        char            subtype;                /*------------
                                                                 *      A = add column
                                                                 *      T = alter column default
+                                                                *      N = alter column drop not null
+                                                                *      O = alter column set not null
                                                                 *      S = alter column statistics
                                                                 *  M = alter column storage
                                                                 *      D = drop column
index ae5a7f4f635fbc99d6a1cf17086d614ae7241e81..6e83edd65c9ea508444b0fad286e0802c4fd95c7 100644 (file)
@@ -578,3 +578,74 @@ ERROR:  Cannot insert a duplicate key into unique index atacc1_pkey
 insert into atacc1 (test2, test) values (1, NULL);
 ERROR:  ExecAppend: Fail to add null value in not null attribute test
 drop table atacc1;
+-- alter table / alter column [set/drop] not null tests
+-- try altering system catalogs, should fail
+alter table pg_class alter column relname drop not null;
+ERROR:  ALTER TABLE: relation "pg_class" is a system catalog
+alter table pg_class alter relname set not null;
+ERROR:  ALTER TABLE: relation "pg_class" is a system catalog
+-- try altering non-existent table, should fail
+alter table foo alter column bar set not null;
+ERROR:  Relation "foo" does not exist
+alter table foo alter column bar drop not null;
+ERROR:  Relation "foo" does not exist
+-- test setting columns to null and not null and vice versa
+-- test checking for null values and primary key
+create table atacc1 (test int not null);
+alter table atacc1 add constraint "atacc1_pkey" primary key (test);
+NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc1_pkey' for table 'atacc1'
+alter table atacc1 alter column test drop not null;
+ERROR:  ALTER TABLE: Attribute "test" is in a primary key
+drop index atacc1_pkey;
+alter table atacc1 alter column test drop not null;
+insert into atacc1 values (null);
+alter table atacc1 alter test set not null;
+ERROR:  ALTER TABLE: Attribute "test" contains NULL values
+delete from atacc1;
+alter table atacc1 alter test set not null;
+-- try altering a non-existent column, should fail
+alter table atacc1 alter bar set not null;
+ERROR:  ALTER TABLE: relation "atacc1" has no column "bar"
+alter table atacc1 alter bar drop not null;
+ERROR:  ALTER TABLE: relation "atacc1" has no column "bar"
+-- try altering the oid column, should fail
+alter table atacc1 alter oid set not null;
+ERROR:  ALTER TABLE: Cannot alter system attribute "oid"
+alter table atacc1 alter oid drop not null;
+ERROR:  ALTER TABLE: Cannot alter system attribute "oid"
+-- try creating a view and altering that, should fail
+create view myview as select * from atacc1;
+alter table myview alter column test drop not null;
+ERROR:  ALTER TABLE: relation "myview" is not a table
+alter table myview alter column test set not null;
+ERROR:  ALTER TABLE: relation "myview" is not a table
+drop view myview;
+drop table atacc1;
+-- test inheritance
+create table parent (a int);
+create table child (b varchar(255)) inherits (parent);
+alter table parent alter a set not null;
+insert into parent values (NULL);
+ERROR:  ExecAppend: Fail to add null value in not null attribute a
+insert into child (a, b) values (NULL, 'foo');
+ERROR:  ExecAppend: Fail to add null value in not null attribute a
+alter table parent alter a drop not null;
+insert into parent values (NULL);
+insert into child (a, b) values (NULL, 'foo');
+alter table only parent alter a set not null;
+ERROR:  ALTER TABLE: Attribute "a" contains NULL values
+alter table child alter a set not null;
+ERROR:  ALTER TABLE: Attribute "a" contains NULL values
+delete from parent;
+alter table only parent alter a set not null;
+insert into parent values (NULL);
+ERROR:  ExecAppend: Fail to add null value in not null attribute a
+alter table child alter a set not null;
+insert into child (a, b) values (NULL, 'foo');
+ERROR:  ExecAppend: Fail to add null value in not null attribute a
+delete from child;
+alter table child alter a set not null;
+insert into child (a, b) values (NULL, 'foo');
+ERROR:  ExecAppend: Fail to add null value in not null attribute a
+drop table child;
+drop table parent;
index 36d9be96d2b9be4e1edb1634e065890cef3b0981..2319372b596b54faa273b29f7d9cc49e01816594 100644 (file)
@@ -452,3 +452,64 @@ insert into atacc1 (test2, test) values (3, 3);
 insert into atacc1 (test2, test) values (2, 3);
 insert into atacc1 (test2, test) values (1, NULL);
 drop table atacc1;
+
+-- alter table / alter column [set/drop] not null tests
+-- try altering system catalogs, should fail
+alter table pg_class alter column relname drop not null;
+alter table pg_class alter relname set not null;
+
+-- try altering non-existent table, should fail
+alter table foo alter column bar set not null;
+alter table foo alter column bar drop not null;
+
+-- test setting columns to null and not null and vice versa
+-- test checking for null values and primary key
+create table atacc1 (test int not null);
+alter table atacc1 add constraint "atacc1_pkey" primary key (test);
+alter table atacc1 alter column test drop not null;
+drop index atacc1_pkey;
+alter table atacc1 alter column test drop not null;
+insert into atacc1 values (null);
+alter table atacc1 alter test set not null;
+delete from atacc1;
+alter table atacc1 alter test set not null;
+
+-- try altering a non-existent column, should fail
+alter table atacc1 alter bar set not null;
+alter table atacc1 alter bar drop not null;
+
+-- try altering the oid column, should fail
+alter table atacc1 alter oid set not null;
+alter table atacc1 alter oid drop not null;
+
+-- try creating a view and altering that, should fail
+create view myview as select * from atacc1;
+alter table myview alter column test drop not null;
+alter table myview alter column test set not null;
+drop view myview;
+
+drop table atacc1;
+
+-- test inheritance
+create table parent (a int);
+create table child (b varchar(255)) inherits (parent);
+
+alter table parent alter a set not null;
+insert into parent values (NULL);
+insert into child (a, b) values (NULL, 'foo');
+alter table parent alter a drop not null;
+insert into parent values (NULL);
+insert into child (a, b) values (NULL, 'foo');
+alter table only parent alter a set not null;
+alter table child alter a set not null;
+delete from parent;
+alter table only parent alter a set not null;
+insert into parent values (NULL);
+alter table child alter a set not null;
+insert into child (a, b) values (NULL, 'foo');
+delete from child;
+alter table child alter a set not null;
+insert into child (a, b) values (NULL, 'foo');
+drop table child;
+drop table parent;
+