Skip to content

Commit

Permalink
explain: change output of non-binary equivalence classes
Browse files Browse the repository at this point in the history
  • Loading branch information
aalexandrov committed Nov 13, 2023
1 parent 5030cc7 commit cdbb5b6
Show file tree
Hide file tree
Showing 22 changed files with 94 additions and 88 deletions.
12 changes: 4 additions & 8 deletions src/compute-types/src/explain/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use std::ops::Deref;

use itertools::{izip, Itertools};
use mz_expr::{Id, MirScalarExpr};
use mz_ore::str::{bracketed, separated, IndentLike, StrExt};
use mz_ore::str::{separated, IndentLike, StrExt};
use mz_repr::explain::text::{fmt_text_constant_rows, DisplayText};
use mz_repr::explain::{CompactScalarSeq, Indices, PlanRenderingContext};

Expand Down Expand Up @@ -575,13 +575,9 @@ impl DisplayText<PlanRenderingContext<'_, Plan>> for JoinClosure {
if !self.ready_equivalences.is_empty() {
let equivalences = separated(
" AND ",
self.ready_equivalences.iter().map(|equivalence| {
if equivalence.len() == 2 {
bracketed("", "", separated(" = ", equivalence))
} else {
bracketed("eq(", ")", separated(", ", equivalence))
}
}),
self.ready_equivalences
.iter()
.map(|equivalence| separated(" = ", equivalence)),
);
writeln!(f, "{}ready_equivalences={}", ctx.indent, equivalences)?;
}
Expand Down
35 changes: 25 additions & 10 deletions src/expr-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ mod relation {

/// Support for parsing [mz_expr::MirScalarExpr].
mod scalar {
use mz_expr::{ColumnOrder, MirScalarExpr};
use mz_expr::{BinaryFunc, ColumnOrder, MirScalarExpr};
use mz_repr::{AsColumnType, Datum, Row};

use super::*;
Expand Down Expand Up @@ -1020,16 +1020,31 @@ mod scalar {
pub fn parse_join_equivalences(input: ParseStream) -> syn::Result<Vec<Vec<MirScalarExpr>>> {
let mut equivalences = vec![];
while !input.is_empty() {
if input.eat(kw::eq) {
let inner;
syn::parenthesized!(inner in input);
equivalences.push(inner.parse_comma_sep(parse_expr)?);
} else {
let lhs = parse_operand(input)?;
input.parse::<syn::Token![=]>()?;
let rhs = parse_operand(input)?;
equivalences.push(vec![lhs, rhs]);
let mut equivalence = vec![];
loop {
let mut worklist = vec![parse_operand(input)?];
while let Some(operand) = worklist.pop() {
// Be more lenient and support parenthesized equivalences,
// e.g. `... AND (x = u + v = z + 1) AND ...`.
if let MirScalarExpr::CallBinary {
func: BinaryFunc::Eq,
expr1,
expr2,
} = operand
{
// We reverse the order in the worklist in order to get
// the correct order in the equivalence class.
worklist.push(*expr2);
worklist.push(*expr1);
} else {
equivalence.push(operand);
}
}
if !input.eat(syn::Token![=]) {
break;
}
}
equivalences.push(equivalence);
input.eat(kw::AND);
}
Ok(equivalences)
Expand Down
4 changes: 2 additions & 2 deletions src/expr-parser/tests/test_mir_parser/join.spec
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ roundtrip OK

# Equi-Join
roundtrip
Join on=((#1 + #2) = #4 AND eq(#0, #3, #6) AND #5 = #8)
Join on=((#1 + #2) = #4 AND #0 = #3 = #6 = #7 AND #5 = #8)
Constant // { types: "(bigint, bigint, text)" }
- (1, 2, "oh, my!")
Constant // { types: "(bigint, bigint, text)" }
Expand All @@ -32,7 +32,7 @@ roundtrip OK

# Equi-Join
roundtrip
Join on=((#0 + #1) = #3 AND eq(#2, #5, #8) AND #4 = (#6 + #7))
Join on=((#0 + #1) = #3 AND #2 = #5 = #8 AND #4 = (#6 + #7))
Constant // { types: "(bigint, bigint, text)" }
- (1, 2, "oh, my!")
Constant // { types: "(bigint, bigint, text)" }
Expand Down
9 changes: 2 additions & 7 deletions src/expr/src/explain/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::collections::BTreeMap;
use std::fmt;

use mz_ore::soft_assert;
use mz_ore::str::{bracketed, closure_to_display, separated, Indent, IndentLike, StrExt};
use mz_ore::str::{closure_to_display, separated, Indent, IndentLike, StrExt};
use mz_repr::explain::text::{fmt_text_constant_rows, DisplayText};
use mz_repr::explain::{
CompactScalarSeq, ExprHumanizer, HumanizedAttributes, IndexUsageType, Indices,
Expand Down Expand Up @@ -545,13 +545,8 @@ impl MirRelationExpr {
let equivalences = separated(
" AND ",
equivalences.iter().map(|equivalence| {
let equivalences = equivalence.len();
let equivalence = HumanizedExpr::seq(equivalence, cols);
if equivalences == 2 {
bracketed("", "", separated(" = ", equivalence))
} else {
bracketed("eq(", ")", separated(", ", equivalence))
}
separated(" = ", equivalence)
}),
);
write!(f, "{}Join on=({})", ctx.indent, equivalences)?;
Expand Down
4 changes: 2 additions & 2 deletions src/transform/tests/test_transforms/anf.spec
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ With

# Joins.
apply pipeline=anf
Join on=(eq(#0, #2, #4))
Join on=(#0 = #2 = #4)
Get t0
Constant <empty> // { types: "(bigint, bigint)" }
Get t0
Expand All @@ -72,7 +72,7 @@ Return
Get l2
With
cte l2 =
Join on=(eq(#0, #2, #4))
Join on=(#0 = #2 = #4)
Get l0
Get l1
Get l0
Expand Down
4 changes: 2 additions & 2 deletions src/transform/tests/test_transforms/typecheck.spec
Original file line number Diff line number Diff line change
Expand Up @@ -353,15 +353,15 @@ expected Bool?
############################################################################

typecheck
Join on=(eq(#0, #3))
Join on=(#0 = #3)
Get t0
Get t0
----
(Int64, Int64?, String, Int64, Int64?, String)

# TODO(mgree): should narrow to non-null with typechecker improvements
typecheck
Join on=(eq(#0, #1, #3, #4))
Join on=(#0 = #1 = #3 = #4)
Get t0
Get t0
----
Expand Down
6 changes: 3 additions & 3 deletions src/transform/tests/testdata/join-fusion
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ build apply=JoinFusion
[[#0 #2]])
----
Filter #3
Join on=(eq(#0, #2, #4))
Join on=(#0 = #2 = #4)
Get x
Get x
Get y
Expand Down Expand Up @@ -72,7 +72,7 @@ build apply=JoinFusion
[[#0 #2 #6]])
----
Filter #3
Join on=(eq(#0, #2, #4, #6))
Join on=(#0 = #2 = #4 = #6)
Get x
Get x
Get y
Expand All @@ -90,7 +90,7 @@ build apply=(JoinFusion,PredicatePushdown)
[#1])]
[[#0 #2 #6]])
----
Join on=(eq(#0, #2, #4, #6))
Join on=(#0 = #2 = #4 = #6)
Get x
Filter #1
Get x
Expand Down
6 changes: 3 additions & 3 deletions src/transform/tests/testdata/partial-reduction-pushdown
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ build apply=ReductionPushdown
[])
----
Project (#0)
Join on=(eq(#0, #1, #2))
Join on=(#0 = #1 = #2)
Distinct project=[#1]
Get x
Distinct project=[#1]
Expand Down Expand Up @@ -267,7 +267,7 @@ build apply=ReductionPushdown
[(sum_int32 (call_unary neg_int32 #0) true) (sum_int16 #2 true)])
----
Project (#2, #1, #3)
Join on=(eq(#0, #2, #4))
Join on=(#0 = #2 = #4)
Reduce group_by=[#1] aggregates=[sum(distinct -(#0))]
Constant <empty>
Reduce group_by=[#1] aggregates=[sum(distinct #0)]
Expand All @@ -292,7 +292,7 @@ build apply=ReductionPushdown
Project (#3, #1, #2)
Join on=(#0 = #3)
Reduce group_by=[#4] aggregates=[sum(distinct (#0 + smallint_to_integer(#2))), sum(distinct (#2 * #4))]
Join on=(eq(#1, #3, #5))
Join on=(#1 = #3 = #5)
Constant <empty>
Constant <empty>
Constant <empty>
Expand Down
6 changes: 3 additions & 3 deletions src/transform/tests/testdata/reduction-pushdown
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ build apply=ReductionPushdown
(reduce (join [(get x) (get y) (get y)] [[#1 #3 #5]]) [#1] [])
----
Project (#0)
Join on=(eq(#0, #1, #2))
Join on=(#0 = #1 = #2)
Distinct project=[#1]
Get x
Distinct project=[#1]
Expand Down Expand Up @@ -282,7 +282,7 @@ build apply=ReductionPushdown
[(sum_int16 (call_unary neg_int16 #0) true) (sum_int16 #2 true)])
----
Project (#2, #1, #3)
Join on=(eq(#0, #2, #4))
Join on=(#0 = #2 = #4)
Reduce group_by=[#1] aggregates=[sum(distinct -(#0))]
Get x
Reduce group_by=[#1] aggregates=[sum(distinct #0)]
Expand All @@ -302,7 +302,7 @@ build apply=ReductionPushdown
Project (#3, #1, #2)
Join on=(#0 = #3)
Reduce group_by=[#4] aggregates=[sum(distinct (#0 + #2)), sum(distinct (#2 * #4))]
Join on=(eq(#1, #3, #5))
Join on=(#1 = #3 = #5)
Get x
Get y
Get z
Expand Down
2 changes: 1 addition & 1 deletion test/sqllogictest/cardinality.slt
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ EXPLAIN WITH(join_impls) SELECT * FROM t JOIN t2 ON t.x = t2.x JOIN t3 ON t.x =
----
Explained Query:
Project (#0, #1, #0, #3, #0, #5, #0, #7, #0, #9, #0, #11, #0, #13, #0, #15, #0, #17, #0, #19)
Join on=(eq(#0, #2, #4, #6, #8, #10, #12, #14, #16, #18)) type=delta
Join on=(#0 = #2 = #4 = #6 = #8 = #10 = #12 = #14 = #16 = #18) type=delta
implementation
%0:t » %2:t3[#0]KA|169| » %9:t10[#0]KA|260| » %8:t9[#0]KA|273| » %5:t6[#0]KA|299| » %6:t7[#0]KA|299| » %7:t8[#0]KA|299| » %4:t5[#0]KA|494| » %3:t4[#0]KA|611| » %1:t2[#0]KA|10000|
%1:t2 » %0:t[#0]KA|4| » %2:t3[#0]KA|169| » %9:t10[#0]KA|260| » %8:t9[#0]KA|273| » %5:t6[#0]KA|299| » %6:t7[#0]KA|299| » %7:t8[#0]KA|299| » %4:t5[#0]KA|494| » %3:t4[#0]KA|611|
Expand Down
20 changes: 10 additions & 10 deletions test/sqllogictest/chbench.slt
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ Explained Query:
Return // { arity: 8 }
Project (#2, #3, #12, #0, #1, #4, #6, #7) // { arity: 8 }
Filter (#9) IS NOT NULL // { arity: 17 }
Join on=(eq(#0, #8, #15) AND #2 = #10 AND #5 = #11 AND #9 = #16 AND #13 = #14) type=differential // { arity: 17 }
Join on=(#0 = #8 = #15 AND #2 = #10 AND #5 = #11 AND #9 = #16 AND #13 = #14) type=differential // { arity: 17 }
implementation
%5[#0]UKA » %0:item[#0]UKlf » %2:stock[#0, #1]KKlf » %1:supplier[#0]UKlf » %3:nation[#0]UKlf » %4:l0[#0]UKlf
ArrangeBy keys=[[#0]] // { arity: 2 }
Expand Down Expand Up @@ -383,7 +383,7 @@ Explained Query:
Reduce group_by=[#2, #1, #0, #3] aggregates=[sum(#4)] // { arity: 5 }
Project (#1..=#3, #10, #22) // { arity: 5 }
Filter (#0) IS NOT NULL AND (date_to_timestamp(#10) > 2007-01-02 00:00:00) // { arity: 24 }
Join on=(#0 = #9 AND eq(#1, #4, #7, #15) AND eq(#2, #5, #8, #16) AND eq(#3, #6, #14)) type=differential // { arity: 24 }
Join on=(#0 = #9 AND #1 = #4 = #7 = #15 AND #2 = #5 = #8 = #16 AND #3 = #6 = #14) type=differential // { arity: 24 }
implementation
%0:customer[#2, #1, #0]UKKKlf » %2:order[#2, #1, #3]KKKAlif » %1:neworder[#0..=#2]UKKKlif » %3:orderline[#2, #1, #0]KKKAlif
ArrangeBy keys=[[#2, #1, #0]] // { arity: 3 }
Expand Down Expand Up @@ -486,7 +486,7 @@ Explained Query:
Reduce group_by=[#1] aggregates=[sum(#0)] // { arity: 2 }
Project (#16, #24) // { arity: 2 }
Filter (#12) IS NOT NULL // { arity: 27 }
Join on=(#0 = #7 AND eq(#1, #5, #9) AND eq(#2, #6, #10, #19) AND eq(#3, #22, #23) AND #4 = #8 AND #12 = #18 AND #20 = #21 AND #25 = #26) type=differential // { arity: 27 }
Join on=(#0 = #7 AND #1 = #5 = #9 AND #2 = #6 = #10 = #19 AND #3 = #22 = #23 AND #4 = #8 AND #12 = #18 AND #20 = #21 AND #25 = #26) type=differential // { arity: 27 }
implementation
%1:order[#3, #1, #2]KKKif » %0:customer[#0..=#2]UKKKif » %5:nation[#0]UKif » %6:region[#0]UKeif » %2:orderline[#2, #1, #0]KKKAeif » %3:stock[#0, #1]UKKeif » %4:supplier[#0, #1]UKKeif
ArrangeBy keys=[[#0..=#2]] // { arity: 4 }
Expand Down Expand Up @@ -594,7 +594,7 @@ Explained Query:
Reduce group_by=[#0, substr(char_to_text(#3), 1, 1), extract_year_d(#2)] aggregates=[sum(#1)] // { arity: 4 }
Project (#1, #10, #15, #19) // { arity: 4 }
Filter (((#22 = "GERMANY") AND (#24 = "CAMBODIA")) OR ((#22 = "CAMBODIA") AND (#24 = "GERMANY"))) // { arity: 25 }
Join on=(#0 = #4 AND #1 = #21 AND #2 = #8 AND #3 = #9 AND #5 = #11 AND eq(#6, #12, #17) AND eq(#7, #13, #18) AND #14 = #16 AND #20 = #23) type=differential // { arity: 25 }
Join on=(#0 = #4 AND #1 = #21 AND #2 = #8 AND #3 = #9 AND #5 = #11 AND #6 = #12 = #17 AND #7 = #13 = #18 AND #14 = #16 AND #20 = #23) type=differential // { arity: 25 }
implementation
%2:orderline[#0..=#2]KKKiif » %3:order[#0..=#2]UKKKiif » %4:customer[#0..=#2]UKKKiif » %1:stock[#0, #1]UKKiif » %6:l0[#0]UKeiif » %0:supplier[#0]UKeiif » %5:l0[#0]UKeiif
ArrangeBy keys=[[#0]] // { arity: 2 }
Expand Down Expand Up @@ -670,7 +670,7 @@ Explained Query:
Reduce group_by=[extract_year_d(#1)] aggregates=[sum(case when (#2 = "GERMANY") then #0 else 0 end), sum(#0)] // { arity: 3 }
Project (#14, #20, #28) // { arity: 3 }
Filter (#0 < 1000) AND (#4) IS NOT NULL // { arity: 30 }
Join on=(eq(#0, #3, #10) AND #1 = #5 AND #2 = #27 AND #4 = #11 AND #6 = #16 AND eq(#7, #17, #22) AND eq(#8, #18, #23) AND #19 = #21 AND #24 = #25 AND #26 = #29) type=differential // { arity: 30 }
Join on=(#0 = #3 = #10 AND #1 = #5 AND #2 = #27 AND #4 = #11 AND #6 = #16 AND #7 = #17 = #22 AND #8 = #18 = #23 AND #19 = #21 AND #24 = #25 AND #26 = #29) type=differential // { arity: 30 }
implementation
%4:order[#3, #1, #2]KKKiif » %5:customer[#0..=#2]UKKKiif » %6:nation[#0]UKiif » %8:region[#0]UKeiif » %3:orderline[#2, #1, #0]KKKAeiiif » %2:stock[#0, #1]UKKeiiiif » %0:item[#0]UKeliiiiif » %1:supplier[#0]UKeliiiiif » %7:nation[#0]UKeliiiiif
ArrangeBy keys=[[#0]] // { arity: 1 }
Expand Down Expand Up @@ -745,7 +745,7 @@ Explained Query:
Reduce group_by=[#2, extract_year_d(#1)] aggregates=[sum(#0)] // { arity: 3 }
Project (#14, #19, #21) // { arity: 3 }
Filter (#0) IS NOT NULL AND (#2) IS NOT NULL // { arity: 22 }
Join on=(eq(#0, #1, #10) AND #2 = #11 AND #3 = #4 AND #5 = #20 AND #6 = #16 AND #7 = #17 AND #8 = #18) type=differential // { arity: 22 }
Join on=(#0 = #1 = #10 AND #2 = #11 AND #3 = #4 AND #5 = #20 AND #6 = #16 AND #7 = #17 AND #8 = #18) type=differential // { arity: 22 }
implementation
%4:order[#2, #1, #0]UKKK » %3:orderline[#2, #1, #0]KKKA » %1:stock[#0, #1]UKK » %0:item[#0]UKlf » %2:supplier[#0]UKlf » %5:nation[#0]UKlf
ArrangeBy keys=[[#0]] // { arity: 1 }
Expand Down Expand Up @@ -803,7 +803,7 @@ Explained Query:
Reduce group_by=[#0..=#3, #5] aggregates=[sum(#4)] // { arity: 6 }
Project (#0, #3..=#5, #20, #23) // { arity: 6 }
Filter (#11 <= #18) // { arity: 24 }
Join on=(#0 = #10 AND eq(#1, #8, #13) AND eq(#2, #9, #14) AND #6 = #22 AND #7 = #12) type=differential // { arity: 24 }
Join on=(#0 = #10 AND #1 = #8 = #13 AND #2 = #9 = #14 AND #6 = #22 AND #7 = #12) type=differential // { arity: 24 }
implementation
%1:order[#3, #1, #2]KKKif » %0:customer[#0..=#2]UKKKif » %3:nation[#0]UKif » %2:orderline[#2, #1, #0]KKKAif
ArrangeBy keys=[[#0..=#2]] // { arity: 7 }
Expand Down Expand Up @@ -1238,7 +1238,7 @@ Explained Query:
Reduce group_by=[#4, #2, #1, #0, #3, #5, #6] aggregates=[sum(#7)] // { arity: 8 }
Project (#0..=#4, #8, #10, #20) // { arity: 8 }
Filter (#0) IS NOT NULL // { arity: 22 }
Join on=(#0 = #7 AND eq(#1, #5, #13) AND eq(#2, #6, #14) AND #4 = #12) type=differential // { arity: 22 }
Join on=(#0 = #7 AND #1 = #5 = #13 AND #2 = #6 = #14 AND #4 = #12) type=differential // { arity: 22 }
implementation
%0:customer[#2, #1, #0]UKKK » %1:order[#2, #1, #3]KKKA » %2:orderline[#2, #1, #0]KKKA
ArrangeBy keys=[[#2, #1, #0]] // { arity: 4 }
Expand Down Expand Up @@ -1360,7 +1360,7 @@ Explained Query:
Reduce group_by=[#0..=#3] aggregates=[sum(#4)] // { arity: 5 }
Project (#0..=#3, #26) // { arity: 5 }
Filter (#1) IS NOT NULL AND (date_to_timestamp(#25) > 2010-05-23 12:00:00) // { arity: 30 }
Join on=(#0 = ((#1 * #2) % 10000) AND eq(#1, #23, #29)) type=differential // { arity: 30 }
Join on=(#0 = ((#1 * #2) % 10000) AND #1 = #23 = #29) type=differential // { arity: 30 }
implementation
%3:item[#0]UKlf » %2:orderline[#4]KAlif » %1:stock[#0]KAlif » %0:l0[#0]UKlif
ArrangeBy keys=[[#0]] // { arity: 1 }
Expand Down Expand Up @@ -1456,7 +1456,7 @@ Explained Query:
cte l1 =
Project (#1, #3..=#5, #9) // { arity: 5 }
Filter (#7) IS NOT NULL AND (#9 > #16) // { arity: 21 }
Join on=(#0 = #19 AND #2 = #20 AND #3 = #13 AND #4 = #14 AND eq(#5, #15, #18) AND #7 = #17) type=differential // { arity: 21 }
Join on=(#0 = #19 AND #2 = #20 AND #3 = #13 AND #4 = #14 AND #5 = #15 = #18 AND #7 = #17) type=differential // { arity: 21 }
implementation
%2:order[#2, #1, #0]UKKK » %1:l0[#2, #1, #0]KKKA » %3:stock[#0, #1]UKK » %0:supplier[#0]UK » %4:nation[#0]UKef
ArrangeBy keys=[[#0]] // { arity: 3 }
Expand Down
2 changes: 1 addition & 1 deletion test/sqllogictest/explain/decorrelated_plan_as_text.slt
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ SELECT (SELECT v.a FROM v WHERE v.b = t.b LIMIT 1), (SELECT mv.a FROM mv WHERE m
Return
Project (#8, #9)
Map (#4, #7)
Join on=(eq(#0, #2, #5) AND eq(#1, #3, #6))
Join on=(#0 = #2 = #5 AND #1 = #3 = #6)
Get l0
Project (#0, #1, #3)
Join on=(#1 = #2)
Expand Down
8 changes: 4 additions & 4 deletions test/sqllogictest/explain/optimized_plan_as_text.slt
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ SELECT (SELECT iv.a FROM iv WHERE iv.b = t.b LIMIT 1), (SELECT mv.a FROM mv WHER
Explained Query:
Return
Project (#2, #4)
Join on=(eq(#0, #1, #3)) type=delta
Join on=(#0 = #1 = #3) type=delta
ArrangeBy keys=[[#0]]
Get l0
ArrangeBy keys=[[#0]]
Expand Down Expand Up @@ -394,7 +394,7 @@ Explained Query:
With
cte l2 =
Project (#0..=#2)
Join on=(eq(#1, #3, #4)) type=delta
Join on=(#1 = #3 = #4) type=delta
Get l1
Get l1
ArrangeBy keys=[[#0]]
Expand Down Expand Up @@ -591,7 +591,7 @@ WHERE t1.b = t2.b AND t2.b = t3.b
Explained Query:
Return
Project (#0, #2)
Join on=(eq(#1, #3, #4)) type=delta
Join on=(#1 = #3 = #4) type=delta
Get l1
Get l1
ArrangeBy keys=[[#0]]
Expand Down Expand Up @@ -1036,7 +1036,7 @@ Explained Query:
Return
Project (#0, #1, #0, #3, #0, #5)
Filter (#0) IS NOT NULL
Join on=(eq(#0, #2, #4)) type=delta
Join on=(#0 = #2 = #4) type=delta
Get l0
Get l0
Get l0
Expand Down
2 changes: 1 addition & 1 deletion test/sqllogictest/joins.slt
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ Explained Query:
ArrangeBy keys=[[#0]] // { arity: 1 }
Distinct project=[#0] // { arity: 1 }
Project (#0) // { arity: 1 }
Join on=(eq(#1, #2, #3)) type=delta // { arity: 4 }
Join on=(#1 = #2 = #3) type=delta // { arity: 4 }
implementation
%0:l1 » %1[#0]UKA » %2[#0]UKA
%1 » %2[#0]UKA » %0:l1[#1]KAf
Expand Down
Loading

0 comments on commit cdbb5b6

Please sign in to comment.