[ 
https://issues.apache.org/jira/browse/CALCITE-7869?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18141580#comment-18141580
 ] 

Mihai Budiu commented on CALCITE-7869:
--------------------------------------

I do not have much experience with lineage in Calcite, so I cannot comment on 
the desired result.

> RelMdExpressionLineage should not combine fields from different Union inputs
> ----------------------------------------------------------------------------
>
>                 Key: CALCITE-7869
>                 URL: https://issues.apache.org/jira/browse/CALCITE-7869
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 1.42.0
>            Reporter: Arnaud Jegou
>            Priority: Major
>
> When an expression reads several fields that come from the same Union, 
> RelMdExpressionLineage resolves each field's origins from every Union input 
> independently, then builds every combination. Most of those combinations 
> cannot occur: a row comes from one input of the Union, so all the fields it 
> reads come from that input.
> The following test in RelMetadataTest fails on main:
> {code:java}
> @Test void testExpressionLineageUnionKeepsInputFieldsTogether() {
>   // empno is column 0 and deptno is column 7 in catalog.sales.emp
>   final RelNode rel = sql("select empno + deptno from (\n"
>       + "  select empno, deptno from emp\n"
>       + "  union all\n"
>       + "  select empno, deptno from emp)").toRel();
>   final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
>   final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList());
>   final Set<RexNode> r = mq.getExpressionLineage(rel, ref);
>   // A row comes from one input of the union, so empno and deptno always
>   // come from the same input: two possible origins, not four.
>   assertThat(r.toString(), r, hasSize(2));
>   for (RexNode result : r) {
>     final RexCall call = (RexCall) result;
>     final RexTableInputRef empno =
>         (RexTableInputRef) call.getOperands().get(0);
>     final RexTableInputRef deptno =
>         (RexTableInputRef) call.getOperands().get(1);
>     assertThat(result.toString(), empno.getIdentifier(),
>         is(deptno.getIdentifier()));
>   }
> }
> {code}
> {noformat}
> java.lang.AssertionError: [+([CATALOG, SALES, EMP].#1.$0, [CATALOG, SALES, 
> EMP].#0.$7), +([CATALOG, SALES, EMP].#1.$0, [CATALOG, SALES, EMP].#1.$7), 
> +([CATALOG, SALES, EMP].#0.$0, [CATALOG, SALES, EMP].#0.$7), +([CATALOG, 
> SALES, EMP].#0.$0, [CATALOG, SALES, EMP].#1.$7)]
> Expected: a collection with size <2>
>      but: collection size was <4>
> {noformat}
> The first and last expressions pair empno from one input with deptno from the 
> other.
> RelMdAllPredicates builds filter predicates the same way, so it reports 
> predicates that the plan does not contain:
> {code:java}
> @Test void testAllPredicatesFilterAboveUnion() {
>   // empno is column 0 and deptno is column 7 in catalog.sales.emp
>   final RelNode rel = sql("select * from (\n"
>       + "  select empno, deptno from emp\n"
>       + "  union all\n"
>       + "  select empno, deptno from emp)\n"
>       + "where empno = deptno").toRel();
>   final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();
>   final RelOptPredicateList predicates = mq.getAllPredicates(rel);
>   assertThat(predicates.pulledUpPredicates,
>       sortsAs("[=([CATALOG, SALES, EMP].#0.$0, [CATALOG, SALES, EMP].#0.$7), "
>           + "=([CATALOG, SALES, EMP].#1.$0, [CATALOG, SALES, EMP].#1.$7)]"));
> }
> {code}
> {noformat}
> Expected: "[=([CATALOG, SALES, EMP].#0.$0, [CATALOG, SALES, EMP].#0.$7), 
> =([CATALOG, SALES, EMP].#1.$0, [CATALOG, SALES, EMP].#1.$7)]"
>      but: was "[=([CATALOG, SALES, EMP].#0.$0, [CATALOG, SALES, EMP].#0.$7), 
> =([CATALOG, SALES, EMP].#0.$0, [CATALOG, SALES, EMP].#1.$7), =([CATALOG, 
> SALES, EMP].#1.$0, [CATALOG, SALES, EMP].#0.$7), =([CATALOG, SALES, 
> EMP].#1.$0, [CATALOG, SALES, EMP].#1.$7)]"
> {noformat}
> The second and third predicates compare empno of one input with deptno of the 
> other. They describe a join condition between the two inputs that does not 
> exist in the query.
> The same happens wherever an expression combines two or more fields from the 
> same Union, at any depth: in the Union handler, as above, or in a Project, 
> Aggregate, Join or Calc above the Union. Each of these handlers resolves the 
> fields separately and then calls createAllPossibleExpressions. An expression 
> reading k fields from a Union of N inputs therefore gets N^k lineage 
> expressions, of which only N can occur.
> This is related to CALCITE-5365, where getAllPredicates runs out of memory on 
> a join of two three-input unions on seven keys. With an inner join, 
> getAllPredicates builds 3^14 combinations for that join condition, of which 
> only 3 x 3 = 9 can occur. On main, the same shape with two keys returns 81 
> predicates instead of 9. A bound on the number of combinations, which would 
> address CALCITE-5365, would not fix this issue. Below the bound, the invalid 
> combinations would still be returned. And because they inflate the count, 
> lineage could reach the bound and be dropped even when only a few valid 
> combinations exist.
> A possible fix is to resolve the whole expression at once, instead of each 
> field separately, so that the fields of one input stay together:
>  * Union: compute the lineage of the whole expression on each input, then 
> gather the results.
>  * Project, Calc and Aggregate: replace the fields in the expression with the 
> input expressions they stand for, then ask the input for the lineage of the 
> result.
>  * Join: combining the two sides is correct, since any left row can pair with 
> any right row, but the fields of each side must be resolved together, for 
> example as a ROW of the fields read from that side.
> RelMdAllPredicates combines the lineage of each field in the same way in its 
> Filter, Calc and Join handlers, so they need the same change: ask for the 
> lineage of the whole condition.
> Plans without a Union do not change, because each field then has a single 
> lineage expression.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to