Commit 9b77f619 authored by Tom Lane's avatar Tom Lane

ALTER TABLE SET/DROP NOT NULL, from Christopher Kings-Lynne.

parent 838fe25a
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -28,6 +28,8 @@ ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ] ...@@ -28,6 +28,8 @@ ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
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 ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET DEFAULT <replaceable
class="PARAMETER">value</replaceable> | DROP DEFAULT } 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 TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ] ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
...@@ -168,6 +170,9 @@ ALTER TABLE <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 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 only apply to subsequent <command>INSERT</command> commands; they do not
cause rows already in the table to change. 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 The <literal>ALTER COLUMN SET STATISTICS</literal> form allows you to
set the statistics-gathering target for subsequent set the statistics-gathering target for subsequent
<xref linkend="sql-analyze" endterm="sql-analyze-title"> operations. <xref linkend="sql-analyze" endterm="sql-analyze-title"> operations.
...@@ -279,6 +284,17 @@ ALTER TABLE distributors RENAME TO suppliers; ...@@ -279,6 +284,17 @@ ALTER TABLE distributors RENAME TO suppliers;
</programlisting> </programlisting>
</para> </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> <para>
To add a check constraint to a table: To add a check constraint to a table:
<programlisting> <programlisting>
......
<!-- <!--
$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"> <appendix id="release">
...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without ...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
worries about funny characters. worries about funny characters.
--> -->
<literallayout><![CDATA[ <literallayout><![CDATA[
ALTER TABLE ALTER COLUMN SET/DROP NOT NULL
EXPLAIN output comes out as a query result, not a NOTICE message EXPLAIN output comes out as a query result, not a NOTICE message
DOMAINs (types that are constrained versions of base types) DOMAINs (types that are constrained versions of base types)
Access privileges on functions Access privileges on functions
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* The PerformAddAttribute() code, like most of the relation * The PerformAddAttribute() code, like most of the relation
...@@ -403,7 +403,7 @@ AlterTableAddColumn(Oid myrelid, ...@@ -403,7 +403,7 @@ AlterTableAddColumn(Oid myrelid,
if (colDef->is_not_null) if (colDef->is_not_null)
elog(ERROR, "Adding NOT NULL columns is not implemented." 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); pgclass = heap_openr(RelationRelationName, RowExclusiveLock);
...@@ -527,6 +527,288 @@ AlterTableAddColumn(Oid myrelid, ...@@ -527,6 +527,288 @@ AlterTableAddColumn(Oid myrelid,
AlterTableCreateToastTable(myrelid, true); 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 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
...@@ -538,7 +820,7 @@ AlterTableAlterColumnDefault(Oid myrelid, ...@@ -538,7 +820,7 @@ AlterTableAlterColumnDefault(Oid myrelid,
{ {
Relation rel; Relation rel;
HeapTuple tuple; HeapTuple tuple;
int16 attnum; AttrNumber attnum;
rel = heap_open(myrelid, AccessExclusiveLock); rel = heap_open(myrelid, AccessExclusiveLock);
...@@ -552,7 +834,7 @@ AlterTableAlterColumnDefault(Oid myrelid, ...@@ -552,7 +834,7 @@ AlterTableAlterColumnDefault(Oid myrelid,
RelationGetRelationName(rel)); RelationGetRelationName(rel));
if (!pg_class_ownercheck(myrelid, GetUserId())) if (!pg_class_ownercheck(myrelid, GetUserId()))
elog(ERROR, "ALTER TABLE: \"%s\" permission denied", elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
RelationGetRelationName(rel)); RelationGetRelationName(rel));
/* /*
...@@ -620,44 +902,36 @@ AlterTableAlterColumnDefault(Oid myrelid, ...@@ -620,44 +902,36 @@ AlterTableAlterColumnDefault(Oid myrelid,
{ {
/* DROP DEFAULT */ /* DROP DEFAULT */
Relation attr_rel; Relation attr_rel;
ScanKeyData scankeys[3];
HeapScanDesc scan;
/* Fix the pg_attribute row */
attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock); 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); tuple = SearchSysCacheCopy(ATTNAME,
AssertState(scan != NULL); 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);
if (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = FALSE;
{
HeapTuple newtuple;
Relation irelations[Num_pg_attr_indices];
/* update to false */ simple_heap_update(attr_rel, &tuple->t_self, tuple);
newtuple = heap_copytuple(tuple);
((Form_pg_attribute) GETSTRUCT(newtuple))->atthasdef = FALSE;
simple_heap_update(attr_rel, &tuple->t_self, newtuple);
/* keep the system catalog indices current */ /* keep the system catalog indices current */
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations); if (RelationGetForm(attr_rel)->relhasindex)
CatalogIndexInsert(irelations, Num_pg_attr_indices, attr_rel, newtuple); {
CatalogCloseIndices(Num_pg_attr_indices, irelations); Relation idescs[Num_pg_attr_indices];
/* get rid of actual default definition */ CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
drop_default(myrelid, attnum); CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
CatalogCloseIndices(Num_pg_attr_indices, idescs);
} }
heap_endscan(scan); heap_close(attr_rel, RowExclusiveLock);
heap_close(attr_rel, NoLock);
/* get rid of actual default definition in pg_attrdef */
drop_default(myrelid, attnum);
} }
heap_close(rel, NoLock); heap_close(rel, NoLock);
...@@ -722,7 +996,7 @@ AlterTableAlterColumnFlags(Oid myrelid, ...@@ -722,7 +996,7 @@ AlterTableAlterColumnFlags(Oid myrelid,
RelationGetRelationName(rel)); RelationGetRelationName(rel));
if (!pg_class_ownercheck(myrelid, GetUserId())) if (!pg_class_ownercheck(myrelid, GetUserId()))
elog(ERROR, "ALTER TABLE: \"%s\" permission denied", elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
RelationGetRelationName(rel)); RelationGetRelationName(rel));
/* /*
...@@ -1011,7 +1285,7 @@ AlterTableDropColumn(Oid myrelid, ...@@ -1011,7 +1285,7 @@ AlterTableDropColumn(Oid myrelid,
RelationGetRelationName(rel)); RelationGetRelationName(rel));
if (!allowSystemTableMods if (!allowSystemTableMods
&& IsSystemRelationName(RelationGetRelationName(rel)) && IsSystemRelationName(RelationGetRelationName(rel)))
elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog", elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
RelationGetRelationName(rel)); RelationGetRelationName(rel));
...@@ -1022,7 +1296,8 @@ AlterTableDropColumn(Oid myrelid, ...@@ -1022,7 +1296,8 @@ AlterTableDropColumn(Oid myrelid,
* normally, only the owner of a class can change its schema. * normally, only the owner of a class can change its schema.
*/ */
if (!pg_class_ownercheck(myrelid, GetUserId())) 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! */ heap_close(rel, NoLock); /* close rel but keep lock! */
...@@ -1670,7 +1945,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent) ...@@ -1670,7 +1945,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
RelationGetRelationName(rel)); RelationGetRelationName(rel));
if (!pg_class_ownercheck(relOid, GetUserId())) 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?) * lock the pg_class tuple for update (is that really needed?)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -1122,6 +1122,24 @@ AlterTableStmt: ...@@ -1122,6 +1122,24 @@ AlterTableStmt:
n->def = $7; n->def = $7;
$$ = (Node *)n; $$ = (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> ALTER [COLUMN] <colname> SET STATISTICS <Iconst> */
| ALTER TABLE relation_expr ALTER opt_column ColId SET STATISTICS Iconst | ALTER TABLE relation_expr ALTER opt_column ColId SET STATISTICS Iconst
{ {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -437,6 +437,16 @@ ProcessUtility(Node *parsetree,
stmt->name, stmt->name,
stmt->def); stmt->def);
break; 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 'S': /* ALTER COLUMN STATISTICS */
case 'M': /* ALTER COLUMN STORAGE */ case 'M': /* ALTER COLUMN STORAGE */
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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); ...@@ -45,6 +45,12 @@ extern void AlterTableAddColumn(Oid myrelid, bool inherits, ColumnDef *colDef);
extern void AlterTableAlterColumnDefault(Oid myrelid, bool inh, extern void AlterTableAlterColumnDefault(Oid myrelid, bool inh,
const char *colName, Node *newDefault); 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, extern void AlterTableAlterColumnFlags(Oid myrelid,
bool inh, const char *colName, bool inh, const char *colName,
Node *flagValue, const char *flagType); Node *flagValue, const char *flagType);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -728,6 +728,8 @@ typedef struct AlterTableStmt
char subtype; /*------------ char subtype; /*------------
* A = add column * A = add column
* T = alter column default * T = alter column default
* N = alter column drop not null
* O = alter column set not null
* S = alter column statistics * S = alter column statistics
* M = alter column storage * M = alter column storage
* D = drop column * D = drop column
......
...@@ -578,3 +578,74 @@ ERROR: Cannot insert a duplicate key into unique index atacc1_pkey ...@@ -578,3 +578,74 @@ ERROR: Cannot insert a duplicate key into unique index atacc1_pkey
insert into atacc1 (test2, test) values (1, NULL); insert into atacc1 (test2, test) values (1, NULL);
ERROR: ExecAppend: Fail to add null value in not null attribute test ERROR: ExecAppend: Fail to add null value in not null attribute test
drop table atacc1; 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;
...@@ -452,3 +452,64 @@ insert into atacc1 (test2, test) values (3, 3); ...@@ -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 (2, 3);
insert into atacc1 (test2, test) values (1, NULL); insert into atacc1 (test2, test) values (1, NULL);
drop table atacc1; 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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment