This is an automated email from the ASF dual-hosted git repository.
duanzhengqiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new 07cc083f2c2 Support parsing SQL Server SELECT
JSON_OBJECT('name':'value', sql #29160 (#29740)
07cc083f2c2 is described below
commit 07cc083f2c26df10b5f388517b8a894a212a1465
Author: yydeng626 <[email protected]>
AuthorDate: Fri Jan 19 04:16:15 2024 -0600
Support parsing SQL Server SELECT JSON_OBJECT('name':'value', sql #29160
(#29740)
* Support parsing SQL Server SELECT JSON_OBJECT('name':'value', sql #29160
* fix jsonNullClauseExpression text value
---
.../src/main/antlr4/imports/sqlserver/BaseRule.g4 | 22 ++-
.../src/main/antlr4/imports/sqlserver/Keyword.g4 | 56 +++---
.../statement/SQLServerStatementVisitor.java | 64 +++++++
.../common/segment/dml/expr/KeyValueSegment.java | 42 +++++
.../sqlserver/json/JsonNullClauseSegment.java | 36 ++++
.../segment/expression/ExpressionAssert.java | 55 +++++-
.../jaxb/segment/impl/expr/ExpectedExpression.java | 7 +
.../segment/impl/expr/ExpectedKeyValueSegment.java | 38 ++++
.../impl/json/ExpectedJsonNullClauseSegment.java | 36 ++++
.../resources/case/dml/select-special-function.xml | 202 +++++++++++++++++++++
.../sql/supported/dml/select-special-function.xml | 5 +
11 files changed, 529 insertions(+), 34 deletions(-)
diff --git
a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4
b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4
index b441a522480..d0290de3dcc 100644
--- a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4
+++ b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/BaseRule.g4
@@ -311,7 +311,27 @@ distinct
;
specialFunction
- : castFunction | charFunction | convertFunction | openJsonFunction |
openRowSetFunction
+ : castFunction | charFunction | convertFunction | openJsonFunction |
jsonFunction | openRowSetFunction
+ ;
+
+jsonFunction
+ : jsonObjectFunction | jsonArrayFunction
+ ;
+
+jsonObjectFunction
+ : JSON_OBJECT LP_ (jsonKeyValue (COMMA_ jsonKeyValue)* jsonNullClause?)?
RP_
+ ;
+
+jsonArrayFunction
+ : JSON_ARRAY LP_ expr (COMMA_ expr)* jsonNullClause? RP_
+ ;
+
+jsonKeyValue
+ : expr COLON_ expr
+ ;
+
+jsonNullClause
+ : NULL ON NULL | ABSENT ON NULL
;
castFunction
diff --git
a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/Keyword.g4
b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/Keyword.g4
index 10b9150f395..f28dc17c8d8 100644
--- a/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/Keyword.g4
+++ b/parser/sql/dialect/sqlserver/src/main/antlr4/imports/sqlserver/Keyword.g4
@@ -728,50 +728,54 @@ NOLOCK
: N O L O C K
;
-NOLOCK
-: N O L O C K
-;
-
NOWAIT
-: N O W A I T
-;
+ : N O W A I T
+ ;
PAGLOCK
-: P A G L O C K
-;
+ : P A G L O C K
+ ;
READCOMMITTED
-: R E A D C O M M I T T E D
-;
+ : R E A D C O M M I T T E D
+ ;
READCOMMITTEDLOCK
-: R E A D C O M M I T T E D L O C K
-;
+ : R E A D C O M M I T T E D L O C K
+ ;
READPAST
-: R E A D P A S T
-;
+ : R E A D P A S T
+ ;
REPEATABLEREAD
-: R E P E A T A B L E R E A D
-;
+ : R E P E A T A B L E R E A D
+ ;
ROWLOCK
-: R O W L O C K
-;
+ : R O W L O C K
+ ;
TABLOCK
-: T A B L O C K
-;
+ : T A B L O C K
+ ;
TABLOCKX
-: T A B L O C K X
-;
+ : T A B L O C K X
+ ;
UPDLOCK
-: U P D L O C K
-;
+ : U P D L O C K
+ ;
XLOCK
-: X L O C K
-;
+ : X L O C K
+ ;
+
+JSON_OBJECT
+ : J S O N UL_ O B J E C T
+ ;
+
+JSON_ARRAY
+ : J S O N UL_ A R R A Y
+ ;
diff --git
a/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
b/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
index e9d14837175..e160f2896db 100644
---
a/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
+++
b/parser/sql/dialect/sqlserver/src/main/java/org/apache/shardingsphere/sql/parser/sqlserver/visitor/statement/SQLServerStatementVisitor.java
@@ -63,6 +63,11 @@ import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.Ide
import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.IndexNameContext;
import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.InsertContext;
import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.InsertDefaultValueContext;
+import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.JsonArrayFunctionContext;
+import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.JsonFunctionContext;
+import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.JsonKeyValueContext;
+import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.JsonNullClauseContext;
+import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.JsonObjectFunctionContext;
import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.WithTableHintContext;
import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.InsertSelectClauseContext;
import
org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.InsertValuesClauseContext;
@@ -138,6 +143,7 @@ import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.BinaryOp
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.FunctionSegment;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.InExpression;
+import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.KeyValueSegment;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ListExpression;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.NotExpression;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.complex.CommonExpressionSegment;
@@ -197,6 +203,7 @@ import
org.apache.shardingsphere.sql.parser.sql.common.value.literal.impl.OtherL
import
org.apache.shardingsphere.sql.parser.sql.common.value.literal.impl.StringLiteralValue;
import
org.apache.shardingsphere.sql.parser.sql.common.value.parametermarker.ParameterMarkerValue;
import
org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.exec.ExecSegment;
+import
org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.json.JsonNullClauseSegment;
import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.ddl.SQLServerCreateTableStatement;
import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.ddl.SQLServerUpdateStatisticsStatement;
import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.dml.SQLServerDeleteStatement;
@@ -665,9 +672,66 @@ public abstract class SQLServerStatementVisitor extends
SQLServerStatementBaseVi
if (null != ctx.openRowSetFunction()) {
return visit(ctx.openRowSetFunction());
}
+ if (null != ctx.jsonFunction()) {
+ return visit(ctx.jsonFunction());
+ }
return new FunctionSegment(ctx.getStart().getStartIndex(),
ctx.getStop().getStopIndex(), ctx.getChild(0).getChild(0).getText(),
getOriginalText(ctx));
}
+ @Override
+ public final ASTNode visitJsonFunction(final JsonFunctionContext ctx) {
+ if (null != ctx.jsonArrayFunction()) {
+ return visit(ctx.jsonArrayFunction());
+ }
+ if (null != ctx.jsonObjectFunction()) {
+ return visit(ctx.jsonObjectFunction());
+ }
+ return new FunctionSegment(ctx.getStart().getStartIndex(),
ctx.getStop().getStopIndex(), ctx.getText(), getOriginalText(ctx));
+ }
+
+ @Override
+ public final ASTNode visitJsonArrayFunction(final JsonArrayFunctionContext
ctx) {
+ FunctionSegment result = new
FunctionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(),
ctx.JSON_ARRAY().getText(), getOriginalText(ctx));
+ if (null != ctx.expr()) {
+ for (ExprContext each : ctx.expr()) {
+ result.getParameters().add((ExpressionSegment) visit(each));
+ }
+ }
+ if (null != ctx.jsonNullClause()) {
+ result.getParameters().add(new
LiteralExpressionSegment(ctx.jsonNullClause().start.getStartIndex(),
ctx.jsonNullClause().stop.getStopIndex(), ctx.jsonNullClause().getText()));
+ }
+ return result;
+ }
+
+ @Override
+ public final ASTNode visitJsonObjectFunction(final
JsonObjectFunctionContext ctx) {
+ FunctionSegment result = new
FunctionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(),
ctx.JSON_OBJECT().getText(), getOriginalText(ctx));
+ if (null != ctx.jsonKeyValue()) {
+ for (JsonKeyValueContext each : ctx.jsonKeyValue()) {
+ result.getParameters().add((ExpressionSegment) visit(each));
+ }
+ }
+ if (null != ctx.jsonNullClause()) {
+ result.getParameters().add((ExpressionSegment)
visit(ctx.jsonNullClause()));
+ }
+ return result;
+ }
+
+ @Override
+ public final ASTNode visitJsonNullClause(final JsonNullClauseContext ctx) {
+ return new JsonNullClauseSegment(ctx.start.getStartIndex(),
ctx.stop.getStopIndex(), getOriginalText(ctx));
+ }
+
+ @Override
+ public final ASTNode visitJsonKeyValue(final JsonKeyValueContext ctx) {
+ KeyValueSegment result = new
KeyValueSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex(),
ctx.getText());
+ if (null != ctx.expr()) {
+ result.setKey((ExpressionSegment) visit(ctx.expr(0)));
+ result.setValue((ExpressionSegment) visit(ctx.expr(1)));
+ }
+ return result;
+ }
+
@Override
public final ASTNode visitCastFunction(final CastFunctionContext ctx) {
calculateParameterCount(Collections.singleton(ctx.expr()));
diff --git
a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/expr/KeyValueSegment.java
b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/expr/KeyValueSegment.java
new file mode 100644
index 00000000000..8e49b035f5a
--- /dev/null
+++
b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/expr/KeyValueSegment.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Key value segment.
+ **/
+@RequiredArgsConstructor
+@Getter
+public final class KeyValueSegment implements ExpressionSegment {
+
+ private final int startIndex;
+
+ private final int stopIndex;
+
+ @Setter
+ private ExpressionSegment key;
+
+ @Setter
+ private ExpressionSegment value;
+
+ private final String text;
+}
diff --git
a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/dialect/segment/sqlserver/json/JsonNullClauseSegment.java
b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/dialect/segment/sqlserver/json/JsonNullClauseSegment.java
new file mode 100644
index 00000000000..a22304fba13
--- /dev/null
+++
b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/dialect/segment/sqlserver/json/JsonNullClauseSegment.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package
org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.json;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
+
+/**
+ * Json null clause segment.
+ **/
+@RequiredArgsConstructor
+@Getter
+public final class JsonNullClauseSegment implements ExpressionSegment {
+
+ private final int startIndex;
+
+ private final int stopIndex;
+
+ private final String text;
+}
diff --git
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java
index 44873376b17..13060c0c952 100644
---
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java
+++
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java
@@ -30,6 +30,7 @@ import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.Expressi
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExtractArgExpression;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.FunctionSegment;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.InExpression;
+import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.KeyValueSegment;
import
org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.interval.IntervalDayToSecondExpression;
import
org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.interval.IntervalYearToMonthExpression;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ListExpression;
@@ -52,6 +53,7 @@ import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.Expressi
import
org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.IntervalExpressionProjection;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.DataTypeSegment;
import
org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.xml.XmlQueryAndExistsFunctionSegment;
+import
org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.json.JsonNullClauseSegment;
import
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext;
import
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.SQLSegmentAssert;
import
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.column.ColumnAssert;
@@ -72,6 +74,7 @@ import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedIntervalDayToSecondExpression;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedIntervalExpression;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedIntervalYearToMonthExpression;
+import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedKeyValueSegment;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedListExpression;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedMatchExpression;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedMultisetExpression;
@@ -86,6 +89,7 @@ import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.simple.ExpectedParameterMarkerExpression;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.simple.ExpectedSubquery;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.function.ExpectedFunction;
+import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.json.ExpectedJsonNullClauseSegment;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.xmlquery.ExpectedXmlQueryAndExistsFunctionSegment;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.sql.type.SQLCaseType;
@@ -100,7 +104,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
- * Expression assert.
+ * Expression assert.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ExpressionAssert {
@@ -217,7 +221,7 @@ public final class ExpressionAssert {
/**
* Assert binary operation expression.
- *
+ *
* @param assertContext assert context
* @param actual actual binary operation expression
* @param expected expected binary operation expression
@@ -238,7 +242,7 @@ public final class ExpressionAssert {
/**
* Assert in operation expression.
- *
+ *
* @param assertContext assert context
* @param actual actual in operation expression
* @param expected expected in operation expression
@@ -259,7 +263,7 @@ public final class ExpressionAssert {
/**
* Assert not operation expression.
- *
+ *
* @param assertContext assert context
* @param actual actual not operation expression
* @param expected expected not operation expression
@@ -277,7 +281,7 @@ public final class ExpressionAssert {
/**
* Assert list operation expression.
- *
+ *
* @param assertContext assert context
* @param actual actual list operation expression
* @param expected expected list operation expression
@@ -301,7 +305,7 @@ public final class ExpressionAssert {
/**
* Assert between operation expression.
- *
+ *
* @param assertContext assert context
* @param actual actual between operation expression
* @param expected expected between operation expression
@@ -548,7 +552,7 @@ public final class ExpressionAssert {
/**
* Assert unary operation expression.
- *
+ *
* @param assertContext assert context
* @param actual actual unary operation expression
* @param expected expected unary operation expression
@@ -584,6 +588,39 @@ public final class ExpressionAssert {
}
}
+ /**
+ * Assert key value segment.
+ *
+ * @param assertContext assert context
+ * @param actual actual key value segment
+ * @param expected expected key value segment
+ */
+ private static void assertKeyValueSegment(final SQLCaseAssertContext
assertContext, final KeyValueSegment actual, final ExpectedKeyValueSegment
expected) {
+ if (null == expected) {
+ assertNull(actual, assertContext.getText("Actual key value should
not exist."));
+ } else {
+ assertNotNull(actual, assertContext.getText("Actual key value
should exist."));
+ assertExpression(assertContext, actual.getKey(),
expected.getKey());
+ assertExpression(assertContext, actual.getValue(),
expected.getValue());
+ }
+ }
+
+ /**
+ * Assert json null clause segment.
+ *
+ * @param assertContext assert context
+ * @param actual actual json null clause segment
+ * @param expected expected json null clause segment
+ */
+ private static void assertJsonNullClauseSegment(final SQLCaseAssertContext
assertContext, final JsonNullClauseSegment actual, final
ExpectedJsonNullClauseSegment expected) {
+ if (null == expected) {
+ assertNull(actual, assertContext.getText("Actual json null clause
should not exist."));
+ } else {
+ assertNotNull(actual, assertContext.getText("Actual json null
clause should exist."));
+ assertThat(assertContext.getText("Json null clause assertion
error."), actual.getText(), is(expected.getText()));
+ }
+ }
+
/**
* Assert expression by actual expression segment class type.
*
@@ -655,6 +692,10 @@ public final class ExpressionAssert {
assertUnaryOperationExpression(assertContext,
(UnaryOperationExpression) actual, expected.getUnaryOperationExpression());
} else if (actual instanceof XmlQueryAndExistsFunctionSegment) {
assertXmlQueryAndExistsFunctionSegment(assertContext,
(XmlQueryAndExistsFunctionSegment) actual,
expected.getExpectedXmlQueryAndExistsFunctionSegment());
+ } else if (actual instanceof KeyValueSegment) {
+ assertKeyValueSegment(assertContext, (KeyValueSegment) actual,
expected.getKeyValueSegment());
+ } else if (actual instanceof JsonNullClauseSegment) {
+ assertJsonNullClauseSegment(assertContext, (JsonNullClauseSegment)
actual, expected.getJsonNullClauseSegment());
} else {
throw new UnsupportedOperationException(String.format("Unsupported
expression: %s", actual.getClass().getName()));
}
diff --git
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java
index d8e3ed2eb2f..d6dc7cc2846 100644
---
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java
+++
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java
@@ -27,6 +27,7 @@ import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.simple.ExpectedSubquery;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.function.ExpectedFunction;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.generic.ExpectedDataType;
+import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.json.ExpectedJsonNullClauseSegment;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.projection.impl.aggregation.ExpectedAggregationProjection;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.projection.impl.expression.ExpectedExpressionProjection;
import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.xmlquery.ExpectedXmlQueryAndExistsFunctionSegment;
@@ -123,4 +124,10 @@ public final class ExpectedExpression extends
AbstractExpectedSQLSegment {
@XmlElement(name = "xmlquery-projection")
private ExpectedXmlQueryAndExistsFunctionSegment
expectedXmlQueryAndExistsFunctionSegment;
+
+ @XmlElement(name = "key-value")
+ private ExpectedKeyValueSegment keyValueSegment;
+
+ @XmlElement(name = "json-null-clause-expression")
+ private ExpectedJsonNullClauseSegment jsonNullClauseSegment;
}
diff --git
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedKeyValueSegment.java
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedKeyValueSegment.java
new file mode 100644
index 00000000000..ac7afa2d098
--- /dev/null
+++
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedKeyValueSegment.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr;
+
+import lombok.Getter;
+import lombok.Setter;
+import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.AbstractExpectedSQLSegment;
+
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ * Expected key value segment.
+ */
+@Getter
+@Setter
+public class ExpectedKeyValueSegment extends AbstractExpectedSQLSegment
implements ExpectedExpressionSegment {
+
+ @XmlElement
+ private ExpectedExpression key;
+
+ @XmlElement
+ private ExpectedExpression value;
+}
diff --git
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/json/ExpectedJsonNullClauseSegment.java
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/json/ExpectedJsonNullClauseSegment.java
new file mode 100644
index 00000000000..d13d6d50d86
--- /dev/null
+++
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/json/ExpectedJsonNullClauseSegment.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.json;
+
+import lombok.Getter;
+import lombok.Setter;
+import
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.simple.ExpectedBaseSimpleExpression;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+/**
+ * Expected json null clause segment.
+ */
+@Getter
+@Setter
+public class ExpectedJsonNullClauseSegment extends
ExpectedBaseSimpleExpression {
+
+ @XmlAttribute
+ private String text;
+
+}
diff --git
a/test/it/parser/src/main/resources/case/dml/select-special-function.xml
b/test/it/parser/src/main/resources/case/dml/select-special-function.xml
index 617ad767568..e6a797c6312 100644
--- a/test/it/parser/src/main/resources/case/dml/select-special-function.xml
+++ b/test/it/parser/src/main/resources/case/dml/select-special-function.xml
@@ -715,4 +715,206 @@
</function-table>
</from>
</select>
+
+ <select sql-case-id="select_json_object_simple_key_value">
+ <projections start-index="7" stop-index="43">
+ <expression-projection start-index="7" stop-index="43"
text="JSON_OBJECT('name':'value', 'type':1)">
+ <expr>
+ <function function-name="JSON_OBJECT"
text="JSON_OBJECT('name':'value', 'type':1)" start-index="7" stop-index="43">
+ <parameter>
+ <key-value start-index="19" stop-index="32"
text="'name':'value'">
+ <key>
+ <literal-expression start-index="19"
stop-index="24" value="name"/>
+ </key>
+ <value>
+ <literal-expression start-index="26"
stop-index="32" value="value"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <key-value start-index="35" stop-index="42"
text="'type':1">
+ <key>
+ <literal-expression start-index="35"
stop-index="40" value="type"/>
+ </key>
+ <value>
+ <literal-expression start-index="42"
stop-index="42" value="1"/>
+ </value>
+ </key-value>
+ </parameter>
+ </function>
+ </expr>
+ </expression-projection>
+ </projections>
+ </select>
+
+ <select sql-case-id="select_nest_json_object">
+ <projections start-index="7" stop-index="78">
+ <expression-projection text="JSON_OBJECT('name':'value',
'type':JSON_OBJECT('type_id':1, 'name':'a'))" start-index="7" stop-index="78">
+ <expr>
+ <function function-name="JSON_OBJECT"
text="JSON_OBJECT('name':'value', 'type':JSON_OBJECT('type_id':1, 'name':'a'))"
start-index="7" stop-index="78">
+ <parameter>
+ <key-value text="'name':'value'" start-index="19"
stop-index="32">
+ <key>
+ <literal-expression value="name"
start-index="19" stop-index="24"/>
+ </key>
+ <value>
+ <literal-expression value="value"
start-index="26" stop-index="32"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <key-value
text="'type':JSON_OBJECT('type_id':1,'name':'a')" start-index="35"
stop-index="77">
+ <key>
+ <literal-expression value="type"
start-index="35" stop-index="40"/>
+ </key>
+ <value>
+ <function function-name="JSON_OBJECT"
text="JSON_OBJECT('type_id':1, 'name':'a')" start-index="42" stop-index="77">
+ <parameter>
+ <key-value text="'type_id':1"
start-index="54" stop-index="64">
+ <key>
+ <literal-expression
value="type_id" start-index="54" stop-index="62"/>
+ </key>
+ <value>
+ <literal-expression
value="1" start-index="64" stop-index="64"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <key-value text="'name':'a'"
start-index="67" stop-index="76">
+ <key>
+ <literal-expression
value="name" start-index="67" stop-index="72"/>
+ </key>
+ <value>
+ <literal-expression
value="a" start-index="74" stop-index="76"/>
+ </value>
+ </key-value>
+ </parameter>
+ </function>
+ </value>
+ </key-value>
+ </parameter>
+ </function>
+ </expr>
+ </expression-projection>
+ </projections>
+ </select>
+
+ <select sql-case-id="select_json_array">
+ <projections start-index="7" stop-index="58">
+ <expression-projection text="JSON_OBJECT('name':'value',
'type':JSON_ARRAY(1, 2))" start-index="7" stop-index="58">
+ <expr>
+ <function function-name="JSON_OBJECT"
text="JSON_OBJECT('name':'value', 'type':JSON_ARRAY(1, 2))" start-index="7"
stop-index="58">
+ <parameter>
+ <key-value text="'name':'value'" start-index="19"
stop-index="32">
+ <key>
+ <literal-expression value="name"
start-index="19" stop-index="24"/>
+ </key>
+ <value>
+ <literal-expression value="value"
start-index="26" stop-index="32"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <key-value text="'type':JSON_ARRAY(1,2)"
start-index="35" stop-index="57">
+ <key>
+ <literal-expression value="type"
start-index="35" stop-index="40"/>
+ </key>
+ <value>
+ <function function-name="JSON_ARRAY"
text="JSON_ARRAY(1, 2)" start-index="42" stop-index="57">
+ <parameter>
+ <literal-expression value="1"
start-index="53" stop-index="53"/>
+ </parameter>
+ <parameter>
+ <literal-expression value="2"
start-index="56" stop-index="56"/>
+ </parameter>
+ </function>
+ </value>
+ </key-value>
+ </parameter>
+ </function>
+ </expr>
+ </expression-projection>
+ </projections>
+ </select>
+
+ <select sql-case-id="select_json_object_absent_not_null">
+ <projections start-index="7" stop-index="61">
+ <expression-projection text="JSON_OBJECT('name':'value',
'type':NULL ABSENT ON NULL)" start-index="7" stop-index="61">
+ <expr>
+ <function function-name="JSON_OBJECT"
text="JSON_OBJECT('name':'value', 'type':NULL ABSENT ON NULL)" start-index="7"
stop-index="61">
+ <parameter>
+ <key-value start-index="19" stop-index="32"
text="'name':'value'">
+ <key>
+ <literal-expression start-index="19"
stop-index="24" value="name"/>
+ </key>
+ <value>
+ <literal-expression start-index="26"
stop-index="32" value="value"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <key-value start-index="35" stop-index="45"
text="'type':NULL">
+ <key>
+ <literal-expression start-index="35"
stop-index="40" value="type"/>
+ </key>
+ <value>
+ <literal-expression start-index="42"
stop-index="45" value="null"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <json-null-clause-expression text="ABSENT ON NULL"
start-index="47" stop-index="60"/>
+ </parameter>
+ </function>
+ </expr>
+ </expression-projection>
+ </projections>
+ </select>
+
+ <select sql-case-id="select_json_object_with_subquery">
+ <projections start-index="7" stop-index="84">
+ <expression-projection text="JSON_OBJECT('user_name':USER_NAME(),
@id_key:@id_value, 'sid':(SELECT @@SPID))" start-index="7" stop-index="84">
+ <expr>
+ <function function-name="JSON_OBJECT"
text="JSON_OBJECT('user_name':USER_NAME(), @id_key:@id_value, 'sid':(SELECT
@@SPID))" start-index="7" stop-index="84">
+ <parameter>
+ <key-value text="'user_name':USER_NAME()"
start-index="19" stop-index="41">
+ <key>
+ <literal-expression value="user_name"
start-index="19" stop-index="29"/>
+ </key>
+ <value>
+ <function function-name="USER_NAME"
text="USER_NAME()" start-index="31" stop-index="41"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <key-value text="@id_key:@id_value"
start-index="44" stop-index="60">
+ <key>
+ <column name="@id_key" start-index="44"
stop-index="50"/>
+ </key>
+ <value>
+ <column name="@id_value" start-index="52"
stop-index="60"/>
+ </value>
+ </key-value>
+ </parameter>
+ <parameter>
+ <key-value text="'sid':(SELECT@@SPID)"
start-index="63" stop-index="83">
+ <key>
+ <literal-expression value="sid"
start-index="63" stop-index="67"/>
+ </key>
+ <value>
+ <subquery start-index="69" stop-index="83">
+ <select>
+ <projections start-index="77"
stop-index="82">
+ <column-projection
name="@@SPID" start-index="77" stop-index="82"/>
+ </projections>
+ </select>
+ </subquery>
+ </value>
+ </key-value>
+ </parameter>
+ </function>
+ </expr>
+ </expression-projection>
+ </projections>
+ </select>
</sql-parser-test-cases>
diff --git
a/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
b/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
index d5493b6bd2c..7abcf363d65 100644
---
a/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
+++
b/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
@@ -49,4 +49,9 @@
<sql-case id="select_from_open_json_function_with_path" value="SELECT
[key], value FROM OPENJSON(@json,'$.path.to."sub-object"')"
db-types="SQLServer"/>
<sql-case id="select_from_open_row_set_with_provider_name" value="SELECT
a.* FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0',
'C:\SAMPLES\Northwind.mdb';'admin';'password', Customers) AS a;"
db-types="SQLServer"/>
<sql-case id="select_from_open_row_set_with_provider_string" value="SELECT
d.* FROM
OPENROWSET('SQLNCLI','Server=Seattle1;Trusted_Connection=yes;',AdventureWorks2022.HumanResources.Department)
AS d;" db-types="SQLServer"/>
+ <sql-case id="select_json_object_simple_key_value" value="SELECT
JSON_OBJECT('name':'value', 'type':1)" db-types="SQLServer" />
+ <sql-case id="select_json_object_absent_not_null" value="SELECT
JSON_OBJECT('name':'value', 'type':NULL ABSENT ON NULL)" db-types="SQLServer" />
+ <sql-case id="select_json_array" value="SELECT JSON_OBJECT('name':'value',
'type':JSON_ARRAY(1, 2))" db-types="SQLServer" />
+ <sql-case id="select_nest_json_object" value="SELECT
JSON_OBJECT('name':'value', 'type':JSON_OBJECT('type_id':1, 'name':'a'))"
db-types="SQLServer" />
+ <sql-case id="select_json_object_with_subquery" value="SELECT
JSON_OBJECT('user_name':USER_NAME(), @id_key:@id_value, 'sid':(SELECT @@SPID))"
db-types="SQLServer" />
</sql-cases>