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

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


The following commit(s) were added to refs/heads/master by this push:
     new 13b74a36de2 [FLINK-40996][table] Make `TRY_PARSE_JSON`, `PARSE_JSON` 
reuse parsed json
13b74a36de2 is described below

commit 13b74a36de2a88aa39ff23b482995b94e55e2c92
Author: Sergey Nuyanzin <[email protected]>
AuthorDate: Fri Oct 16 17:14:46 2026 +0200

    [FLINK-40996][table] Make `TRY_PARSE_JSON`, `PARSE_JSON` reuse parsed json
---
 .../functions/BuiltInFunctionDefinitions.java      |   6 +-
 .../table/planner/codegen/JsonCodeGenUtils.java    | 208 +++++++++++++++++++++
 .../table/planner/codegen/ExprCodeGenerator.scala  |   6 +
 .../planner/codegen/calls/BuiltInMethods.scala     |   8 +-
 .../table/planner/codegen/JsonParseReuseTest.java  | 102 +++++++++-
 .../table/runtime/functions/SqlJsonUtils.java      |  23 +++
 .../functions/scalar/ParseJsonFunction.java        |  67 -------
 .../functions/scalar/TryParseJsonFunction.java     |  54 ------
 8 files changed, 343 insertions(+), 131 deletions(-)

diff --git 
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java
 
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java
index 791d19b2fdb..9e993e26429 100644
--- 
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java
+++ 
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java
@@ -3215,8 +3215,7 @@ public final class BuiltInFunctionDefinitions {
                     .outputTypeStrategy(
                             nullableIfArgs(
                                     ConstantArgumentCount.of(0), 
explicit(DataTypes.VARIANT())))
-                    .runtimeClass(
-                            
"org.apache.flink.table.runtime.functions.scalar.ParseJsonFunction")
+                    .runtimeProvided()
                     .build();
 
     public static final BuiltInFunctionDefinition TRY_PARSE_JSON =
@@ -3230,8 +3229,7 @@ public final class BuiltInFunctionDefinitions {
                                             
logical(LogicalTypeFamily.CHARACTER_STRING),
                                             logical(LogicalTypeRoot.BOOLEAN))))
                     
.outputTypeStrategy(forceNullable(explicit(DataTypes.VARIANT())))
-                    .runtimeClass(
-                            
"org.apache.flink.table.runtime.functions.scalar.TryParseJsonFunction")
+                    .runtimeProvided()
                     .build();
 
     public static final BuiltInFunctionDefinition PARSE_XML =
diff --git 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/codegen/JsonCodeGenUtils.java
 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/codegen/JsonCodeGenUtils.java
index f63c35085ea..2dd2f068c71 100644
--- 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/codegen/JsonCodeGenUtils.java
+++ 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/codegen/JsonCodeGenUtils.java
@@ -22,16 +22,224 @@ import 
org.apache.flink.table.planner.codegen.calls.BuiltInMethods;
 import org.apache.flink.table.planner.codegen.calls.JsonParseReuse;
 import org.apache.flink.table.runtime.functions.SqlJsonUtils;
 import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.types.variant.Variant;
 
 import java.lang.reflect.Method;
 
