[CALCITE-1062] In validation, lookup a (possibly overloaded) operator from an operator table (Hsuan-Yi Chu)
Close apache/calcite#188 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/c1ceba45 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/c1ceba45 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/c1ceba45 Branch: refs/heads/master Commit: c1ceba45e5939be812bbe0bba6ee27586862b8e0 Parents: 670fa73 Author: Hsuan-Yi Chu <[email protected]> Authored: Mon Jan 18 23:20:57 2016 -0800 Committer: Julian Hyde <[email protected]> Committed: Tue Feb 9 16:02:19 2016 -0800 ---------------------------------------------------------------------- .../org/apache/calcite/sql/SqlFunction.java | 77 ++++------- .../org/apache/calcite/sql/SqlOperator.java | 113 +++++++++++++++- .../java/org/apache/calcite/sql/SqlUtil.java | 132 ++++++++++++++----- .../calcite/sql/fun/SqlBetweenOperator.java | 8 +- .../sql/util/ReflectiveSqlOperatorTable.java | 4 +- .../calcite/sql2rel/SqlToRelConverter.java | 11 +- .../calcite/sql/test/SqlOperatorBaseTest.java | 27 ++++ .../apache/calcite/test/SqlValidatorTest.java | 2 +- 8 files changed, 276 insertions(+), 98 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/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 dc63209..2ed8bda 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlFunction.java @@ -19,12 +19,10 @@ 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; 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.type.SqlTypeName; import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.sql.validate.SqlValidatorScope; import org.apache.calcite.util.Util; @@ -235,71 +233,44 @@ public class SqlFunction extends SqlOperator { SqlValidatorScope scope, SqlCall call, boolean convertRowArgToColumnList) { - // Scope for operands. Usually the same as 'scope'. final SqlValidatorScope operandScope = scope.getOperandScope(call); // 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(); - } + List<String> argNames = constructArgNameList( + call); + + List<SqlNode> args = constructOperandList( + validator, + call, + argNames); + + List<RelDataType> argTypes = constructArgTypeList( + validator, + scope, + call, + args, + convertRowArgToColumnList); + + SqlFunction function = (SqlFunction) SqlUtil.lookupRoutine( + validator.getOperatorTable(), + getNameAsId(), + argTypes, + argNames, + SqlSyntax.FUNCTION, + getKind(), + getFunctionType()); try { - final ImmutableList.Builder<RelDataType> argTypeBuilder = - ImmutableList.builder(); boolean containsRowArg = false; for (SqlNode operand : args) { - RelDataType nodeType; - - // for row arguments that should be converted to ColumnList - // types, set the nodeType to a ColumnList type but defer - // validating the arguments of the row constructor until we know - // for sure that the row argument maps to a ColumnList type if (operand.getKind() == SqlKind.ROW && convertRowArgToColumnList) { containsRowArg = true; - RelDataTypeFactory typeFactory = validator.getTypeFactory(); - nodeType = typeFactory.createSqlType(SqlTypeName.COLUMN_LIST); - } else { - nodeType = validator.deriveType(operandScope, operand); + break; } - validator.setValidatedNodeType(operand, nodeType); - argTypeBuilder.add(nodeType); } - final List<RelDataType> argTypes = argTypeBuilder.build(); - SqlFunction function = - SqlUtil.lookupRoutine( - validator.getOperatorTable(), - getNameAsId(), - argTypes, - argNames, - getFunctionType()); - // if we have a match on function name and parameter count, but // couldn't find a function with a COLUMN_LIST type, retry, but // this time, don't convert the row argument to a COLUMN_LIST type; http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/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 476a1c5..e3d9c27 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java @@ -23,6 +23,7 @@ import org.apache.calcite.sql.parser.SqlParserPos; 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.type.SqlTypeName; import org.apache.calcite.sql.util.SqlBasicVisitor; import org.apache.calcite.sql.util.SqlVisitor; import org.apache.calcite.sql.validate.SqlMonotonicity; @@ -32,6 +33,8 @@ import org.apache.calcite.sql.validate.SqlValidatorUtil; import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Util; +import com.google.common.collect.ImmutableList; + import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -480,12 +483,32 @@ public abstract class SqlOperator { SqlValidator validator, SqlValidatorScope scope, SqlCall call) { - for (SqlNode operand : call.getOperandList()) { - RelDataType nodeType = validator.deriveType(scope, operand); - assert nodeType != null; - } - RelDataType type = validateOperands(validator, scope, call); + List<SqlNode> args = constructOperandList( + validator, + call, + null); + + List<RelDataType> argTypes = constructArgTypeList( + validator, + scope, + call, + args, + false); + + SqlOperator sqlOperator = SqlUtil.lookupRoutine( + validator.getOperatorTable(), + new SqlIdentifier( + call.getOperator().getName(), + call.getParserPosition()), + argTypes, + null, + getSyntax(), + getKind(), + null); + + ((SqlBasicCall) call).setOperator(sqlOperator); + RelDataType type = call.getOperator().validateOperands(validator, scope, call); // Validate and determine coercibility and resulting collation // name of binary operator if needed. @@ -494,6 +517,86 @@ public abstract class SqlOperator { return type; } + protected List<String> constructArgNameList(SqlCall call) { + // If any arguments are named, construct a map. + final ImmutableList.Builder<String> nameBuilder = 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()); + } + } + ImmutableList<String> argNames = nameBuilder.build(); + + if (argNames.isEmpty()) { + return null; + } else { + return argNames; + } + } + + protected List<SqlNode> constructOperandList( + SqlValidator validator, + SqlCall call, + List<String> argNames) { + 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(); + argBuilder.add(operandList.get(0)); + } + } + + final List<SqlNode> args; + if (argNames == null) { + args = call.getOperandList(); + } 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(); + } + + return args; + } + + protected List<RelDataType> constructArgTypeList( + SqlValidator validator, + SqlValidatorScope scope, + SqlCall call, + List<SqlNode> args, + boolean convertRowArgToColumnList) { + // Scope for operands. Usually the same as 'scope'. + final SqlValidatorScope operandScope = scope.getOperandScope(call); + + final ImmutableList.Builder<RelDataType> argTypeBuilder = + ImmutableList.builder(); + for (SqlNode operand : args) { + RelDataType nodeType; + // for row arguments that should be converted to ColumnList + // types, set the nodeType to a ColumnList type but defer + // validating the arguments of the row constructor until we know + // for sure that the row argument maps to a ColumnList type + if (operand.getKind() == SqlKind.ROW && convertRowArgToColumnList) { + RelDataTypeFactory typeFactory = validator.getTypeFactory(); + nodeType = typeFactory.createSqlType(SqlTypeName.COLUMN_LIST); + } else { + nodeType = validator.deriveType(operandScope, operand); + } + validator.setValidatedNodeType(operand, nodeType); + argTypeBuilder.add(nodeType); + } + + final List<RelDataType> argTypes = argTypeBuilder.build(); + return argTypes; + } + /** * Returns whether this operator should be surrounded by space when * unparsed. http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/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 87e73e0..2ba78aa 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java @@ -325,15 +325,22 @@ public abstract class SqlUtil { * @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, - List<String> argNames, SqlFunctionCategory category) { - List<SqlFunction> list = + public static SqlOperator lookupRoutine( + SqlOperatorTable opTab, + SqlIdentifier funcName, + List<RelDataType> argTypes, + List<String> argNames, + SqlSyntax sqlSyntax, + SqlKind sqlKind, + SqlFunctionCategory category) { + List<SqlOperator> list = lookupSubjectRoutines( opTab, funcName, argTypes, argNames, + sqlSyntax, + sqlKind, category); if (list.isEmpty()) { return null; @@ -343,23 +350,42 @@ public abstract class SqlUtil { } } + private static void filterOperatorRoutinesByKind( + List<SqlOperator> routines, + SqlKind sqlKind) { + Iterator<SqlOperator> iter = routines.iterator(); + while (iter.hasNext()) { + SqlOperator sqlOperator = iter.next(); + if (sqlKind != sqlOperator.getKind()) { + iter.remove(); + } + } + } + /** * Looks up all subject routines matching the given name and argument types. * - * @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 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 sqlSyntax the SqlSyntax of the SqlOperator being looked up + * @param sqlKind the SqlKind of the SqlOperator being looked up * @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, - List<String> argNames, SqlFunctionCategory category) { + public static List<SqlOperator> lookupSubjectRoutines( + SqlOperatorTable opTab, + SqlIdentifier funcName, + List<RelDataType> argTypes, + List<String> argNames, + SqlSyntax sqlSyntax, + SqlKind sqlKind, + SqlFunctionCategory category) { // start with all routines matching by name - List<SqlFunction> routines = - lookupSubjectRoutinesByName(opTab, funcName, category); + List<SqlOperator> routines = + lookupSubjectRoutinesByName(opTab, funcName, sqlSyntax, category); // first pass: eliminate routines which don't accept the given // number of arguments @@ -373,8 +399,7 @@ public abstract class SqlUtil { // second pass: eliminate routines which don't accept the given // argument types - filterRoutinesByParameterType(routines, argTypes, argNames); - + filterRoutinesByParameterType(sqlSyntax, 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 if (routines.size() < 2) { @@ -384,8 +409,11 @@ public abstract class SqlUtil { // third pass: for each parameter from left to right, eliminate // all routines except those with the best precedence match for // the given arguments - filterRoutinesByTypePrecedence(routines, argTypes); + filterRoutinesByTypePrecedence(sqlSyntax, routines, argTypes); + // fourth pass: eliminate routines which do not have the same + // SqlKind as requested + filterOperatorRoutinesByKind(routines, sqlKind); return routines; } @@ -406,8 +434,8 @@ public abstract class SqlUtil { List<RelDataType> argTypes, SqlFunctionCategory category) { // start with all routines matching by name - List<SqlFunction> routines = - lookupSubjectRoutinesByName(opTab, funcName, category); + List<SqlOperator> routines = + lookupSubjectRoutinesByName(opTab, funcName, SqlSyntax.FUNCTION, category); // first pass: eliminate routines which don't accept the given // number of arguments @@ -416,29 +444,37 @@ public abstract class SqlUtil { return routines.size() > 0; } - private static List<SqlFunction> lookupSubjectRoutinesByName( + private static List<SqlOperator> lookupSubjectRoutinesByName( SqlOperatorTable opTab, SqlIdentifier funcName, + SqlSyntax sqlSyntax, SqlFunctionCategory category) { - final List<SqlOperator> operators = Lists.newArrayList(); - opTab.lookupOperatorOverloads(funcName, category, SqlSyntax.FUNCTION, - operators); - List<SqlFunction> routines = new ArrayList<SqlFunction>(); - for (SqlOperator operator : operators) { - if (operator instanceof SqlFunction) { - routines.add((SqlFunction) operator); + final List<SqlOperator> sqlOperators = Lists.newArrayList(); + opTab.lookupOperatorOverloads( + funcName, + category, + sqlSyntax, + sqlOperators); + List<SqlOperator> routines = Lists.newArrayList(); + for (SqlOperator sqlOperator : sqlOperators) { + if (sqlSyntax == SqlSyntax.FUNCTION) { + if (sqlOperator instanceof SqlFunction) { + routines.add(sqlOperator); + } + } else if (sqlSyntax == sqlOperator.getSyntax()) { + routines.add(sqlOperator); } } return routines; } private static void filterRoutinesByParameterCount( - List<SqlFunction> routines, + List<SqlOperator> routines, List<RelDataType> argTypes) { - Iterator<SqlFunction> iter = routines.iterator(); + Iterator<? extends SqlOperator> iter = routines.iterator(); while (iter.hasNext()) { - SqlFunction function = iter.next(); - SqlOperandCountRange od = function.getOperandCountRange(); + SqlOperator operator = iter.next(); + SqlOperandCountRange od = operator.getOperandCountRange(); if (!od.isValidCount(argTypes.size())) { iter.remove(); } @@ -448,9 +484,21 @@ 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, + private static void filterRoutinesByParameterType( + SqlSyntax sqlSyntax, + List<SqlOperator> routines, final List<RelDataType> argTypes, List<String> argNames) { - Iterator<SqlFunction> iter = routines.iterator(); + if (sqlSyntax != SqlSyntax.FUNCTION) { + return; + } + + List<SqlFunction> sqlFunctions = Lists.newArrayList(); + for (SqlOperator sqlOperator : routines) { + sqlFunctions.add((SqlFunction) sqlOperator); + } + routines.clear(); + + Iterator<SqlFunction> iter = sqlFunctions.iterator(); loop: while (iter.hasNext()) { SqlFunction function = iter.next(); @@ -499,19 +547,32 @@ public abstract class SqlUtil { } } } + + routines.addAll(sqlFunctions); } /** * @sql.99 Part 2 Section 9.4 */ private static void filterRoutinesByTypePrecedence( - List<SqlFunction> routines, + SqlSyntax sqlSyntax, + List<SqlOperator> routines, List<RelDataType> argTypes) { + if (sqlSyntax != SqlSyntax.FUNCTION) { + return; + } + + List<SqlFunction> sqlFunctions = Lists.newArrayList(); + for (SqlOperator sqlOperator : routines) { + sqlFunctions.add((SqlFunction) sqlOperator); + } + routines.clear(); + for (int i = 0; i < argTypes.size(); ++i) { RelDataTypePrecedenceList precList = argTypes.get(i).getPrecedenceList(); RelDataType bestMatch = null; - for (SqlFunction function : routines) { + for (SqlFunction function : sqlFunctions) { List<RelDataType> paramTypes = function.getParamTypes(); if (paramTypes == null) { continue; @@ -530,7 +591,7 @@ public abstract class SqlUtil { } } if (bestMatch != null) { - Iterator<SqlFunction> iter = routines.iterator(); + Iterator<SqlFunction> iter = sqlFunctions.iterator(); while (iter.hasNext()) { SqlFunction function = iter.next(); List<RelDataType> paramTypes = function.getParamTypes(); @@ -548,6 +609,7 @@ public abstract class SqlUtil { } } } + routines.addAll(sqlFunctions); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java index 03b6646..4dff351 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java @@ -159,6 +159,12 @@ public class SqlBetweenOperator extends SqlInfixOperator { return "{1} {0} {2} AND {3}"; } + @Override public String getName() { + return super.getName() + + " " + + flag.name(); + } + public void unparse( SqlWriter writer, SqlCall call, @@ -167,7 +173,7 @@ public class SqlBetweenOperator extends SqlInfixOperator { final SqlWriter.Frame frame = writer.startList(FRAME_TYPE, "", ""); call.operand(VALUE_OPERAND).unparse(writer, getLeftPrec(), 0); - writer.sep(getName()); + writer.sep(super.getName()); writer.sep(flag.name()); // If the expression for the lower bound contains a call to an AND http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java index 97691fa..3291095 100644 --- a/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java +++ b/core/src/main/java/org/apache/calcite/sql/util/ReflectiveSqlOperatorTable.java @@ -143,7 +143,7 @@ public abstract class ReflectiveSqlOperatorTable implements SqlOperatorTable { } public void register(SqlOperator op) { - operators.put(op.getName(), op); + operators.put(op.getName().toUpperCase(), op); if (op instanceof SqlBinaryOperator) { mapNameToOp.put(Pair.of(op.getName(), SqlSyntax.BINARY), op); } else if (op instanceof SqlPrefixOperator) { @@ -159,7 +159,7 @@ public abstract class ReflectiveSqlOperatorTable implements SqlOperatorTable { * @param function Function to register */ public void register(SqlFunction function) { - operators.put(function.getName(), function); + operators.put(function.getName().toUpperCase(), function); SqlFunctionCategory funcType = function.getFunctionType(); assert funcType != null : "Function type for " + function.getName() + " not set"; http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java index 5a51ca9..31a2b5c 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -1831,8 +1831,17 @@ public class SqlToRelConverter { SqlNode windowOrRef = call.operand(1); final SqlWindow window = validator.resolveWindow(windowOrRef, bb.scope, true); + final SqlOperator row_number_function = SqlUtil.lookupRoutine( + opTab, + SqlStdOperatorTable.ROW_NUMBER.getNameAsId(), + ImmutableList.<RelDataType>of(), + ImmutableList.<String>of(), + SqlStdOperatorTable.ROW_NUMBER.getSyntax(), + SqlStdOperatorTable.ROW_NUMBER.getKind(), + SqlStdOperatorTable.ROW_NUMBER.getFunctionType()); + // ROW_NUMBER() expects specific kind of framing. - if (aggCall.getOperator() == SqlStdOperatorTable.ROW_NUMBER) { + if (aggCall.getOperator() == row_number_function) { window.setLowerBound(SqlWindow.createUnboundedPreceding(SqlParserPos.ZERO)); window.setUpperBound(SqlWindow.createCurrentRow(SqlParserPos.ZERO)); window.setRows(SqlLiteral.createBoolean(true, SqlParserPos.ZERO)); http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java index 54e808a..c11571a 100644 --- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java +++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java @@ -67,11 +67,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.TimeZone; import java.util.regex.Pattern; +import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -297,6 +300,30 @@ public abstract class SqlOperatorBaseTest { @Test public void testDummy() { } + @Test public void testSqlOperatorOverloading() { + final SqlStdOperatorTable operatorTable = SqlStdOperatorTable.instance(); + for (SqlOperator sqlOperator : operatorTable.getOperatorList()) { + String operatorName = sqlOperator.getName(); + List<SqlOperator> routines = new ArrayList<>(); + operatorTable.lookupOperatorOverloads( + new SqlIdentifier(operatorName, SqlParserPos.ZERO), + null, + sqlOperator.getSyntax(), + routines); + + Iterator<SqlOperator> iter = routines.iterator(); + while (iter.hasNext()) { + SqlOperator operator = iter.next(); + if (!sqlOperator.getClass().isInstance(operator)) { + iter.remove(); + } + } + assertThat(routines.size(), equalTo(1)); + assertThat(sqlOperator, equalTo(routines.get(0))); + } + } + + @Test public void testBetween() { tester.setFor( SqlStdOperatorTable.BETWEEN, http://git-wip-us.apache.org/repos/asf/calcite/blob/c1ceba45/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java index cde2b8c..b3a8699 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -632,7 +632,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase { checkExp("'a' between 'b' and 'c'"); checkExp("'' between 2 and 3"); // can implicitly convert CHAR to INTEGER checkWholeExpFails("date '2012-02-03' between 2 and 3", - "(?s).*Cannot apply 'BETWEEN' to arguments of type.*"); + "(?s).*Cannot apply 'BETWEEN ASYMMETRIC' to arguments of type.*"); } @Test public void testCharsetMismatch() {
