Skip to content

Commit 8225178

Browse files
fix: JSon functions with Column parameters
- fixes #1753 Signed-off-by: Andreas Reichel <[email protected]>
1 parent 7ed3ce9 commit 8225178

File tree

5 files changed

+138
-91
lines changed

5 files changed

+138
-91
lines changed

build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ dependencies {
9595
testImplementation 'com.h2database:h2:+'
9696

9797
// for JaCoCo Reports
98-
testImplementation 'org.junit.jupiter:junit-jupiter-api:+'
99-
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:+'
100-
testImplementation 'org.junit.jupiter:junit-jupiter-params:+'
98+
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.3'
99+
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.3'
100+
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.3'
101101

102102
// https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter
103103
testImplementation 'org.mockito:mockito-junit-jupiter:+'

src/main/java/net/sf/jsqlparser/expression/JsonAggregateFunction.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class JsonAggregateFunction extends FilterOverImpl implements Expression
2323
private JsonFunctionType functionType;
2424
private Expression expression = null;
2525
private boolean usingKeyKeyword = false;
26-
private String key;
26+
private Object key;
2727
private boolean usingValueKeyword = false;
2828
private Object value;
2929

@@ -112,15 +112,15 @@ public JsonAggregateFunction withUsingKeyKeyword(boolean usingKeyKeyword) {
112112
return this;
113113
}
114114

115-
public String getKey() {
115+
public Object getKey() {
116116
return key;
117117
}
118118

119-
public void setKey(String key) {
119+
public void setKey(Object key) {
120120
this.key = key;
121121
}
122122

123-
public JsonAggregateFunction withKey(String key) {
123+
public JsonAggregateFunction withKey(Object key) {
124124
this.setKey(key);
125125
return this;
126126
}
@@ -188,6 +188,7 @@ public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
188188
public StringBuilder append(StringBuilder builder) {
189189
switch (functionType) {
190190
case OBJECT:
191+
case MYSQL_OBJECT:
191192
appendObject(builder);
192193
break;
193194
case ARRAY:
@@ -209,6 +210,8 @@ public StringBuilder appendObject(StringBuilder builder) {
209210
builder.append("KEY ");
210211
}
211212
builder.append(key).append(" VALUE ").append(value);
213+
} else if (functionType == JsonFunctionType.MYSQL_OBJECT) {
214+
builder.append(key).append(", ").append(value);
212215
} else {
213216
builder.append(key).append(":").append(value);
214217
}

src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePair.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
*/
1919

2020
public class JsonKeyValuePair implements Serializable {
21-
private final String key;
21+
private final Object key;
2222
private final Object value;
2323
private boolean usingKeyKeyword = false;
2424
private boolean usingValueKeyword = false;
2525
private boolean usingFormatJson = false;
2626

27-
public JsonKeyValuePair(String key, Object value, boolean usingKeyKeyword,
27+
public JsonKeyValuePair(Object key, Object value, boolean usingKeyKeyword,
2828
boolean usingValueKeyword) {
2929
this.key = Objects.requireNonNull(key, "The KEY of the Pair must not be null");
3030
this.value = value;
@@ -93,7 +93,7 @@ public boolean equals(Object obj) {
9393
return Objects.equals(this.key, other.key);
9494
}
9595

96-
public String getKey() {
96+
public Object getKey() {
9797
return key;
9898
}
9999

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

Lines changed: 112 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -5193,101 +5193,109 @@ JsonFunction JsonFunction() : {
51935193
Column column = null;
51945194
JsonKeyValuePair keyValuePair;
51955195

5196+
Object key = null;
51965197
Expression expression = null;
51975198
JsonFunctionExpression functionExpression;
51985199

51995200
}
52005201
{
52015202
(
5202-
(
5203-
( <K_JSON_OBJECT>
5204-
"(" { result.setType( JsonFunctionType.OBJECT ); }
5205-
(
5203+
( <K_JSON_OBJECT>
5204+
"(" { result.setType( JsonFunctionType.OBJECT ); }
5205+
(
5206+
// SQL2016 compliant Syntax
5207+
LOOKAHEAD(2) (
5208+
LOOKAHEAD(2) (
5209+
"KEY" { usingKeyKeyword = true; } ( keyToken = <S_CHAR_LITERAL> { key = keyToken.image; } | key = Column() )
5210+
)
5211+
|
5212+
keyToken = <S_CHAR_LITERAL> { key = keyToken.image; }
5213+
|
5214+
key = Column()
5215+
)
5216+
5217+
( LOOKAHEAD(2)
5218+
( ":" | "," { result.setType( JsonFunctionType.POSTGRES_OBJECT ); } | "VALUE" { usingValueKeyword = true; } )
52065219
(
5207-
// SQL2016 compliant Syntax
5208-
(
5209-
[ "KEY" { usingKeyKeyword = true; } ]
5210-
keyToken = <S_CHAR_LITERAL>
5220+
expression = Expression()
5221+
)
5222+
[ <K_FORMAT> <K_JSON> { usingFormatJason = true; } ]
5223+
)?
5224+
{
5225+
if (expression !=null) {
5226+
keyValuePair = new JsonKeyValuePair( key, expression, usingKeyKeyword, usingValueKeyword );
5227+
keyValuePair.setUsingFormatJson( usingFormatJason );
5228+
result.add(keyValuePair);
5229+
} else {
5230+
result.setType( JsonFunctionType.POSTGRES_OBJECT );
5231+
keyValuePair = new JsonKeyValuePair( key, null, false, false );
5232+
result.add(keyValuePair);
5233+
}
5234+
}
52115235

5212-
( LOOKAHEAD(2)
5213-
( ":" | "," { result.setType( JsonFunctionType.POSTGRES_OBJECT ); } | "VALUE" { usingValueKeyword = true; } )
5214-
(
5215-
expression = Expression()
5216-
)
5217-
[ <K_FORMAT> <K_JSON> { usingFormatJason = true; } ]
5218-
)? {
5219-
if (expression !=null) {
5220-
keyValuePair = new JsonKeyValuePair( keyToken.image, expression, usingKeyKeyword, usingValueKeyword );
5221-
keyValuePair.setUsingFormatJson( usingFormatJason );
5222-
result.add(keyValuePair);
5223-
} else {
5224-
result.setType( JsonFunctionType.POSTGRES_OBJECT );
5225-
keyValuePair = new JsonKeyValuePair( keyToken.image, null, false, false );
5226-
result.add(keyValuePair);
5227-
}
5228-
}
5229-
5230-
// --- Next Elements
5231-
( "," { usingKeyKeyword = false; usingValueKeyword = false; }
5232-
[ "KEY" { usingKeyKeyword = true; } ]
5233-
keyToken = <S_CHAR_LITERAL>
5234-
( ":" | "," { result.setType( JsonFunctionType.MYSQL_OBJECT ); } | "VALUE" { usingValueKeyword = true; } )
5235-
(
5236-
expression = Expression() { keyValuePair = new JsonKeyValuePair( keyToken.image, expression, usingKeyKeyword, usingValueKeyword ); result.add(keyValuePair); }
5237-
)
5238-
[ <K_FORMAT> <K_JSON> { keyValuePair.setUsingFormatJson( true ); } ]
5239-
)*
5236+
// --- Next Elements
5237+
( "," { usingKeyKeyword = false; usingValueKeyword = false; }
5238+
(
5239+
LOOKAHEAD(2) (
5240+
"KEY" { usingKeyKeyword = true; } ( keyToken = <S_CHAR_LITERAL> { key = keyToken.image; } | key = Column() )
52405241
)
5241-
)?
5242+
|
5243+
keyToken = <S_CHAR_LITERAL> { key = keyToken.image; }
5244+
|
5245+
key = Column()
5246+
)
5247+
( ":" | "," { result.setType( JsonFunctionType.MYSQL_OBJECT ); } | "VALUE" { usingValueKeyword = true; } )
5248+
expression = Expression() { keyValuePair = new JsonKeyValuePair( key, expression, usingKeyKeyword, usingValueKeyword ); result.add(keyValuePair); }
5249+
[ <K_FORMAT> <K_JSON> { keyValuePair.setUsingFormatJson( true ); } ]
5250+
)*
5251+
)?
52425252

5243-
[
5244-
(
5245-
<K_NULL> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.NULL ); }
5246-
)
5247-
|
5248-
(
5249-
<K_ABSENT> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); }
5250-
)
5251-
]
5253+
[
5254+
(
5255+
<K_NULL> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.NULL ); }
5256+
)
5257+
|
5258+
(
5259+
<K_ABSENT> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); }
5260+
)
5261+
]
52525262

5253-
[
5254-
(
5255-
<K_WITH> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITH ); }
5256-
)
5257-
|
5258-
(
5259-
<K_WITHOUT> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITHOUT ); }
5260-
)
5261-
]
5263+
[
5264+
(
5265+
<K_WITH> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITH ); }
5266+
)
5267+
|
5268+
(
5269+
<K_WITHOUT> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITHOUT ); }
5270+
)
5271+
]
5272+
")"
5273+
)
5274+
|
5275+
(
5276+
<K_JSON_ARRAY> { result.setType( JsonFunctionType.ARRAY ); }
5277+
"("
5278+
(
5279+
LOOKAHEAD(2) (
5280+
<K_NULL> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.NULL ); }
52625281
)
5263-
")"
5264-
)
5265-
|
5266-
(
5267-
<K_JSON_ARRAY> { result.setType( JsonFunctionType.ARRAY ); }
5268-
"("
5282+
|
5283+
expression=Expression() { functionExpression = new JsonFunctionExpression( expression ); result.add( functionExpression ); }
5284+
5285+
[ LOOKAHEAD(2) <K_FORMAT> <K_JSON> { functionExpression.setUsingFormatJson( true ); } ]
52695286
(
5270-
LOOKAHEAD(2) (
5271-
<K_NULL> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.NULL ); }
5272-
)
5273-
|
5287+
","
52745288
expression=Expression() { functionExpression = new JsonFunctionExpression( expression ); result.add( functionExpression ); }
5275-
52765289
[ LOOKAHEAD(2) <K_FORMAT> <K_JSON> { functionExpression.setUsingFormatJson( true ); } ]
5277-
(
5278-
","
5279-
expression=Expression() { functionExpression = new JsonFunctionExpression( expression ); result.add( functionExpression ); }
5280-
[ LOOKAHEAD(2) <K_FORMAT> <K_JSON> { functionExpression.setUsingFormatJson( true ); } ]
5281-
)*
52825290
)*
5291+
)*
52835292

