[CALCITE-941] Named, optional and DEFAULT arguments to function calls
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/d012b245 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/d012b245 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/d012b245 Branch: refs/heads/master Commit: d012b245e4e040ae94bb3d7045cf4d71af8ac279 Parents: 4c3eb7c Author: Julian Hyde <[email protected]> Authored: Tue Oct 27 22:04:05 2015 -0700 Committer: Julian Hyde <[email protected]> Committed: Sat Oct 31 15:09:05 2015 -0700 ---------------------------------------------------------------------- core/src/main/codegen/templates/Parser.jj | 42 +++++- .../calcite/adapter/enumerable/RexImpTable.java | 22 +-- .../calcite/prepare/CalciteCatalogReader.java | 12 +- .../apache/calcite/runtime/CalciteResource.java | 9 ++ .../calcite/schema/FunctionParameter.java | 21 +++ .../schema/impl/AggregateFunctionImpl.java | 31 ++-- .../schema/impl/ReflectiveFunctionBase.java | 88 +++++++---- .../calcite/schema/impl/ScalarFunctionImpl.java | 2 +- .../org/apache/calcite/sql/SqlAsOperator.java | 13 +- .../org/apache/calcite/sql/SqlCallBinding.java | 77 ++++++++++ .../org/apache/calcite/sql/SqlFunction.java | 57 ++++++- .../java/org/apache/calcite/sql/SqlKind.java | 13 +- .../org/apache/calcite/sql/SqlOperator.java | 13 ++ .../java/org/apache/calcite/sql/SqlUtil.java | 76 +++++++--- .../sql/advise/SqlAdvisorGetHintsFunction.java | 8 +- .../sql/fun/SqlArgumentAssignmentOperator.java | 52 +++++++ .../apache/calcite/sql/fun/SqlCastFunction.java | 6 +- .../calcite/sql/fun/SqlDefaultOperator.java | 48 ++++++ .../apache/calcite/sql/fun/SqlItemOperator.java | 4 +- .../apache/calcite/sql/fun/SqlLikeOperator.java | 2 +- .../sql/fun/SqlLiteralChainOperator.java | 3 +- .../calcite/sql/fun/SqlMapValueConstructor.java | 2 +- .../sql/fun/SqlMultisetMemberOfOperator.java | 6 +- .../sql/fun/SqlMultisetQueryConstructor.java | 2 +- .../sql/fun/SqlMultisetValueConstructor.java | 2 +- .../calcite/sql/fun/SqlOverlapsOperator.java | 13 +- .../calcite/sql/fun/SqlQuarterFunction.java | 4 +- .../calcite/sql/fun/SqlStdOperatorTable.java | 13 ++ .../calcite/sql/fun/SqlSubstringFunction.java | 3 +- .../apache/calcite/sql/fun/SqlTrimFunction.java | 7 +- .../sql/type/AssignableOperandTypeChecker.java | 17 ++- .../sql/type/CompositeOperandTypeChecker.java | 9 ++ .../sql/type/FamilyOperandTypeChecker.java | 19 ++- .../org/apache/calcite/sql/type/InferTypes.java | 2 +- .../sql/type/LiteralOperandTypeChecker.java | 6 +- .../sql/type/MultisetOperandTypeChecker.java | 10 +- .../apache/calcite/sql/type/OperandTypes.java | 38 ++++- .../sql/type/SameOperandTypeChecker.java | 6 +- .../sql/type/SetopOperandTypeChecker.java | 10 +- .../calcite/sql/type/SqlOperandCountRanges.java | 5 +- .../calcite/sql/type/SqlOperandTypeChecker.java | 3 + .../sql/validate/SqlUserDefinedFunction.java | 7 + .../calcite/sql/validate/SqlValidator.java | 8 +- .../calcite/sql/validate/SqlValidatorImpl.java | 29 ++-- .../calcite/sql2rel/SqlToRelConverter.java | 4 +- .../org/apache/calcite/util/Compatible.java | 18 +++ .../apache/calcite/util/ImmutableIntList.java | 20 +-- .../org/apache/calcite/util/ReflectUtil.java | 63 ++++---- .../apache/calcite/util/mapping/Mappings.java | 11 ++ .../calcite/runtime/CalciteResource.properties | 3 + .../calcite/sql/parser/SqlParserTest.java | 34 +++++ .../java/org/apache/calcite/test/JdbcTest.java | 150 ++++++++++++++++--- .../java/org/apache/calcite/util/UtilTest.java | 13 ++ .../calcite/linq4j/function/Functions.java | 30 +++- .../calcite/linq4j/function/Parameter.java | 86 +++++++++++ site/_docs/reference.md | 93 ++++++++++++ 56 files changed, 1110 insertions(+), 235 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/codegen/templates/Parser.jj ---------------------------------------------------------------------- diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj index 756ed6a..1e27217 100644 --- a/core/src/main/codegen/templates/Parser.jj +++ b/core/src/main/codegen/templates/Parser.jj @@ -790,10 +790,8 @@ SqlNodeList ParenthesizedQueryOrCommaList( } /** - * Parses function parameter lists including DISTINCT keyword recognition - * - * <p>This is pretty much the same as ParenthesizedQueryOrCommaList but allows - * the DISTINCT keyword to follow the left paren and not be followed by a comma. + * Parses function parameter lists including DISTINCT keyword recognition, + * DEFAULT, and named argument assignment. */ List FunctionParameterList( ExprContext exprContext) : @@ -801,6 +799,7 @@ List FunctionParameterList( SqlNode e = null; List list = new ArrayList(); ExprContext firstExprContext = exprContext; + SqlIdentifier name = null; } { <LPAREN> @@ -829,11 +828,26 @@ List FunctionParameterList( { list.add(e); } - e = OrderedQueryOrExpr(firstExprContext) + [ + name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT> + ] + ( + <DEFAULT_KW> { + e = SqlStdOperatorTable.DEFAULT.createCall(getPos()); + } + | + e = OrderedQueryOrExpr(firstExprContext) + ) { if (e != null) { + if (name != null) { + e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall( + name.getParserPosition().plus(e.getParserPosition()), + e, name); + } list.add(e); } + name = null; } ( <COMMA> @@ -841,8 +855,23 @@ List FunctionParameterList( // a comma-list can't appear where only a query is expected checkNonQueryExpression(exprContext); } - e = Expression(exprContext) + [ + name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT> + ] + ( + <DEFAULT_KW> { + e = SqlStdOperatorTable.DEFAULT.createCall(getPos()); + } + | + e = Expression(exprContext) + ) { + if (name != null) { + e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall( + name.getParserPosition().plus(e.getParserPosition()), + e, name); + name = null; + } list.add(e); } ) * @@ -5437,6 +5466,7 @@ String CommonNonReservedKeyWord() : | < STAR: "*" > | < SLASH: "/" > | < CONCAT: "||" > + | < NAMED_ARGUMENT_ASSIGNMENT: "=>" > | < DOUBLE_PERIOD: ".." > | < QUOTE: "'" > | < DOUBLE_QUOTE: "\"" > http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java ---------------------------------------------------------------------- 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 6f3de92..07f3f43 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 @@ -107,6 +107,7 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_TIMESTAMP; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_USER; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_VALUE; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DATETIME_PLUS; +import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DEFAULT; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DENSE_RANK; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DIVIDE; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DIVIDE_INTEGER; @@ -293,6 +294,14 @@ public class RexImpTable { map.put(ARRAY_VALUE_CONSTRUCTOR, value); map.put(ITEM, new ItemImplementor()); + map.put(DEFAULT, + new CallImplementor() { + public Expression implement(RexToLixTranslator translator, + RexCall call, NullAs nullAs) { + return Expressions.constant(null); + } + }); + // Sequences defineImplementor(CURRENT_VALUE, NullPolicy.STRICT, new MethodImplementor(BuiltInMethod.SEQUENCE_CURRENT_VALUE.method), @@ -822,17 +831,8 @@ public class RexImpTable { NullAs nullAs) { final List<Expression> translatedOperands = translator.translateList(call.getOperands()); - switch (nullAs) { - case NOT_POSSIBLE: - case NULL: - for (Expression translatedOperand : translatedOperands) { - if (Expressions.isConstantNull(translatedOperand)) { - return NULL_EXPR; - } - } - } - Expression result; - result = implementor.implement(translator, call, translatedOperands); + Expression result = + implementor.implement(translator, call, translatedOperands); return nullAs.handle(result); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java index c2a8f7b..9e49d17 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java @@ -50,6 +50,7 @@ import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro; import org.apache.calcite.sql.validate.SqlValidatorUtil; import org.apache.calcite.util.Util; +import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -242,7 +243,7 @@ public class CalciteCatalogReader implements Prepare.CatalogReader { })); } - private SqlOperator toOp(SqlIdentifier name, Function function) { + private SqlOperator toOp(SqlIdentifier name, final Function function) { List<RelDataType> argTypes = new ArrayList<>(); List<SqlTypeFamily> typeFamilies = new ArrayList<>(); for (FunctionParameter o : function.getParameters()) { @@ -253,9 +254,16 @@ public class CalciteCatalogReader implements Prepare.CatalogReader { } final RelDataType returnType; if (function instanceof ScalarFunction) { + Predicate<Integer> optional = + new Predicate<Integer>() { + public boolean apply(Integer input) { + return function.getParameters().get(input).isOptional(); + } + }; return new SqlUserDefinedFunction(name, ReturnTypes.explicit(Schemas.proto((ScalarFunction) function)), - InferTypes.explicit(argTypes), OperandTypes.family(typeFamilies), + InferTypes.explicit(argTypes), + OperandTypes.family(typeFamilies, optional), toSql(argTypes), function); } else if (function instanceof AggregateFunction) { returnType = ((AggregateFunction) function).getReturnType(typeFactory); http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java ---------------------------------------------------------------------- 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 0670948..aa701d9 100644 --- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java +++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java @@ -389,6 +389,15 @@ public interface CalciteResource { @BaseMessage("DISTINCT/ALL not allowed with {0} function") ExInst<SqlValidatorException> functionQuantifierNotAllowed(String a0); + @BaseMessage("Some but not all arguments are named") + ExInst<SqlValidatorException> someButNotAllArgumentsAreNamed(); + + @BaseMessage("Duplicate argument name ''{0}''") + ExInst<SqlValidatorException> duplicateArgumentName(String name); + + @BaseMessage("DEFAULT is only allowed for optional parameters") + ExInst<SqlValidatorException> defaultForOptionalParameter(); + @BaseMessage("Not allowed to perform {0} on {1}") ExInst<SqlValidatorException> accessNotAllowed(String a0, String a1); http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/schema/FunctionParameter.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/schema/FunctionParameter.java b/core/src/main/java/org/apache/calcite/schema/FunctionParameter.java index 11e50c4..3e95628 100644 --- a/core/src/main/java/org/apache/calcite/schema/FunctionParameter.java +++ b/core/src/main/java/org/apache/calcite/schema/FunctionParameter.java @@ -26,6 +26,14 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; * {@link java.lang.reflect.Parameter} was too confusing.</p> */ public interface FunctionParameter { + /** Function to get the name of a parameter. */ + com.google.common.base.Function<FunctionParameter, String> NAME_FN = + new com.google.common.base.Function<FunctionParameter, String>() { + public String apply(FunctionParameter p) { + return p.getName(); + } + }; + /** * Zero-based ordinal of this parameter within the member's parameter * list. @@ -49,6 +57,19 @@ public interface FunctionParameter { * @return Parameter type. */ RelDataType getType(RelDataTypeFactory typeFactory); + + /** + * Returns whether this parameter is optional. + * + * <p>If true, the value of the parameter can be supplied using the DEFAULT + * SQL keyword, or it can be omitted from a function called using argument + * assignment, or the function can be called with fewer parameters (if all + * parameters after it are optional too). + * + * <p>If a parameter is optional its default value is NULL. We may in future + * allow functions to specify other default values. + */ + boolean isOptional(); } // End FunctionParameter.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java b/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java index 52dc9d5..5c24480 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/AggregateFunctionImpl.java @@ -23,8 +23,9 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.schema.AggregateFunction; import org.apache.calcite.schema.FunctionParameter; import org.apache.calcite.schema.ImplementableAggFunction; -import org.apache.calcite.util.Util; +import org.apache.calcite.util.ReflectUtil; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import java.lang.reflect.Method; @@ -56,6 +57,7 @@ public class AggregateFunctionImpl implements AggregateFunction, /** Private constructor; use {@link #create}. */ private AggregateFunctionImpl(Class<?> declaringClass, + List<FunctionParameter> params, List<Class<?>> valueTypes, Class<?> accumulatorType, Class<?> resultType, @@ -64,18 +66,16 @@ public class AggregateFunctionImpl implements AggregateFunction, Method mergeMethod, Method resultMethod) { this.declaringClass = declaringClass; - this.isStatic = Modifier.isStatic(initMethod.getModifiers()); this.valueTypes = ImmutableList.copyOf(valueTypes); - this.parameters = ReflectiveFunctionBase.toFunctionParameters(valueTypes); + this.parameters = params; this.accumulatorType = accumulatorType; this.resultType = resultType; - this.initMethod = initMethod; - this.addMethod = addMethod; + this.initMethod = Preconditions.checkNotNull(initMethod); + this.addMethod = Preconditions.checkNotNull(addMethod); this.mergeMethod = mergeMethod; this.resultMethod = resultMethod; + this.isStatic = Modifier.isStatic(initMethod.getModifiers()); - assert initMethod != null; - assert addMethod != null; assert resultMethod != null || accumulatorType == resultType; } @@ -100,7 +100,17 @@ public class AggregateFunctionImpl implements AggregateFunction, if (addParamTypes.isEmpty() || addParamTypes.get(0) != accumulatorType) { throw RESOURCE.firstParameterOfAdd(clazz.getName()).ex(); } - final List<Class<?>> valueTypes = Util.skip(addParamTypes, 1); + final ReflectiveFunctionBase.ParameterListBuilder params = + ReflectiveFunctionBase.builder(); + final ImmutableList.Builder<Class<?>> valueTypes = + ImmutableList.builder(); + for (int i = 1; i < addParamTypes.size(); i++) { + final Class<?> type = addParamTypes.get(i); + final String name = ReflectUtil.getParameterName(addMethod, i); + final boolean optional = ReflectUtil.isParameterOptional(addMethod, i); + params.add(type, name, optional); + valueTypes.add(type); + } // A init() // A add(A, V) @@ -112,8 +122,9 @@ public class AggregateFunctionImpl implements AggregateFunction, // TODO: check merge args are (A, A) // TODO: check result args are (A) - return new AggregateFunctionImpl(clazz, valueTypes, accumulatorType, - resultType, initMethod, addMethod, mergeMethod, resultMethod); + return new AggregateFunctionImpl(clazz, params.build(), + valueTypes.build(), accumulatorType, resultType, initMethod, + addMethod, mergeMethod, resultMethod); } return null; } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java b/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java index 681b69b..ab0daf5 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ReflectiveFunctionBase.java @@ -20,13 +20,14 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.schema.Function; import org.apache.calcite.schema.FunctionParameter; +import org.apache.calcite.util.ReflectUtil; import com.google.common.collect.ImmutableList; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; /** @@ -46,7 +47,7 @@ public abstract class ReflectiveFunctionBase implements Function { */ public ReflectiveFunctionBase(Method method) { this.method = method; - this.parameters = toFunctionParameters(method.getParameterTypes()); + this.parameters = builder().addMethodParameters(method).build(); } /** @@ -58,37 +59,6 @@ public abstract class ReflectiveFunctionBase implements Function { return parameters; } - - public static ImmutableList<FunctionParameter> toFunctionParameters( - Class... types) { - return toFunctionParameters(Arrays.asList(types)); - } - - public static ImmutableList<FunctionParameter> toFunctionParameters( - Iterable<? extends Class> types) { - final ImmutableList.Builder<FunctionParameter> res = - ImmutableList.builder(); - int i = 0; - for (final Class type : types) { - final int ordinal = i; - res.add(new FunctionParameter() { - public int getOrdinal() { - return ordinal; - } - - public String getName() { - return "arg" + ordinal; - } - - public RelDataType getType(RelDataTypeFactory typeFactory) { - return typeFactory.createJavaType(type); - } - }); - i++; - } - return res.build(); - } - /** * Verifies if given class has public constructor with zero arguments. * @param clazz class to verify @@ -118,6 +88,58 @@ public abstract class ReflectiveFunctionBase implements Function { } return null; } + + /** Creates a ParameterListBuilder. */ + public static ParameterListBuilder builder() { + return new ParameterListBuilder(); + } + + /** Helps build lists of + * {@link org.apache.calcite.schema.FunctionParameter}. */ + public static class ParameterListBuilder { + final List<FunctionParameter> builder = new ArrayList<>(); + + public ImmutableList<FunctionParameter> build() { + return ImmutableList.copyOf(builder); + } + + public ParameterListBuilder add(final Class<?> type, final String name) { + return add(type, name, false); + } + + public ParameterListBuilder add(final Class<?> type, final String name, + final boolean optional) { + final int ordinal = builder.size(); + builder.add( + new FunctionParameter() { + public int getOrdinal() { + return ordinal; + } + + public String getName() { + return name; + } + + public RelDataType getType(RelDataTypeFactory typeFactory) { + return typeFactory.createJavaType(type); + } + + public boolean isOptional() { + return optional; + } + }); + return this; + } + + public ParameterListBuilder addMethodParameters(Method method) { + final Class<?>[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) { + add(types[i], ReflectUtil.getParameterName(method, i), + ReflectUtil.isParameterOptional(method, i)); + } + return this; + } + } } // End ReflectiveFunctionBase.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java b/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java index c34076e..18f398a 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ScalarFunctionImpl.java @@ -113,7 +113,7 @@ public class ScalarFunctionImpl extends ReflectiveFunctionBase implements private static CallImplementor createImplementor(final Method method) { return RexImpTable.createImplementor( - new ReflectiveCallNotNullImplementor(method), NullPolicy.ANY, false); + new ReflectiveCallNotNullImplementor(method), NullPolicy.NONE, false); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/SqlAsOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAsOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlAsOperator.java index 6f4aaf3..4d9ea95 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlAsOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlAsOperator.java @@ -20,6 +20,9 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.type.InferTypes; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.ReturnTypes; +import org.apache.calcite.sql.type.SqlOperandTypeChecker; +import org.apache.calcite.sql.type.SqlOperandTypeInference; +import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.sql.util.SqlBasicVisitor; import org.apache.calcite.sql.util.SqlVisitor; import org.apache.calcite.sql.validate.SqlMonotonicity; @@ -41,7 +44,7 @@ public class SqlAsOperator extends SqlSpecialOperator { * Creates an AS operator. */ public SqlAsOperator() { - super( + this( "AS", SqlKind.AS, 20, @@ -51,6 +54,14 @@ public class SqlAsOperator extends SqlSpecialOperator { OperandTypes.ANY_ANY); } + protected SqlAsOperator(String name, SqlKind kind, int prec, + boolean leftAssoc, SqlReturnTypeInference returnTypeInference, + SqlOperandTypeInference operandTypeInference, + SqlOperandTypeChecker operandTypeChecker) { + super(name, kind, prec, leftAssoc, returnTypeInference, + operandTypeInference, operandTypeChecker); + } + //~ Methods ---------------------------------------------------------------- public void unparse( http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java index aaa8c96..36d2195 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java @@ -20,6 +20,7 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.runtime.CalciteException; import org.apache.calcite.runtime.Resources; import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.validate.SelectScope; import org.apache.calcite.sql.validate.SqlMonotonicity; import org.apache.calcite.sql.validate.SqlValidator; @@ -30,6 +31,9 @@ import org.apache.calcite.sql.validate.SqlValidatorUtil; import org.apache.calcite.util.NlsString; import org.apache.calcite.util.Util; +import com.google.common.base.Function; +import com.google.common.collect.Lists; + import java.math.BigDecimal; import java.util.List; @@ -40,6 +44,8 @@ import static org.apache.calcite.util.Static.RESOURCE; * analyzing to the operands of a {@link SqlCall} with a {@link SqlValidator}. */ public class SqlCallBinding extends SqlOperatorBinding { + private static final SqlCall DEFAULT_CALL = + SqlStdOperatorTable.DEFAULT.createCall(SqlParserPos.ZERO); //~ Instance fields -------------------------------------------------------- private final SqlValidator validator; @@ -112,6 +118,77 @@ public class SqlCallBinding extends SqlOperatorBinding { return call; } + /** Returns the operands to a call permuted into the same order as the + * formal parameters of the function. */ + public List<SqlNode> operands() { + if (hasAssignment()) { + return permutedOperands(call); + } else { + final List<SqlNode> operandList = call.getOperandList(); + if (call.getOperator() instanceof SqlFunction) { + final List<RelDataType> paramTypes = + ((SqlFunction) call.getOperator()).getParamTypes(); + if (paramTypes != null && operandList.size() < paramTypes.size()) { + final List<SqlNode> list = Lists.newArrayList(operandList); + while (list.size() < paramTypes.size()) { + list.add(DEFAULT_CALL); + } + return list; + } + } + return operandList; + } + } + + /** Returns whether arguments have name assignment. */ + private boolean hasAssignment() { + for (SqlNode operand : call.getOperandList()) { + if (operand != null + && operand.getKind() == SqlKind.ARGUMENT_ASSIGNMENT) { + return true; + } + } + return false; + } + + /** Returns the operands to a call permuted into the same order as the + * formal parameters of the function. */ + public List<SqlNode> permutedOperands(final SqlCall call) { + final SqlFunction operator = (SqlFunction) call.getOperator(); + return Lists.transform(operator.getParamNames(), + new Function<String, SqlNode>() { + public SqlNode apply(String paramName) { + for (SqlNode operand2 : call.getOperandList()) { + final SqlCall call2 = (SqlCall) operand2; + assert operand2.getKind() == SqlKind.ARGUMENT_ASSIGNMENT; + final SqlIdentifier id = call2.operand(1); + if (id.getSimple().equals(paramName)) { + return call2.operand(0); + } + } + return DEFAULT_CALL; + } + }); + } + + /** + * Returns a particular operand. + */ + public SqlNode operand(int i) { + return operands().get(i); + } + + /** Returns a call that is equivalent except that arguments have been + * permuted into the logical order. Any arguments whose default value is being + * used are null. */ + public SqlCall permutedCall() { + final List<SqlNode> operandList = operands(); + if (operandList.equals(call.getOperandList())) { + return call; + } + return call.getOperator().createCall(call.pos, operandList); + } + public SqlMonotonicity getOperandMonotonicity(int ordinal) { return call.getOperandList().get(ordinal).getMonotonicity(scope); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/SqlFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlFunction.java index 6e5d99a..dc63209 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlFunction.java @@ -16,6 +16,8 @@ */ package org.apache.calcite.sql; +import org.apache.calcite.linq4j.function.Function1; +import org.apache.calcite.linq4j.function.Functions; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.sql.parser.SqlParserPos; @@ -38,6 +40,14 @@ import static org.apache.calcite.util.Static.RESOURCE; * function-call syntax. */ public class SqlFunction extends SqlOperator { + /** Function that generates "arg{n}" for the {@code n}th argument name. */ + private static final Function1<Integer, String> ARG_FN = + new Function1<Integer, String>() { + public String apply(Integer a0) { + return "arg" + a0; + } + }; + //~ Instance fields -------------------------------------------------------- private final SqlFunctionCategory category; @@ -152,6 +162,15 @@ public class SqlFunction extends SqlOperator { return paramTypes; } + /** + * Returns a list of parameter names. + * + * <p>The default implementation returns {@code [arg0, arg1, ..., argN]}. + */ + public List<String> getParamNames() { + return Functions.generate(paramTypes.size(), ARG_FN); + } + public void unparse( SqlWriter writer, SqlCall call, @@ -216,7 +235,6 @@ public class SqlFunction extends SqlOperator { SqlValidatorScope scope, SqlCall call, boolean convertRowArgToColumnList) { - final List<SqlNode> operands = call.getOperandList(); // Scope for operands. Usually the same as 'scope'. final SqlValidatorScope operandScope = scope.getOperandScope(call); @@ -224,11 +242,38 @@ public class SqlFunction extends SqlOperator { // Indicate to the validator that we're validating a new function call validator.pushFunctionCall(); + // If any arguments are named, construct a map. + final ImmutableList.Builder<String> nameBuilder = ImmutableList.builder(); + final ImmutableList.Builder<SqlNode> argBuilder = ImmutableList.builder(); + for (SqlNode operand : call.getOperandList()) { + if (operand.getKind() == SqlKind.ARGUMENT_ASSIGNMENT) { + final List<SqlNode> operandList = ((SqlCall) operand).getOperandList(); + nameBuilder.add(((SqlIdentifier) operandList.get(1)).getSimple()); + argBuilder.add(operandList.get(0)); + } + } + ImmutableList<String> argNames = nameBuilder.build(); + final List<SqlNode> args; + if (argNames.isEmpty()) { + args = call.getOperandList(); + argNames = null; + } else { + if (argNames.size() < call.getOperandList().size()) { + throw validator.newValidationError(call, + RESOURCE.someButNotAllArgumentsAreNamed()); + } + int duplicate = Util.firstDuplicate(argNames); + if (duplicate >= 0) { + throw validator.newValidationError(call, + RESOURCE.duplicateArgumentName(argNames.get(duplicate))); + } + args = argBuilder.build(); + } try { final ImmutableList.Builder<RelDataType> argTypeBuilder = ImmutableList.builder(); boolean containsRowArg = false; - for (SqlNode operand : operands) { + for (SqlNode operand : args) { RelDataType nodeType; // for row arguments that should be converted to ColumnList @@ -252,6 +297,7 @@ public class SqlFunction extends SqlOperator { validator.getOperatorTable(), getNameAsId(), argTypes, + argNames, getFunctionType()); // if we have a match on function name and parameter count, but @@ -268,14 +314,14 @@ public class SqlFunction extends SqlOperator { getFunctionType())) { // remove the already validated node types corresponding to // row arguments before re-validating - for (SqlNode operand : operands) { + for (SqlNode operand : args) { if (operand.getKind() == SqlKind.ROW) { validator.removeValidatedNodeType(operand); } } return deriveType(validator, scope, call, false); } else if (function != null) { - validator.validateColumnListParams(function, argTypes, operands); + validator.validateColumnListParams(function, argTypes, args); } } @@ -284,7 +330,8 @@ public class SqlFunction extends SqlOperator { argTypes); } if (function == null) { - throw validator.handleUnresolvedFunction(call, this, argTypes); + throw validator.handleUnresolvedFunction(call, this, argTypes, + argNames); } // REVIEW jvs 25-Mar-2005: This is, in a sense, expanding http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/SqlKind.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java index fa821f8..5a8a2fa 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java @@ -182,6 +182,16 @@ public enum SqlKind { AS, /** + * ARGUMENT_ASSIGNMENT operator, {@code =>} + */ + ARGUMENT_ASSIGNMENT, + + /** + * DEFAULT operator + */ + DEFAULT, + + /** * OVER operator */ OVER, @@ -710,7 +720,8 @@ public enum SqlKind { public static final Set<SqlKind> EXPRESSION = EnumSet.complementOf( EnumSet.of( - AS, DESCENDING, CUBE, ROLLUP, GROUPING_SETS, EXTEND, + AS, ARGUMENT_ASSIGNMENT, DEFAULT, + DESCENDING, CUBE, ROLLUP, GROUPING_SETS, EXTEND, SELECT, JOIN, OTHER_FUNCTION, CAST, TRIM, FLOOR, CEIL, LITERAL_CHAIN, JDBC_FN, PRECEDING, FOLLOWING, ORDER_BY, NULLS_FIRST, NULLS_LAST, COLLECTION_TABLE, TABLESAMPLE, http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/SqlOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java index ee35b09..df165ca 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.sql; +import org.apache.calcite.linq4j.Ord; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.sql.parser.SqlParserPos; @@ -547,6 +548,18 @@ public abstract class SqlOperator { throw Util.needToImplement(this); } + if (kind != SqlKind.ARGUMENT_ASSIGNMENT) { + for (Ord<SqlNode> operand : Ord.zip(callBinding.operands())) { + if (operand.e != null + && operand.e.getKind() == SqlKind.DEFAULT + && !operandTypeChecker.isOptional(operand.i)) { + throw callBinding.getValidator().newValidationError( + callBinding.getCall(), + RESOURCE.defaultForOptionalParameter()); + } + } + } + return operandTypeChecker.checkOperandTypes( callBinding, throwOnFailure); http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/SqlUtil.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java index 405b385..66fb911 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.sql; +import org.apache.calcite.linq4j.Ord; +import org.apache.calcite.linq4j.function.Function1; +import org.apache.calcite.linq4j.function.Functions; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypePrecedenceList; @@ -43,8 +46,10 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import static org.apache.calcite.util.Static.RESOURCE; @@ -314,21 +319,21 @@ public abstract class SqlUtil { * @param opTab operator table to search * @param funcName name of function being invoked * @param argTypes argument types + * @param argNames argument names, or null if call by position * @param category whether a function or a procedure. (If a procedure is * being invoked, the overload rules are simpler.) * @return matching routine, or null if none found * @sql.99 Part 2 Section 10.4 */ - public static SqlFunction lookupRoutine( - SqlOperatorTable opTab, - SqlIdentifier funcName, - List<RelDataType> argTypes, - SqlFunctionCategory category) { + public static SqlFunction lookupRoutine(SqlOperatorTable opTab, + SqlIdentifier funcName, List<RelDataType> argTypes, + List<String> argNames, SqlFunctionCategory category) { List<SqlFunction> list = lookupSubjectRoutines( opTab, funcName, argTypes, + argNames, category); if (list.isEmpty()) { return null; @@ -344,15 +349,14 @@ public abstract class SqlUtil { * @param opTab operator table to search * @param funcName name of function being invoked * @param argTypes argument types + * @param argNames argument names, or null if call by position * @param category category of routine to look up * @return list of matching routines * @sql.99 Part 2 Section 10.4 */ - public static List<SqlFunction> lookupSubjectRoutines( - SqlOperatorTable opTab, - SqlIdentifier funcName, - List<RelDataType> argTypes, - SqlFunctionCategory category) { + public static List<SqlFunction> lookupSubjectRoutines(SqlOperatorTable opTab, + SqlIdentifier funcName, List<RelDataType> argTypes, + List<String> argNames, SqlFunctionCategory category) { // start with all routines matching by name List<SqlFunction> routines = lookupSubjectRoutinesByName(opTab, funcName, category); @@ -369,7 +373,7 @@ public abstract class SqlUtil { // second pass: eliminate routines which don't accept the given // argument types - filterRoutinesByParameterType(routines, argTypes); + filterRoutinesByParameterType(routines, argTypes, argNames); // see if we can stop now; this is necessary for the case // of builtin functions where we don't have param type info @@ -444,10 +448,10 @@ public abstract class SqlUtil { /** * @sql.99 Part 2 Section 10.4 Syntax Rule 6.b.iii.2.B */ - private static void filterRoutinesByParameterType( - List<SqlFunction> routines, - List<RelDataType> argTypes) { + private static void filterRoutinesByParameterType(List<SqlFunction> routines, + final List<RelDataType> argTypes, List<String> argNames) { Iterator<SqlFunction> iter = routines.iterator(); + loop: while (iter.hasNext()) { SqlFunction function = iter.next(); List<RelDataType> paramTypes = function.getParamTypes(); @@ -455,19 +459,45 @@ public abstract class SqlUtil { // no parameter information for builtins; keep for now continue; } - assert paramTypes.size() == argTypes.size(); - boolean keep = true; - for (Pair<RelDataType, RelDataType> p : Pair.zip(paramTypes, argTypes)) { + final List<RelDataType> permutedArgTypes; + if (argNames != null) { + // Arguments passed by name. Make sure that the function has parameters + // of all of these names. + final Map<Integer, Integer> map = new HashMap<>(); + for (Ord<String> argName : Ord.zip(argNames)) { + final int i = function.getParamNames().indexOf(argName.e); + if (i < 0) { + iter.remove(); + continue loop; + } + map.put(i, argName.i); + } + permutedArgTypes = Functions.generate(paramTypes.size(), + new Function1<Integer, RelDataType>() { + public RelDataType apply(Integer a0) { + if (map.containsKey(a0)) { + return argTypes.get(map.get(a0)); + } else { + return null; + } + } + }); + } else { + permutedArgTypes = Lists.newArrayList(argTypes); + while (permutedArgTypes.size() < argTypes.size()) { + paramTypes.add(null); + } + } + for (Pair<RelDataType, RelDataType> p + : Pair.zip(paramTypes, permutedArgTypes)) { final RelDataType argType = p.right; final RelDataType paramType = p.left; - if (!SqlTypeUtil.canAssignFrom(paramType, argType)) { - keep = false; - break; + if (argType != null + && !SqlTypeUtil.canAssignFrom(paramType, argType)) { + iter.remove(); + continue loop; } } - if (!keep) { - iter.remove(); - } } } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java index 8e27d5d..ba0cef7 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorGetHintsFunction.java @@ -72,7 +72,10 @@ public class SqlAdvisorGetHintsFunction }, NullPolicy.ANY, false); private static final List<FunctionParameter> PARAMETERS = - ReflectiveFunctionBase.toFunctionParameters(String.class, int.class); + ReflectiveFunctionBase.builder() + .add(String.class, "sql") + .add(int.class, "pos") + .build(); public CallImplementor getImplementor() { return IMPLEMENTOR; @@ -107,8 +110,7 @@ public class SqlAdvisorGetHintsFunction final String[] replaced = {null}; final List<SqlMoniker> hints = advisor.getCompletionHints(sql, pos, replaced); - final List<SqlAdvisorHint> res = - new ArrayList<SqlAdvisorHint>(hints.size() + 1); + final List<SqlAdvisorHint> res = new ArrayList<>(hints.size() + 1); res.add(new SqlAdvisorHint(replaced[0], null, "MATCH")); for (SqlMoniker hint : hints) { res.add(new SqlAdvisorHint(hint)); http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlArgumentAssignmentOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlArgumentAssignmentOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlArgumentAssignmentOperator.java new file mode 100644 index 0000000..188d4aa --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlArgumentAssignmentOperator.java @@ -0,0 +1,52 @@ +/* + * 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.SqlAsOperator; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.type.InferTypes; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.ReturnTypes; + +/** + * Operator that assigns an argument to a function call to a particular named + * parameter. + * + * <p>Not an expression; just a holder to represent syntax until the validator + * has chance to resolve arguments. + * + * <p>Sub-class of {@link SqlAsOperator} ("AS") out of convenience; to be + * consistent with AS, we reverse the arguments. + */ +class SqlArgumentAssignmentOperator extends SqlAsOperator { + public SqlArgumentAssignmentOperator() { + super("=>", SqlKind.ARGUMENT_ASSIGNMENT, 20, true, ReturnTypes.ARG0, + InferTypes.RETURN_TYPE, OperandTypes.ANY_ANY); + } + + @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, + int rightPrec) { + // Arguments are held in reverse order to be consistent with base class (AS). + call.operand(1).unparse(writer, leftPrec, getLeftPrec()); + writer.keyword(getName()); + call.operand(0).unparse(writer, getRightPrec(), rightPrec); + } +} + +// End SqlArgumentAssignmentOperator.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java index f4e933a..c7ded0b 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java @@ -113,7 +113,7 @@ public class SqlCastFunction extends SqlFunction { firstType.isNullable()); if (opBinding instanceof SqlCallBinding) { SqlCallBinding callBinding = (SqlCallBinding) opBinding; - SqlNode operand0 = callBinding.getCall().operand(0); + SqlNode operand0 = callBinding.operand(0); // dynamic parameters and null constants need their types assigned // to them using the type they are casted to. @@ -145,8 +145,8 @@ public class SqlCastFunction extends SqlFunction { public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { - final SqlNode left = callBinding.getCall().operand(0); - final SqlNode right = callBinding.getCall().operand(1); + final SqlNode left = callBinding.operand(0); + final SqlNode right = callBinding.operand(1); if (SqlUtil.isNullLiteral(left, false) || left instanceof SqlDynamicParam) { return true; http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlDefaultOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlDefaultOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlDefaultOperator.java new file mode 100644 index 0000000..5e71a41 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlDefaultOperator.java @@ -0,0 +1,48 @@ +/* + * 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.SqlKind; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.type.InferTypes; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.ReturnTypes; +import org.apache.calcite.sql.type.SqlTypeName; + +/** + * Operator that indicates that an argument to a function call is to take its + * default value. + * + * <p>Not an expression; just a holder to represent syntax until the validator + * has chance to resolve arguments. + */ +class SqlDefaultOperator extends SqlSpecialOperator { + public SqlDefaultOperator() { + super("DEFAULT", SqlKind.DEFAULT, 100, true, + ReturnTypes.explicit(SqlTypeName.ANY), InferTypes.RETURN_TYPE, + OperandTypes.NILADIC); + } + + @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, + int rightPrec) { + writer.sep(getName()); + } +} + +// End SqlDefaultOperator.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java index aafa153..f73ce4a 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java @@ -84,8 +84,8 @@ class SqlItemOperator extends SqlSpecialOperator { @Override public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { - final SqlNode left = callBinding.getCall().operand(0); - final SqlNode right = callBinding.getCall().operand(1); + final SqlNode left = callBinding.operand(0); + final SqlNode right = callBinding.operand(1); if (!ARRAY_OR_MAP.checkSingleOperandType(callBinding, left, 0, throwOnFailure)) { return false; http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java index 7a5cdd3..1de1407 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java @@ -127,7 +127,7 @@ public class SqlLikeOperator extends SqlSpecialOperator { return SqlTypeUtil.isCharTypeComparable( callBinding, - callBinding.getCall().getOperandList(), + callBinding.operands(), throwOnFailure); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java index c0e891c..80d2bc3 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLiteralChainOperator.java @@ -79,9 +79,8 @@ public class SqlLiteralChainOperator extends SqlSpecialOperator { if (callBinding.getOperandCount() < 2) { return true; // nothing to compare } - final List<SqlNode> operandList = callBinding.getCall().getOperandList(); RelDataType firstType = null; - for (Ord<SqlNode> operand : Ord.zip(operandList)) { + for (Ord<SqlNode> operand : Ord.zip(callBinding.operands())) { RelDataType type = callBinding.getValidator().deriveType( callBinding.getScope(), http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java index 2e86321..2945038 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java @@ -61,7 +61,7 @@ public class SqlMapValueConstructor extends SqlMultisetValueConstructor { SqlTypeUtil.deriveAndCollectTypes( callBinding.getValidator(), callBinding.getScope(), - callBinding.getCall().getOperandList()); + callBinding.operands()); if (argTypes.size() == 0) { throw callBinding.newValidationError(RESOURCE.mapRequiresTwoOrMoreArgs()); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetMemberOfOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetMemberOfOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetMemberOfOperator.java index 71b5880..56b65ed 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetMemberOfOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetMemberOfOperator.java @@ -56,7 +56,7 @@ public class SqlMultisetMemberOfOperator extends SqlBinaryOperator { boolean throwOnFailure) { if (!OperandTypes.MULTISET.checkSingleOperandType( callBinding, - callBinding.getCall().operand(1), + callBinding.operand(1), 0, throwOnFailure)) { return false; @@ -65,12 +65,12 @@ public class SqlMultisetMemberOfOperator extends SqlBinaryOperator { MultisetSqlType mt = (MultisetSqlType) callBinding.getValidator().deriveType( callBinding.getScope(), - callBinding.getCall().operand(1)); + callBinding.operand(1)); RelDataType t0 = callBinding.getValidator().deriveType( callBinding.getScope(), - callBinding.getCall().operand(0)); + callBinding.operand(0)); RelDataType t1 = mt.getComponentType(); if (t0.getFamily() != t1.getFamily()) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java index 9e50507..76b09fe 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java @@ -89,7 +89,7 @@ public class SqlMultisetQueryConstructor extends SqlSpecialOperator { SqlTypeUtil.deriveAndCollectTypes( callBinding.getValidator(), callBinding.getScope(), - callBinding.getCall().getOperandList()); + callBinding.operands()); final RelDataType componentType = getComponentType( callBinding.getTypeFactory(), http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java index c407f58..a517391 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetValueConstructor.java @@ -88,7 +88,7 @@ public class SqlMultisetValueConstructor extends SqlSpecialOperator { SqlTypeUtil.deriveAndCollectTypes( callBinding.getValidator(), callBinding.getScope(), - callBinding.getCall().getOperandList()); + callBinding.operands()); if (argTypes.size() == 0) { throw callBinding.newValidationError(RESOURCE.requireAtLeastOneArg()); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlapsOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlapsOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlapsOperator.java index e3643c1..941a06e 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlapsOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlOverlapsOperator.java @@ -111,28 +111,27 @@ public class SqlOverlapsOperator extends SqlSpecialOperator { public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { - SqlCall call = callBinding.getCall(); SqlValidator validator = callBinding.getValidator(); SqlValidatorScope scope = callBinding.getScope(); if (!OperandTypes.DATETIME.checkSingleOperandType( callBinding, - call.operand(0), + callBinding.operand(0), 0, throwOnFailure)) { return false; } if (!OperandTypes.DATETIME.checkSingleOperandType( callBinding, - call.operand(2), + callBinding.operand(2), 0, throwOnFailure)) { return false; } - RelDataType t0 = validator.deriveType(scope, call.operand(0)); - RelDataType t1 = validator.deriveType(scope, call.operand(1)); - RelDataType t2 = validator.deriveType(scope, call.operand(2)); - RelDataType t3 = validator.deriveType(scope, call.operand(3)); + RelDataType t0 = validator.deriveType(scope, callBinding.operand(0)); + RelDataType t1 = validator.deriveType(scope, callBinding.operand(1)); + RelDataType t2 = validator.deriveType(scope, callBinding.operand(2)); + RelDataType t3 = validator.deriveType(scope, callBinding.operand(3)); // t0 must be comparable with t2 if (!SqlTypeUtil.sameNamedType(t0, t2)) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlQuarterFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlQuarterFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlQuarterFunction.java index abc2c9f..b76c1dd 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlQuarterFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlQuarterFunction.java @@ -16,7 +16,6 @@ */ package org.apache.calcite.sql.fun; -import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlCallBinding; import org.apache.calcite.sql.SqlFunction; import org.apache.calcite.sql.SqlFunctionCategory; @@ -56,9 +55,8 @@ public class SqlQuarterFunction extends SqlFunction { public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) { - SqlCall call = callBinding.getCall(); return OperandTypes.DATETIME.checkSingleOperandType(callBinding, - call.operand(0), 0, throwOnFailure); + callBinding.operand(0), 0, throwOnFailure); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.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 8aff020..fc524c6 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 @@ -150,6 +150,19 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { */ public static final SqlAsOperator AS = new SqlAsOperator(); + /** + * <code>ARGUMENT_ASSIGNMENT</code> operator (<code>=<</code>) + * assigns an argument to a function call to a particular named parameter. + */ + public static final SqlSpecialOperator ARGUMENT_ASSIGNMENT = + new SqlArgumentAssignmentOperator(); + + /** + * <code>DEFAULT</code> operator indicates that an argument to a function call + * is to take its default value.. + */ + public static final SqlSpecialOperator DEFAULT = new SqlDefaultOperator(); + /** <code>FILTER</code> operator filters which rows are included in an * aggregate function. */ public static final SqlFilterOperator FILTER = new SqlFilterOperator(); http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlSubstringFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlSubstringFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlSubstringFunction.java index 73eda82..f669a8a 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlSubstringFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlSubstringFunction.java @@ -95,11 +95,10 @@ public class SqlSubstringFunction extends SqlFunction { public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { - SqlCall call = callBinding.getCall(); SqlValidator validator = callBinding.getValidator(); SqlValidatorScope scope = callBinding.getScope(); - final List<SqlNode> operands = call.getOperandList(); + final List<SqlNode> operands = callBinding.operands(); int n = operands.size(); assert (3 == n) || (2 == n); if (!OperandTypes.STRING.checkSingleOperandType( http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java index f798e3e..0ba461b 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlTrimFunction.java @@ -158,10 +158,9 @@ public class SqlTrimFunction extends SqlFunction { if (!super.checkOperandTypes(callBinding, throwOnFailure)) { return false; } - final List<SqlNode> operands = callBinding.getCall().getOperandList(); - return SqlTypeUtil.isCharTypeComparable( - callBinding, - ImmutableList.of(operands.get(1), operands.get(2)), throwOnFailure); + return SqlTypeUtil.isCharTypeComparable(callBinding, + ImmutableList.of(callBinding.operand(1), callBinding.operand(2)), + throwOnFailure); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java index 7ee0a64..6cd3565 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/AssignableOperandTypeChecker.java @@ -37,6 +37,7 @@ public class AssignableOperandTypeChecker implements SqlOperandTypeChecker { //~ Instance fields -------------------------------------------------------- private final List<RelDataType> paramTypes; + private final ImmutableList<String> paramNames; //~ Constructors ----------------------------------------------------------- @@ -45,13 +46,21 @@ public class AssignableOperandTypeChecker implements SqlOperandTypeChecker { * * @param paramTypes parameter types for operands; index in this array * corresponds to operand number + * @param paramNames parameter names, or null */ - public AssignableOperandTypeChecker(List<RelDataType> paramTypes) { + public AssignableOperandTypeChecker(List<RelDataType> paramTypes, + List<String> paramNames) { this.paramTypes = ImmutableList.copyOf(paramTypes); + this.paramNames = + paramNames == null ? null : ImmutableList.copyOf(paramNames); } //~ Methods ---------------------------------------------------------------- + public boolean isOptional(int i) { + return false; + } + public SqlOperandCountRange getOperandCountRange() { return SqlOperandCountRanges.of(paramTypes.size()); } @@ -59,6 +68,8 @@ public class AssignableOperandTypeChecker implements SqlOperandTypeChecker { public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { + // Do not use callBinding.operands(). We have not resolved to a function + // yet, therefore we do not know the ordered parameter names. final List<SqlNode> operands = callBinding.getCall().getOperandList(); for (Pair<RelDataType, SqlNode> pair : Pair.zip(paramTypes, operands)) { RelDataType argType = @@ -84,6 +95,10 @@ public class AssignableOperandTypeChecker implements SqlOperandTypeChecker { if (paramType.i > 0) { sb.append(", "); } + if (paramNames != null) { + sb.append(paramNames.get(paramType.i)) + .append(" => "); + } sb.append("<"); sb.append(paramType.e.getFamily()); sb.append(">"); http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java index 6a77c95..9bfea87 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java @@ -107,6 +107,15 @@ public class CompositeOperandTypeChecker implements SqlOperandTypeChecker { //~ Methods ---------------------------------------------------------------- + public boolean isOptional(int i) { + for (SqlOperandTypeChecker allowedRule : allowedRules) { + if (allowedRule.isOptional(i)) { + return true; + } + } + return false; + } + public ImmutableList<? extends SqlOperandTypeChecker> getRules() { return allowedRules; } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/FamilyOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/FamilyOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/FamilyOperandTypeChecker.java index 4bef483..b9cf248 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/FamilyOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/FamilyOperandTypeChecker.java @@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlOperandCountRange; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlUtil; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import java.util.List; @@ -38,18 +39,25 @@ public class FamilyOperandTypeChecker implements SqlSingleOperandTypeChecker { //~ Instance fields -------------------------------------------------------- protected final ImmutableList<SqlTypeFamily> families; + protected final Predicate<Integer> optional; //~ Constructors ----------------------------------------------------------- /** * Package private. Create using {@link OperandTypes#family}. */ - FamilyOperandTypeChecker(List<SqlTypeFamily> families) { + FamilyOperandTypeChecker(List<SqlTypeFamily> families, + Predicate<Integer> optional) { this.families = ImmutableList.copyOf(families); + this.optional = optional; } //~ Methods ---------------------------------------------------------------- + public boolean isOptional(int i) { + return optional.apply(i); + } + public boolean checkSingleOperandType( SqlCallBinding callBinding, SqlNode node, @@ -97,7 +105,7 @@ public class FamilyOperandTypeChecker implements SqlSingleOperandTypeChecker { return false; } - for (Ord<SqlNode> op : Ord.zip(callBinding.getCall().getOperandList())) { + for (Ord<SqlNode> op : Ord.zip(callBinding.operands())) { if (!checkSingleOperandType( callBinding, op.e, @@ -110,7 +118,12 @@ public class FamilyOperandTypeChecker implements SqlSingleOperandTypeChecker { } public SqlOperandCountRange getOperandCountRange() { - return SqlOperandCountRanges.of(families.size()); + final int max = families.size(); + int min = max; + while (min > 0 && optional.apply(min - 1)) { + --min; + } + return SqlOperandCountRanges.between(min, max); } public String getAllowedSignatures(SqlOperator op, String opName) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java b/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java index f4ab473..938a058 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java @@ -47,7 +47,7 @@ public abstract class InferTypes { final RelDataType unknownType = callBinding.getValidator().getUnknownType(); RelDataType knownType = unknownType; - for (SqlNode operand : callBinding.getCall().getOperandList()) { + for (SqlNode operand : callBinding.operands()) { knownType = callBinding.getValidator().deriveType( callBinding.getScope(), operand); if (!knownType.equals(unknownType)) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/LiteralOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/LiteralOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/LiteralOperandTypeChecker.java index ac94f14..e2ddd7d 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/LiteralOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/LiteralOperandTypeChecker.java @@ -44,6 +44,10 @@ public class LiteralOperandTypeChecker implements SqlSingleOperandTypeChecker { //~ Methods ---------------------------------------------------------------- + public boolean isOptional(int i) { + return false; + } + public boolean checkSingleOperandType( SqlCallBinding callBinding, SqlNode node, @@ -79,7 +83,7 @@ public class LiteralOperandTypeChecker implements SqlSingleOperandTypeChecker { boolean throwOnFailure) { return checkSingleOperandType( callBinding, - callBinding.getCall().operand(0), + callBinding.operand(0), 0, throwOnFailure); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/MultisetOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/MultisetOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/MultisetOperandTypeChecker.java index 2d04c46..c55a1c5 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/MultisetOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/MultisetOperandTypeChecker.java @@ -17,7 +17,6 @@ package org.apache.calcite.sql.type; import org.apache.calcite.rel.type.RelDataType; -import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlCallBinding; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlOperandCountRange; @@ -36,11 +35,14 @@ import static org.apache.calcite.util.Static.RESOURCE; public class MultisetOperandTypeChecker implements SqlOperandTypeChecker { //~ Methods ---------------------------------------------------------------- + public boolean isOptional(int i) { + return false; + } + public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { - SqlCall call = callBinding.getCall(); - final SqlNode op0 = call.operand(0); + final SqlNode op0 = callBinding.operand(0); if (!OperandTypes.MULTISET.checkSingleOperandType( callBinding, op0, @@ -49,7 +51,7 @@ public class MultisetOperandTypeChecker implements SqlOperandTypeChecker { return false; } - final SqlNode op1 = call.operand(1); + final SqlNode op1 = callBinding.operand(1); if (!OperandTypes.MULTISET.checkSingleOperandType( callBinding, op1, http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java index 0d57234..eafd7c6 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java @@ -25,6 +25,8 @@ import org.apache.calcite.sql.SqlOperandCountRange; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlUtil; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import java.math.BigDecimal; @@ -58,7 +60,17 @@ public abstract class OperandTypes { * corresponding family. */ public static FamilyOperandTypeChecker family(SqlTypeFamily... families) { - return new FamilyOperandTypeChecker(ImmutableList.copyOf(families)); + return new FamilyOperandTypeChecker(ImmutableList.copyOf(families), + Predicates.<Integer>alwaysFalse()); + } + + /** + * Creates a checker that passes if each operand is a member of a + * corresponding family, and allows specified parameters to be optional. + */ + public static FamilyOperandTypeChecker family(List<SqlTypeFamily> families, + Predicate<Integer> optional) { + return new FamilyOperandTypeChecker(families, optional); } /** @@ -66,7 +78,7 @@ public abstract class OperandTypes { * corresponding family. */ public static FamilyOperandTypeChecker family(List<SqlTypeFamily> families) { - return new FamilyOperandTypeChecker(families); + return family(families, Predicates.<Integer>alwaysFalse()); } /** @@ -159,6 +171,10 @@ public abstract class OperandTypes { return opName + "(...)"; } + public boolean isOptional(int i) { + return false; + } + public Consistency getConsistency() { return Consistency.NONE; } @@ -241,7 +257,8 @@ public abstract class OperandTypes { * literal. */ public static final SqlSingleOperandTypeChecker POSITIVE_INTEGER_LITERAL = - new FamilyOperandTypeChecker(ImmutableList.of(SqlTypeFamily.INTEGER)) { + new FamilyOperandTypeChecker(ImmutableList.of(SqlTypeFamily.INTEGER), + Predicates.<Integer>alwaysFalse()) { public boolean checkSingleOperandType( SqlCallBinding callBinding, SqlNode node, @@ -416,7 +433,8 @@ public abstract class OperandTypes { public static final SqlSingleOperandTypeChecker MINUS_DATE_OPERATOR = new FamilyOperandTypeChecker( ImmutableList.of(SqlTypeFamily.DATETIME, SqlTypeFamily.DATETIME, - SqlTypeFamily.DATETIME_INTERVAL)) { + SqlTypeFamily.DATETIME_INTERVAL), + Predicates.<Integer>alwaysFalse()) { public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { @@ -474,7 +492,7 @@ public abstract class OperandTypes { boolean throwOnFailure) { return checkSingleOperandType( callBinding, - callBinding.getCall().operand(0), + callBinding.operand(0), 0, throwOnFailure); } @@ -487,6 +505,10 @@ public abstract class OperandTypes { return "UNNEST(<MULTISET>)"; } + public boolean isOptional(int i) { + return false; + } + public Consistency getConsistency() { return Consistency.NONE; } @@ -537,7 +559,7 @@ public abstract class OperandTypes { boolean throwOnFailure) { return checkSingleOperandType( callBinding, - callBinding.getCall().operand(0), + callBinding.operand(0), 0, throwOnFailure); } @@ -551,6 +573,10 @@ public abstract class OperandTypes { ImmutableList.of("RECORDTYPE(SINGLE FIELD)")); } + public boolean isOptional(int i) { + return false; + } + public Consistency getConsistency() { return Consistency.NONE; } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java index dbbd393..b88ea2e 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java @@ -53,6 +53,10 @@ public class SameOperandTypeChecker implements SqlSingleOperandTypeChecker { return Consistency.NONE; } + public boolean isOptional(int i) { + return false; + } + public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { @@ -84,7 +88,7 @@ public class SameOperandTypeChecker implements SqlSingleOperandTypeChecker { if (operatorBinding.isOperandNull(i, false)) { if (throwOnFailure) { throw callBinding.getValidator().newValidationError( - callBinding.getCall().operand(i), RESOURCE.nullIllegal()); + callBinding.operand(i), RESOURCE.nullIllegal()); } else { return false; } http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/SetopOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SetopOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/SetopOperandTypeChecker.java index a38b682..b55d43c 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SetopOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SetopOperandTypeChecker.java @@ -41,6 +41,10 @@ import static org.apache.calcite.util.Static.RESOURCE; public class SetopOperandTypeChecker implements SqlOperandTypeChecker { //~ Methods ---------------------------------------------------------------- + public boolean isOptional(int i) { + return false; + } + public boolean checkOperandTypes( SqlCallBinding callBinding, boolean throwOnFailure) { @@ -70,7 +74,7 @@ public class SetopOperandTypeChecker implements SqlOperandTypeChecker { if (fields.size() != colCount) { if (throwOnFailure) { - SqlNode node = callBinding.getCall().operand(i); + SqlNode node = callBinding.operand(i); if (node instanceof SqlSelect) { node = ((SqlSelect) node).getSelectList(); } @@ -103,9 +107,7 @@ public class SetopOperandTypeChecker implements SqlOperandTypeChecker { if (type == null) { if (throwOnFailure) { SqlNode field = - SqlUtil.getSelectListItem( - callBinding.getCall().operand(0), - i); + SqlUtil.getSelectListItem(callBinding.operand(0), i); throw validator.newValidationError(field, RESOURCE.columnTypeMismatchInSetop(i + 1, // 1-based callBinding.getOperator().getName())); http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/SqlOperandCountRanges.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlOperandCountRanges.java b/core/src/main/java/org/apache/calcite/sql/type/SqlOperandCountRanges.java index 1420c8a..9018084 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlOperandCountRanges.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlOperandCountRanges.java @@ -18,6 +18,8 @@ package org.apache.calcite.sql.type; import org.apache.calcite.sql.SqlOperandCountRange; +import com.google.common.base.Preconditions; + /** * Helpers for {@link SqlOperandCountRange}. */ @@ -27,7 +29,6 @@ public abstract class SqlOperandCountRanges { } public static SqlOperandCountRange between(int min, int max) { - assert min < max; return new RangeImpl(min, max); } @@ -47,6 +48,8 @@ public abstract class SqlOperandCountRanges { public RangeImpl(int min, int max) { this.min = min; this.max = max; + Preconditions.checkArgument(min <= max || max == -1); + Preconditions.checkArgument(min >= 0); } public boolean isValidCount(int count) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/type/SqlOperandTypeChecker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/SqlOperandTypeChecker.java index 5a9eea4..545b2b2 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlOperandTypeChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlOperandTypeChecker.java @@ -59,6 +59,9 @@ public interface SqlOperandTypeChecker { /** Returns the strategy for making the arguments have consistency types. */ Consistency getConsistency(); + /** Returns whether the {@code i}th operand is optional. */ + boolean isOptional(int i); + /** Strategy used to make arguments consistent. */ enum Consistency { /** Do not try to make arguments consistent. */ http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java index 49960f1..163687e 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java @@ -18,6 +18,7 @@ package org.apache.calcite.sql.validate; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.schema.Function; +import org.apache.calcite.schema.FunctionParameter; import org.apache.calcite.sql.SqlFunction; import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlIdentifier; @@ -27,6 +28,8 @@ import org.apache.calcite.sql.type.SqlOperandTypeInference; import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.util.Util; +import com.google.common.collect.Lists; + import java.util.List; /** @@ -57,6 +60,10 @@ public class SqlUserDefinedFunction extends SqlFunction { public Function getFunction() { return function; } + + @Override public List<String> getParamNames() { + return Lists.transform(function.getParameters(), FunctionParameter.NAME_FN); + } } // End SqlUserDefinedFunction.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d012b245/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java index 7e44b15..9ff5e80 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java @@ -636,11 +636,11 @@ public interface SqlValidator { * @param unresolvedFunction Overloaded function which is the target of the * call * @param argTypes Types of arguments + * @param argNames Names of arguments, or null if call by position */ - CalciteException handleUnresolvedFunction( - SqlCall call, - SqlFunction unresolvedFunction, - List<RelDataType> argTypes); + CalciteException handleUnresolvedFunction(SqlCall call, + SqlFunction unresolvedFunction, List<RelDataType> argTypes, + List<String> argNames); /** * Expands an expression in the ORDER BY clause into an expression with the
