[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() {

Reply via email to