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.&quot;sub-object&quot;')" 
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>


Reply via email to