This is an automated email from the ASF dual-hosted git repository.

hongze pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/master by this push:
     new 02eb106  [CALCITE-3010] In SQL parser, move JsonValueExpression into 
Expression
02eb106 is described below

commit 02eb106bcd1b0fdb1d0be06d117a94eddc9f3fce
Author: Hongze Zhang <[email protected]>
AuthorDate: Fri Apr 19 23:40:12 2019 +0800

    [CALCITE-3010] In SQL parser, move JsonValueExpression into Expression
    
    * SqlJsonValueExpressionOperator is now a postfix operator;
    * Remove SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION;
    * Remove SqlStdOperatorTable.JSON_API_COMMON_SYNTAX_WITHOUT_PATH;
    * When "FORMAT JSON" is implicit, a call to SqlJsonValueExpressionOperator 
is added and validated during sql-to-rel process;
    * Correct JSON_DEPTH's return type.
---
 core/src/main/codegen/templates/Parser.jj          | 145 +++------------------
 .../calcite/adapter/enumerable/RexImpTable.java    |   6 -
 .../org/apache/calcite/runtime/SqlFunctions.java   |  34 ++---
 .../sql/fun/SqlJsonApiCommonSyntaxOperator.java    |  30 +----
 .../sql/fun/SqlJsonArrayAggAggFunction.java        |   7 +-
 .../calcite/sql/fun/SqlJsonArrayFunction.java      |   5 +-
 .../calcite/sql/fun/SqlJsonDepthFunction.java      |   4 +-
 .../calcite/sql/fun/SqlJsonKeysFunction.java       |  20 +--
 .../calcite/sql/fun/SqlJsonLengthFunction.java     |  21 +--
 .../sql/fun/SqlJsonObjectAggAggFunction.java       |  12 +-
 .../calcite/sql/fun/SqlJsonObjectFunction.java     |  16 ++-
 .../sql/fun/SqlJsonValueExpressionOperator.java    |  39 ++----
 .../calcite/sql/fun/SqlStdOperatorTable.java       |  15 +--
 .../org/apache/calcite/sql/type/InferTypes.java    |  14 ++
 .../calcite/sql2rel/StandardConvertletTable.java   |  80 +++++++++++-
 .../src/main/java/org/apache/calcite/util/Bug.java |   5 +
 .../org/apache/calcite/util/BuiltInMethod.java     |   4 -
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |  13 ++
 .../apache/calcite/sql/parser/SqlParserTest.java   |  97 ++++++++------
 .../apache/calcite/sql/test/SqlAdvisorTest.java    |   1 +
 .../calcite/sql/test/SqlOperatorBaseTest.java      |  42 +++---
 .../apache/calcite/test/SqlJsonFunctionsTest.java  |  15 +--
 .../apache/calcite/test/SqlToRelConverterTest.java |  40 ++++++
 .../org/apache/calcite/test/SqlValidatorTest.java  |  29 ++++-
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 101 ++++++++++++--
 core/src/test/resources/sql/agg.iq                 | 123 +++++++++--------
 26 files changed, 493 insertions(+), 425 deletions(-)

