dimanche 10 octobre 2021

Apache Calcite: ReflectiveSchema does not seem to support ParameterExpression in comparison expressions of Linq4j "where" clause

Referring to the "Background" chapter of the documentation of Calcite, I have defined the following "tables":

public class Employee {
    public int empid = 0;
    public int deptno = 0;

    public Employee(int empid, int deptno) {
        this.empid = empid;
        this.deptno = deptno;
    }
}

public class Department {
    public int deptno = 0;

    public Department(int deptno) {
        this.deptno = deptno;
    }
}

and the following "database":

public class HrSchema {
    public final Employee[] emps = new Employee[]{
            new Employee(1, 7),
            new Employee(2, 6),
            new Employee(3, 6)
    };
    public final Department[] depts = new Department[]{
            new Department(7),
            new Department(6)
    };
}

I established connection with this "database" using ReflectiveSchema:

Class.forName("org.apache.calcite.jdbc.Driver");
Properties info = new Properties();
info.setProperty("lex", "JAVA");
Connection connection =
        DriverManager.getConnection("jdbc:calcite:", info);
CalciteConnection calciteConnection =
        connection.unwrap(CalciteConnection.class);
SchemaPlus rootSchema = calciteConnection.getRootSchema();
ReflectiveSchema schema = new ReflectiveSchema(new HrSchema());
rootSchema.add("hr", schema);

As I prefer Linq query combo to SQL statement concatenation, after referring to LinqFrontJdbcBackTest.java, adaptively I did the following query:

ParameterExpression e =
        Expressions.parameter(Employee.class, "e");

List<Employee> s =
        Schemas.queryable(Schemas.createDataContext(connection, rootSchema),
                        rootSchema.getSubSchema("hr"),
                        Employee.class, "emps")
                .where(
                        Expressions.lambda(
                                Expressions.greaterThan(
                                        Expressions.field(e, "empid"),
                                        Expressions.constant(0)
                                ), e)
                )
                .toList();

for (Employee emp : s ) {
    System.out.println(emp.empid);
}

At length I got:

Exception in thread "main" java.lang.RuntimeException: Error while compiling generated Java code:
public static class Record2_1 implements java.io.Serializable {
  public int empid;
  public int deptno;
  public Record2_1() {}
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof Record2_1)) {
      return false;
    }
    return this.empid == ((Record2_1) o).empid && this.deptno == ((Record2_1) o).deptno;
  }

  public int hashCode() {
    int h = 0;
    h = org.apache.calcite.runtime.Utilities.hash(h, this.empid);
    h = org.apache.calcite.runtime.Utilities.hash(h, this.deptno);
    return h;
  }

  public int compareTo(Record2_1 that) {
    int c;
    c = org.apache.calcite.runtime.Utilities.compare(this.empid, that.empid);
    if (c != 0) {
      return c;
    }
    c = org.apache.calcite.runtime.Utilities.compare(this.deptno, that.deptno);
    if (c != 0) {
      return c;
    }
    return 0;
  }

  public String toString() {
    return "{empid=" + this.empid + ", deptno=" + this.deptno + "}";
  }

}

public org.apache.calcite.linq4j.Enumerable bind(final org.apache.calcite.DataContext root) {
  final org.apache.calcite.linq4j.Enumerable _inputEnumerable = org.apache.calcite.linq4j.Linq4j.asEnumerable(((demo.HrSchema) ((org.apache.calcite.adapter.java.ReflectiveSchema) root.getRootSchema().getSubSchema("hr").unwrap(org.apache.calcite.adapter.java.ReflectiveSchema.class)).getTarget()).emps);
  return new org.apache.calcite.linq4j.AbstractEnumerable(){
      public org.apache.calcite.linq4j.Enumerator enumerator() {
        return new org.apache.calcite.linq4j.Enumerator(){
            public final org.apache.calcite.linq4j.Enumerator inputEnumerator = _inputEnumerable.enumerator();
            public void reset() {
              inputEnumerator.reset();
            }

            public boolean moveNext() {
              while (inputEnumerator.moveNext()) {
                if (((demo.Employee) inputEnumerator.current()).empid > 0) {
                  return true;
                }
              }
              return false;
            }

            public void close() {
              inputEnumerator.close();
            }

            public Object current() {
              final demo.Employee current = (demo.Employee) inputEnumerator.current();
              return new Record2_1(
                  current.empid,
                  current.deptno);
            }

          };
      }

    };
}