+import scala.Option;
 import scala.collection.Seq;
 
 /** Utilities for the code generation of JSON functions. */
 public final class JsonCodeGenUtils {
 
+    private static final int PARSED_VALUE_INDEX = Integer.MIN_VALUE;
+    private static final int PARSED_ERROR_INDEX = Integer.MIN_VALUE + 1;
+
     private JsonCodeGenUtils() {}
 
+    /**
+     * Generates {@code PARSE_JSON(str[, allowDuplicateKeys])} ({@code isTry 
== false}) or {@code
+     * TRY_PARSE_JSON(...)} ({@code isTry == true}).
+     *
+     * <p>The input is parsed at most once per record for a given (input, 
allowDuplicateKeys) pair
+     * and shared across all such calls; {@code PARSE_JSON} rethrows a parse 
error while {@code
+     * TRY_PARSE_JSON} returns NULL.
+     *
+     * <p>For {@code PARSE_JSON(str)} the generated code looks like:
+     *
+     * <pre>{@code
+     * // reusable members shared by every call on the same (input, allowDup):
+     * Variant variantParsed$0;
+     * Throwable variantParseError$0;
+     * StringData variantParseInput$0;
+     * java.lang.Boolean variantParseDup$0;
+     *
+     * private Variant parseVariant$0(StringData in, java.lang.Boolean 
allowDup) {
+     *   if (in == variantParseInput$0 && java.util.Objects.equals(allowDup, 
variantParseDup$0)) {
+     *     return variantParsed$0;
+     *   }
+     *   variantParseInput$0 = in;
+     *   variantParseDup$0 = allowDup;
+     *   variantParseError$0 = null;
+     *   try {
+     *     variantParsed$0 =
+     *         SqlJsonUtils.parseJsonToVariant(in, 
java.lang.Boolean.TRUE.equals(allowDup));
+     *   } catch (Exception e) {
+     *     variantParsed$0 = null;
+     *     variantParseError$0 = t;
+     *   }
+     *   return variantParsed$0;
+     * }
+     *
+     * // per call site:
+     * result$0 = null;
+     * isNull$0 = true;
+     * if (!inIsNull) {
+     *   result$0 = parseVariant$0(in, null);
+     *   if (variantParseError$0 != null) { // only emitted for PARSE_JSON, 
not TRY_PARSE_JSON
+     *     org.apache.flink.util.ExceptionUtils.rethrow(variantParseError$0);
+     *   }
+     *   isNull$0 = result$0 == null;
+     * }
+     * }</pre>
+     */
+    public static GeneratedExpression generateParseJson(
+            CodeGeneratorContext ctx,
+            LogicalType returnType,
+            Seq<GeneratedExpression> operands,
+            boolean isTry) {
+        final GeneratedExpression input = operands.apply(0);
+        final GeneratedExpression allowDup = operands.length() > 1 ? 
operands.apply(1) : null;
+
+        final String key =
+                "variantParse:"
+                        + input.resultTerm()
+                        + ":"
+                        + (allowDup == null ? "" : allowDup.resultTerm());
+
+        final Option<GeneratedExpression> cachedValue =
+                ctx.getReusableInputUnboxingExprs(key, PARSED_VALUE_INDEX);
+        final Option<GeneratedExpression> cachedError =
+                ctx.getReusableInputUnboxingExprs(key, PARSED_ERROR_INDEX);
+
+        final String valueTerm;
+        final String errorTerm;
+        if (cachedValue.isDefined() && cachedError.isDefined()) {
+            valueTerm = cachedValue.get().resultTerm();
+            errorTerm = cachedError.get().resultTerm();
+        } else {
+            final String variantType = CodeGenUtils.className(Variant.class);
+            final String inputType = 
CodeGenUtils.boxedTypeTermForType(input.resultType());
+            final String parsed = CodeGenUtils.newName(ctx, "variantParsed");
+            final String error = CodeGenUtils.newName(ctx, 
"variantParseError");
+            final String lastInput = CodeGenUtils.newName(ctx, 
"variantParseInput");
+            final String lastDup = CodeGenUtils.newName(ctx, 
"variantParseDup");
+            final String method = CodeGenUtils.newName(ctx, "parseVariant");
+
+            ctx.addReusableMember(variantType + " " + parsed + ";");
+            ctx.addReusableMember("Throwable " + error + ";");
+            ctx.addReusableMember(inputType + " " + lastInput + ";");
+            ctx.addReusableMember("java.lang.Boolean " + lastDup + ";");
+            ctx.addReusableMember(
+                    "private "
+                            + variantType
+                            + " "
+                            + method
+                            + "("
+                            + inputType
+                            + " in, java.lang.Boolean allowDup) {\n"
+                            + "  if (in == "
+                            + lastInput
+                            + " && java.util.Objects.equals(allowDup, "
+                            + lastDup
+                            + ")) {\n"
+                            + "    return "
+                            + parsed
+                            + ";\n"
+                            + "  }\n"
+                            + "  "
+                            + lastInput
+                            + " = in;\n"
+                            + "  "
+                            + lastDup
+                            + " = allowDup;\n"
+                            + "  "
+                            + error
+                            + " = null;\n"
+                            + "  try {\n"
+                            + "    "
+                            + parsed
+                            + " = "
+                            + 
CodeGenUtils.qualifyMethod(BuiltInMethods.PARSE_JSON())
+                            + "(in, 
java.lang.Boolean.TRUE.equals(allowDup));\n"
+                            + "  } catch (Exception e) {\n"
+                            + "    "
+                            + parsed
+                            + " = null;\n"
+                            + "    "
+                            + error
+                            + " = e;\n"
+                            + "  }\n"
+                            + "  return "
+                            + parsed
+                            + ";\n"
+                            + "}\n");
+
+            final String dupArg =
+                    allowDup == null
+                            ? "null"
+                            : "("
+                                    + allowDup.nullTerm()
+                                    + ") ? (java.lang.Boolean) null : 
java.lang.Boolean.valueOf("
+                                    + allowDup.resultTerm()
+                                    + ")";
+            valueTerm = method + "(" + input.resultTerm() + ", " + dupArg + 
")";
+            errorTerm = error;
+
+            ctx.addReusableInputUnboxingExprs(
+                    key,
+                    PARSED_VALUE_INDEX,
+                    new GeneratedExpression(
+                            valueTerm,
+                            GeneratedExpression.NEVER_NULL(),
+                            GeneratedExpression.NO_CODE(),
+                            null,
+                            Option.<Object>empty()));
+            ctx.addReusableInputUnboxingExprs(
+                    key,
+                    PARSED_ERROR_INDEX,
+                    new GeneratedExpression(
+                            error,
+                            GeneratedExpression.NEVER_NULL(),
+                            GeneratedExpression.NO_CODE(),
+                            null,
+                            Option.<Object>empty()));
+        }
+
+        final String resultTerm =
+                ctx.addReusableLocalVariable(
+                        CodeGenUtils.primitiveTypeTermForType(returnType), 
"result");
+        final String nullTerm = ctx.addReusableLocalVariable("boolean", 
"isNull");
+
+        final String rethrow =
+                isTry
+                        ? ""
+                        : "  if ("
+                                + errorTerm
+                                + " != null) {\n"
+                                + "    
org.apache.flink.util.ExceptionUtils.rethrow("
+                                + errorTerm
+                                + ");\n"
+                                + "  }\n";
+
+        final String code =
+                input.code()
+                        + "\n"
+                        + (allowDup == null ? "" : allowDup.code() + "\n")
+                        + resultTerm
+                        + " = null;\n"
+                        + nullTerm
+                        + " = true;\n"
+                        + "if (!"
+                        + input.nullTerm()
+                        + ") {\n"
+                        + "  "
+                        + resultTerm
+                        + " = "
+                        + valueTerm
+                        + ";\n"
+                        + rethrow
+                        + "  "
+                        + nullTerm
+                        + " = "
+                        + resultTerm
+                        + " == null;\n"
+                        + "}\n";
+
+        return new GeneratedExpression(
+                resultTerm, nullTerm, code, returnType, 
Option.<Object>empty());
+    }
+
     /**
      * Generates {@code JSON_TYPE(jsonValue)} or {@code JSON_TYPE(jsonValue, 
path)}.
      *
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
index 1053e21741e..8b66910d2aa 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
@@ -948,6 +948,12 @@ class ExprCodeGenerator(
           case BuiltInFunctionDefinitions.JSON_STRING =>
             new JsonStringCallGen(call, rexProgram).generate(ctx, operands, 
resultType)
 
+          case BuiltInFunctionDefinitions.PARSE_JSON =>
+            JsonCodeGenUtils.generateParseJson(ctx, resultType, operands, 
false)
+
+          case BuiltInFunctionDefinitions.TRY_PARSE_JSON =>
+            JsonCodeGenUtils.generateParseJson(ctx, resultType, operands, true)
+
           case BuiltInFunctionDefinitions.JSON_LENGTH =>
             JsonCodeGenUtils.generateJsonLength(ctx, resultType, operands)
 
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala
index d9378d7116f..9bb1de380a5 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/BuiltInMethods.scala
@@ -18,7 +18,7 @@
 package org.apache.flink.table.planner.codegen.calls
 
 import org.apache.flink.table.api.{JsonExistsOnError, JsonQueryOnEmptyOrError, 
JsonQueryWrapper, JsonValueOnEmptyOrError}
-import org.apache.flink.table.data.{DecimalData, DecimalDataUtils, 
TimestampData}
+import org.apache.flink.table.data.{DecimalData, DecimalDataUtils, StringData, 
TimestampData}
 import org.apache.flink.table.data.binary.{BinaryStringData, 
BinaryStringDataUtil}
 import org.apache.flink.table.functions.SqlLikeUtils
 import org.apache.flink.table.runtime.functions._
@@ -514,6 +514,12 @@ object BuiltInMethods {
   val JSON_PARSE =
     Types.lookupMethod(classOf[SqlJsonUtils], "jsonParse", classOf[String])
 
+  val PARSE_JSON = Types.lookupMethod(
+    classOf[SqlJsonUtils],
+    "parseJsonToVariant",
+    classOf[StringData],
+    classOf[Boolean])
+
   val JSON_TYPE =
     Types.lookupMethod(classOf[SqlJsonUtils], "jsonType", 
classOf[SqlJsonUtils.JsonValueContext])
 
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/codegen/JsonParseReuseTest.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/codegen/JsonParseReuseTest.java
index cf7e19fd4f2..a429e541911 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/codegen/JsonParseReuseTest.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/codegen/JsonParseReuseTest.java
@@ -37,10 +37,12 @@ import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** Tests that multiple JSON function calls on the same input reuse the parsed 
JSON. */
 class JsonParseReuseTest {
@@ -48,6 +50,9 @@ class JsonParseReuseTest {
     private static final Pattern JSON_PARSE_PATTERN =
             Pattern.compile("\\b" + 
Pattern.quote(BuiltInMethods.JSON_PARSE().getName()) + "\\(");
 
+    private static final Pattern PARSE_JSON_PATTERN =
+            Pattern.compile("\\b" + 
Pattern.quote(BuiltInMethods.PARSE_JSON().getName()) + "\\(");
+
     private StreamTableEnvironment tEnv;
 
     private static final String JSON_ROW1 =
@@ -72,7 +77,20 @@ class JsonParseReuseTest {
     }
 
     private static int countJsonParse(final String code) {
-        return GeneratedCodeTestUtils.countMatches(JSON_PARSE_PATTERN, code);
+        return count(JSON_PARSE_PATTERN, code);
+    }
+
+    private static int countParseJson(final String code) {
+        return count(PARSE_JSON_PATTERN, code);
+    }
+
+    private static int count(final Pattern pattern, final String code) {
+        final Matcher m = pattern.matcher(code);
+        int count = 0;
+        while (m.find()) {
+            count++;
+        }
+        return count;
     }
 
     private List<String> generatedClassCodes(final String sql) {
@@ -185,15 +203,25 @@ class JsonParseReuseTest {
                 "SELECT JSON_VALUE(TRIM(json_data), '$.type'), "
                         + "JSON_QUERY(TRIM(json_data), '$.address'), "
                         + "JSON_TYPE(TRIM(json_data)), "
-                        + "JSON_LENGTH(TRIM(json_data)) FROM json_src";
+                        + "JSON_LENGTH(TRIM(json_data)), "
+                        + "JSON_STRING(PARSE_JSON(TRIM(json_data))), "
+                        + "JSON_STRING(TRY_PARSE_JSON(TRIM(json_data))) FROM 
json_src";
         final List<Row> rows = collect(sql);
+        // PARSE_JSON returns a VARIANT, which sorts object keys
+        final String variant1 =
+                
"{\"address\":{\"city\":\"Munich\"},\"age\":42,\"roles\":[\"user\",\"viewer\"],\"type\":\"account\"}";
+        final String variant2 =
+                
"{\"address\":{\"city\":\"Berlin\"},\"age\":30,\"roles\":[\"admin\"],\"type\":\"admin\"}";
         assertThat(rows)
                 .containsExactlyInAnyOrder(
-                        Row.of("account", "{\"city\":\"Munich\"}", "object", 
4),
-                        Row.of("admin", "{\"city\":\"Berlin\"}", "object", 4));
+                        Row.of("account", "{\"city\":\"Munich\"}", "object", 
4, variant1, variant1),
+                        Row.of("admin", "{\"city\":\"Berlin\"}", "object", 4, 
variant2, variant2));
         final String code = extractGeneratedCode(sql);
         assertThat(countJsonParse(code))
-                .as("Calls on the same computed input should parse once")
+                .as("JSON_VALUE-family calls on the same computed input should 
parse once")
+                .isOne();
+        assertThat(countParseJson(code))
+                .as("PARSE_JSON + TRY_PARSE_JSON on the same computed input 
should parse once")
                 .isOne();
     }
 
@@ -482,4 +510,68 @@ class JsonParseReuseTest {
                 .as("JSON_TYPE + JSON_VALUE on the same input should parse 
once")
                 .isOne();
     }
+
+    @Test
+    void testParseJsonAndTryParseJsonShareParse() {
+        final String sql =
+                "SELECT JSON_STRING(PARSE_JSON(json_data)), "
+                        + "JSON_STRING(TRY_PARSE_JSON(json_data)) FROM 
json_src";
+        final List<Row> rows = collect(sql);
+        assertThat(rows).hasSize(2).allSatisfy(row -> 
assertThat(row.getField(0)).isNotNull());
+        assertThat(rows).allSatisfy(row -> 
assertThat(row.getField(0)).isEqualTo(row.getField(1)));
+        assertThat(countParseJson(extractGeneratedCode(sql)))
+                .as("PARSE_JSON + TRY_PARSE_JSON on the same input should 
parse once")
+                .isOne();
+    }
+
+    @Test
+    void testParseErrorRethrowForParseJson() {
+        final String badSrc = "(VALUES ('not json')) AS t(j)";
+        final String sharedSql =
+                "SELECT JSON_STRING(TRY_PARSE_JSON(j)), 
JSON_STRING(PARSE_JSON(j)) FROM " + badSrc;
+        assertThat(countParseJson(extractGeneratedCode(sharedSql)))
+                .as("PARSE_JSON + TRY_PARSE_JSON on the same input should 
parse once")
+                .isOne();
+        assertThatThrownBy(() -> collect(sharedSql))
+                .hasStackTraceContaining("Failed to parse JSON string");
+
+        assertThat(collect("SELECT JSON_STRING(TRY_PARSE_JSON(j)) FROM " + 
badSrc))
+                .containsExactly(Row.of((String) null));
+    }
+
+    @Test
+    void testParseJsonDifferentInputsParseSeparately() {
+        final String sql =
+                "SELECT JSON_STRING(PARSE_JSON(json_data)), "
+                        + "JSON_STRING(TRY_PARSE_JSON(other_json)) FROM 
json_src";
+        final List<Row> rows = collect(sql);
+        assertThat(rows).hasSize(2);
+        assertThat(countParseJson(extractGeneratedCode(sql)))
+                .as("PARSE_JSON calls on different inputs should parse 
separately")
+                .isEqualTo(2);
+    }
+
+    @Test
+    void testParseJsonSameAllowDuplicateKeysSharesParse() {
+        final String sql =
+                "SELECT JSON_STRING(PARSE_JSON(json_data, true)), "
+                        + "JSON_STRING(TRY_PARSE_JSON(json_data, true)) FROM 
json_src";
+        final List<Row> rows = collect(sql);
+        assertThat(rows).hasSize(2);
+        assertThat(countParseJson(extractGeneratedCode(sql)))
+                .as("same input and allowDuplicateKeys should parse once")
+                .isOne();
+    }
+
+    @Test
+    void testParseJsonDifferentAllowDuplicateKeysParseSeparately() {
+        final String sql =
+                "SELECT JSON_STRING(PARSE_JSON(json_data, true)), "
+                        + "JSON_STRING(TRY_PARSE_JSON(json_data, false)) FROM 
json_src";
+        final List<Row> rows = collect(sql);
+        assertThat(rows).hasSize(2);
+        assertThat(countParseJson(extractGeneratedCode(sql)))
+                .as("different allowDuplicateKeys should parse separately")
+                .isEqualTo(2);
+    }
 }
diff --git 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
 
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
index bcbf7a3f5f2..b524854b574 100644
--- 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
+++ 
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
@@ -26,6 +26,9 @@ import org.apache.flink.table.api.JsonValueOnEmptyOrError;
 import org.apache.flink.table.api.TableRuntimeException;
 import org.apache.flink.table.data.GenericArrayData;
 import org.apache.flink.table.data.StringData;
+import org.apache.flink.table.data.binary.StringUtf8Utils;
+import org.apache.flink.types.variant.BinaryVariantInternalBuilder;
+import org.apache.flink.types.variant.Variant;
 
 import org.apache.flink.shaded.com.jayway.jsonpath.Configuration;
 import org.apache.flink.shaded.com.jayway.jsonpath.DocumentContext;
@@ -611,6 +614,26 @@ public class SqlJsonUtils {
         return jsonValueExpression(input);
     }
 
+    /** Parses a JSON string into a {@link Variant}, throwing on malformed 
input. */
+    public static Variant parseJsonToVariant(StringData jsonStr, boolean 
allowDuplicateKeys) {
+        final byte[] bytes = jsonStr.toBytes();
+        try {
+            return BinaryVariantInternalBuilder.parseJson(bytes, 
allowDuplicateKeys);
+        } catch (Exception e) {
+            final int invalidIndex =
+                    StringUtf8Utils.firstInvalidUtf8ByteIndex(bytes, 0, 
bytes.length);
+            if (invalidIndex >= 0) {
+                throw new TableRuntimeException(
+                        String.format(
+                                "Failed to parse JSON string: Invalid UTF-8 
byte at index %d of %d.",
+                                invalidIndex, bytes.length),
+                        e);
+            }
+            throw new TableRuntimeException(
+                    String.format("Failed to parse JSON string: %s", jsonStr), 
e);
+        }
+    }
+
     private static JsonValueContext jsonValueExpression(String input) {
         try {
             return JsonValueContext.withJavaObj(dejsonize(input));
diff --git 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/ParseJsonFunction.java
 
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/ParseJsonFunction.java
deleted file mode 100644
index 7c2f5789c8f..00000000000
--- 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/ParseJsonFunction.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.flink.table.runtime.functions.scalar;
-
-import org.apache.flink.table.api.TableRuntimeException;
-import org.apache.flink.table.data.StringData;
-import org.apache.flink.table.data.binary.StringUtf8Utils;
-import org.apache.flink.table.functions.BuiltInFunctionDefinitions;
-import org.apache.flink.table.functions.SpecializedFunction;
-import org.apache.flink.types.variant.BinaryVariantInternalBuilder;
-import org.apache.flink.types.variant.Variant;
-
-import javax.annotation.Nullable;
-
-/** Implementation of {@link BuiltInFunctionDefinitions#PARSE_JSON}. */
-public class ParseJsonFunction extends BuiltInScalarFunction {
-
-    public ParseJsonFunction(SpecializedFunction.SpecializedContext context) {
-        super(BuiltInFunctionDefinitions.PARSE_JSON, context);
-    }
-
-    public @Nullable Variant eval(@Nullable StringData jsonStr) {
-        return eval(jsonStr, false);
-    }
-
-    /** A {@code NULL} {@code allowDuplicateKeys} is treated as {@code false}. 
*/
-    public @Nullable Variant eval(
-            @Nullable StringData jsonStr, @Nullable Boolean 
allowDuplicateKeys) {
-        if (jsonStr == null) {
-            return null;
-        }
-
-        final byte[] bytes = jsonStr.toBytes();
-        try {
-            return BinaryVariantInternalBuilder.parseJson(
-                    bytes, Boolean.TRUE.equals(allowDuplicateKeys));
-        } catch (Throwable e) {
-            final int invalidIndex =
-                    StringUtf8Utils.firstInvalidUtf8ByteIndex(bytes, 0, 
bytes.length);
-            if (invalidIndex >= 0) {
-                throw new TableRuntimeException(
-                        String.format(
-                                "Failed to parse JSON string: Invalid UTF-8 
byte at index %d of %d.",
-                                invalidIndex, bytes.length),
-                        e);
-            }
-            throw new TableRuntimeException(
-                    String.format("Failed to parse JSON string: %s", jsonStr), 
e);
-        }
-    }
-}
diff --git 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/TryParseJsonFunction.java
 
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/TryParseJsonFunction.java
deleted file mode 100644
index c3e4dc66bef..00000000000
--- 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/TryParseJsonFunction.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.flink.table.runtime.functions.scalar;
-
-import org.apache.flink.table.data.StringData;
-import org.apache.flink.table.functions.BuiltInFunctionDefinitions;
-import org.apache.flink.table.functions.SpecializedFunction;
-import org.apache.flink.types.variant.BinaryVariantInternalBuilder;
-import org.apache.flink.types.variant.Variant;
-
-import javax.annotation.Nullable;
-
-/** Implementation of {@link BuiltInFunctionDefinitions#TRY_PARSE_JSON}. */
-public class TryParseJsonFunction extends BuiltInScalarFunction {
-
-    public TryParseJsonFunction(SpecializedFunction.SpecializedContext 
context) {
-        super(BuiltInFunctionDefinitions.TRY_PARSE_JSON, context);
-    }
-
-    public @Nullable Variant eval(@Nullable StringData jsonStr) {
-        return eval(jsonStr, false);
-    }
-
-    /** A {@code NULL} {@code allowDuplicateKeys} is treated as {@code false}. 
*/
-    public @Nullable Variant eval(
-            @Nullable StringData jsonStr, @Nullable Boolean 
allowDuplicateKeys) {
-        if (jsonStr == null) {
-            return null;
-        }
-
-        try {
-            return BinaryVariantInternalBuilder.parseJson(
-                    jsonStr.toBytes(), 
Boolean.TRUE.equals(allowDuplicateKeys));
-        } catch (Throwable e) {
-            return null;
-        }
-    }
-}

Reply via email to