diff --git a/core/src/main/codegen/templates/Parser.jj 
b/core/src/main/codegen/templates/Parser.jj
index 77ecacb..cd49717 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -3090,6 +3090,7 @@ List<Object> Expression2(ExprContext exprContext) :
 {
     Expression2b(exprContext, list)
     (
+        LOOKAHEAD(2)
         (
             LOOKAHEAD(2)
             (
@@ -4882,20 +4883,10 @@ SqlNode BuiltinFunctionCall() :
     |
         node = JsonValueFunctionCall() { return node; }
     |
-        node = JsonKeysFunctionCall() { return node; }
-    |
-        node = JsonPrettyFunctionCall() { return node; }
-    |
         node = JsonQueryFunctionCall() { return node; }
     |
         node = JsonObjectFunctionCall() { return node; }
     |
-        node = JsonTypeFunctionCall() { return node; }
-    |
-        node = JsonDepthFunctionCall() { return node; }
-    |
-        node = JsonLengthFunctionCall() { return node; }
-    |
         node = JsonObjectAggFunctionCall() { return node; }
     |
         node = JsonArrayFunctionCall() { return node; }
@@ -4912,7 +4903,7 @@ SqlJsonEncoding JsonRepresentation() :
     <JSON>
     [
         // Encoding is currently ignored.
-        <ENCODING>
+        LOOKAHEAD(2) <ENCODING>
         (
             <UTF8> { return SqlJsonEncoding.UTF8; }
             |
@@ -4956,30 +4947,6 @@ SqlDataTypeSpec JsonOutputClause() :
     }
 }
 
-SqlNode JsonValueExpression(boolean implicitFormatJson) :
-{
-    SqlNode e;
-    List<SqlNode> args = new ArrayList<SqlNode>();
-    Span span;
-}
-{
-    e = Expression(ExprContext.ACCEPT_NON_QUERY) {
-        args.add(e);
-        span = Span.of(e);
-    }
-    [
-        JsonInputClause() {
-            return 
SqlStdOperatorTable.JSON_VALUE_EXPRESSION.createCall(span.end(this), args);
-        }
-    ]
-    {
-        if (implicitFormatJson) {
-            return 
SqlStdOperatorTable.JSON_VALUE_EXPRESSION.createCall(span.end(this), args);
-        }
-        return 
SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION.createCall(span.end(this), 
args);
-    }
-}
-
 SqlNode JsonPathSpec() :
 {
     SqlNode e;
@@ -4995,17 +4962,15 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
     SqlNode e;
     List<SqlNode> args = new ArrayList<SqlNode>();
     Span span;
-    SqlOperator op;
 }
 {
-    e = JsonValueExpression(true) {
+    e = Expression(ExprContext.ACCEPT_NON_QUERY) {
         args.add(e);
         span = Span.of(e);
     }
     (
         <COMMA>
         e = Expression(ExprContext.ACCEPT_NON_QUERY) {
-            op = SqlStdOperatorTable.JSON_API_COMMON_SYNTAX;
             args.add(e);
         }
     |
@@ -5013,11 +4978,10 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
             if (!acceptNonPath) {
               throw new 
ParseException(RESOURCE.jsonPathMustBeSpecified().str());
             }
-            op = SqlStdOperatorTable.JSON_API_COMMON_SYNTAX_WITHOUT_PATH;
         }
     )
     [
-        <PASSING> e = JsonValueExpression(false) {
+        <PASSING> e = Expression(ExprContext.ACCEPT_NON_QUERY) {
             args.add(e);
         }
         <AS> e = SimpleIdentifier() {
@@ -5025,7 +4989,7 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
         }
         (
             <COMMA>
-            e = JsonValueExpression(false) {
+            e = Expression(ExprContext.ACCEPT_NON_QUERY) {
                         args.add(e);
             }
             <AS> e = SimpleIdentifier() {
@@ -5034,7 +4998,7 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
         )*
     ]
     {
-        return op.createCall(span.end(this), args);
+        return 
SqlStdOperatorTable.JSON_API_COMMON_SYNTAX.createCall(span.end(this), args);
     }
 }
 
@@ -5147,39 +5111,6 @@ SqlCall JsonValueFunctionCall() :
     }
 }
 
-SqlCall JsonKeysFunctionCall() :
-{
-    final SqlNode[] args = new SqlNode[1];
-    SqlNode e;
-    final Span span;
-    List<SqlNode> behavior;
-}
-{
-    <JSON_KEYS> { span = span(); }
-    <LPAREN> e = JsonApiCommonSyntax(true) {
-       args[0] = e;
-    }
-    <RPAREN> {
-        return SqlStdOperatorTable.JSON_KEYS.createCall(span.end(this), args);
-    }
-}
-
-SqlCall JsonPrettyFunctionCall() :
-{
-    final SqlNode[] args = new SqlNode[1];
-    SqlNode e;
-    final Span span;
-}
-{
-    <JSON_PRETTY> { span = span(); }
-    <LPAREN> e = JsonValueExpression(true) {
-        args[0] = e;
-    }
-    <RPAREN> {
-        return SqlStdOperatorTable.JSON_PRETTY.createCall(span.end(this), 
args);
-    }
-}
-
 List<SqlNode> JsonQueryEmptyOrErrorBehavior() :
 {
     final List<SqlNode> list = new ArrayList<SqlNode>();
@@ -5305,7 +5236,7 @@ List<SqlNode> JsonNameAndValue() :
             }
         }
     )
-    e = JsonValueExpression(false) {
+    e = Expression(ExprContext.ACCEPT_NON_QUERY) {
         list.add(e);
     }
     {
@@ -5361,55 +5292,6 @@ SqlCall JsonObjectFunctionCall() :
     }
 }
 
-SqlCall JsonTypeFunctionCall() :
-{
-    final SqlNode[] args = new SqlNode[1];
-    SqlNode e;
-    final Span span;
-}
-{
-    <JSON_TYPE> { span = span(); }
-    <LPAREN> e = JsonValueExpression(true) {
-        args[0] = e;
-    }
-    <RPAREN> {
-        return SqlStdOperatorTable.JSON_TYPE.createCall(span.end(this), args);
-    }
-}
-
-SqlCall JsonDepthFunctionCall() :
-{
-    final SqlNode[] args = new SqlNode[1];
-    SqlNode e;
-    final Span span;
-}
-{
-    <JSON_DEPTH> { span = span(); }
-    <LPAREN> e = JsonValueExpression(true) {
-        args[0] = e;
-    }
-    <RPAREN> {
-        return SqlStdOperatorTable.JSON_DEPTH.createCall(span.end(this), args);
-    }
-}
-
-SqlCall JsonLengthFunctionCall() :
-{
-    final SqlNode[] args = new SqlNode[1];
-    SqlNode e;
-    final Span span;
-    List<SqlNode> behavior;
-}
-{
-    <JSON_LENGTH> { span = span(); }
-    <LPAREN> e = JsonApiCommonSyntax(true) {
-       args[0] = e;
-    }
-    <RPAREN> {
-        return SqlStdOperatorTable.JSON_LENGTH.createCall(span.end(this), 
args);
-    }
-}
-
 SqlCall JsonObjectAggFunctionCall() :
 {
     final SqlNode[] args = new SqlNode[2];
@@ -5447,12 +5329,12 @@ SqlCall JsonArrayFunctionCall() :
     <JSON_ARRAY> { span = span(); }
     <LPAREN> [
         LOOKAHEAD(2)
-        e = JsonValueExpression(false) {
+        e = Expression(ExprContext.ACCEPT_NON_QUERY) {
             elements.add(e);
         }
         (
             <COMMA>
-            e = JsonValueExpression(false) {
+            e = Expression(ExprContext.ACCEPT_NON_QUERY) {
                 elements.add(e);
             }
         )*
@@ -5498,7 +5380,7 @@ SqlCall JsonArrayAggFunctionCall() :
 }
 {
     <JSON_ARRAYAGG> { span = span(); }
-    <LPAREN> e = JsonValueExpression(false) {
+    <LPAREN> e = Expression(ExprContext.ACCEPT_NON_QUERY) {
         valueExpr = e;
     }
     orderList = JsonArrayAggOrderByClause()
@@ -6232,6 +6114,13 @@ SqlPostfixOperator PostfixRowOperator() :
         |   <JSON> { return SqlStdOperatorTable.IS_JSON_VALUE; }
         )
     )
+|
+    <FORMAT>
+    (
+        JsonRepresentation() {
+            return SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
+        }
+    )
 }
 
 
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 157e10d..87afe4b 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -162,7 +162,6 @@ import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NULL;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_TRUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ITEM;
 import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_API_COMMON_SYNTAX;
-import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_API_COMMON_SYNTAX_WITHOUT_PATH;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAY;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAYAGG;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_DEPTH;
@@ -173,7 +172,6 @@ import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECT;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECTAGG;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_PRETTY;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_QUERY;
-import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_TYPE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE_ANY;
 import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
@@ -457,12 +455,8 @@ public class RexImpTable {
     // Json Operators
     defineMethod(JSON_VALUE_EXPRESSION,
         BuiltInMethod.JSON_VALUE_EXPRESSION.method, NullPolicy.STRICT);
-    defineMethod(JSON_STRUCTURED_VALUE_EXPRESSION,
-        BuiltInMethod.JSON_STRUCTURED_VALUE_EXPRESSION.method, 
NullPolicy.STRICT);
     defineMethod(JSON_API_COMMON_SYNTAX, 
BuiltInMethod.JSON_API_COMMON_SYNTAX.method,
             NullPolicy.NONE);
-    defineMethod(JSON_API_COMMON_SYNTAX_WITHOUT_PATH,
-        BuiltInMethod.JSON_API_COMMON_SYNTAX_WITHOUT_PATH.method, 
NullPolicy.NONE);
     defineMethod(JSON_EXISTS, BuiltInMethod.JSON_EXISTS.method, 
NullPolicy.NONE);
     defineMethod(JSON_VALUE_ANY, BuiltInMethod.JSON_VALUE_ANY.method, 
NullPolicy.NONE);
     defineMethod(JSON_QUERY, BuiltInMethod.JSON_QUERY.method, NullPolicy.NONE);
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java 
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 2298661..aa3d7d4 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -2456,10 +2456,6 @@ public class SqlFunctions {
     }
   }
 
-  public static Object jsonStructuredValueExpression(Object input) {
-    return input;
-  }
-
   public static PathContext jsonApiCommonSyntax(Object input) {
     return jsonApiCommonSyntax(input, "strict $");
   }
@@ -2807,14 +2803,17 @@ public class SqlFunctions {
   }
 
   public static Integer jsonLength(Object input) {
+    return jsonLength(jsonApiCommonSyntax(input));
+  }
+
+  public static Integer jsonLength(Object input, String pathSpec) {
+    return jsonLength(jsonApiCommonSyntax(input, pathSpec));
+  }
+
+  public static Integer jsonLength(PathContext context) {
     final Integer result;
     final Object value;
     try {
-      if (!isJsonPathContext(input)) {
-        throw RESOURCE.invalidInputForJsonLength(
-            input.toString()).ex();
-      }
-      PathContext context = (PathContext) input;
       if (context.exc != null) {
         throw toUnchecked(context.exc);
       }
@@ -2835,20 +2834,23 @@ public class SqlFunctions {
       }
     } catch (Exception ex) {
       throw RESOURCE.invalidInputForJsonLength(
-          input.toString()).ex();
+          context.toString()).ex();
     }
     return result;
   }
 
   public static String jsonKeys(Object input) {
+    return jsonKeys(jsonApiCommonSyntax(input));
+  }
+
+  public static String jsonKeys(Object input, String pathSpec) {
+    return jsonKeys(jsonApiCommonSyntax(input, pathSpec));
+  }
+
+  public static String jsonKeys(PathContext context) {
     List<String> list = new ArrayList<>();
     final Object value;
     try {
-      if (!isJsonPathContext(input)) {
-        throw RESOURCE.invalidInputForJsonLength(
-            input.toString()).ex();
-      }
-      PathContext context = (PathContext) input;
       if (context.exc != null) {
         throw toUnchecked(context.exc);
       }
@@ -2864,7 +2866,7 @@ public class SqlFunctions {
       }
     } catch (Exception ex) {
       throw RESOURCE.invalidInputForJsonKeys(
-              input.toString()).ex();
+              context.toString()).ex();
     }
     return jsonize(list);
   }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
index 6557935..7fdfe43 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
@@ -22,10 +22,8 @@ import org.apache.calcite.sql.SqlSpecialOperator;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlOperandTypeChecker;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.sql.validate.SqlValidator;
 
 /**
  * The JSON API common syntax including a path specification, which is for
@@ -33,38 +31,20 @@ import org.apache.calcite.sql.validate.SqlValidator;
  */
 public class SqlJsonApiCommonSyntaxOperator extends SqlSpecialOperator {
 
-  // If true, the syntax must contain a JSON path expression, e.g. 
'{'foo':'bar'}', 'lax $.foo';
-  // otherwise the syntax can be specified within JSON text only, e.g. 
'{'foo':'bar'}'.
-  private final boolean hasPath;
-
-  public SqlJsonApiCommonSyntaxOperator(String name, boolean hasPath) {
+  public SqlJsonApiCommonSyntaxOperator(String name) {
     super(name, SqlKind.JSON_API_COMMON_SYNTAX, 100, true,
         ReturnTypes.explicit(SqlTypeName.ANY), null,
-        hasPath ? OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)
-            : OperandTypes.family(SqlTypeFamily.ANY));
-    this.hasPath = hasPath;
-  }
-
-  @Override protected void checkOperandCount(SqlValidator validator,
-      SqlOperandTypeChecker argType, SqlCall call) {
-    if (hasPath) {
-      if (call.operandCount() < 2) {
-        throw new UnsupportedOperationException(
-            "JSON API common syntax requires at least 2 parameters");
-      }
-    } else {
-      if (call.operandCount() < 1) {
-        throw new UnsupportedOperationException(
-            "JSON API common syntax requires at least 1 parameter");
-      }
-    }
+        OperandTypes.or(OperandTypes.family(SqlTypeFamily.ANY, 
SqlTypeFamily.STRING),
+            OperandTypes.family(SqlTypeFamily.ANY)));
   }
 
   @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
       int rightPrec) {
+    final boolean hasPath = call.operandCount() % 2 == 0;
     SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.SIMPLE);
     call.operand(0).unparse(writer, 0, 0);
     if (hasPath) {
+      // the case '{'foo':'bar'}', 'lax $.foo'
       writer.sep(",", true);
       call.operand(1).unparse(writer, 0, 0);
     }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
index 6fcbc3f..94d8670 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
@@ -26,6 +26,7 @@ import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.InferTypes;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeFamily;
@@ -44,9 +45,9 @@ public class SqlJsonArrayAggAggFunction extends 
SqlAggFunction {
 
   public SqlJsonArrayAggAggFunction(SqlKind kind,
       SqlJsonConstructorNullClause nullClause) {
-    super(kind + "_" + nullClause.name(), null, kind, 
ReturnTypes.VARCHAR_2000, null,
-        OperandTypes.family(SqlTypeFamily.ANY), SqlFunctionCategory.SYSTEM,
-        false, false, Optionality.OPTIONAL);
+    super(kind + "_" + nullClause.name(), null, kind, ReturnTypes.VARCHAR_2000,
+        InferTypes.ANY_NULLABLE, OperandTypes.family(SqlTypeFamily.ANY),
+        SqlFunctionCategory.SYSTEM, false, false, Optionality.OPTIONAL);
     this.nullClause = Objects.requireNonNull(nullClause);
   }
 
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
index 29ae0f8..f13b01b 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
@@ -26,6 +26,7 @@ import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlOperandCountRange;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.InferTypes;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlOperandCountRanges;
@@ -39,8 +40,8 @@ import java.util.Locale;
  */
 public class SqlJsonArrayFunction extends SqlFunction {
   public SqlJsonArrayFunction() {
-    super("JSON_ARRAY", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000, null,
-        OperandTypes.VARIADIC, SqlFunctionCategory.SYSTEM);
+    super("JSON_ARRAY", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000,
+        InferTypes.ANY_NULLABLE, OperandTypes.VARIADIC, 
SqlFunctionCategory.SYSTEM);
   }
 
   @Override public SqlOperandCountRange getOperandCountRange() {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java
index 6137096..81cc322 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java
@@ -28,6 +28,7 @@ import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlOperandCountRanges;
 import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.type.SqlTypeTransforms;
 import org.apache.calcite.sql.validate.SqlValidator;
 
 /**
@@ -37,7 +38,8 @@ public class SqlJsonDepthFunction extends SqlFunction {
   public SqlJsonDepthFunction() {
     super("JSON_DEPTH",
         SqlKind.OTHER_FUNCTION,
-        ReturnTypes.INTEGER_NULLABLE,
+        ReturnTypes.cascade(ReturnTypes.INTEGER,
+            SqlTypeTransforms.FORCE_NULLABLE),
         null,
         OperandTypes.ANY,
         SqlFunctionCategory.SYSTEM);
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java
index 8cd6444..17c4507 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java
@@ -16,17 +16,12 @@
  */
 package org.apache.calcite.sql.fun;
 
-import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlFunction;
 import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlLiteral;
-import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlOperandTypeChecker;
-import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.type.SqlTypeFamily;
 
 /**
  * The <code>JSON_KEYS</code> function.
@@ -36,19 +31,10 @@ public class SqlJsonKeysFunction extends SqlFunction {
     super("JSON_KEYS", SqlKind.OTHER_FUNCTION,
           ReturnTypes.VARCHAR_2000,
           null,
-          OperandTypes.ANY,
+          OperandTypes.or(OperandTypes.ANY,
+              OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER)),
           SqlFunctionCategory.SYSTEM);
   }
-
-  @Override protected void checkOperandCount(SqlValidator validator,
-      SqlOperandTypeChecker argType, SqlCall call) {
-    assert call.operandCount() == 1;
-  }
-
-  @Override public SqlCall createCall(SqlLiteral functionQualifier,
-                                        SqlParserPos pos, SqlNode... operands) 
{
-    return super.createCall(functionQualifier, pos, operands);
-  }
 }
 
 // End SqlJsonKeysFunction.java
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java
index 4b9308a..ee911d1 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java
@@ -16,16 +16,12 @@
  */
 package org.apache.calcite.sql.fun;
 
-import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlFunction;
 import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlLiteral;
-import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.SqlWriter;
-import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeTransforms;
 
 /**
@@ -37,21 +33,10 @@ public class SqlJsonLengthFunction extends SqlFunction {
         ReturnTypes.cascade(ReturnTypes.INTEGER,
             SqlTypeTransforms.FORCE_NULLABLE),
         null,
-        OperandTypes.ANY,
+        OperandTypes.or(OperandTypes.ANY,
+            OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER)),
         SqlFunctionCategory.SYSTEM);
   }
-
-  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
-      int rightPrec) {
-    final SqlWriter.Frame frame = writer.startFunCall(getName());
-    call.operand(0).unparse(writer, 0, 0);
-    writer.endFunCall(frame);
-  }
-
-  @Override public SqlCall createCall(SqlLiteral functionQualifier,
-      SqlParserPos pos, SqlNode... operands) {
-    return super.createCall(functionQualifier, pos, operands);
-  }
 }
 
 // End SqlJsonLengthFunction.java
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
index 3955e0d..d1e8d63 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql.fun;
 
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlFunctionCategory;
@@ -27,6 +28,7 @@ import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorImpl;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
@@ -43,8 +45,14 @@ public class SqlJsonObjectAggAggFunction extends 
SqlAggFunction {
   /** Creates a SqlJsonObjectAggAggFunction. */
   public SqlJsonObjectAggAggFunction(SqlKind kind,
       SqlJsonConstructorNullClause nullClause) {
-    super(kind + "_" + nullClause.name(), null, kind, 
ReturnTypes.VARCHAR_2000, null,
-        OperandTypes.family(SqlTypeFamily.CHARACTER, SqlTypeFamily.ANY),
+    super(kind + "_" + nullClause.name(), null, kind, ReturnTypes.VARCHAR_2000,
+        (callBinding, returnType, operandTypes) -> {
+          RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
+          operandTypes[0] = typeFactory.createSqlType(SqlTypeName.VARCHAR);
+          operandTypes[1] = typeFactory.createTypeWithNullability(
+              typeFactory.createSqlType(SqlTypeName.ANY), true);
+        }, OperandTypes.family(SqlTypeFamily.CHARACTER,
+            SqlTypeFamily.ANY),
         SqlFunctionCategory.SYSTEM, false, false, Optionality.FORBIDDEN);
     this.nullClause = Objects.requireNonNull(nullClause);
   }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java
index 0240532..f74d577 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql.fun;
 
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlCallBinding;
 import org.apache.calcite.sql.SqlFunction;
@@ -31,6 +32,7 @@ import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlOperandCountRanges;
 import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.sql.validate.SqlValidator;
 
@@ -43,8 +45,18 @@ import static org.apache.calcite.util.Static.RESOURCE;
  */
 public class SqlJsonObjectFunction extends SqlFunction {
   public SqlJsonObjectFunction() {
-    super("JSON_OBJECT", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000, 
null,
-        null, SqlFunctionCategory.SYSTEM);
+    super("JSON_OBJECT", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000,
+        (callBinding, returnType, operandTypes) -> {
+          RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
+          for (int i = 0; i < operandTypes.length; i++) {
+            if (i % 2 == 0) {
+              operandTypes[i] = typeFactory.createSqlType(SqlTypeName.VARCHAR);
+              continue;
+            }
+            operandTypes[i] = typeFactory.createTypeWithNullability(
+                typeFactory.createSqlType(SqlTypeName.ANY), true);
+          }
+        }, null, SqlFunctionCategory.SYSTEM);
   }
 
   @Override public SqlOperandCountRange getOperandCountRange() {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
index fbad8e1..6ea1f8f 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
@@ -16,46 +16,23 @@
  */
 package org.apache.calcite.sql.fun;
 
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlSpecialOperator;
-import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.SqlPostfixOperator;
 import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeTransforms;
 
 /**
  * The JSON value expression operator that indicates that the value expression
  * should be parsed as JSON.
  */
-public class SqlJsonValueExpressionOperator extends SqlSpecialOperator {
-  private final boolean structured;
+public class SqlJsonValueExpressionOperator extends SqlPostfixOperator {
 
-  public SqlJsonValueExpressionOperator(String name, boolean structured) {
-    super(name, SqlKind.JSON_VALUE_EXPRESSION, 100, true,
-        opBinding -> {
-          final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
-          return typeFactory.createTypeWithNullability(
-              typeFactory.createSqlType(SqlTypeName.ANY), true);
-        },
-        (callBinding, returnType, operandTypes) -> {
-          if (callBinding.isOperandNull(0, false)) {
-            final RelDataTypeFactory typeFactory =
-                callBinding.getTypeFactory();
-            operandTypes[0] = typeFactory.createTypeWithNullability(
-                typeFactory.createSqlType(SqlTypeName.ANY), true);
-          }
-        },
-        structured ? OperandTypes.ANY : OperandTypes.STRING);
-    this.structured = structured;
-  }
-
-  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
-      int rightPrec) {
-    call.operand(0).unparse(writer, 0, 0);
-    if (!structured) {
-      writer.keyword("FORMAT JSON");
-    }
+  public SqlJsonValueExpressionOperator() {
+    super("FORMAT JSON", SqlKind.JSON_VALUE_EXPRESSION, 28,
+        ReturnTypes.cascade(ReturnTypes.explicit(SqlTypeName.ANY),
+            SqlTypeTransforms.TO_NULLABLE), null, OperandTypes.CHARACTER);
   }
 }
 
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 8654f7d..81f08c8 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -807,6 +807,9 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
           null,
           OperandTypes.CHARACTER);
 
+  public static final SqlPostfixOperator JSON_VALUE_EXPRESSION =
+      new SqlJsonValueExpressionOperator();
+
 
   //-------------------------------------------------------------
   //                   PREFIX OPERATORS
@@ -1291,18 +1294,8 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
 
   public static final SqlThrowOperator THROW = new SqlThrowOperator();
 
-  public static final SqlJsonValueExpressionOperator JSON_VALUE_EXPRESSION =
-      new SqlJsonValueExpressionOperator("JSON_VALUE_EXPRESSION", false);
-
-  public static final SqlJsonValueExpressionOperator 
JSON_STRUCTURED_VALUE_EXPRESSION =
-      new SqlJsonValueExpressionOperator("JSON_STRUCTURED_VALUE_EXPRESSION",
-          true);
-
   public static final SqlJsonApiCommonSyntaxOperator JSON_API_COMMON_SYNTAX =
-      new SqlJsonApiCommonSyntaxOperator("JSON_API_COMMON_SYNTAX", true);
-
-  public static final SqlJsonApiCommonSyntaxOperator 
JSON_API_COMMON_SYNTAX_WITHOUT_PATH =
-      new 
SqlJsonApiCommonSyntaxOperator("JSON_API_COMMON_SYNTAX_WITHOUT_PATH", false);
+      new SqlJsonApiCommonSyntaxOperator("JSON_API_COMMON_SYNTAX");
 
   public static final SqlFunction JSON_EXISTS = new SqlJsonExistsFunction();
 
diff --git a/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java 
b/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java
index 10dd95c..9e78a08 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java
@@ -104,6 +104,20 @@ public abstract class InferTypes {
         }
       };
 
+  /**
+   * Operand type-inference strategy where an unknown operand type is assumed
+   * to be nullable ANY.
+   */
+  public static final SqlOperandTypeInference ANY_NULLABLE =
+      (callBinding, returnType, operandTypes) -> {
+        RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
+        for (int i = 0; i < operandTypes.length; ++i) {
+          operandTypes[i] =
+              typeFactory.createTypeWithNullability(
+                  typeFactory.createSqlType(SqlTypeName.ANY), true);
+        }
+      };
+
   /** Returns an {@link SqlOperandTypeInference} that returns a given list of
    * types. */
   public static SqlOperandTypeInference explicit(List<RelDataType> types) {
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java 
b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
index 6f783f9..aa0db73 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
@@ -45,6 +45,7 @@ import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlNodeList;
 import org.apache.calcite.sql.SqlNumericLiteral;
 import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlPostfixOperator;
 import org.apache.calcite.sql.SqlUtil;
 import org.apache.calcite.sql.fun.OracleSqlOperatorTable;
 import org.apache.calcite.sql.fun.SqlArrayValueConstructor;
@@ -77,9 +78,12 @@ import com.google.common.collect.Lists;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
+import static org.apache.calcite.util.Static.RESOURCE;
+
 /**
  * Standard implementation of {@link SqlRexConvertletTable}.
  */
@@ -116,8 +120,8 @@ public class StandardConvertletTable extends 
ReflectiveConvertletTable {
     registerOp(SqlStdOperatorTable.MINUS,
         (cx, call) -> {
           final RexCall e =
-              (RexCall) StandardConvertletTable.this.convertCall(cx, call,
-                  call.getOperator());
+              (RexCall) StandardConvertletTable.this.convertCall(cx, 
call.getOperator(),
+                  call.getOperandList());
           switch (e.getOperands().get(0).getType().getSqlTypeName()) {
           case DATE:
           case TIME:
@@ -209,6 +213,19 @@ public class StandardConvertletTable extends 
ReflectiveConvertletTable {
                 call.operand(0),
                 SqlLiteral.createExactNumeric("0.5", SqlParserPos.ZERO))));
 
+    registerOp(SqlStdOperatorTable.JSON_API_COMMON_SYNTAX,
+        new JsonOperatorValueExprConvertlet(0));
+    registerOp(SqlStdOperatorTable.JSON_PRETTY,
+        new JsonOperatorValueExprConvertlet(0));
+    registerOp(SqlStdOperatorTable.JSON_TYPE,
+        new JsonOperatorValueExprConvertlet(0));
+    registerOp(SqlStdOperatorTable.JSON_DEPTH,
+        new JsonOperatorValueExprConvertlet(0));
+    registerOp(SqlStdOperatorTable.JSON_LENGTH,
+        new JsonOperatorValueExprConvertlet(0));
+    registerOp(SqlStdOperatorTable.JSON_KEYS,
+        new JsonOperatorValueExprConvertlet(0));
+
     // Convert json_value('{"foo":"bar"}', 'lax $.foo', returning 
varchar(2000))
     // to cast(json_value('{"foo":"bar"}', 'lax $.foo') as varchar(2000))
     registerOp(
@@ -747,16 +764,13 @@ public class StandardConvertletTable extends 
ReflectiveConvertletTable {
   public RexNode convertCall(
       SqlRexContext cx,
       SqlCall call) {
-    return convertCall(cx, call, call.getOperator());
+    return convertCall(cx, call.getOperator(), call.getOperandList());
   }
 
   /** Converts a {@link SqlCall} to a {@link RexCall} with a perhaps different
    * operator. */
   private RexNode convertCall(
-      SqlRexContext cx,
-      SqlCall call,
-      SqlOperator op) {
-    final List<SqlNode> operands = call.getOperandList();
+      SqlRexContext cx, SqlOperator op, List<SqlNode> operands) {
     final RexBuilder rexBuilder = cx.getRexBuilder();
     final SqlOperandTypeChecker.Consistency consistency =
         op.getOperandTypeChecker() == null
@@ -1432,6 +1446,58 @@ public class StandardConvertletTable extends 
ReflectiveConvertletTable {
     }
   }
 
+  /** Convertlet that adds implicit calls to
+   * {@link org.apache.calcite.sql.fun.SqlJsonValueExpressionOperator}
+   * for a series fo JSON operators.
+   *
+   * <p> A call to JSON operator sometimes has a specific expression
+   * argument (it's always the first argument of the call), which implies
+   * a "FORMAT JSON" postfix when the argument is not a explicit call to the
+   * {@link org.apache.calcite.sql.fun.SqlJsonValueExpressionOperator}. */
+  private class JsonOperatorValueExprConvertlet implements SqlRexConvertlet {
+    private final int[] implicitJsonValueExprOrdinals;
+
+    private JsonOperatorValueExprConvertlet(int[] 
implicitJsonValueExprOrdinals) {
+      this.implicitJsonValueExprOrdinals = implicitJsonValueExprOrdinals;
+    }
+
+    private JsonOperatorValueExprConvertlet(int implicitJsonValueExprOrdinal) {
+      this(new int[]{implicitJsonValueExprOrdinal});
+    }
+
+    public RexNode convertCall(SqlRexContext cx, SqlCall call) {
+      List<SqlNode> newOperands = new ArrayList<>(call.getOperandList());
+      for (int ordinal : implicitJsonValueExprOrdinals) {
+        assert ordinal < newOperands.size();
+        final SqlNode operand = call.operand(ordinal);
+        if (operand.getKind() != SqlKind.JSON_VALUE_EXPRESSION) {
+          final SqlPostfixOperator newOp = 
SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
+
+          // Validate the original operand using the new operator. JSON value 
expression
+          // accepts only a character input.
+          //
+          // Once CALCITE-2869 is fixed, we can validate the original operand 
during SQL
+          // validation using SqlTypeName.JSON.
+          RelDataType validateOperandType = 
cx.getValidator().getValidatedNodeType(operand);
+          assert validateOperandType != null;
+          if (!SqlTypeUtil.inCharFamily(validateOperandType)) {
+            // must be character type
+            throw cx.getValidator().newValidationError(call,
+                RESOURCE.canNotApplyOp2Type(newOp.getName(),
+                    SqlUtil.getOperatorSignature(newOp,
+                        Collections.singletonList(validateOperandType)),
+                    newOp.getAllowedSignatures()));
+          }
+
+          final SqlNode replacement = newOp
+              .createCall(SqlParserPos.ZERO, operand);
+          newOperands.set(ordinal, replacement);
+        }
+      }
+      return StandardConvertletTable.this.convertCall(cx, call.getOperator(), 
newOperands);
+    }
+  }
+
   /** Convertlet that handles the {@code TIMESTAMPADD} function. */
   private static class TimestampAddConvertlet implements SqlRexConvertlet {
     public RexNode convertCall(SqlRexContext cx, SqlCall call) {
diff --git a/core/src/main/java/org/apache/calcite/util/Bug.java 
b/core/src/main/java/org/apache/calcite/util/Bug.java
index 2c16bd7..6e167a0 100644
--- a/core/src/main/java/org/apache/calcite/util/Bug.java
+++ b/core/src/main/java/org/apache/calcite/util/Bug.java
@@ -175,6 +175,11 @@ public abstract class Bug {
   public static final boolean CALCITE_2776_FIXED = false;
 
   /** Whether
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-2869";>[CALCITE-2869]
+   * JSON data type support</a> is fixed. */
+  public static final boolean CALCITE_2869_FIXED = false;
+
+  /** Whether
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-2933";>[CALCITE-2933]
    * In Druid adapter, expression like "cast(cast(\"timestamp\" as timestamp) 
as varchar)"
    * returns as epoch millisecond</a> is fixed. */
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 9d2de30..f06e2f5 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -271,12 +271,8 @@ public enum BuiltInMethod {
   DEJSONIZE(SqlFunctions.class, "dejsonize", String.class),
   JSON_VALUE_EXPRESSION(SqlFunctions.class, "jsonValueExpression",
       String.class),
-  JSON_STRUCTURED_VALUE_EXPRESSION(SqlFunctions.class,
-      "jsonStructuredValueExpression", Object.class),
   JSON_API_COMMON_SYNTAX(SqlFunctions.class, "jsonApiCommonSyntax",
       Object.class, String.class),
-  JSON_API_COMMON_SYNTAX_WITHOUT_PATH(SqlFunctions.class, 
"jsonApiCommonSyntax",
-      Object.class),
   JSON_EXISTS(SqlFunctions.class, "jsonExists", Object.class),
   JSON_VALUE_ANY(SqlFunctions.class, "jsonValueAny", Object.class,
       SqlJsonValueEmptyOrErrorBehavior.class, Object.class,
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index b82b52c..5ae565e 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -3147,6 +3147,19 @@ public class RelToSqlConverterTest {
     sql(query).ok(expected);
   }
 
+  @Test public void testJsonValueExpressionOperator() {
+    String query = "select \"product_name\" format json, "
+        + "\"product_name\" format json encoding utf8, "
+        + "\"product_name\" format json encoding utf16, "
+        + "\"product_name\" format json encoding utf32 from \"product\"";
+    final String expected = "SELECT \"product_name\" FORMAT JSON, "
+        + "\"product_name\" FORMAT JSON, "
+        + "\"product_name\" FORMAT JSON, "
+        + "\"product_name\" FORMAT JSON\n"
+        + "FROM \"foodmart\".\"product\"";
+    sql(query).ok(expected);
+  }
+
   @Test public void testJsonExists() {
     String query = "select json_exists(\"product_name\", 'lax $') from 
\"product\"";
     final String expected = "SELECT JSON_EXISTS(\"product_name\" FORMAT JSON, 
'lax $')\n"
diff --git 
a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java 
b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index bfb0291..bee50f9 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -721,8 +721,8 @@ public class SqlParserTest {
         "(?s).*Encountered \".1\" at line 1, column 13.\n"
             + "Was expecting one of:\n"
             + "    <EOF> \n"
-            + "    \"AND\" \\.\\.\\.\n"
             + "    \"AS\" \\.\\.\\.\n"
+            + "    \"EXCEPT\" \\.\\.\\.\n"
             + ".*");
   }
 
@@ -6691,8 +6691,8 @@ public class SqlParserTest {
         "(?s)Encountered \"to year\" at line 1, column 19.\n"
             + "Was expecting one of:\n"
             + "    <EOF> \n"
-            + "    \"AND\" \\.\\.\\.\n"
-            + "    \"BETWEEN\" \\.\\.\\..*");
+            + "    \"\\(\" \\.\\.\\.\n"
+            + "    \"\\.\" \\.\\.\\..*");
     checkExpFails("interval '1-2' year ^to^ day", ANY);
     checkExpFails("interval '1-2' year ^to^ hour", ANY);
     checkExpFails("interval '1-2' year ^to^ minute", ANY);
@@ -8357,64 +8357,83 @@ public class SqlParserTest {
     sql(sql).ok(expected);
   }
 
+  @Test public void testJsonValueExpressionOperator() {
+    checkExp("foo format json",
+        "`FOO` FORMAT JSON");
+    // Currently, encoding js not valid
+    checkExp("foo format json encoding utf8",
+        "`FOO` FORMAT JSON");
+    checkExp("foo format json encoding utf16",
+        "`FOO` FORMAT JSON");
+    checkExp("foo format json encoding utf32",
+        "`FOO` FORMAT JSON");
+    checkExp("null format json", "NULL FORMAT JSON");
+    // Test case to eliminate choice conflict on token <FORMAT>
+    check("select foo format from tab", "SELECT `FOO` AS `FORMAT`\n"
+        + "FROM `TAB`");
+    // Test case to eliminate choice conflict on token <ENCODING>
+    check("select foo format json encoding from tab", "SELECT `FOO` FORMAT 
JSON AS `ENCODING`\n"
+        + "FROM `TAB`");
+  }
+
   @Test public void testJsonExists() {
     checkExp("json_exists('{\"foo\": \"bar\"}', 'lax $.foo')",
-        "JSON_EXISTS('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $.foo')");
+        "JSON_EXISTS('{\"foo\": \"bar\"}', 'lax $.foo')");
     checkExp("json_exists('{\"foo\": \"bar\"}', 'lax $.foo' error on error)",
-        "JSON_EXISTS('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $.foo' ERROR ON 
ERROR)");
+        "JSON_EXISTS('{\"foo\": \"bar\"}', 'lax $.foo' ERROR ON ERROR)");
   }
 
   @Test public void testJsonValue() {
     checkExp("json_value('{\"foo\": \"100\"}', 'lax $.foo' "
             + "returning integer)",
-        "JSON_VALUE('{\"foo\": \"100\"}' FORMAT JSON, 'lax $.foo' "
+        "JSON_VALUE('{\"foo\": \"100\"}', 'lax $.foo' "
             + "RETURNING INTEGER NULL ON EMPTY NULL ON ERROR)");
     checkExp("json_value('{\"foo\": \"100\"}', 'lax $.foo' "
             + "returning integer default 10 on empty error on error)",
-        "JSON_VALUE('{\"foo\": \"100\"}' FORMAT JSON, 'lax $.foo' "
+        "JSON_VALUE('{\"foo\": \"100\"}', 'lax $.foo' "
             + "RETURNING INTEGER DEFAULT 10 ON EMPTY ERROR ON ERROR)");
   }
 
   @Test public void testJsonQuery() {
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITHOUT ARRAY WRAPPER)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY NULL ON ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITH WRAPPER)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITH UNCONDITIONAL ARRAY WRAPPER NULL ON EMPTY NULL ON 
ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITH UNCONDITIONAL 
WRAPPER)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITH UNCONDITIONAL ARRAY WRAPPER NULL ON EMPTY NULL ON 
ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITH CONDITIONAL 
WRAPPER)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITH CONDITIONAL ARRAY WRAPPER NULL ON EMPTY NULL ON 
ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' NULL ON EMPTY)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY NULL ON ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' ERROR ON EMPTY)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER ERROR ON EMPTY NULL ON ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY ARRAY ON EMPTY)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER EMPTY ARRAY ON EMPTY NULL ON 
ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY OBJECT ON EMPTY)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER EMPTY OBJECT ON EMPTY NULL ON 
ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' NULL ON ERROR)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY NULL ON ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' ERROR ON ERROR)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY ERROR ON ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY ARRAY ON ERROR)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY EMPTY ARRAY ON 
ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY OBJECT ON ERROR)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY EMPTY OBJECT ON 
ERROR)");
     checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY ARRAY ON EMPTY "
             + "EMPTY OBJECT ON ERROR)",
-        "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+        "JSON_QUERY('{\"foo\": \"bar\"}', "
             + "'lax $' WITHOUT ARRAY WRAPPER EMPTY ARRAY ON EMPTY EMPTY OBJECT 
ON ERROR)");
   }
 
@@ -8438,43 +8457,43 @@ public class SqlParserTest {
   }
 
   @Test public void testJsonType() {
-    checkExp("json_type('11.56')", "JSON_TYPE('11.56' FORMAT JSON)");
-    checkExp("json_type('{}')", "JSON_TYPE('{}' FORMAT JSON)");
-    checkExp("json_type(null)", "JSON_TYPE(NULL FORMAT JSON)");
+    checkExp("json_type('11.56')", "JSON_TYPE('11.56')");
+    checkExp("json_type('{}')", "JSON_TYPE('{}')");
+    checkExp("json_type(null)", "JSON_TYPE(NULL)");
     checkExp("json_type('[\"foo\",null]')",
-            "JSON_TYPE('[\"foo\",null]' FORMAT JSON)");
+            "JSON_TYPE('[\"foo\",null]')");
     checkExp("json_type('{\"foo\": \"100\"}')",
-            "JSON_TYPE('{\"foo\": \"100\"}' FORMAT JSON)");
+            "JSON_TYPE('{\"foo\": \"100\"}')");
   }
 
   @Test public void testJsonDepth() {
-    checkExp("json_depth('11.56')", "JSON_DEPTH('11.56' FORMAT JSON)");
-    checkExp("json_depth('{}')", "JSON_DEPTH('{}' FORMAT JSON)");
-    checkExp("json_depth(null)", "JSON_DEPTH(NULL FORMAT JSON)");
+    checkExp("json_depth('11.56')", "JSON_DEPTH('11.56')");
+    checkExp("json_depth('{}')", "JSON_DEPTH('{}')");
+    checkExp("json_depth(null)", "JSON_DEPTH(NULL)");
     checkExp("json_depth('[\"foo\",null]')",
-            "JSON_DEPTH('[\"foo\",null]' FORMAT JSON)");
+            "JSON_DEPTH('[\"foo\",null]')");
     checkExp("json_depth('{\"foo\": \"100\"}')",
-            "JSON_DEPTH('{\"foo\": \"100\"}' FORMAT JSON)");
+            "JSON_DEPTH('{\"foo\": \"100\"}')");
   }
 
   @Test public void testJsonLength() {
     checkExp("json_length('{\"foo\": \"bar\"}')",
-            "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON)");
+            "JSON_LENGTH('{\"foo\": \"bar\"}')");
     checkExp("json_length('{\"foo\": \"bar\"}', 'lax $')",
-            "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $')");
+            "JSON_LENGTH('{\"foo\": \"bar\"}', 'lax $')");
     checkExp("json_length('{\"foo\": \"bar\"}', 'strict $')",
-            "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON, 'strict $')");
+            "JSON_LENGTH('{\"foo\": \"bar\"}', 'strict $')");
     checkExp("json_length('{\"foo\": \"bar\"}', 'invalid $')",
-            "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON, 'invalid $')");
+            "JSON_LENGTH('{\"foo\": \"bar\"}', 'invalid $')");
   }
 
   @Test public void testJsonKeys() {
     checkExp("json_keys('{\"foo\": \"bar\"}', 'lax $')",
-            "JSON_KEYS('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $')");
+            "JSON_KEYS('{\"foo\": \"bar\"}', 'lax $')");
     checkExp("json_keys('{\"foo\": \"bar\"}', 'strict $')",
-            "JSON_KEYS('{\"foo\": \"bar\"}' FORMAT JSON, 'strict $')");
+            "JSON_KEYS('{\"foo\": \"bar\"}', 'strict $')");
     checkExp("json_keys('{\"foo\": \"bar\"}', 'invalid $')",
-            "JSON_KEYS('{\"foo\": \"bar\"}' FORMAT JSON, 'invalid $')");
+            "JSON_KEYS('{\"foo\": \"bar\"}', 'invalid $')");
   }
 
   @Test public void testJsonObjectAgg() {
@@ -8507,9 +8526,9 @@ public class SqlParserTest {
 
   @Test public void testJsonPretty() {
     checkExp("json_pretty('foo')",
-            "JSON_PRETTY('foo' FORMAT JSON)");
+            "JSON_PRETTY('foo')");
     checkExp("json_pretty(null)",
-            "JSON_PRETTY(NULL FORMAT JSON)");
+            "JSON_PRETTY(NULL)");
   }
 
   @Test public void testJsonArrayAgg1() {
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java 
b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
index aa3d706..a31e5b6 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
@@ -292,6 +292,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
           "KEYWORD(BETWEEN)",
           "KEYWORD(CONTAINS)",
           "KEYWORD(EQUALS)",
+          "KEYWORD(FORMAT)",
           "KEYWORD(IMMEDIATELY)",
           "KEYWORD(IN)",
           "KEYWORD(IS)",
diff --git 
a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java 
b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 4c77dde..c7847f6 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -4462,26 +4462,6 @@ public abstract class SqlOperatorBaseTest {
 
   }
 
-  @Test public void testJsonObject() {
-    tester.checkString("json_object()", "{}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': 'bar')",
-        "{\"foo\":\"bar\"}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': 'bar', 'foo2': 'bar2')",
-        "{\"foo\":\"bar\",\"foo2\":\"bar2\"}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': null)",
-        "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': null null on null)",
-        "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': null absent on null)",
-        "{}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': 100)",
-        "{\"foo\":100}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': json_object('foo': 'bar'))",
-        "{\"foo\":\"{\\\"foo\\\":\\\"bar\\\"}\"}", "VARCHAR(2000) NOT NULL");
-    tester.checkString("json_object('foo': json_object('foo': 'bar') format 
json)",
-        "{\"foo\":{\"foo\":\"bar\"}}", "VARCHAR(2000) NOT NULL");
-  }
-
   @Test public void testJsonPretty() {
     tester.checkString("json_pretty('{\"foo\":100}')",
         "{\n  \"foo\" : 100\n}", "VARCHAR(2000) NOT NULL");
@@ -4643,8 +4623,29 @@ public abstract class SqlOperatorBaseTest {
             "(?s).*No results for path.*", true);
   }
 
