This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/calcite.git
commit 1cc1eb3a477ce7687436024b4760cf1f04a80cbc Author: xiaogang <[email protected]> AuthorDate: Tue Sep 19 16:27:15 2023 +0800 [CALCITE-5995] JSON_VALUE, JSON_EXISTS, JSON_QUERY functions should cache generated objects between calls Close apache/calcite#3432 --- .../calcite/adapter/enumerable/RexImpTable.java | 18 +- .../org/apache/calcite/runtime/JsonFunctions.java | 399 +++++++++++---------- .../org/apache/calcite/runtime/SqlFunctions.java | 5 +- .../org/apache/calcite/util/BuiltInMethod.java | 16 +- .../apache/calcite/test/SqlJsonFunctionsTest.java | 121 ++++--- 5 files changed, 299 insertions(+), 260 deletions(-) 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 de84ea7d71..8cd68d859e 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 @@ -905,10 +905,11 @@ public class RexImpTable { BuiltInMethod.JSON_VALUE_EXPRESSION.method, NullPolicy.STRICT); defineMethod(JSON_TYPE_OPERATOR, BuiltInMethod.JSON_VALUE_EXPRESSION.method, NullPolicy.STRICT); - defineMethod(JSON_EXISTS, BuiltInMethod.JSON_EXISTS.method, NullPolicy.ARG0); + defineReflective(JSON_EXISTS, BuiltInMethod.JSON_EXISTS2.method, + BuiltInMethod.JSON_EXISTS3.method); map.put(JSON_VALUE, new JsonValueImplementor(BuiltInMethod.JSON_VALUE.method)); - defineMethod(JSON_QUERY, BuiltInMethod.JSON_QUERY.method, NullPolicy.ARG0); + defineReflective(JSON_QUERY, BuiltInMethod.JSON_QUERY.method); defineMethod(JSON_TYPE, BuiltInMethod.JSON_TYPE.method, NullPolicy.ARG0); defineMethod(JSON_DEPTH, BuiltInMethod.JSON_DEPTH.method, NullPolicy.ARG0); defineMethod(JSON_INSERT, BuiltInMethod.JSON_INSERT.method, NullPolicy.ARG0); @@ -2778,7 +2779,6 @@ public class RexImpTable { @Override Expression implementSafe(RexToLixTranslator translator, RexCall call, List<Expression> argValueList) { - final Expression expression; final List<Expression> newOperands = new ArrayList<>(); newOperands.add(argValueList.get(0)); newOperands.add(argValueList.get(1)); @@ -2821,13 +2821,11 @@ public class RexImpTable { newOperands.add(defaultValueOnEmpty); newOperands.add(errorBehavior); newOperands.add(defaultValueOnError); - @SuppressWarnings("rawtypes") - final Class clazz = method.getDeclaringClass(); - expression = EnumUtils.call(null, clazz, method.getName(), newOperands); - - final Type returnType = - translator.typeFactory.getJavaClass(call.getType()); - return EnumUtils.convert(expression, returnType); + List<Expression> argValueList0 = + EnumUtils.fromInternal(method.getParameterTypes(), newOperands); + final Expression target = + Expressions.new_(method.getDeclaringClass()); + return Expressions.call(target, method, argValueList0); } } diff --git a/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java b/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java index c65f70c1f6..6cd9258633 100644 --- a/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.runtime; +import org.apache.calcite.linq4j.function.Deterministic; import org.apache.calcite.sql.SqlJsonConstructorNullClause; import org.apache.calcite.sql.SqlJsonExistsErrorBehavior; import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior; @@ -27,6 +28,9 @@ import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.core.PrettyPrinter; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.InvalidPathException; @@ -55,6 +59,7 @@ import java.util.Queue; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.apache.calcite.config.CalciteSystemProperty.FUNCTION_LEVEL_CACHE_MAX_SIZE; import static org.apache.calcite.linq4j.Nullness.castNonNull; import static org.apache.calcite.util.Static.RESOURCE; @@ -170,212 +175,232 @@ public class JsonFunctions { } } - public static @Nullable Boolean jsonExists(String input, String pathSpec) { - return jsonExists(jsonApiCommonSyntax(input, pathSpec)); - } - public static @Nullable Boolean jsonExists(String input, String pathSpec, - SqlJsonExistsErrorBehavior errorBehavior) { - return jsonExists(jsonApiCommonSyntax(input, pathSpec), errorBehavior); - } + /** State for {@code JSON_EXISTS}, {@code JSON_VALUE}, {@code JSON_QUERY}. + * + * <p>Marked deterministic so that the code generator instantiates one once + * per query, not once per row. */ + @Deterministic + public static class StatefulFunction { + private final LoadingCache<String, JsonValueContext> cache = + CacheBuilder.newBuilder() + .maximumSize(FUNCTION_LEVEL_CACHE_MAX_SIZE.value()) + .build(CacheLoader.from(JsonFunctions::jsonValueExpression)); - public static @Nullable Boolean jsonExists(JsonValueContext input, String pathSpec) { - return jsonExists(jsonApiCommonSyntax(input, pathSpec)); - } + public JsonPathContext jsonApiCommonSyntaxWithCache(String input, + String pathSpec) { + return jsonApiCommonSyntax(cache.getUnchecked(input), pathSpec); + } - public static @Nullable Boolean jsonExists(JsonValueContext input, String pathSpec, - SqlJsonExistsErrorBehavior errorBehavior) { - return jsonExists(jsonApiCommonSyntax(input, pathSpec), errorBehavior); - } + public @Nullable Boolean jsonExists(String input, String pathSpec) { + return jsonExists(jsonApiCommonSyntaxWithCache(input, pathSpec)); + } - public static @Nullable Boolean jsonExists(JsonPathContext context) { - return jsonExists(context, SqlJsonExistsErrorBehavior.FALSE); - } + public @Nullable Boolean jsonExists(String input, String pathSpec, + SqlJsonExistsErrorBehavior errorBehavior) { + return jsonExists(jsonApiCommonSyntaxWithCache(input, pathSpec), + errorBehavior); + } - public static @Nullable Boolean jsonExists(JsonPathContext context, - SqlJsonExistsErrorBehavior errorBehavior) { - if (context.hasException()) { - switch (errorBehavior) { - case TRUE: - return Boolean.TRUE; - case FALSE: - return Boolean.FALSE; - case ERROR: - throw toUnchecked(context.exc); - case UNKNOWN: - return null; - default: - throw RESOURCE.illegalErrorBehaviorInJsonExistsFunc( - errorBehavior.toString()).ex(); - } - } else { - return context.obj != null; - } - } - - public static @Nullable Object jsonValue(String input, - String pathSpec, - SqlJsonValueEmptyOrErrorBehavior emptyBehavior, - Object defaultValueOnEmpty, - SqlJsonValueEmptyOrErrorBehavior errorBehavior, - Object defaultValueOnError) { - return jsonValue( - jsonApiCommonSyntax(input, pathSpec), - emptyBehavior, - defaultValueOnEmpty, - errorBehavior, - defaultValueOnError); - } - - public static @Nullable Object jsonValue(JsonValueContext input, - String pathSpec, - SqlJsonValueEmptyOrErrorBehavior emptyBehavior, - Object defaultValueOnEmpty, - SqlJsonValueEmptyOrErrorBehavior errorBehavior, - Object defaultValueOnError) { - return jsonValue( - jsonApiCommonSyntax(input, pathSpec), - emptyBehavior, - defaultValueOnEmpty, - errorBehavior, - defaultValueOnError); - } - - public static @Nullable Object jsonValue(JsonPathContext context, - SqlJsonValueEmptyOrErrorBehavior emptyBehavior, - Object defaultValueOnEmpty, - SqlJsonValueEmptyOrErrorBehavior errorBehavior, - Object defaultValueOnError) { - final Exception exc; - if (context.hasException()) { - exc = context.exc; - } else { - Object value = context.obj; - if (value == null || context.mode == PathMode.LAX - && !isScalarObject(value)) { - switch (emptyBehavior) { + public @Nullable Boolean jsonExists(JsonValueContext input, + String pathSpec) { + return jsonExists(jsonApiCommonSyntax(input, pathSpec)); + } + + public @Nullable Boolean jsonExists(JsonValueContext input, String pathSpec, + SqlJsonExistsErrorBehavior errorBehavior) { + return jsonExists(jsonApiCommonSyntax(input, pathSpec), errorBehavior); + } + + public @Nullable Boolean jsonExists(JsonPathContext context) { + return jsonExists(context, SqlJsonExistsErrorBehavior.FALSE); + } + + public @Nullable Boolean jsonExists(JsonPathContext context, + SqlJsonExistsErrorBehavior errorBehavior) { + if (context.hasException()) { + switch (errorBehavior) { + case TRUE: + return Boolean.TRUE; + case FALSE: + return Boolean.FALSE; case ERROR: - throw RESOURCE.emptyResultOfJsonValueFuncNotAllowed().ex(); - case NULL: + throw toUnchecked(context.exc); + case UNKNOWN: return null; - case DEFAULT: - return defaultValueOnEmpty; default: - throw RESOURCE.illegalEmptyBehaviorInJsonValueFunc( - emptyBehavior.toString()).ex(); + throw RESOURCE.illegalErrorBehaviorInJsonExistsFunc( + errorBehavior.toString()).ex(); } - } else if (context.mode == PathMode.STRICT - && !isScalarObject(value)) { - exc = - RESOURCE.scalarValueRequiredInStrictModeOfJsonValueFunc( - value.toString()).ex(); } else { - return value; + return context.obj != null; } } - switch (errorBehavior) { - case ERROR: - throw toUnchecked(exc); - case NULL: - return null; - case DEFAULT: - return defaultValueOnError; - default: - throw RESOURCE.illegalErrorBehaviorInJsonValueFunc( - errorBehavior.toString()).ex(); - } - } - - public static @Nullable String jsonQuery(String input, - String pathSpec, - SqlJsonQueryWrapperBehavior wrapperBehavior, - SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, - SqlJsonQueryEmptyOrErrorBehavior errorBehavior) { - return jsonQuery( - jsonApiCommonSyntax(input, pathSpec), - wrapperBehavior, emptyBehavior, errorBehavior); - } - - public static @Nullable String jsonQuery(JsonValueContext input, - String pathSpec, - SqlJsonQueryWrapperBehavior wrapperBehavior, - SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, - SqlJsonQueryEmptyOrErrorBehavior errorBehavior) { - return jsonQuery( - jsonApiCommonSyntax(input, pathSpec), - wrapperBehavior, emptyBehavior, errorBehavior); - } - - public static @Nullable String jsonQuery(JsonPathContext context, - SqlJsonQueryWrapperBehavior wrapperBehavior, - SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, - SqlJsonQueryEmptyOrErrorBehavior errorBehavior) { - final Exception exc; - if (context.hasException()) { - exc = context.exc; - } else { - Object value; - if (context.obj == null) { - value = null; + + public @Nullable Object jsonValue(String input, + String pathSpec, + SqlJsonValueEmptyOrErrorBehavior emptyBehavior, + Object defaultValueOnEmpty, + SqlJsonValueEmptyOrErrorBehavior errorBehavior, + Object defaultValueOnError) { + return jsonValue( + jsonApiCommonSyntaxWithCache(input, pathSpec), + emptyBehavior, + defaultValueOnEmpty, + errorBehavior, + defaultValueOnError); + } + + public @Nullable Object jsonValue(JsonValueContext input, + String pathSpec, + SqlJsonValueEmptyOrErrorBehavior emptyBehavior, + Object defaultValueOnEmpty, + SqlJsonValueEmptyOrErrorBehavior errorBehavior, + Object defaultValueOnError) { + return jsonValue( + jsonApiCommonSyntax(input, pathSpec), + emptyBehavior, + defaultValueOnEmpty, + errorBehavior, + defaultValueOnError); + } + + public @Nullable Object jsonValue(JsonPathContext context, + SqlJsonValueEmptyOrErrorBehavior emptyBehavior, + Object defaultValueOnEmpty, + SqlJsonValueEmptyOrErrorBehavior errorBehavior, + Object defaultValueOnError) { + final Exception exc; + if (context.hasException()) { + exc = context.exc; } else { - switch (wrapperBehavior) { - case WITHOUT_ARRAY: - value = context.obj; - break; - case WITH_UNCONDITIONAL_ARRAY: - value = Collections.singletonList(context.obj); - break; - case WITH_CONDITIONAL_ARRAY: - if (context.obj instanceof Collection) { - value = context.obj; - } else { - value = Collections.singletonList(context.obj); + Object value = context.obj; + if (value == null || context.mode == PathMode.LAX + && !isScalarObject(value)) { + switch (emptyBehavior) { + case ERROR: + throw RESOURCE.emptyResultOfJsonValueFuncNotAllowed().ex(); + case NULL: + return null; + case DEFAULT: + return defaultValueOnEmpty; + default: + throw RESOURCE.illegalEmptyBehaviorInJsonValueFunc( + emptyBehavior.toString()).ex(); } - break; - default: - throw RESOURCE.illegalWrapperBehaviorInJsonQueryFunc( - wrapperBehavior.toString()).ex(); + } else if (context.mode == PathMode.STRICT + && !isScalarObject(value)) { + exc = + RESOURCE.scalarValueRequiredInStrictModeOfJsonValueFunc( + value.toString()).ex(); + } else { + return value; } } - if (value == null || context.mode == PathMode.LAX - && isScalarObject(value)) { - switch (emptyBehavior) { - case ERROR: - throw RESOURCE.emptyResultOfJsonQueryFuncNotAllowed().ex(); - case NULL: - return null; - case EMPTY_ARRAY: - return "[]"; - case EMPTY_OBJECT: - return "{}"; - default: - throw RESOURCE.illegalEmptyBehaviorInJsonQueryFunc( - emptyBehavior.toString()).ex(); - } - } else if (context.mode == PathMode.STRICT && isScalarObject(value)) { - exc = - RESOURCE.arrayOrObjectValueRequiredInStrictModeOfJsonQueryFunc( - value.toString()).ex(); + switch (errorBehavior) { + case ERROR: + throw toUnchecked(exc); + case NULL: + return null; + case DEFAULT: + return defaultValueOnError; + default: + throw RESOURCE.illegalErrorBehaviorInJsonValueFunc( + errorBehavior.toString()).ex(); + } + } + + public @Nullable String jsonQuery(String input, + String pathSpec, + SqlJsonQueryWrapperBehavior wrapperBehavior, + SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, + SqlJsonQueryEmptyOrErrorBehavior errorBehavior) { + return jsonQuery( + jsonApiCommonSyntaxWithCache(input, pathSpec), + wrapperBehavior, emptyBehavior, errorBehavior); + } + + public @Nullable String jsonQuery(JsonValueContext input, + String pathSpec, + SqlJsonQueryWrapperBehavior wrapperBehavior, + SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, + SqlJsonQueryEmptyOrErrorBehavior errorBehavior) { + return jsonQuery( + jsonApiCommonSyntax(input, pathSpec), + wrapperBehavior, emptyBehavior, errorBehavior); + } + + public @Nullable String jsonQuery(JsonPathContext context, + SqlJsonQueryWrapperBehavior wrapperBehavior, + SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, + SqlJsonQueryEmptyOrErrorBehavior errorBehavior) { + final Exception exc; + if (context.hasException()) { + exc = context.exc; } else { - try { - return jsonize(value); - } catch (Exception e) { - exc = e; + Object value; + if (context.obj == null) { + value = null; + } else { + switch (wrapperBehavior) { + case WITHOUT_ARRAY: + value = context.obj; + break; + case WITH_UNCONDITIONAL_ARRAY: + value = Collections.singletonList(context.obj); + break; + case WITH_CONDITIONAL_ARRAY: + if (context.obj instanceof Collection) { + value = context.obj; + } else { + value = Collections.singletonList(context.obj); + } + break; + default: + throw RESOURCE.illegalWrapperBehaviorInJsonQueryFunc( + wrapperBehavior.toString()).ex(); + } + } + if (value == null || context.mode == PathMode.LAX + && isScalarObject(value)) { + switch (emptyBehavior) { + case ERROR: + throw RESOURCE.emptyResultOfJsonQueryFuncNotAllowed().ex(); + case NULL: + return null; + case EMPTY_ARRAY: + return "[]"; + case EMPTY_OBJECT: + return "{}"; + default: + throw RESOURCE.illegalEmptyBehaviorInJsonQueryFunc( + emptyBehavior.toString()).ex(); + } + } else if (context.mode == PathMode.STRICT && isScalarObject(value)) { + exc = + RESOURCE.arrayOrObjectValueRequiredInStrictModeOfJsonQueryFunc( + value.toString()).ex(); + } else { + try { + return jsonize(value); + } catch (Exception e) { + exc = e; + } } } - } - switch (errorBehavior) { - case ERROR: - throw toUnchecked(exc); - case NULL: - return null; - case EMPTY_ARRAY: - return "[]"; - case EMPTY_OBJECT: - return "{}"; - default: - throw RESOURCE.illegalErrorBehaviorInJsonQueryFunc( - errorBehavior.toString()).ex(); + switch (errorBehavior) { + case ERROR: + throw toUnchecked(exc); + case NULL: + return null; + case EMPTY_ARRAY: + return "[]"; + case EMPTY_OBJECT: + return "{}"; + default: + throw RESOURCE.illegalErrorBehaviorInJsonQueryFunc( + errorBehavior.toString()).ex(); + } } } 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 1b659d6cb3..fc163fc659 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java @@ -406,7 +406,8 @@ public class SqlFunctions { } /** Helper for multiple capturing group regex check in REGEXP_* fns. */ - private void checkMultipleCapturingGroupsInRegex(Matcher matcher, String methodName) { + private static void checkMultipleCapturingGroupsInRegex(Matcher matcher, + String methodName) { if (matcher.groupCount() > 1) { throw RESOURCE.multipleCapturingGroupsForRegexpExtract( Integer.toString(matcher.groupCount()), methodName).ex(); @@ -416,7 +417,7 @@ public class SqlFunctions { /** Helper for checking values of position and occurrence arguments in REGEXP_* fns. * Regex fns not using occurrencePosition param pass a default value of 0. * Throws an exception or returns true in case of failed value checks. */ - private boolean checkPosOccurrenceParamValues(int position, + private static boolean checkPosOccurrenceParamValues(int position, int occurrence, int occurrencePosition, String value, String methodName) { if (position <= 0) { throw RESOURCE.invalidIntegerInputForRegexpFunctions(Integer.toString(position), 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 4652ca6bab..fc040dac70 100644 --- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java +++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java @@ -110,6 +110,7 @@ import org.apache.calcite.schema.Schemas; import org.apache.calcite.schema.Table; import org.apache.calcite.sql.SqlExplainLevel; import org.apache.calcite.sql.SqlJsonConstructorNullClause; +import org.apache.calcite.sql.SqlJsonExistsErrorBehavior; import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior; import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior; import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior; @@ -395,13 +396,18 @@ public enum BuiltInMethod { String.class), JSON_API_COMMON_SYNTAX(JsonFunctions.class, "jsonApiCommonSyntax", String.class, String.class), - JSON_EXISTS(JsonFunctions.class, "jsonExists", String.class, String.class), - JSON_VALUE(JsonFunctions.class, "jsonValue", String.class, String.class, + JSON_API_COMMON_SYNTAX_WITH_CACHE(JsonFunctions.StatefulFunction.class, + "jsonApiCommonSyntaxWithCache", String.class, String.class), + JSON_EXISTS2(JsonFunctions.StatefulFunction.class, "jsonExists", + String.class, String.class), + JSON_EXISTS3(JsonFunctions.StatefulFunction.class, "jsonExists", + String.class, String.class, SqlJsonExistsErrorBehavior.class), + JSON_VALUE(JsonFunctions.StatefulFunction.class, "jsonValue", + String.class, String.class, SqlJsonValueEmptyOrErrorBehavior.class, Object.class, SqlJsonValueEmptyOrErrorBehavior.class, Object.class), - JSON_QUERY(JsonFunctions.class, "jsonQuery", String.class, - String.class, - SqlJsonQueryWrapperBehavior.class, + JSON_QUERY(JsonFunctions.StatefulFunction.class, "jsonQuery", String.class, + String.class, SqlJsonQueryWrapperBehavior.class, SqlJsonQueryEmptyOrErrorBehavior.class, SqlJsonQueryEmptyOrErrorBehavior.class), JSON_OBJECT(JsonFunctions.class, "jsonObject", 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 605b99320f..b69f30d804 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java @@ -631,35 +631,42 @@ class SqlJsonFunctionsTest { private void assertJsonValueExpression(String input, Matcher<? super JsonFunctions.JsonValueContext> matcher) { assertThat( - invocationDesc(BuiltInMethod.JSON_VALUE_EXPRESSION.getMethodName(), input), + invocationDesc(BuiltInMethod.JSON_VALUE_EXPRESSION, input), JsonFunctions.jsonValueExpression(input), matcher); } private void assertJsonApiCommonSyntax(String input, String pathSpec, Matcher<? super JsonFunctions.JsonPathContext> matcher) { assertThat( - invocationDesc(BuiltInMethod.JSON_API_COMMON_SYNTAX.getMethodName(), input, pathSpec), + invocationDesc(BuiltInMethod.JSON_API_COMMON_SYNTAX, input, pathSpec), JsonFunctions.jsonApiCommonSyntax(input, pathSpec), matcher); } - private void assertJsonApiCommonSyntax(JsonFunctions.JsonValueContext input, String pathSpec, - Matcher<? super JsonFunctions.JsonPathContext> matcher) { + private void assertJsonApiCommonSyntax(JsonFunctions.JsonValueContext input, + String pathSpec, Matcher<? super JsonFunctions.JsonPathContext> matcher) { assertThat( - invocationDesc(BuiltInMethod.JSON_API_COMMON_SYNTAX.getMethodName(), input, pathSpec), + invocationDesc(BuiltInMethod.JSON_API_COMMON_SYNTAX, input, pathSpec), JsonFunctions.jsonApiCommonSyntax(input, pathSpec), matcher); } private void assertJsonExists(JsonFunctions.JsonPathContext context, - SqlJsonExistsErrorBehavior errorBehavior, Matcher<? super Boolean> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_EXISTS.getMethodName(), context, errorBehavior), - JsonFunctions.jsonExists(context, errorBehavior), matcher); + SqlJsonExistsErrorBehavior errorBehavior, + Matcher<? super Boolean> matcher) { + final JsonFunctions.StatefulFunction f = + new JsonFunctions.StatefulFunction(); + assertThat( + invocationDesc(BuiltInMethod.JSON_EXISTS2, context, errorBehavior), + f.jsonExists(context, errorBehavior), matcher); } private void assertJsonExistsFailed(JsonFunctions.JsonPathContext context, SqlJsonExistsErrorBehavior errorBehavior, Matcher<? super Throwable> matcher) { - assertFailed(invocationDesc(BuiltInMethod.JSON_EXISTS.getMethodName(), context, errorBehavior), - () -> JsonFunctions.jsonExists( + final JsonFunctions.StatefulFunction f = + new JsonFunctions.StatefulFunction(); + assertFailed( + invocationDesc(BuiltInMethod.JSON_EXISTS2, context, errorBehavior), + () -> f.jsonExists( context, errorBehavior), matcher); } @@ -669,10 +676,12 @@ class SqlJsonFunctionsTest { SqlJsonValueEmptyOrErrorBehavior errorBehavior, Object defaultValueOnError, Matcher<Object> matcher) { + final JsonFunctions.StatefulFunction f = + new JsonFunctions.StatefulFunction(); assertThat( - invocationDesc(BuiltInMethod.JSON_VALUE.getMethodName(), context, emptyBehavior, + invocationDesc(BuiltInMethod.JSON_VALUE, context, emptyBehavior, defaultValueOnEmpty, errorBehavior, defaultValueOnError), - JsonFunctions.jsonValue(context, emptyBehavior, defaultValueOnEmpty, + f.jsonValue(context, emptyBehavior, defaultValueOnEmpty, errorBehavior, defaultValueOnError), matcher); } @@ -683,10 +692,12 @@ class SqlJsonFunctionsTest { SqlJsonValueEmptyOrErrorBehavior errorBehavior, Object defaultValueOnError, Matcher<? super Throwable> matcher) { + final JsonFunctions.StatefulFunction f = + new JsonFunctions.StatefulFunction(); assertFailed( - invocationDesc(BuiltInMethod.JSON_VALUE.getMethodName(), input, emptyBehavior, + invocationDesc(BuiltInMethod.JSON_VALUE, input, emptyBehavior, defaultValueOnEmpty, errorBehavior, defaultValueOnError), - () -> JsonFunctions.jsonValue(input, emptyBehavior, + () -> f.jsonValue(input, emptyBehavior, defaultValueOnEmpty, errorBehavior, defaultValueOnError), matcher); } @@ -696,10 +707,12 @@ class SqlJsonFunctionsTest { SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, SqlJsonQueryEmptyOrErrorBehavior errorBehavior, Matcher<? super String> matcher) { + final JsonFunctions.StatefulFunction f = + new JsonFunctions.StatefulFunction(); assertThat( - invocationDesc(BuiltInMethod.JSON_QUERY.getMethodName(), input, wrapperBehavior, + invocationDesc(BuiltInMethod.JSON_QUERY, input, wrapperBehavior, emptyBehavior, errorBehavior), - JsonFunctions.jsonQuery(input, wrapperBehavior, emptyBehavior, + f.jsonQuery(input, wrapperBehavior, emptyBehavior, errorBehavior), matcher); } @@ -709,90 +722,89 @@ class SqlJsonFunctionsTest { SqlJsonQueryEmptyOrErrorBehavior emptyBehavior, SqlJsonQueryEmptyOrErrorBehavior errorBehavior, Matcher<? super Throwable> matcher) { + final JsonFunctions.StatefulFunction f = + new JsonFunctions.StatefulFunction(); assertFailed( - invocationDesc(BuiltInMethod.JSON_QUERY.getMethodName(), input, wrapperBehavior, + invocationDesc(BuiltInMethod.JSON_QUERY, input, wrapperBehavior, emptyBehavior, errorBehavior), - () -> JsonFunctions.jsonQuery(input, wrapperBehavior, emptyBehavior, + () -> f.jsonQuery(input, wrapperBehavior, emptyBehavior, errorBehavior), matcher); } private void assertJsonize(Object input, Matcher<? super String> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSONIZE.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSONIZE, input), JsonFunctions.jsonize(input), matcher); } private void assertJsonPretty(JsonFunctions.JsonValueContext input, Matcher<? super String> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_PRETTY.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSON_PRETTY, input), JsonFunctions.jsonPretty(input), matcher); } private void assertJsonPrettyFailed(JsonFunctions.JsonValueContext input, Matcher<? super Throwable> matcher) { - assertFailed(invocationDesc(BuiltInMethod.JSON_PRETTY.getMethodName(), input), + assertFailed(invocationDesc(BuiltInMethod.JSON_PRETTY, input), () -> JsonFunctions.jsonPretty(input), matcher); } private void assertJsonLength(JsonFunctions.JsonPathContext input, Matcher<? super Integer> matcher) { - assertThat( - invocationDesc(BuiltInMethod.JSON_LENGTH.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSON_LENGTH, input), JsonFunctions.jsonLength(input), matcher); } private void assertJsonLengthFailed(JsonFunctions.JsonValueContext input, Matcher<? super Throwable> matcher) { - assertFailed( - invocationDesc(BuiltInMethod.JSON_LENGTH.getMethodName(), input), + assertFailed(invocationDesc(BuiltInMethod.JSON_LENGTH, input), () -> JsonFunctions.jsonLength(input), matcher); } private void assertJsonKeys(JsonFunctions.JsonPathContext input, Matcher<? super String> matcher) { - assertThat( - invocationDesc(BuiltInMethod.JSON_KEYS.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSON_KEYS, input), JsonFunctions.jsonKeys(input), matcher); } private void assertJsonKeysFailed(JsonFunctions.JsonValueContext input, Matcher<? super Throwable> matcher) { - assertFailed(invocationDesc(BuiltInMethod.JSON_KEYS.getMethodName(), input), + assertFailed(invocationDesc(BuiltInMethod.JSON_KEYS, input), () -> JsonFunctions.jsonKeys(input), matcher); } - private void assertJsonRemove(JsonFunctions.JsonValueContext input, String[] pathSpecs, - Matcher<? super String> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_REMOVE.getMethodName(), input, pathSpecs), + private void assertJsonRemove(JsonFunctions.JsonValueContext input, + String[] pathSpecs, Matcher<? super String> matcher) { + assertThat(invocationDesc(BuiltInMethod.JSON_REMOVE, input, pathSpecs), JsonFunctions.jsonRemove(input, pathSpecs), matcher); } private void assertJsonStorageSize(String input, Matcher<? super Integer> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_STORAGE_SIZE.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSON_STORAGE_SIZE, input), JsonFunctions.jsonStorageSize(input), matcher); } private void assertJsonStorageSize(JsonFunctions.JsonValueContext input, Matcher<? super Integer> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_STORAGE_SIZE.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSON_STORAGE_SIZE, input), JsonFunctions.jsonStorageSize(input), matcher); } private void assertJsonStorageSizeFailed(String input, Matcher<? super Throwable> matcher) { - assertFailed(invocationDesc(BuiltInMethod.JSON_STORAGE_SIZE.getMethodName(), input), + assertFailed(invocationDesc(BuiltInMethod.JSON_STORAGE_SIZE, input), () -> JsonFunctions.jsonStorageSize(input), matcher); } @@ -800,7 +812,7 @@ class SqlJsonFunctionsTest { private void assertJsonInsert(JsonFunctions.JsonValueContext jsonDoc, Object[] kvs, Matcher<? super String> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_INSERT.getMethodName(), jsonDoc, kvs), + assertThat(invocationDesc(BuiltInMethod.JSON_INSERT, jsonDoc, kvs), JsonFunctions.jsonInsert(jsonDoc, kvs), matcher); } @@ -808,7 +820,7 @@ class SqlJsonFunctionsTest { private void assertJsonReplace(JsonFunctions.JsonValueContext jsonDoc, Object[] kvs, Matcher<? super String> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_REPLACE.getMethodName(), jsonDoc, kvs), + assertThat(invocationDesc(BuiltInMethod.JSON_REPLACE, jsonDoc, kvs), JsonFunctions.jsonReplace(jsonDoc, kvs), matcher); } @@ -816,21 +828,21 @@ class SqlJsonFunctionsTest { private void assertJsonSet(JsonFunctions.JsonValueContext jsonDoc, Object[] kvs, Matcher<? super String> matcher) { - assertThat(invocationDesc(BuiltInMethod.JSON_SET.getMethodName(), jsonDoc, kvs), + assertThat(invocationDesc(BuiltInMethod.JSON_SET, jsonDoc, kvs), JsonFunctions.jsonSet(jsonDoc, kvs), matcher); } private void assertDejsonize(String input, Matcher<Object> matcher) { - assertThat(invocationDesc(BuiltInMethod.DEJSONIZE.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.DEJSONIZE, input), JsonFunctions.dejsonize(input), matcher); } private void assertDejsonizeFailed(String input, Matcher<? super Throwable> matcher) { - assertFailed(invocationDesc(BuiltInMethod.DEJSONIZE.getMethodName(), input), + assertFailed(invocationDesc(BuiltInMethod.DEJSONIZE, input), () -> JsonFunctions.dejsonize(input), matcher); } @@ -838,23 +850,21 @@ class SqlJsonFunctionsTest { private void assertJsonObject(Matcher<? super String> matcher, SqlJsonConstructorNullClause nullClause, Object... kvs) { - assertThat(invocationDesc(BuiltInMethod.JSON_OBJECT.getMethodName(), nullClause, kvs), + assertThat(invocationDesc(BuiltInMethod.JSON_OBJECT, nullClause, kvs), JsonFunctions.jsonObject(nullClause, kvs), matcher); } private void assertJsonType(Matcher<? super String> matcher, String input) { - assertThat( - invocationDesc(BuiltInMethod.JSON_TYPE.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSON_TYPE, input), JsonFunctions.jsonType(input), matcher); } private void assertJsonDepth(Matcher<? super Integer> matcher, String input) { - assertThat( - invocationDesc(BuiltInMethod.JSON_DEPTH.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.JSON_DEPTH, input), JsonFunctions.jsonDepth(input), matcher); } @@ -864,13 +874,13 @@ class SqlJsonFunctionsTest { Matcher<? super Map> matcher) { JsonFunctions.jsonObjectAggAdd(map, k, v, nullClause); assertThat( - invocationDesc(BuiltInMethod.JSON_ARRAYAGG_ADD.getMethodName(), map, k, v, nullClause), + invocationDesc(BuiltInMethod.JSON_ARRAYAGG_ADD, map, k, v, nullClause), map, matcher); } private void assertJsonArray(Matcher<? super String> matcher, SqlJsonConstructorNullClause nullClause, Object... elements) { - assertThat(invocationDesc(BuiltInMethod.JSON_ARRAY.getMethodName(), nullClause, elements), + assertThat(invocationDesc(BuiltInMethod.JSON_ARRAY, nullClause, elements), JsonFunctions.jsonArray(nullClause, elements), matcher); } @@ -880,44 +890,43 @@ class SqlJsonFunctionsTest { Matcher<? super List> matcher) { JsonFunctions.jsonArrayAggAdd(list, element, nullClause); assertThat( - invocationDesc(BuiltInMethod.JSON_ARRAYAGG_ADD.getMethodName(), list, element, + invocationDesc(BuiltInMethod.JSON_ARRAYAGG_ADD, list, element, nullClause), list, matcher); } private void assertIsJsonValue(String input, Matcher<? super Boolean> matcher) { - assertThat(invocationDesc(BuiltInMethod.IS_JSON_VALUE.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.IS_JSON_VALUE, input), JsonFunctions.isJsonValue(input), matcher); } private void assertIsJsonScalar(String input, Matcher<? super Boolean> matcher) { - assertThat(invocationDesc(BuiltInMethod.IS_JSON_SCALAR.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.IS_JSON_SCALAR, input), JsonFunctions.isJsonScalar(input), matcher); } private void assertIsJsonArray(String input, Matcher<? super Boolean> matcher) { - assertThat(invocationDesc(BuiltInMethod.IS_JSON_ARRAY.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.IS_JSON_ARRAY, input), JsonFunctions.isJsonArray(input), matcher); } private void assertIsJsonObject(String input, Matcher<? super Boolean> matcher) { - assertThat(invocationDesc(BuiltInMethod.IS_JSON_OBJECT.getMethodName(), input), + assertThat(invocationDesc(BuiltInMethod.IS_JSON_OBJECT, input), JsonFunctions.isJsonObject(input), matcher); } - private String invocationDesc(String methodName, Object... args) { - return methodName + "(" + String.join(", ", - Arrays.stream(args) - .map(Objects::toString) - .collect(Collectors.toList())) + ")"; + private static String invocationDesc(BuiltInMethod method, Object... args) { + return Arrays.stream(args) + .map(Objects::toString) + .collect(Collectors.joining(", ", method.getMethodName() + "(", ")")); } private void assertFailed(String invocationDesc, Supplier<?> supplier,