5284-
[
5285-
<K_ABSENT> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); }
5286-
]
5293+
[
5294+
<K_ABSENT> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); }
5295+
]
52875296

5288-
")"
5289-
)
5290-
)
5297+
")"
5298+
)
52915299
)
52925300

52935301
{
@@ -5298,6 +5306,7 @@ JsonFunction JsonFunction() : {
52985306
JsonAggregateFunction JsonAggregateFunction() : {
52995307
JsonAggregateFunction result = new JsonAggregateFunction();
53005308
Token token;
5309+
Object key;
53015310
Expression expression;
53025311
List<OrderByElement> expressionOrderByList = null;
53035312

@@ -5312,10 +5321,32 @@ JsonAggregateFunction JsonAggregateFunction() : {
53125321
(
53135322
( <K_JSON_OBJECTAGG>
53145323
"(" { result.setType( JsonFunctionType.OBJECT ); }
5315-
[ "KEY" { result.setUsingKeyKeyword( true ); } ]
5316-
( token = <DT_ZONE> | token = <S_DOUBLE> | token = <S_LONG> | token = <S_HEX> | token = <S_CHAR_LITERAL> | token = <S_IDENTIFIER> | token = <S_QUOTED_IDENTIFIER> ) { result.setKey( token.image ); }
5317-
( ":" | "VALUE" {result.setUsingValueKeyword( true ); } )
5318-
( token = <S_IDENTIFIER> | token = <S_QUOTED_IDENTIFIER> ) { result.setValue( token.image ); }
5324+
(
5325+
LOOKAHEAD(2) (
5326+
"KEY" { result.setUsingKeyKeyword( true ); }
5327+
(
5328+
( token = <DT_ZONE> | token = <S_DOUBLE> | token = <S_LONG> | token = <S_HEX> | token = <S_CHAR_LITERAL> )
5329+
{
5330+
key = token.image;
5331+
}
5332+
|
5333+
key = Column()
5334+
)
5335+
)
5336+
|
5337+
( token = <DT_ZONE> | token = <S_DOUBLE> | token = <S_LONG> | token = <S_HEX> | token = <S_CHAR_LITERAL> )
5338+
{
5339+
key = token.image;
5340+
}
5341+
|
5342+
key = Column()
5343+
)
5344+
{
5345+
result.setKey( key );
5346+
}
5347+
5348+
( ":" | "," { result.setType( JsonFunctionType.MYSQL_OBJECT ); } | "VALUE" {result.setUsingValueKeyword( true ); } )
5349+
expression = Expression() { result.setValue( expression ); }
53195350

53205351
[ <K_FORMAT> <K_JSON> { result.setUsingFormatJson( true ); } ]
53215352

src/test/java/net/sf/jsqlparser/expression/JsonFunctionTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ public void testArrayAgg() throws JSQLParserException {
131131

132132
@Test
133133
public void testObject() throws JSQLParserException {
134+
TestUtils.assertSqlCanBeParsedAndDeparsed(
135+
"WITH Items AS (SELECT 'hello' AS key, 'world' AS value)\n" +
136+
"SELECT JSON_OBJECT(key, value) AS json_data FROM Items",
137+
true);
134138
TestUtils.assertSqlCanBeParsedAndDeparsed(
135139
"SELECT JSON_OBJECT( KEY 'foo' VALUE bar, KEY 'foo' VALUE bar) FROM dual ", true);
136140
TestUtils.assertSqlCanBeParsedAndDeparsed(
@@ -280,4 +284,13 @@ public void testJavaMethods() throws JSQLParserException {
280284
Assertions.assertEquals(JsonAggregateUniqueKeysType.WITH, jsonFunction
281285
.withUniqueKeysType(JsonAggregateUniqueKeysType.WITH).getUniqueKeysType());
282286
}
287+
288+
@Test
289+
void testIssue1753JSonObjectAggWithColumns() throws JSQLParserException {
290+
String sqlStr = "SELECT JSON_OBJECTAGG( KEY q.foo VALUE q.bar) FROM dual";
291+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr);
292+
293+
sqlStr = "SELECT JSON_OBJECTAGG(foo, bar) FROM dual";
294+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr);
295+
}
283296
}

0 commit comments

Comments
 (0)