Skip to content

Commit

Permalink
Fix parsing ALTER TABLE ... ADD COLUMNS (...)
Browse files Browse the repository at this point in the history
The current behaviour fails when it encounters `ADD COLUMNS` and merges
everything in the `()` into one string with no whitespace.

So `ALTER TABLE catalog.table.name ADD COLUMNS (apples int)` becomes
`ALTER TABLE catalog.table.name ADD COLUMNS (applesint)`.

This commit adds an understanding of how `ADD COLUMNS` to the grammar.
  • Loading branch information
njaremko committed Oct 15, 2024
1 parent be8ff93 commit 9672d76
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class AlterExpression implements Serializable {
private String commentText;

private boolean hasColumn = false;
private boolean hasColumns = false;


private boolean useBrackets = false;
Expand All @@ -80,6 +81,10 @@ public boolean hasColumn() {
return hasColumn;
}

public boolean hasColumns() {
return hasColumns;
}

public boolean useBrackets() {
return useBrackets;
}
Expand All @@ -92,6 +97,10 @@ public void hasColumn(boolean hasColumn) {
this.hasColumn = hasColumn;
}

public void hasColumns(boolean hasColumns) {
this.hasColumns = hasColumns;
}

public String getFkSourceSchema() {
return fkSourceSchema;
}
Expand Down Expand Up @@ -503,6 +512,8 @@ public String toString() {
} else {
if (hasColumn) {
b.append("COLUMN ");
} else if (hasColumns) {
b.append("COLUMNS ");
}
if (useIfNotExists
&& operation == AlterOperation.ADD) {
Expand Down
68 changes: 37 additions & 31 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ DescribeStatement Describe(): {
DescribeStatement stmt = new DescribeStatement();
Token tk = null;
} {
(tk=<K_DESCRIBE> | tk=<K_DESC>)
(tk=<K_DESCRIBE> | tk=<K_DESC>)
table = Table() { stmt.setDescribeType(tk.image).setTable(table); }
{
return stmt;
Expand Down Expand Up @@ -1388,13 +1388,13 @@ Statement RefreshMaterializedView(): {
}
{
<K_REFRESH> <K_MATERIALIZED> <K_VIEW>
[ LOOKAHEAD(2) <K_CONCURRENTLY> { concurrently = true; } ]
[ LOOKAHEAD(2) <K_CONCURRENTLY> { concurrently = true; } ]
view = Table()
[
<K_WITH> { refreshMode = RefreshMode.WITH_DATA; }
[
[
<K_NO> { refreshMode = RefreshMode.WITH_NO_DATA; }
]
]
<K_DATA>
]
captureRest = captureRest()
Expand Down Expand Up @@ -2017,7 +2017,7 @@ The following tokens are allowed as Names for Schema, Table, Column and Aliases
String RelObjectNameWithoutValue() :
{ Token tk = null; }
{
( tk=<DATA_TYPE> | tk=<S_IDENTIFIER> | tk=<S_QUOTED_IDENTIFIER> | tk=<K_DATE_LITERAL> | tk=<K_DATETIMELITERAL> | tk=<K_STRING_FUNCTION_NAME> | tk=<K_ISOLATION> | tk=<K_TIME_KEY_EXPR> | tk=<K_TEXT_LITERAL>
( tk=<DATA_TYPE> | tk=<S_IDENTIFIER> | tk=<S_QUOTED_IDENTIFIER> | tk=<K_DATE_LITERAL> | tk=<K_DATETIMELITERAL> | tk=<K_STRING_FUNCTION_NAME> | tk=<K_ISOLATION> | tk=<K_TIME_KEY_EXPR> | tk=<K_TEXT_LITERAL>
| tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="APPROXIMATE" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="BASE64" | tk="BEGIN" | tk="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | tk="BROWSE" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMIT" | tk="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="COUNT" | tk="CS" | tk="CYCLE" | tk="DATA" | tk="DATABASE" | tk="DATETIME" | tk="DBA_RECYCLEBIN" | tk="DDL" | tk="DECLARE" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="ELEMENTS" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ERROR" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXPLICIT" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GRANT" | tk="GROUP_CONCAT" | tk="GUARD" | tk="HASH" | tk="HIGH_PRIORITY" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCLUDE_NULL_VALUES" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="JSON_ARRAY" | tk="JSON_ARRAYAGG" | tk="JSON_OBJECT" | tk="JSON_OBJECTAGG" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LONGTEXT" | tk="LOOP" | tk="LOW_PRIORITY" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAX" | tk="MAXVALUE" | tk="MEDIUMTEXT" | tk="MEMBER" | tk="MERGE" | tk="MIN" | tk="MINVALUE" | tk="MODIFY" | tk="MOVEMENT" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NOORDER" | tk="NOTHING" | tk="NOTNULL" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERFLOW" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARENT" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PLAN" | tk="PRECEDING" | tk="PRIMARY" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="RAW" | tk="READ" | tk="RECURSIVE" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGEXP" | tk="REGEXP_LIKE" | tk="REGISTER" | tk="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESPECT" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROLLUP" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAFE_CAST" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SECURE" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="STORED" | tk="STRING" | tk="STRUCT" | tk="SUMMARIZE" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="TEXT" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TIMEZONE" | tk="TINYTEXT" | tk="TO" | tk="TRIGGER" | tk="TRUE" | tk="TRUNCATE" | tk="TRY_CAST" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="VOLATILE" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WITHOUT_ARRAY_WRAPPER" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLDATA" | tk="XMLSCHEMA" | tk="XMLTEXT" | tk="XSINIL" | tk="YAML" | tk="YES" | tk="ZONE" )
{ return tk.image; }
}
Expand Down Expand Up @@ -2227,7 +2227,7 @@ TableStatement TableStatement():
List<OrderByElement> orderByElements = null;
Limit limit = null;
Offset offset = null;
TableStatement tableStatement = new TableStatement();
TableStatement tableStatement = new TableStatement();
}{
<K_TABLE>
table = Table()
Expand Down Expand Up @@ -2560,12 +2560,12 @@ PlainSelect PlainSelect() #PlainSelect:
[ LOOKAHEAD(<K_LIMIT>, { limit==null }) limit = LimitWithOffset() { plainSelect.setLimit(limit); } ]
[ LOOKAHEAD(<K_FETCH>) fetch = Fetch() { plainSelect.setFetch(fetch); } ]
[ LOOKAHEAD(<K_WITH> <K_ISOLATION>) withIsolation = WithIsolation() { plainSelect.setIsolation(withIsolation); } ]
[ LOOKAHEAD(2)
<K_FOR>
[ LOOKAHEAD(2)
<K_FOR>
(
<K_UPDATE> { plainSelect.setForMode(ForMode.UPDATE); }
| <K_SHARE> { plainSelect.setForMode(ForMode.SHARE); }
| (<K_NO> <K_KEY> <K_UPDATE> { plainSelect.setForMode(ForMode.NO_KEY_UPDATE); })
<K_UPDATE> { plainSelect.setForMode(ForMode.UPDATE); }
| <K_SHARE> { plainSelect.setForMode(ForMode.SHARE); }
| (<K_NO> <K_KEY> <K_UPDATE> { plainSelect.setForMode(ForMode.NO_KEY_UPDATE); })
| (<K_KEY> <K_SHARE> { plainSelect.setForMode(ForMode.KEY_SHARE); })
)
[ LOOKAHEAD(2) <K_OF> updateTable = Table() { plainSelect.setForUpdateTable(updateTable); } ]
Expand Down Expand Up @@ -3480,7 +3480,7 @@ GroupByElement GroupByColumnReferences():
<K_GROUP> <K_BY>
(
LOOKAHEAD(2) (
<K_GROUPING> <K_SETS>
<K_GROUPING> <K_SETS>
"("
list = GroupingSet() { groupBy.addGroupingSet(list); }
( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })*
Expand All @@ -3490,7 +3490,7 @@ GroupByElement GroupByColumnReferences():
(
list = ExpressionList() { groupBy.setGroupByExpressions(list); }
(
LOOKAHEAD(2) <K_GROUPING> <K_SETS>
LOOKAHEAD(2) <K_GROUPING> <K_SETS>
"("
list = GroupingSet() { groupBy.addGroupingSet(list); }
( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })*
Expand Down Expand Up @@ -6064,10 +6064,10 @@ CreateIndex CreateIndex():
)
|
(
[ <K_USING> using=<S_IDENTIFIER> {
[ <K_USING> using=<S_IDENTIFIER> {
index.setUsing(using.image);
createIndex.setIndexTypeBeforeOn(true);
}
}
]
<K_ON> table=Table()
)
Expand Down Expand Up @@ -6605,8 +6605,8 @@ List<String> CreateViewTailComment():
if (op != null) {
result.add(op);
}
result.add(tk2.image);
}
result.add(tk2.image);
}
{ return result;}
}

Expand Down Expand Up @@ -7071,11 +7071,17 @@ AlterExpression AlterExpression():
)
|
LOOKAHEAD(3) (
( LOOKAHEAD(2) <K_COLUMN> { alterExp.hasColumn(true); } )?
( LOOKAHEAD(2)
(
<K_COLUMN> { alterExp.hasColumn(true); }
|
<K_COLUMNS> { alterExp.hasColumns(true); }
)
)?
[ <K_IF> <K_NOT> <K_EXISTS> { alterExp.setUseIfNotExists(true); } ]
(
LOOKAHEAD(4) (
"("
"("
{ alterExp.useBrackets(true);}
alterExpressionColumnDataType = AlterExpressionColumnDataType() {
alterExp.addColDataType(alterExpressionColumnDataType);
Expand All @@ -7089,22 +7095,22 @@ AlterExpression AlterExpression():
")"
)
|
LOOKAHEAD(2) alterExpressionColumnDataType = AlterExpressionColumnDataType()
LOOKAHEAD(2) alterExpressionColumnDataType = AlterExpressionColumnDataType()
{ alterExp.addColDataType(alterExpressionColumnDataType); }
|
LOOKAHEAD(3) alterExpressionColumnDropNotNull = AlterExpressionColumnDropNotNull()
LOOKAHEAD(3) alterExpressionColumnDropNotNull = AlterExpressionColumnDropNotNull()
{ alterExp.addColDropNotNull( alterExpressionColumnDropNotNull);}
|
alterExpressionColumnDropDefault = AlterExpressionColumnDropDefault()
alterExpressionColumnDropDefault = AlterExpressionColumnDropDefault()
{ alterExp.addColDropDefault( alterExpressionColumnDropDefault); }
)
)
|
(
"(" alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); }
(","
alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); }
)*
(","
alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); }
)*
")"
)
|
Expand Down Expand Up @@ -7156,7 +7162,7 @@ AlterExpression AlterExpression():
[LOOKAHEAD(2) (<K_ON> (tk=<K_DELETE> | tk=<K_UPDATE>) action = Action()
{ fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); }
)]
[LOOKAHEAD(2) (<K_ON> (tk=<K_DELETE> | tk=<K_UPDATE>) action = Action()
[LOOKAHEAD(2) (<K_ON> (tk=<K_DELETE> | tk=<K_UPDATE>) action = Action()
{ fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); }
)]
constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); }
Expand Down Expand Up @@ -7310,18 +7316,18 @@ AlterExpression AlterExpression():
)
|
LOOKAHEAD(2)
(<K_RENAME> ((<K_INDEX> {alterExp.setOperation(AlterOperation.RENAME_INDEX);}
| <K_KEY> {alterExp.setOperation(AlterOperation.RENAME_KEY);})
(<K_RENAME> ((<K_INDEX> {alterExp.setOperation(AlterOperation.RENAME_INDEX);}
| <K_KEY> {alterExp.setOperation(AlterOperation.RENAME_KEY);})
| <K_CONSTRAINT> { alterExp.setOperation(AlterOperation.RENAME_CONSTRAINT); }
)
)
(tk=<S_IDENTIFIER> | tk=<S_QUOTED_IDENTIFIER>){
alterExp.setOldIndex(new Index().withName(tk.image));
}
}
<K_TO>
(tk2=<S_IDENTIFIER> | tk2=<S_QUOTED_IDENTIFIER>){
index = new Index().withName(tk2.image);
alterExp.setIndex(index);
}
}
)
|
LOOKAHEAD(2) <K_TRUNCATE> <K_PARTITION> { alterExp.setOperation(AlterOperation.TRUNCATE_PARTITION); } truncatePartitionName = RelObjectName() { alterExp.setTruncatePartitionName(truncatePartitionName); }
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ public void testAlterTableAddColumn() throws JSQLParserException {
assertEquals("varchar (255)", colDataTypes.get(0).getColDataType().toString());
}

@Test
public void testAlterTableAddColumnsWhitespace() throws JSQLParserException {
Statement stmt =
CCJSqlParserUtil.parse("ALTER TABLE test_catalog.test20241014.tt ADD COLUMNS (apples string, bees int)");
assertTrue(stmt instanceof Alter);
Alter alter = (Alter) stmt;
assertEquals("test_catalog.test20241014.tt", alter.getTable().getFullyQualifiedName());
AlterExpression alterExp = alter.getAlterExpressions().get(0);
assertNotNull(alterExp);
List<ColumnDataType> colDataTypes = alterExp.getColDataTypeList();
assertEquals("apples", colDataTypes.get(0).getColumnName());
assertEquals("string", colDataTypes.get(0).getColDataType().toString());
assertEquals("bees", colDataTypes.get(1).getColumnName());
assertEquals("int", colDataTypes.get(1).getColDataType().toString());
}

@Test
public void testAlterTableAddColumn_ColumnKeyWordImplicit() throws JSQLParserException {
Statement stmt = CCJSqlParserUtil.parse("ALTER TABLE mytable ADD mycolumn varchar (255)");
Expand Down

0 comments on commit 9672d76

Please sign in to comment.