提交 8eb12a7a authored 作者: plus33's avatar plus33

Added Oracle compatibility of the NOT NULL syntax.

Oracles NOT NULL syntax can have optional ENABLE VALIDATE added e.g  
  create table T (C int NOT NULL ENABLE VALIDATE)
Some SQL-generators produce such SQL and H2 can now parse it with this
change.
上级 d6532794
...@@ -4073,14 +4073,22 @@ public class Parser { ...@@ -4073,14 +4073,22 @@ public class Parser {
} else if (readIf("VISIBLE")) { } else if (readIf("VISIBLE")) {
column.setVisible(true); column.setVisible(true);
} }
if (readIf("NOT")) { NULL_CONSTRAINT nullConstraint = parseNotNullConstraint();
read("NULL"); switch (nullConstraint) {
column.setNullable(false); case NULL_IS_ALLOWED:
} else if (readIf("NULL")) {
column.setNullable(true); column.setNullable(true);
} else { break;
case NULL_IS_NOT_ALLOWED:
column.setNullable(false);
break;
case NO_NULL_CONSTRAINT_FOUND:
// domains may be defined as not nullable // domains may be defined as not nullable
column.setNullable(defaultNullable & column.isNullable()); column.setNullable(defaultNullable & column.isNullable());
break;
default:
throw DbException.get(ErrorCode.UNKNOWN_MODE_1,
"Internal Error - unhandled case: " + nullConstraint.name());
} }
if (readIf("AS")) { if (readIf("AS")) {
if (isIdentity) { if (isIdentity) {
...@@ -4113,23 +4121,21 @@ public class Parser { ...@@ -4113,23 +4121,21 @@ public class Parser {
column.setPrimaryKey(true); column.setPrimaryKey(true);
column.setAutoIncrement(true, start, increment); column.setAutoIncrement(true, start, increment);
} }
if (readIf("NOT")) { nullConstraint = parseNotNullConstraint();
read("NULL"); switch (nullConstraint) {
case NULL_IS_NOT_ALLOWED:
column.setNullable(false); column.setNullable(false);
} else { break;
readIf("NULL"); default:
// do nothing
} }
if (readIf("AUTO_INCREMENT") || readIf("BIGSERIAL") || readIf("SERIAL")) { if (readIf("AUTO_INCREMENT") || readIf("BIGSERIAL") || readIf("SERIAL")) {
parseAutoIncrement(column); parseAutoIncrement(column);
if (readIf("NOT")) { parseNotNullConstraint();
read("NULL");
}
} else if (readIf("IDENTITY")) { } else if (readIf("IDENTITY")) {
parseAutoIncrement(column); parseAutoIncrement(column);
column.setPrimaryKey(true); column.setPrimaryKey(true);
if (readIf("NOT")) { parseNotNullConstraint();
read("NULL");
}
} }
if (readIf("NULL_TO_DEFAULT")) { if (readIf("NULL_TO_DEFAULT")) {
column.setConvertNullToDefault(true); column.setConvertNullToDefault(true);
...@@ -5867,25 +5873,32 @@ public class Parser { ...@@ -5867,25 +5873,32 @@ public class Parser {
return command; return command;
} else if (readIf("MODIFY")) { } else if (readIf("MODIFY")) {
// MySQL compatibility // MySQL compatibility
readIf("COLUMN"); readIf("COLUMN"); // optional
String columnName = readColumnIdentifier(); String columnName = readColumnIdentifier();
if ((isToken("NOT") || isToken("NULL"))) { AlterTableAlterColumn command = null;
AlterTableAlterColumn command = new AlterTableAlterColumn( NULL_CONSTRAINT nullConstraint = parseNotNullConstraint();
session, schema); switch (nullConstraint) {
command.setTableName(tableName); case NULL_IS_ALLOWED:
command.setIfTableExists(ifTableExists); case NULL_IS_NOT_ALLOWED:
Column column = columnIfTableExists(schema, tableName, columnName, ifTableExists); command = new AlterTableAlterColumn(session, schema);
command.setOldColumn(column); command.setTableName(tableName);
if (readIf("NOT")) { command.setIfTableExists(ifTableExists);
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL); Column column = columnIfTableExists(schema, tableName, columnName, ifTableExists);
} else { command.setOldColumn(column);
read("NULL"); if (nullConstraint == NULL_CONSTRAINT.NULL_IS_ALLOWED) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL);
} else {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL);
} }
return command; break;
} else { case NO_NULL_CONSTRAINT_FOUND:
return parseAlterTableAlterColumnType(schema, tableName, columnName, ifTableExists); command = parseAlterTableAlterColumnType(schema, tableName, columnName, ifTableExists);
break;
default:
throw DbException.get(ErrorCode.UNKNOWN_MODE_1,
"Internal Error - unhandled case: " + nullConstraint.name());
} }
return command;
} else if (readIf("ALTER")) { } else if (readIf("ALTER")) {
readIf("COLUMN"); readIf("COLUMN");
String columnName = readColumnIdentifier(); String columnName = readColumnIdentifier();
...@@ -5937,27 +5950,34 @@ public class Parser { ...@@ -5937,27 +5950,34 @@ public class Parser {
command.setTableName(tableName); command.setTableName(tableName);
command.setIfTableExists(ifTableExists); command.setIfTableExists(ifTableExists);
command.setOldColumn(column); command.setOldColumn(column);
if (readIf("NULL")) { NULL_CONSTRAINT nullConstraint = parseNotNullConstraint();
switch (nullConstraint) {
case NULL_IS_ALLOWED:
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL);
return command; break;
} else if (readIf("NOT")) { case NULL_IS_NOT_ALLOWED:
read("NULL");
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL);
return command; break;
} else if (readIf("DEFAULT")) { case NO_NULL_CONSTRAINT_FOUND:
Expression defaultExpression = readExpression(); if (readIf("DEFAULT")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT); Expression defaultExpression = readExpression();
command.setDefaultExpression(defaultExpression); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT);
return command; command.setDefaultExpression(defaultExpression);
} else if (readIf("INVISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY); } else if (readIf("INVISIBLE")) {
command.setVisible(false); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
return command; command.setVisible(false);
} else if (readIf("VISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY); } else if (readIf("VISIBLE")) {
command.setVisible(true); command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
return command; command.setVisible(true);
}
break;
default:
throw DbException.get(ErrorCode.UNKNOWN_MODE_1,
"Internal Error - unhandled case: " + nullConstraint.name());
} }
return command;
} else if (readIf("RESTART")) { } else if (readIf("RESTART")) {
readIf("WITH"); readIf("WITH");
Expression start = readExpression(); Expression start = readExpression();
...@@ -6352,11 +6372,14 @@ public class Parser { ...@@ -6352,11 +6372,14 @@ public class Parser {
unique.setTableName(tableName); unique.setTableName(tableName);
command.addConstraintCommand(unique); command.addConstraintCommand(unique);
} }
if (readIf("NOT")) {
read("NULL"); NULL_CONSTRAINT nullConstraint = parseNotNullConstraint();
switch (nullConstraint) {
case NULL_IS_NOT_ALLOWED:
column.setNullable(false); column.setNullable(false);
} else { break;
readIf("NULL"); default:
// do nothing
} }
if (readIf("CHECK")) { if (readIf("CHECK")) {
Expression expr = readExpression(); Expression expr = readExpression();
...@@ -6459,6 +6482,34 @@ public class Parser { ...@@ -6459,6 +6482,34 @@ public class Parser {
return command; return command;
} }
private enum NULL_CONSTRAINT {
NULL_IS_ALLOWED, NULL_IS_NOT_ALLOWED, NO_NULL_CONSTRAINT_FOUND
}
private NULL_CONSTRAINT parseNotNullConstraint() {
NULL_CONSTRAINT nullConstraint = NULL_CONSTRAINT.NO_NULL_CONSTRAINT_FOUND;
if ((isToken("NOT") || isToken("NULL"))) {
if (readIf("NOT")) {
read("NULL");
nullConstraint = NULL_CONSTRAINT.NULL_IS_NOT_ALLOWED;
} else {
read("NULL");
nullConstraint = NULL_CONSTRAINT.NULL_IS_ALLOWED;
}
if (readIf("ENABLE")) {
readIf("VALIDATE"); // Leave constraint 'as is'
if (readIf("NOVALIDATE")) { // Turn off constraint, thus allow NULLs
nullConstraint = NULL_CONSTRAINT.NULL_IS_ALLOWED;
}
}
if (readIf("DISABLE")) { // Turn off constraint, thus allow NULLs
nullConstraint = NULL_CONSTRAINT.NULL_IS_ALLOWED;
readIf("VALIDATE"); // ignore validate
readIf("NOVALIDATE"); // ignore novalidate
}
}
return nullConstraint;
}
private CreateSynonym parseCreateSynonym(boolean orReplace) { private CreateSynonym parseCreateSynonym(boolean orReplace) {
boolean ifNotExists = readIfNotExists(); boolean ifNotExists = readIfNotExists();
......
...@@ -304,7 +304,28 @@ public class TestAlter extends TestBase { ...@@ -304,7 +304,28 @@ public class TestAlter extends TestBase {
// This failed in v1.4.196 // This failed in v1.4.196
stat.execute("create table T (C int not null)"); stat.execute("create table T (C int not null)");
stat.execute("alter table T modify C null"); // Silently corrupted column C stat.execute("alter table T modify C null"); // Silently corrupted column C
stat.execute("insert into T values(null)"); // <- ERROR: NULL not allowed stat.execute("insert into T values(null)"); // <- Fixed in v1.4.196 - NULL is allowed
stat.execute("drop table T");
// Some other variation (oracle syntax)
stat.execute("create table T (C int not null)");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null enable");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null enable validate");
stat.execute("insert into T values(1)");
stat.execute("drop table T");
// can set NULL
stat.execute("create table T (C int null)");
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C null enable");
stat.execute("alter table T modify C null enable validate");
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C not null disable"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C not null enable novalidate"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("drop table T"); stat.execute("drop table T");
} }
} }
...@@ -34,6 +34,7 @@ public class TestCompatibilityOracle extends TestBase { ...@@ -34,6 +34,7 @@ public class TestCompatibilityOracle extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testNotNullSyntax();
testTreatEmptyStringsAsNull(); testTreatEmptyStringsAsNull();
testDecimalScale(); testDecimalScale();
testPoundSymbolInColumnName(); testPoundSymbolInColumnName();
...@@ -41,6 +42,28 @@ public class TestCompatibilityOracle extends TestBase { ...@@ -41,6 +42,28 @@ public class TestCompatibilityOracle extends TestBase {
testForbidEmptyInClause(); testForbidEmptyInClause();
} }
private void testNotNullSyntax() throws SQLException {
deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle");
Statement stat = conn.createStatement();
// Some other variation (oracle syntax)
stat.execute("create table T (C int not null enable)");
stat.execute("insert into T values(1)");
stat.execute("drop table T");
stat.execute("create table T (C int not null enable validate)");
stat.execute("insert into T values(1)");
stat.execute("drop table T");
// can set NULL
stat.execute("create table T (C int not null disable)"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("drop table T");
stat.execute("create table T (C int not null enable novalidate)"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("drop table T");
conn.close();
}
private void testTreatEmptyStringsAsNull() throws SQLException { private void testTreatEmptyStringsAsNull() throws SQLException {
deleteDb("oracle"); deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle"); Connection conn = getConnection("oracle;MODE=Oracle");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论