提交 6aca33dc authored 作者: Noel Grandin's avatar Noel Grandin

Merge branch 'plus33-master'

......@@ -96,7 +96,7 @@ import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Mode;
import org.h2.engine.Mode.ModeEnum;
import org.h2.engine.Procedure;
import org.h2.engine.Right;
import org.h2.engine.Session;
......@@ -869,7 +869,7 @@ public class Parser {
}
currentPrepared = command;
int start = lastParseIndex;
if (!readIf("FROM") && database.getMode() == Mode.getMySQL()) {
if (!readIf("FROM") && database.getMode().getEnum() == ModeEnum.MySQL) {
readIdentifierWithSchema();
read("FROM");
}
......@@ -4284,14 +4284,21 @@ public class Parser {
} else if (readIf("VISIBLE")) {
column.setVisible(true);
}
if (readIf("NOT")) {
read("NULL");
column.setNullable(false);
} else if (readIf("NULL")) {
NullConstraintType nullConstraint = parseNotNullConstraint();
switch (nullConstraint) {
case NULL_IS_ALLOWED:
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
column.setNullable(defaultNullable & column.isNullable());
break;
default:
throw DbException.get(ErrorCode.UNKNOWN_MODE_1,
"Internal Error - unhandled case: " + nullConstraint.name());
}
if (readIf("AS")) {
if (isIdentity) {
......@@ -4324,23 +4331,16 @@ public class Parser {
column.setPrimaryKey(true);
column.setAutoIncrement(true, start, increment);
}
if (readIf("NOT")) {
read("NULL");
if (NullConstraintType.NULL_IS_NOT_ALLOWED == parseNotNullConstraint()) {
column.setNullable(false);
} else {
readIf("NULL");
}
if (readIf("AUTO_INCREMENT") || readIf("BIGSERIAL") || readIf("SERIAL")) {
parseAutoIncrement(column);
if (readIf("NOT")) {
read("NULL");
}
parseNotNullConstraint();
} else if (readIf("IDENTITY")) {
parseAutoIncrement(column);
column.setPrimaryKey(true);
if (readIf("NOT")) {
read("NULL");
}
parseNotNullConstraint();
}
if (readIf("NULL_TO_DEFAULT")) {
column.setConvertNullToDefault(true);
......@@ -6107,25 +6107,32 @@ public class Parser {
return command;
} else if (readIf("MODIFY")) {
// MySQL compatibility
readIf("COLUMN");
readIf("COLUMN"); // optional
String columnName = readColumnIdentifier();
if ((isToken("NOT") || isToken("NULL"))) {
AlterTableAlterColumn command = new AlterTableAlterColumn(
session, schema);
AlterTableAlterColumn command = null;
NullConstraintType nullConstraint = parseNotNullConstraint();
switch (nullConstraint) {
case NULL_IS_ALLOWED:
case NULL_IS_NOT_ALLOWED:
command = new AlterTableAlterColumn(session, schema);
command.setTableName(tableName);
command.setIfTableExists(ifTableExists);
Column column = columnIfTableExists(schema, tableName, columnName, ifTableExists);
command.setOldColumn(column);
if (readIf("NOT")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL);
} else {
read("NULL");
if (nullConstraint == NullConstraintType.NULL_IS_ALLOWED) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL);
} else {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL);
}
return command;
} else {
return parseAlterTableAlterColumnType(schema, tableName, columnName, ifTableExists);
break;
case NO_NULL_CONSTRAINT_FOUND:
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")) {
readIf("COLUMN");
String columnName = readColumnIdentifier();
......@@ -6177,27 +6184,34 @@ public class Parser {
command.setTableName(tableName);
command.setIfTableExists(ifTableExists);
command.setOldColumn(column);
if (readIf("NULL")) {
NullConstraintType nullConstraint = parseNotNullConstraint();
switch (nullConstraint) {
case NULL_IS_ALLOWED:
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NULL);
return command;
} else if (readIf("NOT")) {
read("NULL");
break;
case NULL_IS_NOT_ALLOWED:
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_NOT_NULL);
return command;
} else if (readIf("DEFAULT")) {
Expression defaultExpression = readExpression();
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT);
command.setDefaultExpression(defaultExpression);
return command;
} else if (readIf("INVISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(false);
return command;
} else if (readIf("VISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(true);
return command;
break;
case NO_NULL_CONSTRAINT_FOUND:
if (readIf("DEFAULT")) {
Expression defaultExpression = readExpression();
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_DEFAULT);
command.setDefaultExpression(defaultExpression);
} else if (readIf("INVISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(false);
} else if (readIf("VISIBLE")) {
command.setType(CommandInterface.ALTER_TABLE_ALTER_COLUMN_VISIBILITY);
command.setVisible(true);
}
break;
default:
throw DbException.get(ErrorCode.UNKNOWN_MODE_1,
"Internal Error - unhandled case: " + nullConstraint.name());
}
return command;
} else if (readIf("RESTART")) {
readIf("WITH");
Expression start = readExpression();
......@@ -6592,11 +6606,8 @@ public class Parser {
unique.setTableName(tableName);
command.addConstraintCommand(unique);
}
if (readIf("NOT")) {
read("NULL");
if (NullConstraintType.NULL_IS_NOT_ALLOWED == parseNotNullConstraint()) {
column.setNullable(false);
} else {
readIf("NULL");
}
if (readIf("CHECK")) {
Expression expr = readExpression();
......@@ -6699,6 +6710,36 @@ public class Parser {
return command;
}
private enum NullConstraintType {
NULL_IS_ALLOWED, NULL_IS_NOT_ALLOWED, NO_NULL_CONSTRAINT_FOUND
}
private NullConstraintType parseNotNullConstraint() {
NullConstraintType nullConstraint = NullConstraintType.NO_NULL_CONSTRAINT_FOUND;
if ((isToken("NOT") || isToken("NULL"))) {
if (readIf("NOT")) {
read("NULL");
nullConstraint = NullConstraintType.NULL_IS_NOT_ALLOWED;
} else {
read("NULL");
nullConstraint = NullConstraintType.NULL_IS_ALLOWED;
}
if (database.getMode().getEnum() == ModeEnum.Oracle) {
if (readIf("ENABLE")) {
readIf("VALIDATE"); // Leave constraint 'as is'
if (readIf("NOVALIDATE")) { // Turn off constraint, allow NULLs
nullConstraint = NullConstraintType.NULL_IS_ALLOWED;
}
}
if (readIf("DISABLE")) { // Turn off constraint, allow NULLs
nullConstraint = NullConstraintType.NULL_IS_ALLOWED;
readIf("VALIDATE"); // ignore validate
readIf("NOVALIDATE"); // ignore novalidate
}
}
}
return nullConstraint;
}
private CreateSynonym parseCreateSynonym(boolean orReplace) {
boolean ifNotExists = readIfNotExists();
......
......@@ -19,9 +19,14 @@ import org.h2.util.StringUtils;
public class Mode {
public enum ModeEnum {
REGULAR, DB2, Derby, MSSQLServer, HSQLDB, MySQL, Oracle, PostgreSQL, Ignite
REGULAR, DB2, Derby, MSSQLServer, HSQLDB, MySQL, Oracle, PostgreSQL, Ignite,
}
/**
* The name of the default mode.
*/
static final String REGULAR = ModeEnum.REGULAR.name();
private static final HashMap<String, Mode> MODES = New.hashMap();
// Modes are also documented in the features section
......@@ -177,14 +182,16 @@ public class Mode {
*/
public Set<String> disallowedTypes = Collections.emptySet();
private final String name;
private ModeEnum modeEnum;
static {
Mode mode = new Mode(ModeEnum.REGULAR);
Mode mode = new Mode(ModeEnum.REGULAR.name());
mode.nullConcatIsNull = true;
add(mode);
mode = new Mode(ModeEnum.DB2);
mode = new Mode(ModeEnum.DB2.name());
mode.aliasColumnName = true;
mode.supportOffsetFetch = true;
mode.sysDummy1 = true;
......@@ -199,7 +206,7 @@ public class Mode {
mode.allowDB2TimestampFormat = true;
add(mode);
mode = new Mode(ModeEnum.Derby);
mode = new Mode(ModeEnum.Derby.name());
mode.aliasColumnName = true;
mode.uniqueIndexSingleNull = true;
mode.supportOffsetFetch = true;
......@@ -209,7 +216,7 @@ public class Mode {
mode.supportedClientInfoPropertiesRegEx = null;
add(mode);
mode = new Mode(ModeEnum.HSQLDB);
mode = new Mode(ModeEnum.HSQLDB.name());
mode.aliasColumnName = true;
mode.convertOnlyToSmallerScale = true;
mode.nullConcatIsNull = true;
......@@ -222,7 +229,7 @@ public class Mode {
mode.supportedClientInfoPropertiesRegEx = null;
add(mode);
mode = new Mode(ModeEnum.MSSQLServer);
mode = new Mode(ModeEnum.MSSQLServer.name());
mode.aliasColumnName = true;
mode.squareBracketQuotedNames = true;
mode.uniqueIndexSingleNull = true;
......@@ -234,7 +241,7 @@ public class Mode {
mode.supportedClientInfoPropertiesRegEx = null;
add(mode);
mode = new Mode(ModeEnum.MySQL);
mode = new Mode(ModeEnum.MySQL.name());
mode.convertInsertNullToZero = true;
mode.indexDefinitionInCreateTable = true;
mode.lowerCaseIdentifiers = true;
......@@ -248,7 +255,7 @@ public class Mode {
mode.prohibitEmptyInPredicate = true;
add(mode);
mode = new Mode(ModeEnum.Oracle);
mode = new Mode(ModeEnum.Oracle.name());
mode.aliasColumnName = true;
mode.convertOnlyToSmallerScale = true;
mode.uniqueIndexSingleNullExceptAllColumnsAreNull = true;
......@@ -261,7 +268,7 @@ public class Mode {
mode.prohibitEmptyInPredicate = true;
add(mode);
mode = new Mode(ModeEnum.PostgreSQL);
mode = new Mode(ModeEnum.PostgreSQL.name());
mode.aliasColumnName = true;
mode.nullConcatIsNull = true;
mode.supportOffsetFetch = true;
......@@ -284,19 +291,20 @@ public class Mode {
mode.disallowedTypes = disallowedTypes;
add(mode);
mode = new Mode(ModeEnum.Ignite);
mode = new Mode(ModeEnum.Ignite.name());
mode.nullConcatIsNull = true;
mode.allowAffinityKey = true;
mode.indexDefinitionInCreateTable = true;
add(mode);
}
private Mode(ModeEnum modeEnum) {
this.modeEnum = modeEnum;
private Mode(String name) {
this.name = name;
this.modeEnum = ModeEnum.valueOf(name);
}
private static void add(Mode mode) {
MODES.put(StringUtils.toUpperEnglish(mode.modeEnum.name()), mode);
MODES.put(StringUtils.toUpperEnglish(mode.name), mode);
}
/**
......@@ -309,10 +317,6 @@ public class Mode {
return MODES.get(StringUtils.toUpperEnglish(name));
}
public static Mode getRegular() {
return getInstance(ModeEnum.REGULAR.name());
}
public static Mode getMySQL() {
return getInstance(ModeEnum.MySQL.name());
}
......@@ -321,12 +325,25 @@ public class Mode {
return getInstance(ModeEnum.Oracle.name());
}
public static Mode getRegular() {
return getInstance(ModeEnum.REGULAR.name());
}
public String getName() {
return modeEnum.name();
return name;
}
public ModeEnum getEnum() {
return this.modeEnum;
}
public boolean isDbTypeOneOf(Mode.ModeEnum... dbTypes) {
for (Mode.ModeEnum dbType : dbTypes) {
if (this.modeEnum == dbType) {
return true;
}
}
return false;
}
}
......@@ -304,7 +304,7 @@ public class TestAlter extends TestBase {
// This failed in v1.4.196
stat.execute("create table T (C int not null)");
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");
}
}
......@@ -34,6 +34,7 @@ public class TestCompatibilityOracle extends TestBase {
@Override
public void test() throws Exception {
testNotNullSyntax();
testTreatEmptyStringsAsNull();
testDecimalScale();
testPoundSymbolInColumnName();
......@@ -41,6 +42,51 @@ public class TestCompatibilityOracle extends TestBase {
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");
// Some other variation with 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");
conn.close();
}
private void testTreatEmptyStringsAsNull() throws SQLException {
deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论