+  @Test public void testJsonObject() {
+    tester.checkString("json_object()", "{}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': 'bar')",
+        "{\"foo\":\"bar\"}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': 'bar', 'foo2': 'bar2')",
+        "{\"foo\":\"bar\",\"foo2\":\"bar2\"}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': null)",
+        "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': null null on null)",
+        "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': null absent on null)",
+        "{}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': 100)",
+        "{\"foo\":100}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': json_object('foo': 'bar'))",
+        "{\"foo\":\"{\\\"foo\\\":\\\"bar\\\"}\"}", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_object('foo': json_object('foo': 'bar') format 
json)",
+        "{\"foo\":{\"foo\":\"bar\"}}", "VARCHAR(2000) NOT NULL");
+  }
+
   @Test public void testJsonObjectAgg() {
     checkAggType(tester, "json_objectagg('foo': 'bar')", "VARCHAR(2000) NOT 
NULL");
+    checkAggType(tester, "json_objectagg('foo': null)", "VARCHAR(2000) NOT 
NULL");
     tester.checkFails("^json_objectagg(100: 'bar')^",
         "(?s).*Cannot apply.*", false);
     final String[][] values = {
@@ -4688,6 +4689,7 @@ public abstract class SqlOperatorBaseTest {
 
   @Test public void testJsonArrayAgg() {
     checkAggType(tester, "json_arrayagg('foo')", "VARCHAR(2000) NOT NULL");
+    checkAggType(tester, "json_arrayagg(null)", "VARCHAR(2000) NOT NULL");
     final String[] values = {
         "'foo'",
         "cast(null as varchar(2000))",
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
index 5d9ca43..bc4fbd0 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
@@ -62,12 +62,6 @@ public class SqlJsonFunctionsTest {
   }
 
   @Test
-  public void testJsonStructuredValueExpression() {
-    assertJsonStructuredValueExpression("bar", is("bar"));
-    assertJsonStructuredValueExpression(100, is(100));
-  }
-
-  @Test
   public void testJsonApiCommonSyntax() {
     assertJsonApiCommonSyntax(ImmutableMap.of("foo", "bar"), "lax $.foo",
         contextMatches(
@@ -604,13 +598,6 @@ public class SqlJsonFunctionsTest {
         SqlFunctions.jsonValueExpression(input), matcher);
   }
 
-  private void assertJsonStructuredValueExpression(Object input,
-      Matcher<Object> matcher) {
-    assertThat(
-        
invocationDesc(BuiltInMethod.JSON_STRUCTURED_VALUE_EXPRESSION.getMethodName(), 
input),
-        SqlFunctions.jsonStructuredValueExpression(input), matcher);
-  }
-
   private void assertJsonApiCommonSyntax(Object input, String pathSpec,
       Matcher<? super SqlFunctions.PathContext> matcher) {
     assertThat(
@@ -706,7 +693,7 @@ public class SqlJsonFunctionsTest {
         matcher);
   }
 
-  private void assertJsonLength(Object input,
+  private void assertJsonLength(SqlFunctions.PathContext input,
       Matcher<? super Integer> matcher) {
     assertThat(
         invocationDesc(BuiltInMethod.JSON_LENGTH.getMethodName(), input),
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index e1a5ef1..dbe51d2 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -3037,6 +3037,16 @@ public class SqlToRelConverterTest extends 
SqlToRelTestBase {
     sql(sql).with(tester).ok();
   }
 
+  @Test public void testJsonValueExpressionOperator() {
+    final String sql = "select ename format json,\n"
+        + "ename format json encoding utf8,\n"
+        + "ename format json encoding utf16,\n"
+        + "ename format json encoding utf32\n"
+        + "from emp";
+    System.out.println(sql);
+    sql(sql).ok();
+  }
+
   @Test public void testJsonExists() {
     final String sql = "select json_exists(ename, 'lax $')\n"
         + "from emp";
@@ -3055,6 +3065,36 @@ public class SqlToRelConverterTest extends 
SqlToRelTestBase {
     sql(sql).ok();
   }
 
+  @Test public void testJsonType() {
+    final String sql = "select json_type(ename)\n"
+        + "from emp";
+    sql(sql).ok();
+  }
+
+  @Test public void testJsonPretty() {
+    final String sql = "select json_pretty(ename)\n"
+        + "from emp";
+    sql(sql).ok();
+  }
+
+  @Test public void testJsonDepth() {
+    final String sql = "select json_depth(ename)\n"
+        + "from emp";
+    sql(sql).ok();
+  }
+
+  @Test public void testJsonLength() {
+    final String sql = "select json_length(ename, 'strict $')\n"
+        + "from emp";
+    sql(sql).ok();
+  }
+
+  @Test public void testJsonKeys() {
+    final String sql = "select json_keys(ename, 'strict $')\n"
+        + "from emp";
+    sql(sql).ok();
+  }
+
   @Test public void testJsonArray() {
     final String sql = "select json_array(ename, ename)\n"
         + "from emp";
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index b9dad50..a70664c 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -8882,9 +8882,6 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         + "DOT -\n"
         + "ITEM -\n"
         + "JSON_API_COMMON_SYNTAX -\n"
-        + "JSON_API_COMMON_SYNTAX_WITHOUT_PATH -\n"
-        + "JSON_STRUCTURED_VALUE_EXPRESSION -\n"
-        + "JSON_VALUE_EXPRESSION -\n"
         + "NEXT_VALUE -\n"
         + "PATTERN_EXCLUDE -\n"
         + "PATTERN_PERMUTE -\n"
@@ -8956,6 +8953,7 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         + "SUBMULTISET OF left\n"
         + "SUCCEEDS left\n"
         + "\n"
+        + "FORMAT JSON post\n"
         + "IS A SET post\n"
         + "IS EMPTY post\n"
         + "IS FALSE post\n"
@@ -10886,6 +10884,15 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
             .fails(onError);
   }
 
+  @Test public void testJsonValueExpressionOperator() {
+    checkExp("'{}' format json");
+    checkExp("'{}' format json encoding utf8");
+    checkExp("'{}' format json encoding utf16");
+    checkExp("'{}' format json encoding utf32");
+    checkExpType("'{}' format json", "ANY NOT NULL");
+    checkExpFails("^null^ format json", "(?s).*Illegal use of .NULL.*");
+  }
+
   @Test public void testJsonExists() {
     checkExp("json_exists('{}', 'lax $')");
     checkExpType("json_exists('{}', 'lax $')", "BOOLEAN");
@@ -10952,6 +10959,14 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
     checkExp("json_pretty('{\"foo\":\"bar\"}')");
     checkExpType("json_pretty('{\"foo\":\"bar\"}')", "VARCHAR(2000) NOT NULL");
     checkFails("select json_pretty(^NULL^) from emp", "(?s).*Illegal use of 
.NULL.*");
+
+    if (!Bug.CALCITE_2869_FIXED) {
+      // the case should throw an error but currently validation
+      // is done during sql-to-rel process.
+      //
+      // see StandardConvertletTable.JsonOperatorValueExprConvertlet
+      return;
+    }
     checkFails("select json_pretty(^1^) from emp",
             "(.*)JSON_VALUE_EXPRESSION(.*)");
   }
@@ -10960,6 +10975,10 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
     check("select json_type(ename) from emp");
     checkExp("json_type('{\"foo\":\"bar\"}')");
     checkExpType("json_type('{\"foo\":\"bar\"}')", "VARCHAR(20) NOT NULL");
+
+    if (!Bug.CALCITE_2869_FIXED) {
+      return;
+    }
     checkFails("select json_type(^1^) from emp",
         "(.*)JSON_VALUE_EXPRESSION(.*)");
   }
@@ -10968,6 +10987,10 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
     check("select json_depth(ename) from emp");
     checkExp("json_depth('{\"foo\":\"bar\"}')");
     checkExpType("json_depth('{\"foo\":\"bar\"}')", "INTEGER");
+
+    if (!Bug.CALCITE_2869_FIXED) {
+      return;
+    }
     checkFails("select json_depth(^1^) from emp",
             "(.*)JSON_VALUE_EXPRESSION(.*)");
   }
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index d5c6fcd..a6752d1 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5524,6 +5524,21 @@ LogicalProject(ANYEMPNO=[$1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testJsonValueExpressionOperator">
+        <Resource name="sql">
+            <![CDATA[select ename format json,
+ename format json encoding utf8,
+ename format json encoding utf16,
+ename format json encoding utf32
+from emp]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(EXPR$0=[FORMAT JSON($1)], EXPR$1=[FORMAT JSON($1)], 
EXPR$2=[FORMAT JSON($1)], EXPR$3=[FORMAT JSON($1)])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testJsonExists">
         <Resource name="sql">
             <![CDATA[select json_exists(ename, 'lax $')
@@ -5531,7 +5546,7 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EXPR$0=[JSON_EXISTS(JSON_API_COMMON_SYNTAX(JSON_VALUE_EXPRESSION($1),
 'lax $'))])
+LogicalProject(EXPR$0=[JSON_EXISTS(JSON_API_COMMON_SYNTAX(FORMAT JSON($1), 
'lax $'))])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5543,7 +5558,7 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EXPR$0=[CAST(JSON_VALUE_ANY(JSON_API_COMMON_SYNTAX(JSON_VALUE_EXPRESSION($1),
 'lax $'), FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY, 
FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY)):VARCHAR(2000)])
+LogicalProject(EXPR$0=[CAST(JSON_VALUE_ANY(JSON_API_COMMON_SYNTAX(FORMAT 
JSON($1), 'lax $'), FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY, 
FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY)):VARCHAR(2000)])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5555,7 +5570,67 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EXPR$0=[JSON_QUERY(JSON_API_COMMON_SYNTAX(JSON_VALUE_EXPRESSION($1),
 'lax $'), FLAG(WITHOUT_ARRAY), FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]), 
FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]))])
+LogicalProject(EXPR$0=[JSON_QUERY(JSON_API_COMMON_SYNTAX(FORMAT JSON($1), 'lax 
$'), FLAG(WITHOUT_ARRAY), FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]), 
FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]))])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testJsonType">
+        <Resource name="sql">
+            <![CDATA[select json_type(ename)
+from emp]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(EXPR$0=[JSON_TYPE(FORMAT JSON($1))])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testJsonPretty">
+        <Resource name="sql">
+            <![CDATA[select json_pretty(ename)
+from emp]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(EXPR$0=[JSON_PRETTY(FORMAT JSON($1))])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testJsonDepth">
+        <Resource name="sql">
+            <![CDATA[select json_depth(ename)
+from emp]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(EXPR$0=[JSON_DEPTH(FORMAT JSON($1))])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testJsonLength">
+        <Resource name="sql">
+            <![CDATA[select json_length(ename, 'strict $')
+from emp]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(EXPR$0=[JSON_LENGTH(FORMAT JSON($1), 'strict $')])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testJsonKeys">
+        <Resource name="sql">
+            <![CDATA[select json_keys(ename, 'strict $')
+from emp]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(EXPR$0=[JSON_KEYS(FORMAT JSON($1), 'strict $')])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5567,7 +5642,7 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EXPR$0=[JSON_ARRAY(FLAG(ABSENT_ON_NULL), 
JSON_STRUCTURED_VALUE_EXPRESSION($1), JSON_STRUCTURED_VALUE_EXPRESSION($1))])
+LogicalProject(EXPR$0=[JSON_ARRAY(FLAG(ABSENT_ON_NULL), $1, $1)])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5580,7 +5655,7 @@ from emp]]>
         <Resource name="plan">
             <![CDATA[
 LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)])
-  LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)])
+  LogicalProject(ENAME=[$1])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5592,8 +5667,8 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($0) WITHIN 
GROUP ([1])])
-  LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)], ENAME=[$1])
+LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($0) WITHIN 
GROUP ([0])])
+  LogicalProject(ENAME=[$1])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5605,8 +5680,8 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN 
GROUP ([1])])
-  LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)], ENAME=[$1])
+LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN 
GROUP ([0])])
+  LogicalProject(ENAME=[$1])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5618,8 +5693,8 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN 
GROUP ([1])])
-  LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)], ENAME=[$1])
+LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN 
GROUP ([0])])
+  LogicalProject(ENAME=[$1])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5631,7 +5706,7 @@ from emp]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
-LogicalProject(EXPR$0=[JSON_OBJECT(FLAG(NULL_ON_NULL), $1, 
JSON_STRUCTURED_VALUE_EXPRESSION($7), $1, 
JSON_STRUCTURED_VALUE_EXPRESSION($7))])
+LogicalProject(EXPR$0=[JSON_OBJECT(FLAG(NULL_ON_NULL), $1, $7, $1, $7)])
   LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -5644,7 +5719,7 @@ from emp]]>
         <Resource name="plan">
             <![CDATA[
 LogicalAggregate(group=[{}], EXPR$0=[JSON_OBJECTAGG_NULL_ON_NULL($0, $1)])
-  LogicalProject(ENAME=[$1], $f1=[JSON_STRUCTURED_VALUE_EXPRESSION($7)])
+  LogicalProject(ENAME=[$1], DEPTNO=[$7])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
diff --git a/core/src/test/resources/sql/agg.iq 
b/core/src/test/resources/sql/agg.iq
index c3fda24..4dba368 100644
--- a/core/src/test/resources/sql/agg.iq
+++ b/core/src/test/resources/sql/agg.iq
@@ -2602,27 +2602,26 @@ from emp group by gender;
 (2 rows)
 
 !ok
-EnumerableAggregate(group=[{0}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($1) 
WITHIN GROUP ([2])], EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($1) WITHIN GROUP ([2 
DESC])])
-  EnumerableCalc(expr#0..1=[{inputs}], 
expr#2=[JSON_STRUCTURED_VALUE_EXPRESSION($t0)], GENDER=[$t1], $f1=[$t2], 
DEPTNO=[$t0])
-    EnumerableUnion(all=[true])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
+EnumerableAggregate(group=[{1}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($0) 
WITHIN GROUP ([0])], EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($0) WITHIN GROUP ([0 
DESC])])
+  EnumerableUnion(all=[true])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
 !plan
 
 # [CALCITE-2787] Json aggregate calls with different null clause get 
