From 20e8982b989f6b662c1c99423599e433f5a1bf21 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 10 Apr 2021 06:44:00 +0700 Subject: [PATCH 1/4] Implement Oracle Hints for INSERT, UPDATE, MERGE, DELETE --- .../jsqlparser/statement/delete/Delete.java | 10 +++ .../jsqlparser/statement/insert/Insert.java | 10 +++ .../sf/jsqlparser/statement/merge/Merge.java | 10 +++ .../jsqlparser/statement/update/Update.java | 12 ++- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 8 +- .../statement/delete/DeleteTest.java | 10 +++ .../statement/insert/InsertTest.java | 8 ++ .../jsqlparser/statement/merge/MergeTest.java | 19 +++++ .../statement/update/UpdateTest.java | 8 ++ .../net/sf/jsqlparser/test/TestUtils.java | 75 +++++++++++++------ 10 files changed, 141 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java index 5c9dbfc6a..18eea1cfb 100644 --- a/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java +++ b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java @@ -16,6 +16,7 @@ import java.util.Optional; import static java.util.stream.Collectors.joining; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; @@ -27,6 +28,7 @@ public class Delete implements Statement { private Table table; + private OracleHint oracleHint = null; private List tables; private List joins; private Expression where; @@ -61,6 +63,14 @@ public void setTable(Table name) { public void setWhere(Expression expression) { where = expression; } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } public Limit getLimit() { return limit; diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java index 469c94805..f23717c68 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.Optional; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.expression.operators.relational.ItemsList; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; @@ -29,6 +30,7 @@ public class Insert implements Statement { private Table table; + private OracleHint oracleHint = null; private List columns; private ItemsList itemsList; private boolean useValues = true; @@ -61,6 +63,14 @@ public Table getTable() { public void setTable(Table name) { table = name; } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } public List getColumns() { return columns; diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java index bd894ca38..985ed6603 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java @@ -11,6 +11,7 @@ import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; @@ -19,6 +20,7 @@ public class Merge implements Statement { private Table table; + private OracleHint oracleHint = null; private Table usingTable; private SubSelect usingSelect; private Alias usingAlias; @@ -34,6 +36,14 @@ public Table getTable() { public void setTable(Table name) { table = name; } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } public Table getUsingTable() { return usingTable; diff --git a/src/main/java/net/sf/jsqlparser/statement/update/Update.java b/src/main/java/net/sf/jsqlparser/statement/update/Update.java index 8b3ece703..6fc03c399 100644 --- a/src/main/java/net/sf/jsqlparser/statement/update/Update.java +++ b/src/main/java/net/sf/jsqlparser/statement/update/Update.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Optional; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; @@ -39,6 +40,7 @@ public class Update implements Statement { private Select select; private boolean useColumnsBrackets = true; private boolean useSelect = false; + private OracleHint oracleHint = null; private List orderByElements; private Limit limit; private boolean returningAllColumns = false; @@ -64,6 +66,14 @@ public void setTable(Table table) { public void setWhere(Expression expression) { where = expression; } + + public OracleHint getOracleHint() { + return oracleHint; + } + + public void setOracleHint(OracleHint oracleHint) { + this.oracleHint = oracleHint; + } public List getColumns() { return columns; @@ -160,7 +170,7 @@ public List getReturningExpressionList() { public void setReturningExpressionList(List returningExpressionList) { this.returningExpressionList = returningExpressionList; } - + @Override public String toString() { StringBuilder b = new StringBuilder("UPDATE "); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 3076b6d04..64c84aa37 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -879,7 +879,7 @@ Update Update(): List returning = null; } { - table=TableWithAlias() + { update.setOracleHint(getOracleHint()); } table=TableWithAlias() startJoins=JoinsList() @@ -1023,7 +1023,7 @@ Insert Insert( List with ): boolean useAs = false; } { - + { insert.setOracleHint(getOracleHint()); } [(tk = | tk = | tk = ) {if (tk!=null) modifierPriority = InsertModifierPriority.valueOf(tk.image.toUpperCase()); @@ -1209,7 +1209,7 @@ Delete Delete(): List orderByElements; } { - [LOOKAHEAD(2) (table=TableWithAlias() { tables.add(table); } + { delete.setOracleHint(getOracleHint()); } [LOOKAHEAD(2) (table=TableWithAlias() { tables.add(table); } ("," table=TableWithAlias() { tables.add(table); } )* | )] @@ -1235,7 +1235,7 @@ Statement Merge() : { MergeInsert insert; } { - table=TableWithAlias() { merge.setTable(table); } + { merge.setOracleHint(getOracleHint()); } table=TableWithAlias() { merge.setTable(table); } ( table=Table() { merge.setUsingTable(table); } | "(" select=SubSelect() { merge.setUsingSelect(select); } ")" ) diff --git a/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java b/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java index 416bdd0a2..f7785968f 100644 --- a/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java @@ -21,6 +21,7 @@ import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.schema.Column; +import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists; import org.junit.Test; public class DeleteTest { @@ -94,4 +95,13 @@ public void testDeleteFromTableUsingInnerJoinToAnotherTableWithAlias() throws JS public void testDeleteMultiTableIssue878() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("DELETE table1, table2 FROM table1, table2"); } + + @Test + public void testOracleHint() throws JSQLParserException { + String sql = "DELETE /*+ SOMEHINT */ FROM mytable WHERE mytable.col = 9"; + + assertOracleHintExists(sql, true, "SOMEHINT"); + + //@todo: add a testcase supposed to not finding a misplaced hint + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java b/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java index 87a1c95cb..5cac42e9a 100644 --- a/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java @@ -34,6 +34,7 @@ import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; +import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists; public class InsertTest { @@ -350,4 +351,11 @@ public void testDisableKeywordIssue945() throws JSQLParserException { public void testWithListIssue282() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("WITH myctl AS (SELECT a, b FROM mytable) INSERT INTO mytable SELECT a, b FROM myctl"); } + + @Test + public void testOracleHint() throws JSQLParserException { + assertOracleHintExists("INSERT /*+ SOMEHINT */ INTO mytable VALUES (1, 2, 3)", true, "SOMEHINT"); + + //@todo: add a testcase supposed to not finding a misplaced hint + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java index 0cf0037b7..985407278 100644 --- a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java @@ -176,4 +176,23 @@ public void testMergeUpdateInsertOrderIssue401_3() throws JSQLParserException { //expected to fail } } + + @Test + public void testOracleHint() throws JSQLParserException { + String sql = "MERGE /*+ SOMEHINT */ INTO bonuses B\n" + + "USING (\n" + + " SELECT employee_id, salary\n" + + " FROM employee\n" + + " WHERE dept_no =20) E\n" + + "ON (B.employee_id = E.employee_id)\n" + + "WHEN MATCHED THEN\n" + + " UPDATE SET B.bonus = E.salary * 0.1\n" + + "WHEN NOT MATCHED THEN\n" + + " INSERT (B.employee_id, B.bonus)\n" + + " VALUES (E.employee_id, E.salary * 0.05) "; + + assertOracleHintExists(sql, true, "SOMEHINT"); + + //@todo: add a testcase supposed to not finding a misplaced hint + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java index ec50e5e48..379fa26e0 100644 --- a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java @@ -165,4 +165,12 @@ public void testUpdateIssue962Validate() throws JSQLParserException { public void testUpdateVariableAssignment() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("UPDATE transaction_id SET latest_id_wallet = (@cur_id_wallet := latest_id_wallet) + 1"); } + + @Test + public void testOracleHint() throws JSQLParserException { + assertOracleHintExists("UPDATE /*+ SOMEHINT */ mytable set col1='as', col2=?, col3=565 Where o >= 3", true, "SOMEHINT"); + + //@todo: add a testcase supposed to not finding a misplaced hint + // assertOracleHintExists("UPDATE mytable /*+ SOMEHINT */ set col1='as', col2=?, col3=565 Where o >= 3", true, "SOMEHINT"); + } } diff --git a/src/test/java/net/sf/jsqlparser/test/TestUtils.java b/src/test/java/net/sf/jsqlparser/test/TestUtils.java index f94ad1360..a5a0893d8 100644 --- a/src/test/java/net/sf/jsqlparser/test/TestUtils.java +++ b/src/test/java/net/sf/jsqlparser/test/TestUtils.java @@ -29,9 +29,12 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.parser.Node; import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.util.deparser.ExpressionDeParser; import net.sf.jsqlparser.util.deparser.SelectDeParser; import net.sf.jsqlparser.util.deparser.StatementDeParser; @@ -281,31 +284,55 @@ public static void assertExpressionCanBeDeparsedAs(final Expression parsed, Stri assertEquals(expression, stringBuilder.toString()); } - public static void assertOracleHintExists(String sql, boolean assertDeparser, String... hints) throws JSQLParserException { - if (assertDeparser) { - assertSqlCanBeParsedAndDeparsed(sql, true); - } - Select stmt = (Select) CCJSqlParserUtil.parse(sql); - if (stmt.getSelectBody() instanceof PlainSelect) { - PlainSelect ps = (PlainSelect) stmt.getSelectBody(); - OracleHint hint = ps.getOracleHint(); - assertNotNull(hint); - assertEquals(hints[0], hint.getValue()); - } else { - if (stmt.getSelectBody() instanceof SetOperationList) { - SetOperationList setop = (SetOperationList) stmt.getSelectBody(); - for (int i = 0; i < setop.getSelects().size(); i++) { - PlainSelect pselect = (PlainSelect) setop.getSelects().get(i); - OracleHint hint = pselect.getOracleHint(); - if (hints[i] == null) { - Assert.assertNull(hint); - } else { - assertNotNull(hint); - assertEquals(hints[i], hint.getValue()); - } - } + public static void assertOracleHintExists(String sql, boolean assertDeparser, String... hints) + throws JSQLParserException { + if (assertDeparser) { + assertSqlCanBeParsedAndDeparsed(sql, true); + } + + Statement statement = CCJSqlParserUtil.parse(sql); + if (statement instanceof Select) { + Select stmt = (Select) statement; + if (stmt.getSelectBody() instanceof PlainSelect) { + PlainSelect ps = (PlainSelect) stmt.getSelectBody(); + OracleHint hint = ps.getOracleHint(); + assertNotNull(hint); + assertEquals(hints[0], hint.getValue()); + } else { + if (stmt.getSelectBody() instanceof SetOperationList) { + SetOperationList setop = (SetOperationList) stmt.getSelectBody(); + for (int i = 0; i < setop.getSelects().size(); i++) { + PlainSelect pselect = (PlainSelect) setop.getSelects().get(i); + OracleHint hint = pselect.getOracleHint(); + if (hints[i] == null) { + Assert.assertNull(hint); + } else { + assertNotNull(hint); + assertEquals(hints[i], hint.getValue()); } + } } + } + } else if (statement instanceof Update) { + Update stmt = (Update) statement; + OracleHint hint = stmt.getOracleHint(); + assertNotNull(hint); + assertEquals(hints[0], hint.getValue()); + } else if (statement instanceof Insert) { + Insert stmt = (Insert) statement; + OracleHint hint = stmt.getOracleHint(); + assertNotNull(hint); + assertEquals(hints[0], hint.getValue()); + } else if (statement instanceof Update) { + Update stmt = (Update) statement; + OracleHint hint = stmt.getOracleHint(); + assertNotNull(hint); + assertEquals(hints[0], hint.getValue()); + } else if (statement instanceof Delete) { + Delete stmt = (Delete) statement; + OracleHint hint = stmt.getOracleHint(); + assertNotNull(hint); + assertEquals(hints[0], hint.getValue()); } - + } } From dfb25e1aac5bdebe057e1b06f8f2e644cf9b4dee Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sun, 11 Apr 2021 17:02:34 +0700 Subject: [PATCH 2/4] Correct CreateIndex TailOptions Add a Test Case for CreateIndex TailOptions --- nb-configuration.xml | 2 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 8 +++++--- .../statement/create/CreateIndexTest.java | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/nb-configuration.xml b/nb-configuration.xml index 0e29529e1..c60897d90 100644 --- a/nb-configuration.xml +++ b/nb-configuration.xml @@ -13,7 +13,7 @@ That way multiple projects can share the same settings (useful for formatting rules for example). Any value defined here will override the pom.xml file value but is only applicable to the current project. --> - none + all false true LF diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 64c84aa37..963a8de6d 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -4026,7 +4026,7 @@ CreateIndex CreateIndex(): Index index = null; //String name = null; List parameter = new ArrayList(); - List tailParameter = null; + List tailParameters = new ArrayList(); List name; } { @@ -4047,13 +4047,15 @@ CreateIndex CreateIndex(): colNames = ColumnNamesWithParamsList() - [ tailParameter = CreateParameter() {} ] + /* [ tailParameter = CreateParameter() {} ] */ + + ( parameter=CreateParameter() { tailParameters.addAll(parameter); } )* { index.setColumns(colNames); createIndex.setIndex(index); createIndex.setTable(table); - createIndex.setTailParameters(tailParameter); + createIndex.setTailParameters(tailParameters); return createIndex; } } diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java index 38e1fc4c2..01a233724 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java @@ -10,6 +10,7 @@ package net.sf.jsqlparser.statement.create; import java.io.StringReader; +import java.util.List; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.statement.create.index.CreateIndex; @@ -119,4 +120,20 @@ public void testFullIndexNameIssue936() throws JSQLParserException { public void testFullIndexNameIssue936_2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("CREATE INDEX \"TS\".\"IDX\" ON \"TEST\" (\"ID\") TABLESPACE \"TS\""); } + + @Test + public void testCreateIndexTrailingOptions() throws JSQLParserException { + String statement = + "CREATE UNIQUE INDEX cfe.version_info_idx2\n" + + " ON cfe.version_info ( major_version\n" + + " , minor_version\n" + + " , patch_level ) parallel compress nologging\n" + + ";"; + CreateIndex createIndex = (CreateIndex) parserManager.parse(new StringReader(statement)); + List tailParameters = createIndex.getTailParameters(); + assertEquals(3, tailParameters.size()); + assertEquals(tailParameters.get(0), "parallel"); + assertEquals(tailParameters.get(1), "compress"); + assertEquals(tailParameters.get(2), "nologging"); + } } From 92b64f326dc2d744e8ae24008021fb0ef6e2d834 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 12 Apr 2021 07:08:35 +0700 Subject: [PATCH 3/4] Add WHERE expression to MergeInsert Add test case for MergeInsert WHERE expression --- .../statement/merge/MergeInsert.java | 21 +++++++++++- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 10 ++++-- .../jsqlparser/statement/merge/MergeTest.java | 32 +++++++++++++++++-- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java index eb6c7cf5f..02025eed4 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java @@ -22,6 +22,7 @@ public class MergeInsert { private List columns = null; private List values = null; + private Expression whereCondition; public List getColumns() { return columns; @@ -38,12 +39,21 @@ public List getValues() { public void setValues(List values) { this.values = values; } + + public Expression getWhereCondition() { + return whereCondition; + } + + public void setWhereCondition(Expression whereCondition) { + this.whereCondition = whereCondition; + } @Override public String toString() { return " WHEN NOT MATCHED THEN INSERT " + (columns.isEmpty() ? "" : PlainSelect.getStringList(columns, true, true)) - + " VALUES " + PlainSelect.getStringList(values, true, true); + + " VALUES " + PlainSelect.getStringList(values, true, true) + + (whereCondition != null ? (" WHERE " + whereCondition) : ""); } public MergeInsert withColumns(List columns) { @@ -79,4 +89,13 @@ public MergeInsert addValues(Collection values) { collection.addAll(values); return this.withValues(collection); } + + public MergeInsert withWhereCondition(Expression whereCondition) { + this.setWhereCondition(whereCondition); + return this; + } + + public E getWhereCondition(Class type) { + return type.cast(getWhereCondition()); + } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index cedc4ea67..812827bd9 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1287,14 +1287,18 @@ MergeInsert MergeInsertClause() : { List expList = new ArrayList(); Column col; Expression exp; + Expression condition; } { ["(" col=Column() { columns.add(col); } ("," col=Column() { columns.add(col); } )* ")"] "(" exp=SimpleExpression() { expList.add(exp); } ("," exp=SimpleExpression() { expList.add(exp); } )* ")" - { - return mi.withColumns(columns).withValues(expList); - } + + { mi.withColumns(columns).withValues(expList); } + + [ condition = Expression() { mi.setWhereCondition(condition); }] + + { return mi; } } List RelObjectNameList() : { diff --git a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java index 985407278..7dd309ed8 100644 --- a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java @@ -13,6 +13,7 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.Statement; import static net.sf.jsqlparser.test.TestUtils.*; +import org.assertj.core.api.Assertions; import static org.junit.Assert.fail; import org.junit.Test; @@ -151,9 +152,6 @@ public void testComplexOracleMergeIntoStatement() throws JSQLParserException { + " )\n"; Statement statement = CCJSqlParserUtil.parse(sql); - - System.out.println(statement.toString()); - assertSqlCanBeParsedAndDeparsed(sql, true); } @@ -195,4 +193,32 @@ public void testOracleHint() throws JSQLParserException { //@todo: add a testcase supposed to not finding a misplaced hint } + + @Test + public void testInsertMergeWhere() throws JSQLParserException { + String sql = + "-- Both clauses present.\n" + + "MERGE INTO test1 a\n" + + " USING all_objects b\n" + + " ON (a.object_id = b.object_id)\n" + + " WHEN MATCHED THEN\n" + + " UPDATE SET a.status = b.status\n" + + " WHERE b.status != 'VALID'\n" + + " WHEN NOT MATCHED THEN\n" + + " INSERT (object_id, status)\n" + + " VALUES (b.object_id, b.status)\n" + + "\n" + + " WHERE b.status != 'VALID'\n" + ; + + Statement statement = CCJSqlParserUtil.parse(sql); + assertSqlCanBeParsedAndDeparsed(sql, true); + + Merge merge = (Merge) statement; + MergeInsert mergeInsert = merge.getMergeInsert(); + Assertions.assertThat( mergeInsert.getWhereCondition() ); + + MergeUpdate mergeUpdate = merge.getMergeUpdate(); + Assertions.assertThat( mergeUpdate.getWhereCondition() ); + } } From 805c951abcd984260d74ee84d91348b41591f415 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Tue, 13 Apr 2021 10:50:43 +0700 Subject: [PATCH 4/4] Fix Issue #1156: ALTER TABLE ADD FOREIGN KEY with schema reference Add a specific test case --- .../statement/alter/AlterExpression.java | 31 ++++++++++++++++--- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 10 +++++- .../jsqlparser/statement/alter/AlterTest.java | 14 +++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java index 0d0feb4d7..7385e759a 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java @@ -48,6 +48,8 @@ public class AlterExpression { private Set referentialActions = new LinkedHashSet<>(2); private List fkColumns; + private String fkSourceSchema; + private String fkSourceTable; private List fkSourceColumns; private boolean uk; @@ -56,6 +58,13 @@ public class AlterExpression { private List constraints; private List parameters; private String commentText; + + public String getFkSourceSchema() { + return fkSourceSchema; + } + public void setFkSourceSchema(String fkSourceSchema) { + this.fkSourceSchema = fkSourceSchema; + } public String getCommentText() { return commentText; @@ -405,11 +414,18 @@ public String toString() { } b.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")"); } else if (fkColumns != null) { - b.append("FOREIGN KEY (").append(PlainSelect.getStringList(fkColumns)).append(") REFERENCES ") - .append(fkSourceTable).append(" (").append( - PlainSelect.getStringList(fkSourceColumns)) - .append(")"); - referentialActions.forEach(b::append); + b.append("FOREIGN KEY (") + .append(PlainSelect.getStringList(fkColumns)) + .append(") REFERENCES ") + .append( + fkSourceSchema != null && fkSourceSchema.trim().length() > 0 + ? fkSourceSchema + "." + : "") + .append(fkSourceTable) + .append(" (") + .append(PlainSelect.getStringList(fkSourceColumns)) + .append(")"); + referentialActions.forEach(b::append); } else if (index != null) { b.append(index); } @@ -490,6 +506,11 @@ public AlterExpression withFkColumns(List fkColumns) { this.setFkColumns(fkColumns); return this; } + + public AlterExpression withFkSourceSchema(String fkSourceSchema) { + this.setFkSourceTable(fkSourceSchema); + return this; + } public AlterExpression withFkSourceTable(String fkSourceTable) { this.setFkSourceTable(fkSourceTable); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 812827bd9..2aa2b2f19 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -4796,8 +4796,16 @@ AlterExpression AlterExpression(): | //following two choices regarding foreign keys should be merged ( columnNames=ColumnsNamesList() { alterExp.setFkColumns(columnNames); columnNames = null; } - tk= [ columnNames=ColumnsNamesList() ] + /* + tk= [ columnNames=ColumnsNamesList() ] { alterExp.setFkSourceTable(tk.image); alterExp.setFkSourceColumns(columnNames); } + */ + fkTable=Table() [ columnNames=ColumnsNamesList() ] + { + alterExp.setFkSourceSchema(fkTable.getSchemaName()); + alterExp.setFkSourceTable(fkTable.getName()); + alterExp.setFkSourceColumns(columnNames); + } [LOOKAHEAD(2) ( (tk= | tk=) action = Action() { alterExp.setReferentialAction(ReferentialAction.Type.valueOf(tk.image), action); } diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index 70e06f155..ac084fa1f 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -146,6 +146,20 @@ public void testAlterTableForgeignKey3() throws JSQLParserException { public void testAlterTableForgeignKey4() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id) ON DELETE SET NULL"); } + + @Test + public void testAlterTableForgeignWithFkSchema() throws JSQLParserException { + final String FK_SCHEMA_NAME = "my_schema"; + final String FK_TABLE_NAME= "ra_user"; + String sql = "ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES " + FK_SCHEMA_NAME +"." + FK_TABLE_NAME + " (id) ON DELETE SET NULL"; + assertSqlCanBeParsedAndDeparsed(sql); + + Alter alter = (Alter) CCJSqlParserUtil.parse(sql); + AlterExpression alterExpression = alter.getAlterExpressions().get(0); + + assertEquals(alterExpression.getFkSourceSchema(), FK_SCHEMA_NAME); + assertEquals(alterExpression.getFkSourceTable(), FK_TABLE_NAME); + } @Test public void testAlterTableDropColumn() throws JSQLParserException {