Skip to content

Commit 6ef5e0b

Browse files
feat: chaining JSON Expressions
- supports chains like '{"obj":{"field": "value"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT - fixes #1792
1 parent a7dfb94 commit 6ef5e0b

File tree

5 files changed

+177
-121
lines changed

5 files changed

+177
-121
lines changed

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4057,7 +4057,6 @@ JsonExpression JsonExpression() : {
40574057
CastExpression castExpr = null;
40584058
}
40594059
{
4060-
// LOOKAHEAD(3, {getAsBoolean(Feature.allowComplexParsing) && !interrupted})
40614060
(
40624061
LOOKAHEAD(3, {!interrupted}) expr=CaseWhenExpression()
40634062
|
@@ -4082,19 +4081,65 @@ JsonExpression JsonExpression() : {
40824081
LOOKAHEAD( {!interrupted} ) "(" expr=ParenthesedSelect() ")"
40834082
)
40844083

4085-
( "::" type=ColDataType() {
4084+
(
4085+
"::" type=ColDataType()
4086+
{
40864087
castExpr = new CastExpression();
40874088
castExpr.setUseCastKeyword(false);
40884089
castExpr.setLeftExpression(expr);
40894090
castExpr.setColDataType(type);
40904091
expr=castExpr;
4091-
} )*
4092+
}
4093+
)*
4094+
{
4095+
result.setExpression(expr);
4096+
}
4097+
40924098
(
4093-
"->" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->");} |
4094-
"->>" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->>");} |
4095-
"#>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>");} |
4096-
"#>>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>>");}
4099+
LOOKAHEAD(2) (
4100+
"->" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->");}
4101+
|
4102+
"->>" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->>");}
4103+
|
4104+
"#>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>");}
4105+
|
4106+
"#>>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>>");}
4107+
)
40974108
)+
4109+
4110+
// chaining JSON Expressions, e.g.
4111+
// '{"obj":{"field": "value"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT
4112+
(
4113+
LOOKAHEAD(2, {!interrupted} ) (
4114+
LOOKAHEAD(2) (
4115+
"::" type=ColDataType()
4116+
{
4117+
castExpr = new CastExpression();
4118+
castExpr.setUseCastKeyword(false);
4119+
castExpr.setLeftExpression(result);
4120+
castExpr.setColDataType(type);
4121+
expr=castExpr;
4122+
}
4123+
)
4124+
)+
4125+
{
4126+
result = new JsonExpression();
4127+
result.setExpression(expr);
4128+
}
4129+
4130+
(
4131+
LOOKAHEAD(2) (
4132+
"->" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->");}
4133+
|
4134+
"->>" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->>");}
4135+
|
4136+
"#>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>");}
4137+
|
4138+
"#>>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>>");}
4139+
)
4140+
)*
4141+
)*
4142+
40984143
{
40994144
result.setExpression(expr);
41004145
return result;
@@ -5254,7 +5299,7 @@ ColDataType ColDataType():
52545299

52555300
[LOOKAHEAD(2) "(" {tk2 =null;} ( ( ( tk=<S_LONG> [ LOOKAHEAD(2) (tk2=<K_BYTE> | tk2=<K_CHAR>) ] ) | tk=<S_CHAR_LITERAL> | tk=<S_IDENTIFIER> | tk=<K_CHAR> )
52565301
{ argumentsStringList.add(tk.image + (tk2!=null?" " + tk2.image:"")); } ["," {/*argumentsStringList.add(",");*/}] )* ")"]
5257-
[( "[" {tk=null;} [ tk=<S_LONG> ] { array.add(tk!=null?Integer.valueOf(tk.image):null); } "]" )+ { colDataType.setArrayData(array); } ]
5302+
[ LOOKAHEAD(2) ( LOOKAHEAD(2) "[" {tk=null;} [ tk=<S_LONG> ] { array.add(tk!=null?Integer.valueOf(tk.image):null); } "]" )+ { colDataType.setArrayData(array); } ]
52585303
[LOOKAHEAD(2) <K_CHARACTER> <K_SET> (tk=<S_IDENTIFIER> | tk=<K_BINARY>) { colDataType.setCharacterSet(tk.image); } ]
52595304

52605305
{
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package net.sf.jsqlparser.expression;
2+
3+
import net.sf.jsqlparser.JSQLParserException;
4+
import net.sf.jsqlparser.test.TestUtils;
5+
import org.junit.jupiter.api.Test;
6+
7+
class JsonExpressionTest {
8+
9+
@Test
10+
void testIssue1792() throws JSQLParserException, InterruptedException {
11+
String sqlStr =
12+
"SELECT ''::JSON -> 'obj'::TEXT";
13+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
14+
15+
sqlStr =
16+
"SELECT ('{\"obj\":{\"field\": \"value\"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT)";
17+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
18+
19+
sqlStr =
20+
"SELECT\n"
21+
+ " CASE\n"
22+
+ " WHEN true\n"
23+
+ " THEN (SELECT ((('{\"obj\":{\"field\": \"value\"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT))))\n"
24+
+ " END";
25+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
26+
}
27+
}

0 commit comments

Comments
 (0)