public Class getElementType() {
  return Record2_1.class;
}



    at org.apache.calcite.avatica.Helper.wrap(Helper.java:37)
    at org.apache.calcite.adapter.enumerable.EnumerableInterpretable.toBindable(EnumerableInterpretable.java:129)
    at org.apache.calcite.prepare.CalcitePrepareImpl$CalcitePreparingStmt.implement(CalcitePrepareImpl.java:1130)
    at org.apache.calcite.prepare.CalcitePrepareImpl$CalcitePreparingStmt.prepare_(CalcitePrepareImpl.java:1032)
    at org.apache.calcite.prepare.CalcitePrepareImpl$CalcitePreparingStmt.prepareQueryable(CalcitePrepareImpl.java:979)
    at org.apache.calcite.prepare.CalcitePrepareImpl.prepare2_(CalcitePrepareImpl.java:663)
    at org.apache.calcite.prepare.CalcitePrepareImpl.prepare_(CalcitePrepareImpl.java:513)
    at org.apache.calcite.prepare.CalcitePrepareImpl.prepareQueryable(CalcitePrepareImpl.java:474)
    at org.apache.calcite.jdbc.CalciteStatement.prepare(CalciteStatement.java:80)
    at org.apache.calcite.jdbc.CalciteConnectionImpl.executeQuery(CalciteConnectionImpl.java:299)
    at org.apache.calcite.linq4j.QueryableDefaults$ReplayableQueryable.enumerator(QueryableDefaults.java:1171)
    at org.apache.calcite.linq4j.EnumerableDefaults.into(EnumerableDefaults.java:3810)
    at org.apache.calcite.linq4j.DefaultEnumerable.into(DefaultEnumerable.java:372)
    at org.apache.calcite.linq4j.EnumerableDefaults.toList(EnumerableDefaults.java:3556)
    at org.apache.calcite.linq4j.DefaultEnumerable.toList(DefaultEnumerable.java:744)
    at demo.Main.main(Main.java:61)
