[CALCITE-937] User-defined function within view

Rationalize how validators are created during prepare.

Every CatalogReader now implements SqlOperatorTable.

Add convenience factory method for ChainedSqlOperatorTable.


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/74803d0a
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/74803d0a
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/74803d0a

Branch: refs/heads/branch-release
Commit: 74803d0ac093a291b38f629fdc3dc720bcc5cd4c
Parents: 52b0621
Author: Julian Hyde <[email protected]>
Authored: Mon Oct 26 16:16:19 2015 -0700
Committer: Julian Hyde <[email protected]>
Committed: Mon Oct 26 16:16:19 2015 -0700

----------------------------------------------------------------------
 .../calcite/prepare/CalciteCatalogReader.java   | 10 ++--
 .../calcite/prepare/CalcitePrepareImpl.java     | 27 ++++++-----
 .../org/apache/calcite/prepare/Prepare.java     |  3 +-
 .../sql/util/ChainedSqlOperatorTable.java       | 12 +++--
 .../java/org/apache/calcite/test/JdbcTest.java  | 49 ++++++++++++++++++++
 .../apache/calcite/test/MockCatalogReader.java  | 14 +++++-
 .../org/apache/calcite/tools/PlannerTest.java   |  6 +--
 7 files changed, 95 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/74803d0a/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 8412305..c2a8f7b 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -34,7 +34,6 @@ import org.apache.calcite.schema.TableMacro;
 import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlIdentifier;
 import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.SqlSyntax;
 import org.apache.calcite.sql.type.InferTypes;
 import org.apache.calcite.sql.type.OperandTypes;
@@ -67,8 +66,7 @@ import java.util.NavigableSet;
  * and also {@link org.apache.calcite.sql.SqlOperatorTable} based on tables and
  * functions defined schemas.
  */
