[ https://issues.apache.org/jira/browse/CALCITE-4852?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Ian Bertolacci closed CALCITE-4852. ----------------------------------- Resolution: Duplicate > RelToSqlConverter creates unparseable SQL string from right associative > multi-way join. > --------------------------------------------------------------------------------------- > > Key: CALCITE-4852 > URL: https://issues.apache.org/jira/browse/CALCITE-4852 > Project: Calcite > Issue Type: Bug > Components: core > Reporter: Ian Bertolacci > Priority: Minor > > As discussed inĀ CALCITE-35, Calcite cannot parse parenthesized join > expressions (such as `select ... from A join (B join C)`). > But as suggested in CALCITE-2152, those expressions can be converted into > parenthesized select on the join expression (`select ... from A join (select > ... from B join C)`). > However, RelToSqlConverter will convert the RelNode representation of > right-associative joins into a parenthesized join expression, which is > unparseable. > For example, this RelNode tree > {code} > LogicalProject(_T5_ID=[$2], C0_51=[$3], _T1_ID=[$1], C0_53=[$4], _T3_ID=[$0]) > LogicalJoin(condition=[=($0, $4)], joinType=[inner]) > LogicalProject(_T3_ID=[$0]) > LogicalTableScan(table=[[QUERY, T3]], fields=[[0, 1, 2]]) > // RHS child of right-associate join > LogicalJoin(condition=[=($0, $2)], joinType=[inner]) > LogicalProject(_T1_ID=[$0]) > LogicalTableScan(table=[[QUERY, T1]], fields=[[0, 1, 2]]) > LogicalProject(_T5_ID=[$0], C0_51=[$1], C0_53=[$3]) > LogicalTableScan(table=[[QUERY, T5]], fields=[[0, 1, 2, 3]]) > {code} > creates the following unparseable SQL string > {code} > SELECT `t1`.`_t5_id`, > `t1`.`c0_51`, > `t0`.`_t1_id`, > `t1`.`c0_53`, > `t`.`_t3_id` > FROM (SELECT `id` AS `_T3_ID` > FROM `query`.`t3`) AS `t` > INNER JOIN ((SELECT `id` AS `_T1_ID` > FROM `query`.`t1`) AS `t0` > INNER JOIN (SELECT `id` AS `_T5_ID`, > `c0_51`, > `c0_53` > FROM `query`.`t5`) AS `t1` > ON `t0`.`_t1_id` = `t1`.`c0_51`) > ON `t`.`_t3_id` = `t1`.`c0_53` > {code} > This is an issue, because it is very easy to make such trees, and ideally, > all SQL strings generated by Calcite would also be parseable by Calcite. > To get around this, we found that the insertion of a projection node between > a join and it's RHS child (the problematic join) forces RelToSqlConverter to > create a parenthesized select statement, which *is* parseable, and (at least > for us) semantically identical. > For example, this virtually identical tree: > {code} > LogicalProject(_T5_ID=[$2], C0_51=[$3], _T1_ID=[$1], C0_53=[$4], _T3_ID=[$0]) > LogicalJoin(condition=[=($0, $4)], joinType=[inner]) > LogicalProject(_T3_ID=[$0]) > LogicalTableScan(table=[[QUERY, T3]], fields=[[0, 1, 2]]) > // Inserted projection on top of RHS child > LogicalProject(_T1_ID=[$0], _T5_ID=[$1], C0_51=[$2], C0_53=[$3]) > // RHS child of right-associate join > LogicalJoin(condition=[=($0, $2)], joinType=[inner]) > LogicalProject(_T1_ID=[$0]) > LogicalTableScan(table=[[QUERY, T1]], fields=[[0, 1, 2]]) > LogicalProject(_T5_ID=[$0], C0_51=[$1], C0_53=[$3]) > LogicalTableScan(table=[[QUERY, T5]], fields=[[0, 1, 2, 3]]) > {code} > creates the following parseable SQL string > {code} > SELECT `t2`.`_t5_id`, > `t2`.`c0_51`, > `t2`.`_t1_id`, > `t2`.`c0_53`, > `t`.`_t3_id` > FROM (SELECT `id` AS `_T3_ID` > FROM `query`.`t3`) AS `t` > INNER JOIN (SELECT * > FROM (SELECT `id` AS `_T1_ID` > FROM `query`.`t1`) AS `t0` > INNER JOIN (SELECT `id` AS `_T5_ID`, > `c0_51`, > `c0_53` > FROM `query`.`t5`) AS `t1` > ON `t0`.`_t1_id` = `t1`.`c0_51`) AS `t2` > ON `t`.`_t3_id` = `t2`.`c0_53` > {code} > We solved this our RelToSqlConverter extending class by cloning the parent > join and inserting a projection between it and an RHS join: > {code} > override def visit(join: Join): Result = { > if (join.getRight.isInstanceOf[Join]) { > super.visit( > join.copy( > join.getTraitSet, > join.getCondition, > join.getLeft, > new LogicalProject( > join.getCluster, > join.getTraitSet, > join.getHints, > join.getRight, > // Construct projection of all expressions from the rhs join > join.getRight.getRowType.getFieldList.asScala.map(field => new > RexInputRef(field.getIndex, field.getType)).asJava, > join.getRight.getRowType > ), > join.getJoinType, > join.isSemiJoinDone > ) > ) > } else { > super.visit(join) > } > } > {code} > (we tried to do it "correctly" by constructing a proper Result object, but > found it too difficult. Hopefully someone else can figure out how do it the > right way). -- This message was sent by Atlassian Jira (v8.3.4#803005)