From 7dfad9112db7393a436eea4fdf596afee122793f Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 24 May 2021 08:26:13 +0700 Subject: [PATCH 1/3] Fix Nested CASE WHEN performance, fixes issue #1162 --- .../jsqlparser/expression/CaseExpression.java | 27 +++++++- .../util/deparser/ExpressionDeParser.java | 4 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 17 +++-- .../select/NestedBracketsPerformanceTest.java | 68 ++++++++++++++++++- .../statement/select/SelectTest.java | 5 ++ 5 files changed, 111 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/CaseExpression.java b/src/main/java/net/sf/jsqlparser/expression/CaseExpression.java index 9544615bd..371c40bf0 100644 --- a/src/main/java/net/sf/jsqlparser/expression/CaseExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/CaseExpression.java @@ -43,6 +43,7 @@ */ public class CaseExpression extends ASTNodeAccessImpl implements Expression { + private boolean usingBrackets = false; private Expression switchExpression; private List whenClauses; private Expression elseExpression; @@ -90,9 +91,9 @@ public void setWhenClauses(List whenClauses) { @Override public String toString() { - return "CASE " + ((switchExpression != null) ? switchExpression + " " : "") + return (usingBrackets ? "(" : "") + "CASE " + ((switchExpression != null) ? switchExpression + " " : "") + PlainSelect.getStringList(whenClauses, false, false) + " " - + ((elseExpression != null) ? "ELSE " + elseExpression + " " : "") + "END"; + + ((elseExpression != null) ? "ELSE " + elseExpression + " " : "") + "END" + (usingBrackets ? ")" : ""); } public CaseExpression withSwitchExpression(Expression switchExpression) { @@ -129,4 +130,26 @@ public E getSwitchExpression(Class type) { public E getElseExpression(Class type) { return type.cast(getElseExpression()); } + + /** + * @return the usingBrackets + */ + public boolean isUsingBrackets() { + return usingBrackets; + } + + /** + * @param usingBrackets the usingBrackets to set + */ + public void setUsingBrackets(boolean usingBrackets) { + this.usingBrackets = usingBrackets; + } + + /** + * @param usingBrackets the usingBrackets to set + */ + public CaseExpression withUsingBrackets(boolean usingBrackets) { + this.usingBrackets=usingBrackets; + return this; + } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java index fc4ea0d36..db810b29e 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -604,7 +604,7 @@ public void visit(TimeValue timeValue) { @Override public void visit(CaseExpression caseExpression) { - buffer.append("CASE "); + buffer.append(caseExpression.isUsingBrackets() ? "(" : "").append("CASE "); Expression switchExp = caseExpression.getSwitchExpression(); if (switchExp != null) { switchExp.accept(this); @@ -622,7 +622,7 @@ public void visit(CaseExpression caseExpression) { buffer.append(" "); } - buffer.append("END"); + buffer.append("END").append(caseExpression.isUsingBrackets() ? ")" : ""); } @Override diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index ab31845fc..95b5ad739 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -67,6 +67,8 @@ import java.util.*; * The parser generated by JavaCC */ public class CCJSqlParser extends AbstractJSqlParser { + public int bracketsCounter = 0; + public int caseCounter = 0; public CCJSqlParser withConfiguration(FeatureConfiguration configuration) { token_source.configuration = configuration; @@ -3697,11 +3699,14 @@ Expression CaseWhenExpression() #CaseWhenExpression: Expression elseExp = null; } { - + { caseCounter++; } [ switchExp=Condition() ] ( clause=WhenThenSearchCondition() { whenClauses.add(clause); } )+ - [ elseExp=Condition()] - + [ (LOOKAHEAD( ["("] CaseWhenExpression() [")"] ) ["("] elseExp=CaseWhenExpression() [")" { ((CaseExpression) elseExp).setUsingBrackets(true); } ] + | elseExp=Condition() + ) + ] + { caseCounter--; } { caseExp.setSwitchExpression(switchExp); caseExp.setWhenClauses(whenClauses); @@ -3717,8 +3722,10 @@ WhenClause WhenThenSearchCondition(): Expression thenExp = null; } { - (LOOKAHEAD(Expression()) whenExp=Expression() | whenExp=SimpleExpression()) - thenExp=Expression() + whenExp=Expression() + (LOOKAHEAD( ["("] CaseWhenExpression() [")"] ) ["("] thenExp=CaseWhenExpression() [")" { ((CaseExpression) thenExp).setUsingBrackets(true); }] + | thenExp=Expression() + ) { whenThen.setWhenExpression(whenExp); whenThen.setThenExpression(thenExp); diff --git a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java index 396197777..0ff88eaae 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java @@ -38,7 +38,73 @@ public void testIssue235() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT CASE WHEN ( CASE WHEN ( CASE WHEN ( CASE WHEN ( 1 ) THEN 0 END ) THEN 0 END ) THEN 0 END ) THEN 0 END FROM a", true); } - @Test(timeout = 100000) + @Test + public void testNestedCaseWhen_Issue1162_small() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("CREATE VIEW VIEW_NAME1 AS\n" + + "SELECT CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE CASE WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT' ELSE '0' END END END END END END END END END END END END END END COLUMNALIAS\n" + + "FROM TABLE1", true); + } + + @Test + public void testNestedCaseWhen_Issue1162() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("CREATE VIEW VIEW_NAME1 AS\n" + + "SELECT CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE\n" + + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + + "ELSE (CASE WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT' ELSE '0' END) END) END) END) END) END) END) END) END) END) END) END) END) END COLUMNALIAS\n" + + "FROM TABLE1", true); + } + + @Test(timeout = 200000) @Ignore public void testIssue496() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("select isNull(charLen(TEST_ID,0)+ isNull(charLen(TEST_DVC,0)+ isNull(charLen(TEST_NO,0)+ isNull(charLen(ATEST_ID,0)+ isNull(charLen(TESTNO,0)+ isNull(charLen(TEST_CTNT,0)+ isNull(charLen(TEST_MESG_CTNT,0)+ isNull(charLen(TEST_DTM,0)+ isNull(charLen(TEST_DTT,0)+ isNull(charLen(TEST_ADTT,0)+ isNull(charLen(TEST_TCD,0)+ isNull(charLen(TEST_PD,0)+ isNull(charLen(TEST_VAL,0)+ isNull(charLen(TEST_YN,0)+ isNull(charLen(TEST_DTACM,0)+ isNull(charLen(TEST_MST,0) from test_info_m"); 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 105182e9a..daaa77562 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -1358,7 +1358,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 From 7aa073f979932b902e706188067d48aede08196d Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 24 May 2021 09:05:48 +0700 Subject: [PATCH 2/3] Apease Codazy --- pmd-rules.xml | 11 ++++++++++- .../select/NestedBracketsPerformanceTest.java | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pmd-rules.xml b/pmd-rules.xml index 08a020da1..bb1d6344f 100644 --- a/pmd-rules.xml +++ b/pmd-rules.xml @@ -55,7 +55,16 @@ under the License. - + + + + + + + + + diff --git a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java index 0ff88eaae..1e09a9483 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java @@ -39,7 +39,7 @@ public void testIssue235() throws JSQLParserException { } @Test - public void testNestedCaseWhen_Issue1162_small() throws JSQLParserException { + public void testNestedCaseWhenWithoutBracketsIssue1162_small() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("CREATE VIEW VIEW_NAME1 AS\n" + "SELECT CASE\n" + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + @@ -72,7 +72,7 @@ public void testNestedCaseWhen_Issue1162_small() throws JSQLParserException { } @Test - public void testNestedCaseWhen_Issue1162() throws JSQLParserException { + public void testNestedCaseWhenWithBracketsIssue1162() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("CREATE VIEW VIEW_NAME1 AS\n" + "SELECT CASE\n" + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" + From 456d95da067949d9e89574b79daea2e43f6e8465 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 24 May 2021 09:26:59 +0700 Subject: [PATCH 3/3] Apease Codazy --- pmd-rules.xml | 20 +++++++++---------- .../select/NestedBracketsPerformanceTest.java | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pmd-rules.xml b/pmd-rules.xml index bb1d6344f..28f4a94d5 100644 --- a/pmd-rules.xml +++ b/pmd-rules.xml @@ -54,16 +54,16 @@ under the License. - - - - - - - - - + + + + + + + + + + diff --git a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java index 1e09a9483..d7fd43c67 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java @@ -39,7 +39,7 @@ public void testIssue235() throws JSQLParserException { } @Test - public void testNestedCaseWhenWithoutBracketsIssue1162_small() throws JSQLParserException { + public void testNestedCaseWhenWithoutBracketsIssue1162() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("CREATE VIEW VIEW_NAME1 AS\n" + "SELECT CASE\n" + "WHEN WDGFLD.PORTTYPE = 1 THEN 'INPUT PORT'\n" +