[jOOQ/jOOQ#5317] Add support for ALTER TABLE .. ADD [ COLUMN ] .. [ { PRIMARY KEY | UNIQUE } ] [ REFERENCES | CHECK .. ]

This commit is contained in:
Lukas Eder 2019-12-04 09:37:01 +01:00
parent f0950e0779
commit ee98a3c377

View File

@ -3256,195 +3256,25 @@ final class ParserImpl implements Parser {
throw ctx.expected("CHECK", "CONSTRAINT", "FOREIGN KEY", "INDEX", "KEY", "PRIMARY KEY", "UNIQUE");
Name fieldName = parseIdentifier(ctx);
DataType<?> type = null;
if (ctas == null)
ctas = peek(ctx, ',') || peek(ctx, ')');
type = !TRUE.equals(ctas)
? parseDataType(ctx)
: SQLDataType.OTHER;
// If only we had multiple return values or destructuring...
ParseInlineConstraints inlineConstraints = parseInlineConstraints(
ctx,
fieldName,
!TRUE.equals(ctas) ? parseDataType(ctx) : SQLDataType.OTHER,
constraints,
primary
);
Comment fieldComment = null;
boolean nullable = false;
boolean defaultValue = false;
boolean onUpdate = false;
boolean unique = false;
boolean identity = type.identity();
boolean comment = false;
for (;;) {
if (!nullable) {
if (parseKeywordIf(ctx, "NULL")) {
type = type.nullable(true);
nullable = true;
continue;
}
else if (parseKeywordIf(ctx, "NOT NULL")) {
type = type.nullable(false);
nullable = true;
continue;
}
}
if (!defaultValue) {
if (parseKeywordIf(ctx, "IDENTITY")) {
if (parseIf(ctx, '(')) {
parseSignedInteger(ctx);
parse(ctx, ',');
parseSignedInteger(ctx);
parse(ctx, ')');
}
type = type.identity(true);
defaultValue = true;
identity = true;
continue;
}
else if (parseKeywordIf(ctx, "DEFAULT")) {
// TODO: Ignored keyword from Oracle
parseKeywordIf(ctx, "ON NULL");
type = type.defaultValue((Field) toField(ctx, parseConcat(ctx, null)));
defaultValue = true;
identity = true;
continue;
}
else if (parseKeywordIf(ctx, "GENERATED")) {
if (!parseKeywordIf(ctx, "ALWAYS")) {
parseKeyword(ctx, "BY DEFAULT");
// TODO: Ignored keyword from Oracle
parseKeywordIf(ctx, "ON NULL");
}
parseKeyword(ctx, "AS IDENTITY");
// TODO: Ignored identity options from Oracle
if (parseIf(ctx, '(')) {
boolean identityOption = false;
for (;;) {
if (identityOption)
parseIf(ctx, ',');
if (parseKeywordIf(ctx, "START WITH")) {
if (!parseKeywordIf(ctx, "LIMIT VALUE"))
parseUnsignedIntegerOrBindVariable(ctx);
identityOption = true;
continue;
}
else if (parseKeywordIf(ctx, "INCREMENT BY")
|| parseKeywordIf(ctx, "MAXVALUE")
|| parseKeywordIf(ctx, "MINVALUE")
|| parseKeywordIf(ctx, "CACHE")) {
parseUnsignedIntegerOrBindVariable(ctx);
identityOption = true;
continue;
}
else if (parseKeywordIf(ctx, "NOMAXVALUE")
|| parseKeywordIf(ctx, "NOMINVALUE")
|| parseKeywordIf(ctx, "CYCLE")
|| parseKeywordIf(ctx, "NOCYCLE")
|| parseKeywordIf(ctx, "NOCACHE")
|| parseKeywordIf(ctx, "ORDER")
|| parseKeywordIf(ctx, "NOORDER")) {
identityOption = true;
continue;
}
if (identityOption)
break;
else
throw ctx.unsupportedClause();
}
parse(ctx, ')');
}
type = type.identity(true);
defaultValue = true;
identity = true;
continue;
}
}
if (!onUpdate) {
if (parseKeywordIf(ctx, "ON UPDATE")) {
// [#6132] TODO: Support this feature in the jOOQ DDL API
parseConcat(ctx, null);
onUpdate = true;
continue;
}
}
ConstraintTypeStep inlineConstraint = null;
if (parseKeywordIf(ctx, "CONSTRAINT"))
inlineConstraint = constraint(parseIdentifier(ctx));
if (!unique) {
if (parsePrimaryKeyClusteredNonClusteredKeywordIf(ctx)) {
if (!parseKeywordIf(ctx, "CLUSTERED"))
parseKeywordIf(ctx, "NONCLUSTERED");
constraints.add(inlineConstraint == null
? primaryKey(fieldName)
: inlineConstraint.primaryKey(fieldName));
primary = true;
unique = true;
continue;
}
else if (parseKeywordIf(ctx, "UNIQUE")) {
if (!parseKeywordIf(ctx, "KEY"))
parseKeywordIf(ctx, "INDEX");
constraints.add(inlineConstraint == null
? unique(fieldName)
: inlineConstraint.unique(fieldName));
unique = true;
continue;
}
}
if (parseKeywordIf(ctx, "CHECK")) {
constraints.add(parseCheckSpecification(ctx, inlineConstraint));
continue;
}
if (parseKeywordIf(ctx, "REFERENCES")) {
constraints.add(parseForeignKeyReferenceSpecification(ctx, inlineConstraint, new Field[] { field(fieldName) }));
continue;
}
if (inlineConstraint != null)
throw ctx.expected("CHECK", "PRIMARY KEY", "REFERENCES", "UNIQUE");
if (!identity) {
if (parseKeywordIf(ctx, "AUTO_INCREMENT") ||
parseKeywordIf(ctx, "AUTOINCREMENT")) {
type = type.identity(true);
identity = true;
continue;
}
}
if (!comment) {
if (parseKeywordIf(ctx, "COMMENT")) {
fieldComment = parseComment(ctx);
continue;
}
}
break;
}
primary = inlineConstraints.primary;
if (ctas)
fields.add(field(fieldName));
else
fields.add(field(fieldName, type, fieldComment));
fields.add(field(fieldName, inlineConstraints.type, inlineConstraints.fieldComment));
}
while (parseIf(ctx, ','));
@ -3641,7 +3471,204 @@ final class ParserImpl implements Parser {
return storageStep;
}
private static boolean parsePrimaryKeyClusteredNonClusteredKeywordIf(ParserContext ctx) {
private static final class ParseInlineConstraints {
final DataType<?> type;
final Comment fieldComment;
final boolean primary;
ParseInlineConstraints(DataType<?> type, Comment fieldComment, boolean primary) {
this.type = type;
this.fieldComment = fieldComment;
this.primary = primary;
}
}
private static final ParseInlineConstraints parseInlineConstraints(
ParserContext ctx,
Name fieldName,
DataType<?> type,
List<? super Constraint> constraints,
boolean primary
) {
boolean nullable = false;
boolean defaultValue = false;
boolean onUpdate = false;
boolean unique = primary;
boolean identity = type.identity();
boolean comment = false;
Comment fieldComment = null;
for (;;) {
if (!nullable) {
if (parseKeywordIf(ctx, "NULL")) {
type = type.nullable(true);
nullable = true;
continue;
}
else if (parseKeywordIf(ctx, "NOT NULL")) {
type = type.nullable(false);
nullable = true;
continue;
}
}
if (!defaultValue) {
if (parseKeywordIf(ctx, "IDENTITY")) {
if (parseIf(ctx, '(')) {
parseSignedInteger(ctx);
parse(ctx, ',');
parseSignedInteger(ctx);
parse(ctx, ')');
}
type = type.identity(true);
defaultValue = true;
identity = true;
continue;
}
else if (parseKeywordIf(ctx, "DEFAULT")) {
// TODO: Ignored keyword from Oracle
parseKeywordIf(ctx, "ON NULL");
type = type.defaultValue((Field) toField(ctx, parseConcat(ctx, null)));
defaultValue = true;
identity = true;
continue;
}
else if (parseKeywordIf(ctx, "GENERATED")) {
if (!parseKeywordIf(ctx, "ALWAYS")) {
parseKeyword(ctx, "BY DEFAULT");
// TODO: Ignored keyword from Oracle
parseKeywordIf(ctx, "ON NULL");
}
parseKeyword(ctx, "AS IDENTITY");
// TODO: Ignored identity options from Oracle
if (parseIf(ctx, '(')) {
boolean identityOption = false;
for (;;) {
if (identityOption)
parseIf(ctx, ',');
if (parseKeywordIf(ctx, "START WITH")) {
if (!parseKeywordIf(ctx, "LIMIT VALUE"))
parseUnsignedIntegerOrBindVariable(ctx);
identityOption = true;
continue;
}
else if (parseKeywordIf(ctx, "INCREMENT BY")
|| parseKeywordIf(ctx, "MAXVALUE")
|| parseKeywordIf(ctx, "MINVALUE")
|| parseKeywordIf(ctx, "CACHE")) {
parseUnsignedIntegerOrBindVariable(ctx);
identityOption = true;
continue;
}
else if (parseKeywordIf(ctx, "NOMAXVALUE")
|| parseKeywordIf(ctx, "NOMINVALUE")
|| parseKeywordIf(ctx, "CYCLE")
|| parseKeywordIf(ctx, "NOCYCLE")
|| parseKeywordIf(ctx, "NOCACHE")
|| parseKeywordIf(ctx, "ORDER")
|| parseKeywordIf(ctx, "NOORDER")) {
identityOption = true;
continue;
}
if (identityOption)
break;
else
throw ctx.unsupportedClause();
}
parse(ctx, ')');
}
type = type.identity(true);
defaultValue = true;
identity = true;
continue;
}
}
if (!onUpdate) {
if (parseKeywordIf(ctx, "ON UPDATE")) {
// [#6132] TODO: Support this feature in the jOOQ DDL API
parseConcat(ctx, null);
onUpdate = true;
continue;
}
}
ConstraintTypeStep inlineConstraint = null;
if (parseKeywordIf(ctx, "CONSTRAINT"))
inlineConstraint = constraint(parseIdentifier(ctx));
if (!unique) {
if (!primary && parsePrimaryKeyClusteredNonClusteredKeywordIf(ctx)) {
if (!parseKeywordIf(ctx, "CLUSTERED"))
parseKeywordIf(ctx, "NONCLUSTERED");
constraints.add(inlineConstraint == null
? primaryKey(fieldName)
: inlineConstraint.primaryKey(fieldName));
primary = true;
unique = true;
continue;
}
else if (parseKeywordIf(ctx, "UNIQUE")) {
if (!parseKeywordIf(ctx, "KEY"))
parseKeywordIf(ctx, "INDEX");
constraints.add(inlineConstraint == null
? unique(fieldName)
: inlineConstraint.unique(fieldName));
unique = true;
continue;
}
}
if (parseKeywordIf(ctx, "CHECK")) {
constraints.add(parseCheckSpecification(ctx, inlineConstraint));
continue;
}
if (parseKeywordIf(ctx, "REFERENCES")) {
constraints.add(parseForeignKeyReferenceSpecification(ctx, inlineConstraint, new Field[] { field(fieldName) }));
continue;
}
if (inlineConstraint != null)
throw ctx.expected("CHECK", "PRIMARY KEY", "REFERENCES", "UNIQUE");
if (!identity) {
if (parseKeywordIf(ctx, "AUTO_INCREMENT") ||
parseKeywordIf(ctx, "AUTOINCREMENT")) {
type = type.identity(true);
identity = true;
continue;
}
}
if (!comment) {
if (parseKeywordIf(ctx, "COMMENT")) {
fieldComment = parseComment(ctx);
continue;
}
}
break;
}
return new ParseInlineConstraints(type, fieldComment, primary);
}
private static final boolean parsePrimaryKeyClusteredNonClusteredKeywordIf(ParserContext ctx) {
if (!parseKeywordIf(ctx, "PRIMARY KEY"))
return false;
@ -3990,7 +4017,7 @@ final class ParserImpl implements Parser {
if (parseIf(ctx, '(')) {
do {
list.add(parseAlterTableAddFieldOrConstraint(ctx));
parseAlterTableAddFieldsOrConstraints(ctx, list);
}
while (parseIf(ctx, ','));
@ -3998,11 +4025,11 @@ final class ParserImpl implements Parser {
}
else if (parseKeywordIf(ctx, "COLUMN IF NOT EXISTS")
|| parseKeywordIf(ctx, "IF NOT EXISTS")) {
return parseAlterTableAddFieldFirstBeforeLast(ctx, s1.addColumnIfNotExists(parseAlterTableAddField(ctx)));
return parseAlterTableAddFieldFirstBeforeLast(ctx, s1.addColumnIfNotExists(parseAlterTableAddField(ctx, null)));
}
else {
do {
list.add(parseAlterTableAddFieldOrConstraint(ctx));
parseAlterTableAddFieldsOrConstraints(ctx, list);
}
while (parseIf(ctx, ',') && parseKeyword(ctx, "ADD"));
}
@ -4038,32 +4065,27 @@ final class ParserImpl implements Parser {
|| parseKeywordIf(ctx, "KEY");
}
private static final FieldOrConstraint parseAlterTableAddFieldOrConstraint(ParserContext ctx) {
private static final void parseAlterTableAddFieldsOrConstraints(ParserContext ctx, List<FieldOrConstraint> list) {
ConstraintTypeStep constraint = null;
if (parseKeywordIf(ctx, "CONSTRAINT"))
constraint = constraint(parseIdentifier(ctx));
if (parsePrimaryKeyClusteredNonClusteredKeywordIf(ctx))
return parsePrimaryKeySpecification(ctx, constraint);
else if (parseKeywordIf(ctx, "UNIQUE")) {
if (!parseKeywordIf(ctx, "KEY"))
parseKeywordIf(ctx, "INDEX");
return parseUniqueSpecification(ctx, constraint);
}
list.add(parsePrimaryKeySpecification(ctx, constraint));
else if (parseKeywordIf(ctx, "UNIQUE") && (parseKeywordIf(ctx, "KEY") || parseKeywordIf(ctx, "INDEX") || true))
list.add(parseUniqueSpecification(ctx, constraint));
else if (parseKeywordIf(ctx, "FOREIGN KEY"))
return parseForeignKeySpecification(ctx, constraint);
list.add(parseForeignKeySpecification(ctx, constraint));
else if (parseKeywordIf(ctx, "CHECK"))
return parseCheckSpecification(ctx, constraint);
list.add(parseCheckSpecification(ctx, constraint));
else if (constraint != null)
throw ctx.expected("CHECK", "FOREIGN KEY", "PRIMARY KEY", "UNIQUE");
parseKeywordIf(ctx, "COLUMN");
return parseAlterTableAddField(ctx);
else if (parseKeywordIf(ctx, "COLUMN") || true)
parseAlterTableAddField(ctx, list);
}
private static final Field<?> parseAlterTableAddField(ParserContext ctx) {
private static final Field<?> parseAlterTableAddField(ParserContext ctx, List<FieldOrConstraint> list) {
// The below code is taken from CREATE TABLE, with minor modifications as
// https://github.com/jOOQ/jOOQ/issues/5317 has not yet been implemented
@ -4072,66 +4094,15 @@ final class ParserImpl implements Parser {
Name fieldName = parseIdentifier(ctx);
DataType type = parseDataType(ctx);
Comment fieldComment = null;
int position = list == null ? -1 : list.size();
boolean nullable = false;
boolean defaultValue = false;
boolean onUpdate = false;
boolean unique = false;
boolean comment = false;
ParseInlineConstraints inline = parseInlineConstraints(ctx, fieldName, type, list, false);
Field<?> result = field(fieldName, inline.type, inline.fieldComment);
for (;;) {
if (!nullable) {
if (parseKeywordIf(ctx, "NULL")) {
type = type.nullable(true);
nullable = true;
continue;
}
else if (parseKeywordIf(ctx, "NOT NULL")) {
type = type.nullable(false);
nullable = true;
continue;
}
}
if (list != null)
list.add(position, result);
if (!defaultValue) {
if (parseKeywordIf(ctx, "DEFAULT")) {
type = type.defaultValue(toField(ctx, parseConcat(ctx, null)));
defaultValue = true;
continue;
}
}
if (!onUpdate) {
if (parseKeywordIf(ctx, "ON UPDATE")) {
// [#6132] TODO: Support this feature in the jOOQ DDL API
parseConcat(ctx, null);
onUpdate = true;
continue;
}
}
if (!unique)
if (parseKeywordIf(ctx, "PRIMARY KEY"))
throw ctx.notImplemented("Inline primary key specification");
else if (parseKeywordIf(ctx, "UNIQUE"))
throw ctx.notImplemented("Inline unique key specification");
if (parseKeywordIf(ctx, "CHECK"))
throw ctx.notImplemented("Inline check constraint specification");
if (!comment) {
if (parseKeywordIf(ctx, "COMMENT")) {
fieldComment = parseComment(ctx);
continue;
}
}
break;
}
return field(fieldName, type, fieldComment);
return result;
}
private static final DDLQuery parseAlterTableAlterColumn(ParserContext ctx, AlterTableStep s1) {