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