diff --git a/src/main/java/org/qed/Generated/MySQL/FilterMerge.sql b/src/main/java/org/qed/Generated/MySQL/FilterMerge.sql new file mode 100644 index 0000000..d26fd20 --- /dev/null +++ b/src/main/java/org/qed/Generated/MySQL/FilterMerge.sql @@ -0,0 +1,5 @@ +INSERT INTO query_rewrite.rewrite_rules + (pattern, replacement) VALUES( + 'SELECT * FROM (SELECT * FROM Source WHERE ? = ?) AS t0 WHERE ? = ?', + 'SELECT * FROM Source WHERE ? = ? AND ? = ?' +); \ No newline at end of file diff --git a/src/main/java/org/qed/Generated/MySQL/ProjectMerge.sql b/src/main/java/org/qed/Generated/MySQL/ProjectMerge.sql new file mode 100644 index 0000000..c2f7fad --- /dev/null +++ b/src/main/java/org/qed/Generated/MySQL/ProjectMerge.sql @@ -0,0 +1,5 @@ +INSERT INTO query_rewrite.rewrite_rules + (pattern, replacement) VALUES( + 'SELECT ? FROM (SELECT ? FROM (SELECT * FROM Source) AS t0) AS t1', + 'SELECT ?, ? FROM Source' +); \ No newline at end of file diff --git a/src/main/java/org/qed/Generated/MySQLGenerator.java b/src/main/java/org/qed/Generated/MySQLGenerator.java new file mode 100644 index 0000000..de04203 --- /dev/null +++ b/src/main/java/org/qed/Generated/MySQLGenerator.java @@ -0,0 +1,106 @@ +package org.qed.Generated; + +import org.qed.RelRN; +import org.qed.RexRN; + +import java.util.ArrayList; +import java.util.List; + +public class MySQLGenerator { + + private int subqueryCounter = 0; + + private static class FlattenedSQLParts { + String fromClause = ""; + List projections = new ArrayList<>(); + List conditions = new ArrayList<>(); + } + + public String translate(String name, RelRN before, RelRN after) { + subqueryCounter = 0; + + String beforeSQL = transformNested(before, true); + + String afterSQL = transformFlatten(after); + + return "INSERT INTO query_rewrite.rewrite_rules\n" + + "\t(pattern, replacement) VALUES(\n" + + "\t'" + beforeSQL + "',\n" + + "\t'" + afterSQL + "'\n" + + ");"; + } + + public String transformNested(RelRN node, boolean isRoot) { + return switch (node) { + case RelRN.Scan scan -> "SELECT * FROM " + scan.name(); + case RelRN.Project project -> { + String innerSQL = transformNested(project.source(), false); + String alias = "t" + (subqueryCounter++); + yield "SELECT ? FROM (" + innerSQL + ") AS " + alias; + } + case RelRN.Filter filter -> { + String innerSQL = transformNested(filter.source(), false); + String condSQL = "? = ?"; + if (isRoot) { + yield innerSQL + " WHERE " + condSQL; + } else { + String alias = "t" + (subqueryCounter++); + yield "SELECT * FROM (" + innerSQL + " WHERE " + condSQL + ") AS " + alias; + } + } + default -> throw new UnsupportedOperationException("Unsupported RelRN: " + node); + }; + } + + public String transformFlatten(RelRN node) { + FlattenedSQLParts parts = new FlattenedSQLParts(); + collectFlattenedParts(node, parts); + + String selectClause = parts.projections.isEmpty() + ? "SELECT *" + : "SELECT " + String.join(", ", parts.projections); + + String whereClause = parts.conditions.isEmpty() + ? "" + : " WHERE " + String.join(" AND ", parts.conditions); + + return selectClause + " FROM " + parts.fromClause + whereClause; + } + + private void collectFlattenedParts(RelRN node, FlattenedSQLParts parts) { + switch (node) { + case RelRN.Scan scan -> parts.fromClause = scan.name(); + case RelRN.Project project -> { + collectFlattenedParts(project.source(), parts); + addPlaceholdersForRex(project.map(), parts.projections); + } + case RelRN.Filter filter -> { + collectFlattenedParts(filter.source(), parts); + collectPredConditions(filter.cond(), parts.conditions); + } + default -> throw new UnsupportedOperationException("Unsupported RelRN: " + node); + } + } + + private void addPlaceholdersForRex(RexRN rex, List projections) { + if (rex instanceof RexRN.Proj proj) { + projections.add("?"); + for (RexRN source : proj.sources()) { + addPlaceholdersForRex(source, projections); + } + } + } + + private void collectPredConditions(RexRN pred, List conditions) { + switch (pred) { + case RexRN.Pred p -> conditions.add("? = ?"); + case RexRN.And and -> { + for (RexRN child : and.sources()) { + collectPredConditions(child, conditions); + } + } + default -> throw new UnsupportedOperationException("Unsupported RexRN: " + pred); + } + } +} + diff --git a/src/main/java/org/qed/Generated/MySQLTester.java b/src/main/java/org/qed/Generated/MySQLTester.java new file mode 100644 index 0000000..01f29d3 --- /dev/null +++ b/src/main/java/org/qed/Generated/MySQLTester.java @@ -0,0 +1,33 @@ +package org.qed.Generated; + +import kala.collection.Seq; +import kala.tuple.Tuple; +import kala.tuple.Tuple2; +import org.qed.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class MySQLTester { + + public static String genPath = "src/main/java/org/qed/Generated/MySQL"; + + public static void main(String[] args) { + var rule = new org.qed.Generated.RRuleInstances.FilterMerge(); + new MySQLTester().serialize(rule, genPath); + } + + public void serialize(RRule rule, String path) { + var generator = new MySQLGenerator(); + var codeGen = generator.translate(rule.name(), rule.before(), rule.after()); + try { + Files.createDirectories(Path.of(path)); + Files.write(Path.of(path, rule.name() + ".sql"), codeGen.getBytes()); + } catch (IOException ioe) { + System.err.println(ioe.getMessage()); + } + } + +}