Skip to content

Commit 05944e4

Browse files
authored
Patch fix to how COUNT(*) calls above joins get split (#437)
Patching the logic of how `COUNT(*)` calls are handled in the aggregation splitting logic to avoid bugs elsewhere int he codebase. Also added several simplification rules exposed by the changes: - `INTEGER(true)` -> `1` - `INTEGER(false)` -> `0` - `INTEGER(int_literal)` -> `int_literal` - `1 * x` -> `x` - `x * 1` -> `x` - `0 * x` -> `0` - `x * 0` -> `0`
1 parent b90a863 commit 05944e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+186
-148
lines changed

pydough/conversion/agg_split.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -375,11 +375,6 @@ def attempt_join_aggregate_transpose(
375375
# perform the transpose.
376376
return node, True
377377

378-
if need_count_aggs and not (can_push_left and can_push_right):
379-
# If we need to push down COUNT(*) aggregates, but cannot push into
380-
# both sides of the join, we cannot perform the transpose.
381-
return node, True
382-
383378
# Parse the join condition to identify the lists of equi-join keys
384379
# from the LHS and RHS, and verify that all of the columns used by
385380
# the condition are in those lists.
@@ -406,21 +401,31 @@ def attempt_join_aggregate_transpose(
406401

407402
# If we need count aggregates, add one to each side of the join.
408403
if need_count_aggs:
409-
assert len(count_aggs) > 0
410-
lhs_aggs.append(count_aggs.pop())
411-
new_agg_name: str
404+
lhs_agg_name: str
405+
rhs_agg_name: str
412406
idx: int = 0
413407
while True:
414-
new_agg_name = f"agg_{idx}"
415-
if new_agg_name not in node.columns:
408+
lhs_agg_name = f"agg_{idx}"
409+
idx += 1
410+
if lhs_agg_name not in node.columns and lhs_agg_name not in join.columns:
416411
break
412+
while True:
413+
rhs_agg_name = f"agg_{idx}"
417414
idx += 1
418-
node.aggregations[new_agg_name] = CallExpression(
415+
if rhs_agg_name not in node.columns and rhs_agg_name not in join.columns:
416+
break
417+
node.aggregations[lhs_agg_name] = CallExpression(
419418
pydop.COUNT,
420419
NumericType(),
421420
[],
422421
)
423-
rhs_aggs.append(new_agg_name)
422+
node.aggregations[rhs_agg_name] = CallExpression(
423+
pydop.COUNT,
424+
NumericType(),
425+
[],
426+
)
427+
lhs_aggs.append(lhs_agg_name)
428+
rhs_aggs.append(rhs_agg_name)
424429

425430
# Loop over both inputs and perform the pushdown into whichever one(s)
426431
# will allow an aggregate to be pushed into them.
@@ -445,20 +450,32 @@ def attempt_join_aggregate_transpose(
445450
if side_count_ref is not None:
446451
count_refs.append(side_count_ref)
447452

448-
# For each COUNT(*) aggregate, replace with the product of the COUNT(*)
449-
# calls that were pushed into each side of the join.
453+
# For each COUNT(*) aggregate, replace with the product of the calls that
454+
# were pushed into each side of the join.
450455
for count_call_name in count_aggs:
451-
assert len(count_refs) > 1
452-
product: RelationalExpression = CallExpression(
453-
pydop.MUL, NumericType(), count_refs
454-
)
455-
projection_columns[count_call_name] = product
456-
need_projection = True
456+
if len(count_refs) > 1:
457+
product: RelationalExpression = CallExpression(
458+
pydop.MUL, NumericType(), count_refs
459+
)
460+
product_sum: CallExpression = CallExpression(
461+
pydop.SUM, NumericType(), [product]
462+
)
463+
node.aggregations[count_call_name] = product_sum
464+
node.columns[count_call_name] = product_sum
465+
elif len(count_refs) == 1:
466+
regular_sum: CallExpression = CallExpression(
467+
pydop.SUM, NumericType(), [count_refs[0]]
468+
)
469+
node.aggregations[count_call_name] = regular_sum
470+
node.columns[count_call_name] = regular_sum
457471

458472
# If the node requires projection at the end, create a new Project node on
459473
# top of the top aggregate.
460474
if need_projection:
461-
return Project(node, projection_columns), True
475+
new_node: RelationalNode = node.copy(
476+
inputs=[split_partial_aggregates(input, config) for input in node.inputs]
477+
)
478+
return Project(new_node, projection_columns), False
462479
else:
463480
return node, True
464481

pydough/conversion/relational_simplification.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,16 @@ def simplify_function_call(
884884
not_null=True, not_negative=True
885885
)
886886

887+
# INTEGER(x) -> x if x is a literal integer. Also simplify for
888+
# booleans.
889+
case pydop.INTEGER:
890+
if isinstance(expr.inputs[0], LiteralExpression) and isinstance(
891+
expr.inputs[0].value, (int, bool)
892+
):
893+
output_expr = LiteralExpression(
894+
int(expr.inputs[0].value), expr.data_type
895+
)
896+
887897
# The result of addition is non-negative or positive if all the
888898
# operands are. It is also positive if all the operands are
889899
# non-negative and at least one of them is positive.
@@ -895,11 +905,35 @@ def simplify_function_call(
895905
output_predicates.positive = True
896906

897907
# The result of multiplication is non-negative or positive if all
898-
# the operands are.
908+
# the operands are. Also, simplify when any argument is 0 to the
909+
# output being 0, and remove any arguments that are 1.
899910
case pydop.MUL:
900911
output_predicates |= intersect_set & PredicateSet(
901912
not_negative=True, positive=True
902913
)
914+
remaining_args: list[RelationalExpression] = [
915+
arg
916+
for arg in expr.inputs
917+
if not (
918+
isinstance(arg, LiteralExpression)
919+
and arg.value in (1, 1.0, True)
920+
)
921+
]
922+
if len(remaining_args) == 0:
923+
output_expr = expr.inputs[0]
924+
elif len(remaining_args) == 1:
925+
output_expr = remaining_args[0]
926+
elif len(remaining_args) < len(expr.inputs):
927+
output_expr = CallExpression(
928+
pydop.MUL, expr.data_type, remaining_args
929+
)
930+
for arg in expr.inputs:
931+
if isinstance(arg, LiteralExpression) and arg.value in (
932+
0,
933+
0.0,
934+
False,
935+
):
936+
output_expr = LiteralExpression(0, expr.data_type)
903937

904938
# The result of division is non-negative or positive if all the
905939
# operands are, and is also non-null if both operands are non-null

tests/test_pipeline_custom_datasets.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@
3030
"synthea",
3131
lambda: pd.DataFrame(
3232
{
33-
"condition_description": [
34-
"Escherichia coli urinary tract infection"
35-
],
33+
"condition_description": ["Normal pregnancy"],
3634
}
3735
),
3836
"synthea_most_common_conditions",

tests/test_pipeline_defog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,5 +1503,5 @@ def test_defog_e2e(
15031503
same database connector. Run on the defog.ai queries.
15041504
"""
15051505
defog_pipeline_test_data.run_e2e_test(
1506-
defog_graphs, sqlite_defog_connection, defog_config
1506+
defog_graphs, sqlite_defog_connection, defog_config, coerce_types=True
15071507
)

tests/test_pipeline_defog_custom.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2304,7 +2304,7 @@ def test_pipeline_e2e_defog_custom(
23042304
schemas.
23052305
"""
23062306
defog_custom_pipeline_test_data.run_e2e_test(
2307-
defog_graphs, sqlite_defog_connection, config=defog_config
2307+
defog_graphs, sqlite_defog_connection, config=defog_config, coerce_types=True
23082308
)
23092309

23102310

tests/test_pipeline_mysql.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,4 +554,5 @@ def test_pipeline_e2e_mysql_defog(
554554
defog_config,
555555
reference_database=sqlite_defog_connection,
556556
coerce_types=True,
557+
rtol=1e4,
557558
)

tests/test_plan_refsols/common_prefix_b.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
ROOT(columns=[('name', r_name), ('n_nations', n_rows), ('n_customers', sum_sum_expr), ('n_suppliers', sum_n_rows)], orderings=[(r_name):asc_first])
22
JOIN(condition=t0.r_regionkey == t1.n_regionkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_rows': t1.n_rows, 'r_name': t0.r_name, 'sum_n_rows': t1.sum_n_rows, 'sum_sum_expr': t1.sum_sum_expr})
33
SCAN(table=tpch.REGION, columns={'r_name': r_name, 'r_regionkey': r_regionkey})
4-
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr': SUM(sum_expr)})
5-
JOIN(condition=t0.n_nationkey == t1.s_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows, 'sum_expr': t0.n_rows})
4+
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr': SUM(sum_expr_4)})
5+
JOIN(condition=t0.n_nationkey == t1.s_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows, 'sum_expr_4': t0.n_rows})
66
JOIN(condition=t0.n_nationkey == t1.c_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_nationkey': t0.n_nationkey, 'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows})
77
SCAN(table=tpch.NATION, columns={'n_nationkey': n_nationkey, 'n_regionkey': n_regionkey})
88
AGGREGATE(keys={'c_nationkey': c_nationkey}, aggregations={'n_rows': COUNT()})

tests/test_plan_refsols/common_prefix_c.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
ROOT(columns=[('name', r_name), ('n_nations', n_rows), ('n_customers', sum_sum_expr), ('n_suppliers', sum_n_rows), ('n_orders', DEFAULT_TO(sum_sum_expr_15, 0:numeric)), ('n_parts', sum_sum_n_rows)], orderings=[(r_name):asc_first])
2-
JOIN(condition=t0.r_regionkey == t1.n_regionkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_rows': t1.n_rows, 'r_name': t0.r_name, 'sum_n_rows': t1.sum_sum_n_rows_0, 'sum_sum_expr': t1.sum_n_rows, 'sum_sum_expr_15': t1.sum_sum_expr_15, 'sum_sum_n_rows': t1.sum_sum_n_rows})
1+
ROOT(columns=[('name', r_name), ('n_nations', n_rows), ('n_customers', sum_expr), ('n_suppliers', sum_n_rows), ('n_orders', DEFAULT_TO(sum_sum_expr, 0:numeric)), ('n_parts', sum_sum_n_rows)], orderings=[(r_name):asc_first])
2+
JOIN(condition=t0.r_regionkey == t1.n_regionkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_rows': t1.n_rows, 'r_name': t0.r_name, 'sum_expr': t1.sum_n_rows, 'sum_n_rows': t1.sum_sum_n_rows_0, 'sum_sum_expr': t1.sum_sum_expr, 'sum_sum_n_rows': t1.sum_sum_n_rows})
33
SCAN(table=tpch.REGION, columns={'r_name': r_name, 'r_regionkey': r_regionkey})
4-
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr_15': SUM(sum_expr_15), 'sum_sum_n_rows': SUM(sum_n_rows), 'sum_sum_n_rows_0': SUM(sum_n_rows_0)})
4+
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr': SUM(sum_expr_15), 'sum_sum_n_rows': SUM(sum_n_rows), 'sum_sum_n_rows_0': SUM(sum_n_rows_0)})
55
JOIN(condition=t0.n_nationkey == t1.s_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_regionkey': t0.n_regionkey, 'n_rows': t0.n_rows, 'sum_expr_15': t0.sum_n_rows, 'sum_n_rows': t1.sum_n_rows, 'sum_n_rows_0': t1.n_rows})
66
JOIN(condition=t0.n_nationkey == t1.c_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_nationkey': t0.n_nationkey, 'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows, 'sum_n_rows': t1.sum_n_rows})
77
SCAN(table=tpch.NATION, columns={'n_nationkey': n_nationkey, 'n_regionkey': n_regionkey})
Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
1-
ROOT(columns=[('name', r_name), ('n_nations', n_rows), ('n_customers', sum_sum_expr), ('n_suppliers', sum_n_rows), ('n_orders_94', DEFAULT_TO(sum_sum_sum_expr, 0:numeric)), ('n_orders_95', DEFAULT_TO(sum_sum_expr_10, 0:numeric)), ('n_orders_96', DEFAULT_TO(sum_sum_n_rows, 0:numeric))], orderings=[(r_name):asc_first])
2-
JOIN(condition=t0.r_regionkey == t1.n_regionkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_rows': t1.n_rows, 'r_name': t0.r_name, 'sum_n_rows': t1.sum_sum_n_rows_0, 'sum_sum_expr': t1.sum_n_rows, 'sum_sum_expr_10': t1.sum_sum_expr_10, 'sum_sum_n_rows': t1.sum_sum_n_rows, 'sum_sum_sum_expr': t1.sum_sum_sum_expr})
1+
ROOT(columns=[('name', r_name), ('n_nations', n_rows), ('n_customers', sum_expr), ('n_suppliers', sum_n_rows), ('n_orders_94', DEFAULT_TO(sum_sum_expr, 0:numeric)), ('n_orders_95', DEFAULT_TO(sum_sum_expr_10, 0:numeric)), ('n_orders_96', DEFAULT_TO(sum_sum_n_rows, 0:numeric))], orderings=[(r_name):asc_first])
2+
JOIN(condition=t0.r_regionkey == t1.n_regionkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_rows': t1.n_rows, 'r_name': t0.r_name, 'sum_expr': t1.sum_n_rows, 'sum_n_rows': t1.sum_sum_n_rows_0, 'sum_sum_expr': t1.sum_sum_expr, 'sum_sum_expr_10': t1.sum_sum_expr_10, 'sum_sum_n_rows': t1.sum_sum_n_rows})
33
SCAN(table=tpch.REGION, columns={'r_name': r_name, 'r_regionkey': r_regionkey})
4-
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr_10': SUM(sum_expr_10), 'sum_sum_n_rows': SUM(sum_n_rows), 'sum_sum_n_rows_0': SUM(sum_n_rows_0), 'sum_sum_sum_expr': SUM(sum_sum_expr)})
5-
JOIN(condition=t0.n_nationkey == t1.s_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_regionkey': t0.n_regionkey, 'n_rows': t0.n_rows, 'sum_expr_10': t0.sum_expr_10, 'sum_n_rows': t0.sum_n_rows, 'sum_n_rows_0': t1.n_rows, 'sum_sum_expr': t0.sum_sum_expr})
6-
PROJECT(columns={'n_nationkey': n_nationkey, 'n_regionkey': n_regionkey, 'n_rows': n_rows, 'sum_expr_10': sum_expr_10, 'sum_n_rows': sum_n_rows, 'sum_sum_expr': sum_expr})
7-
JOIN(condition=t0.n_nationkey == t1.c_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_nationkey': t0.n_nationkey, 'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows, 'sum_expr': t1.sum_expr_7, 'sum_expr_10': t1.sum_expr_10, 'sum_n_rows': t1.sum_n_rows})
8-
SCAN(table=tpch.NATION, columns={'n_nationkey': n_nationkey, 'n_regionkey': n_regionkey})
9-
AGGREGATE(keys={'c_nationkey': c_nationkey}, aggregations={'n_rows': COUNT(), 'sum_expr_10': SUM(expr_10), 'sum_expr_7': SUM(expr_7), 'sum_n_rows': SUM(n_rows)})
10-
JOIN(condition=t0.c_custkey == t1.o_custkey, type=LEFT, cardinality=SINGULAR_FILTER, columns={'c_nationkey': t0.c_nationkey, 'expr_10': t0.n_rows, 'expr_7': t0.expr_7, 'n_rows': t1.n_rows})
11-
JOIN(condition=t0.c_custkey == t1.o_custkey, type=LEFT, cardinality=SINGULAR_FILTER, columns={'c_custkey': t0.c_custkey, 'c_nationkey': t0.c_nationkey, 'expr_7': t0.n_rows, 'n_rows': t1.n_rows})
12-
JOIN(condition=t0.c_custkey == t1.o_custkey, type=LEFT, cardinality=SINGULAR_FILTER, columns={'c_custkey': t0.c_custkey, 'c_nationkey': t0.c_nationkey, 'n_rows': t1.n_rows})
13-
SCAN(table=tpch.CUSTOMER, columns={'c_custkey': c_custkey, 'c_nationkey': c_nationkey})
14-
AGGREGATE(keys={'o_custkey': o_custkey}, aggregations={'n_rows': COUNT()})
15-
FILTER(condition=YEAR(o_orderdate) == 1994:numeric, columns={'o_custkey': o_custkey})
16-
SCAN(table=tpch.ORDERS, columns={'o_custkey': o_custkey, 'o_orderdate': o_orderdate})
4+
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr': SUM(sum_expr), 'sum_sum_expr_10': SUM(sum_expr_10), 'sum_sum_n_rows': SUM(sum_n_rows), 'sum_sum_n_rows_0': SUM(sum_n_rows_0)})
5+
JOIN(condition=t0.n_nationkey == t1.s_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_regionkey': t0.n_regionkey, 'n_rows': t0.n_rows, 'sum_expr': t0.sum_expr, 'sum_expr_10': t0.sum_expr_10, 'sum_n_rows': t0.sum_n_rows, 'sum_n_rows_0': t1.n_rows})
6+
JOIN(condition=t0.n_nationkey == t1.c_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_nationkey': t0.n_nationkey, 'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows, 'sum_expr': t1.sum_expr_7, 'sum_expr_10': t1.sum_expr_10, 'sum_n_rows': t1.sum_n_rows})
7+
SCAN(table=tpch.NATION, columns={'n_nationkey': n_nationkey, 'n_regionkey': n_regionkey})
8+
AGGREGATE(keys={'c_nationkey': c_nationkey}, aggregations={'n_rows': COUNT(), 'sum_expr_10': SUM(expr_10), 'sum_expr_7': SUM(expr_7), 'sum_n_rows': SUM(n_rows)})
9+
JOIN(condition=t0.c_custkey == t1.o_custkey, type=LEFT, cardinality=SINGULAR_FILTER, columns={'c_nationkey': t0.c_nationkey, 'expr_10': t0.n_rows, 'expr_7': t0.expr_7, 'n_rows': t1.n_rows})
10+
JOIN(condition=t0.c_custkey == t1.o_custkey, type=LEFT, cardinality=SINGULAR_FILTER, columns={'c_custkey': t0.c_custkey, 'c_nationkey': t0.c_nationkey, 'expr_7': t0.n_rows, 'n_rows': t1.n_rows})
11+
JOIN(condition=t0.c_custkey == t1.o_custkey, type=LEFT, cardinality=SINGULAR_FILTER, columns={'c_custkey': t0.c_custkey, 'c_nationkey': t0.c_nationkey, 'n_rows': t1.n_rows})
12+
SCAN(table=tpch.CUSTOMER, columns={'c_custkey': c_custkey, 'c_nationkey': c_nationkey})
1713
AGGREGATE(keys={'o_custkey': o_custkey}, aggregations={'n_rows': COUNT()})
18-
FILTER(condition=YEAR(o_orderdate) == 1995:numeric, columns={'o_custkey': o_custkey})
14+
FILTER(condition=YEAR(o_orderdate) == 1994:numeric, columns={'o_custkey': o_custkey})
1915
SCAN(table=tpch.ORDERS, columns={'o_custkey': o_custkey, 'o_orderdate': o_orderdate})
2016
AGGREGATE(keys={'o_custkey': o_custkey}, aggregations={'n_rows': COUNT()})
21-
FILTER(condition=YEAR(o_orderdate) == 1996:numeric, columns={'o_custkey': o_custkey})
17+
FILTER(condition=YEAR(o_orderdate) == 1995:numeric, columns={'o_custkey': o_custkey})
2218
SCAN(table=tpch.ORDERS, columns={'o_custkey': o_custkey, 'o_orderdate': o_orderdate})
19+
AGGREGATE(keys={'o_custkey': o_custkey}, aggregations={'n_rows': COUNT()})
20+
FILTER(condition=YEAR(o_orderdate) == 1996:numeric, columns={'o_custkey': o_custkey})
21+
SCAN(table=tpch.ORDERS, columns={'o_custkey': o_custkey, 'o_orderdate': o_orderdate})
2322
AGGREGATE(keys={'s_nationkey': s_nationkey}, aggregations={'n_rows': COUNT()})
2423
SCAN(table=tpch.SUPPLIER, columns={'s_nationkey': s_nationkey})

tests/test_plan_refsols/common_prefix_f.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
ROOT(columns=[('name', r_name), ('n_customers', sum_sum_expr), ('n_nations', n_rows), ('n_suppliers', sum_n_rows)], orderings=[(r_name):asc_first])
22
JOIN(condition=t0.r_regionkey == t1.n_regionkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_rows': t1.n_rows, 'r_name': t0.r_name, 'sum_n_rows': t1.sum_n_rows, 'sum_sum_expr': t1.sum_sum_expr})
33
SCAN(table=tpch.REGION, columns={'r_name': r_name, 'r_regionkey': r_regionkey})
4-
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr': SUM(sum_expr)})
5-
JOIN(condition=t0.n_nationkey == t1.s_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows, 'sum_expr': t0.n_rows})
4+
AGGREGATE(keys={'n_regionkey': n_regionkey}, aggregations={'n_rows': COUNT(), 'sum_n_rows': SUM(n_rows), 'sum_sum_expr': SUM(sum_expr_4)})
5+
JOIN(condition=t0.n_nationkey == t1.s_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows, 'sum_expr_4': t0.n_rows})
66
JOIN(condition=t0.n_nationkey == t1.c_nationkey, type=INNER, cardinality=SINGULAR_ACCESS, columns={'n_nationkey': t0.n_nationkey, 'n_regionkey': t0.n_regionkey, 'n_rows': t1.n_rows})
77
SCAN(table=tpch.NATION, columns={'n_nationkey': n_nationkey, 'n_regionkey': n_regionkey})
88
AGGREGATE(keys={'c_nationkey': c_nationkey}, aggregations={'n_rows': COUNT()})

0 commit comments

Comments
 (0)