Skip to content

Commit 13c2321

Browse files
committed
Merge origin/master
2 parents 00b2440 + e3f531c commit 13c2321

File tree

8 files changed

+142
-82
lines changed

8 files changed

+142
-82
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ plugins {
1212
}
1313

1414
group = 'com.github.jsqlparser'
15-
version = '4.3-SNAPSHOT'
15+
version = '4.4-SNAPSHOT'
1616
description = 'JSQLParser library'
1717
java.sourceCompatibility = JavaVersion.VERSION_1_8
1818

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public void add(int i, JsonFunctionExpression expression) {
5757
expressions.add(i, expression);
5858
}
5959

60+
public boolean isEmpty() {
61+
return keyValuePairs.isEmpty();
62+
}
63+
6064
public JsonAggregateOnNullType getOnNullType() {
6165
return onNullType;
6266
}
@@ -122,6 +126,9 @@ public StringBuilder append(StringBuilder builder) {
122126
case POSTGRES_OBJECT:
123127
appendPostgresObject(builder);
124128
break;
129+
case MYSQL_OBJECT:
130+
appendMySqlObject(builder);
131+
break;
125132
case ARRAY:
126133
appendArray(builder);
127134
break;
@@ -200,6 +207,22 @@ public StringBuilder appendPostgresObject(StringBuilder builder) {
200207
return builder;
201208
}
202209

210+
public StringBuilder appendMySqlObject(StringBuilder builder) {
211+
builder.append("JSON_OBJECT( ");
212+
int i=0;
213+
for (JsonKeyValuePair keyValuePair : keyValuePairs) {
214+
if (i>0) {
215+
builder.append(", ");
216+
}
217+
builder.append(keyValuePair.getKey());
218+
builder.append(", ").append(keyValuePair.getValue());
219+
i++;
220+
}
221+
builder.append(" ) ");
222+
223+
return builder;
224+
}
225+
203226
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
204227
public StringBuilder appendArray(StringBuilder builder) {
205228
builder.append("JSON_ARRAY( ");

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ public enum JsonFunctionType {
1818
OBJECT
1919
, ARRAY
2020
, POSTGRES_OBJECT
21+
, MYSQL_OBJECT
2122
}

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

Lines changed: 60 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3735,6 +3735,8 @@ Expression PrimaryExpression() #PrimaryExpression:
37353735

37363736
| LOOKAHEAD(JsonExpression()) retval=JsonExpression()
37373737

3738+
| LOOKAHEAD(Function()) retval=Function() [ LOOKAHEAD(2) retval = AnalyticExpression( (Function) retval ) ]
3739+
37383740
| LOOKAHEAD(JsonFunction()) retval = JsonFunction()
37393741

37403742
| LOOKAHEAD(JsonAggregateFunction()) retval = JsonAggregateFunction()
@@ -3743,7 +3745,7 @@ Expression PrimaryExpression() #PrimaryExpression:
37433745

37443746
| LOOKAHEAD(FullTextSearch()) retval = FullTextSearch()
37453747

3746-
| LOOKAHEAD(Function()) retval=Function() [ LOOKAHEAD(2) retval = AnalyticExpression( (Function) retval ) ]
3748+
37473749

37483750
| LOOKAHEAD(2) retval = IntervalExpression() { dateExpressionAllowed = false; }
37493751

@@ -3981,11 +3983,13 @@ JsonFunction JsonFunction() : {
39813983
JsonFunction result = new JsonFunction();
39823984
boolean usingKeyKeyword = false;
39833985
boolean usingValueKeyword = false;
3986+
boolean usingFormatJason = false;
39843987
Token keyToken;
39853988
Token valueToken = null;
3989+
Column column = null;
39863990
JsonKeyValuePair keyValuePair;
39873991

3988-
Expression expression;
3992+
Expression expression = null;
39893993
JsonFunctionExpression functionExpression;
39903994

39913995
}
@@ -3994,64 +3998,64 @@ JsonFunction JsonFunction() : {
39943998
(
39953999
( <K_JSON_OBJECT>
39964000
"(" { result.setType( JsonFunctionType.OBJECT ); }
3997-
3998-
(
3999-
// --- First Element
4000-
LOOKAHEAD(2)
4001-
(
4002-
// Postgres Specific Syntax:
4003-
// SELECT json_object('{a, 1, b, 2}');
4004-
// SELECT json_object('{{a, 1}, {b, 2}}');
4005-
// SELECT json_object('{a, b}', '{1,2 }');
4006-
{ result.setType( JsonFunctionType.POSTGRES_OBJECT ); }
4007-
keyToken = <S_CHAR_LITERAL>
4008-
[ "," valueToken = <S_CHAR_LITERAL> ]
4009-
{ keyValuePair = new JsonKeyValuePair( keyToken.image, valueToken !=null ? valueToken.image : null, false, false ); result.add(keyValuePair); }
4010-
)
4011-
|
40124001
(
4002+
(
4003+
// SQL2016 compliant Syntax
4004+
(
4005+
[ "KEY" { usingKeyKeyword = true; } ]
4006+
keyToken = <S_CHAR_LITERAL>
4007+
4008+
( LOOKAHEAD(2)
4009+
( ":" | "," { result.setType( JsonFunctionType.POSTGRES_OBJECT ); } | "VALUE" { usingValueKeyword = true; } )
4010+
(
4011+
expression = Expression()
4012+
)
4013+
[ <K_FORMAT> <K_JSON> { usingFormatJason = true; } ]
4014+
)? {
4015+
if (expression !=null) {
4016+
keyValuePair = new JsonKeyValuePair( keyToken.image, expression, usingKeyKeyword, usingValueKeyword );
4017+
keyValuePair.setUsingFormatJson( usingFormatJason );
4018+
result.add(keyValuePair);
4019+
} else {
4020+
result.setType( JsonFunctionType.POSTGRES_OBJECT );
4021+
keyValuePair = new JsonKeyValuePair( keyToken.image, null, false, false );
4022+
result.add(keyValuePair);
4023+
}
4024+
}
4025+
4026+
// --- Next Elements
4027+
( "," { usingKeyKeyword = false; usingValueKeyword = false; }
4028+
[ "KEY" { usingKeyKeyword = true; } ]
4029+
keyToken = <S_CHAR_LITERAL>
4030+
( ":" | "," { result.setType( JsonFunctionType.MYSQL_OBJECT ); } | "VALUE" { usingValueKeyword = true; } )
4031+
(
4032+
expression = Expression() { keyValuePair = new JsonKeyValuePair( keyToken.image, expression, usingKeyKeyword, usingValueKeyword ); result.add(keyValuePair); }
4033+
)
4034+
[ <K_FORMAT> <K_JSON> { keyValuePair.setUsingFormatJson( true ); } ]
4035+
)*
4036+
)
4037+
)?
40134038

4014-
// SQL2016 compliant Syntax
4015-
[ "KEY" { usingKeyKeyword = true; } ]
4039+
[
4040+
(
4041+
<K_NULL> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.NULL ); }
4042+
)
4043+
|
4044+
(
4045+
<K_ABSENT> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); }
4046+
)
4047+
]
40164048

4017-
( keyToken = <DT_ZONE> | keyToken = <S_DOUBLE> | keyToken = <S_LONG> | keyToken = <S_HEX> | keyToken = <S_CHAR_LITERAL> | keyToken = <S_IDENTIFIER> | keyToken = <S_QUOTED_IDENTIFIER> )
4018-
( ":" | "VALUE" { usingValueKeyword = true; } )
4019-
( valueToken = <S_IDENTIFIER> | valueToken = <S_QUOTED_IDENTIFIER> ) { keyValuePair = new JsonKeyValuePair( keyToken.image, valueToken.image, usingKeyKeyword, usingValueKeyword ); result.add(keyValuePair); }
4049+
[
4050+
(
4051+
<K_WITH> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITH ); }
4052+
)
4053+
|
4054+
(
4055+
<K_WITHOUT> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITHOUT ); }
4056+
)
4057+
]
40204058
)
4021-
4022-
[ <K_FORMAT> <K_JSON> { keyValuePair.setUsingFormatJson( true ); } ]
4023-
4024-
// --- Next Elements
4025-
( "," { usingKeyKeyword = false; usingValueKeyword = false; }
4026-
[ "KEY" { usingKeyKeyword = true; } ]
4027-
keyToken = <S_IDENTIFIER>
4028-
( ":" | "VALUE" { usingValueKeyword = true; } )
4029-
// token = <DT_ZONE> | <S_DOUBLE> | <S_LONG> | <S_HEX> | <S_CHAR_LITERAL> { result.setValue( token.image ); }
4030-
valueToken = <S_IDENTIFIER> { keyValuePair = new JsonKeyValuePair( keyToken.image, valueToken.image, usingKeyKeyword, usingValueKeyword ); result.add(keyValuePair); }
4031-
4032-
[ <K_FORMAT> <K_JSON> { keyValuePair.setUsingFormatJson( true ); } ]
4033-
)*
4034-
)*
4035-
4036-
[
4037-
(
4038-
<K_NULL> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.NULL ); }
4039-
)
4040-
|
4041-
(
4042-
<K_ABSENT> <K_ON> <K_NULL> { result.setOnNullType( JsonAggregateOnNullType.ABSENT ); }
4043-
)
4044-
]
4045-
4046-
[
4047-
(
4048-
<K_WITH> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITH ); }
4049-
)
4050-
|
4051-
(
4052-
<K_WITHOUT> <K_UNIQUE> <K_KEYS> { result.setUniqueKeysType( JsonAggregateUniqueKeysType.WITHOUT ); }
4053-
)
4054-
]
40554059
")"
40564060
)
40574061
|

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public void testAtTimeZoneExpression() throws JSQLParserException {
216216
public void testJsonFunction() throws JSQLParserException {
217217
ExpressionVisitorAdapter adapter = new ExpressionVisitorAdapter();
218218
CCJSqlParserUtil
219-
.parseExpression("JSON_OBJECT( KEY foo VALUE bar, KEY foo VALUE bar)")
219+
.parseExpression("JSON_OBJECT( KEY 'foo' VALUE bar, KEY 'foo' VALUE bar)")
220220
.accept(adapter);
221221
CCJSqlParserUtil
222222
.parseExpression("JSON_ARRAY( (SELECT * from dual) )")

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

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,10 @@
99
*/
1010
package net.sf.jsqlparser.expression;
1111

12-
import java.util.Objects;
1312
import net.sf.jsqlparser.JSQLParserException;
1413
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
1514
import net.sf.jsqlparser.test.TestUtils;
1615
import org.junit.jupiter.api.Assertions;
17-
import static org.junit.jupiter.api.Assertions.assertEquals;
18-
import static org.junit.jupiter.api.Assertions.assertFalse;
19-
import static org.junit.jupiter.api.Assertions.assertNotNull;
20-
import static org.junit.jupiter.api.Assertions.assertTrue;
2116
import org.junit.jupiter.api.Test;
2217

2318
/**
@@ -69,17 +64,17 @@ public void testObjectBuilder() throws JSQLParserException {
6964
JsonKeyValuePair keyValuePair2 = new JsonKeyValuePair("foo", "bar", false, false).withUsingKeyKeyword(true).withUsingValueKeyword(true).withUsingFormatJson(false);
7065

7166
// this should work because we compare based on KEY only
72-
assertEquals(keyValuePair1, keyValuePair2);
67+
Assertions.assertEquals(keyValuePair1, keyValuePair2);
7368

7469
// this must fail because all the properties are considered
75-
assertFalse(Objects.equals(keyValuePair1.toString(), keyValuePair2.toString()));
70+
Assertions.assertNotEquals(keyValuePair1.toString(), keyValuePair2.toString());
7671

7772
JsonKeyValuePair keyValuePair3 = new JsonKeyValuePair("foo", "bar", false, false).withUsingKeyKeyword(false).withUsingValueKeyword(false).withUsingFormatJson(false);
78-
assertNotNull(keyValuePair3);
79-
assertEquals(keyValuePair3, keyValuePair3);
80-
assertFalse(Objects.equals(keyValuePair3, f));
73+
Assertions.assertNotNull(keyValuePair3);
74+
Assertions.assertEquals(keyValuePair3, keyValuePair3);
75+
Assertions.assertNotEquals(keyValuePair3, f);
8176

82-
assertTrue(keyValuePair3.hashCode() != 0);
77+
Assertions.assertTrue(keyValuePair3.hashCode() != 0);
8378

8479
f.add(keyValuePair2);
8580
}
@@ -95,7 +90,7 @@ public void testArrayBuilder() throws JSQLParserException {
9590
JsonFunctionExpression expression2 = new JsonFunctionExpression(new NullValue()).withUsingFormatJson(
9691
true);
9792

98-
assertTrue(Objects.equals(expression1.toString(), expression2.toString()));
93+
Assertions.assertEquals(expression1.toString(), expression2.toString());
9994

10095
f.add(expression1);
10196
f.add(expression2);
@@ -131,24 +126,24 @@ public void testArrayAgg() throws JSQLParserException {
131126
@Test
132127
public void testObject() throws JSQLParserException {
133128
TestUtils.assertSqlCanBeParsedAndDeparsed(
134-
"SELECT JSON_OBJECT( KEY foo VALUE bar, KEY foo VALUE bar) FROM dual ", true);
135-
TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT JSON_OBJECT( foo:bar, foo:bar) FROM dual ",
129+
"SELECT JSON_OBJECT( KEY 'foo' VALUE bar, KEY 'foo' VALUE bar) FROM dual ", true);
130+
TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT JSON_OBJECT( 'foo' : bar, 'foo' : bar) FROM dual ",
136131
true);
137132
TestUtils.assertSqlCanBeParsedAndDeparsed(
138-
"SELECT JSON_OBJECT( foo:bar, foo:bar FORMAT JSON) FROM dual ", true);
133+
"SELECT JSON_OBJECT( 'foo':bar, 'foo':bar FORMAT JSON) FROM dual ", true);
139134
TestUtils.assertSqlCanBeParsedAndDeparsed(
140-
"SELECT JSON_OBJECT( KEY foo VALUE bar, foo:bar FORMAT JSON, foo:bar NULL ON NULL) FROM dual ",
135+
"SELECT JSON_OBJECT( KEY 'foo' VALUE bar, 'foo':bar FORMAT JSON, 'foo':bar NULL ON NULL) FROM dual ",
141136
true);
142137
TestUtils.assertSqlCanBeParsedAndDeparsed(
143-
"SELECT JSON_OBJECT( KEY foo VALUE bar FORMAT JSON, foo:bar, foo:bar ABSENT ON NULL) FROM dual ",
138+
"SELECT JSON_OBJECT( KEY 'foo' VALUE bar FORMAT JSON, 'foo':bar, 'foo':bar ABSENT ON NULL) FROM dual ",
144139
true);
145140

146141
TestUtils.assertSqlCanBeParsedAndDeparsed(
147-
"SELECT JSON_OBJECT( KEY foo VALUE bar FORMAT JSON, foo:bar, foo:bar ABSENT ON NULL WITH UNIQUE KEYS) FROM dual ",
142+
"SELECT JSON_OBJECT( KEY 'foo' VALUE bar FORMAT JSON, 'foo':bar, 'foo':bar ABSENT ON NULL WITH UNIQUE KEYS) FROM dual ",
148143
true);
149144

150145
TestUtils.assertSqlCanBeParsedAndDeparsed(
151-
"SELECT JSON_OBJECT( KEY foo VALUE bar FORMAT JSON, foo:bar, foo:bar ABSENT ON NULL WITHOUT UNIQUE KEYS) FROM dual ",
146+
"SELECT JSON_OBJECT( KEY 'foo' VALUE bar FORMAT JSON, 'foo':bar, 'foo':bar ABSENT ON NULL WITHOUT UNIQUE KEYS) FROM dual ",
152147
true);
153148

154149
TestUtils.assertExpressionCanBeParsedAndDeparsed("json_object(null on null)", true);
@@ -158,6 +153,42 @@ public void testObject() throws JSQLParserException {
158153
TestUtils.assertExpressionCanBeParsedAndDeparsed("json_object()", true);
159154
}
160155

156+
@Test
157+
public void testObjectWithExpression() throws JSQLParserException {
158+
TestUtils.assertSqlCanBeParsedAndDeparsed(
159+
"SELECT JSON_OBJECT( KEY 'foo' VALUE cast( bar AS VARCHAR(40)), KEY 'foo' VALUE bar) FROM dual ", true);
160+
161+
TestUtils.assertSqlCanBeParsedAndDeparsed(
162+
"SELECT JSON_ARRAYAGG(obj) FROM (SELECT trt.relevance_id,JSON_OBJECT('id',CAST(trt.id AS CHAR),'taskName',trt.task_name,'openStatus',trt.open_status,'taskSort',trt.task_sort) as obj FROM tb_review_task trt ORDER BY trt.task_sort ASC)", true);
163+
}
164+
165+
@Test
166+
public void testObjectIssue1504() throws JSQLParserException {
167+
TestUtils.assertSqlCanBeParsedAndDeparsed(
168+
"SELECT JSON_OBJECT(key 'person' value tp.account) obj", true);
169+
170+
TestUtils.assertSqlCanBeParsedAndDeparsed(
171+
"SELECT JSON_OBJECT(key 'person' value tp.account, key 'person' value tp.account) obj", true);
172+
173+
TestUtils.assertSqlCanBeParsedAndDeparsed(
174+
"SELECT JSON_OBJECT( 'person' : tp.account) obj", true);
175+
176+
TestUtils.assertSqlCanBeParsedAndDeparsed(
177+
"SELECT JSON_OBJECT( 'person' : tp.account, 'person' : tp.account) obj", true);
178+
179+
TestUtils.assertSqlCanBeParsedAndDeparsed(
180+
"SELECT JSON_OBJECT( 'person' : '1', 'person' : '2') obj", true);
181+
182+
TestUtils.assertSqlCanBeParsedAndDeparsed(
183+
"SELECT JSON_OBJECT( 'person' VALUE tp.person, 'account' VALUE tp.account) obj", true);
184+
}
185+
186+
@Test
187+
public void testObjectMySQL() throws JSQLParserException {
188+
TestUtils.assertSqlCanBeParsedAndDeparsed(
189+
"SELECT JSON_OBJECT('person', tp.person, 'account', tp.account) obj", true);
190+
}
191+
161192
@Test
162193
public void testArray() throws JSQLParserException {
163194
TestUtils.assertSqlCanBeParsedAndDeparsed(
@@ -213,14 +244,14 @@ public void testIssue1371() throws JSQLParserException {
213244

214245
@Test
215246
public void testJavaMethods() throws JSQLParserException {
216-
String expressionStr = "JSON_OBJECT( KEY foo VALUE bar FORMAT JSON, foo:bar, foo:bar ABSENT ON NULL WITHOUT UNIQUE KEYS)";
247+
String expressionStr = "JSON_OBJECT( KEY 'foo' VALUE bar FORMAT JSON, 'foo':bar, 'foo':bar ABSENT ON NULL WITHOUT UNIQUE KEYS)";
217248
JsonFunction jsonFunction = (JsonFunction) CCJSqlParserUtil.parseExpression(expressionStr);
218249

219250
Assertions.assertEquals(JsonFunctionType.OBJECT, jsonFunction.getType());
220251
Assertions.assertNotEquals(jsonFunction.withType(JsonFunctionType.POSTGRES_OBJECT), jsonFunction.getType());
221252

222253
Assertions.assertEquals(3, jsonFunction.getKeyValuePairs().size());
223-
Assertions.assertEquals(new JsonKeyValuePair("foo", "bar", true, true), jsonFunction.getKeyValuePair(0));
254+
Assertions.assertEquals(new JsonKeyValuePair("'foo'", "bar", true, true), jsonFunction.getKeyValuePair(0));
224255

225256
jsonFunction.setOnNullType(JsonAggregateOnNullType.NULL);
226257
Assertions.assertEquals(JsonAggregateOnNullType.ABSENT, jsonFunction.withOnNullType(JsonAggregateOnNullType.ABSENT).getOnNullType());

src/test/java/net/sf/jsqlparser/test/TestUtils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ public class TestUtils {
5454
private static final Pattern SQL_SANITATION_PATTERN
5555
= Pattern.compile("(\\s+)", Pattern.MULTILINE);
5656

57+
// Assure SPACE around Syntax Characters
5758
private static final Pattern SQL_SANITATION_PATTERN2
58-
= Pattern.compile("\\s*([!/,()=+\\-*|\\]<>])\\s*", Pattern.MULTILINE);
59+
= Pattern.compile("\\s*([!/,()=+\\-*|\\]<>:])\\s*", Pattern.MULTILINE);
5960

6061
/**
6162
* @param statement
@@ -265,7 +266,7 @@ public static String buildSqlString(final String originalSql, boolean laxDeparsi
265266
// redundant white space
266267
sanitizedSqlStr = SQL_SANITATION_PATTERN.matcher(sanitizedSqlStr).replaceAll(" ");
267268

268-
// replace some more stuff
269+
// assure spacing around Syntax Characters
269270
sanitizedSqlStr = SQL_SANITATION_PATTERN2.matcher(sanitizedSqlStr).replaceAll("$1");
270271
return sanitizedSqlStr.trim().toLowerCase();
271272
} else {

src/test/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidatorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ public void testJsonFunctionExpression() throws JSQLParserException {
226226
public void testJsonAggregartFunctionExpression() throws JSQLParserException {
227227
validateNoErrors("SELECT JSON_ARRAYAGG( a FORMAT JSON ABSENT ON NULL ) FILTER( WHERE name = 'Raj' ) OVER( PARTITION BY name ) FROM mytbl", 1,
228228
EXPRESSIONS);
229-
validateNoErrors("SELECT JSON_OBJECT( KEY foo VALUE bar FORMAT JSON, foo:bar, foo:bar ABSENT ON NULL) FROM mytbl", 1,
229+
validateNoErrors("SELECT JSON_OBJECT( KEY 'foo' VALUE bar FORMAT JSON, 'foo':bar, 'foo':bar ABSENT ON NULL) FROM mytbl", 1,
230230
EXPRESSIONS);
231231
}
232232

0 commit comments

Comments
 (0)