You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by vo...@apache.org on 2022/06/13 18:15:47 UTC
[calcite] branch main updated: [CALCITE-4448] Use TableMacro user-defined table functions with QueryableTable
This is an automated email from the ASF dual-hosted git repository.
volodymyr pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new d1cf4a38b [CALCITE-4448] Use TableMacro user-defined table functions with QueryableTable
d1cf4a38b is described below
commit d1cf4a38b71b514b1d208b5ffa3220b92693464f
Author: Volodymyr Vysotskyi <vv...@gmail.com>
AuthorDate: Sun Dec 27 23:01:49 2020 +0200
[CALCITE-4448] Use TableMacro user-defined table functions with QueryableTable
---
.../apache/calcite/prepare/RelOptTableImpl.java | 79 ++++++-------
.../java/org/apache/calcite/schema/Schemas.java | 29 +++++
.../apache/calcite/sql2rel/SqlToRelConverter.java | 12 +-
.../java/org/apache/calcite/test/JdbcTest.java | 4 +-
.../calcite/test/RelMdColumnOriginsTest.java | 3 +-
.../org/apache/calcite/test/TableFunctionTest.java | 22 ++++
.../apache/calcite/test/TableInRootSchemaTest.java | 111 +----------------
.../linq4j/tree/TableExpressionFactory.java | 33 ++++++
.../java/org/apache/calcite/piglet/PigTable.java | 2 +-
.../main/java/org/apache/calcite/util/Smalls.java | 131 +++++++++++++++++++++
10 files changed, 269 insertions(+), 157 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
index 087829249..29a6f07c0 100644
--- a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
@@ -18,6 +18,7 @@ package org.apache.calcite.prepare;
import org.apache.calcite.jdbc.CalciteSchema;
import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.TableExpressionFactory;
import org.apache.calcite.materialize.Lattice;
import org.apache.calcite.plan.RelOptSchema;
import org.apache.calcite.plan.RelOptTable;
@@ -34,11 +35,8 @@ import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelProtoDataType;
import org.apache.calcite.rel.type.RelRecordType;
import org.apache.calcite.schema.ColumnStrategy;
-import org.apache.calcite.schema.FilterableTable;
import org.apache.calcite.schema.ModifiableTable;
import org.apache.calcite.schema.Path;
-import org.apache.calcite.schema.ProjectableFilterableTable;
-import org.apache.calcite.schema.QueryableTable;
import org.apache.calcite.schema.ScannableTable;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
@@ -66,7 +64,6 @@ import java.util.AbstractList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
-import java.util.function.Function;
import static java.util.Objects.requireNonNull;
@@ -77,7 +74,7 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
private final @Nullable RelOptSchema schema;
private final RelDataType rowType;
private final @Nullable Table table;
- private final @Nullable Function<Class, Expression> expressionFunction;
+ private final @Nullable TableExpressionFactory tableExpressionFactory;
private final ImmutableList<String> names;
/** Estimate for the row count, or null.
@@ -94,13 +91,13 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
RelDataType rowType,
List<String> names,
@Nullable Table table,
- @Nullable Function<Class, Expression> expressionFunction,
+ @Nullable TableExpressionFactory tableExpressionFactory,
@Nullable Double rowCount) {
this.schema = schema;
this.rowType = requireNonNull(rowType, "rowType");
this.names = ImmutableList.copyOf(names);
this.table = table; // may be null
- this.expressionFunction = expressionFunction; // may be null
+ this.tableExpressionFactory = tableExpressionFactory; // may be null
this.rowCount = rowCount; // may be null
}
@@ -113,29 +110,53 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
c -> expression, null);
}
+ @Deprecated // to be removed before 2.0
public static RelOptTableImpl create(
@Nullable RelOptSchema schema,
RelDataType rowType,
List<String> names,
Table table,
Expression expression) {
+ return create(schema, rowType, names, table, c -> expression);
+ }
+
+ /**
+ * Creates {@link RelOptTableImpl} instance with specified arguments
+ * and row count obtained from table statistic.
+ *
+ * @param schema table schema
+ * @param rowType table row type
+ * @param names full table path
+ * @param table table
+ * @param expressionFactory expression function for accessing table data
+ * in the generated code
+ *
+ * @return {@link RelOptTableImpl} instance
+ */
+ public static RelOptTableImpl create(
+ @Nullable RelOptSchema schema,
+ RelDataType rowType,
+ List<String> names,
+ Table table,
+ TableExpressionFactory expressionFactory) {
return new RelOptTableImpl(schema, rowType, names, table,
- c -> expression, table.getStatistic().getRowCount());
+ expressionFactory, table.getStatistic().getRowCount());
}
public static RelOptTableImpl create(@Nullable RelOptSchema schema, RelDataType rowType,
Table table, Path path) {
final SchemaPlus schemaPlus = MySchemaPlus.create(path);
return new RelOptTableImpl(schema, rowType, Pair.left(path), table,
- getClassExpressionFunction(schemaPlus, Util.last(path).left, table),
+ c -> Schemas.getTableExpression(schemaPlus, Util.last(path).left, table, c),
table.getStatistic().getRowCount());
}
public static RelOptTableImpl create(@Nullable RelOptSchema schema, RelDataType rowType,
final CalciteSchema.TableEntry tableEntry, @Nullable Double rowCount) {
final Table table = tableEntry.getTable();
- return new RelOptTableImpl(schema, rowType, tableEntry.path(),
- table, getClassExpressionFunction(tableEntry, table), rowCount);
+ return new RelOptTableImpl(schema, rowType, tableEntry.path(), table,
+ c -> Schemas.getTableExpression(tableEntry.schema.plus(), tableEntry.name, table, c),
+ rowCount);
}
/**
@@ -143,7 +164,7 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
*/
public RelOptTableImpl copy(RelDataType newRowType) {
return new RelOptTableImpl(this.schema, newRowType, this.names, this.table,
- this.expressionFunction, this.rowCount);
+ this.tableExpressionFactory, this.rowCount);
}
@Override public String toString() {
@@ -155,32 +176,6 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
+ '}';
}
- private static Function<Class, Expression> getClassExpressionFunction(
- CalciteSchema.TableEntry tableEntry, Table table) {
- return getClassExpressionFunction(tableEntry.schema.plus(), tableEntry.name,
- table);
- }
-
- private static Function<Class, Expression> getClassExpressionFunction(
- final SchemaPlus schema, final String tableName, final Table table) {
- if (table instanceof QueryableTable) {
- final QueryableTable queryableTable = (QueryableTable) table;
- return clazz -> queryableTable.getExpression(schema, tableName, clazz);
- } else if (table instanceof ScannableTable
- || table instanceof FilterableTable
- || table instanceof ProjectableFilterableTable) {
- return clazz -> Schemas.tableExpression(schema, Object[].class, tableName,
- table.getClass());
- } else if (table instanceof StreamableTable) {
- return getClassExpressionFunction(schema, tableName,
- ((StreamableTable) table).stream());
- } else {
- return input -> {
- throw new UnsupportedOperationException();
- };
- }
- }
-
public static RelOptTableImpl create(@Nullable RelOptSchema schema,
RelDataType rowType, Table table, ImmutableList<String> names) {
assert table instanceof TranslatableTable
@@ -211,10 +206,10 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
}
@Override public @Nullable Expression getExpression(Class clazz) {
- if (expressionFunction == null) {
+ if (tableExpressionFactory == null) {
return null;
}
- return expressionFunction.apply(clazz);
+ return tableExpressionFactory.create(clazz);
}
@Override protected RelOptTable extend(Table extendedTable) {
@@ -222,7 +217,7 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
final RelDataType extendedRowType =
extendedTable.getRowType(schema.getTypeFactory());
return new RelOptTableImpl(schema, extendedRowType, getQualifiedName(),
- extendedTable, expressionFunction, getRowCount());
+ extendedTable, tableExpressionFactory, getRowCount());
}
@Override public boolean equals(@Nullable Object obj) {
@@ -274,7 +269,7 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
}
final RelOptTable relOptTable =
new RelOptTableImpl(this.schema, b.build(), this.names, this.table,
- this.expressionFunction, this.rowCount) {
+ this.tableExpressionFactory, this.rowCount) {
@Override public <T extends Object> @Nullable T unwrap(Class<T> clazz) {
if (clazz.isAssignableFrom(InitializerExpressionFactory.class)) {
return clazz.cast(NullInitializerExpressionFactory.INSTANCE);
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index 54a82f1b8..634abda7f 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -184,6 +184,35 @@ public final class Schemas {
return EnumUtils.convert(expression, clazz);
}
+ /**
+ * Generates an expression with which table can be referenced in
+ * generated code.
+ *
+ * @param schema Schema
+ * @param tableName Table name (unique within schema)
+ * @param table Table to be referenced
+ * @param clazz Class that provides specific methods for accessing table data.
+ * It may differ from the {@code table} class; for example {@code clazz} may be
+ * {@code MongoTable.MongoQueryable}, though {@code table} is {@code MongoTable}
+ */
+ public static Expression getTableExpression(SchemaPlus schema, String tableName,
+ Table table, Class<?> clazz) {
+ if (table instanceof QueryableTable) {
+ QueryableTable queryableTable = (QueryableTable) table;
+ return queryableTable.getExpression(schema, tableName, clazz);
+ } else if (table instanceof ScannableTable
+ || table instanceof FilterableTable
+ || table instanceof ProjectableFilterableTable) {
+ return tableExpression(schema, Object[].class, tableName,
+ table.getClass());
+ } else if (table instanceof StreamableTable) {
+ return getTableExpression(schema, tableName,
+ ((StreamableTable) table).stream(), clazz);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
public static DataContext createDataContext(
Connection connection, @Nullable SchemaPlus rootSchema) {
return DataContexts.of((CalciteConnection) connection, rootSchema);
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 adc19e5d2..166b593f1 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -17,7 +17,9 @@
package org.apache.calcite.sql2rel;
import org.apache.calcite.avatica.util.Spaces;
+import org.apache.calcite.jdbc.CalciteSchema;
import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.linq4j.tree.TableExpressionFactory;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptSamplingParameters;
@@ -90,6 +92,7 @@ import org.apache.calcite.rex.RexWindowBounds;
import org.apache.calcite.schema.ColumnStrategy;
import org.apache.calcite.schema.ModifiableTable;
import org.apache.calcite.schema.ModifiableView;
+import org.apache.calcite.schema.Schemas;
import org.apache.calcite.schema.Table;
import org.apache.calcite.schema.TranslatableTable;
import org.apache.calcite.schema.Wrapper;
@@ -2667,8 +2670,13 @@ public class SqlToRelConverter {
(SqlUserDefinedTableMacro) operator;
final TranslatableTable table = udf.getTable(callBinding);
final RelDataType rowType = table.getRowType(typeFactory);
- RelOptTable relOptTable = RelOptTableImpl.create(null, rowType, table,
- udf.getNameAsId().names);
+ CalciteSchema schema = Schemas.subSchema(
+ catalogReader.getRootSchema(), udf.getNameAsId().skipLast(1).names);
+ TableExpressionFactory expressionFunction =
+ clazz -> Schemas.getTableExpression(Objects.requireNonNull(schema, "schema").plus(),
+ Util.last(udf.getNameAsId().names), table, clazz);
+ RelOptTable relOptTable = RelOptTableImpl.create(null, rowType,
+ udf.getNameAsId().names, table, expressionFunction);
RelNode converted = toRel(relOptTable, ImmutableList.of());
bb.setRoot(converted, true);
return;
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 2ae92cb1b..0725666ef 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -7262,8 +7262,8 @@ public class JdbcTest {
}
// add tables and retrieve with various case sensitivities
- final TableInRootSchemaTest.SimpleTable table =
- new TableInRootSchemaTest.SimpleTable();
+ final Smalls.SimpleTable table =
+ new Smalls.SimpleTable();
a2Schema.add("table1", table);
a2Schema.add("TABLE1", table);
a2Schema.add("tabLe1", table);
diff --git a/core/src/test/java/org/apache/calcite/test/RelMdColumnOriginsTest.java b/core/src/test/java/org/apache/calcite/test/RelMdColumnOriginsTest.java
index 4f19ebc9b..d034e9906 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMdColumnOriginsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMdColumnOriginsTest.java
@@ -17,6 +17,7 @@
package org.apache.calcite.test;
import org.apache.calcite.jdbc.CalciteConnection;
+import org.apache.calcite.util.Smalls;
import com.google.common.collect.ImmutableMultiset;
@@ -43,7 +44,7 @@ class RelMdColumnOriginsTest {
connection.unwrap(CalciteConnection.class);
calciteConnection.getRootSchema().add("T1",
- new TableInRootSchemaTest.SimpleTable());
+ new Smalls.SimpleTable());
Statement statement = calciteConnection.createStatement();
ResultSet resultSet =
statement.executeQuery("SELECT TABLE1.ID, TABLE2.ID FROM "
diff --git a/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java b/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java
index 36d32295f..ab50be530 100644
--- a/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java
+++ b/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java
@@ -547,4 +547,26 @@ class TableFunctionTest {
assertThat(CalciteAssert.toString(resultSet), equalTo(expected));
}
}
+
+ /** Test case for
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-4448">[CALCITE-4448]
+ * Use TableMacro user-defined table functions with QueryableTable</a>. */
+ @Test void testQueryableTableWithTableMacro() throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection("jdbc:calcite:")) {
+ CalciteConnection calciteConnection =
+ connection.unwrap(CalciteConnection.class);
+ SchemaPlus rootSchema = calciteConnection.getRootSchema();
+ SchemaPlus schema = rootSchema.add("s", new AbstractSchema());
+ schema.add("simple", new Smalls.SimpleTableMacro());
+
+ String sql = "select * from table(\"s\".\"simple\"())";
+ ResultSet resultSet = connection.createStatement().executeQuery(sql);
+ String expected = "A=foo; B=5\n"
+ + "A=bar; B=4\n"
+ + "A=foo; B=3\n";
+ assertThat(CalciteAssert.toString(resultSet),
+ equalTo(expected));
+ }
+ }
}
diff --git a/core/src/test/java/org/apache/calcite/test/TableInRootSchemaTest.java b/core/src/test/java/org/apache/calcite/test/TableInRootSchemaTest.java
index 5127fd87c..55b058a2f 100644
--- a/core/src/test/java/org/apache/calcite/test/TableInRootSchemaTest.java
+++ b/core/src/test/java/org/apache/calcite/test/TableInRootSchemaTest.java
@@ -16,21 +16,8 @@
*/
package org.apache.calcite.test;
-import org.apache.calcite.adapter.enumerable.EnumerableTableScan;
-import org.apache.calcite.adapter.java.AbstractQueryableTable;
import org.apache.calcite.jdbc.CalciteConnection;
-import org.apache.calcite.linq4j.Enumerator;
-import org.apache.calcite.linq4j.Linq4j;
-import org.apache.calcite.linq4j.QueryProvider;
-import org.apache.calcite.linq4j.Queryable;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.schema.SchemaPlus;
-import org.apache.calcite.schema.TranslatableTable;
-import org.apache.calcite.schema.impl.AbstractTableQueryable;
-import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Smalls;
import com.google.common.collect.ImmutableMultiset;
@@ -41,10 +28,6 @@ import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
@@ -61,7 +44,7 @@ class TableInRootSchemaTest {
CalciteConnection calciteConnection =
connection.unwrap(CalciteConnection.class);
- calciteConnection.getRootSchema().add("SAMPLE", new SimpleTable());
+ calciteConnection.getRootSchema().add("SAMPLE", new Smalls.SimpleTable());
Statement statement = calciteConnection.createStatement();
ResultSet resultSet =
statement.executeQuery("select A, SUM(B) from SAMPLE group by A");
@@ -90,94 +73,4 @@ class TableInRootSchemaTest {
connection.close();
}
- /** Table with columns (A, B). */
- public static class SimpleTable extends AbstractQueryableTable
- implements TranslatableTable {
- private String[] columnNames = { "A", "B" };
- private Class[] columnTypes = { String.class, Integer.class };
- private Object[][] rows = new Object[3][];
-
- SimpleTable() {
- super(Object[].class);
-
- rows[0] = new Object[] { "foo", 5 };
- rows[1] = new Object[] { "bar", 4 };
- rows[2] = new Object[] { "foo", 3 };
- }
-
- public RelDataType getRowType(RelDataTypeFactory typeFactory) {
- int columnCount = columnNames.length;
- final List<Pair<String, RelDataType>> columnDesc =
- new ArrayList<>(columnCount);
- for (int i = 0; i < columnCount; i++) {
- final RelDataType colType = typeFactory
- .createJavaType(columnTypes[i]);
- columnDesc.add(Pair.of(columnNames[i], colType));
- }
- return typeFactory.createStructType(columnDesc);
- }
-
- public Iterator<Object[]> iterator() {
- return Linq4j.enumeratorIterator(enumerator());
- }
-
- public Enumerator<Object[]> enumerator() {
- return enumeratorImpl(null);
- }
-
- public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
- SchemaPlus schema, String tableName) {
- return new AbstractTableQueryable<T>(queryProvider, schema, this,
- tableName) {
- public Enumerator<T> enumerator() {
- //noinspection unchecked
- return (Enumerator<T>) enumeratorImpl(null);
- }
- };
- }
-
- private Enumerator<Object[]> enumeratorImpl(final int[] fields) {
- return new Enumerator<Object[]>() {
- private Object[] current;
- private Iterator<Object[]> iterator = Arrays.asList(rows)
- .iterator();
-
- public Object[] current() {
- return current;
- }
-
- public boolean moveNext() {
- if (iterator.hasNext()) {
- Object[] full = iterator.next();
- current = fields != null ? convertRow(full) : full;
- return true;
- } else {
- current = null;
- return false;
- }
- }
-
- public void reset() {
- throw new UnsupportedOperationException();
- }
-
- public void close() {
- // noop
- }
-
- private Object[] convertRow(Object[] full) {
- final Object[] objects = new Object[fields.length];
- for (int i = 0; i < fields.length; i++) {
- objects[i] = full[fields[i]];
- }
- return objects;
- }
- };
- }
-
- public RelNode toRel(RelOptTable.ToRelContext context,
- RelOptTable relOptTable) {
- return EnumerableTableScan.create(context.getCluster(), relOptTable);
- }
- }
}
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TableExpressionFactory.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TableExpressionFactory.java
new file mode 100644
index 000000000..e3ea73da8
--- /dev/null
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/TableExpressionFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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.linq4j.tree;
+
+/**
+ * Factory for creating table expressions that may be used in generated code
+ * for accessing table data.
+ */
+public interface TableExpressionFactory {
+
+ /**
+ * Creates {@link Expression} to be used in generated code for accessing table data.
+ *
+ * @param clazz Class that provides specific methods for accessing table data.
+ *
+ * @return {@link Expression} instance
+ */
+ Expression create(Class clazz);
+}
diff --git a/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java b/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java
index 0ab303a5a..94a8f7427 100644
--- a/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java
+++ b/piglet/src/main/java/org/apache/calcite/piglet/PigTable.java
@@ -59,7 +59,7 @@ public class PigTable extends AbstractTable implements ScannableTable {
RelDataType rowType, List<String> names) {
final PigTable pigTable = new PigTable(rowType);
return RelOptTableImpl.create(schema, rowType, names, pigTable,
- Expressions.constant(Boolean.TRUE));
+ c -> Expressions.constant(Boolean.TRUE));
}
@Override public RelDataType getRowType(final RelDataTypeFactory typeFactory) {
diff --git a/testkit/src/main/java/org/apache/calcite/util/Smalls.java b/testkit/src/main/java/org/apache/calcite/util/Smalls.java
index fefa32491..deba6b010 100644
--- a/testkit/src/main/java/org/apache/calcite/util/Smalls.java
+++ b/testkit/src/main/java/org/apache/calcite/util/Smalls.java
@@ -17,6 +17,7 @@
package org.apache.calcite.util;
import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.enumerable.EnumerableTableScan;
import org.apache.calcite.adapter.java.AbstractQueryableTable;
import org.apache.calcite.config.CalciteConnectionConfig;
import org.apache.calcite.linq4j.AbstractEnumerable;
@@ -29,21 +30,30 @@ import org.apache.calcite.linq4j.Queryable;
import org.apache.calcite.linq4j.function.Deterministic;
import org.apache.calcite.linq4j.function.Parameter;
import org.apache.calcite.linq4j.function.SemiStrict;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.linq4j.tree.MethodCallExpression;
import org.apache.calcite.linq4j.tree.Types;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.externalize.RelJsonReader;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.runtime.SqlFunctions;
import org.apache.calcite.schema.FunctionContext;
+import org.apache.calcite.schema.FunctionParameter;
import org.apache.calcite.schema.QueryableTable;
import org.apache.calcite.schema.ScannableTable;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.Schemas;
import org.apache.calcite.schema.Statistic;
import org.apache.calcite.schema.Statistics;
+import org.apache.calcite.schema.TableMacro;
import org.apache.calcite.schema.TranslatableTable;
import org.apache.calcite.schema.impl.AbstractTable;
+import org.apache.calcite.schema.impl.AbstractTableQueryable;
import org.apache.calcite.schema.impl.ViewTable;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlNode;
@@ -61,7 +71,10 @@ import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.AbstractList;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -1313,4 +1326,122 @@ public class Smalls {
this.sale0 = sale;
}
}
+
+ /**
+ * Implementation of {@link TableMacro} interface with
+ * {@link #apply} method that returns {@link Queryable} table.
+ */
+ public static class SimpleTableMacro implements TableMacro {
+
+ @Override public TranslatableTable apply(List<?> arguments) {
+ return new SimpleTable();
+ }
+
+ @Override public List<FunctionParameter> getParameters() {
+ return Collections.emptyList();
+ }
+ }
+
+ /** Table with columns (A, B). */
+ public static class SimpleTable extends AbstractQueryableTable
+ implements TranslatableTable {
+ private final String[] columnNames = { "A", "B" };
+ private final Class<?>[] columnTypes = { String.class, Integer.class };
+ private final Object[][] rows = new Object[3][];
+
+ public SimpleTable() {
+ super(Object[].class);
+
+ rows[0] = new Object[] { "foo", 5 };
+ rows[1] = new Object[] { "bar", 4 };
+ rows[2] = new Object[] { "foo", 3 };
+ }
+
+ @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+ int columnCount = columnNames.length;
+ final List<Pair<String, RelDataType>> columnDesc =
+ new ArrayList<>(columnCount);
+ for (int i = 0; i < columnCount; i++) {
+ final RelDataType colType = typeFactory
+ .createJavaType(columnTypes[i]);
+ columnDesc.add(Pair.of(columnNames[i], colType));
+ }
+ return typeFactory.createStructType(columnDesc);
+ }
+
+ public Iterator<Object[]> iterator() {
+ return Linq4j.enumeratorIterator(enumerator());
+ }
+
+ public Enumerator<Object[]> enumerator() {
+ return enumeratorImpl(null);
+ }
+
+ @Override public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
+ SchemaPlus schema, String tableName) {
+ return new AbstractTableQueryable<T>(queryProvider, schema, this,
+ tableName) {
+ @Override public Enumerator<T> enumerator() {
+ //noinspection unchecked
+ return (Enumerator<T>) enumeratorImpl(null);
+ }
+ };
+ }
+
+ private Enumerator<Object[]> enumeratorImpl(final int[] fields) {
+ return new Enumerator<Object[]>() {
+ private Object[] current;
+ private final Iterator<Object[]> iterator = Arrays.asList(rows)
+ .iterator();
+
+ @Override public Object[] current() {
+ return current;
+ }
+
+ @Override public boolean moveNext() {
+ if (iterator.hasNext()) {
+ Object[] full = iterator.next();
+ current = fields != null ? convertRow(full) : full;
+ return true;
+ } else {
+ current = null;
+ return false;
+ }
+ }
+
+ @Override public void reset() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public void close() {
+ // noop
+ }
+
+ private Object[] convertRow(Object[] full) {
+ final Object[] objects = new Object[fields.length];
+ for (int i = 0; i < fields.length; i++) {
+ objects[i] = full[fields[i]];
+ }
+ return objects;
+ }
+ };
+ }
+
+ @Override public RelNode toRel(
+ RelOptTable.ToRelContext context,
+ RelOptTable relOptTable) {
+ return EnumerableTableScan.create(context.getCluster(), relOptTable);
+ }
+
+ @Override public Expression getExpression(SchemaPlus schema, String tableName, Class clazz) {
+ MethodCallExpression queryableExpression =
+ Expressions.call(Expressions.new_(SimpleTable.class),
+ BuiltInMethod.QUERYABLE_TABLE_AS_QUERYABLE.method,
+ Expressions.constant(null),
+ Schemas.expression(schema),
+ Expressions.constant(tableName));
+ return Expressions.call(queryableExpression,
+ BuiltInMethod.QUERYABLE_AS_ENUMERABLE.method);
+ }
+ }
}