-public class CalciteCatalogReader implements Prepare.CatalogReader,
-    SqlOperatorTable {
+public class CalciteCatalogReader implements Prepare.CatalogReader {
   final CalciteSchema rootSchema;
   final JavaTypeFactory typeFactory;
   private final List<String> defaultSchema;
@@ -171,7 +169,7 @@ public class CalciteCatalogReader implements 
Prepare.CatalogReader,
     if (schema == null) {
       return ImmutableList.of();
     }
-    final List<SqlMoniker> result = new ArrayList<SqlMoniker>();
+    final List<SqlMoniker> result = new ArrayList<>();
     final Map<String, CalciteSchema> schemaMap = schema.getSubSchemaMap();
 
     for (String subSchema : schemaMap.keySet()) {
@@ -245,8 +243,8 @@ public class CalciteCatalogReader implements 
Prepare.CatalogReader,
   }
 
   private SqlOperator toOp(SqlIdentifier name, Function function) {
-    List<RelDataType> argTypes = new ArrayList<RelDataType>();
-    List<SqlTypeFamily> typeFamilies = new ArrayList<SqlTypeFamily>();
+    List<RelDataType> argTypes = new ArrayList<>();
+    List<SqlTypeFamily> typeFamilies = new ArrayList<>();
     for (FunctionParameter o : function.getParameters()) {
       final RelDataType type = o.getType(typeFactory);
       argTypes.add(type);

http://git-wip-us.apache.org/repos/asf/calcite/blob/74803d0a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java 
b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 89180ac..bdcd0e9 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -113,14 +113,13 @@ import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParseException;
 import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.util.ChainedSqlOperatorTable;
-import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.sql.validate.SqlValidator;
-import org.apache.calcite.sql.validate.SqlValidatorImpl;
 import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.calcite.sql2rel.StandardConvertletTable;
 import org.apache.calcite.tools.Frameworks;
@@ -277,8 +276,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
       throw new RuntimeException("parse failed", e);
     }
     final SqlValidator validator =
-        new CalciteSqlValidator(
-            SqlStdOperatorTable.instance(), catalogReader, typeFactory);
+        createSqlValidator(catalogReader, typeFactory);
     SqlNode sqlNode1 = validator.validate(sqlNode);
     if (convert) {
       return convert_(
@@ -713,11 +711,8 @@ public class CalcitePrepareImpl implements CalcitePrepare {
       }
 
       final CalciteSchema rootSchema = context.getRootSchema();
-      final ChainedSqlOperatorTable opTab =
-          new ChainedSqlOperatorTable(
-              ImmutableList.of(SqlStdOperatorTable.instance(), catalogReader));
       final SqlValidator validator =
-          new CalciteSqlValidator(opTab, catalogReader, typeFactory);
+          createSqlValidator(catalogReader, typeFactory);
       validator.setIdentifierExpansion(true);
 
       final List<Prepare.Materialization> materializations =
@@ -794,6 +789,14 @@ public class CalcitePrepareImpl implements CalcitePrepare {
         statementType);
   }
 
+  private SqlValidator createSqlValidator(CalciteCatalogReader catalogReader,
+      JavaTypeFactory typeFactory) {
+    final SqlOperatorTable opTab =
+        ChainedSqlOperatorTable.of(SqlStdOperatorTable.instance(),
+            catalogReader);
+    return new CalciteSqlValidator(opTab, catalogReader, typeFactory);
+  }
+
   private List<ColumnMetaData> getColumnMetaDataList(
       JavaTypeFactory typeFactory, RelDataType x, RelDataType jdbcType,
       List<List<String>> originList) {
@@ -1123,10 +1126,10 @@ public class CalcitePrepareImpl implements 
CalcitePrepare {
       return root;
     }
 
-    private SqlValidatorImpl createSqlValidator(CatalogReader catalogReader) {
-      return new SqlValidatorImpl(
-          SqlStdOperatorTable.instance(), catalogReader,
-          rexBuilder.getTypeFactory(), SqlConformance.DEFAULT) { };
+    private SqlValidator createSqlValidator(CatalogReader catalogReader) {
+      return prepare.createSqlValidator(
+          (CalciteCatalogReader) catalogReader,
+          (JavaTypeFactory) typeFactory);
     }
 
     @Override protected SqlValidator getSqlValidator() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/74803d0a/core/src/main/java/org/apache/calcite/prepare/Prepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java 
b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
index 43ae738..399a178 100644
--- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java
+++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
@@ -42,6 +42,7 @@ import org.apache.calcite.sql.SqlExplain;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorCatalogReader;
 import org.apache.calcite.sql.validate.SqlValidatorTable;
@@ -373,7 +374,7 @@ public abstract class Prepare {
 
   /** Interface by which validator and planner can read table metadata. */
   public interface CatalogReader
-      extends RelOptSchema, SqlValidatorCatalogReader {
+      extends RelOptSchema, SqlValidatorCatalogReader, SqlOperatorTable {
     PreparingTable getTableForMember(List<String> names);
 
     /** Returns a catalog reader the same as this one but with a possibly

http://git-wip-us.apache.org/repos/asf/calcite/blob/74803d0a/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java 
b/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java
index 538c10a..835ae50 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/util/ChainedSqlOperatorTable.java
@@ -22,6 +22,8 @@ import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.SqlSyntax;
 
+import com.google.common.collect.ImmutableList;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -40,7 +42,12 @@ public class ChainedSqlOperatorTable implements 
SqlOperatorTable {
    * Creates a table based on a given list.
    */
   public ChainedSqlOperatorTable(List<SqlOperatorTable> tableList) {
-    this.tableList = tableList;
+    this.tableList = ImmutableList.copyOf(tableList);
+  }
+
+  /** Creates a {@code ChainedSqlOperatorTable}. */
+  public static SqlOperatorTable of(SqlOperatorTable... tables) {
+    return new ChainedSqlOperatorTable(ImmutableList.copyOf(tables));
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -66,9 +73,8 @@ public class ChainedSqlOperatorTable implements 
SqlOperatorTable {
     }
   }
 
-  // implement SqlOperatorTable
   public List<SqlOperator> getOperatorList() {
-    List<SqlOperator> list = new ArrayList<SqlOperator>();
+    List<SqlOperator> list = new ArrayList<>();
     for (SqlOperatorTable table : tableList) {
       list.addAll(table.getOperatorList());
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/74803d0a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index e62c4d0..24dc2ce 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -75,6 +75,7 @@ import org.apache.calcite.schema.TranslatableTable;
 import org.apache.calcite.schema.impl.AbstractSchema;
 import org.apache.calcite.schema.impl.AbstractTable;
 import org.apache.calcite.schema.impl.AbstractTableQueryable;
+import org.apache.calcite.schema.impl.ScalarFunctionImpl;
 import org.apache.calcite.schema.impl.TableFunctionImpl;
 import org.apache.calcite.schema.impl.TableMacroImpl;
 import org.apache.calcite.schema.impl.ViewTable;
@@ -5329,6 +5330,47 @@ public class JdbcTest {
             + "P=20\n");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-937";>[CALCITE-937]
+   * User-defined function within view</a>. */
+  @Test public void testUserDefinedFunctionInView() throws Exception {
+    Class.forName("org.apache.calcite.jdbc.Driver");
+    Connection connection = DriverManager.getConnection("jdbc:calcite:");
+    CalciteConnection calciteConnection =
+        connection.unwrap(CalciteConnection.class);
+    SchemaPlus rootSchema = calciteConnection.getRootSchema();
+    rootSchema.add("hr", new ReflectiveSchema(new HrSchema()));
+
+    SchemaPlus post = rootSchema.add("POST", new AbstractSchema());
+    post.add("MY_INCREMENT",
+        ScalarFunctionImpl.create(MyIncrement.class, "eval"));
+
+    final String viewSql = "select \"empid\" as EMPLOYEE_ID,\n"
+        + "  \"name\" || ' ' || \"name\" as EMPLOYEE_NAME,\n"
+        + "  \"salary\" as EMPLOYEE_SALARY,\n"
+        + "  POST.MY_INCREMENT(\"empid\", 10) as INCREMENTED_SALARY\n"
+        + "from \"hr\".\"emps\"";
+    post.add("V_EMP",
+        ViewTable.viewMacro(post, viewSql, ImmutableList.<String>of(), null));
+
+    final String result = ""
+        + "EMPLOYEE_ID=100; EMPLOYEE_NAME=Bill Bill; EMPLOYEE_SALARY=10000.0; 
INCREMENTED_SALARY=110.0\n"
+        + "EMPLOYEE_ID=200; EMPLOYEE_NAME=Eric Eric; EMPLOYEE_SALARY=8000.0; 
INCREMENTED_SALARY=220.0\n"
+        + "EMPLOYEE_ID=150; EMPLOYEE_NAME=Sebastian Sebastian; 
EMPLOYEE_SALARY=7000.0; INCREMENTED_SALARY=165.0\n"
+        + "EMPLOYEE_ID=110; EMPLOYEE_NAME=Theodore Theodore; 
EMPLOYEE_SALARY=11500.0; INCREMENTED_SALARY=121.0\n";
+
+    Statement statement = connection.createStatement();
+    ResultSet resultSet = statement.executeQuery(viewSql);
+    assertThat(CalciteAssert.toString(resultSet), is(result));
+    resultSet.close();
+
+    ResultSet viewResultSet =
+        statement.executeQuery("select * from \"POST\".\"V_EMP\"");
+    assertThat(CalciteAssert.toString(viewResultSet), is(result));
+    statement.close();
+    connection.close();
+  }
+
   /**
    * Tests that IS NULL/IS NOT NULL is properly implemented for non-strict
    * functions.
@@ -6962,6 +7004,13 @@ public class JdbcTest {
     }
   }
 
+  /** User-defined function with two arguments. */
+  public static class MyIncrement {
+    public float eval(int x, int y) {
+      return x + x * y / 100;
+    }
+  }
+
   /** Example of a UDF that has overloaded UDFs (same name, different args). */
   public abstract static class CountArgs0Function {
     private CountArgs0Function() {}

http://git-wip-us.apache.org/repos/asf/calcite/blob/74803d0a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java 
b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
index 36d7788..33ecff5 100644
--- a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
+++ b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
@@ -48,7 +48,10 @@ import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.schema.Schemas;
 import org.apache.calcite.schema.Table;
 import org.apache.calcite.sql.SqlAccessType;
+import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSyntax;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.ObjectSqlType;
@@ -369,6 +372,15 @@ public class MockCatalogReader implements 
Prepare.CatalogReader {
 
   //~ Methods ----------------------------------------------------------------
 
+  public void lookupOperatorOverloads(SqlIdentifier opName,
+      SqlFunctionCategory category, SqlSyntax syntax,
+      List<SqlOperator> operatorList) {
+  }
+
+  public List<SqlOperator> getOperatorList() {
+    return ImmutableList.of();
+  }
+
   public Prepare.CatalogReader withSchemaPath(List<String> schemaPath) {
     return this;
   }
@@ -580,7 +592,7 @@ public class MockCatalogReader implements 
Prepare.CatalogReader {
                 return null;
               }
 
-              @Override public <T> Queryable<T>
+              @Override public <E> Queryable<E>
               asQueryable(QueryProvider queryProvider, SchemaPlus schema,
                   String tableName) {
                 return null;

http://git-wip-us.apache.org/repos/asf/calcite/blob/74803d0a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java 
b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
index 05c0bde..8b24317 100644
--- a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
+++ b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
@@ -186,10 +186,10 @@ public class PlannerTest {
 
   @Test public void testValidateUserDefinedAggregate() throws Exception {
     final SqlStdOperatorTable stdOpTab = SqlStdOperatorTable.instance();
-    SqlOperatorTable opTab = new ChainedSqlOperatorTable(
-        ImmutableList.of(stdOpTab,
+    SqlOperatorTable opTab =
+        ChainedSqlOperatorTable.of(stdOpTab,
             new ListSqlOperatorTable(
-                ImmutableList.<SqlOperator>of(new MyCountAggFunction()))));
+                ImmutableList.<SqlOperator>of(new MyCountAggFunction())));
     final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
     final FrameworkConfig config = Frameworks.newConfigBuilder()
         .defaultSchema(

Reply via email to