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 eff41f769957..776eac2eba99 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 @@ -46,6 +46,8 @@ import org.apache.calcite.rex.RexPatternFieldRef; import org.apache.calcite.rex.RexWindowExclusion; import org.apache.calcite.runtime.FlatLists; +import org.apache.calcite.runtime.ImmutablePairList; +import org.apache.calcite.runtime.PairList; import org.apache.calcite.runtime.SqlFunctions; import org.apache.calcite.schema.FunctionContext; import org.apache.calcite.schema.ImplementableAggFunction; @@ -540,7 +542,7 @@ public class RexImpTable { public static final MemberExpression BOXED_TRUE_EXPR = Expressions.field(null, Boolean.class, "TRUE"); - private final ImmutableMap map; + private final ImmutableMap> map; private final ImmutableMap> aggMap; private final ImmutableMap> winAggMap; private final ImmutableMap> matchMap; @@ -548,7 +550,12 @@ public class RexImpTable { tvfImplementorMap; private RexImpTable(Builder builder) { - this.map = ImmutableMap.copyOf(builder.map); + final ImmutableMap.Builder> + mapBuilder = ImmutableMap.builder(); + builder.map.forEach((k, v) -> { + mapBuilder.put(k, v.immutable()); + }); + this.map = ImmutableMap.copyOf(mapBuilder.build()); this.aggMap = ImmutableMap.copyOf(builder.aggMap); this.winAggMap = ImmutableMap.copyOf(builder.winAggMap); this.matchMap = ImmutableMap.copyOf(builder.matchMap); @@ -839,7 +846,6 @@ void populate1() { new SafeArithmeticImplementor(BuiltInMethod.SAFE_SUBTRACT.method)); define(PI, new PiImplementor()); - populate2(); } /** Second step of population. */ @@ -1267,7 +1273,8 @@ private static Supplier constructorSupplier(Class klass) { /** Holds intermediate state from which a RexImpTable can be constructed. */ private static class Builder extends AbstractBuilder { - private final Map map = new HashMap<>(); + private final Map> map = + new HashMap<>(); private final Map> aggMap = new HashMap<>(); private final Map> winAggMap = @@ -1278,13 +1285,28 @@ private static class Builder extends AbstractBuilder { tvfImplementorMap = new HashMap<>(); @Override protected RexCallImplementor get(SqlOperator operator) { - return requireNonNull(map.get(operator), - () -> "no implementor for " + operator); + final PairList implementors = + requireNonNull(map.get(operator)); + if (implementors.size() == 1) { + return implementors.get(0).getValue(); + } else { + for (Map.Entry entry : implementors) { + if (operator == entry.getKey()) { + return entry.getValue(); + } + } + throw new NullPointerException(); + } } @Override T define(SqlOperator operator, T implementor) { - map.put(operator, requireNonNull(implementor, "implementor")); + if (map.containsKey(operator)) { + map.get(operator).add(operator, implementor); + } else { + map.put(operator, PairList.builder() + .add(operator, implementor).build()); + } return implementor; } @@ -1359,9 +1381,27 @@ private static RexCallImplementor wrapAsRexCallImplementor( ((ImplementableFunction) udf).getImplementor(); return wrapAsRexCallImplementor(implementor); } else if (operator instanceof SqlTypeConstructorFunction) { - return map.get(SqlStdOperatorTable.ROW); + final ImmutablePairList implementors = + map.get(SqlStdOperatorTable.ROW); + if (implementors != null && implementors.size() == 1) { + return implementors.get(0).getValue(); + } + } else { + final ImmutablePairList implementors = + map.get(operator); + if (implementors != null) { + if (implementors.size() == 1) { + return implementors.get(0).getValue(); + } else { + for (Map.Entry entry : implementors) { + if (operator == entry.getKey()) { + return entry.getValue(); + } + } + } + } } - return map.get(operator); + return null; } public @Nullable AggImplementor get(final SqlAggFunction aggregation, diff --git a/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java index 05c1f80c0b6b..ceba36579e08 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlBasicFunction.java @@ -67,7 +67,7 @@ public class SqlBasicFunction extends SqlFunction { * @param category Categorization for function * @param monotonicityInference Strategy to infer monotonicity of a call */ - protected SqlBasicFunction(String name, SqlKind kind, SqlSyntax syntax, + private SqlBasicFunction(String name, SqlKind kind, SqlSyntax syntax, boolean deterministic, SqlReturnTypeInference returnTypeInference, @Nullable SqlOperandTypeInference operandTypeInference, SqlOperandHandler operandHandler, diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java index e882697af691..019342728b7f 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java @@ -42,7 +42,6 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeTransforms; import org.apache.calcite.sql.type.SqlTypeUtil; -import org.apache.calcite.sql.validate.SqlMonotonicity; import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.sql.validate.SqlValidatorUtil; import org.apache.calcite.util.Litmus; @@ -577,10 +576,8 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding, * {@code rep} and returns modified value. */ @LibraryOperator(libraries = {REDSHIFT}) public static final SqlFunction REGEXP_REPLACE_2 = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.STRING_STRING, 0, - SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.STRING_STRING, SqlFunctionCategory.STRING); /** The "REGEXP_REPLACE(value, regexp, rep)" * function. Replaces all substrings of value that match regexp with @@ -596,11 +593,10 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding, * pos. */ @LibraryOperator(libraries = {MYSQL, ORACLE, REDSHIFT}) public static final SqlFunction REGEXP_REPLACE_4 = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, - SqlTypeFamily.STRING, SqlTypeFamily.INTEGER), - 0, SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.STRING, + SqlTypeFamily.INTEGER), + SqlFunctionCategory.STRING); /** The "REGEXP_REPLACE(value, regexp, rep, pos, [ occurrence | matchType ])" * function. Replaces all substrings of value that match regexp with @@ -609,15 +605,13 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding, * is a string of flags to apply to the search. */ @LibraryOperator(libraries = {MYSQL, REDSHIFT}) public static final SqlFunction REGEXP_REPLACE_5 = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, OperandTypes.or( OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER), OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, SqlTypeFamily.STRING)), - 0, SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlFunctionCategory.STRING); /** The "REGEXP_REPLACE(value, regexp, rep, pos, matchType)" * function. Replaces all substrings of value that match regexp with @@ -625,11 +619,10 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding, * pos. Replace only the occurrence match or all matches if occurrence is 0. */ @LibraryOperator(libraries = {ORACLE}) public static final SqlFunction REGEXP_REPLACE_5_ORACLE = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, - SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER), - 0, SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, + SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER), + SqlFunctionCategory.STRING); /** The "REGEXP_REPLACE(value, regexp, rep, pos, occurrence, matchType)" * function. Replaces all substrings of value that match regexp with @@ -638,41 +631,34 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding, * is a string of flags to apply to the search. */ @LibraryOperator(libraries = {MYSQL, ORACLE, REDSHIFT}) public static final SqlFunction REGEXP_REPLACE_6 = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, - SqlTypeFamily.STRING, SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER, SqlTypeFamily.STRING), - 0, SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.STRING, + SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER, SqlTypeFamily.STRING), + SqlFunctionCategory.STRING); /** The "REGEXP_REPLACE(value, regexp, rep)" * function. Replaces all substrings of value that match regexp with * {@code rep} and returns modified value. */ @LibraryOperator(libraries = {BIG_QUERY}) public static final SqlFunction REGEXP_REPLACE_BIG_QUERY_3 = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.STRING_STRING_STRING, 0, - SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.STRING_STRING_STRING, SqlFunctionCategory.STRING); /** The "REGEXP_REPLACE(value, regexp, rep)" * function. Replaces all substrings of value that match regexp with * {@code rep} and returns modified value. */ @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = REDSHIFT) public static final SqlFunction REGEXP_REPLACE_PG_3 = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.STRING_STRING_STRING, 0, - SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.STRING_STRING_STRING, SqlFunctionCategory.STRING); /** The "REGEXP_REPLACE(value, regexp, rep, flags)" * function. Replaces all substrings of value that match regexp with * {@code rep} and returns modified value. flags are applied to the search. */ @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = REDSHIFT) public static final SqlFunction REGEXP_REPLACE_PG_4 = - new SqlBasicFunction("REGEXP_REPLACE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.STRING_STRING_STRING_STRING, 0, - SqlFunctionCategory.STRING, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("REGEXP_REPLACE", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.STRING_STRING_STRING_STRING, SqlFunctionCategory.STRING); /** The "REGEXP_SUBSTR(value, regexp[, position[, occurrence]])" function. * Returns the substring in value that matches the regexp. Returns NULL if there is no match. */ @@ -1875,10 +1861,8 @@ private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding opBinding * converts {@code timestamp} to string according to the given {@code format}. */ @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = {REDSHIFT}) public static final SqlFunction TO_CHAR_PG = - new SqlBasicFunction("TO_CHAR", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.VARCHAR_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.TIMESTAMP_STRING, 0, - SqlFunctionCategory.TIMEDATE, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("TO_CHAR", ReturnTypes.VARCHAR_NULLABLE, + OperandTypes.TIMESTAMP_STRING, SqlFunctionCategory.TIMEDATE); /** The "TO_DATE(string1, string2)" function; casts string1 * to a DATE using the format specified in string2. */ @@ -1893,10 +1877,8 @@ private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding opBinding * to a DATE using the format specified in string2. */ @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = {REDSHIFT}) public static final SqlFunction TO_DATE_PG = - new SqlBasicFunction("TO_DATE", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.DATE_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.STRING_STRING, 0, - SqlFunctionCategory.TIMEDATE, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("TO_DATE", ReturnTypes.DATE_NULLABLE, + OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE); /** The "TO_TIMESTAMP(string1, string2)" function; casts string1 * to a TIMESTAMP using the format specified in string2. */ @@ -1911,10 +1893,8 @@ private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding opBinding * to a TIMESTAMP using the format specified in string2. */ @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = {REDSHIFT}) public static final SqlFunction TO_TIMESTAMP_PG = - new SqlBasicFunction("TO_TIMESTAMP", SqlKind.OTHER_FUNCTION, - SqlSyntax.FUNCTION, true, ReturnTypes.TIMESTAMP_TZ_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.STRING_STRING, 0, - SqlFunctionCategory.TIMEDATE, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("TO_TIMESTAMP", ReturnTypes.TIMESTAMP_TZ_NULLABLE, + OperandTypes.STRING_STRING, SqlFunctionCategory.TIMEDATE); /** * The "PARSE_TIME(string, string)" function (BigQuery); @@ -2512,10 +2492,8 @@ private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding opBinding * to base numeric1.*/ @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = {REDSHIFT}) public static final SqlFunction LOG_POSTGRES = - new SqlBasicFunction("LOG", SqlKind.LOG, - SqlSyntax.FUNCTION, true, ReturnTypes.DOUBLE_NULLABLE, null, - OperandHandlers.DEFAULT, OperandTypes.NUMERIC_OPTIONAL_NUMERIC, 0, - SqlFunctionCategory.NUMERIC, call -> SqlMonotonicity.NOT_MONOTONIC, false) { }; + SqlBasicFunction.create("LOG", ReturnTypes.DOUBLE_NULLABLE, + OperandTypes.NUMERIC_OPTIONAL_NUMERIC, SqlFunctionCategory.NUMERIC); /** The "LOG2(numeric)" function. Returns the base 2 logarithm of numeric. */ @LibraryOperator(libraries = {MYSQL, SPARK})