[ 
https://issues.apache.org/jira/browse/CALCITE-3364?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Anton Haidai updated CALCITE-3364:
----------------------------------
    Description: 
I was not able to find a suitable test, so I'll describe the issue using a 
custom minimal table function returning 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:
{code}
SQL:
select val from table(my_dummy())
Result:
val : INTEGER
5
4
3
2
1
{code}

However, when trying to make an aggregation on top of it, there is a class cast 
error:
{code}
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)
{code}
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):
{code}
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();
    }
}
{code}

And adding it into a schema:
{code}
mySchemaPlus.add(DUMMY_FUNCTION_NAME, DummyFunction.DUMMY_TABLE_FUNCTION);
{code}



  was:
I was not able to find a suitable test, so I'll describe the issue using a 
custom minimal table function 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:
{code}
SQL:
select val from table(my_dummy())
Result:
val : INTEGER
5
4
3
2
1
{code}

However, when trying to make an aggregation on top of it, there is a class cast 
error:
{code}
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)
{code}
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):
{code}
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();
    }
}
{code}

And adding it into a schema:
{code}
mySchemaPlus.add(DUMMY_FUNCTION_NAME, DummyFunction.DUMMY_TABLE_FUNCTION);
{code}




> Can't group table function result due to a type cast error if table functions 
> returns a single value
> ----------------------------------------------------------------------------------------------------
>
>                 Key: CALCITE-3364
>                 URL: https://issues.apache.org/jira/browse/CALCITE-3364
>             Project: Calcite
>          Issue Type: Bug
>    Affects Versions: 1.21.0
>            Reporter: Anton Haidai
>            Priority: Major
>
> I was not able to find a suitable test, so I'll describe the issue using a 
> custom minimal table function returning 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:
> {code}
> SQL:
> select val from table(my_dummy())
> Result:
> val : INTEGER
> 5
> 4
> 3
> 2
> 1
> {code}
> However, when trying to make an aggregation on top of it, there is a class 
> cast error:
> {code}
> 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)
> {code}
> 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):
> {code}
> 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();
>     }
> }
> {code}
> And adding it into a schema:
> {code}
> mySchemaPlus.add(DUMMY_FUNCTION_NAME, DummyFunction.DUMMY_TABLE_FUNCTION);
> {code}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to