incorrectly merged
@@ -2640,27 +2639,26 @@ from emp group by gender;
 (2 rows)
 
 !ok
-EnumerableAggregate(group=[{0}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)], 
EXPR$2=[JSON_ARRAYAGG_NULL_ON_NULL($1)])
-  EnumerableCalc(expr#0..1=[{inputs}], 
expr#2=[JSON_STRUCTURED_VALUE_EXPRESSION($t0)], GENDER=[$t1], $f1=[$t2])
-    EnumerableUnion(all=[true])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
-        EnumerableValues(tuples=[[{ 0 }]])
+EnumerableAggregate(group=[{1}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)], 
EXPR$2=[JSON_ARRAYAGG_NULL_ON_NULL($0)])
+  EnumerableUnion(all=[true])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'], EXPR$1=[$t1], 
EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'], 
EXPR$1=[$t1], EXPR$2=[$t2])
+      EnumerableValues(tuples=[[{ 0 }]])
 !plan
 
 select gender,
@@ -2676,27 +2674,26 @@ from emp group by gender;
 (2 rows)
 
 !ok
-EnumerableAggregate(group=[{0}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($1, $2)], 
EXPR$2=[JSON_OBJECTAGG_ABSENT_ON_NULL($1, $2)])
-  EnumerableCalc(expr#0..2=[{inputs}], 
expr#3=[JSON_STRUCTURED_VALUE_EXPRESSION($t1)], GENDER=[$t2], ENAME=[$t0], 
$f2=[$t3])
-    EnumerableUnion(all=[true])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Jane'], expr#2=[10], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Bob'], expr#2=[10], 
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Eric'], expr#2=[20], 
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Susan'], expr#2=[30], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Alice'], expr#2=[30], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Adam'], expr#2=[50], 
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Eve'], expr#2=[50], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Grace'], expr#2=[60], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
-      EnumerableCalc(expr#0=[{inputs}], expr#1=['Wilma'], 
expr#2=[null:INTEGER], expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
-        EnumerableValues(tuples=[[{ 0 }]])
+EnumerableAggregate(group=[{2}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($0, $1)], 
EXPR$2=[JSON_OBJECTAGG_ABSENT_ON_NULL($0, $1)])
+  EnumerableUnion(all=[true])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Jane'], expr#2=[10], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Bob'], expr#2=[10], 
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Eric'], expr#2=[20], 
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Susan'], expr#2=[30], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Alice'], expr#2=[30], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Adam'], expr#2=[50], 
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Eve'], expr#2=[50], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Grace'], expr#2=[60], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
+    EnumerableCalc(expr#0=[{inputs}], expr#1=['Wilma'], expr#2=[null:INTEGER], 
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+      EnumerableValues(tuples=[[{ 0 }]])
 !plan
 
 select listagg(ename) as combined_name from emp;

Reply via email to