Hi team,
I am trying to join 2 tables, but want to provide the join condition (join
ON x=y) as a text input which is parsed.

Example scenario:

1. user provides input:
    emps.deptno = depts.deptno
2. calcite parses join expression and creates join

Expected result
    SELECT * FROM emps JOIN depts ON emps.deptno = depts.deptno

Actual result:
I am facing error:
Table 'emps' not found
at
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method)
at
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at
java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at
java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at
org.apache.calcite.runtime.Resources$ExInstWithCause.ex(Resources.java:507)
at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:948)
at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:933)
at
org.apache.calcite.sql.validate.SqlValidatorImpl.newValidationError(SqlValidatorImpl.java:5643)
at
org.apache.calcite.sql.validate.DelegatingScope.fullyQualify(DelegatingScope.java:364)
at
org.apache.calcite.sql.validate.SqlValidatorImpl.validateIdentifier(SqlValidatorImpl.java:3348)
at org.apache.calcite.sql.SqlIdentifier.validateExpr(SqlIdentifier.java:307)
at org.apache.calcite.sql.SqlOperator.validateCall(SqlOperator.java:475)
at
org.apache.calcite.sql.validate.SqlValidatorImpl.validateCall(SqlValidatorImpl.java:6371)
at org.apache.calcite.sql.SqlCall.validate(SqlCall.java:143)
at
org.apache.calcite.sql.validate.SqlValidatorImpl.validateScopedExpression(SqlValidatorImpl.java:1101)
at
org.apache.calcite.sql.validate.SqlValidatorImpl.validate(SqlValidatorImpl.java:807)


Code:

```java
import com.google.common.collect.ImmutableList;
import org.apache.calcite.adapter.java.ReflectiveSchema;
import org.apache.calcite.config.Lex;
import org.apache.calcite.jdbc.CalciteConnection;
import org.apache.calcite.jdbc.CalciteSchema;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.ViewExpanders;
import org.apache.calcite.prepare.CalciteCatalogReader;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.sql2rel.SqlToRelConverter;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Frameworks;
import org.apache.calcite.tools.RelBuilder;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

public class JoinTest {

    public static class HrSchema {
        public final Employee[] emps = {
                new Employee(100, "Alice", 10),
                new Employee(200, "Bob", 20)
        };
        public final Department[] depts = {
                new Department(10, "Sales"),
                new Department(20, "Engineering")
        };
    }

    public static class Employee {
        public final int empid;
        public final String name;
        public final int deptno;

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

    public static class Department {
        public final int deptno;
        public final String name;

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

    @Test
    public void testJoinToRexNode() throws Exception {
        Properties props = new Properties();
        Connection connection =
DriverManager.getConnection("jdbc:calcite:", props);
        CalciteConnection calciteConn =
connection.unwrap(CalciteConnection.class);
        SchemaPlus rootSchema = calciteConn.getRootSchema();
        rootSchema.add("hr", new ReflectiveSchema(new HrSchema()));
        calciteConn.setSchema("hr");

        FrameworkConfig config = Frameworks.newConfigBuilder()
                .parserConfig(SqlParser.config().withLex(Lex.MYSQL))
                .defaultSchema(rootSchema.getSubSchema("hr"))
                .build();

        String sql = "SELECT * FROM emps JOIN depts ON emps.deptno =
depts.deptno";

        SqlValidator.Config sqlValidatorConfig =
config.getSqlValidatorConfig();
        SqlParser.Config parserConfig = config.getParserConfig();

        ImmutableList<String> defaultSchemaPath = ImmutableList.of();
        RelBuilder relBuilder = RelBuilder.create(config);
        RelOptCluster cluster = relBuilder.getCluster();
        RexBuilder rexBuilder = relBuilder.getRexBuilder();
        RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory();
        CalciteSchema calciteSchema =
CalciteSchema.from(rootSchema.getSubSchema("hr"));
        CalciteCatalogReader calciteCatalogReader = new
CalciteCatalogReader(
                calciteSchema,
                defaultSchemaPath,
                typeFactory,
                calciteConn.config()
        );

        SqlValidator sqlValidator = SqlValidatorUtil.newValidator(
                SqlStdOperatorTable.instance(),
                calciteCatalogReader,
                typeFactory,
                sqlValidatorConfig
        );

        SqlToRelConverter sqlToRelConverter = new SqlToRelConverter(
                ViewExpanders.simpleContext(cluster),
                sqlValidator,
                calciteCatalogReader,
                cluster,
                config.getConvertletTable(),
                config.getSqlToRelConverterConfig()
        );

        // First test (works)
        SqlParser firstParser = SqlParser.create(sql, parserConfig);
        SqlNode firstSqlNode = firstParser.parseQuery();
        SqlNode firstValidatedSqlNode = sqlValidator.validate(firstSqlNode);
        RexNode firstRexNode =
sqlToRelConverter.convertExpression(firstValidatedSqlNode);
        System.out.println(firstRexNode);

        // Second test (fails)
        sql = "emps.deptno = depts.deptno";
        SqlParser secondParser = SqlParser.create(sql, parserConfig);
        SqlNode secondSqlNode = secondParser.parseExpression();
        SqlNode secondValidatedSqlNode =
sqlValidator.validate(secondSqlNode);
        RexNode secondRexNode =
sqlToRelConverter.convertExpression(secondValidatedSqlNode);
    }
}
```

error is raised at this line:
SqlNode secondValidatedSqlNode = sqlValidator.validate(secondSqlNode);

my questions are,
1. is this type of expression even supported?
2. its a bug with calcite or a bug with my code?

From,
Hugh Pearse

Reply via email to