Index: java/engine/org/apache/derby/impl/sql/compile/TableElementList.java =================================================================== --- java/engine/org/apache/derby/impl/sql/compile/TableElementList.java (revision 156271) +++ java/engine/org/apache/derby/impl/sql/compile/TableElementList.java (working copy) @@ -33,7 +33,6 @@ import org.apache.derby.iapi.sql.dictionary.DataDictionary; import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor; import org.apache.derby.iapi.sql.dictionary.TableDescriptor; -import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor; import org.apache.derby.iapi.sql.depend.DependencyManager; import org.apache.derby.iapi.sql.depend.ProviderInfo; @@ -47,6 +46,7 @@ import org.apache.derby.impl.sql.execute.IndexConstantAction; import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList; +import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor; import org.apache.derby.catalog.UUID; @@ -62,8 +62,8 @@ public class TableElementList extends QueryTreeNodeVector { - int numColumns; - TableDescriptor td; + private int numColumns; + private TableDescriptor td; /** * Add a TableElementNode to this TableElementList @@ -173,7 +173,7 @@ checkForDuplicateColumns(ddlStmt, columnHT, cdn.getColumnName()); cdn.checkUserType(td); cdn.bindAndValidateDefault(dd, td); - + cdn.validateAutoincrement(dd, td, tableType); if (tableElement instanceof ModifyColumnNode) @@ -262,7 +262,7 @@ String dropSchemaName = cdn.getDropSchemaName(); - SchemaDescriptor sd = dropSchemaName == null ? td.getSchemaDescriptor() : + SchemaDescriptor sd = dropSchemaName == null ? td.getSchemaDescriptor() : getSchemaDescriptor(dropSchemaName); ConstraintDescriptor cd = @@ -280,22 +280,33 @@ } } - /* For primary/unique/unique keys, verify that the constraint's column - * list contains valid columns and does not contain any duplicates - * (Also, all columns in a primary key will be set to non-null, - but only in Cloudscape mode. SQL and DB2 require explict NOT NULL. - */ - if (cdn.hasPrimaryKeyConstraint() || - cdn.hasForeignKeyConstraint() || - cdn.hasUniqueKeyConstraint()) - { - verifyUniqueColumnList(ddlStmt, cdn); - /* Raise error if primary or unique key columns can be nullable. */ - if (cdn.hasPrimaryKeyConstraint() || cdn.hasUniqueKeyConstraint()) - { - setColumnListToNotNull(cdn, td); - } - } + if (cdn.hasPrimaryKeyConstraint()) + { + // for PRIMARY KEY, check that columns are unique + verifyUniqueColumnList(ddlStmt, cdn); + + if (td == null) + { + // in CREATE TABLE so set PRIMARY KEY columns to NOT NULL + setColumnListToNotNull(cdn); + } + else + { + // in ALTER TABLE so raise error if any columns are nullable + checkForNullColumns(cdn, td); + } + } + else if (cdn.hasUniqueKeyConstraint()) + { + // for UNIQUE, check that columns are unique and NOT NULL + verifyUniqueColumnList(ddlStmt, cdn); + checkForNullColumns(cdn, td); + } + else if (cdn.hasForeignKeyConstraint()) + { + // for FOREIGN KEY, check that columns are unique + verifyUniqueColumnList(ddlStmt, cdn); + } } /* Can have only one autoincrement column in DB2 mode */ @@ -303,8 +314,8 @@ throw StandardException.newException(SQLState.LANG_MULTIPLE_AUTOINCREMENT_COLUMNS); } - - /** + + /** * Count the number of constraints of the specified type. * * @param constraintType The constraint type to search for. @@ -872,62 +883,78 @@ } /** - * Set all columns in that appear in a primary/unique key constraint in a create - * table statement to NOT NULL in Cloudscape mode and raises an error in DB2 mode. + * Set all columns in that appear in a PRIMARY KEY constraint in a CREATE TABLE statement to NOT NULL. * - * @param cdn The ConstraintDefinitionNode - * @param td TableDescriptor for the table + * @param cdn The ConstraintDefinitionNode for a PRIMARY KEY constraint */ - private void setColumnListToNotNull(ConstraintDefinitionNode cdn, TableDescriptor td) - throws StandardException + private void setColumnListToNotNull(ConstraintDefinitionNode cdn) { ResultColumnList rcl = cdn.getColumnList(); int rclSize = rcl.size(); for (int index = 0; index < rclSize; index++) { String colName = ((ResultColumn) rcl.elementAt(index)).getName(); + DataTypeDescriptor dtd = getColumnDataTypeDescriptor(colName); + dtd.setNullability(false); + } + } - /* For ALTER TABLE ADD CONSTRAINT, make sure columns are not nullable for - * primary and unique constraints. - */ - if (td != null && cdn instanceof ConstraintDefinitionNode) - { - ColumnDescriptor cd = td.getColumnDescriptor(colName); - if (cd != null && cd.getType().isNullable()) - throw StandardException.newException(SQLState.LANG_DB2_ADD_UNIQUE_OR_PRIMARY_KEY_ON_NULL_COLS, colName); - } - setColumnToNotNull(colName); - } - } + private void checkForNullColumns(ConstraintDefinitionNode cdn, TableDescriptor td) throws StandardException + { + ResultColumnList rcl = cdn.getColumnList(); + int rclSize = rcl.size(); + for (int index = 0; index < rclSize; index++) + { + String colName = ((ResultColumn) rcl.elementAt(index)).getName(); + DataTypeDescriptor dtd; + if (td == null) + { + dtd = getColumnDataTypeDescriptor(colName); + } + else + { + dtd = getColumnDataTypeDescriptor(colName, td); + } + // todo dtd may be null if the column does not exist, we should check that first + if (dtd != null && dtd.isNullable()) + { + throw StandardException.newException(SQLState.LANG_DB2_ADD_UNIQUE_OR_PRIMARY_KEY_ON_NULL_COLS, colName); + } + } + } - /** - * Set a column that appears in a primary/unique key constraint in - * a create table statement to NOT NULL (but only in Cloudscape mode). - * - * @param colName The column name - */ - private void setColumnToNotNull(String colName) throws StandardException - { - int size = size(); + private DataTypeDescriptor getColumnDataTypeDescriptor(String colName) + { + int size = size(); - for (int index = 0; index < size; index++) - { - TableElementNode tableElement = (TableElementNode) elementAt(index); + for (int index = 0; index < size; index++) + { + TableElementNode tableElement = (TableElementNode) elementAt(index); - if (tableElement instanceof ColumnDefinitionNode) - { - ColumnDefinitionNode cdn = (ColumnDefinitionNode) tableElement; - if (colName.equals(cdn.getColumnName())) - { - DataTypeDescriptor dtd = cdn.getDataTypeServices(); + if (tableElement instanceof ColumnDefinitionNode) + { + ColumnDefinitionNode cdn = (ColumnDefinitionNode) tableElement; + if (colName.equals(cdn.getColumnName())) + { + return cdn.getDataTypeServices(); + } + } + } + return null; + } - if (dtd.isNullable()) - throw StandardException.newException(SQLState.LANG_DB2_ADD_UNIQUE_OR_PRIMARY_KEY_ON_NULL_COLS, colName); - } - } - } - } + private DataTypeDescriptor getColumnDataTypeDescriptor(String colName, TableDescriptor td) + { + // check existing columns + ColumnDescriptor cd = td.getColumnDescriptor(colName); + if (cd != null) + { + return cd.getType(); + } + // check for new columns + return getColumnDataTypeDescriptor(colName); + } /** * Determine whether or not the parameter matches a column name in this list. Index: java/testing/org/apache/derbyTesting/functionTests/tests/lang/nulls.sql =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/lang/nulls.sql (revision 156271) +++ java/testing/org/apache/derbyTesting/functionTests/tests/lang/nulls.sql (working copy) @@ -80,10 +80,6 @@ -- give error create table a1(ac1 int null, ac2 int not null, primary key(ac1,ac2)); --- should fail because --- not null must explicitly be specified for columns that have primary keys -create table a1(ac1 int, ac2 int not null, primary key(ac1,ac2)); - -- say null twice should fail create table a2(ac1 int null null); Index: java/testing/org/apache/derbyTesting/functionTests/tests/lang/primarykey.sql =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/lang/primarykey.sql (revision 156271) +++ java/testing/org/apache/derbyTesting/functionTests/tests/lang/primarykey.sql (working copy) @@ -49,19 +49,21 @@ create table neg2(c1 int null constraint asdf primary key); create table neg2(c1 int null, c2 int, constraint asdf primary key(c1, c2)); --- verify that you can not create a primary key column with default null --- a primary key column can only be create if the column is explicitly not null -create table neg1 (c1 int default null primary key); -create table neg1 (c1 int default null, c2 int not null, primary key(c2, c1)); - -- test that a unique key can be not be explicitly nullable -create table neg1(c1 int null unique); -create table neg1(c1 int null, c2 int, constraint asdf unique(c1)); +create table neg1(c1 int unique); +create table neg1(c1 int, c2 int, constraint asdf unique(c1)); -- positive tests +-- verify that a primary key implies not null +create table pos1 (c1 int primary key); +insert into pos1(c1) values(1); +insert into pos1(c1) values(1); +insert into pos1(c1) values(null); +drop table pos1; + -- verify that a unique key can not contain nulls create table pos1 (c1 int not null unique, c2 int); insert into pos1 (c1) values(null); Index: java/testing/org/apache/derbyTesting/functionTests/tests/lang/db2Compatibility.sql =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/lang/db2Compatibility.sql (revision 156271) +++ java/testing/org/apache/derbyTesting/functionTests/tests/lang/db2Compatibility.sql (working copy) @@ -872,12 +872,6 @@ drop table likeable; drop table likes; --- no schema names in constraint names (beetle 5143) -CREATE TABLE S5143.T5143_1 (C1 int CONSTRAINT S5143.CPK1 PRIMARY KEY); -CREATE TABLE S5143.T5143_2 (C1 int, C2 int, CONSTRAINT S5143.CPK1 PRIMARY KEY(C1,C2)); -CREATE TABLE S5143.T5143_3 (C1 int, C2 int, CONSTRAINT S5143.C3 CHECK(C1 > C2)); - - -- READ ONLY not allowed in "FOR" clause of a select. create table roTable (i int); insert into roTable values (8); Index: java/testing/org/apache/derbyTesting/functionTests/master/db2Compatibility.out =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/master/db2Compatibility.out (revision 156271) +++ java/testing/org/apache/derbyTesting/functionTests/master/db2Compatibility.out (working copy) @@ -90,13 +90,13 @@ ERROR 42X01: Syntax error: Encountered "STATEMENT" at line 1, column 6. ij> -- Primary key constraint, DB2 requires NOT null on the columns. create table customer (id int primary key, name char(100)); -ERROR 42831: 'ID' cannot be a column of a primary key or unique key because it can contain null values. +0 rows inserted/updated/deleted ij> drop table customer; -ERROR 42Y55: 'DROP TABLE' cannot be performed on 'CUSTOMER' because it does not exist. +0 rows inserted/updated/deleted ij> create table customer (id int NOT NULL, id2 int, name char(100), primary key (id, id2)); -ERROR 42831: 'ID2' cannot be a column of a primary key or unique key because it can contain null values. +0 rows inserted/updated/deleted ij> drop table customer; -ERROR 42Y55: 'DROP TABLE' cannot be performed on 'CUSTOMER' because it does not exist. +0 rows inserted/updated/deleted ij> -- Unique key constraint, DB2 requires NOT null on the columns. create table customer (id int unique, name char(100)); ERROR 42831: 'ID' cannot be a column of a primary key or unique key because it can contain null values. @@ -1309,13 +1309,6 @@ 0 rows inserted/updated/deleted ij> drop table likes; 0 rows inserted/updated/deleted -ij> -- no schema names in constraint names (beetle 5143) -CREATE TABLE S5143.T5143_1 (C1 int CONSTRAINT S5143.CPK1 PRIMARY KEY); -ERROR 42831: 'C1' cannot be a column of a primary key or unique key because it can contain null values. -ij> CREATE TABLE S5143.T5143_2 (C1 int, C2 int, CONSTRAINT S5143.CPK1 PRIMARY KEY(C1,C2)); -ERROR 42831: 'C1' cannot be a column of a primary key or unique key because it can contain null values. -ij> CREATE TABLE S5143.T5143_3 (C1 int, C2 int, CONSTRAINT S5143.C3 CHECK(C1 > C2)); -0 rows inserted/updated/deleted ij> -- READ ONLY not allowed in "FOR" clause of a select. create table roTable (i int); 0 rows inserted/updated/deleted Index: java/testing/org/apache/derbyTesting/functionTests/master/nulls.out =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/master/nulls.out (revision 156271) +++ java/testing/org/apache/derbyTesting/functionTests/master/nulls.out (working copy) @@ -85,10 +85,6 @@ -- give error create table a1(ac1 int null, ac2 int not null, primary key(ac1,ac2)); ERROR 42X01: Syntax error: Encountered "null" at line 3, column 25. -ij> -- should fail because --- not null must explicitly be specified for columns that have primary keys -create table a1(ac1 int, ac2 int not null, primary key(ac1,ac2)); -ERROR 42831: 'AC1' cannot be a column of a primary key or unique key because it can contain null values. ij> -- say null twice should fail create table a2(ac1 int null null); ERROR 42X01: Syntax error: Encountered "null" at line 2, column 25. Index: java/testing/org/apache/derbyTesting/functionTests/master/primarykey.out =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/master/primarykey.out (revision 156271) +++ java/testing/org/apache/derbyTesting/functionTests/master/primarykey.out (working copy) @@ -59,19 +59,24 @@ ERROR 42X01: Syntax error: Encountered "null" at line 2, column 26. ij> create table neg2(c1 int null, c2 int, constraint asdf primary key(c1, c2)); ERROR 42X01: Syntax error: Encountered "null" at line 1, column 26. -ij> -- verify that you can not create a primary key column with default null --- a primary key column can only be create if the column is explicitly not null -create table neg1 (c1 int default null primary key); +ij> -- test that a unique key can be not be explicitly nullable +create table neg1(c1 int unique); ERROR 42831: 'C1' cannot be a column of a primary key or unique key because it can contain null values. -ij> create table neg1 (c1 int default null, c2 int not null, primary key(c2, c1)); +ij> create table neg1(c1 int, c2 int, constraint asdf unique(c1)); ERROR 42831: 'C1' cannot be a column of a primary key or unique key because it can contain null values. -ij> -- test that a unique key can be not be explicitly nullable -create table neg1(c1 int null unique); -ERROR 42X01: Syntax error: Encountered "null" at line 2, column 26. -ij> create table neg1(c1 int null, c2 int, constraint asdf unique(c1)); -ERROR 42X01: Syntax error: Encountered "null" at line 1, column 26. ij> -- positive tests --- verify that a unique key can not contain nulls +-- verify that a primary key implies not null +create table pos1 (c1 int primary key); +0 rows inserted/updated/deleted +ij> insert into pos1(c1) values(1); +1 row inserted/updated/deleted +ij> insert into pos1(c1) values(1); +ERROR 23505: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'xxxxGENERATED-IDxxxx' defined on 'POS1'. +ij> insert into pos1(c1) values(null); +ERROR 23502: Column 'C1' cannot accept a NULL value. +ij> drop table pos1; +0 rows inserted/updated/deleted +ij> -- verify that a unique key can not contain nulls create table pos1 (c1 int not null unique, c2 int); 0 rows inserted/updated/deleted ij> insert into pos1 (c1) values(null);