diff --git a/rules/JoinCommute-.json b/rules/JoinCommute-.json new file mode 100644 index 0000000..b6bdec7 --- /dev/null +++ b/rules/JoinCommute-.json @@ -0,0 +1,72 @@ +{ + "help" : [ "LogicalJoin(condition=[pred($0, $1)], joinType=[inner])\n LogicalTableScan(table=[[Left]])\n LogicalTableScan(table=[[Right]])\n", "LogicalProject(col-Left=[$1], col-Right=[$0])\n LogicalJoin(condition=[pred($1, $0)], joinType=[inner])\n LogicalTableScan(table=[[Right]])\n LogicalTableScan(table=[[Left]])\n" ], + "schemas" : [ { + "types" : [ "INTEGER" ], + "nullable" : [ true ], + "name" : "Left", + "guaranteed" : [ ], + "fields" : [ "col-Left" ], + "key" : [ ] + }, { + "types" : [ "INTEGER" ], + "nullable" : [ true ], + "name" : "Right", + "guaranteed" : [ ], + "fields" : [ "col-Right" ], + "key" : [ ] + } ], + "queries" : [ { + "join" : { + "condition" : { + "type" : "BOOLEAN", + "operand" : [ { + "column" : 0, + "type" : "INTEGER" + }, { + "column" : 1, + "type" : "INTEGER" + } ], + "operator" : "pred" + }, + "left" : { + "scan" : 0 + }, + "kind" : "INNER", + "right" : { + "scan" : 1 + } + } + }, { + "project" : { + "source" : { + "join" : { + "condition" : { + "type" : "BOOLEAN", + "operand" : [ { + "column" : 1, + "type" : "INTEGER" + }, { + "column" : 0, + "type" : "INTEGER" + } ], + "operator" : "pred" + }, + "left" : { + "scan" : 1 + }, + "kind" : "INNER", + "right" : { + "scan" : 0 + } + } + }, + "target" : [ { + "column" : 1, + "type" : "INTEGER" + }, { + "column" : 0, + "type" : "INTEGER" + } ] + } + } ] +} \ No newline at end of file diff --git a/src/main/java/org/qed/CodeGenerator.java b/src/main/java/org/qed/CodeGenerator.java index bf7da08..7b5009e 100644 --- a/src/main/java/org/qed/CodeGenerator.java +++ b/src/main/java/org/qed/CodeGenerator.java @@ -28,6 +28,7 @@ default E onMatch(E env, RelRN pattern) { case RelRN.Intersect intersect -> onMatchIntersect(env, intersect); case RelRN.Minus minus -> onMatchMinus(env, minus); case RelRN.Empty empty -> onMatchEmpty(env, empty); + case RelRN.Aggregate aggregate -> onMatchAggregate(env, aggregate); default -> onMatchCustom(env, pattern); }; } @@ -65,6 +66,7 @@ default E transform(E env, RelRN target) { case RelRN.Intersect intersect -> transformIntersect(env, intersect); case RelRN.Minus minus -> transformMinus(env, minus); case RelRN.Empty empty -> transformEmpty(env, empty); + case RelRN.Aggregate aggregate -> transformAggregate(env, aggregate); default -> transformCustom(env, target); }; } @@ -250,4 +252,12 @@ default E transformFalse(E env, RexRN literal) { default E transformEmpty(E env, RelRN.Empty empty) { return unimplementedTransform(env, empty); } + + default E onMatchAggregate(E env, RelRN.Aggregate aggregate) { + return unimplementedOnMatch(env, aggregate); + } + + default E transformAggregate(E env, RelRN.Aggregate aggregate) { + return unimplementedTransform(env, aggregate); + } } diff --git a/src/main/java/org/qed/Generated/PruneEmptyMinus.java b/src/main/java/org/qed/Generated/PruneEmptyMinus.java new file mode 100644 index 0000000..41bffd1 --- /dev/null +++ b/src/main/java/org/qed/Generated/PruneEmptyMinus.java @@ -0,0 +1,39 @@ +package org.qed.Generated; + +import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.plan.RelRule; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.logical.*; + +public class PruneEmptyMinus extends RelRule { + protected PruneEmptyMinus(Config config) { + super(config); + } + + @Override + public void onMatch(RelOptRuleCall call) { + var var_3 = call.builder(); + call.transformTo(var_3.empty().build()); + } + + public interface Config extends EmptyConfig { + Config DEFAULT = new Config() {}; + + @Override + default PruneEmptyMinus toRule() { + return new PruneEmptyMinus(this); + } + + @Override + default String description() { + return "PruneEmptyMinus"; + } + + @Override + default RelRule.OperandTransform operandSupplier() { + return s_2 -> s_2.operand(LogicalMinus.class).inputs(s_0 -> s_0.operand(LogicalValues.class).noInputs(), s_1 -> s_1.operand(RelNode.class).anyInputs()); + } + + } +} diff --git a/src/main/java/org/qed/Generated/PruneEmptyUnion.java b/src/main/java/org/qed/Generated/PruneEmptyUnion.java new file mode 100644 index 0000000..df48a7d --- /dev/null +++ b/src/main/java/org/qed/Generated/PruneEmptyUnion.java @@ -0,0 +1,39 @@ +package org.qed.Generated; + +import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.plan.RelRule; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.logical.*; + +public class PruneEmptyUnion extends RelRule { + protected PruneEmptyUnion(Config config) { + super(config); + } + + @Override + public void onMatch(RelOptRuleCall call) { + var var_3 = call.builder(); + call.transformTo(var_3.empty().build()); + } + + public interface Config extends EmptyConfig { + Config DEFAULT = new Config() {}; + + @Override + default PruneEmptyUnion toRule() { + return new PruneEmptyUnion(this); + } + + @Override + default String description() { + return "PruneEmptyUnion"; + } + + @Override + default RelRule.OperandTransform operandSupplier() { + return s_2 -> s_2.operand(LogicalUnion.class).inputs(s_0 -> s_0.operand(LogicalValues.class).noInputs(), s_1 -> s_1.operand(LogicalValues.class).noInputs()); + } + + } +} diff --git a/src/main/java/org/qed/Generated/PruneZeroRowsTable.java b/src/main/java/org/qed/Generated/PruneZeroRowsTable.java new file mode 100644 index 0000000..af02cf5 --- /dev/null +++ b/src/main/java/org/qed/Generated/PruneZeroRowsTable.java @@ -0,0 +1,39 @@ +package org.qed.Generated; + +import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.plan.RelRule; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.logical.*; + +public class PruneZeroRowsTable extends RelRule { + protected PruneZeroRowsTable(Config config) { + super(config); + } + + @Override + public void onMatch(RelOptRuleCall call) { + var var_1 = call.builder(); + call.transformTo(var_1.push(call.rel(0)).build()); + } + + public interface Config extends EmptyConfig { + Config DEFAULT = new Config() {}; + + @Override + default PruneZeroRowsTable toRule() { + return new PruneZeroRowsTable(this); + } + + @Override + default String description() { + return "PruneZeroRowsTable"; + } + + @Override + default RelRule.OperandTransform operandSupplier() { + return s_0 -> s_0.operand(RelNode.class).anyInputs(); + } + + } +} diff --git a/src/main/java/org/qed/Generated/RRuleInstances/PruneEmptyMinus.java b/src/main/java/org/qed/Generated/RRuleInstances/PruneEmptyMinus.java new file mode 100644 index 0000000..09c424c --- /dev/null +++ b/src/main/java/org/qed/Generated/RRuleInstances/PruneEmptyMinus.java @@ -0,0 +1,20 @@ +package org.qed.Generated.RRuleInstances; + +import org.qed.RRule; +import org.qed.RelRN; +import org.qed.RexRN; + +public record PruneEmptyMinus() implements RRule { + static final RelRN a = RelRN.scan("A", "Common_Type"); + static final RelRN b = RelRN.scan("B", "Common_Type"); + + @Override + public RelRN before() { + return a.empty().minus(false, b); + } + + @Override + public RelRN after() { + return a.empty(); + } +} diff --git a/src/main/java/org/qed/Generated/RRuleInstances/PruneEmptyUnion.java b/src/main/java/org/qed/Generated/RRuleInstances/PruneEmptyUnion.java new file mode 100644 index 0000000..74d3fc5 --- /dev/null +++ b/src/main/java/org/qed/Generated/RRuleInstances/PruneEmptyUnion.java @@ -0,0 +1,20 @@ +package org.qed.Generated.RRuleInstances; + +import org.qed.RRule; +import org.qed.RelRN; +import org.qed.RexRN; + +public record PruneEmptyUnion() implements RRule { + static final RelRN a = RelRN.scan("A", "Common_Type"); + static final RelRN b = RelRN.scan("B", "Common_Type"); + + @Override + public RelRN before() { + return a.empty().union(false, b.empty()); + } + + @Override + public RelRN after() { + return a.empty(); + } +} diff --git a/src/main/java/org/qed/Generated/RRuleInstances/PruneZeroRowsTable.java b/src/main/java/org/qed/Generated/RRuleInstances/PruneZeroRowsTable.java new file mode 100644 index 0000000..393b226 --- /dev/null +++ b/src/main/java/org/qed/Generated/RRuleInstances/PruneZeroRowsTable.java @@ -0,0 +1,19 @@ +package org.qed.Generated.RRuleInstances; + +import org.qed.RRule; +import org.qed.RelRN; +import org.qed.RexRN; + +public record PruneZeroRowsTable() implements RRule { + static final RelRN a = RelRN.scan("A", "Common_Type"); + + @Override + public RelRN before() { + return a; + } + + @Override + public RelRN after() { + return a; + } +} diff --git a/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyFilterTest.java b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyFilterTest.java new file mode 100644 index 0000000..dde4f6e --- /dev/null +++ b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyFilterTest.java @@ -0,0 +1,46 @@ +package org.qed.Generated.Tests; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import org.qed.Generated.CalciteTester; +import org.qed.RelType; +import org.qed.RuleBuilder; + +public class PruneEmptyFilterTest { + + public static void runTest() { + var tester = new CalciteTester(); + var builder = RuleBuilder.create(); + + var table = builder.createQedTable( + Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false)) + ); + builder.addTable(table); + + var before = builder + .scan(table.getName()) + .filter( + builder.call( + builder.genericPredicateOp("filter_cond", true), + builder.fields() + ) + ) + .empty() + .build(); + + var after = builder + .scan(table.getName()) + .empty() + .build(); + + var runner = CalciteTester.loadRule( + org.qed.Generated.PruneEmptyFilter.Config.DEFAULT.toRule() + ); + tester.verify(runner, before, after); + } + + public static void main(String[] args) { + System.out.println("Running PruneEmptyFilter test..."); + runTest(); + } +} diff --git a/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyMinusTest.java b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyMinusTest.java new file mode 100644 index 0000000..d24d6ad --- /dev/null +++ b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyMinusTest.java @@ -0,0 +1,51 @@ +package org.qed.Generated.Tests; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import org.qed.Generated.CalciteTester; +import org.qed.RelType; +import org.qed.RuleBuilder; +import org.apache.calcite.rel.RelNode; + +public class PruneEmptyMinusTest { + + public static void runTest() { + var tester = new CalciteTester(); + var builder = RuleBuilder.create(); + + var table = builder.createQedTable( + Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false)) + ); + builder.addTable(table); + + RelNode scanA = builder + .scan(table.getName()) + .build(); + + RelNode scanB = builder + .scan(table.getName()) + .build(); + + RelNode before = builder + .push(scanA) + .push(scanB) + .minus(false) + .empty() + .build(); + + RelNode after = builder + .push(scanA) + .empty() + .build(); + +// var runner = CalciteTester.loadRule( +// org.qed.Generated.PruneEmptyMinus.Config.DEFAULT.toRule() +// ); +// tester.verify(runner, before, after); + } + + public static void main(String[] args) { + System.out.println("Running PruneEmptyMinus test..."); + runTest(); + } +} diff --git a/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyProjectTest.java b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyProjectTest.java new file mode 100644 index 0000000..547da2c --- /dev/null +++ b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyProjectTest.java @@ -0,0 +1,41 @@ +package org.qed.Generated.Tests; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import org.qed.Generated.CalciteTester; +import org.qed.RelType; +import org.qed.RuleBuilder; + +public class PruneEmptyProjectTest { + + public static void runTest() { + var tester = new CalciteTester(); + var builder = RuleBuilder.create(); + + var table = builder.createQedTable( + Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false)) + ); + builder.addTable(table); + + var before = builder + .scan(table.getName()) + .empty() + .project(builder.field(0)) + .build(); + + var after = builder + .scan(table.getName()) + .empty() + .build(); + + var runner = CalciteTester.loadRule( + org.qed.Generated.PruneEmptyProject.Config.DEFAULT.toRule() + ); + tester.verify(runner, before, after); + } + + public static void main(String[] args) { + System.out.println("Running PruneEmptyProject test..."); + runTest(); + } +} diff --git a/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyUnionTest.java b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyUnionTest.java new file mode 100644 index 0000000..dcbbc9d --- /dev/null +++ b/src/main/java/org/qed/Generated/Tests-Trivial/PruneEmptyUnionTest.java @@ -0,0 +1,62 @@ +package org.qed.Generated.Tests; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import org.qed.Generated.CalciteTester; +import org.qed.RelType; +import org.qed.RuleBuilder; +import org.apache.calcite.rel.RelNode; + +public class PruneEmptyUnionTest { + + public static void runTest() { + var tester = new CalciteTester(); + var builder = RuleBuilder.create(); + + var table = builder.createQedTable( + Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false)) + ); + builder.addTable(table); + + RelNode scanA = builder + .scan(table.getName()) + .build(); + + RelNode emptyA = builder + .push(scanA) + .empty() + .build(); + + RelNode scanB = builder + .scan(table.getName()) + .build(); + + RelNode emptyB = builder + .push(scanB) + .empty() + .build(); + + RelNode before = builder + .push(scanA) + .push(scanB) + .union(false) + .empty() + .build(); + + RelNode after = builder + .push(emptyA) + .push(emptyB) + .union(false) + .build(); + + var runner = CalciteTester.loadRule( + org.qed.Generated.PruneEmptyUnion.Config.DEFAULT.toRule() + ); + tester.verify(runner, before, after); + } + + public static void main(String[] args) { + System.out.println("Running PruneEmptyMinus test..."); + runTest(); + } +} diff --git a/src/main/java/org/qed/Generated/Tests-Trivial/PruneZeroRowsTableTest.java b/src/main/java/org/qed/Generated/Tests-Trivial/PruneZeroRowsTableTest.java new file mode 100644 index 0000000..b7a91ed --- /dev/null +++ b/src/main/java/org/qed/Generated/Tests-Trivial/PruneZeroRowsTableTest.java @@ -0,0 +1,40 @@ +package org.qed.Generated.Tests; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import org.qed.Generated.CalciteTester; +import org.qed.RelType; +import org.qed.RuleBuilder; + +public class PruneZeroRowsTableTest { + + public static void runTest() { + var tester = new CalciteTester(); + var builder = RuleBuilder.create(); + + var table = builder.createQedTable( + Seq.of(Tuple.of(RelType.fromString("INTEGER", true), false)) + ); + builder.addTable(table); + + var before = builder + .scan(table.getName()) + .empty() + .build(); + + var after = builder + .scan(table.getName()) + .empty() + .build(); + +// var runner = CalciteTester.loadRule( +// org.qed.Generated.PruneZeroRowsTable.Config.DEFAULT.toRule() +// ); +// tester.verify(runner, before, after); + } + + public static void main(String[] args) { + System.out.println("Running PruneZeroRowsTableTest..."); + runTest(); + } +} diff --git a/src/main/java/org/qed/RelRN.java b/src/main/java/org/qed/RelRN.java index becc662..638985b 100644 --- a/src/main/java/org/qed/RelRN.java +++ b/src/main/java/org/qed/RelRN.java @@ -112,6 +112,10 @@ default Empty empty() { return new Empty(this); } + default Aggregate aggregate(Seq groupSet, Seq aggCalls) { + return new Aggregate(this, groupSet, aggCalls); + } + record Scan(String name, RelType.VarType ty, boolean unique) implements RelRN { @Override @@ -200,4 +204,21 @@ public RelNode semantics() { } } -} + record AggCall(String name, boolean distinct, RelType type, Seq operands) { + } + + record Aggregate(RelRN source, Seq groupSet, Seq aggCalls) implements RelRN { + @Override + public RelNode semantics() { + var builder = RuleBuilder.create(); + builder.push(source.semantics()); + var groupKey = builder.groupKey(groupSet.map(RexRN::semantics)); + var calls = aggCalls.map(agg -> { + var aggFunc = builder.genericAggregateOp(agg.name(), agg.type()); + return builder.aggregateCall(aggFunc, agg.distinct(), null, agg.name(), agg.operands().map(RexRN::semantics).asJava()); + }); + return builder.aggregate(groupKey, calls).build(); + } + } + +} \ No newline at end of file