From 3aacd37208fa1a8f7890d53e7e73061f2ea71d41 Mon Sep 17 00:00:00 2001 From: wkaiz Date: Thu, 31 Jul 2025 22:19:46 -0700 Subject: [PATCH 1/5] AggJoinRmv and AggJoinJoinRmv --- .../AggregateJoinJoinRemove.java | 40 +++++++++++++++++++ .../RRuleInstances/AggregateJoinRemove.java | 32 +++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinJoinRemove.java create mode 100644 src/main/java/org/qed/Generated/RRuleInstances/AggregateJoinRemove.java 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 From 804b5bbacfac4fdd66a95c2ab82cd1c41025dff8 Mon Sep 17 00:00:00 2001 From: wkaiz Date: Thu, 31 Jul 2025 22:29:17 -0700 Subject: [PATCH 2/5] Removing Comments for Simplicity --- .../AggregateProjectConstantToDummyJoin.java | 118 +++-------------- .../RRuleInstances/AggregateProjectMerge.java | 34 +---- .../RRuleInstances/ProjectAggregateMerge.java | 68 ++-------- .../RRuleInstances/UnionPullUpConstants.java | 121 +++--------------- .../RRuleInstances/UnionToDistinct.java | 81 ++++-------- 5 files changed, 78 insertions(+), 344 deletions(-) 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/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(); } } From 44fb6c4a61d898b8414faaadeeab46a9a7e09502 Mon Sep 17 00:00:00 2001 From: wkaiz Date: Sun, 17 Aug 2025 15:03:08 -0700 Subject: [PATCH 3/5] Adding ReduceExpression Rules for Joins --- .../RRuleInstances/JoinReduceFalse.java | 19 ++++++++++++++++++ .../RRuleInstances/JoinReduceTrue.java | 20 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java create mode 100644 src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java 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); + } +} From 39c82e488dd28f32af6d8dbb0a1593495d7e2e87 Mon Sep 17 00:00:00 2001 From: wkaiz Date: Fri, 22 Aug 2025 22:09:18 -0700 Subject: [PATCH 4/5] Typo, Test Cases for JoinReduce --- pom.xml | 4 +- rules/FilterProjectTranspose-.json | 68 +++++++++++++++++++ .../org/qed/Generated/JoinReduceFalse.java | 41 +++++++++++ .../org/qed/Generated/JoinReduceTrue.java | 41 +++++++++++ .../RRuleInstances/JoinReduceFalse.java | 3 +- .../RRuleInstances/JoinReduceTrue.java | 3 +- .../Generated/Tests/JoinReduceFalseTest.java | 43 ++++++++++++ .../Generated/Tests/JoinReduceTrueTest.java | 43 ++++++++++++ 8 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 rules/FilterProjectTranspose-.json create mode 100644 src/main/java/org/qed/Generated/JoinReduceFalse.java create mode 100644 src/main/java/org/qed/Generated/JoinReduceTrue.java create mode 100644 src/main/java/org/qed/Generated/Tests/JoinReduceFalseTest.java create mode 100644 src/main/java/org/qed/Generated/Tests/JoinReduceTrueTest.java diff --git a/pom.xml b/pom.xml index fd29e81..9bf55a1 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,8 @@ org.apache.maven.plugins maven-compiler-plugin - 23 - 23 + 24 + 24 --enable-preview diff --git a/rules/FilterProjectTranspose-.json b/rules/FilterProjectTranspose-.json new file mode 100644 index 0000000..c9a0d44 --- /dev/null +++ b/rules/FilterProjectTranspose-.json @@ -0,0 +1,68 @@ +{ + "help" : [ "LogicalFilter(condition=[pred($0)])\n LogicalProject($f0=[proj($0)])\n LogicalTableScan(table=[[Source]])\n", "LogicalProject($f0=[proj($0)])\n LogicalFilter(condition=[pred(proj($0))])\n LogicalTableScan(table=[[Source]])\n" ], + "schemas" : [ { + "types" : [ "INTEGER" ], + "nullable" : [ true ], + "name" : "Source", + "guaranteed" : [ ], + "fields" : [ "col-Source" ], + "key" : [ ] + } ], + "queries" : [ { + "filter" : { + "condition" : { + "type" : "BOOLEAN", + "operand" : [ { + "column" : 0, + "type" : "INTEGER" + } ], + "operator" : "pred" + }, + "source" : { + "project" : { + "source" : { + "scan" : 0 + }, + "target" : [ { + "type" : "INTEGER", + "operand" : [ { + "column" : 0, + "type" : "INTEGER" + } ], + "operator" : "proj" + } ] + } + } + } + }, { + "project" : { + "source" : { + "filter" : { + "condition" : { + "type" : "BOOLEAN", + "operand" : [ { + "type" : "INTEGER", + "operand" : [ { + "column" : 0, + "type" : "INTEGER" + } ], + "operator" : "proj" + } ], + "operator" : "pred" + }, + "source" : { + "scan" : 0 + } + } + }, + "target" : [ { + "type" : "INTEGER", + "operand" : [ { + "column" : 0, + "type" : "INTEGER" + } ], + "operator" : "proj" + } ] + } + } ] +} \ No newline at end of file diff --git a/src/main/java/org/qed/Generated/JoinReduceFalse.java b/src/main/java/org/qed/Generated/JoinReduceFalse.java new file mode 100644 index 0000000..087d3f3 --- /dev/null +++ b/src/main/java/org/qed/Generated/JoinReduceFalse.java @@ -0,0 +1,41 @@ +package org.qed.Generated; + +import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.plan.RelRule; +import org.apache.calcite.plan.RelOptUtil; +import java.util.List; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.logical.*; + +public class JoinReduceFalse extends RelRule { + protected JoinReduceFalse(Config config) { + super(config); + } + + @Override + public void onMatch(RelOptRuleCall call) { + var var_5 = call.builder(); + call.transformTo(var_5.push(call.rel(1)).push(call.rel(2)).join(JoinRelType.LEFT, var_5.push(call.rel(1)).push(call.rel(2)).literal(false)).build()); + } + + public interface Config extends EmptyConfig { + Config DEFAULT = new Config() {}; + + @Override + default JoinReduceFalse toRule() { + return new JoinReduceFalse(this); + } + + @Override + default String description() { + return "JoinReduceFalse"; + } + + @Override + default RelRule.OperandTransform operandSupplier() { + return s_2 -> s_2.operand(LogicalJoin.class).inputs(s_0 -> s_0.operand(RelNode.class).anyInputs(), s_1 -> s_1.operand(RelNode.class).anyInputs()); + } + + } +} diff --git a/src/main/java/org/qed/Generated/JoinReduceTrue.java b/src/main/java/org/qed/Generated/JoinReduceTrue.java new file mode 100644 index 0000000..fc9e0a2 --- /dev/null +++ b/src/main/java/org/qed/Generated/JoinReduceTrue.java @@ -0,0 +1,41 @@ +package org.qed.Generated; + +import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.plan.RelRule; +import org.apache.calcite.plan.RelOptUtil; +import java.util.List; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.logical.*; + +public class JoinReduceTrue extends RelRule { + protected JoinReduceTrue(Config config) { + super(config); + } + + @Override + public void onMatch(RelOptRuleCall call) { + var var_5 = call.builder(); + call.transformTo(var_5.push(call.rel(1)).push(call.rel(2)).join(JoinRelType.LEFT, ((LogicalJoin) call.rel(0)).getCondition()).build()); + } + + public interface Config extends EmptyConfig { + Config DEFAULT = new Config() {}; + + @Override + default JoinReduceTrue toRule() { + return new JoinReduceTrue(this); + } + + @Override + default String description() { + return "JoinReduceTrue"; + } + + @Override + default RelRule.OperandTransform operandSupplier() { + return s_2 -> s_2.operand(LogicalJoin.class).inputs(s_0 -> s_0.operand(RelNode.class).anyInputs(), s_1 -> s_1.operand(RelNode.class).anyInputs()); + } + + } +} diff --git a/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java index 7098d7a..4b21855 100644 --- a/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java +++ b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceFalse.java @@ -1,10 +1,11 @@ package org.qed.Generated.RRuleInstances; import org.apache.calcite.rel.core.JoinRelType; +import org.qed.RRule; import org.qed.RelRN; import org.qed.RexRN; -public record JoinReduceFalse() { +public record JoinReduceFalse() implements RRule { 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()); diff --git a/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java index 2e26bfe..cb3d2bb 100644 --- a/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java +++ b/src/main/java/org/qed/Generated/RRuleInstances/JoinReduceTrue.java @@ -1,10 +1,11 @@ package org.qed.Generated.RRuleInstances; import org.apache.calcite.rel.core.JoinRelType; +import org.qed.RRule; import org.qed.RelRN; import org.qed.RexRN; -public record JoinReduceTrue() { +public record JoinReduceTrue() implements RRule { 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); diff --git a/src/main/java/org/qed/Generated/Tests/JoinReduceFalseTest.java b/src/main/java/org/qed/Generated/Tests/JoinReduceFalseTest.java new file mode 100644 index 0000000..95f238d --- /dev/null +++ b/src/main/java/org/qed/Generated/Tests/JoinReduceFalseTest.java @@ -0,0 +1,43 @@ +package org.qed.Generated.Tests; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.qed.Generated.CalciteTester; +import org.qed.RelType; +import org.qed.Generated.RRuleInstances.JoinReduceFalse; +import org.qed.RuleBuilder; + +public class JoinReduceFalseTest { + + public static void runTest() { + var tester = new CalciteTester(); + var builder = RuleBuilder.create(); + + var leftTable = builder.createQedTable(Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false))); + var rightTable = builder.createQedTable(Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false))); + builder.addTable(leftTable); + builder.addTable(rightTable); + + var before = builder.scan(leftTable.getName()) + .scan(rightTable.getName()) + .join(JoinRelType.INNER, builder.call(SqlStdOperatorTable.AND, + builder.call(builder.genericPredicateOp("join", true), builder.joinFields()), + builder.literal(false))) + .build(); + + var after = builder.scan(leftTable.getName()) + .scan(rightTable.getName()) + .join(JoinRelType.LEFT, builder.literal(false)) + .build(); + + var runner = CalciteTester.loadRule(org.qed.Generated.JoinReduceFalse.Config.DEFAULT.toRule()); + tester.verify(runner, before, after); + } + + public static void main(String[] args) { + System.out.println("Running JoinReduceFalse test..."); + runTest(); + } +} \ No newline at end of file diff --git a/src/main/java/org/qed/Generated/Tests/JoinReduceTrueTest.java b/src/main/java/org/qed/Generated/Tests/JoinReduceTrueTest.java new file mode 100644 index 0000000..1168ddf --- /dev/null +++ b/src/main/java/org/qed/Generated/Tests/JoinReduceTrueTest.java @@ -0,0 +1,43 @@ +package org.qed.Generated.Tests; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.qed.Generated.CalciteTester; +import org.qed.RelType; +import org.qed.Generated.RRuleInstances.JoinReduceTrue; +import org.qed.RuleBuilder; + +public class JoinReduceTrueTest { + + public static void runTest() { + var tester = new CalciteTester(); + var builder = RuleBuilder.create(); + + var leftTable = builder.createQedTable(Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false))); + var rightTable = builder.createQedTable(Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false))); + builder.addTable(leftTable); + builder.addTable(rightTable); + + var before = builder.scan(leftTable.getName()) + .scan(rightTable.getName()) + .join(JoinRelType.INNER, builder.call(SqlStdOperatorTable.AND, + builder.call(builder.genericPredicateOp("join", true), builder.joinFields()), + builder.literal(true))) + .build(); + + var after = builder.scan(leftTable.getName()) + .scan(rightTable.getName()) + .join(JoinRelType.LEFT, builder.call(builder.genericPredicateOp("join", true), builder.joinFields())) + .build(); + + var runner = CalciteTester.loadRule(org.qed.Generated.JoinReduceTrue.Config.DEFAULT.toRule()); + tester.verify(runner, before, after); + } + + public static void main(String[] args) { + System.out.println("Running JoinReduceTrue test..."); + runTest(); + } +} \ No newline at end of file From 63e83a613c1a67eed2dd1d13ba0d802e61ac3a6e Mon Sep 17 00:00:00 2001 From: wkaiz Date: Fri, 22 Aug 2025 22:11:26 -0700 Subject: [PATCH 5/5] reverting back java version --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9bf55a1..fd29e81 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,8 @@ org.apache.maven.plugins maven-compiler-plugin - 24 - 24 + 23 + 23 --enable-preview