Skip to content

Add support for expressions in case#938

Closed
tomershay wants to merge 2 commits intoJSQLParser:masterfrom
tomershay:add_support_for_expressions_in_case
Closed

Add support for expressions in case#938
tomershay wants to merge 2 commits intoJSQLParser:masterfrom
tomershay:add_support_for_expressions_in_case

Conversation

@tomershay
Copy link
Contributor

MySQL supports expressions in CASE statements before the WHEN clause.
Example: CASE <expr> WHEN ....

The expression after the CASE keyword is optional.
This PR adds support for such expressions, in case they exist in the query.

Relevant docs for both options: https://dev.mysql.com/doc/refman/5.7/en/case.html

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.02%) to 83.438% when pulling 2e3d2b1 on tomershay:add_support_for_expressions_in_case into 430b3ee on JSQLParser:master.

@wumpz wumpz self-assigned this Jan 28, 2020
@wumpz
Copy link
Member

wumpz commented Jan 28, 2020

This expression right after case is supported a while. Your test sql is parsed using an unmodified JSqlParser V3.2-SNAPSHOT:

String sqls = "SELECT a FROM tbl1 LEFT JOIN tbl2 ON CASE tbl1.col1 WHEN tbl1.col1 = 1 THEN tbl1.col2 = tbl2.col2 ELSE tbl1.col3 = tbl2.col3 END";

So maybe you had another problem?

Here are the issues, that solved and extended your kind of syntax:

#371, #862

at least the older one is from dec 2016.

@tomershay
Copy link
Contributor Author

Maybe I'm missing something, but I just built the latest master from source, and the following test fails:

public void testExpressionsInCaseBeforeWhen() throws JSQLParserException {
    assertSqlCanBeParsedAndDeparsed("SELECT a FROM tbl1 LEFT JOIN tbl2 ON CASE tbl1.col1 WHEN tbl1.col1 = 1 THEN tbl1.col2 = tbl2.col2 ELSE tbl1.col3 = tbl2.col3 END");
}

The error:

net.sf.jsqlparser.JSQLParserException
	at net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(CCJSqlParserUtil.java:65)
	at net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed(TestUtils.java:57)
	at net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed(TestUtils.java:53)
	at net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed(TestUtils.java:41)
	at net.sf.jsqlparser.statement.select.SelectTest.testExpressionsInCaseBeforeWhen(SelectTest.java:1307)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: "CASE" "CASE"
    at line 1, column 38.

Was expecting one of:

    "!"
    "("
    "NOT"

	at net.sf.jsqlparser.parser.CCJSqlParser.generateParseException(CCJSqlParser.java:22874)
	at net.sf.jsqlparser.parser.CCJSqlParser.jj_consume_token(CCJSqlParser.java:22721)
	at net.sf.jsqlparser.parser.CCJSqlParser.AndExpression(CCJSqlParser.java:7552)
	at net.sf.jsqlparser.parser.CCJSqlParser.OrExpression(CCJSqlParser.java:7492)
	at net.sf.jsqlparser.parser.CCJSqlParser.Expression(CCJSqlParser.java:7463)
	at net.sf.jsqlparser.parser.CCJSqlParser.JoinerExpression(CCJSqlParser.java:6274)
	at net.sf.jsqlparser.parser.CCJSqlParser.JoinsList(CCJSqlParser.java:6081)
	at net.sf.jsqlparser.parser.CCJSqlParser.PlainSelect(CCJSqlParser.java:3746)
	at net.sf.jsqlparser.parser.CCJSqlParser.SetOperationList(CCJSqlParser.java:3954)
	at net.sf.jsqlparser.parser.CCJSqlParser.SelectBody(CCJSqlParser.java:3613)
	at net.sf.jsqlparser.parser.CCJSqlParser.Select(CCJSqlParser.java:3606)
	at net.sf.jsqlparser.parser.CCJSqlParser.SingleStatement(CCJSqlParser.java:124)
	at net.sf.jsqlparser.parser.CCJSqlParser.Statement(CCJSqlParser.java:75)
	at net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(CCJSqlParserUtil.java:63)
	... 29 more
Caused by:
net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: "CASE" "CASE"
    at line 1, column 38.

@tomershay
Copy link
Contributor Author

It seems that the source code might indicate the problem:

<K_CASE>
    (
            ( clause=WhenThenSearchCondition() { whenClauses.add(clause); } )+
        |
            switchExp=Condition()
             ( clause=WhenThenValue() { whenClauses.add(clause); } )+
    )

From what I understand, the expression-after-case is currently supported when it has a simple expression following it (handled by WhenThenValue).
I believe that the issue I ran into with the query sample I mentioned, is that the WHEN/THEN includes a more complex expression (i.e., equality condition), which should be handled by WhenThenSearchCondition, but in that case, the expression-after-case isn't allowed by the existing template.
So is switchExp actually there to represent the expression after the CASE keyword?
If that's the case, I can try to fix the pull request and fix the code accordingly to support the use case I'm describing.
Please share your thoughts.

@wumpz
Copy link
Member

wumpz commented Jan 29, 2020

That is right. switchExp represents exactly the expression after CASE. Strangely enough, here I am not anymore able to parse your test statement, but I have no actual sources at hand. Maybe some of my last not yet pushed changes did have this effect. I have to look into this.

@tomershay
Copy link
Contributor Author

Thanks for the update. Please let me know if a fix for this is coming up in your internal changes. If not, let me know what you think we should change in this PR to make it valuable and approvable.

@wumpz
Copy link
Member

wumpz commented Jan 29, 2020

I don't know what I saw :(. Now it fails here as well. However, I changed it to accept your case as well. Indeed the whole case parsing was simplified because there is no more difference between both cases.

Just wait a bit. I have another issue to close. JSqlParser here would fail some tests and I do not yet want to push this version.

@wumpz wumpz closed this in 39e920d Feb 1, 2020
@wumpz
Copy link
Member

wumpz commented Feb 1, 2020

I just deployed a new version. Could you check it?

@tomershay
Copy link
Contributor Author

@wumpz, just tested it and it works great!
Thanks!

@tomershay tomershay deleted the add_support_for_expressions_in_case branch February 18, 2020 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants