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 7c905ff  [CALCITE-2881] Add the JSON_PRETTY function (Ritesh Kapoor)
7c905ff is described below

commit 7c905ff3263bcc6cec826789305f1be9fba0962e
Author: Ritesh Kapoor <[email protected]>
AuthorDate: Thu Feb 28 15:04:33 2019 +0530

    [CALCITE-2881] Add the JSON_PRETTY function (Ritesh Kapoor)
    
    Also refactor SqlJsonFunctionsTest a little to better generate invocation 
descriptions.
    
    Close apache/calcite#1075
---
 babel/src/main/codegen/config.fmpp                 |  3 +
 core/src/main/codegen/config.fmpp                  |  1 +
 core/src/main/codegen/templates/Parser.jj          | 19 +++++
 .../calcite/adapter/enumerable/RexImpTable.java    |  2 +
 .../apache/calcite/runtime/CalciteResource.java    |  3 +
 .../org/apache/calcite/runtime/SqlFunctions.java   | 12 ++-
 .../calcite/sql/fun/SqlJsonPrettyFunction.java     | 68 ++++++++++++++++
 .../calcite/sql/fun/SqlStdOperatorTable.java       |  3 +
 .../org/apache/calcite/util/BuiltInMethod.java     |  6 ++
 .../calcite/runtime/CalciteResource.properties     |  1 +
 core/src/test/codegen/config.fmpp                  |  1 +
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |  7 ++
 .../apache/calcite/sql/parser/SqlParserTest.java   |  7 ++
 .../calcite/sql/test/SqlOperatorBaseTest.java      |  9 ++
 .../java/org/apache/calcite/test/JdbcTest.java     | 12 +++
 .../apache/calcite/test/SqlJsonFunctionsTest.java  | 95 ++++++++++++----------
 .../org/apache/calcite/test/SqlValidatorTest.java  | 11 ++-
 server/src/main/codegen/config.fmpp                |  1 +
 site/_docs/reference.md                            |  2 +
 19 files changed, 216 insertions(+), 47 deletions(-)

diff --git a/babel/src/main/codegen/config.fmpp 
b/babel/src/main/codegen/config.fmpp
index de1efb7..dab4251 100644
--- a/babel/src/main/codegen/config.fmpp
+++ b/babel/src/main/codegen/config.fmpp
@@ -555,6 +555,9 @@ data: {
         "JSON_ARRAYAGG",
         "JSON_EXISTS",
         "JSON_VALUE",
+        "JSON_TYPE",
+        "JSON_DEPTH"
+        "JSON_PRETTY",
         "JSON_OBJECT",
         "JSON_OBJECTAGG",
         "JSON_QUERY",
diff --git a/core/src/main/codegen/config.fmpp 
b/core/src/main/codegen/config.fmpp
index a28294a..c8f9ec8 100644
--- a/core/src/main/codegen/config.fmpp
+++ b/core/src/main/codegen/config.fmpp
@@ -161,6 +161,7 @@ data: {
         "JSON"
         "JSON_TYPE"
         "JSON_DEPTH"
+        "JSON_PRETTY"
         "K"
         "KEY"
         "KEY_MEMBER"
diff --git a/core/src/main/codegen/templates/Parser.jj 
b/core/src/main/codegen/templates/Parser.jj
index 9bdae0d..56a3a7f 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -4823,6 +4823,8 @@ SqlNode BuiltinFunctionCall() :
     |
         node = JsonValueFunctionCall() { return node; }
     |
+        node = JsonPrettyFunctionCall() { return node; }
+    |
         node = JsonQueryFunctionCall() { return node; }
     |
         node = JsonObjectFunctionCall() { return node; }
@@ -5072,6 +5074,22 @@ SqlCall JsonValueFunctionCall() :
     }
 }
 
+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>();
@@ -6355,6 +6373,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < JSON_ARRAYAGG: "JSON_ARRAYAGG">
 |   < JSON_EXISTS: "JSON_EXISTS" >
 |   < JSON_VALUE: "JSON_VALUE" >
+|   < JSON_PRETTY: "JSON_PRETTY" >
 |   < JSON_OBJECT: "JSON_OBJECT">
 |   < JSON_TYPE: "JSON_TYPE">
 |   < JSON_DEPTH: "JSON_DEPTH">
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 e44af2b..dcab895 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
@@ -167,6 +167,7 @@ import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_DEPTH;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_EXISTS;
 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;
@@ -456,6 +457,7 @@ public class RexImpTable {
     defineMethod(JSON_ARRAY, BuiltInMethod.JSON_ARRAY.method, NullPolicy.NONE);
     defineMethod(JSON_TYPE, BuiltInMethod.JSON_TYPE.method, NullPolicy.NONE);
     defineMethod(JSON_DEPTH, BuiltInMethod.JSON_DEPTH.method, NullPolicy.NONE);
+    defineMethod(JSON_PRETTY, BuiltInMethod.JSON_PRETTY.method, 
NullPolicy.NONE);
     
aggMap.put(JSON_OBJECTAGG.with(SqlJsonConstructorNullClause.ABSENT_ON_NULL),
         JsonObjectAggImplementor
             .supplierFor(BuiltInMethod.JSON_OBJECTAGG_ADD.method));
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java 
b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index a7f2203..2aeb60d 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -861,6 +861,9 @@ public interface CalciteResource {
 
   @BaseMessage("Unknown JSON depth in JSON_DEPTH function, and the object is: 
''{0}''")
   ExInst<CalciteException> unknownObjectOfJsonDepth(String value);
+
+  @BaseMessage("Cannot serialize object to JSON, and the object is: ''{0}''")
+  ExInst<CalciteException> exceptionWhileSerializingToJson(String value);
 }
 
 // End CalciteResource.java
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 fed1d97..888c8a2 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -48,7 +48,6 @@ import com.jayway.jsonpath.DocumentContext;
 import com.jayway.jsonpath.JsonPath;
 import com.jayway.jsonpath.Option;
 import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
-import com.jayway.jsonpath.spi.json.JsonProvider;
 import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
 import com.jayway.jsonpath.spi.mapper.MappingProvider;
 
@@ -131,7 +130,7 @@ public class SqlFunctions {
       Pattern.compile("^\\s*(?<mode>strict|lax)\\s+(?<spec>.+)$",
           Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
 
-  private static final JsonProvider JSON_PATH_JSON_PROVIDER =
+  private static final JacksonJsonProvider JSON_PATH_JSON_PROVIDER =
       new JacksonJsonProvider();
   private static final MappingProvider JSON_PATH_MAPPING_PROVIDER =
       new JacksonMappingProvider();
@@ -2677,6 +2676,15 @@ public class SqlFunctions {
     }
   }
 
+  public static String jsonPretty(Object input) {
+    try {
+      return 
JSON_PATH_JSON_PROVIDER.getObjectMapper().writerWithDefaultPrettyPrinter()
+          .writeValueAsString(input);
+    } catch (Exception e) {
+      throw RESOURCE.exceptionWhileSerializingToJson(input.toString()).ex();
+    }
+  }
+
   public static String jsonType(Object o) {
     final String result;
     try {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonPrettyFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonPrettyFunction.java
new file mode 100644
index 0000000..cc33d31
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonPrettyFunction.java
@@ -0,0 +1,68 @@
+/*
+ * 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.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.SqlOperandCountRange;
+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.SqlOperandCountRanges;
+import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.validate.SqlValidator;
+
+/**
+ * The <code>JSON_TYPE</code> function.
+ */
+public class SqlJsonPrettyFunction extends SqlFunction {
+
+  public SqlJsonPrettyFunction() {
+    super("JSON_PRETTY",
+        SqlKind.OTHER_FUNCTION,
+        ReturnTypes.VARCHAR_2000,
+        null,
+        OperandTypes.ANY,
+        SqlFunctionCategory.SYSTEM);
+  }
+
+  @Override public SqlOperandCountRange getOperandCountRange() {
+    return SqlOperandCountRanges.of(1);
+  }
+
+  @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);
+  }
+
+  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
+      int rightPrec) {
+    super.unparse(writer, call, 0, 0);
+  }
+}
+
+// End SqlJsonPrettyFunction.java
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 5843835..3e66b9c 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
@@ -1297,6 +1297,9 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
   public static final SqlFunction JSON_VALUE =
       new SqlJsonValueFunction("JSON_VALUE", false);
 
+  public static final SqlFunction JSON_PRETTY =
+          new SqlJsonPrettyFunction();
+
   public static final SqlFunction JSON_VALUE_ANY =
       new SqlJsonValueFunction("JSON_VALUE_ANY", true);
 
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 f0ce346..fc7cb60 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -266,6 +266,7 @@ public enum BuiltInMethod {
   UPPER(SqlFunctions.class, "upper", String.class),
   LOWER(SqlFunctions.class, "lower", String.class),
   JSONIZE(SqlFunctions.class, "jsonize", Object.class),
+  DEJSONIZE(SqlFunctions.class, "dejsonize", String.class),
   JSON_VALUE_EXPRESSION(SqlFunctions.class, "jsonValueExpression",
       String.class),
   JSON_STRUCTURED_VALUE_EXPRESSION(SqlFunctions.class,
@@ -284,6 +285,7 @@ public enum BuiltInMethod {
       SqlJsonConstructorNullClause.class),
   JSON_TYPE(SqlFunctions.class, "jsonType", Object.class),
   JSON_DEPTH(SqlFunctions.class, "jsonDepth", Object.class),
+  JSON_PRETTY(SqlFunctions.class, "jsonPretty", Object.class),
   JSON_OBJECTAGG_ADD(SqlFunctions.class, "jsonObjectAggAdd", Map.class,
       String.class, Object.class, SqlJsonConstructorNullClause.class),
   JSON_ARRAY(SqlFunctions.class, "jsonArray",
@@ -533,6 +535,10 @@ public enum BuiltInMethod {
     this(null, null, Types.lookupField(clazz, fieldName));
     assert dummy : "dummy value for method overloading must be true";
   }
+
+  public String getMethodName() {
+    return method.getName();
+  }
 }
 
 // End BuiltInMethod.java
diff --git 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index acb35d1..fc6ef27 100644
--- 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -280,4 +280,5 @@ AmbiguousSortOrderInJsonArrayAggFunc=Including both WITHIN 
GROUP(...) and inside
 ExceptionWhilePerformingQueryOnJdbcSubSchema = While executing SQL [{0}] on 
JDBC sub-schema
 UnknownObjectOfJsonType=Unknown JSON type in JSON_TYPE function, and the 
object is: ''{0}''
 UnknownObjectOfJsonDepth=Unknown JSON depth in JSON_DEPTH function, and the 
object is: ''{0}''
+ExceptionWhileSerializingToJson=Cannot serialize object to JSON, and the 
object is: ''{0}''
 # End CalciteResource.properties
diff --git a/core/src/test/codegen/config.fmpp 
b/core/src/test/codegen/config.fmpp
index c1096b0..d9ae98b 100644
--- a/core/src/test/codegen/config.fmpp
+++ b/core/src/test/codegen/config.fmpp
@@ -145,6 +145,7 @@ data: {
         "JSON"
         "JSON_TYPE"
         "JSON_DEPTH"
+        "JSON_PRETTY"
         "K"
         "KEY"
         "KEY_MEMBER"
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 31c51e7..9a79b50 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
@@ -2990,6 +2990,13 @@ public class RelToSqlConverterTest {
     sql(query).ok(expected);
   }
 
+  @Test public void testJsonPretty() {
+    String query = "select json_pretty(\"product_name\") from \"product\"";
+    final String expected = "SELECT JSON_PRETTY(\"product_name\" FORMAT 
JSON)\n"
+            + "FROM \"foodmart\".\"product\"";
+    sql(query).ok(expected);
+  }
+
   @Test public void testJsonValue() {
     String query = "select json_value(\"product_name\", 'lax $') from 
\"product\"";
     // todo translate to JSON_VALUE rather than CAST
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 6badf4c..62d7852 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
@@ -8427,6 +8427,13 @@ public class SqlParserTest {
         "JSON_ARRAY(JSON_ARRAY('foo', 'bar' ABSENT ON NULL) FORMAT JSON ABSENT 
ON NULL)");
   }
 
+  @Test public void testJsonPretty() {
+    checkExp("json_pretty('foo')",
+            "JSON_PRETTY('foo' FORMAT JSON)");
+    checkExp("json_pretty(null)",
+            "JSON_PRETTY(NULL FORMAT JSON)");
+  }
+
   @Test public void testJsonArrayAgg1() {
     checkExp("json_arrayagg(\"column\")",
         "JSON_ARRAYAGG(`column` ABSENT ON NULL)");
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 22a13e8..bb9c880 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
@@ -4475,6 +4475,15 @@ public abstract class SqlOperatorBaseTest {
         "{\"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");
+    tester.checkString("json_pretty('[1,2,3]')",
+        "[ 1, 2, 3 ]", "VARCHAR(2000) NOT NULL");
+    tester.checkString("json_pretty('null')",
+        "null", "VARCHAR(2000) NOT NULL");
+  }
+
   @Test public void testJsonType() {
     tester.setFor(SqlStdOperatorTable.JSON_TYPE);
     tester.checkString("json_type('\"1\"')",
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 959aa12..86482a4 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -6817,6 +6817,18 @@ public class JdbcTest {
         .returns("C1=3; C2=2; C3=1; C4=1\n");
   }
 
+  @Test
+  public void testJsonPretty() {
+    CalciteAssert.that()
+        .query("SELECT JSON_PRETTY(v) AS c1\n"
+            + "FROM (VALUES ('{\"a\": [10, true],\"b\": [10, true]}')) as 
t(v)\n"
+            + "limit 10")
+        .returns("C1={\n"
+            + "  \"a\" : [ 10, true ],\n"
+            + "  \"b\" : [ 10, true ]\n"
+            + "}\n");
+  }
+
   /**
    * Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-2609";>[CALCITE-2609]
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 262a010..3912a3d 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
@@ -23,8 +23,10 @@ import org.apache.calcite.sql.SqlJsonExistsErrorBehavior;
 import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior;
 import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior;
 import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
+import org.apache.calcite.util.BuiltInMethod;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.Longs;
 import com.jayway.jsonpath.InvalidJsonException;
 import com.jayway.jsonpath.PathNotFoundException;
 
@@ -53,28 +55,6 @@ import static org.junit.Assert.fail;
  * Unit test for the methods in {@link SqlFunctions} that implement JSON 
processing functions.
  */
 public class SqlJsonFunctionsTest {
-  public static final String INVOC_DESC_JSON_VALUE_EXPRESSION =
-      "jsonValueExpression";
-  public static final String INVOC_DESC_JSON_STRUCTURED_VALUE_EXPRESSION =
-      "jsonStructuredValueExpression";
-  public static final String INVOC_DESC_JSON_API_COMMON_SYNTAX =
-      "jsonApiCommonSyntax";
-  public static final String INVOC_DESC_JSON_EXISTS = "jsonExists";
-  public static final String INVOC_DESC_JSON_VALUE_ANY = "jsonValueAny";
-  public static final String INVOC_DESC_JSON_QUERY = "jsonQuery";
-  public static final String INVOC_DESC_JSONIZE = "jsonize";
-  public static final String INVOC_DESC_DEJSONIZE = "dejsonize";
-  public static final String INVOC_DESC_JSON_OBJECT = "jsonObject";
-  public static final String INVOC_DESC_JSON_TYPE = "jsonType";
-  public static final String INVOC_DESC_JSON_DEPTH = "jsonDepth";
-  public static final String INVOC_DESC_JSON_OBJECT_AGG_ADD =
-      "jsonObjectAggAdd";
-  public static final String INVOC_DESC_JSON_ARRAY = "jsonArray";
-  public static final String INVOC_DESC_JSON_ARRAY_AGG_ADD = "jsonArrayAggAdd";
-  public static final String INVOC_DESC_IS_JSON_VALUE = "isJsonValue";
-  public static final String INVOC_DESC_IS_JSON_SCALAR = "isJsonScalar";
-  public static final String INVOC_DESC_IS_JSON_ARRAY = "isJsonArray";
-  public static final String INVOC_DESC_IS_JSON_OBJECT = "isJsonObject";
 
   @Test
   public void testJsonValueExpression() {
@@ -445,6 +425,19 @@ public class SqlJsonFunctionsTest {
   }
 
   @Test
+  public void assertJsonPretty() {
+    assertJsonPretty(new HashMap<>(), is("{ }"));
+    assertJsonPretty(Longs.asList(1, 2), is("[ 1, 2 ]"));
+
+    Object input = new Object() {
+      private final Object self = this;
+    };
+    CalciteException expected = new CalciteException(
+        "Cannot serialize object to JSON, and the object is: '" + input + "'", 
null);
+    assertJsonPrettyFailed(input, errorMatches(expected));
+  }
+
+  @Test
   public void testDejsonize() {
     assertDejsonize("{}",
         is(Collections.emptyMap()));
@@ -568,34 +561,34 @@ public class SqlJsonFunctionsTest {
   private void assertJsonValueExpression(String input,
       Matcher<Object> matcher) {
     assertThat(
-        invocationDesc(INVOC_DESC_JSON_VALUE_EXPRESSION, input),
+        invocationDesc(BuiltInMethod.JSON_VALUE_EXPRESSION.getMethodName(), 
input),
         SqlFunctions.jsonValueExpression(input), matcher);
   }
 
   private void assertJsonStructuredValueExpression(Object input,
       Matcher<Object> matcher) {
     assertThat(
-        invocationDesc(INVOC_DESC_JSON_STRUCTURED_VALUE_EXPRESSION, input),
+        
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(
-        invocationDesc(INVOC_DESC_JSON_API_COMMON_SYNTAX, input, pathSpec),
+        invocationDesc(BuiltInMethod.JSON_API_COMMON_SYNTAX.getMethodName(), 
input, pathSpec),
         SqlFunctions.jsonApiCommonSyntax(input, pathSpec), matcher);
   }
 
   private void assertJsonExists(Object input, SqlJsonExistsErrorBehavior 
errorBehavior,
       Matcher<? super Boolean> matcher) {
-    assertThat(invocationDesc(INVOC_DESC_JSON_EXISTS, input, errorBehavior),
+    assertThat(invocationDesc(BuiltInMethod.JSON_EXISTS.getMethodName(), 
input, errorBehavior),
         SqlFunctions.jsonExists(input, errorBehavior), matcher);
   }
 
   private void assertJsonExistsFailed(Object input,
       SqlJsonExistsErrorBehavior errorBehavior,
       Matcher<? super Throwable> matcher) {
-    assertFailed(invocationDesc(INVOC_DESC_JSON_EXISTS, input, errorBehavior),
+    assertFailed(invocationDesc(BuiltInMethod.JSON_EXISTS.getMethodName(), 
input, errorBehavior),
         () -> SqlFunctions.jsonExists(input, errorBehavior), matcher);
   }
 
@@ -606,7 +599,7 @@ public class SqlJsonFunctionsTest {
       Object defaultValueOnError,
       Matcher<Object> matcher) {
     assertThat(
-        invocationDesc(INVOC_DESC_JSON_VALUE_ANY, input, emptyBehavior,
+        invocationDesc(BuiltInMethod.JSON_VALUE_ANY.getMethodName(), input, 
emptyBehavior,
             defaultValueOnEmpty, errorBehavior, defaultValueOnError),
         SqlFunctions.jsonValueAny(input, emptyBehavior, defaultValueOnEmpty,
             errorBehavior, defaultValueOnError),
@@ -620,7 +613,7 @@ public class SqlJsonFunctionsTest {
       Object defaultValueOnError,
       Matcher<? super Throwable> matcher) {
     assertFailed(
-        invocationDesc(INVOC_DESC_JSON_VALUE_ANY, input, emptyBehavior,
+        invocationDesc(BuiltInMethod.JSON_VALUE_ANY.getMethodName(), input, 
emptyBehavior,
             defaultValueOnEmpty, errorBehavior, defaultValueOnError),
         () -> SqlFunctions.jsonValueAny(input, emptyBehavior,
             defaultValueOnEmpty, errorBehavior, defaultValueOnError),
@@ -633,7 +626,7 @@ public class SqlJsonFunctionsTest {
       SqlJsonQueryEmptyOrErrorBehavior errorBehavior,
       Matcher<? super String> matcher) {
     assertThat(
-        invocationDesc(INVOC_DESC_JSON_QUERY, input, wrapperBehavior,
+        invocationDesc(BuiltInMethod.JSON_QUERY.getMethodName(), input, 
wrapperBehavior,
             emptyBehavior, errorBehavior),
         SqlFunctions.jsonQuery(input, wrapperBehavior, emptyBehavior,
             errorBehavior),
@@ -646,7 +639,7 @@ public class SqlJsonFunctionsTest {
       SqlJsonQueryEmptyOrErrorBehavior errorBehavior,
       Matcher<? super Throwable> matcher) {
     assertFailed(
-        invocationDesc(INVOC_DESC_JSON_QUERY, input, wrapperBehavior,
+        invocationDesc(BuiltInMethod.JSON_QUERY.getMethodName(), input, 
wrapperBehavior,
             emptyBehavior, errorBehavior),
         () -> SqlFunctions.jsonQuery(input, wrapperBehavior, emptyBehavior,
             errorBehavior),
@@ -655,21 +648,35 @@ public class SqlJsonFunctionsTest {
 
   private void assertJsonize(Object input,
       Matcher<? super String> matcher) {
-    assertThat(invocationDesc(INVOC_DESC_JSONIZE, input),
+    assertThat(invocationDesc(BuiltInMethod.JSONIZE.getMethodName(), input),
         SqlFunctions.jsonize(input),
         matcher);
   }
 
+  private void assertJsonPretty(Object input,
+      Matcher<? super String> matcher) {
+    assertThat(invocationDesc(BuiltInMethod.JSON_PRETTY.getMethodName(), 
input),
+        SqlFunctions.jsonPretty(input),
+        matcher);
+  }
+
+  private void assertJsonPrettyFailed(Object input,
+      Matcher<? super Throwable> matcher) {
+    assertFailed(invocationDesc(BuiltInMethod.JSON_PRETTY.getMethodName(), 
input),
+        () -> SqlFunctions.jsonPretty(input),
+        matcher);
+  }
+
   private void assertDejsonize(String input,
       Matcher<Object> matcher) {
-    assertThat(invocationDesc(INVOC_DESC_DEJSONIZE, input),
+    assertThat(invocationDesc(BuiltInMethod.DEJSONIZE.getMethodName(), input),
             SqlFunctions.dejsonize(input),
         matcher);
   }
 
   private void assertDejsonizeFailed(String input,
       Matcher<? super Throwable> matcher) {
-    assertFailed(invocationDesc(INVOC_DESC_DEJSONIZE, input),
+    assertFailed(invocationDesc(BuiltInMethod.DEJSONIZE.getMethodName(), 
input),
         () -> SqlFunctions.dejsonize(input),
         matcher);
   }
@@ -677,7 +684,7 @@ public class SqlJsonFunctionsTest {
   private void assertJsonObject(Matcher<? super String> matcher,
       SqlJsonConstructorNullClause nullClause,
       Object... kvs) {
-    assertThat(invocationDesc(INVOC_DESC_JSON_OBJECT, nullClause, kvs),
+    assertThat(invocationDesc(BuiltInMethod.JSON_OBJECT.getMethodName(), 
nullClause, kvs),
         SqlFunctions.jsonObject(nullClause, kvs),
         matcher);
   }
@@ -685,7 +692,7 @@ public class SqlJsonFunctionsTest {
   private void assertJsonType(Matcher<? super String> matcher,
                                 String input) {
     assertThat(
-        invocationDesc(INVOC_DESC_JSON_TYPE, input),
+        invocationDesc(BuiltInMethod.JSON_TYPE.getMethodName(), input),
         SqlFunctions.jsonType(
                 SqlFunctions.dejsonize(input)),
         matcher);
@@ -694,7 +701,7 @@ public class SqlJsonFunctionsTest {
   private void assertJsonDepth(Matcher<? super Integer> matcher,
                               String input) {
     assertThat(
-            invocationDesc(INVOC_DESC_JSON_DEPTH, input),
+            invocationDesc(BuiltInMethod.JSON_DEPTH.getMethodName(), input),
             SqlFunctions.jsonDepth(
                     SqlFunctions.dejsonize(input)),
             matcher);
@@ -705,13 +712,13 @@ public class SqlJsonFunctionsTest {
       Matcher<? super Map> matcher) {
     SqlFunctions.jsonObjectAggAdd(map, k, v, nullClause);
     assertThat(
-        invocationDesc(INVOC_DESC_JSON_OBJECT_AGG_ADD, map, k, v, nullClause),
+        invocationDesc(BuiltInMethod.JSON_ARRAYAGG_ADD.getMethodName(), map, 
k, v, nullClause),
         map, matcher);
   }
 
   private void assertJsonArray(Matcher<? super String> matcher,
       SqlJsonConstructorNullClause nullClause, Object... elements) {
-    assertThat(invocationDesc(INVOC_DESC_JSON_ARRAY, nullClause, elements),
+    assertThat(invocationDesc(BuiltInMethod.JSON_ARRAY.getMethodName(), 
nullClause, elements),
         SqlFunctions.jsonArray(nullClause, elements),
         matcher);
   }
@@ -721,35 +728,35 @@ public class SqlJsonFunctionsTest {
       Matcher<? super List> matcher) {
     SqlFunctions.jsonArrayAggAdd(list, element, nullClause);
     assertThat(
-        invocationDesc(INVOC_DESC_JSON_ARRAY_AGG_ADD, list, element,
+        invocationDesc(BuiltInMethod.JSON_ARRAYAGG_ADD.getMethodName(), list, 
element,
             nullClause),
         list, matcher);
   }
 
   private void assertIsJsonValue(String input,
       Matcher<? super Boolean> matcher) {
-    assertThat(invocationDesc(INVOC_DESC_IS_JSON_VALUE, input),
+    assertThat(invocationDesc(BuiltInMethod.IS_JSON_VALUE.getMethodName(), 
input),
         SqlFunctions.isJsonValue(input),
         matcher);
   }
 
   private void assertIsJsonScalar(String input,
       Matcher<? super Boolean> matcher) {
-    assertThat(invocationDesc(INVOC_DESC_IS_JSON_SCALAR, input),
+    assertThat(invocationDesc(BuiltInMethod.IS_JSON_SCALAR.getMethodName(), 
input),
         SqlFunctions.isJsonScalar(input),
         matcher);
   }
 
   private void assertIsJsonArray(String input,
       Matcher<? super Boolean> matcher) {
-    assertThat(invocationDesc(INVOC_DESC_IS_JSON_ARRAY, input),
+    assertThat(invocationDesc(BuiltInMethod.IS_JSON_ARRAY.getMethodName(), 
input),
         SqlFunctions.isJsonArray(input),
         matcher);
   }
 
   private void assertIsJsonObject(String input,
       Matcher<? super Boolean> matcher) {
-    assertThat(invocationDesc(INVOC_DESC_IS_JSON_OBJECT, input),
+    assertThat(invocationDesc(BuiltInMethod.IS_JSON_OBJECT.getMethodName(), 
input),
         SqlFunctions.isJsonObject(input),
         matcher);
   }
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 434d284..158848c 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -10769,12 +10769,21 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         "(?s).*Expected a character type*");
   }
 
+  @Test public void testJsonPretty() {
+    check("select json_pretty(ename) from emp");
+    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.*");
+    checkFails("select json_pretty(^1^) from emp",
+            "(.*)JSON_VALUE_EXPRESSION(.*)");
+  }
+
   @Test public void testJsonType() {
     check("select json_type(ename) from emp");
     checkExp("json_type('{\"foo\":\"bar\"}')");
     checkExpType("json_type('{\"foo\":\"bar\"}')", "VARCHAR(20) NOT NULL");
     checkFails("select json_type(^1^) from emp",
-            "(.*)JSON_VALUE_EXPRESSION(.*)");
+        "(.*)JSON_VALUE_EXPRESSION(.*)");
   }
 
   @Test public void testJsonDepth() {
diff --git a/server/src/main/codegen/config.fmpp 
b/server/src/main/codegen/config.fmpp
index f7d2318..0a110fd 100644
--- a/server/src/main/codegen/config.fmpp
+++ b/server/src/main/codegen/config.fmpp
@@ -153,6 +153,7 @@ data: {
         "JSON"
         "JSON_TYPE"
         "JSON_DEPTH"
+        "JSON_PRETTY"
         "K"
         "KEY"
         "KEY_MEMBER"
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 708ea79..198a236 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -567,6 +567,7 @@ JSON_DEPTH,
 **JSON_EXISTS**,
 **JSON_OBJECT**,
 **JSON_OBJECTAGG**,
+JSON_PRETTY,
 **JSON_QUERY**,
 JSON_TYPE,
 **JSON_VALUE**,
@@ -2003,6 +2004,7 @@ Note:
 |:------------------------------------------------- |:-----------
 | JSON_TYPE(value)                                  | Returns a string 
indicating the type of a JSON **value**. This can be an object, an array, or a 
scalar type
 | JSON_DEPTH(value)                                 | Returns a integer 
indicating the depth of a JSON **value**. This can be an object, an array, or a 
scalar type
+| JSON_PRETTY(value)                                | Returns a 
pretty-printing of JSON **value**.
 
 * JSON_TYPE
 

Reply via email to