From 35ebbc5280ba2d7a51365ad8af2571f1d3e2542c Mon Sep 17 00:00:00 2001 From: Zhang Zhongliang Date: Tue, 16 Aug 2022 16:17:24 +0800 Subject: [PATCH 1/9] support clickhouse global keyword in join --- .../sf/jsqlparser/statement/select/Join.java | 13 +++++++++++ .../util/deparser/SelectDeParser.java | 4 ++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 1 + .../statement/select/ClickHouseTest.java | 23 +++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Join.java b/src/main/java/net/sf/jsqlparser/statement/select/Join.java index 7c4200bef..661e0c4c8 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Join.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Join.java @@ -21,6 +21,7 @@ public class Join extends ASTNodeAccessImpl { private boolean right = false; private boolean left = false; private boolean natural = false; + private boolean global = false; private boolean full = false; private boolean inner = false; private boolean simple = false; @@ -166,6 +167,10 @@ public boolean isNatural() { return natural; } + public boolean isGlobal() { + return global; + } + public Join withNatural(boolean b) { this.setNatural(b); return this; @@ -175,6 +180,10 @@ public void setNatural(boolean b) { natural = b; } + public void setGlobal(boolean b) { + global = b; + } + /** * Whether is a "FULL" join * @@ -299,6 +308,10 @@ public void setJoinWindow(KSQLJoinWindow joinWindow) { public String toString() { StringBuilder builder = new StringBuilder(); + if(isGlobal()){ + builder.append("GLOBAL "); + } + if (isSimple() && isOuter()) { builder.append("OUTER ").append(rightItem); } else if (isSimple()) { diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java index a997c5056..41be5764e 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -399,6 +399,10 @@ public void visit(SubJoin subjoin) { @SuppressWarnings({"PMD.CyclomaticComplexity"}) public void deparseJoin(Join join) { + if(join.isGlobal()){ + buffer.append(" GLOBAL "); + } + if (join.isSimple() && join.isOuter()) { buffer.append(", OUTER "); } else if (join.isSimple()) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 5bc91eb99..bdc0645d0 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -2719,6 +2719,7 @@ Join JoinerExpression() #JoinerExpression: } { + [ { join.setGlobal(true); } ] [ { join.setNatural(true); } ] [ diff --git a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java new file mode 100644 index 000000000..d7d97f627 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java @@ -0,0 +1,23 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import org.junit.jupiter.api.Test; + +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; + +public class ClickHouseTest { + + @Test + public void testGlobalJoin() throws Exception { + String sql = "SELECT a.*,b.* from lineorder_all as a global left join supplier_all as b on a.LOLINENUMBER=b.SSUPPKEY"; + assertSqlCanBeParsedAndDeparsed(sql, true); + } +} From 09c2fb78ed4e67eb392e5558ad511652cd0714e4 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Thu, 15 Sep 2022 18:21:49 +0700 Subject: [PATCH 2/9] fix: add missing public Getter Add public Getter for `updateSets` Fixes #1630 --- .../jsqlparser/statement/insert/InsertConflictAction.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictAction.java b/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictAction.java index 32d9313eb..efe3161f4 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictAction.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictAction.java @@ -32,15 +32,17 @@ */ public class InsertConflictAction { - ConflictActionType conflictActionType; - private final ArrayList updateSets = new ArrayList<>(); + ConflictActionType conflictActionType; Expression whereExpression; - public InsertConflictAction(ConflictActionType conflictActionType) { this.conflictActionType = Objects.requireNonNull(conflictActionType, "The Conflict Action Type is mandatory and must not be Null."); } + public ArrayList getUpdateSets() { + return updateSets; + } + public ConflictActionType getConflictActionType() { return conflictActionType; } From 7086f18129e91dfacfb3b1ecf9f566a751ad5c85 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 28 Nov 2022 17:26:30 +0700 Subject: [PATCH 3/9] feat: Clickhouse GLOBAL JOIN All credits to @julianzlzhang fixes #1615 fixes #1535 --- src/main/java/net/sf/jsqlparser/statement/select/Join.java | 2 +- .../java/net/sf/jsqlparser/util/deparser/SelectDeParser.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Join.java b/src/main/java/net/sf/jsqlparser/statement/select/Join.java index 661e0c4c8..e56b37cee 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Join.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Join.java @@ -308,7 +308,7 @@ public void setJoinWindow(KSQLJoinWindow joinWindow) { public String toString() { StringBuilder builder = new StringBuilder(); - if(isGlobal()){ + if ( isGlobal() ) { builder.append("GLOBAL "); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java index 84a6550f5..3e2c18e83 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -401,7 +401,7 @@ public void visit(SubJoin subjoin) { @SuppressWarnings({"PMD.CyclomaticComplexity"}) public void deparseJoin(Join join) { - if(join.isGlobal()){ + if ( join.isGlobal() ) { buffer.append(" GLOBAL "); } From f9eee97a46590068e469a4be0f98a35163998871 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 9 Dec 2022 13:42:42 +0700 Subject: [PATCH 4/9] feat: IF/ELSE statements supports Block Make `If... Else...` statements work with Blocks Make `Statement()` production work with `Block()` Rewrite the `Block()` related Unit Tests fixes #1682 --- .../net/sf/jsqlparser/statement/Block.java | 23 +++++++- .../sf/jsqlparser/statement/Statements.java | 4 +- .../util/deparser/StatementDeParser.java | 3 ++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 16 +++--- .../sf/jsqlparser/statement/BlockTest.java | 54 +++++++++++++------ .../oracle-tests/compound_statements03.sql | 3 +- .../statement/select/oracle-tests/loop01.sql | 3 +- .../statement/select/oracle-tests/loop02.sql | 3 +- 8 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/Block.java b/src/main/java/net/sf/jsqlparser/statement/Block.java index 6ee605518..cbbbb3c87 100644 --- a/src/main/java/net/sf/jsqlparser/statement/Block.java +++ b/src/main/java/net/sf/jsqlparser/statement/Block.java @@ -10,6 +10,7 @@ package net.sf.jsqlparser.statement; public class Block implements Statement { + private boolean hasSemicolonAfterEnd = false; private Statements statements; @@ -21,14 +22,34 @@ public void setStatements(Statements statements) { this.statements = statements; } + public boolean hasSemicolonAfterEnd() { + return hasSemicolonAfterEnd; + } + + public void setSemicolonAfterEnd(boolean hasSemicolonAfterEnd) { + this.hasSemicolonAfterEnd = hasSemicolonAfterEnd; + } + @Override public void accept(StatementVisitor statementVisitor) { statementVisitor.visit(this); } + public StringBuilder appendTo(StringBuilder builder) { + builder.append("BEGIN\n"); + if (statements != null) { + builder.append(statements); + } + builder.append("END"); + if (hasSemicolonAfterEnd) { + builder.append(";"); + } + return builder; + } + @Override public String toString() { - return "BEGIN\n" + (statements != null ? statements.toString() : "") + "END"; + return appendTo(new StringBuilder()).toString(); } public Block withStatements(Statements statements) { diff --git a/src/main/java/net/sf/jsqlparser/statement/Statements.java b/src/main/java/net/sf/jsqlparser/statement/Statements.java index 21c130842..363db0795 100644 --- a/src/main/java/net/sf/jsqlparser/statement/Statements.java +++ b/src/main/java/net/sf/jsqlparser/statement/Statements.java @@ -35,8 +35,8 @@ public void accept(StatementVisitor statementVisitor) { public String toString() { StringBuilder b = new StringBuilder(); for (Statement stmt : statements) { - if (stmt instanceof IfElseStatement) { - // IfElseStatements print the Semicolons by themselves + // IfElseStatements and Blocks control the Semicolons by themselves + if (stmt instanceof IfElseStatement || stmt instanceof Block) { b.append(stmt).append("\n"); } else { b.append(stmt).append(";\n"); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index f10651f22..4b8b6da68 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -294,6 +294,9 @@ public void visit(Block block) { } } buffer.append("END"); + if (block.hasSemicolonAfterEnd()) { + buffer.append(";"); + } } @Override diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index c8a156c94..11a1ef1b3 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -513,17 +513,17 @@ Statement Statement() #Statement: try { ( condition=Condition() - stm = SingleStatement() { ifElseStatement = new IfElseStatement(condition, stm); } + ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] [ LOOKAHEAD(2) - stm2 = SingleStatement() { ifElseStatement.setElseStatement(stm2); } + ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } [ { ifElseStatement.setUsingSemicolonForElseStatement(true); }] ] ) | ( - stm = SingleStatement() + ( stm = SingleStatement() | stm = Block() ) [ ] ) @@ -723,7 +723,7 @@ Block Block() #Block : { block.setStatements(stmts); } - [LOOKAHEAD(2) ] + [LOOKAHEAD(2) { block.setSemicolonAfterEnd(true); } ] { return block; } @@ -744,10 +744,10 @@ Statements Statements() #Statements : { ( ( condition=Condition() - stm = SingleStatement() { ifElseStatement = new IfElseStatement(condition, stm); } + ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } [ LOOKAHEAD(2) [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] - stm2 = SingleStatement() { ifElseStatement.setElseStatement(stm2); } + ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } ] { list.add( ifElseStatement ); } @@ -772,10 +772,10 @@ Statements Statements() #Statements : { [ ( condition=Condition() - stm = SingleStatement() { ifElseStatement = new IfElseStatement(condition, stm); } + ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } [ LOOKAHEAD(2) [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] - stm2 = SingleStatement() { ifElseStatement.setElseStatement(stm2); } + ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } ] { list.add( ifElseStatement ); } diff --git a/src/test/java/net/sf/jsqlparser/statement/BlockTest.java b/src/test/java/net/sf/jsqlparser/statement/BlockTest.java index bd0bdd3dc..e0d46acf7 100644 --- a/src/test/java/net/sf/jsqlparser/statement/BlockTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/BlockTest.java @@ -11,12 +11,13 @@ import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import org.junit.jupiter.api.Test; /** - * * @author Tobias Warneke (t.warneke@gmx.net) */ public class BlockTest { @@ -26,24 +27,19 @@ public class BlockTest { */ @Test public void testGetStatements() throws JSQLParserException { - Statements stmts = CCJSqlParserUtil.parseStatements("begin\nselect * from feature;\nend"); - assertEquals("BEGIN\n" - + "SELECT * FROM feature;\n" - + "END;\n", stmts.toString()); + String sqlStr = "begin\n" + + "select * from feature;\n" + + "end;"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); } @Test public void testBlock2() throws JSQLParserException { - Statements stmts = CCJSqlParserUtil.parseStatements("begin\n" - + "update table1 set a = 'xx' where b = 'condition1';\n" - + "update table1 set a = 'xx' where b = 'condition2';\n" - + "end;"); - assertEquals("BEGIN\n" - + "UPDATE table1 SET a = 'xx' WHERE b = 'condition1';\n" - + "UPDATE table1 SET a = 'xx' WHERE b = 'condition2';\n" - + "END;\n" - + "", stmts.toString()); - + String sqlStr="begin\n" + + "update table1 set a = 'xx' where b = 'condition1';\n" + + "update table1 set a = 'xx' where b = 'condition2';\n" + + "end;"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); } @Test @@ -58,7 +54,31 @@ public void testBlockToStringIsNullSafe() throws JSQLParserException { Block block = new Block(); block.setStatements(null); assertEquals("BEGIN\n" - + "END", block.toString()); + + "END", block.toString()); + } + + @Test + public void testIfElseBlock() throws JSQLParserException { + String sqlStr = "if (a=b) begin\n" + + "update table1 set a = 'xx' where b = 'condition1';\n" + + "update table1 set a = 'xx' where b = 'condition2';\n" + + "end"; + + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + + String sqlStr2 = "if (a=b) begin\n" + + "update table1 set a = 'xx' where b = 'condition1';\n" + + "update table1 set a = 'xx' where b = 'condition2';\n" + + "end;\n" + + "else begin\n" + + "update table1 set a = 'xx' where b = 'condition1';\n" + + "update table1 set a = 'xx' where b = 'condition2';\n" + + "end;"; + + Statements statements = CCJSqlParserUtil.parseStatements(sqlStr2); + for (Statement stm : statements.getStatements()) { + TestUtils.assertDeparse(stm, sqlStr2, true); + } } } diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql index 12e993aad..980b443a9 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql @@ -14,4 +14,5 @@ BEGIN example_table; END ---@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on May 27, 2022, 10:29:48 PM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on May 27, 2022, 10:29:48 PM +--@FAILURE: Encountered unexpected token: ":" ":" recorded first on 9 Dec 2022, 12:49:49 \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql index c9d4dd3f1..57b4b5e18 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql @@ -16,4 +16,5 @@ begin bulk collect into :empnos; end; ---@FAILURE: Encountered unexpected token: "begin" "BEGIN" recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "begin" "BEGIN" recorded first on Aug 3, 2021, 7:20:08 AM +--@FAILURE: Encountered unexpected token: "forall" recorded first on 9 Dec 2022, 12:49:50 \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql index 06e0fac64..c2959507c 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql @@ -21,4 +21,5 @@ BEGIN END LOOP outer_loop; END; ---@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on Aug 3, 2021, 7:20:07 AM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on Aug 3, 2021, 7:20:07 AM +--@FAILURE: Encountered unexpected token: "<<" "<<" recorded first on 9 Dec 2022, 12:49:50 \ No newline at end of file From c125fbfbb4f7ddad7ffe01620388cf566a7eabef Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 9 Dec 2022 13:47:46 +0700 Subject: [PATCH 5/9] fix: Revert unintended changes to the Special Oracle Tests --- .../statement/select/oracle-tests/compound_statements03.sql | 3 +-- .../net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql | 3 +-- .../net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql index 980b443a9..12e993aad 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql @@ -14,5 +14,4 @@ BEGIN example_table; END ---@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on May 27, 2022, 10:29:48 PM ---@FAILURE: Encountered unexpected token: ":" ":" recorded first on 9 Dec 2022, 12:49:49 \ No newline at end of file +--@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on May 27, 2022, 10:29:48 PM \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql index 57b4b5e18..c9d4dd3f1 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql @@ -16,5 +16,4 @@ begin bulk collect into :empnos; end; ---@FAILURE: Encountered unexpected token: "begin" "BEGIN" recorded first on Aug 3, 2021, 7:20:08 AM ---@FAILURE: Encountered unexpected token: "forall" recorded first on 9 Dec 2022, 12:49:50 \ No newline at end of file +--@FAILURE: Encountered unexpected token: "begin" "BEGIN" recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql index c2959507c..06e0fac64 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql @@ -21,5 +21,4 @@ BEGIN END LOOP outer_loop; END; ---@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on Aug 3, 2021, 7:20:07 AM ---@FAILURE: Encountered unexpected token: "<<" "<<" recorded first on 9 Dec 2022, 12:49:50 \ No newline at end of file +--@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on Aug 3, 2021, 7:20:07 AM \ No newline at end of file From 42b429f0285c7e504e44fafcb4e242c93377e443 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 9 Dec 2022 14:07:14 +0700 Subject: [PATCH 6/9] fix: `SET` statement supports `UserVariable` Make `SetStatement` parse Objects instead of Names only Add Grammar to accept `UserVariable` (e.g. "set @Flag = 1") Add Test Case for `UserVariable` fixes #1682 --- .../net/sf/jsqlparser/statement/SetStatement.java | 12 ++++++------ .../jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt | 9 +++++---- .../sf/jsqlparser/statement/SetStatementTest.java | 6 ++++++ .../select/oracle-tests/compound_statements03.sql | 3 ++- .../statement/select/oracle-tests/loop01.sql | 3 ++- .../statement/select/oracle-tests/loop02.sql | 3 ++- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/SetStatement.java b/src/main/java/net/sf/jsqlparser/statement/SetStatement.java index 1035ac4f7..849cdd440 100644 --- a/src/main/java/net/sf/jsqlparser/statement/SetStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/SetStatement.java @@ -23,11 +23,11 @@ public SetStatement() { // empty constructor } - public SetStatement(String name, List value) { + public SetStatement(Object name, List value) { add(name, value, true); } - public void add(String name, List value, boolean useEqual) { + public void add(Object name, List value, boolean useEqual) { values.add(new NameExpr(name, value, useEqual)); } @@ -66,11 +66,11 @@ public SetStatement setUseEqual(boolean useEqual) { return setUseEqual(0, useEqual); } - public String getName() { + public Object getName() { return getName(0); } - public String getName(int idx) { + public Object getName(int idx) { return values.get(idx).name; } @@ -129,11 +129,11 @@ public void accept(StatementVisitor statementVisitor) { static class NameExpr { - private String name; + private Object name; private List expressions; private boolean useEqual; - public NameExpr(String name, List expressions, boolean useEqual) { + public NameExpr(Object name, List expressions, boolean useEqual) { this.name = name; this.expressions = expressions; this.useEqual = useEqual; diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 11a1ef1b3..d6c369661 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -861,7 +861,7 @@ DeclareStatement Declare(): { SetStatement Set(): { - String name; + Object name; ArrayList expList; boolean useEqual = false; SetStatement set; @@ -876,10 +876,11 @@ SetStatement Set(): { [LOOKAHEAD(3) (tk = | tk = ) {effectParameter = tk.image; } ] ( LOOKAHEAD(2) { name = "Time Zone"; useEqual=false; } + | (name = UserVariable() ["=" { useEqual=true; } ]) | (name = RelObjectNameExt() ["=" { useEqual=true; } ]) ) ) - exp=SimpleExpression() + exp=Expression() {expList = new ArrayList(); expList.add(exp); } { set = new SetStatement(name,expList).withUseEqual(useEqual).withEffectParameter(effectParameter); } @@ -892,12 +893,12 @@ SetStatement Set(): { { name = "Time Zone"; useEqual=false; } | (name = RelObjectNameExt() ["=" { useEqual=true; } ]) ) - exp=SimpleExpression() + exp=Expression() {expList = new ArrayList(); expList.add(exp); set.add(name, expList, useEqual);} ) - | exp=SimpleExpression() { expList.add(exp); } + | exp=Expression() { expList.add(exp); } ))* { return set; } } diff --git a/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java b/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java index 605d0af5e..d845cdf53 100644 --- a/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java @@ -70,4 +70,10 @@ public void testObject() { assertEquals(0, setStatement.getCount()); } + + @Test + public void testSettingUserVariable() throws JSQLParserException { + String sqlStr="set @Flag = 1"; + assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql index 12e993aad..d75c5a304 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/compound_statements03.sql @@ -14,4 +14,5 @@ BEGIN example_table; END ---@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on May 27, 2022, 10:29:48 PM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on May 27, 2022, 10:29:48 PM +--@FAILURE: Encountered unexpected token: ":" ":" recorded first on 9 Dec 2022, 14:03:29 \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql index c9d4dd3f1..409d8754d 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop01.sql @@ -16,4 +16,5 @@ begin bulk collect into :empnos; end; ---@FAILURE: Encountered unexpected token: "begin" "BEGIN" recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "begin" "BEGIN" recorded first on Aug 3, 2021, 7:20:08 AM +--@FAILURE: Encountered unexpected token: "forall" recorded first on 9 Dec 2022, 14:03:29 \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql index 06e0fac64..035af9ec2 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/loop02.sql @@ -21,4 +21,5 @@ BEGIN END LOOP outer_loop; END; ---@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on Aug 3, 2021, 7:20:07 AM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "BEGIN" "BEGIN" recorded first on Aug 3, 2021, 7:20:07 AM +--@FAILURE: Encountered unexpected token: "<<" "<<" recorded first on 9 Dec 2022, 14:03:29 \ No newline at end of file From b4eb8d834e5af531ab7bd579d4a91d2d57748756 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 9 Dec 2022 20:12:47 +0700 Subject: [PATCH 7/9] feat: Google Spanner Support Replaces PR #1415, all credit goes to @s13o Re-arranged some recently added Tokens in alphabetical order Update Keywords --- .../expression/SpannerInterleaveIn.java | 75 +++++++++++++++++++ .../statement/create/table/CreateTable.java | 19 +++++ .../util/deparser/CreateTableDeParser.java | 3 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 62 ++++++++++++--- .../jsqlparser/statement/alter/AlterTest.java | 66 ++++++++++++++-- .../statement/create/CreateTableTest.java | 36 ++++++++- 6 files changed, 244 insertions(+), 17 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java diff --git a/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java b/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java new file mode 100644 index 000000000..59a8ec856 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/SpannerInterleaveIn.java @@ -0,0 +1,75 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2021 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.schema.Table; + +import java.util.Collections; +import java.util.List; + +public class SpannerInterleaveIn { + + public enum OnDelete { + CASCADE, + NO_ACTION + } + + private Table table; + private OnDelete onDelete; + + public SpannerInterleaveIn() { + + } + + public SpannerInterleaveIn(Table table, OnDelete action) { + setTable(table); + setOnDelete(action); + } + + public SpannerInterleaveIn(List nameParts) { + this(new Table(nameParts), null); + } + + public SpannerInterleaveIn(String tableName) { + this(Collections.singletonList(tableName)); + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public OnDelete getOnDelete() { + return onDelete; + } + + public void setOnDelete(OnDelete action) { + this.onDelete = action; + } + + @Override + public String toString() { + return "INTERLEAVE IN PARENT " + getTable().getName() + + (getOnDelete() == null ? "" : " ON DELETE " + (getOnDelete() == OnDelete.CASCADE ? "CASCADE" : "NO ACTION")); + } + + public SpannerInterleaveIn withTable(Table table) { + this.setTable(table); + return this; + } + + public SpannerInterleaveIn withOnDelete(OnDelete action) { + this.setOnDelete(action); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java b/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java index 8fb5b4cc1..d3793589e 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Optional; +import net.sf.jsqlparser.expression.SpannerInterleaveIn; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; @@ -38,6 +39,8 @@ public class CreateTable implements Statement { private RowMovement rowMovement; + private SpannerInterleaveIn interleaveIn = null; + @Override public void accept(StatementVisitor statementVisitor) { statementVisitor.visit(this); @@ -199,6 +202,9 @@ public String toString() { if (likeTable != null) { sql += " LIKE " + (selectParenthesis ? "(" : "") + likeTable.toString() + (selectParenthesis ? ")" : ""); } + if (interleaveIn != null) { + sql += ", " + interleaveIn; + } return sql; } @@ -299,4 +305,17 @@ public CreateTable addIndexes(Collection indexes) { collection.addAll(indexes); return this.withIndexes(collection); } + + public SpannerInterleaveIn getSpannerInterleaveIn() { + return interleaveIn; + } + + public void setSpannerInterleaveIn(SpannerInterleaveIn spannerInterleaveIn) { + this.interleaveIn = spannerInterleaveIn; + } + + public CreateTable withSpannerInterleaveIn(SpannerInterleaveIn spannerInterleaveIn) { + this.interleaveIn = spannerInterleaveIn; + return this; + } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java index 4036d9ad0..d930e944c 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java @@ -120,6 +120,9 @@ public void deParse(CreateTable createTable) { buffer.append(")"); } } + if (createTable.getSpannerInterleaveIn() != null) { + buffer.append(", ").append(createTable.getSpannerInterleaveIn()); + } } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index d6c369661..181e4db2e 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -155,6 +155,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -163,8 +164,6 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | /* H2 casewhen function */ | -| -| | | | @@ -258,6 +257,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -325,6 +325,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -350,6 +351,8 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| +| | | | @@ -359,6 +362,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -367,8 +371,6 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | -| -| | | | @@ -378,6 +380,8 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| +| | | | @@ -399,6 +403,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -1828,7 +1833,7 @@ String RelObjectNameWithoutValue() : { Token tk = null; } { ( tk= | tk= | tk= | tk= | tk= | tk= | tk= - | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="BEGIN" | tk="BINARY" | tk="BIT" | tk="BUFFERS" | tk="BY" | tk="BYTE" | 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="CONFLICT" | tk="CONSTRAINTS" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATABASE" | tk="DDL" | tk="DECLARE" | tk="DEFAULT" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GLOBAL" | tk="GRANT" | tk="GUARD" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MERGE" | 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="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PRECEDING" | tk="PRECISION" | tk="PRIMARY" | tk="PRIOR" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="READ" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REGISTER" | tk="RENAME" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RLIKE" | tk="ROLLBACK" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TO" | tk="TRUE" | tk="TRUNCATE" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNQUIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLTEXT" | tk="YAML" | tk="ZONE" ) + | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="BEGIN" | tk="BINARY" | tk="BIT" | 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="CONFLICT" | tk="CONSTRAINTS" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATABASE" | tk="DDL" | tk="DECLARE" | tk="DEFAULT" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GLOBAL" | tk="GRANT" | tk="GUARD" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MERGE" | 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="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARENT" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PRECEDING" | tk="PRECISION" | tk="PRIMARY" | tk="PRIOR" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="READ" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REGISTER" | tk="RENAME" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RLIKE" | tk="ROLLBACK" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="STORED" | tk="STRING" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TO" | tk="TRUE" | tk="TRUNCATE" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNQUIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLTEXT" | tk="YAML" | tk="ZONE" ) { return tk.image; } } @@ -5164,6 +5169,7 @@ CreateTable CreateTable(): List parameter = new ArrayList(); List idxSpec = new ArrayList(); Table fkTable = null; + SpannerInterleaveIn interleaveIn = null; Select select = null; Table likeTable = null; CheckConstraint checkCs = null; @@ -5336,6 +5342,7 @@ CreateTable CreateTable(): ( LOOKAHEAD("(" Table() ")") "(" likeTable=Table() { createTable.setLikeTable(likeTable, true); } ")" | likeTable=Table() { createTable.setLikeTable(likeTable, false); } ) ] + [ interleaveIn = SpannerInterleaveIn( ) { createTable.setSpannerInterleaveIn(interleaveIn); } ] { createTable.setTable(table); if (indexes.size() > 0) @@ -5352,6 +5359,25 @@ CreateTable CreateTable(): } } +SpannerInterleaveIn SpannerInterleaveIn(): +{ + Table table = null; + SpannerInterleaveIn.OnDelete action = null; +} +{ + table = Table() + [ + + ( + { action = SpannerInterleaveIn.OnDelete.NO_ACTION; } + | { action = SpannerInterleaveIn.OnDelete.CASCADE; } + ) + ] + { + return new SpannerInterleaveIn(table, action); + } +} + ColDataType ColDataType(): { ColDataType colDataType = new ColDataType(); @@ -5360,13 +5386,25 @@ ColDataType ColDataType(): List argumentsStringList = new ArrayList(); List array = new ArrayList(); List name; + ColDataType arrayType; } { ( - (tk= | tk=) [tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } - | tk= [LOOKAHEAD(2) tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } + tk= ( + ("<" arrayType = ColDataType() ">") { + colDataType.setDataType("ARRAY<" + arrayType.getDataType() + ">"); + } + ) + | + LOOKAHEAD(2) ( + tk= "(" (tk2= | tk2=) ")" + | tk= "(" (tk2= | tk2=) ")" + | tk= "(" (tk2= | tk2=) ")" + ) { colDataType.setDataType(tk.image + " (" + tk2.image + ")"); } + | (tk= | tk=) [tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } + | tk= [LOOKAHEAD(2) tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } | ( tk= | tk= | tk= | tk= | tk= | tk= - | tk= | tk= | tk= | tk= | tk= ) + | tk= | tk= | tk= | tk= | tk= | tk= ) [ "." (tk2= | tk2=) ] { if (tk2!=null) colDataType.setDataType(tk.image + "." + tk2.image); else colDataType.setDataType(tk.image); } | tk= [LOOKAHEAD(2) tk2=] @@ -5505,7 +5543,13 @@ List CreateParameter(): ) { param.add(retval); } | - tk= { param.add(tk.image); } + tk= ( + ("(" exp = Expression() ")") { param.add("AS"); param.add("(" + exp.toString() + ")");} + | + { param.add(tk.image);} + ) + | + tk= { param.add(tk.image); } | tk= { param.add(tk.image); } | 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 544fe253b..0bc241766 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; + import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; @@ -31,6 +32,7 @@ import net.sf.jsqlparser.statement.create.table.NamedConstraint; import static net.sf.jsqlparser.test.TestUtils.*; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -287,7 +289,7 @@ public void testAlterTableAddColumn5() throws JSQLParserException { assertEquals("col1", col1DataTypes.get(0).getColumnName()); assertEquals("timestamp (3)", col1DataTypes.get(0).getColDataType().toString()); - assertEquals(col1Exp.hasColumn(), false); + assertFalse(col1Exp.hasColumn()); } @Test @@ -301,7 +303,7 @@ public void testAlterTableAddColumn6() throws JSQLParserException { assertEquals("not", col1Exp.getColDataTypeList().get(0).getColumnSpecs().get(0)); assertEquals("null", col1Exp.getColDataTypeList().get(0).getColumnSpecs().get(1)); - assertEquals(col1Exp.hasColumn(), true); + assertTrue(col1Exp.hasColumn()); } @Test @@ -319,7 +321,7 @@ public void testAlterTableModifyColumn2() throws JSQLParserException { assertEquals(AlterOperation.MODIFY, alterExpression.getOperation()); - assertEquals(alterExpression.hasColumn(), false); + assertFalse(alterExpression.hasColumn()); } @Test @@ -333,7 +335,7 @@ public void testAlterTableAlterColumn() throws JSQLParserException { assertEquals(AlterOperation.ALTER, alterExpression.getOperation()); - assertEquals(alterExpression.hasColumn(), true); + assertTrue(alterExpression.hasColumn()); } @Test @@ -425,7 +427,7 @@ public void testAddConstraintKeyIssue320() throws JSQLParserException { } @Test - public void testIssue633() throws JSQLParserException, JSQLParserException, JSQLParserException { + public void testIssue633() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE team_phases ADD CONSTRAINT team_phases_id_key UNIQUE (id)"); } @@ -469,8 +471,9 @@ public void testAlterOnlyIssue928() throws JSQLParserException { Statement parsed = assertSqlCanBeParsedAndDeparsed(statement); Alter created = new Alter().withUseOnly(true).withTable(new Table("categories")).addAlterExpressions( new AlterExpression().withOperation(AlterOperation.ADD).withIndex(new NamedConstraint() - .withName(Arrays.asList("pk_categories")).withType("PRIMARY KEY") - .addColumns(new ColumnParams("category_id")))); + .withName(Collections.singletonList( + "pk_categories")).withType("PRIMARY KEY") + .addColumns(new ColumnParams("category_id")))); assertDeparse(created, statement); assertEqualsObjectTree(parsed, created); } @@ -480,6 +483,7 @@ public void testAlterConstraintWithoutFKSourceColumnsIssue929() throws JSQLParse assertSqlCanBeParsedAndDeparsed("ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (customer_id) REFERENCES customers"); } + @Test public void testAlterTableAlterColumnDropNotNullIssue918() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE \"user_table_t\" ALTER COLUMN name DROP NOT NULL"); } @@ -818,4 +822,52 @@ public void testAlterTableDropMultipleColumnsIfExists() throws JSQLParserExcepti public void testAlterTableDropMultipleColumnsIfExistsWithParams() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE test DROP COLUMN IF EXISTS name CASCADE, DROP COLUMN IF EXISTS surname CASCADE"); } + + @Test + public void testAlterTableAddColumnSpanner7() throws JSQLParserException { + final String sql = "ALTER TABLE ORDER_PATIENT ADD COLUMN FIRST_NAME_UPPERCASE STRING(MAX)" + + " AS (UPPER(FIRST_NAME)) STORED"; + Statement stmt = CCJSqlParserUtil.parse(sql); + assertStatementCanBeDeparsedAs(stmt, sql, true); + Alter alter = (Alter) stmt; + List alterExps = alter.getAlterExpressions(); + AlterExpression col1Exp = alterExps.get(0); + assertTrue(col1Exp.getColDataTypeList().get(0).toString().endsWith(" STORED")); + assertTrue(col1Exp.hasColumn()); + } + + @Test + public void testAlterTableAddColumnSpanner8() throws JSQLParserException { + final String sql = "ALTER TABLE ORDER_PATIENT ADD COLUMN NAMES ARRAY"; + Statement stmt = CCJSqlParserUtil.parse(sql); + assertStatementCanBeDeparsedAs(stmt, sql, true); + Alter alter = (Alter) stmt; + List alterExps = alter.getAlterExpressions(); + AlterExpression col1Exp = alterExps.get(0); + assertTrue(col1Exp.hasColumn()); + assertNotNull(col1Exp.getColDataTypeList()); + assertEquals(1, col1Exp.getColDataTypeList().size()); + ColumnDataType type = col1Exp.getColDataTypeList().get(0); + assertEquals("NAMES", type.getColumnName()); + assertEquals("ARRAY", type.getColDataType().toString()); + } + + @Test + public void testAlterColumnSetCommitTimestamp1() throws JSQLParserException { + // @todo: properly implement SET OPTIONS, the current hack is terrible + // final String sql = "ALTER TABLE FOCUS_PATIENT ALTER COLUMN UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=null)"; + + final String sql = "ALTER TABLE FOCUS_PATIENT ALTER COLUMN UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=true)"; + Statement stmt = CCJSqlParserUtil.parse(sql); + assertStatementCanBeDeparsedAs(stmt, sql); + Alter alter = (Alter) stmt; + List alterExps = alter.getAlterExpressions(); + AlterExpression col1Exp = alterExps.get(0); + assertTrue(col1Exp.hasColumn()); + assertNotNull(col1Exp.getColDataTypeList()); + assertEquals(1, col1Exp.getColDataTypeList().size()); + ColumnDataType type = col1Exp.getColDataTypeList().get(0); + assertEquals("UPDATE_DATE_TIME_GMT", type.getColumnName()); + assertEquals("UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=true)", type.toString()); + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java index 6522f40d3..5ee5f9dbe 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java @@ -64,7 +64,7 @@ public void testCreateTable3() throws JSQLParserException { @Test public void testCreateTableAsSelect() - throws JSQLParserException, JSQLParserException, JSQLParserException, JSQLParserException { + throws JSQLParserException { String statement = "CREATE TABLE a AS SELECT col1, col2 FROM b"; assertSqlCanBeParsedAndDeparsed(statement); } @@ -905,4 +905,38 @@ public void testCreateTableBinaryIssue1596() throws JSQLParserException { + "INDEX name (name) USING BTREE" + ") ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_bin"); } + + @Test + public void testCreateTableSpanner() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed( + "CREATE TABLE COMMAND (\n" + + " DATASET_ID INT64 NOT NULL,\n" + + " COMMAND_ID STRING(MAX) NOT NULL,\n" + + " VAL_BOOL BOOL,\n" + + " VAL_BYTES BYTES(1024),\n" + + " VAL_DATE DATE,\n" + + " VAL_TIMESTAMP TIMESTAMP,\n" + + " VAL_COMMIT_TIMESTAMP TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp = true),\n" + + " VAL_FLOAT64 FLOAT64,\n" + + " VAL_JSON JSON(2048),\n" + + " VAL_NUMERIC NUMERIC,\n" + + " VAL_STRING STRING(MAX),\n" + + " VAL_TIMESTAMP TIMESTAMP,\n" + + " ARR_BOOL ARRAY,\n" + + " ARR_BYTES ARRAY,\n" + + " ARR_DATE ARRAY,\n" + + " ARR_TIMESTAMP ARRAY,\n" + + " ARR_FLOAT64 ARRAY,\n" + + " ARR_JSON ARRAY,\n" + + " ARR_NUMERIC ARRAY,\n" + + " ARR_STRING ARRAY,\n" + + " ARR_TIMESTAMP ARRAY,\n" + + " PAYLOAD STRING(MAX),\n" + + " AUTHOR STRING(MAX) NOT NULL,\n" + + " SEARCH STRING(MAX) AS (UPPER(AUTHOR)) STORED\n" + + " ) PRIMARY KEY ( DATASET_ID, COMMAND_ID )\n" + + ", INTERLEAVE IN PARENT DATASET ON DELETE CASCADE", true); + } + + } From 1ce014ac5069af3ec2dfe6cdc62fe130de9c5e1c Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 16 Dec 2022 20:09:26 +0700 Subject: [PATCH 8/9] fix: fix JSonExpression, accept Expressions Make JSonExpression accept Expressions Add Testcase Expose Idents() and Operators() Fixes #1696 --- .../jsqlparser/expression/JsonExpression.java | 24 +++++-------- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 34 ++++++++++++++++--- .../statement/select/PostgresTest.java | 12 +++++++ 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java index fb5a16931..5d6aa794c 100644 --- a/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java @@ -15,7 +15,6 @@ import net.sf.jsqlparser.parser.ASTNodeAccessImpl; public class JsonExpression extends ASTNodeAccessImpl implements Expression { - private Expression expr; private List idents = new ArrayList(); @@ -34,26 +33,19 @@ public void setExpression(Expression expr) { this.expr = expr; } -// public List getIdents() { -// return idents; -// } -// -// public void setIdents(List idents) { -// this.idents = idents; -// operators = new ArrayList(); -// for (String ident : idents) { -// operators.add("->"); -// } -// } -// -// public void addIdent(String ident) { -// addIdent(ident, "->"); -// } public void addIdent(String ident, String operator) { idents.add(ident); operators.add(operator); } + public List getIdents() { + return idents; + } + + public List getOperators() { + return operators; + } + @Override public String toString() { StringBuilder b = new StringBuilder(); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 181e4db2e..567bcd9ed 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -3951,11 +3951,11 @@ Expression PrimaryExpression() #PrimaryExpression: | LOOKAHEAD(3, {!interrupted}) retval=CaseWhenExpression() - | retval = SimpleJdbcParameter() + | LOOKAHEAD(3) retval = SimpleJdbcParameter() - | LOOKAHEAD(2, {!interrupted}) retval=JdbcNamedParameter() + | LOOKAHEAD(2) retval=JdbcNamedParameter() - | retval=UserVariable() + | LOOKAHEAD(3) retval=UserVariable() | LOOKAHEAD(2, {!interrupted}) retval=NumericBind() @@ -4178,7 +4178,33 @@ JsonExpression JsonExpression() : { CastExpression castExpr = null; } { - expr=Column() + ( + LOOKAHEAD(3, {!interrupted}) expr=CaseWhenExpression() + | + expr = SimpleJdbcParameter() + | + LOOKAHEAD(2, {!interrupted}) expr=JdbcNamedParameter() + | + expr=UserVariable() + | + LOOKAHEAD(JsonFunction(), {!interrupted}) expr = JsonFunction() + | + LOOKAHEAD(JsonAggregateFunction(), {!interrupted}) expr = JsonAggregateFunction() + | + LOOKAHEAD(FullTextSearch(), {!interrupted}) expr = FullTextSearch() + /* Do not parse Functions as this will result in a major performance loss + The Performance related tests will fail. + | + LOOKAHEAD(Function(), {!interrupted}) expr=Function() + */ + | + LOOKAHEAD(2, {!interrupted}) expr=Column() + | + token= { expr = new StringValue(token.image); } + | + LOOKAHEAD("(" expr=SubSelect() ")", {!interrupted} ) "(" expr=SubSelect() ")" + ) + ( "::" type=ColDataType() { castExpr = new CastExpression(); castExpr.setUseCastKeyword(false); diff --git a/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java b/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java index 7eccca477..0751a5369 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java @@ -10,7 +10,9 @@ package net.sf.jsqlparser.statement.select; import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.JsonExpression; import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class PostgresTest { @@ -41,4 +43,14 @@ public void testExtractFunctionIssue1582() throws JSQLParserException { " as snijtijd_interval"; TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + + @Test + public void testJSonExpressionIssue1696() throws JSQLParserException { + String sqlStr="SELECT '{\"key\": \"value\"}'::json -> 'key' AS X"; + Select select = (Select) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + SelectExpressionItem selectExpressionItem = (SelectExpressionItem) plainSelect.getSelectItems().get(0); + Assertions.assertEquals("'key'", selectExpressionItem.getExpression(JsonExpression.class).getIdents().get(0)); + } } From 19572da17e04fddbde571ea42b5ef2fa555253e9 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 16 Dec 2022 21:35:45 +0700 Subject: [PATCH 9/9] test: add Test for Issue #1237 --- .../java/net/sf/jsqlparser/statement/SetStatementTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java b/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java index d845cdf53..b838f2224 100644 --- a/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/SetStatementTest.java @@ -75,5 +75,9 @@ public void testObject() { public void testSettingUserVariable() throws JSQLParserException { String sqlStr="set @Flag = 1"; assertSqlCanBeParsedAndDeparsed(sqlStr, true); + + // issue #1237 + sqlStr="SET @@global.time_zone = '01:00'"; + assertSqlCanBeParsedAndDeparsed(sqlStr, true); } }