Skip to content

Commit

Permalink
feat: JsonExpression supports Expressions rather than Char/Long only
Browse files Browse the repository at this point in the history
- fixes #2054

Signed-off-by: Andreas Reichel <[email protected]>
  • Loading branch information
manticore-projects committed Sep 19, 2024
1 parent 90adf82 commit 3d48835
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 27 deletions.
22 changes: 11 additions & 11 deletions src/main/java/net/sf/jsqlparser/expression/JsonExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import java.util.Map;

public class JsonExpression extends ASTNodeAccessImpl implements Expression {
private final List<Map.Entry<String, String>> idents = new ArrayList<>();
private final List<Map.Entry<Expression, String>> idents = new ArrayList<>();
private Expression expr;

public JsonExpression() {
Expand All @@ -29,7 +29,7 @@ public JsonExpression(Expression expr) {
this.expr = expr;
}

public JsonExpression(Expression expr, List<Map.Entry<String, String>> idents) {
public JsonExpression(Expression expr, List<Map.Entry<Expression, String>> idents) {
this.expr = expr;
this.idents.addAll(idents);
}
Expand All @@ -47,26 +47,26 @@ public void setExpression(Expression expr) {
this.expr = expr;
}

public void addIdent(String ident, String operator) {
public void addIdent(Expression ident, String operator) {
idents.add(new AbstractMap.SimpleEntry<>(ident, operator));
}

public void addAllIdents(Collection<Map.Entry<String, String>> idents) {
public void addAllIdents(Collection<Map.Entry<Expression, String>> idents) {
this.idents.addAll(idents);
}

public List<Map.Entry<String, String>> getIdentList() {
public List<Map.Entry<Expression, String>> getIdentList() {
return idents;
}

public Map.Entry<String, String> getIdent(int index) {
public Map.Entry<Expression, String> getIdent(int index) {
return idents.get(index);
}

@Deprecated
public List<String> getIdents() {
ArrayList<String> l = new ArrayList<>();
for (Map.Entry<String, String> ident : idents) {
public List<Expression> getIdents() {
ArrayList<Expression> l = new ArrayList<>();
for (Map.Entry<Expression, String> ident : idents) {
l.add(ident.getKey());
}

Expand All @@ -76,7 +76,7 @@ public List<String> getIdents() {
@Deprecated
public List<String> getOperators() {
ArrayList<String> l = new ArrayList<>();
for (Map.Entry<String, String> ident : idents) {
for (Map.Entry<Expression, String> ident : idents) {
l.add(ident.getValue());
}
return l;
Expand All @@ -86,7 +86,7 @@ public List<String> getOperators() {
public String toString() {
StringBuilder b = new StringBuilder();
b.append(expr.toString());
for (Map.Entry<String, String> ident : idents) {
for (Map.Entry<Expression, String> ident : idents) {
b.append(ident.getValue()).append(ident.getKey());
}
return b.toString();
Expand Down
38 changes: 23 additions & 15 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -4706,6 +4706,7 @@ Expression ArrayExpression(Expression obj): {
Expression PrimaryExpression() #PrimaryExpression:
{
Expression retval = null;
Expression expression = null;
CastExpression castExpr = null;
TimezoneExpression timezoneExpr = null;
Expression timezoneRightExpr = null;
Expand All @@ -4718,7 +4719,7 @@ Expression PrimaryExpression() #PrimaryExpression:
boolean dateExpressionAllowed = true;
ExpressionList list;

final List<Map.Entry<String, String>> jsonIdents = new ArrayList<Map.Entry<String, String>>();
final List<Map.Entry<Expression, String>> jsonIdents = new ArrayList<Map.Entry<Expression, String>>();
}
{
[ <K_NOT> { not=true; } | "!" { not=true; exclamationMarkNot=true; } ]
Expand Down Expand Up @@ -4846,14 +4847,17 @@ Expression PrimaryExpression() #PrimaryExpression:
[
LOOKAHEAD(2) (
LOOKAHEAD(2) (
"->" ( token=<S_CHAR_LITERAL> | token=<S_LONG>) { jsonIdents.add(new AbstractMap.SimpleEntry<String, String>(token.image,"->")); }
token="->" expression=Expression()
|
"->>" (token=<S_CHAR_LITERAL> | token=<S_LONG>) { jsonIdents.add(new AbstractMap.SimpleEntry<String, String>(token.image,"->>")); }
token="->>" expression=Expression()
|
"#>" token=<S_CHAR_LITERAL> { jsonIdents.add(new AbstractMap.SimpleEntry<String, String>(token.image,"#>")); }
token="#>" expression=Expression()
|
"#>>" token=<S_CHAR_LITERAL> { jsonIdents.add(new AbstractMap.SimpleEntry<String, String>(token.image,"#>>")); }
token="#>>" expression=Expression()
)
{
jsonIdents.add(new AbstractMap.SimpleEntry<Expression, String>(expression, token.image ));
}
)+
retval = JsonExpression(retval, jsonIdents)
]
Expand Down Expand Up @@ -5083,8 +5087,9 @@ StructType StructType() #StruckType:
}
}

JsonExpression JsonExpression(Expression expr, List<Map.Entry<String, String>> idents) : {
JsonExpression JsonExpression(Expression expr, List<Map.Entry<Expression, String>> idents) : {
JsonExpression result = new JsonExpression(expr, idents);
Expression expression;
Token token;
ColDataType type = null;
CastExpression castExpr = null;
Expand All @@ -5111,15 +5116,18 @@ JsonExpression JsonExpression(Expression expr, List<Map.Entry<String, String>> i
}

(
LOOKAHEAD(2) (
"->" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->");}
|
"->>" (token=<S_CHAR_LITERAL> | token=<S_LONG>) {result.addIdent(token.image,"->>");}
|
"#>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>");}
|
"#>>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>>");}
)
LOOKAHEAD(2) (
token="->" expression=Expression()
|
token="->>" expression=Expression()
|
token="#>" expression=Expression()
|
token="#>>" expression=Expression()
)
{
result.addIdent( expression, token.image );
}
)*
)*

Expand Down
25 changes: 25 additions & 0 deletions src/test/java/net/sf/jsqlparser/expression/JsonExpressionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import net.sf.jsqlparser.JSQLParserException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;

Expand Down Expand Up @@ -108,4 +110,27 @@ void testParenthesedJsonExpressionsIssue1792() throws JSQLParserException {

assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}

@ParameterizedTest
@ValueSource(strings = {
"SELECT ( JSONB_AGG(variables) " +
" FILTER (WHERE variables IS NOT NULL) " +
" OVER (PARTITION BY deviceid ORDER BY time)->>-1 )::JSONB AS variables\n" +
"FROM devices\n" +
";",
"SELECT ( JSONB_AGG(variables) " +
" FILTER (WHERE variables IS NOT NULL) " +
" OVER (PARTITION BY deviceid ORDER BY time)->>(0-1) )::JSONB AS variables\n"
+
"FROM devices\n" +
";",
"SELECT ( JSONB_AGG(variables) " +
" FILTER (WHERE variables IS NOT NULL) " +
" OVER (PARTITION BY deviceid ORDER BY time)->>(jsonb_array_length(JSONB_AGG(variables) FILTER (WHERE variables IS NOT NULL) OVER (PARTITION BY deviceid ORDER BY time))-1) )::JSONB AS variables\n"
+
"FROM devices\n" +
";"})
void testIssue2054(String sqlStr) throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.JsonExpression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statements;
Expand Down Expand Up @@ -58,7 +59,7 @@ public void testJSonExpressionIssue1696() throws JSQLParserException {
PlainSelect plainSelect = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sqlStr, true);
SelectItem<?> selectExpressionItem =
plainSelect.getSelectItems().get(0);
Assertions.assertEquals("'key'",
Assertions.assertEquals(new StringValue("key"),
selectExpressionItem.getExpression(JsonExpression.class).getIdent(0).getKey());
}

Expand Down

0 comments on commit 3d48835

Please sign in to comment.