Caused by: org.codehaus.commons.compiler.CompileException: Line 66, Column 25: No applicable constructor/method found for actual parameters "int, int"; candidates are: "Baz$Record2_1()"
    at org.codehaus.janino.UnitCompiler.compileError(UnitCompiler.java:12211)
    at org.codehaus.janino.UnitCompiler.findMostSpecificIInvocable(UnitCompiler.java:9263)
    at org.codehaus.janino.UnitCompiler.invokeConstructor(UnitCompiler.java:7971)
    at org.codehaus.janino.UnitCompiler.compileGet2(UnitCompiler.java:5409)
    at org.codehaus.janino.UnitCompiler.access$9800(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$16.visitNewClassInstance(UnitCompiler.java:4435)
    at org.codehaus.janino.UnitCompiler$16.visitNewClassInstance(UnitCompiler.java:4396)
    at org.codehaus.janino.Java$NewClassInstance.accept(Java.java:5190)
    at org.codehaus.janino.UnitCompiler.compileGet(UnitCompiler.java:4396)
    at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5662)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:2649)
    at org.codehaus.janino.UnitCompiler.access$2800(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1504)
    at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1487)
    at org.codehaus.janino.Java$ReturnStatement.accept(Java.java:3563)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:1487)
    at org.codehaus.janino.UnitCompiler.compileStatements(UnitCompiler.java:1567)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:3388)
    at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1357)
    at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1330)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:822)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:981)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:951)
    at org.codehaus.janino.UnitCompiler.access$200(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$2.visitAnonymousClassDeclaration(UnitCompiler.java:409)
    at org.codehaus.janino.UnitCompiler$2.visitAnonymousClassDeclaration(UnitCompiler.java:406)
    at org.codehaus.janino.Java$AnonymousClassDeclaration.accept(Java.java:1149)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:406)
    at org.codehaus.janino.UnitCompiler.compileGet2(UnitCompiler.java:5509)
    at org.codehaus.janino.UnitCompiler.access$9500(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4432)
    at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4396)
    at org.codehaus.janino.Java$NewAnonymousClassInstance.accept(Java.java:5238)
    at org.codehaus.janino.UnitCompiler.compileGet(UnitCompiler.java:4396)
    at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5662)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:2649)
    at org.codehaus.janino.UnitCompiler.access$2800(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1504)
    at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1487)
    at org.codehaus.janino.Java$ReturnStatement.accept(Java.java:3563)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:1487)
    at org.codehaus.janino.UnitCompiler.compileStatements(UnitCompiler.java:1567)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:3388)
    at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1357)
    at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1330)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:822)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:981)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:951)
    at org.codehaus.janino.UnitCompiler.access$200(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$2.visitAnonymousClassDeclaration(UnitCompiler.java:409)
    at org.codehaus.janino.UnitCompiler$2.visitAnonymousClassDeclaration(UnitCompiler.java:406)
    at org.codehaus.janino.Java$AnonymousClassDeclaration.accept(Java.java:1149)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:406)
    at org.codehaus.janino.UnitCompiler.compileGet2(UnitCompiler.java:5509)
    at org.codehaus.janino.UnitCompiler.access$9500(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4432)
    at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4396)
    at org.codehaus.janino.Java$NewAnonymousClassInstance.accept(Java.java:5238)
    at org.codehaus.janino.UnitCompiler.compileGet(UnitCompiler.java:4396)
    at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5662)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:2649)
    at org.codehaus.janino.UnitCompiler.access$2800(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1504)
    at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1487)
    at org.codehaus.janino.Java$ReturnStatement.accept(Java.java:3563)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:1487)
    at org.codehaus.janino.UnitCompiler.compileStatements(UnitCompiler.java:1567)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:3388)
    at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1357)
    at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1330)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:822)
    at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:432)
    at org.codehaus.janino.UnitCompiler.access$400(UnitCompiler.java:215)
    at org.codehaus.janino.UnitCompiler$2.visitPackageMemberClassDeclaration(UnitCompiler.java:411)
    at org.codehaus.janino.UnitCompiler$2.visitPackageMemberClassDeclaration(UnitCompiler.java:406)
    at org.codehaus.janino.Java$PackageMemberClassDeclaration.accept(Java.java:1414)
    at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:406)
    at org.codehaus.janino.UnitCompiler.compileUnit(UnitCompiler.java:378)
    at org.codehaus.janino.SimpleCompiler.cook(SimpleCompiler.java:237)
    at org.codehaus.janino.SimpleCompiler.compileToClassLoader(SimpleCompiler.java:465)
    at org.codehaus.janino.ClassBodyEvaluator.compileToClass(ClassBodyEvaluator.java:313)
    at org.codehaus.janino.ClassBodyEvaluator.cook(ClassBodyEvaluator.java:235)
    at org.codehaus.janino.SimpleCompiler.cook(SimpleCompiler.java:207)
    at org.codehaus.commons.compiler.Cookable.cook(Cookable.java:50)
    at org.codehaus.janino.ClassBodyEvaluator.createInstance(ClassBodyEvaluator.java:347)
    at org.apache.calcite.adapter.enumerable.EnumerableInterpretable.getBindable(EnumerableInterpretable.java:163)
    at org.apache.calcite.adapter.enumerable.EnumerableInterpretable.toBindable(EnumerableInterpretable.java:126)
    ... 14 more

Process finished with exit code 1

However, if I replace Expressions.field(e, "empid") with Expressions.constant(5), there's no trouble at all! Successfully the query returns:

1
2
3

There must be something wrong with ReflectiveSchema getting along with Expressions.field in "where" clause. Because constructing the expression outside of "where" clause makes no trouble.

Expressions.lambda(
        Expressions.greaterThan(
                Expressions.field(e, "empid"),
                Expressions.constant(0)
        ), e);

Expressions.field(e, "empid") must have got the right field because when the expression slightly modified to Expressions.field(e, "empi"), exception arises:

Exception in thread "main" java.lang.RuntimeException: Unknown field 'empi' in class class demo.Employee
    at org.apache.calcite.linq4j.tree.Types.getField(Types.java:110)
    at org.apache.calcite.linq4j.tree.Types.getField(Types.java:121)
    at org.apache.calcite.linq4j.tree.Expressions.field(Expressions.java:840)
    at demo.main(Main.java:51)
Caused by: java.lang.NoSuchFieldException: empi
    at java.lang.Class.getField(Class.java:1703)
    at org.apache.calcite.linq4j.tree.Types.getField(Types.java:108)
    ... 3 more

Anyone having idea what the trouble is? Any configuration needed to let "where" clause make sense of the ParameterExpression-containing Linq comparison query on a ReflectiveSchema-based "database"?





Aucun commentaire:

Enregistrer un commentaire