Details
-
Bug
-
Status: Closed
-
Major
-
Resolution: Fixed
-
1.21.0
-
None
Description
I was not able to find a suitable test, so I'll describe the issue using a custom minimal table function returning a row with a single value as an example. I believe, that it should be reproducible against any table function.
There is a simple table function my_dummy() that just prints numbers 1 to 5. Simple select works as expected:
SQL: select val from table(my_dummy()) Result: val : INTEGER 5 4 3 2 1
However, when trying to make an aggregation on top of it, there is a class cast error:
SQL: select val from table(my_dummy()) group by val Error: java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to java.lang.Integer at org.apache.calcite.avatica.util.AbstractCursor$IntAccessor.getInt(AbstractCursor.java:541) at org.apache.calcite.avatica.AvaticaSite.get(AvaticaSite.java:340) at org.apache.calcite.avatica.AvaticaResultSet.getObject(AvaticaResultSet.java:393)
Actually, this array Object[] contains a single integer produced by the table function. Also I faced similar class cast errors in a generated code making hashJoin (in a complex production code), I believe, the cause should be the same as in this minimal example.
Here is this my_dummy() table function implementation (single file):
package com.myapp.tf; import org.apache.calcite.DataContext; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; import org.apache.calcite.linq4j.tree.Types; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.schema.ScannableTable; import org.apache.calcite.schema.TableFunction; import org.apache.calcite.schema.impl.AbstractTable; import org.apache.calcite.schema.impl.TableFunctionImpl; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlOperatorBinding; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.calcite.sql.type.OperandTypes.family; public class DummyFunction extends SqlUserDefinedTableFunction { public static String DUMMY_FUNCTION_NAME = "my_dummy"; public static final TableFunction DUMMY_TABLE_FUNCTION = TableFunctionImpl.create(Types.lookupMethod( DummyFunction.class, "createDummyTable" )); public static DummyTable createDummyTable() { return new DummyTable(); } public DummyFunction() { super(new SqlIdentifier(DUMMY_FUNCTION_NAME, SqlParserPos.ZERO), null, null, family(), Collections.emptyList(), DUMMY_TABLE_FUNCTION ); } @Override public RelDataType inferReturnType(SqlOperatorBinding opBinding) { RelDataTypeFactory typeFactory = opBinding.getTypeFactory(); return typeFactory.builder() .add("val", SqlTypeName.INTEGER) .build(); } } class DummyTable extends AbstractTable implements ScannableTable { private final AtomicInteger cnt = new AtomicInteger(6); @Override public Enumerable<Object[]> scan(DataContext root) { return new AbstractEnumerable<Object[]>() { public Enumerator<Object[]> enumerator() { return new Enumerator<Object[]> () { @Override public Object[] current() { return new Object[] { cnt.intValue() }; } @Override public boolean moveNext() { return cnt.decrementAndGet() > 0; } @Override public void reset() { } @Override public void close() { } }; } }; } @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) { return typeFactory.builder() .add("val", SqlTypeName.INTEGER) .build(); } }
And adding it into a schema:
mySchemaPlus.add(DUMMY_FUNCTION_NAME, DummyFunction.DUMMY_TABLE_FUNCTION);
Attachments
Issue Links
- links to