diff --git a/README.md b/README.md index 8c260f6a6..3eb909c14 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,11 @@ Also I would like to know about needed examples or documentation stuff. ## Extensions in the latest SNAPSHOT version 4.1 -* support for **with** (ctl) for **delete**, **update** and **merge** +* support for nested `WITH` CTEs +* support for **with** (cte) for **delete**, **update** and **merge** * introduce a max depth to allow parsing complex expression lists without performance loss (thx to @manticore-projects) * allow all functions to have complex expressions as parameters (thx to @manticore-projects) -** API change FunctionWithCondParams production removed +* API change FunctionWithCondParams production removed * API change in ValuesStatement: the expression list is now hold as a ItemList and not as a List * support for parser modification within **parseExpression** and **parseCondExpression** * support for table schema for foreign keys diff --git a/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java b/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java index d1ac98385..e3cb63494 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java @@ -14,14 +14,64 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ItemsList; public class WithItem implements SelectBody { private String name; private List withItemList; - private SelectBody selectBody; + private ItemsList itemsList; + private boolean useValues = true; + private boolean useBracketsForValues = false; + + private SubSelect subSelect; private boolean recursive; + /** + * Get the values (as VALUES (...) or SELECT) + * + * @return the values of the insert + */ + public ItemsList getItemsList() { + return itemsList; + } + + public void setItemsList(ItemsList list) { + itemsList = list; + } + + public boolean isUseValues() { + return useValues; + } + + public void setUseValues(boolean useValues) { + this.useValues = useValues; + } + + public WithItem withItemsList(ItemsList itemsList) { + this.setItemsList(itemsList); + return this; + } + + public WithItem withUseValues(boolean useValues) { + this.setUseValues(useValues); + return this; + } + + public boolean isUsingBracketsForValues() { + return useBracketsForValues; + } + + public void setUseBracketsForValues(boolean useBracketsForValues) { + this.useBracketsForValues = useBracketsForValues; + } + + public WithItem withUseBracketsForValues(boolean useBracketsForValues) { + this.setUseBracketsForValues(useBracketsForValues); + return this; + } + public String getName() { return name; } @@ -38,12 +88,12 @@ public void setRecursive(boolean recursive) { this.recursive = recursive; } - public SelectBody getSelectBody() { - return selectBody; + public SubSelect getSubSelect() { + return subSelect.withUseBrackets(false); } - public void setSelectBody(SelectBody selectBody) { - this.selectBody = selectBody; + public void setSubSelect(SubSelect subSelect) { + this.subSelect = subSelect.withUseBrackets(false); } /** @@ -62,9 +112,26 @@ public void setWithItemList(List withItemList) { @Override @SuppressWarnings({"PMD.CyclomaticComplexity"}) public String toString() { - return (recursive ? "RECURSIVE " : "") + name + ((withItemList != null) ? " " + PlainSelect. - getStringList(withItemList, true, true) : "") - + " AS (" + selectBody + ")"; + StringBuilder builder = new StringBuilder(); + builder.append(recursive ? "RECURSIVE " : ""); + builder.append(name); + builder.append( + (withItemList != null) ? " " + PlainSelect.getStringList(withItemList, true, true) : ""); + builder.append(" AS "); + + if (useValues) { + builder.append("(VALUES "); + ExpressionList expressionList = (ExpressionList) itemsList; + builder.append( + PlainSelect.getStringList(expressionList.getExpressions(), true, useBracketsForValues)); + builder.append(")"); + } else { + builder.append(subSelect.isUseBrackets() ? "" : "("); + builder.append(subSelect); + + builder.append(subSelect.isUseBrackets() ? "" : ")"); + } + return builder.toString(); } @Override @@ -82,8 +149,8 @@ public WithItem withWithItemList(List withItemList) { return this; } - public WithItem withSelectBody(SelectBody selectBody) { - this.setSelectBody(selectBody); + public WithItem withSubSelect(SubSelect subSelect) { + this.setSubSelect(subSelect); return this; } @@ -103,8 +170,4 @@ public WithItem addWithItemList(Collection withItemList) { collection.addAll(withItemList); return this.withWithItemList(collection); } - - public E getSelectBody(Class type) { - return type.cast(getSelectBody()); - } } diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index eee11272e..fd4f704d0 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -147,7 +147,7 @@ public List getTableList(Expression expr) { @Override public void visit(WithItem withItem) { otherItemNames.add(withItem.getName().toLowerCase()); - withItem.getSelectBody().accept(this); + withItem.getSubSelect().accept((ItemsListVisitor) this); } @Override 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 aa12b3aa5..22afaac67 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -19,6 +19,7 @@ import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.expression.SQLServerHints; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ItemsList; import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; @@ -240,7 +241,7 @@ public void visit(SelectExpressionItem selectExpressionItem) { @Override public void visit(SubSelect subSelect) { - buffer.append("("); + buffer.append(subSelect.isUseBrackets() ? "(" : ""); if (subSelect.getWithItemsList() != null && !subSelect.getWithItemsList().isEmpty()) { buffer.append("WITH "); for (Iterator iter = subSelect.getWithItemsList().iterator(); iter.hasNext();) { @@ -253,7 +254,7 @@ public void visit(SubSelect subSelect) { } } subSelect.getSelectBody().accept(this); - buffer.append(")"); + buffer.append(subSelect.isUseBrackets() ? ")" : ""); Alias alias = subSelect.getAlias(); if (alias != null) { buffer.append(alias.toString()); @@ -484,9 +485,27 @@ public void visit(WithItem withItem) { if (withItem.getWithItemList() != null) { buffer.append(" ").append(PlainSelect.getStringList(withItem.getWithItemList(), true, true)); } - buffer.append(" AS ("); - withItem.getSelectBody().accept(this); - buffer.append(")"); + buffer.append(" AS "); + + if (withItem.isUseValues()) { + ItemsList itemsList = withItem.getItemsList(); + boolean useBracketsForValues = withItem.isUsingBracketsForValues(); + buffer.append("(VALUES "); + + ExpressionList expressionList = (ExpressionList) itemsList; + buffer.append( + PlainSelect.getStringList(expressionList.getExpressions(), true, useBracketsForValues)); + buffer.append(")"); + } else { + SubSelect subSelect = withItem.getSubSelect(); + if (!subSelect.isUseBrackets()) { + buffer.append("("); + } + subSelect.accept((FromItemVisitor) this); + if (!subSelect.isUseBrackets()) { + buffer.append(")"); + } + } } @Override diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java index 7d45ce5a2..807b9df08 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java @@ -287,7 +287,7 @@ public void visit(WithItem withItem) { if (isNotEmpty(withItem.getWithItemList())) { withItem.getWithItemList().forEach(wi -> wi.accept(this)); } - withItem.getSelectBody().accept(this); + withItem.getSubSelect().accept(this); } @Override diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index f0953db01..0fa05248a 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1691,13 +1691,24 @@ WithItem WithItem() #WithItem: WithItem with = new WithItem(); String name = null; List selectItems = null; - SelectBody selectBody = null; + SubSelect select = null; + + ExpressionList simpleExpressionList; } { [ { with.setRecursive(true); } ] name=RelObjectName() { with.setName(name); } [ "(" selectItems=SelectItemsList() ")" { with.setWithItemList(selectItems); } ] - "(" selectBody = SelectBody() { with.setSelectBody(selectBody); } ")" + // if the next block looks alike an ExpressionList without Brackets, then parse as List + ( LOOKAHEAD( "(" SimpleExpressionList(true) ")" ) + "(" + simpleExpressionList = SimpleExpressionList(true) { with.withUseBracketsForValues(false).setItemsList(simpleExpressionList); } + ")" + + // Otherwise parse it as a SubSelect + | "(" select = SubSelect() { with.setSubSelect(select.withUseBrackets(false)); with.setUseValues(false); } ")" + + ) { return with; } } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java index 04ca3b36d..5e8cff630 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -960,7 +960,7 @@ public void testDistinct() throws JSQLParserException { assertEquals("myid", ((Column) ((SelectExpressionItem) plainSelect.getDistinct().getOnSelectItems(). get(0)).getExpression()) - .getColumnName()); + .getColumnName()); assertEquals("mycol", ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)). getExpression()).getColumnName()); @@ -975,7 +975,7 @@ public void testDistinctTop() throws JSQLParserException { assertEquals("myid", ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)). getExpression()) - .getColumnName()); + .getColumnName()); assertEquals("mycol", ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)). getExpression()).getColumnName()); @@ -1020,7 +1020,7 @@ public void testJoin() throws JSQLParserException { assertEquals("tab1.id", ((Column) ((EqualsTo) plainSelect.getJoins().get(0).getOnExpression()). getLeftExpression()) - .getFullyQualifiedName()); + .getFullyQualifiedName()); assertTrue(plainSelect.getJoins().get(0).isOuter()); assertStatementCanBeDeparsedAs(select, statement); @@ -1278,7 +1278,7 @@ public void testOrderBy() throws JSQLParserException { assertEquals(2, plainSelect.getOrderByElements().size()); assertEquals("tab1.a", ((Column) plainSelect.getOrderByElements().get(0).getExpression()) - .getFullyQualifiedName()); + .getFullyQualifiedName()); assertEquals("b", ((Column) plainSelect.getOrderByElements().get(1).getExpression()).getColumnName()); assertTrue(plainSelect.getOrderByElements().get(1).isAsc()); @@ -1316,7 +1316,7 @@ public void testTimestamp() throws JSQLParserException { PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); assertEquals("2004-04-30 04:05:34.56", ((TimestampValue) ((GreaterThan) plainSelect.getWhere()).getRightExpression()). - getValue().toString()); + getValue().toString()); assertStatementCanBeDeparsedAs(select, statement); } @@ -1370,12 +1370,12 @@ public void testCase() throws JSQLParserException { + // "WHEN (SELECT c FROM tab2 WHERE d = 2) = 3 THEN 'AAA' " + "END) FROM tab1"; assertSqlCanBeParsedAndDeparsed(statement); - } + } - @Test - public void testNestedCaseCondition() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT CASE WHEN CASE WHEN 1 THEN 10 ELSE 20 END > 15 THEN 'BBB' END FROM tab1"); - assertSqlCanBeParsedAndDeparsed("SELECT (CASE WHEN (CASE a WHEN 1 THEN 10 ELSE 20 END) > 15 THEN 'BBB' END) FROM tab1"); + @Test + public void testNestedCaseCondition() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT CASE WHEN CASE WHEN 1 THEN 10 ELSE 20 END > 15 THEN 'BBB' END FROM tab1"); + assertSqlCanBeParsedAndDeparsed("SELECT (CASE WHEN (CASE a WHEN 1 THEN 10 ELSE 20 END) > 15 THEN 'BBB' END) FROM tab1"); } @Test @@ -2549,12 +2549,12 @@ public void testRegexpMySQL() throws JSQLParserException { String stmt = "SELECT * FROM mytable WHERE first_name REGEXP '^Ste(v|ph)en$'"; assertSqlCanBeParsedAndDeparsed(stmt); } - + @Test public void testNotRegexpMySQLIssue887() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE first_name NOT REGEXP '^Ste(v|ph)en$'"); } - + @Test public void testNotRegexpMySQLIssue887_2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE NOT first_name REGEXP '^Ste(v|ph)en$'"); @@ -2571,7 +2571,6 @@ public void testXorCondition() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE field = value XOR other_value"); } - @Test public void testRlike() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE first_name RLIKE '^Ste(v|ph)en$'"); @@ -2736,10 +2735,10 @@ public void testJsonExpression() throws JSQLParserException { //The following staments can be parsed but not deparsed for (String statement : new String[]{ - "SELECT doc->'site_name' FROM websites WHERE doc @> '{\"tags\":[{\"term\":\"paris\"}, {\"term\":\"food\"}]}'", - "SELECT * FROM sales where sale ->'items' @> '[{\"count\":0}]'", - "SELECT * FROM sales where sale ->'items' ? 'name'", - "SELECT * FROM sales where sale ->'items' -# 'name'" + "SELECT doc->'site_name' FROM websites WHERE doc @> '{\"tags\":[{\"term\":\"paris\"}, {\"term\":\"food\"}]}'", + "SELECT * FROM sales where sale ->'items' @> '[{\"count\":0}]'", + "SELECT * FROM sales where sale ->'items' ? 'name'", + "SELECT * FROM sales where sale ->'items' -# 'name'" }) { Select select = (Select) parserManager.parse(new StringReader(statement)); assertStatementCanBeDeparsedAs(select, statement, true); @@ -2809,12 +2808,11 @@ public void testSelectOracleColl() throws JSQLParserException { public void testSelectInnerWith() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM (WITH actor AS (SELECT 'a' aid FROM DUAL) SELECT aid FROM actor)"); } - + // @Test // public void testSelectInnerWithAndUnionIssue1084() throws JSQLParserException { // assertSqlCanBeParsedAndDeparsed("WITH actor AS (SELECT 'b' aid FROM DUAL) SELECT aid FROM actor UNION WITH actor2 AS (SELECT 'a' aid FROM DUAL) SELECT aid FROM actor2"); // } - @Test public void testSelectInnerWithAndUnionIssue1084_2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("WITH actor AS (SELECT 'b' aid FROM DUAL) SELECT aid FROM actor UNION SELECT aid FROM actor2"); @@ -2971,7 +2969,7 @@ public void testOracleHint() throws JSQLParserException { + " b.application_id\n" + "FROM jl_br_journals j,\n" + " po_vendors p", true, "ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b) \n" - + " USE_NL (glcc glf) USE_MERGE (gp gsb)"); + + " USE_NL (glcc glf) USE_MERGE (gp gsb)"); assertOracleHintExists("SELECT /*+ROWID(emp)*/ /*+ THIS IS NOT HINT! ***/ * \n" + "FROM emp \n" + "WHERE rowid > 'AAAAtkAABAAAFNTAAA' AND empno = 155", false, "ROWID(emp)"); @@ -3751,7 +3749,7 @@ public void testFuncConditionParameter2() throws JSQLParserException { public void testFuncConditionParameter3() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT CAST((MAX(CAST(IIF(isnumeric(license_no) = 1, license_no, 0) AS INT)) + 2) AS varchar) FROM lcps.t_license WHERE profession_id = 60 and license_type = 100 and YEAR(issue_date) % 2 = case when YEAR(issue_date) % 2 = 0 then 0 else 1 end and ISNUMERIC(license_no) = 1", true); } - + @Test public void testFuncConditionParameter4() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT IIF(isnumeric(license_no) = 1, license_no, 0) FROM mytable", true); @@ -3945,7 +3943,7 @@ public void visit(Select select) { @Override public void visit(PlainSelect plainSelect) { SelectExpressionItem typedExpression - = (SelectExpressionItem) plainSelect.getSelectItems().get(0); + = (SelectExpressionItem) plainSelect.getSelectItems().get(0); assertNotNull(typedExpression); assertNull(typedExpression.getAlias()); StringValue value = (StringValue) typedExpression.getExpression(); @@ -4203,13 +4201,13 @@ public void testWrongParseTreeIssue89() throws JSQLParserException { SetOperationList unionQueries = (SetOperationList) unionQuery.getSelectBody(); assertThat(unionQueries.getSelects()) - .extracting(select -> (PlainSelect) select).allSatisfy(ps -> assertNull(ps.getOrderByElements())); + .extracting(select -> (PlainSelect) select).allSatisfy(ps -> assertNull(ps.getOrderByElements())); assertThat(unionQueries.getOrderByElements()) - .isNotNull() - .hasSize(1) - .extracting(item -> item.toString()) - .contains("col"); + .isNotNull() + .hasSize(1) + .extracting(item -> item.toString()) + .contains("col"); } @Test @@ -4250,7 +4248,6 @@ public void testTableFunctionInExprIssue923() throws JSQLParserException { // .replace("@Prompt", "MyFunc"); // assertSqlCanBeParsedAndDeparsed(stmt, true); // } - @Test public void testTableFunctionInExprIssue923_3() throws JSQLParserException, IOException { String stmt = IOUtils.toString( @@ -4301,7 +4298,7 @@ public void testPreserveAndOperator() throws JSQLParserException { new Select().withSelectBody(new PlainSelect() .addSelectItems(Collections.singleton(new AllColumns())) .withFromItem(new Table("mytable")).withWhere( - new AndExpression().withUseOperator(true) + new AndExpression().withUseOperator(true) .withLeftExpression(new EqualsTo(new LongValue(1), new LongValue(2))) .withRightExpression(new EqualsTo(new LongValue(2), new LongValue(3))))), statement); @@ -4351,7 +4348,7 @@ public void testVariableAssignment3() throws JSQLParserException { public void testKeyWordOfIssue1029() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT of.Full_Name_c AS FullName FROM comdb.Offer_c AS of"); } - + @Test public void testKeyWordExceptIssue1026() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM xxx WHERE exclude = 1"); @@ -4368,32 +4365,32 @@ public void testSelectConditionsIssue720And991() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT 1 < 2 AS a, 0 IS NULL AS b"); // assertSqlCanBeParsedAndDeparsed("SELECT 1 < 2 AS a, (0 IS NULL) AS b"); } - + @Test public void testKeyWordExceptIssue1040() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT FORMAT(100000, 2)"); } - + @Test public void testKeyWordExceptIssue1044() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT SP_ID FROM ST_PR WHERE INSTR(',' || SP_OFF || ',', ',' || ? || ',') > 0"); } - + @Test public void testKeyWordExceptIssue1055() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT INTERVAL ? DAY"); } - + @Test public void testKeyWordExceptIssue1055_2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE A.end_time > now() AND A.end_time <= date_add(now(), INTERVAL ? DAY)"); } - + @Test public void testIssue1062() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE temperature.timestamp <= @to AND temperature.timestamp >= @from"); } - + @Test public void testIssue1062_2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE temperature.timestamp <= @until AND temperature.timestamp >= @from"); @@ -4403,101 +4400,101 @@ public void testIssue1062_2() throws JSQLParserException { public void testIssue1068() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT t2.c AS div"); } - + @Test public void selectWithSingleIn() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT 1 FROM dual WHERE a IN 1"); } - + @Test public void testKeywordSequenceIssue1075() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT a.sequence FROM all_procedures a"); } - + @Test public void testKeywordSequenceIssue1074() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM t_user WITH (NOLOCK)"); } - + @Test public void testContionItemsSelectedIssue1077() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT 1 > 0"); } - + @Test public void testExistsKeywordIssue1076() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT EXISTS (4)"); } - + @Test public void testExistsKeywordIssue1076_1() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT mycol, EXISTS (SELECT mycol FROM mytable) mycol2 FROM mytable"); } - + @Test public void testFormatKeywordIssue1078() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT FORMAT(date, 'yyyy-MM') AS year_month FROM mine_table"); } - + @Test public void testConditionalParametersForFunctions() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT myFunc(SELECT mycol FROM mytable)"); } - + @Test public void testCreateTableWithParameterDefaultFalseIssue1088() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT p.*, rhp.house_id FROM rel_house_person rhp INNER JOIN person p ON rhp.person_id = p.if WHERE rhp.house_id IN (SELECT house_id FROM rel_house_person WHERE person_id = :personId AND current_occupant = :current) AND rhp.current_occupant = :currentOccupant"); } - + @Test public void testMissingLimitKeywordIssue1006() throws JSQLParserException { Statement stmt = CCJSqlParserUtil.parse("SELECT id, name FROM test OFFSET 20 LIMIT 10"); assertEquals("SELECT id, name FROM test LIMIT 10 OFFSET 20", stmt.toString()); } - + @Test public void testKeywordUnsignedIssue961() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT COLUMN1, COLUMN2, CASE WHEN COLUMN1.DATA NOT IN ('1', '3') THEN CASE WHEN CAST(COLUMN2 AS UNSIGNED) IN ('1', '2', '3') THEN 'Q1' ELSE 'Q2' END END AS YEAR FROM TESTTABLE"); } - + @Test public void testH2CaseWhenFunctionIssue1091() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT CASEWHEN(ID = 1, 'A', 'B') FROM mytable"); } - + @Test public void testMultiPartTypesIssue992() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT CAST('*' AS pg_catalog.text)"); } - + @Test public void testSetOperationWithParenthesisIssue1094() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM ((SELECT A FROM tbl) UNION DISTINCT (SELECT B FROM tbl2)) AS union1"); } - + @Test public void testSetOperationWithParenthesisIssue1094_2() throws JSQLParserException { Statement stmt = CCJSqlParserUtil.parse("SELECT * FROM (((SELECT A FROM tbl)) UNION DISTINCT (SELECT B FROM tbl2)) AS union1"); assertEquals("SELECT * FROM ((SELECT A FROM tbl) UNION DISTINCT (SELECT B FROM tbl2)) AS union1", stmt.toString()); } - + @Test public void testSetOperationWithParenthesisIssue1094_3() throws JSQLParserException { Statement stmt = CCJSqlParserUtil.parse("SELECT * FROM (((SELECT A FROM tbl)) UNION DISTINCT ((SELECT B FROM tbl2))) AS union1"); assertEquals("SELECT * FROM ((SELECT A FROM tbl) UNION DISTINCT ((SELECT B FROM tbl2))) AS union1", stmt.toString()); } - + @Test public void testSetOperationWithParenthesisIssue1094_4() throws JSQLParserException { Statement stmt = CCJSqlParserUtil.parse("SELECT * FROM (((((SELECT A FROM tbl)))) UNION DISTINCT (((((((SELECT B FROM tbl2)))))))) AS union1"); assertEquals("SELECT * FROM ((SELECT A FROM tbl) UNION DISTINCT ((SELECT B FROM tbl2))) AS union1", stmt.toString()); } - + @Test public void testSignedKeywordIssue1100() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT signed, unsigned FROM mytable"); } - + @Test public void testSignedKeywordIssue995() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT leading FROM prd_reprint"); @@ -4518,17 +4515,17 @@ public void testColonDelimiterIssue1134() throws JSQLParserException { Statement stmt = CCJSqlParserUtil.parse("SELECT * FROM stores_demo:informix.accounts"); assertEquals("SELECT * FROM stores_demo.informix.accounts", stmt.toString()); } - + @Test public void testKeywordSkipIssue1136() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT skip"); } - + @Test public void testKeywordAlgorithmIssue1137() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT algorithm FROM tablename"); } - + @Test public void testKeywordAlgorithmIssue1138() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM in.tablename"); @@ -4538,22 +4535,22 @@ public void testKeywordAlgorithmIssue1138() throws JSQLParserException { public void testFunctionOrderBy() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT array_agg(DISTINCT s ORDER BY b)[1] FROM t"); } - + @Test public void testProblematicDeparsingIssue1183() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT ARRAY_AGG(NAME ORDER BY ID) FILTER (WHERE NAME IS NOT NULL)"); } - + @Test public void testProblematicDeparsingIssue1183_2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT ARRAY_AGG(ID ORDER BY ID) OVER (ORDER BY ID)"); } - + @Test public void testKeywordCostsIssue1185() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("WITH costs AS (SELECT * FROM MY_TABLE1 AS ALIAS_TABLE1) SELECT * FROM TESTSTMT"); } - + @Test public void testFunctionWithComplexParameters_Issue1190() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT to_char(a = '3') FROM dual", true); @@ -4563,16 +4560,25 @@ public void testFunctionWithComplexParameters_Issue1190() throws JSQLParserExcep public void testConditionsWithExtraBrackets_Issue1194() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT (col IS NULL) FROM tbl", true); } - + public void testWithValueListWithExtraBrackets1135() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))) select day, value from sample_data", true); } - + @Test public void testWithValueListWithOutExtraBrackets1135() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("with sample_data(\"DAY\") as (values 0, 1, 2)\n" + + " select \"DAY\" from sample_data", true); assertSqlCanBeParsedAndDeparsed("with sample_data(day, value) as (values (0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16)) select day, value from sample_data", true); } - + + @Test + public void testWithInsideWithIssue1186() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed( + "WITH TESTSTMT1 AS ( WITH TESTSTMT2 AS (SELECT * FROM MY_TABLE2) SELECT col1, col2 FROM TESTSTMT2) SELECT * FROM TESTSTMT", + true); + } + @Test public void testKeywordSynonymIssue1211() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("select businessDate as \"bd\", synonym as \"synonym\" from sc.tab", true); diff --git a/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java b/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java index 724e1f893..6273b6cc4 100644 --- a/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java +++ b/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java @@ -35,6 +35,7 @@ import net.sf.jsqlparser.statement.select.OrderByElement; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; +import net.sf.jsqlparser.statement.select.SubSelect; import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.upsert.Upsert; @@ -96,8 +97,8 @@ public void shouldUseProvidedDeparsersWhenDeParsingInsert() throws JSQLParserExc List withItemsList = new ArrayList(); WithItem withItem1 = spy(new WithItem()); WithItem withItem2 = spy(new WithItem()); - SelectBody withItem1SelectBody = mock(SelectBody.class); - SelectBody withItem2SelectBody = mock(SelectBody.class); + SubSelect withItem1SubSelect = mock(SubSelect.class); + SubSelect withItem2SubSelect = mock(SubSelect.class); SelectBody selectBody = mock(SelectBody.class); insert.setSelect(select); @@ -114,8 +115,8 @@ public void shouldUseProvidedDeparsersWhenDeParsingInsert() throws JSQLParserExc select.setSelectBody(selectBody); withItemsList.add(withItem1); withItemsList.add(withItem2); - withItem1.setSelectBody(withItem1SelectBody); - withItem2.setSelectBody(withItem2SelectBody); + withItem1.setSubSelect(withItem1SubSelect); + withItem2.setSubSelect(withItem2SubSelect); statementDeParser.visit(insert); @@ -329,8 +330,8 @@ public void shouldUseProvidedDeparsersWhenDeParsingUpsertWithExpressionList() th List withItemsList = new ArrayList(); WithItem withItem1 = spy(new WithItem()); WithItem withItem2 = spy(new WithItem()); - SelectBody withItem1SelectBody = mock(SelectBody.class); - SelectBody withItem2SelectBody = mock(SelectBody.class); + SubSelect withItem1SubSelect = mock(SubSelect.class); + SubSelect withItem2SubSelect = mock(SubSelect.class); SelectBody selectBody = mock(SelectBody.class); upsert.setSelect(select); @@ -347,8 +348,8 @@ public void shouldUseProvidedDeparsersWhenDeParsingUpsertWithExpressionList() th select.setSelectBody(selectBody); withItemsList.add(withItem1); withItemsList.add(withItem2); - withItem1.setSelectBody(withItem1SelectBody); - withItem2.setSelectBody(withItem2SelectBody); + withItem1.setSubSelect(withItem1SubSelect); + withItem2.setSubSelect(withItem2SubSelect); statementDeParser.visit(upsert);