Skip to content

Commit e7c99cf

Browse files
committed
Adding Generalized Rule
1 parent 4e6ed82 commit e7c99cf

2 files changed

Lines changed: 114 additions & 2 deletions

File tree

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
<groupId>org.apache.maven.plugins</groupId>
5252
<artifactId>maven-compiler-plugin</artifactId>
5353
<configuration>
54-
<source>23</source>
55-
<target>23</target>
54+
<source>25</source>
55+
<target>25</target>
5656
<compilerArgs>
5757
<arg>--enable-preview</arg>
5858
</compilerArgs>
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.qed.RRuleInstances;
2+
3+
import kala.collection.Seq;
4+
import org.apache.calcite.rel.RelNode;
5+
import org.qed.RelRN;
6+
import org.qed.RexRN;
7+
import org.qed.RRule;
8+
import org.qed.RuleBuilder;
9+
10+
/**
11+
* Generalized version of ProjectAggregateMerge using abstract sources and aggregate calls.
12+
*
13+
* This rule eliminates unused aggregate calls when a Project only uses
14+
* a subset of the aggregate's output fields.
15+
*
16+
* Pattern: Project(selected fields) over Aggregate(all fields)
17+
* -> Aggregate(only used fields) + Project(adjusted field indices)
18+
*
19+
* Unlike the concrete version, this uses:
20+
* - Abstract source (RelRN.scan) instead of concrete table
21+
* - Abstract aggregate calls instead of concrete SUM/AVG/COUNT/MAX
22+
* - Same projection pattern but with abstract field references
23+
*/
24+
public record ProjectAggregateMergeGeneralized() implements RRule {
25+
// Abstract source - no concrete schema
26+
static final RelRN source = RelRN.scan("Source", "Source_Type");
27+
28+
// Group by expression
29+
static final RexRN.GroupBy groupExpr = source.groupBy("groupByName");
30+
31+
// Multiple aggregate calls - some will be used, some unused
32+
static final RelRN.AggCall usedAgg1 = source.aggCall("usedAgg1");
33+
static final RelRN.AggCall unusedAgg = source.aggCall("unusedAgg");
34+
static final RelRN.AggCall usedAgg2 = source.aggCall("usedAgg2");
35+
36+
@Override
37+
public RelRN before() {
38+
// Aggregate with multiple calls: [groupKey, usedAgg1, unusedAgg, usedAgg2]
39+
// Field layout: 0=groupKey, 1=usedAgg1, 2=unusedAgg, 3=usedAgg2
40+
RelRN aggregated = new RelRN.Aggregate(
41+
source,
42+
Seq.of(groupExpr),
43+
Seq.of(usedAgg1, unusedAgg, usedAgg2)
44+
);
45+
46+
// Project that only uses fields 0 (group), 1 (usedAgg1), and 3 (usedAgg2)
47+
// Field 2 (unusedAgg) is skipped
48+
return new ProjectUsingSubsetOfAggregates(aggregated);
49+
}
50+
51+
@Override
52+
public RelRN after() {
53+
// Optimized aggregate with only used calls: [groupKey, usedAgg1, usedAgg2]
54+
// Field layout: 0=groupKey, 1=usedAgg1, 2=usedAgg2
55+
RelRN optimizedAgg = new RelRN.Aggregate(
56+
source,
57+
Seq.of(groupExpr),
58+
Seq.of(usedAgg1, usedAgg2)
59+
);
60+
61+
// Project with adjusted field indices:
62+
// field(0) = groupKey (was 0, still 0)
63+
// field(1) = usedAgg1 (was 1, still 1)
64+
// field(2) = usedAgg2 (was 3, now 2)
65+
return new ProjectOptimized(optimizedAgg);
66+
}
67+
68+
/**
69+
* Project that selects a subset of aggregate output fields.
70+
* Uses fields 0 (group), 1 (usedAgg1), and 3 (usedAgg2), skipping field 2 (unusedAgg).
71+
*/
72+
public static record ProjectUsingSubsetOfAggregates(RelRN input) implements RelRN {
73+
@Override
74+
public RelNode semantics() {
75+
var builder = RuleBuilder.create();
76+
builder.push(input.semantics());
77+
78+
// Project only the fields that are actually used
79+
builder.project(
80+
builder.alias(builder.field(0), "groupByOut"),
81+
builder.alias(builder.field(1), "usedAgg1Out"),
82+
builder.alias(builder.field(3), "usedAgg2Out")
83+
);
84+
85+
return builder.build();
86+
}
87+
}
88+
89+
/**
90+
* Optimized project with adjusted field references after removing unused aggregates.
91+
*/
92+
public static record ProjectOptimized(RelRN input) implements RelRN {
93+
@Override
94+
public RelNode semantics() {
95+
var builder = RuleBuilder.create();
96+
builder.push(input.semantics());
97+
98+
// After optimization, field layout is:
99+
// field(0) = groupKey (was 0, still 0)
100+
// field(1) = usedAgg1 (was 1, still 1)
101+
// field(2) = usedAgg2 (was 3, now 2)
102+
builder.project(
103+
builder.alias(builder.field(0), "groupByOut"),
104+
builder.alias(builder.field(1), "usedAgg1Out"),
105+
builder.alias(builder.field(2), "usedAgg2Out")
106+
);
107+
108+
return builder.build();
109+
}
110+
}
111+
}
112+

0 commit comments

Comments
 (0)