diff --git a/src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinJoinRemove.java b/src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinJoinRemove.java new file mode 100644 index 0000000..7cac22a --- /dev/null +++ b/src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinJoinRemove.java @@ -0,0 +1,40 @@ +package org.qed.Generated.RRuleInstances; + +import kala.collection.Seq; +import org.apache.calcite.rel.core.JoinRelType; +import org.qed.RRule; +import org.qed.RelRN; +import org.qed.RexRN; +import org.qed.RuleBuilder; + +public record AggregateJoinJoinRemove() implements RRule { + static final RelRN tblA = RelRN.scan("sourceA1", "typeA1") + .join(JoinRelType.INNER, RexRN.trueLiteral(), RelRN.scan("sourceA2", "typeA2")); + static final RelRN tblB = RelRN.scan("sourceB1", "typeB1") + .join(JoinRelType.INNER, RexRN.trueLiteral(), RelRN.scan("sourceB2", "typeB2")); + static final RelRN tblC = RelRN.scan("sourceC1", "typeC1") + .join(JoinRelType.INNER, RexRN.trueLiteral(), RelRN.scan("sourceC2", "typeC2")); + + static final RexRN bottomJoinCondition = new RexRN.Pred( + RuleBuilder.create().genericPredicateOp("=", true), + Seq.of(tblA.field(0), tblB.field(0)) + ); + + static final RexRN topJoinCondition = new RexRN.Pred( + RuleBuilder.create().genericPredicateOp("=", true), + Seq.of(tblA.field(0), tblC.field(0)) + ); + + @Override + public RelRN before() { + RelRN bottomJoin = tblA.join(JoinRelType.LEFT, bottomJoinCondition, tblB); + RelRN topJoin = bottomJoin.join(JoinRelType.LEFT, topJoinCondition, tblC); + return new RelRN.Aggregate(topJoin, Seq.of(topJoin.field(0), topJoin.field(4)), Seq.empty()); + } + + @Override + public RelRN after() { + RelRN newJoin = tblA.join(JoinRelType.LEFT, topJoinCondition, tblC); + return new RelRN.Aggregate(newJoin, Seq.of(newJoin.field(0), newJoin.field(2)), Seq.empty()); + } +} \ No newline at end of file diff --git a/src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinRemove.java b/src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinRemove.java new file mode 100644 index 0000000..9972737 --- /dev/null +++ b/src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinRemove.java @@ -0,0 +1,32 @@ +package org.qed.Generated.RRuleInstances; + +import kala.collection.Seq; +import org.apache.calcite.rel.core.JoinRelType; +import org.qed.RRule; +import org.qed.RelRN; +import org.qed.RexRN; +import org.qed.RuleBuilder; + +public record AggregateJoinRemove() implements RRule { + static final RelRN tblA = RelRN.scan("sourceA1", "typeA1") + .join(JoinRelType.INNER, RexRN.trueLiteral(), RelRN.scan("sourceA2", "typeA2")); + + static final RelRN tblB = RelRN.scan("sourceB1", "typeB1") + .join(JoinRelType.INNER, RexRN.trueLiteral(), RelRN.scan("sourceB2", "typeB2")); + + static final RexRN joinCondition = new RexRN.Pred( + RuleBuilder.create().genericPredicateOp("=", true), + Seq.of(tblA.field(0), tblB.field(0)) + ); + + @Override + public RelRN before() { + RelRN leftJoin = tblA.join(JoinRelType.LEFT, joinCondition, tblB); + return new RelRN.Aggregate(leftJoin, Seq.of(leftJoin.field(0)), Seq.empty()); + } + + @Override + public RelRN after() { + return new RelRN.Aggregate(tblA, Seq.of(tblA.field(0)), Seq.empty()); + } +} \ No newline at end of file diff --git a/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectConstantToDummyJoin.java b/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectConstantToDummyJoin.java index 847ad7d..8f88ce8 100644 --- a/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectConstantToDummyJoin.java +++ b/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectConstantToDummyJoin.java @@ -10,184 +10,108 @@ import kala.collection.Seq; import kala.tuple.Tuple; -/** - * AggregateProjectConstantToDummyJoinRule: Replaces constant literals in GROUP BY - * with a dummy table join. - * - * Pattern: - * Aggregate(group=[constant_literal, regular_field]) - * Project(constant_literal, regular_field) - * Scan - * - * => - * - * Aggregate(group=[dummy.constant, regular_field]) - * Project(dummy.constant, regular_field) - * Join(Scan, DummyValues(constant_literal)) - * - * This optimization can help with certain database engines that handle - * joins more efficiently than literal constants in GROUP BY clauses. - */ public record AggregateProjectConstantToDummyJoin() implements RRule { - - // Base table for the pattern + static final RelRN baseTable = new BaseEmployeeTable(); @Override public RelRN before() { - // Aggregate over project with constant literals var projectWithConstants = new ProjectWithConstantLiterals(baseTable); return new AggregateGroupingByConstants(projectWithConstants); } @Override public RelRN after() { - // Optimized: join with dummy table containing constants var dummyTable = new DummyConstantsTable(); var joinWithDummy = new JoinWithDummyTable(baseTable, dummyTable); var projectWithDummyFields = new ProjectWithDummyFields(joinWithDummy); return new AggregateGroupingByDummyFields(projectWithDummyFields); } - /** - * Base employee table - */ public static record BaseEmployeeTable() implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); var table = builder.createQedTable(Seq.of( - Tuple.of(RelType.fromString("INTEGER", true), false), // emp_id - Tuple.of(RelType.fromString("DECIMAL", true), false), // salary - Tuple.of(RelType.fromString("INTEGER", true), false) // dept_id + Tuple.of(RelType.fromString("INTEGER", true), false), + Tuple.of(RelType.fromString("DECIMAL", true), false), + Tuple.of(RelType.fromString("INTEGER", true), false) )); builder.addTable(table); return builder.scan(table.getName()).build(); } } - - /** - * Project with constant literals: SELECT emp_id, TRUE as active_flag, '2024' as year_label, salary - */ + + public static record ProjectWithConstantLiterals(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - builder.project( - builder.field(0), // emp_id - builder.alias(builder.literal(true), "active_flag"), // constant: TRUE - builder.alias(builder.literal("2024"), "year_label"), // constant: '2024' - builder.field(1) // salary - ); - + builder.project(builder.field(0), builder.alias(builder.literal(true), "active_flag"), builder.alias(builder.literal("2024"), "year_label"), builder.field(1)); return builder.build(); } } - /** - * Aggregate grouping by constant literals: GROUP BY active_flag, year_label, emp_id - */ + public static record AggregateGroupingByConstants(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Group by the constant fields and emp_id - var groupKey = builder.groupKey( - builder.field(1), // active_flag (constant) - builder.field(2), // year_label (constant) - builder.field(0) // emp_id (regular field) - ); - - // Aggregate: AVG(salary) + + var groupKey = builder.groupKey(builder.field(1), builder.field(2), builder.field(0)); + var avgSalary = builder.avg(builder.field(3)); builder.aggregate(groupKey, avgSalary); return builder.build(); } } - - /** - * Dummy table containing the constant values: VALUES (TRUE, '2024') - */ + public static record DummyConstantsTable() implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - - // Create a values table with the constants - // Using the correct values() method signature - builder.values( - new String[]{"active_flag", "year_label"}, // Column names - true, // TRUE constant value - "2024" // '2024' constant value - ); + + builder.values(new String[]{"active_flag", "year_label"}, true, "2024"); return builder.build(); } } - - /** - * Join base table with dummy constants table - */ + public static record JoinWithDummyTable(RelRN baseTable, RelRN dummyTable) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - builder.push(baseTable.semantics()); builder.push(dummyTable.semantics()); - - // Cross join (INNER JOIN with TRUE condition) builder.join(JoinRelType.INNER, builder.literal(true)); - return builder.build(); } } - - /** - * Project using dummy fields instead of constants: SELECT emp_id, dummy.active_flag, dummy.year_label, salary - */ + public static record ProjectWithDummyFields(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // After join: base table fields are 0,1,2 and dummy fields are 3,4 - builder.project( - builder.field(0), // emp_id (from base table) - builder.field(3), // active_flag (from dummy table) - builder.field(4), // year_label (from dummy table) - builder.field(1) // salary (from base table) - ); + + builder.project(builder.field(0), builder.field(3), builder.field(4), builder.field(1)); return builder.build(); } } - - /** - * Aggregate grouping by dummy fields: GROUP BY dummy.active_flag, dummy.year_label, emp_id - */ + public static record AggregateGroupingByDummyFields(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Group by the dummy fields and emp_id - var groupKey = builder.groupKey( - builder.field(1), // active_flag (from dummy) - builder.field(2), // year_label (from dummy) - builder.field(0) // emp_id (regular field) - ); - - // Same aggregate: AVG(salary) + var groupKey = builder.groupKey(builder.field(1), builder.field(2), builder.field(0)); + var avgSalary = builder.avg(builder.field(3)); builder.aggregate(groupKey, avgSalary); diff --git a/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectMerge.java b/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectMerge.java index 35d38e0..6ce8a62 100644 --- a/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectMerge.java +++ b/src/main/java/org/qed/Generated/RRuleInstances/AggregateProjectMerge.java @@ -8,34 +8,21 @@ import kala.collection.Seq; import kala.tuple.Tuple; -/** - * Abstract AggregateProjectMergeRule that represents valid transformations: - * - * Aggregate(Project_with_field_references(R)) => Aggregate(R) - * - * The project must contain only field references, not expressions. - */ public record AggregateProjectMerge() implements RRule { - - // Multi-column base relation + static final RelRN R = new MultiColumnRelation(); @Override public RelRN before() { - // Pattern: Aggregate over Project that selects/reorders fields var projection = new FieldReferenceProject(R); return new SimpleAggregate(projection); } @Override public RelRN after() { - // Pattern: Same aggregate directly on base relation return new SimpleAggregate(R); } - /** - * Base relation with multiple columns - */ public static record MultiColumnRelation() implements RelRN { @Override public RelNode semantics() { @@ -51,30 +38,17 @@ public RelNode semantics() { return builder.scan(table.getName()).build(); } } - - /** - * Project that contains ONLY field references (no expressions) - * This represents field selection/reordering that can be eliminated - */ + public static record FieldReferenceProject(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Project that selects only first 2 fields (eliminates 3rd) - // This is pure field selection, not function application - builder.project( - builder.field(0) // $f0 = $0 (field reference) - ); - + builder.project(builder.field(0)); return builder.build(); } } - - /** - * Simple aggregate: GROUP BY first field, COUNT(*) - */ + public static record SimpleAggregate(RelRN input) implements RelRN { @Override public RelNode semantics() { diff --git a/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java new file mode 100644 index 0000000..7098d7a --- /dev/null +++ b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java @@ -0,0 +1,19 @@ +package org.qed.Generated.RRuleInstances; + +import org.apache.calcite.rel.core.JoinRelType; +import org.qed.RelRN; +import org.qed.RexRN; + +public record JoinReduceFalse() { + static final RelRN left = RelRN.scan("Left", "Left_Type"); + static final RelRN right = RelRN.scan("Right", "Right_Type"); + static final RexRN joinCond = RexRN.and(left.joinPred("join", right), RexRN.falseLiteral()); + + public RelRN before() { + return left.join(JoinRelType.INNER, joinCond, right); + } + + public RelRN after() { + return left.join(JoinRelType.LEFT, RexRN.falseLiteral(), right); + } +} diff --git a/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java new file mode 100644 index 0000000..2e26bfe --- /dev/null +++ b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java @@ -0,0 +1,20 @@ +package org.qed.Generated.RRuleInstances; + +import org.apache.calcite.rel.core.JoinRelType; +import org.qed.RelRN; +import org.qed.RexRN; + +public record JoinReduceTrue() { + static final RelRN left = RelRN.scan("Left", "Left_Type"); + static final RelRN right = RelRN.scan("Right", "Right_Type"); + static final RexRN afterJoinCond = left.joinPred("join", right); + static final RexRN beforeJoinCond = RexRN.and(afterJoinCond, RexRN.trueLiteral()); + + public RelRN before() { + return left.join(JoinRelType.INNER, beforeJoinCond, right); + } + + public RelRN after() { + return left.join(JoinRelType.LEFT, afterJoinCond, right); + } +} diff --git a/src/main/java/org/qed/Generated/RRuleInstances/ProjectAggregateMerge.java b/src/main/java/org/qed/Generated/RRuleInstances/ProjectAggregateMerge.java index da37476..f4dc6f7 100644 --- a/src/main/java/org/qed/Generated/RRuleInstances/ProjectAggregateMerge.java +++ b/src/main/java/org/qed/Generated/RRuleInstances/ProjectAggregateMerge.java @@ -27,27 +27,21 @@ * aggregate computations that are not used in the final result. */ public record ProjectAggregateMerge() implements RRule { - - // Base table for the pattern + static final RelRN baseTable = new SalesTable(); @Override public RelRN before() { - // Project that uses only some of the aggregate results var aggregateWithUnusedCalls = new AggregateWithMultipleCalls(baseTable); return new ProjectUsingSubsetOfAggregates(aggregateWithUnusedCalls); } @Override public RelRN after() { - // Optimized: aggregate with only used calls var aggregateOptimized = new AggregateWithUsedCallsOnly(baseTable); return new ProjectOptimized(aggregateOptimized); } - /** - * Sales table with multiple numeric columns for aggregation - */ public static record SalesTable() implements RelRN { @Override public RelNode semantics() { @@ -64,21 +58,14 @@ public RelNode semantics() { return builder.scan(table.getName()).build(); } } - - /** - * Aggregate with multiple calls: GROUP BY region_id, SUM(sales), AVG(cost), COUNT(quantity), MAX(sales) - * Some of these aggregates will be unused in the projection - */ + public static record AggregateWithMultipleCalls(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Group by region_id + var groupKey = builder.groupKey(builder.field(0)); - - // Multiple aggregate calls var sumSales = builder.sum(false, "sum_sales", builder.field(1)); // Will be used var avgCost = builder.avg(builder.field(2)); // Will be unused var countQty = builder.count(false, "count_qty", builder.field(3)); // Will be used @@ -88,76 +75,37 @@ public RelNode semantics() { return builder.build(); } } - - /** - * Project that uses only some aggregates: SELECT region_id, sum_sales, count_qty - * (avgCost and maxSales are not projected, so they're unused) - */ + public static record ProjectUsingSubsetOfAggregates(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Project only used fields: - // field(0) = region_id (group key) - // field(1) = sum_sales (used aggregate) - // field(2) = avg_cost (UNUSED - not projected) - // field(3) = count_qty (used aggregate) - // field(4) = max_sales (UNUSED - not projected) - builder.project( - builder.alias(builder.field(0), "region_id"), // Group key - builder.alias(builder.field(1), "total_sales"), // Used: sum_sales - builder.alias(builder.field(3), "total_count") // Used: count_qty - // avg_cost (field 2) and max_sales (field 4) are not projected - ); + builder.project(builder.alias(builder.field(0), "region_id"), builder.alias(builder.field(1), "total_sales"), builder.alias(builder.field(3), "total_count")); return builder.build(); } } - - /** - * Optimized aggregate with only used calls: GROUP BY region_id, SUM(sales), COUNT(quantity) - * avgCost and maxSales are eliminated since they're not used - */ + public static record AggregateWithUsedCallsOnly(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Same group key var groupKey = builder.groupKey(builder.field(0)); - - // Only the aggregate calls that are actually used var sumSales = builder.sum(false, "sum_sales", builder.field(1)); // Used var countQty = builder.count(false, "count_qty", builder.field(3)); // Used - // avgCost and maxSales removed - they were unused - builder.aggregate(groupKey, sumSales, countQty); return builder.build(); } } - - /** - * Optimized project with adjusted field references - */ + public static record ProjectOptimized(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // After optimization, field layout is: - // field(0) = region_id (group key) - // field(1) = sum_sales (was field 1, still field 1) - // field(2) = count_qty (was field 3, now field 2) - builder.project( - builder.alias(builder.field(0), "region_id"), // Group key - builder.alias(builder.field(1), "total_sales"), // sum_sales - builder.alias(builder.field(2), "total_count") // count_qty (field index adjusted) - ); - + builder.project(builder.alias(builder.field(0), "region_id"), builder.alias(builder.field(1), "total_sales"), builder.alias(builder.field(2), "total_count")); return builder.build(); } } diff --git a/src/main/java/org/qed/Generated/RRuleInstances/UnionPullUpConstants.java b/src/main/java/org/qed/Generated/RRuleInstances/UnionPullUpConstants.java index 6e3b060..f8d3c1c 100644 --- a/src/main/java/org/qed/Generated/RRuleInstances/UnionPullUpConstants.java +++ b/src/main/java/org/qed/Generated/RRuleInstances/UnionPullUpConstants.java @@ -8,34 +8,13 @@ import kala.collection.Seq; import kala.tuple.Tuple; -/** - * UnionPullUpConstantsRule: Pulls up constant expressions through Union operators - * - * Pattern: - * Union( - * Project(col1, constant_value, col3), - * Project(col1, constant_value, col3) - * ) - * => - * Project(col1, constant_value, col3, - * Union( - * Project(col1, col3), - * Project(col1, col3) - * ) - * ) - * - * This optimization reduces the Union to only non-constant columns, - * then adds back the constants in a top-level projection. - */ public record UnionPullUpConstants() implements RRule { - - // Base tables for demonstrating the pattern + static final RelRN leftTable = new LeftTableWithConstants(); static final RelRN rightTable = new RightTableWithConstants(); @Override public RelRN before() { - // Union of two projections that both have constants var leftProjection = new LeftProjectionWithConstants(leftTable); var rightProjection = new RightProjectionWithConstants(rightTable); return new UnionWithConstantColumns(leftProjection, rightProjection); @@ -43,92 +22,61 @@ public RelRN before() { @Override public RelRN after() { - // Optimized: constants pulled up, union reduced to non-constant columns var leftProjectionReduced = new LeftProjectionNonConstants(leftTable); var rightProjectionReduced = new RightProjectionNonConstants(rightTable); var reducedUnion = new UnionReducedColumns(leftProjectionReduced, rightProjectionReduced); return new TopProjectionWithConstants(reducedUnion); } - /** - * Left source table - */ public static record LeftTableWithConstants() implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - var table = builder.createQedTable(Seq.of( - Tuple.of(RelType.fromString("INTEGER", true), false), // emp_id - Tuple.of(RelType.fromString("VARCHAR", true), false), // emp_name - Tuple.of(RelType.fromString("INTEGER", true), false) // dept_id - )); + var table = builder.createQedTable(Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false), Tuple.of(RelType.fromString("VARCHAR", true), false), Tuple.of(RelType.fromString("INTEGER", true), false))); builder.addTable(table); return builder.scan(table.getName()).build(); } } - /** - * Right source table - */ + public static record RightTableWithConstants() implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - var table = builder.createQedTable(Seq.of( - Tuple.of(RelType.fromString("INTEGER", true), false), // emp_id - Tuple.of(RelType.fromString("VARCHAR", true), false), // emp_name - Tuple.of(RelType.fromString("INTEGER", true), false) // dept_id - )); + var table = builder.createQedTable(Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false), Tuple.of(RelType.fromString("VARCHAR", true), false), Tuple.of(RelType.fromString("INTEGER", true), false))); builder.addTable(table); return builder.scan(table.getName()).build(); } } - - /** - * Left projection with constants: SELECT emp_id, 'ACTIVE' as status, dept_id - */ public static record LeftProjectionWithConstants(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - builder.project( - builder.field(0), // emp_id - builder.alias(builder.literal("ACTIVE"), "status"), // constant: 'ACTIVE' - builder.field(2) // dept_id - ); + builder.project(builder.field(0), builder.alias(builder.literal("ACTIVE"), "status"), builder.field(2)); return builder.build(); } } - - /** - * Right projection with SAME constants: SELECT emp_id, 'ACTIVE' as status, dept_id - */ + + public static record RightProjectionWithConstants(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - builder.project( - builder.field(0), // emp_id - builder.alias(builder.literal("ACTIVE"), "status"), // same constant: 'ACTIVE' - builder.field(2) // dept_id - ); + builder.project(builder.field(0), builder.alias(builder.literal("ACTIVE"), "status"), builder.field(2)); return builder.build(); } } - - /** - * Union with constant columns (before optimization) - */ + public static record UnionWithConstantColumns(RelRN left, RelRN right) implements RelRN { @Override public RelNode semantics() { @@ -137,55 +85,36 @@ public RelNode semantics() { builder.push(left.semantics()); builder.push(right.semantics()); - builder.union(true, 2); // UNION ALL + builder.union(true, 2); return builder.build(); } } - - /** - * Left projection with constants removed: SELECT emp_id, dept_id - */ + public static record LeftProjectionNonConstants(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Project only non-constant columns - builder.project( - builder.field(0), // emp_id - builder.field(2) // dept_id - // status constant removed - ); + + builder.project(builder.field(0), builder.field(2)); return builder.build(); } } - - /** - * Right projection with constants removed: SELECT emp_id, dept_id - */ + public static record RightProjectionNonConstants(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Project only non-constant columns - builder.project( - builder.field(0), // emp_id - builder.field(2) // dept_id - // status constant removed - ); + + builder.project(builder.field(0), builder.field(2)); return builder.build(); } } - - /** - * Union of reduced columns (constants removed) - */ + public static record UnionReducedColumns(RelRN left, RelRN right) implements RelRN { @Override public RelNode semantics() { @@ -194,27 +123,19 @@ public RelNode semantics() { builder.push(left.semantics()); builder.push(right.semantics()); - builder.union(true, 2); // UNION ALL on reduced columns + builder.union(true, 2); return builder.build(); } } - - /** - * Top projection that adds back the constants: SELECT emp_id, 'ACTIVE' as status, dept_id - */ + public static record TopProjectionWithConstants(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Add back the constant in the final projection - builder.project( - builder.field(0), // emp_id (from union) - builder.alias(builder.literal("ACTIVE"), "status"), // constant added back - builder.field(1) // dept_id (from union) - ); + + builder.project(builder.field(0), builder.alias(builder.literal("ACTIVE"), "status"), builder.field(1)); return builder.build(); } diff --git a/src/main/java/org/qed/Generated/RRuleInstances/UnionToDistinct.java b/src/main/java/org/qed/Generated/RRuleInstances/UnionToDistinct.java index e45c069..b7e1bf0 100644 --- a/src/main/java/org/qed/Generated/RRuleInstances/UnionToDistinct.java +++ b/src/main/java/org/qed/Generated/RRuleInstances/UnionToDistinct.java @@ -8,123 +8,90 @@ import kala.collection.Seq; import kala.tuple.Tuple; -/** - * UnionToDistinctRule: Transforms UNION DISTINCT into UNION ALL + DISTINCT aggregate - * - * Pattern: Union(all=false, inputs...) => Aggregate(DISTINCT group by all fields)(Union(all=true, inputs...)) - * - * This optimization can be beneficial when the underlying system can handle - * UNION ALL more efficiently than UNION DISTINCT. - */ public record UnionToDistinct() implements RRule { - - // Base tables for the union + static final RelRN leftTable = new LeftSourceTable(); static final RelRN rightTable = new RightSourceTable(); - + @Override public RelRN before() { - // UNION DISTINCT (all=false) return new DistinctUnion(leftTable, rightTable); } - + @Override public RelRN after() { - // UNION ALL + DISTINCT aggregate var unionAll = new UnionAll(leftTable, rightTable); return new DistinctAggregate(unionAll); } - /** - * Left source table - */ public static record LeftSourceTable() implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - + var table = builder.createQedTable(Seq.of( - Tuple.of(RelType.fromString("INTEGER", true), false), // col0 - Tuple.of(RelType.fromString("VARCHAR", true), false) // col1 + Tuple.of(RelType.fromString("INTEGER", true), false), + Tuple.of(RelType.fromString("VARCHAR", true), false) )); - + builder.addTable(table); return builder.scan(table.getName()).build(); } } - - /** - * Right source table (same schema as left for UNION compatibility) - */ + public static record RightSourceTable() implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - + var table = builder.createQedTable(Seq.of( - Tuple.of(RelType.fromString("INTEGER", true), false), // col0 - Tuple.of(RelType.fromString("VARCHAR", true), false) // col1 + Tuple.of(RelType.fromString("INTEGER", true), false), + Tuple.of(RelType.fromString("VARCHAR", true), false) )); - + builder.addTable(table); return builder.scan(table.getName()).build(); } } - - /** - * UNION DISTINCT operation (all=false) - */ + public static record DistinctUnion(RelRN left, RelRN right) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - - // Push both inputs + builder.push(left.semantics()); builder.push(right.semantics()); - - // Create UNION with all=false (DISTINCT) + builder.union(false, 2); - + return builder.build(); } } - - /** - * UNION ALL operation (all=true) - */ + public static record UnionAll(RelRN left, RelRN right) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); - - // Push both inputs + builder.push(left.semantics()); builder.push(right.semantics()); - - // Create UNION with all=true (ALL) + builder.union(true, 2); - + return builder.build(); } } - - /** - * DISTINCT aggregate - groups by all fields to eliminate duplicates - */ + public static record DistinctAggregate(RelRN input) implements RelRN { @Override public RelNode semantics() { var builder = RuleBuilder.create(); builder.push(input.semantics()); - - // Group by all fields (creates DISTINCT effect) - // For a 2-column table, group by field 0 and field 1 + var groupKey = builder.groupKey(builder.field(0), builder.field(1)); - - // No aggregate functions needed - just grouping creates DISTINCT + builder.aggregate(groupKey); - + return builder.build(); } }