diff --git a/pom.xml b/pom.xml index 3802e5f..d60c9a4 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,8 @@ org.apache.maven.plugins maven-compiler-plugin - 23 - 23 + 25 + 25 --enable-preview diff --git a/src/main/java/org/qed/Backends/Cockroach/CockroachGenerator.java b/src/main/java/org/qed/Backends/Cockroach/CockroachGenerator.java index 20fc7db..b873b09 100644 --- a/src/main/java/org/qed/Backends/Cockroach/CockroachGenerator.java +++ b/src/main/java/org/qed/Backends/Cockroach/CockroachGenerator.java @@ -27,11 +27,26 @@ public Env onMatchFilter(Env env, RelRN.Filter filter) { String sourcePattern = sourceEnv.current(); Env condEnv = onMatch(sourceEnv, filter.cond()); String condPattern; + + if (filter.source() instanceof RelRN.Empty) { + String inputVar = condEnv.generateVar("input"); + String filtersVar = condEnv.generateVar("filters"); + String pattern = "(Select\n $" + inputVar + ":* & (HasZeroRows $" + inputVar + ")\n $" + filtersVar + ":*\n)"; + return condEnv.addBinding("isPruneEmptyFilter", "true") + .addBinding("pruneEmptyInput", inputVar) + .setPattern(pattern).focus(pattern); + } + if (filter.cond() instanceof RexRN.True) { String pattern = "(Select\n " + sourcePattern + "\n []\n)"; return condEnv.setPattern(pattern).focus(pattern); } else if (filter.cond() instanceof RexRN.False) { - condPattern = condEnv.pattern(); + String onVar = condEnv.generateVar("on"); + Env onEnv = condEnv.addBinding("on", onVar); + String itemVar = onEnv.generateVar("item"); + Env itemEnv = onEnv.addBinding("item", itemVar); + String pattern = "(Select\n " + sourcePattern + "\n $" + onVar + ":[\n ...\n $" + itemVar + ":(FiltersItem (False))\n ...\n ]\n)"; + return itemEnv.setPattern(pattern).focus(pattern); } else { condPattern = condEnv.current(); } @@ -40,6 +55,17 @@ public Env onMatchFilter(Env env, RelRN.Filter filter) { } public Env onMatchProject(Env env, RelRN.Project project) { + if (project.source() instanceof RelRN.Empty) { + String inputVar = env.generateVar("input"); + Env inputEnv = env.addBinding("zeroInput", inputVar) + .addBinding("hasZeroRows", "true"); + String projectionsVar = inputEnv.generateVar("projections"); + Env projectionsEnv = inputEnv.addBinding("projections", projectionsVar); + String passthroughVar = projectionsEnv.generateVar("passthrough"); + Env passthroughEnv = projectionsEnv.addBinding("passthrough", passthroughVar); + String pattern = "(Project\n $" + inputVar + ":* & (HasZeroRows $" + inputVar + ")\n $" + projectionsVar + ":*\n $" + passthroughVar + ":*\n)"; + return passthroughEnv.setPattern(pattern).focus(pattern); + } if (env.rulename.equals("ProjectMerge") && project.source() instanceof RelRN.Project) { Env outerProjEnv = onMatch(env, project.map()); String outerProjPattern = outerProjEnv.current(); @@ -70,6 +96,76 @@ public Env onMatchProject(Env env, RelRN.Project project) { @Override public Env onMatchJoin(Env env, RelRN.Join join) { + if (join.cond() instanceof RexRN.And and) { + if (and.sources().size() == 2) { + boolean hasTrue = false; + boolean hasFalse = false; + RexRN otherCond = null; + + for (RexRN side : and.sources()) { + if (side instanceof RexRN.True) { + hasTrue = true; + } else if (side instanceof RexRN.False) { + hasFalse = true; + } else { + otherCond = side; + } + } + + if (hasTrue && otherCond != null) { + Env leftEnv = onMatch(env, join.left()); + String leftPattern = leftEnv.current(); + Env rightEnv = onMatch(leftEnv, join.right()); + String rightPattern = rightEnv.current(); + String onVar = rightEnv.generateVar("on"); + Env onEnv = rightEnv.addBinding("on", onVar); + String itemVar = onEnv.generateVar("item"); + Env itemEnv = onEnv.addBinding("item", itemVar); + String privateVar = itemEnv.generateVar("private"); + Env privateEnv = itemEnv.addBinding("private_" + System.identityHashCode(join), privateVar) + .addBinding("last_private", privateVar) + .addBinding("joinReduceTrue", "true"); + String joinType = getJoinType(join.ty().semantics()); + String pattern = "(" + joinType + "\n " + leftPattern + "\n " + rightPattern + "\n $" + onVar + ":[\n ...\n $" + itemVar + ":(FiltersItem (True))\n ...\n ]\n $" + privateVar + ":*\n)"; + return privateEnv.setPattern(pattern).focus(pattern); + } else if (hasFalse && otherCond != null) { + Env leftEnv = onMatch(env, join.left()); + String leftPattern = leftEnv.current(); + Env rightEnv = onMatch(leftEnv, join.right()); + String rightPattern = rightEnv.current(); + String onVar = rightEnv.generateVar("on"); + Env onEnv = rightEnv.addBinding("on", onVar); + String itemVar = onEnv.generateVar("item"); + Env itemEnv = onEnv.addBinding("item", itemVar); + String privateVar = itemEnv.generateVar("private"); + Env privateEnv = itemEnv.addBinding("private_" + System.identityHashCode(join), privateVar) + .addBinding("last_private", privateVar) + .addBinding("joinReduceFalse", "true"); + String joinType = getJoinType(join.ty().semantics()); + String pattern = "(" + joinType + "\n " + leftPattern + "\n " + rightPattern + "\n $" + onVar + ":[\n ...\n $" + itemVar + ":(FiltersItem (False))\n ...\n ]\n $" + privateVar + ":*\n)"; + return privateEnv.setPattern(pattern).focus(pattern); + } + } + if (join.ty().semantics() == org.apache.calcite.rel.core.JoinRelType.INNER + && and.sources().size() > 2) { + String leftVar = env.generateVar("left"); + Env leftEnv = env.addBinding("left", leftVar); + String rightVar = leftEnv.generateVar("right"); + Env rightEnv = leftEnv.addBinding("right", rightVar); + String onVar = rightEnv.generateVar("on"); + Env onEnv = rightEnv.addBinding("on", onVar); + String privateVar = onEnv.generateVar("private"); + Env privateEnv = onEnv.addBinding("private", privateVar); + String pattern = "(InnerJoin\n" + + " $" + leftVar + ":* & ^(HasOuterCols $" + leftVar + ")\n" + + " $" + rightVar + ":* & ^(HasOuterCols $" + rightVar + ")\n" + + " $" + onVar + ":*\n" + + " $" + privateVar + ":*\n" + + ")"; + return privateEnv.setPattern(pattern).focus(pattern); + } + } + Env leftEnv = onMatch(env, join.left()); String leftPattern = leftEnv.current(); Env rightEnv = onMatch(leftEnv, join.right()); @@ -86,6 +182,52 @@ public Env onMatchJoin(Env env, RelRN.Join join) { @Override public Env transformJoin(Env env, RelRN.Join join) { + if (env.bindings().containsKey("joinReduceTrue")) { + Env leftEnv = transform(env, join.left()); + String leftPattern = leftEnv.current(); + Env rightEnv = transform(leftEnv, join.right()); + String rightPattern = rightEnv.current(); + String onVar = rightEnv.bindings().get("on"); + String itemVar = rightEnv.bindings().get("item"); + String privateVar = rightEnv.bindings().getOrDefault("private_" + System.identityHashCode(join), + rightEnv.bindings().getOrDefault("last_private", "private")); + String joinType = getJoinType(join.ty().semantics()); + String pattern = "(" + joinType + "\n " + leftPattern + "\n " + rightPattern + "\n (RemoveFiltersItem $" + onVar + " $" + itemVar + ")\n $" + privateVar + "\n)"; + return rightEnv.setPattern(pattern).focus(pattern); + } else if (env.bindings().containsKey("joinReduceFalse")) { + Env leftEnv = transform(env, join.left()); + String leftPattern = leftEnv.current(); + Env rightEnv = transform(leftEnv, join.right()); + String rightPattern = rightEnv.current(); + String privateVar = rightEnv.bindings().getOrDefault("private_" + System.identityHashCode(join), + rightEnv.bindings().getOrDefault("last_private", "private")); + String joinType = getJoinType(join.ty().semantics()); + String pattern = "(" + joinType + "\n " + leftPattern + "\n " + rightPattern + "\n [ (FiltersItem (False)) ]\n $" + privateVar + "\n)"; + return rightEnv.setPattern(pattern).focus(pattern); + } + if (join.ty().semantics() == org.apache.calcite.rel.core.JoinRelType.INNER + && env.bindings().containsKey("left") + && env.bindings().containsKey("right") + && env.bindings().containsKey("on") + && env.bindings().containsKey("private") + && !env.bindings().containsKey("joinReduceTrue") + && !env.bindings().containsKey("joinReduceFalse")) { + String leftVar = env.bindings().get("left"); + String rightVar = env.bindings().get("right"); + String onVar = env.bindings().get("on"); + String privateVar = env.bindings().get("private"); + String pattern = "(InnerJoin\n" + + " (Select $" + leftVar + " (ExtractBoundConditions $" + onVar + " (OutputCols $" + leftVar + ")))\n" + + " (Select $" + rightVar + " (ExtractBoundConditions $" + onVar + " (OutputCols $" + rightVar + ")))\n" + + " (ExtractUnboundConditions\n" + + " (ExtractUnboundConditions $" + onVar + " (OutputCols $" + leftVar + "))\n" + + " (OutputCols $" + rightVar + ")\n" + + " )\n" + + " $" + privateVar + "\n" + + ")"; + return env.setPattern(pattern).focus(pattern); + } + Env leftEnv = transform(env, join.left()); String leftPattern = leftEnv.current(); Env rightEnv = transform(leftEnv, join.right()); @@ -105,6 +247,17 @@ public Env transformJoin(Env env, RelRN.Join join) { @Override public Env onMatchUnion(Env env, RelRN.Union union) { + if (union.sources().size() == 2) { + RelRN leftSource = union.sources().get(0); + RelRN rightSource = union.sources().get(1); + if (leftSource instanceof RelRN.Empty && rightSource instanceof RelRN.Empty) { + String leftVar = env.generateVar("left"); + String rightVar = env.generateVar("right"); + String unionType = union.all() ? "UnionAll" : "Union"; + String pattern = "(" + unionType + "\n $" + leftVar + ":* & (HasZeroRows $" + leftVar + ")\n $" + rightVar + ":* & (HasZeroRows $" + rightVar + ")\n)"; + return env.setPattern(pattern).focus(pattern); + } + } Env currentEnv = env; Seq sourcePatterns = Seq.empty(); for (RelRN source : union.sources()) { @@ -141,6 +294,20 @@ private String buildNestedUnion(String unionType, Seq sources, String pr @Override public Env onMatchIntersect(Env env, RelRN.Intersect intersect) { + if (intersect.sources().size() == 2) { + RelRN leftSource = intersect.sources().get(0); + RelRN rightSource = intersect.sources().get(1); + if (rightSource instanceof RelRN.Empty) { + String leftVar = env.generateVar("left"); + String rightVar = env.generateVar("right"); + String intersectType = intersect.all() ? "IntersectAll" : "Intersect"; + String pattern = "(" + intersectType + "\n $" + leftVar + ":*\n $" + rightVar + ":* & (HasZeroRows $" + rightVar + ")\n)"; + return env.addBinding("isPruneEmptyIntersect", "true") + .addBinding("pruneEmptyLeft", leftVar) + .setPattern(pattern).focus(pattern); + } + } + Env currentEnv = env; Seq sourcePatterns = Seq.empty(); for (RelRN source : intersect.sources()) { @@ -176,8 +343,171 @@ private String buildNestedIntersect(String intersectType, Seq sources, S return "(" + intersectType + "\n " + first + "\n " + nested + "\n $" + privatePattern + "\n)"; } + @Override + public Env onMatchMinus(Env env, RelRN.Minus minus) { + if (minus.sources().size() == 2 && minus.sources().get(0) instanceof RelRN.Minus inner) { + String leftVar = env.generateVar("left"); + Env leftEnv = env.addBinding("left", leftVar); + String rightBVar = leftEnv.generateVar("rightB"); + Env rightBEnv = leftEnv.addBinding("rightB", rightBVar); + String pInnerVar = rightBEnv.generateVar("pInner"); + Env pInnerEnv = rightBEnv.addBinding("pInner", pInnerVar); + String rightCVar = pInnerEnv.generateVar("rightC"); + Env rightCEnv = pInnerEnv.addBinding("rightC", rightCVar); + String pOuterVar = rightCEnv.generateVar("pOuter"); + Env pOuterEnv = rightCEnv.addBinding("pOuter", pOuterVar); + String pattern = "(Except\n" + + " (Except\n" + + " $" + leftVar + ":*\n" + + " $" + rightBVar + ":*\n" + + " $" + pInnerVar + ":*\n" + + " )\n" + + " $" + rightCVar + ":*\n" + + " $" + pOuterVar + ":*\n" + + ")"; + return pOuterEnv.setPattern(pattern).focus(pattern); + } + Env leftEnv = onMatch(env, minus.sources().get(0)); + String leftPattern = leftEnv.current(); + Env rightEnv = onMatch(leftEnv, minus.sources().get(1)); + String rightPattern = rightEnv.current(); + String privateVar = rightEnv.generateVar("private"); + Env privateEnv = rightEnv.addBinding("minus_private", privateVar); + String pattern = "(Except\n " + leftPattern + "\n " + rightPattern + "\n $" + privateVar + ":*\n)"; + return privateEnv.setPattern(pattern).focus(pattern); + } + @Override public Env onMatchAggregate(Env env, RelRN.Aggregate aggregate) { + if (aggregate.source() instanceof RelRN.Join topJoin + && topJoin.ty().semantics() == org.apache.calcite.rel.core.JoinRelType.LEFT + && topJoin.left() instanceof RelRN.Join bottomJoin + && bottomJoin.ty().semantics() == org.apache.calcite.rel.core.JoinRelType.LEFT) { + String topJoinVar = env.generateVar("topJoin"); + Env topEnv = env.addBinding("topJoin", topJoinVar); + String bottomJoinVar = topEnv.generateVar("bottomJoin"); + Env bottomEnv = topEnv.addBinding("bottomJoin", bottomJoinVar); + String leftVar = bottomEnv.generateVar("left"); + Env leftEnv = bottomEnv.addBinding("left", leftVar); + String middleVar = leftEnv.generateVar("middle"); + Env middleEnv = leftEnv.addBinding("middle", middleVar); + String rightVar = middleEnv.generateVar("right"); + Env rightEnv = middleEnv.addBinding("right", rightVar); + String topOnVar = rightEnv.generateVar("topOn"); + Env topOnEnv = rightEnv.addBinding("topOn", topOnVar); + String topPrivateVar = topOnEnv.generateVar("topPrivate"); + Env topPrivateEnv = topOnEnv.addBinding("topPrivate", topPrivateVar); + String aggregationsVar = topPrivateEnv.generateVar("aggregations"); + Env aggsEnv = topPrivateEnv.addBinding("aggregations", aggregationsVar); + String groupingPrivateVar = aggsEnv.generateVar("groupingPrivate"); + Env groupingPrivateEnv = aggsEnv.addBinding("groupingPrivate", groupingPrivateVar); + String groupingColsVar = groupingPrivateEnv.generateVar("groupingCols"); + Env groupingColsEnv = groupingPrivateEnv.addBinding("groupingCols", groupingColsVar); + String orderingVar = groupingColsEnv.generateVar("ordering"); + Env orderingEnv = groupingColsEnv.addBinding("ordering", orderingVar); + + String head = "DistinctOn"; + String pattern = "(" + head + "\n" + + " $" + topJoinVar + ":(LeftJoin\n" + + " $" + bottomJoinVar + ":(LeftJoin $" + leftVar + ":* $" + middleVar + ":* * *) &\n" + + " (JoinPreservesLeftRows $" + bottomJoinVar + ") &\n" + + " (JoinDoesNotDuplicateLeftRows $" + bottomJoinVar + ")\n" + + " $" + rightVar + ":*\n" + + " $" + topOnVar + ":*\n" + + " $" + topPrivateVar + ":*\n" + + " ) &\n" + + " (JoinPreservesLeftRows $" + topJoinVar + ") &\n" + + " (JoinDoesNotDuplicateLeftRows $" + topJoinVar + ")\n" + + " $" + aggregationsVar + ":[]\n" + + " $" + groupingPrivateVar + ":(GroupingPrivate $" + groupingColsVar + ":* $" + orderingVar + ":*) &\n" + + " (ColsAreSubset\n" + + " (UnionCols\n" + + " $" + groupingColsVar + "\n" + + " (AggregationOuterCols $" + aggregationsVar + ")\n" + + " )\n" + + " (UnionCols\n" + + " (OutputCols $" + leftVar + ")\n" + + " (OutputCols $" + rightVar + ")\n" + + " )\n" + + " ) &\n" + + " ^(ColsIntersect\n" + + " (UnionCols\n" + + " $" + groupingColsVar + "\n" + + " (AggregationOuterCols $" + aggregationsVar + ")\n" + + " )\n" + + " (OutputCols $" + middleVar + ")\n" + + " ) &\n" + + " (OrderingCanProjectCols\n" + + " $" + orderingVar + "\n" + + " (UnionCols\n" + + " (OutputCols $" + leftVar + ")\n" + + " (OutputCols $" + rightVar + ")\n" + + " )\n" + + " )\n" + + ")"; + return orderingEnv.setPattern(pattern).focus(pattern); + } + if (aggregate.source() instanceof RelRN.Join join && join.ty().semantics() == org.apache.calcite.rel.core.JoinRelType.LEFT) { + String inputVar = env.generateVar("input"); + Env inputEnv = env.addBinding("input", inputVar); + String leftVar = inputEnv.generateVar("left"); + Env leftEnv = inputEnv.addBinding("left", leftVar); + String aggsVar = leftEnv.generateVar("aggregations"); + Env aggsEnv = leftEnv.addBinding("aggregations", aggsVar); + String groupingPrivateVar = aggsEnv.generateVar("groupingPrivate"); + Env groupingPrivateEnv = aggsEnv.addBinding("groupingPrivate", groupingPrivateVar); + String groupingColsVar = groupingPrivateEnv.generateVar("groupingCols"); + Env groupingColsEnv = groupingPrivateEnv.addBinding("groupingCols", groupingColsVar); + String orderingVar = groupingColsEnv.generateVar("ordering"); + Env orderingEnv = groupingColsEnv.addBinding("ordering", orderingVar); + + String head = "DistinctOn"; + String matchPattern = "(" + head + "\n" + + " $" + inputVar + ":(LeftJoin $" + leftVar + ":* * * *) &\n" + + " (JoinPreservesLeftRows $" + inputVar + ") &\n" + + " (JoinDoesNotDuplicateLeftRows $" + inputVar + ")\n" + + " $" + aggsVar + ":[]\n" + + " $" + groupingPrivateVar + ":(GroupingPrivate $" + groupingColsVar + ":* $" + orderingVar + ":*) &\n" + + " (ColsAreSubset\n" + + " (UnionCols\n" + + " $" + groupingColsVar + "\n" + + " (AggregationOuterCols $" + aggsVar + ")\n" + + " )\n" + + " (OutputCols $" + leftVar + ")\n" + + " ) &\n" + + " (OrderingCanProjectCols\n" + + " $" + orderingVar + "\n" + + " (OutputCols $" + leftVar + ")\n" + + " )\n" + + ")"; + return orderingEnv.setPattern(matchPattern).focus(matchPattern); + } + if (aggregate.source() instanceof RelRN.Project project) { + String inputVar = env.generateVar("input"); + Env inputEnv = env.addBinding("input", inputVar); + String projectionsVar = inputEnv.generateVar("projections"); + Env projectionsEnv = inputEnv.addBinding("projections", projectionsVar); + String passthroughVar = projectionsEnv.generateVar("passthrough"); + Env passthroughEnv = projectionsEnv.addBinding("passthrough", passthroughVar); + + String aggregationsVar = passthroughEnv.generateVar("aggregations"); + Env aggregationsBindEnv = passthroughEnv.addBinding("aggregations", aggregationsVar); + String groupingPrivateVar = aggregationsBindEnv.generateVar("groupingPrivate"); + Env groupingPrivateBindEnv = aggregationsBindEnv.addBinding("groupingPrivate", groupingPrivateVar); + + String aggregateType = determineAggregateType(aggregate); + String pattern = "(" + aggregateType + "\n" + + " (Project\n" + + " $" + inputVar + ":*\n" + + " $" + projectionsVar + ":*\n" + + " $" + passthroughVar + ":*\n" + + " )\n" + + " $" + aggregationsVar + ":*\n" + + " $" + groupingPrivateVar + ":* & (CanRemapGroupingColsThroughProject $" + groupingPrivateVar + " $" + projectionsVar + " $" + passthroughVar + ")\n" + + ")"; + return groupingPrivateBindEnv.setPattern(pattern).focus(pattern); + } + Env sourceEnv = onMatch(env, aggregate.source()); String sourcePattern = sourceEnv.current(); Env aggsEnv = onMatchAggCalls(sourceEnv, aggregate.aggCalls()); @@ -187,6 +517,19 @@ public Env onMatchAggregate(Env env, RelRN.Aggregate aggregate) { String privateVar = groupingEnv.generateVar("private"); Env privateEnv = groupingEnv.addBinding("aggregate_private", privateVar); String aggregateType = determineAggregateType(aggregate); + + boolean hasProjectionExpressions = hasProjectionExpressionsInAggregate(aggregate); + if (hasProjectionExpressions) { + String inputVar = privateEnv.generateVar("input"); + Env inputEnv = privateEnv.addBinding("input", inputVar); + String aggregationsVar = inputEnv.generateVar("aggregations"); + Env aggsBindEnv = inputEnv.addBinding("aggregations", aggregationsVar); + String groupingPrivateVar = aggsBindEnv.generateVar("groupingPrivate"); + Env gpEnv = aggsBindEnv.addBinding("groupingPrivate", groupingPrivateVar); + String pattern = "(" + aggregateType + "\n $" + inputVar + ":*\n $" + aggregationsVar + ":*\n $" + groupingPrivateVar + ":*\n)"; + return gpEnv.addBinding("isAggregateExtractProject", "true").setPattern(pattern).focus(pattern); + } + String pattern = "(" + aggregateType + "\n " + sourcePattern + "\n " + aggsPattern + "\n $" + privateVar + ":*\n)"; return privateEnv.setPattern(pattern).focus(pattern); } @@ -194,14 +537,29 @@ public Env onMatchAggregate(Env env, RelRN.Aggregate aggregate) { private Env onMatchAggCalls(Env env, Seq aggCalls) { Env currentEnv = env; Seq aggPatterns = Seq.empty(); + boolean hasProjOperand = false; for (RelRN.AggCall aggCall : aggCalls) { + if (aggCall.operands().size() == 1) { + RexRN operand = aggCall.operands().get(0); + if (operand instanceof RexRN.Proj proj) { + String projVar = currentEnv.bindings().getOrDefault(proj.operator().getName(), null); + if (projVar != null) { + aggPatterns = aggPatterns.appended("$" + projVar + ":*"); + hasProjOperand = true; + continue; + } + } + } String aggVar = currentEnv.generateVar("agg"); Env aggEnv = currentEnv.addBinding(aggCall.name(), aggVar); aggPatterns = aggPatterns.appended("$" + aggVar + ":*"); currentEnv = aggEnv; } String pattern; - if (aggCalls.size() == 1) { + if (aggCalls.size() == 1 && hasProjOperand) { + pattern = aggPatterns.get(0); + return currentEnv.setPattern(pattern).focus(pattern); + } else if (aggCalls.size() == 1) { String aggVar = currentEnv.generateVar("aggregations"); Env boundEnv = currentEnv.addBinding("aggregations", aggVar); pattern = "$" + aggVar + ":*"; @@ -257,6 +615,15 @@ public Env onMatchProj(Env env, RexRN.Proj proj) { } public Env onMatchGroupBy(Env env, RexRN.GroupBy groupBy) { + if (groupBy.sources().size() == 1) { + RexRN innerExpr = groupBy.sources().get(0); + if (innerExpr instanceof RexRN.Proj proj) { + String projVar = env.bindings().getOrDefault(proj.operator().getName(), null); + if (projVar != null) { + return env.focus("$" + projVar + ":*"); + } + } + } String varName = env.generateVar("groupBy"); return env.addBinding(groupBy.operator().getName(), varName) .focus("$" + varName + ":*"); @@ -291,8 +658,8 @@ private String buildNestedAndPattern(Seq operands) { public Env onMatchTrue(Env env, RexRN literal) { String varName = env.generateVar("true"); return env.addBinding("true_" + System.identityHashCode(literal), varName) - .focus("$" + varName + ":(True)") - .setPattern("$" + varName + ":(True)"); + .focus("$" + varName + ":True") + .setPattern("$" + varName + ":True"); } @Override @@ -318,12 +685,24 @@ public Env transformScan(Env env, RelRN.Scan scan) { @Override public Env transformFilter(Env env, RelRN.Filter filter) { + if (env.bindings().containsKey("isPruneEmptyFilter")) { + String inputVar = env.bindings().get("pruneEmptyInput"); + String pattern = "$" + inputVar; + return env.setPattern(pattern).focus(pattern); + } + if (filter.cond() instanceof RexRN.True) { return transform(env, filter.source()); } if (filter.source() instanceof RelRN.Empty) { return transform(env, filter.source()); } + if (filter.cond() instanceof RexRN.False) { + Env sourceEnv = transform(env, filter.source()); + String sourcePattern = sourceEnv.current(); + String pattern = "(ConstructEmptyValues (OutputCols " + sourcePattern + "))"; + return sourceEnv.setPattern(pattern).focus(pattern); + } Env sourceEnv = transform(env, filter.source()); String sourcePattern = sourceEnv.current(); Env condEnv = transform(sourceEnv, filter.cond()); @@ -342,6 +721,11 @@ public Env transformFilter(Env env, RelRN.Filter filter) { @Override public Env transformProject(Env env, RelRN.Project project) { + if (env.bindings().containsKey("hasZeroRows")) { + String inputVar = env.bindings().getOrDefault("zeroInput", "input"); + String pattern = "$" + inputVar; + return env.setPattern(pattern).focus(pattern); + } if (env.rulename.equals("ProjectMerge")) { String pattern = "(Project\n $input_1\n (MergeProjections\n $proj_0\n $proj_2\n $passthrough_4\n )\n (DifferenceCols\n $innerPassthrough_3\n (ProjectionCols $proj_2)\n )\n)"; return env.setPattern(pattern).focus(pattern); @@ -350,16 +734,18 @@ public Env transformProject(Env env, RelRN.Project project) { String sourcePattern = sourceEnv.current(); Env projEnv = transform(sourceEnv, project.map()); String projPattern = projEnv.current(); - String passthroughVar = projEnv.bindings().get("passthrough"); - if (passthroughVar == null) { - passthroughVar = "passthrough"; - } + String passthroughVar = projEnv.bindings().getOrDefault("passthrough", "passthrough"); String pattern = "(Project\n " + sourcePattern + "\n " + projPattern + "\n $" + passthroughVar + "\n)"; return projEnv.setPattern(pattern).focus(pattern); } @Override public Env transformUnion(Env env, RelRN.Union union) { + if (env.bindings().containsKey("hasZeroRows")) { + String leftVar = env.bindings().getOrDefault("zeroInput", "input"); + String pattern = "(ConstructEmptyValues (OutputCols $" + leftVar + "))"; + return env.setPattern(pattern).focus(pattern); + } Env currentEnv = env; Seq sourcePatterns = Seq.empty(); for (RelRN source : union.sources()) { @@ -391,6 +777,12 @@ private String buildNestedUnionTransform(String unionType, Seq sources, @Override public Env transformIntersect(Env env, RelRN.Intersect intersect) { + if (env.bindings().containsKey("isPruneEmptyIntersect")) { + String leftVar = env.bindings().get("pruneEmptyLeft"); + String pattern = "(ConstructEmptyValues (OutputCols $" + leftVar + "))"; + return env.setPattern(pattern).focus(pattern); + } + Env currentEnv = env; Seq sourcePatterns = Seq.empty(); for (RelRN source : intersect.sources()) { @@ -427,18 +819,123 @@ private String buildNestedIntersectTransform(String intersectType, Seq s return "(" + intersectType + "\n " + first + "\n " + nested + "\n $" + privateVar + "\n)"; } + @Override + public Env transformMinus(Env env, RelRN.Minus minus) { + String pattern = "(Except\n" + + " $left\n" + + " (Union\n" + + " $rightB\n" + + " $rightC\n" + + " (MakeUnionPrivateForExcept $pInner $pOuter)\n" + + " )\n" + + " $pOuter\n" + + ")"; + return env.setPattern(pattern).focus(pattern); + } + @Override public Env transformAggregate(Env env, RelRN.Aggregate aggregate) { + if (env.bindings().containsKey("left") && env.bindings().containsKey("right") + && env.bindings().containsKey("topOn") && env.bindings().containsKey("topPrivate") + && env.bindings().containsKey("aggregations") && env.bindings().containsKey("groupingCols") + && env.bindings().containsKey("ordering")) { + String leftVar = env.bindings().get("left"); + String rightVar = env.bindings().get("right"); + String topOnVar = env.bindings().get("topOn"); + String topPrivateVar = env.bindings().get("topPrivate"); + String aggsVar = env.bindings().get("aggregations"); + String groupingColsVar = env.bindings().get("groupingCols"); + String orderingVar = env.bindings().get("ordering"); + String pattern = "(DistinctOn\n" + + " (LeftJoin $" + leftVar + " $" + rightVar + " $" + topOnVar + " $" + topPrivateVar + ")\n" + + " $" + aggsVar + "\n" + + " (MakeGrouping\n" + + " $" + groupingColsVar + "\n" + + " (PruneOrdering\n" + + " $" + orderingVar + "\n" + + " (UnionCols\n" + + " (OutputCols $" + leftVar + ")\n" + + " (OutputCols $" + rightVar + ")\n" + + " )\n" + + " )\n" + + " )\n" + + ")"; + return env.setPattern(pattern).focus(pattern); + } + if (env.bindings().containsKey("left") && env.bindings().containsKey("aggregations") + && env.bindings().containsKey("groupingCols") && env.bindings().containsKey("ordering")) { + String leftVar = env.bindings().get("left"); + String aggsVar = env.bindings().get("aggregations"); + String groupingColsVar = env.bindings().get("groupingCols"); + String orderingVar = env.bindings().get("ordering"); + String head = "DistinctOn"; + String pattern = "(" + head + "\n" + + " $" + leftVar + "\n" + + " $" + aggsVar + "\n" + + " (MakeGrouping\n" + + " $" + groupingColsVar + "\n" + + " (PruneOrdering $" + orderingVar + " (OutputCols $" + leftVar + "))\n" + + " )\n" + + ")"; + return env.setPattern(pattern).focus(pattern); + } + if (env.bindings().containsKey("input") && env.bindings().containsKey("projections") + && env.bindings().containsKey("passthrough") && env.bindings().containsKey("aggregations") + && env.bindings().containsKey("groupingPrivate")) { + String inputVar = env.bindings().get("input"); + String projectionsVar = env.bindings().get("projections"); + String passthroughVar = env.bindings().get("passthrough"); + String aggregationsVar = env.bindings().get("aggregations"); + String groupingPrivateVar = env.bindings().get("groupingPrivate"); + String aggregateType = determineAggregateType(aggregate); + String pattern = "(" + aggregateType + "\n" + + " $" + inputVar + "\n" + + " (RemapAggregationsThroughProject $" + aggregationsVar + " $" + projectionsVar + ")\n" + + " (RemapGroupingColsThroughProject $" + groupingPrivateVar + " $" + projectionsVar + " $" + passthroughVar + ")\n" + + ")"; + return env.setPattern(pattern).focus(pattern); + } + + if (env.bindings().containsKey("isAggregateExtractProject")) { + Env sourceEnv = transform(env, aggregate.source()); + String sourcePattern = sourceEnv.current(); + Env aggsEnv = transformAggCalls(sourceEnv, aggregate.aggCalls()); + String aggsPattern = aggsEnv.current(); + Env groupingEnv = transformGroupSet(aggsEnv, aggregate.groupSet()); + String groupingPattern = groupingEnv.current(); + String privateVar = groupingEnv.bindings().getOrDefault("aggregate_private", "private"); + String aggregateType = determineAggregateType(aggregate); + String pattern = "(" + aggregateType + "\n (Project\n $input\n []\n (UnionCols\n (GroupingCols $groupingPrivate)\n (AggregationOuterCols $aggregations)\n )\n )\n $aggregations\n $groupingPrivate\n)"; + return groupingEnv.setPattern(pattern).focus(pattern); + } + Env sourceEnv = transform(env, aggregate.source()); String sourcePattern = sourceEnv.current(); - Env aggsEnv = transformAggCalls(sourceEnv, aggregate.aggCalls()); + Env groupingEnv = transformGroupSet(sourceEnv, aggregate.groupSet()); + Env aggsEnv = transformAggCalls(groupingEnv, aggregate.aggCalls()); String aggsPattern = aggsEnv.current(); - Env groupingEnv = transformGroupSet(aggsEnv, aggregate.groupSet()); - String groupingPattern = groupingEnv.current(); - String privateVar = groupingEnv.bindings().getOrDefault("aggregate_private", "private"); + String privateVar = aggsEnv.bindings().getOrDefault("aggregate_private", "private"); String aggregateType = determineAggregateType(aggregate); String pattern = "(" + aggregateType + "\n " + sourcePattern + "\n " + aggsPattern + "\n $" + privateVar + "\n)"; - return groupingEnv.setPattern(pattern).focus(pattern); + return aggsEnv.setPattern(pattern).focus(pattern); + } + + private boolean hasProjectionExpressionsInAggregate(RelRN.Aggregate aggregate) { + for (RexRN groupExpr : aggregate.groupSet()) { + if (groupExpr instanceof RexRN.Proj) { + return true; + } + } + + for (RelRN.AggCall aggCall : aggregate.aggCalls()) { + for (RexRN operand : aggCall.operands()) { + if (operand instanceof RexRN.Proj) { + return true; + } + } + } + + return false; } private Env transformAggCalls(Env env, Seq aggCalls) { @@ -473,7 +970,22 @@ private Env transformGroupSet(Env env, Seq groupSet) { @Override public Env transformEmpty(Env env, RelRN.Empty empty) { - String pattern = "(Values)"; + if (env.bindings().containsKey("hasZeroRows")) { + String inputVar = env.bindings().getOrDefault("zeroInput", "input"); + String patternStr = env.pattern(); + if (patternStr != null && patternStr.contains("Union") && (patternStr.contains("$left") || inputVar.startsWith("left"))) { + String pattern = "(ConstructEmptyValues (OutputCols $" + inputVar + "))"; + return env.setPattern(pattern).focus(pattern); + } + String pattern = "$" + inputVar; + return env.setPattern(pattern).focus(pattern); + } + if (env.bindings().containsKey("isPruneEmptyFilter")) { + String inputVar = env.bindings().get("pruneEmptyInput"); + String pattern = "$" + inputVar; + return env.setPattern(pattern).focus(pattern); + } + String pattern = "(ConstructEmptyValues (OutputCols $input_0))"; return env.setPattern(pattern).focus(pattern); } @@ -489,25 +1001,28 @@ public Env transformField(Env env, RexRN.Field field) { @Override public Env transformPred(Env env, RexRN.Pred pred) { - String varName = env.bindings().get(pred.operator().getName()); - if (varName == null) { - varName = "cond"; - } + String varName = env.bindings().getOrDefault(pred.operator().getName(), "cond"); String pattern = "$" + varName; return env.setPattern(pattern).focus(pattern); } @Override public Env transformProj(Env env, RexRN.Proj proj) { - String varName = env.bindings().get(proj.operator().getName()); - if (varName == null) { - varName = "proj"; - } + String varName = env.bindings().getOrDefault(proj.operator().getName(), "proj"); String pattern = "$" + varName; return env.setPattern(pattern).focus(pattern); } public Env transformGroupBy(Env env, RexRN.GroupBy groupBy) { + if (groupBy.sources().size() == 1) { + RexRN innerExpr = groupBy.sources().get(0); + if (innerExpr instanceof RexRN.Proj proj) { + String projVar = env.bindings().get(proj.operator().getName()); + if (projVar != null) { + return env.setPattern("$" + projVar).focus("$" + projVar); + } + } + } String varName = env.bindings().get(groupBy.operator().getName()); if (varName == null) { varName = "groupBy"; @@ -562,12 +1077,132 @@ public Env transformCustom(Env env, RexRN custom) { public String translate(String name, Env onMatch, Env transform) { StringBuilder sb = new StringBuilder(); sb.append("[").append(name).append(", Normalize]\n"); - sb.append(onMatch.pattern()).append("\n"); + String match = onMatch.pattern(); + if (name.equals("PruneEmptyProject")) { + match = match.replaceAll("\\$projections_\\d+", java.util.regex.Matcher.quoteReplacement("$projections")); + match = match.replaceAll("\\$passthrough_\\d+", java.util.regex.Matcher.quoteReplacement("$passthrough")); + } + if (name.equals("PruneEmptyFilter")) { + match = match.replaceAll("\\$filters_\\d+", java.util.regex.Matcher.quoteReplacement("$filters")); + } + if (match.contains("HasZeroRows") && (match.startsWith("(Union") || match.startsWith("(UnionAll"))) { + match = match.replaceAll("\\s+\\$private_\\d+:\\*\\s*\\)", "\n)"); + match = match.replaceAll("\\s+\\$private_\\d+:\\*\\)", ")"); + } + else if (match.startsWith("(Union\n")) { + String[] lines = match.split("\n"); + if (lines.length >= 3 && lines[1].contains(":(Values)") && lines[2].contains(":(Values)")) { + String leftVar = extractVar(lines[1]); + String rightVar = extractVar(lines[2]); + String unionType = "Union"; + if (lines[0].startsWith("(UnionAll")) unionType = "UnionAll"; + match = "(" + unionType + "\n $" + leftVar + ":* & (HasZeroRows $" + leftVar + ")\n $" + rightVar + ":* & (HasZeroRows $" + rightVar + ")\n)"; + } + } + sb.append(match).append("\n"); sb.append("=>\n"); - sb.append(transform.pattern()).append("\n"); + String out = transform.pattern(); + if (out.startsWith("(ConstructEmptyValues (OutputCols $")) { + int startIdx = "(ConstructEmptyValues (OutputCols $".length(); + int endIdx = startIdx; + while (endIdx < out.length() && (Character.isLetterOrDigit(out.charAt(endIdx)) || out.charAt(endIdx) == '_')) { + endIdx++; + } + String varInOutput = out.substring(startIdx, endIdx); + if (varInOutput.equals("input")) { + String numbered = findFirstVar(match); + if (numbered != null) { + out = out.replace("(ConstructEmptyValues (OutputCols $input)", + "(ConstructEmptyValues (OutputCols $" + numbered + ")"); + } + } + } else if (out.equals("$input")) { + String numbered = findFirstVar(match); + if (numbered != null) { + out = "$" + numbered; + } + } + if (match.contains("HasZeroRows") && match.contains("$left") && out.contains("ConstructEmptyValues")) { + int leftIdx = match.indexOf("$left"); + if (leftIdx >= 0) { + int start = leftIdx + 1; + int end = start; + while (end < match.length() && (Character.isLetterOrDigit(match.charAt(end)) || match.charAt(end) == '_')) end++; + String leftVar = match.substring(start, end); + out = out.replaceAll("(OutputCols \\$)[a-zA-Z_][a-zA-Z0-9_]*", "$1" + leftVar); + } + } + java.util.Map varMap = extractNumberedVarMap(match); + if (!varMap.isEmpty()) { + for (java.util.Map.Entry e : varMap.entrySet()) { + String base = e.getKey(); + String numbered = e.getValue(); + out = out.replaceAll( + "\\$" + java.util.regex.Pattern.quote(base) + "(?![_0-9])", + java.util.regex.Matcher.quoteReplacement(numbered) + ); + } + } + if (name.equals("PruneEmptyProject")) { + out = out.replaceAll("\\$projections_\\d+", java.util.regex.Matcher.quoteReplacement("$projections")); + out = out.replaceAll("\\$passthrough_\\d+", java.util.regex.Matcher.quoteReplacement("$passthrough")); + } + if (name.equals("PruneEmptyFilter")) { + out = out.replaceAll("\\$filters_\\d+", java.util.regex.Matcher.quoteReplacement("$filters")); + } + sb.append(out).append("\n"); return sb.toString(); } + private static String extractVar(String line) { + int i = line.indexOf('$'); + if (i < 0) return null; + int j = i + 1; + while (j < line.length() && (Character.isLetterOrDigit(line.charAt(j)) || line.charAt(j) == '_')) j++; + return line.substring(i + 1, j); + } + + private static String findFirstVar(String match) { + for (String line : match.split("\n")) { + if (line.contains("$private")) continue; + if (line.contains("$")) { + String var = extractVar(line); + if (var != null) return var; + } + } + return null; + } + + private static java.util.Map extractNumberedVarMap(String match) { + java.util.Map map = new java.util.HashMap<>(); + java.util.regex.Matcher m = java.util.regex.Pattern.compile("\\$([A-Za-z][A-Za-z0-9_]*)_([0-9]+)").matcher(match); + while (m.find()) { + String base = m.group(1); + String numbered = "$" + base + "_" + m.group(2); + map.putIfAbsent(base, numbered); + } + return map; + } + + @Override + public Env preTransform(Env env) { + String p = env.pattern(); + if (p != null) { + int idx = p.indexOf("(HasZeroRows $"); + if (idx >= 0) { + int start = idx + "(HasZeroRows $".length(); + int end = p.indexOf(")", start); + if (end > start) { + String var = p.substring(start, end).trim(); + if (!var.isEmpty()) { + return env.addBinding("hasZeroRows", "true").addBinding("zeroInput", var); + } + } + } + } + return env; + } + private String getJoinType(org.apache.calcite.rel.core.JoinRelType joinType) { return switch (joinType) { case INNER -> "InnerJoin"; diff --git a/src/main/java/org/qed/Backends/Cockroach/CockroachTester.java b/src/main/java/org/qed/Backends/Cockroach/CockroachTester.java index 567eee6..787262b 100644 --- a/src/main/java/org/qed/Backends/Cockroach/CockroachTester.java +++ b/src/main/java/org/qed/Backends/Cockroach/CockroachTester.java @@ -57,7 +57,9 @@ public static Seq ruleList() { if (files != null) { for (java.io.File file : files) { String className = file.getName().replace(".java", ""); - if (className.contains("Distinct") || className.contains("Extract") || className.contains("Pull") || className.contains("False") || className.contains("Prune") || className.contains("Minus") || className.contains("AggregateMerge")) { + if (className.contains("Distinct") || className.contains("Pull") || + className.contains("ProjectAggregateMerge") || + className.contains("AggregativeJoinRemove") || className.contains("AggregateProjectConstantToDummyJoin")) { continue; } diff --git a/src/main/java/org/qed/Backends/Cockroach/CockroachTests b/src/main/java/org/qed/Backends/Cockroach/CockroachTests index 1ca9357..cec8170 100644 --- a/src/main/java/org/qed/Backends/Cockroach/CockroachTests +++ b/src/main/java/org/qed/Backends/Cockroach/CockroachTests @@ -68,7 +68,7 @@ CREATE ROLE alice; # -------------------------------------------------- # FilterMerge # -------------------------------------------------- -norm expect=FilterMerge +norm expect=FilterMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM (SELECT * FROM a WHERE k=3) WHERE s='foo' ---- select @@ -84,7 +84,7 @@ select ├── k:1 = 3 [outer=(1), constraints=(/1: [/3 - /3]; tight), fd=()-->(1)] └── s:4 = 'foo' [outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)] -norm expect=FilterMerge +norm expect=FilterMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM (SELECT * FROM a WHERE k=3) WHERE s='foo' ---- select @@ -100,7 +100,7 @@ select ├── k:1 = 3 [outer=(1), constraints=(/1: [/3 - /3]; tight), fd=()-->(1)] └── s:4 = 'foo' [outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)] -norm expect=FilterMerge +norm expect=FilterMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM (SELECT * FROM a WHERE i=1) WHERE False ---- values @@ -109,7 +109,7 @@ values ├── key: () └── fd: ()-->(1-5) -norm expect=FilterMerge +norm expect=FilterMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM (SELECT * FROM a WHERE i<5) WHERE s='foo' ---- select @@ -124,7 +124,7 @@ select ├── i:2 < 5 [outer=(2), constraints=(/2: (/NULL - /4]; tight)] └── s:4 = 'foo' [outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)] -norm expect=FilterMerge +norm expect=FilterMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM (SELECT * FROM a WHERE i>1 AND i<10) WHERE s='foo' OR k=5 ---- select @@ -139,7 +139,7 @@ select ├── (i:2 > 1) AND (i:2 < 10) [outer=(2), constraints=(/2: [/2 - /9]; tight)] └── (s:4 = 'foo') OR (k:1 = 5) [outer=(1,4)] -norm expect=FilterIntoJoin +norm expect=FilterIntoJoin disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM a INNER JOIN b ON a.k=b.x WHERE a.s='foo' ---- inner-join (hash) @@ -164,7 +164,7 @@ inner-join (hash) └── filters └── k:1 = x:8 [outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)] -norm expect=FilterProjectTranspose disable=ProjectFilterTranspose +norm expect=FilterProjectTranspose disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM (SELECT i, i+1 AS r, f FROM a) a WHERE f=10.0 ---- project @@ -181,7 +181,7 @@ project └── projections └── i:2 + 1 [as=r:8, outer=(2), immutable] -norm expect=SemiJoinFilterTranspose disable=ProjectFilterTranspose +norm expect=SemiJoinFilterTranspose disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,UnionMerge) SELECT * FROM xy WHERE EXISTS (SELECT 1 FROM uv WHERE uv.u = xy.x) AND xy.y > 10 ---- semi-join (hash) @@ -204,7 +204,7 @@ semi-join (hash) └── filters └── u:5 = x:1 [outer=(1,5), constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)] -norm expect=ProjectFilterTranspose disable=FilterProjectTranspose +norm expect=ProjectFilterTranspose disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM (SELECT i FROM a WHERE i=100) a ---- select @@ -215,7 +215,7 @@ select └── filters └── i:2 = 100 [outer=(2), constraints=(/2: [/100 - /100]; tight), fd=()-->(2)] -norm expect=FilterReduceTrue +norm expect=FilterReduceTrue disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM a WHERE True ---- scan a @@ -227,7 +227,7 @@ exec-ddl CREATE INDEX partial_idx ON a (s) WHERE true ---- -norm expect=FilterReduceTrue +norm expect=FilterReduceTrue disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM a ---- scan a @@ -241,7 +241,7 @@ exec-ddl DROP INDEX partial_idx ---- -norm expect=UnionMerge disable=ConvertUnionToDistinctUnionAll +norm expect=UnionMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,ConvertUnionToDistinctUnionAll) SELECT a, b, c FROM (SELECT a, b, c FROM t WHERE a < 0) UNION @@ -291,7 +291,7 @@ union └── t.b:15 > 1000 [outer=(15), constraints=(/15: [/1001 - ]; tight)] -norm expect=JoinPushTransitivePredicates disable=FilterIntoJoin +norm expect=JoinPushTransitivePredicates disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) SELECT * FROM a INNER JOIN b ON a.k = b.x WHERE a.s='foo' ---- inner-join (hash) @@ -316,3 +316,268 @@ inner-join (hash) └── filters └── k:1 = x:8 [outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)] + + +exec-ddl +CREATE TABLE sales (id INT PRIMARY KEY, category1 STRING, category2 STRING, amount DECIMAL) +---- + +exec-ddl +CREATE TABLE emp (empno INT PRIMARY KEY, ename STRING, job STRING, mgr INT, hiredate DATE, sal DECIMAL, comm DECIMAL, deptno INT) +---- + +exec-ddl +CREATE TABLE dept (deptno INT PRIMARY KEY, dname STRING, loc STRING) +---- + +norm expect=AggregateFilterTranspose disable=(FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge,PushSelectIntoGroupBy) +SELECT category1, category2, SUM(amount) FROM (SELECT * FROM sales WHERE category1 = category2) GROUP BY category1, category2 +---- +select + ├── columns: category1:2!null category2:3!null sum:7 + ├── key: (3) + ├── fd: (2,3)-->(7), (2)==(3), (3)==(2) + ├── group-by (hash) + │ ├── columns: category1:2 category2:3 sum:7 + │ ├── grouping columns: category1:2 category2:3 + │ ├── key: (2,3) + │ ├── fd: (2,3)-->(7) + │ ├── scan sales + │ │ └── columns: category1:2 category2:3 amount:4 + │ └── aggregations + │ └── sum [as=sum:7, outer=(4)] + │ └── amount:4 + └── filters + └── category1:2 = category2:3 [outer=(2,3), constraints=(/2: (/NULL - ]; /3: (/NULL - ]), fd=(2)==(3), (3)==(2)] + +norm expect=FilterAggregateTranspose disable=(AggregateFilterTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) +SELECT * FROM (SELECT category1, category2, SUM(amount) FROM sales GROUP BY category1, category2) WHERE category1 = category2 +---- +group-by (hash) + ├── columns: category1:2!null category2:3!null sum:7 + ├── grouping columns: category2:3!null + ├── key: (3) + ├── fd: (3)-->(2,7), (2)==(3), (3)==(2) + ├── select + │ ├── columns: category1:2!null category2:3!null amount:4 + │ ├── fd: (2)==(3), (3)==(2) + │ ├── scan sales + │ │ └── columns: category1:2 category2:3 amount:4 + │ └── filters + │ └── category1:2 = category2:3 [outer=(2,3), constraints=(/2: (/NULL - ]; /3: (/NULL - ]), fd=(2)==(3), (3)==(2)] + └── aggregations + ├── sum [as=sum:7, outer=(4)] + │ └── amount:4 + └── const-agg [as=category1:2, outer=(2)] + └── category1:2 + +norm expect=ProjectMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,SemiJoinFilterTranspose,UnionMerge,EliminateProject) +SELECT deptno, ename, sal * 2 AS doubled_sal FROM (SELECT deptno, ename, sal, comm FROM emp) +---- +project + ├── columns: deptno:8 ename:2 doubled_sal:11 + ├── immutable + ├── scan emp + │ └── columns: ename:2 sal:6 deptno:8 + └── projections + └── sal:6 * 2 [as=doubled_sal:11, outer=(6), immutable] + +# Additional test cases using emp/dept/sales tables +norm expect=FilterMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) +SELECT * FROM (SELECT * FROM emp WHERE sal > 1000) WHERE deptno = 10 +---- +select + ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6!null comm:7 deptno:8!null + ├── immutable + ├── key: (1) + ├── fd: ()-->(8), (1)-->(2-7) + ├── scan emp + │ ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6 comm:7 deptno:8 + │ ├── key: (1) + │ └── fd: (1)-->(2-8) + └── filters + ├── sal:6 > 1000 [outer=(6), immutable, constraints=(/6: (/1000 - ]; tight)] + └── deptno:8 = 10 [outer=(8), constraints=(/8: [/10 - /10]; tight), fd=()-->(8)] + +norm expect=FilterIntoJoin disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) +SELECT * FROM emp INNER JOIN dept ON emp.deptno = dept.deptno WHERE emp.sal > 2000 +---- +inner-join (hash) + ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6!null comm:7 deptno:8!null deptno:11!null dname:12 loc:13 + ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more) + ├── immutable + ├── key: (1) + ├── fd: (1)-->(2-8), (11)-->(12,13), (8)==(11), (11)==(8) + ├── select + │ ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6!null comm:7 emp.deptno:8 + │ ├── immutable + │ ├── key: (1) + │ ├── fd: (1)-->(2-8) + │ ├── scan emp + │ │ ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6 comm:7 emp.deptno:8 + │ │ ├── key: (1) + │ │ └── fd: (1)-->(2-8) + │ └── filters + │ └── sal:6 > 2000 [outer=(6), immutable, constraints=(/6: (/2000 - ]; tight)] + ├── scan dept + │ ├── columns: dept.deptno:11!null dname:12 loc:13 + │ ├── key: (11) + │ └── fd: (11)-->(12,13) + └── filters + └── emp.deptno:8 = dept.deptno:11 [outer=(8,11), constraints=(/8: (/NULL - ]; /11: (/NULL - ]), fd=(8)==(11), (11)==(8)] + +norm expect=FilterProjectTranspose disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) +SELECT * FROM (SELECT empno, sal * 1.1 AS new_sal, job FROM emp) WHERE empno > 100 +---- +project + ├── columns: empno:1!null new_sal:11 job:3 + ├── immutable + ├── key: (1) + ├── fd: (1)-->(3,11) + ├── select + │ ├── columns: empno:1!null job:3 sal:6 + │ ├── key: (1) + │ ├── fd: (1)-->(3,6) + │ ├── scan emp + │ │ ├── columns: empno:1!null job:3 sal:6 + │ │ ├── key: (1) + │ │ └── fd: (1)-->(3,6) + │ └── filters + │ └── empno:1 > 100 [outer=(1), constraints=(/1: [/101 - ]; tight)] + └── projections + └── sal:6 * 1.1 [as=new_sal:11, outer=(6), immutable] + +norm expect=SemiJoinFilterTranspose disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,UnionMerge) +SELECT * FROM emp WHERE EXISTS (SELECT 1 FROM dept WHERE dept.deptno = emp.deptno) AND emp.sal > 1500 +---- +semi-join (hash) + ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6!null comm:7 deptno:8 + ├── immutable + ├── key: (1) + ├── fd: (1)-->(2-8) + ├── select + │ ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6!null comm:7 emp.deptno:8 + │ ├── immutable + │ ├── key: (1) + │ ├── fd: (1)-->(2-8) + │ ├── scan emp + │ │ ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6 comm:7 emp.deptno:8 + │ │ ├── key: (1) + │ │ └── fd: (1)-->(2-8) + │ └── filters + │ └── sal:6 > 1500 [outer=(6), immutable, constraints=(/6: (/1500 - ]; tight)] + ├── scan dept + │ ├── columns: dept.deptno:11!null + │ └── key: (11) + └── filters + └── dept.deptno:11 = emp.deptno:8 [outer=(8,11), constraints=(/8: (/NULL - ]; /11: (/NULL - ]), fd=(8)==(11), (11)==(8)] + +norm expect=ProjectFilterTranspose disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) +SELECT * FROM (SELECT empno, job FROM emp WHERE job = 'MANAGER') WHERE empno > 100 +---- +select + ├── columns: empno:1!null job:3!null + ├── key: (1) + ├── fd: ()-->(3) + ├── scan emp + │ ├── columns: empno:1!null job:3 + │ ├── key: (1) + │ └── fd: (1)-->(3) + └── filters + ├── job:3 = 'MANAGER' [outer=(3), constraints=(/3: [/'MANAGER' - /'MANAGER']; tight), fd=()-->(3)] + └── empno:1 > 100 [outer=(1), constraints=(/1: [/101 - ]; tight)] + +norm expect=FilterReduceTrue disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) +SELECT * FROM sales WHERE True +---- +scan sales + ├── columns: id:1!null category1:2 category2:3 amount:4 + ├── key: (1) + └── fd: (1)-->(2-4) + +norm expect=UnionMerge disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,JoinPushTransitivePredicates,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,ConvertUnionToDistinctUnionAll) +SELECT deptno, dname FROM + (SELECT deptno, dname FROM dept WHERE loc = 'NEW YORK') +UNION + (SELECT deptno, dname FROM dept WHERE loc = 'CHICAGO') +UNION + (SELECT deptno, dname FROM dept WHERE loc = 'BOSTON') +---- +union + ├── columns: deptno:18 dname:19 + ├── left columns: deptno:11 dname:12 + ├── right columns: dept.deptno:13 dept.dname:14 + ├── key: (18,19) + ├── project + │ ├── columns: dept.deptno:1!null dept.dname:2 + │ ├── key: (1) + │ ├── fd: (1)-->(2) + │ └── select + │ ├── columns: dept.deptno:1!null dept.dname:2 loc:3!null + │ ├── key: (1) + │ ├── fd: ()-->(3), (1)-->(2) + │ ├── scan dept + │ │ ├── columns: dept.deptno:1!null dept.dname:2 loc:3 + │ │ ├── key: (1) + │ │ └── fd: (1)-->(2,3) + │ └── filters + │ └── loc:3 = 'NEW YORK' [outer=(3), constraints=(/3: [/'NEW YORK' - /'NEW YORK']; tight), fd=()-->(3)] + └── union + ├── columns: deptno:11 dname:12 + ├── left columns: dept.deptno:1 dept.dname:2 + ├── right columns: dept.deptno:6 dept.dname:7 + ├── key: (11,12) + ├── project + │ ├── columns: dept.deptno:6!null dept.dname:7 + │ ├── key: (6) + │ ├── fd: (6)-->(7) + │ └── select + │ ├── columns: dept.deptno:6!null dept.dname:7 loc:8!null + │ ├── key: (6) + │ ├── fd: ()-->(8), (6)-->(7) + │ ├── scan dept + │ │ ├── columns: dept.deptno:6!null dept.dname:7 loc:8 + │ │ ├── key: (6) + │ │ └── fd: (6)-->(7,8) + │ └── filters + │ └── loc:8 = 'CHICAGO' [outer=(8), constraints=(/8: [/'CHICAGO' - /'CHICAGO']; tight), fd=()-->(8)] + └── project + ├── columns: dept.deptno:13!null dept.dname:14 + ├── key: (13) + ├── fd: (13)-->(14) + └── select + ├── columns: dept.deptno:13!null dept.dname:14 loc:15!null + ├── key: (13) + ├── fd: ()-->(15), (13)-->(14) + ├── scan dept + │ ├── columns: dept.deptno:13!null dept.dname:14 loc:15 + │ ├── key: (13) + │ └── fd: (13)-->(14,15) + └── filters + └── loc:15 = 'BOSTON' [outer=(15), constraints=(/15: [/'BOSTON' - /'BOSTON']; tight), fd=()-->(15)] + +norm expect=JoinPushTransitivePredicates disable=(AggregateFilterTranspose,FilterAggregateTranspose,FilterIntoJoin,FilterMerge,FilterProjectTranspose,FilterReduceTrue,FilterSetOpTranspose,IntersectMerge,JoinAddRedundantSemiJoin,JoinCommute,ProjectFilterTranspose,ProjectMerge,SemiJoinFilterTranspose,UnionMerge) +SELECT * FROM emp INNER JOIN dept ON emp.deptno = dept.deptno WHERE emp.job = 'MANAGER' +---- +inner-join (hash) + ├── columns: empno:1!null ename:2 job:3!null mgr:4 hiredate:5 sal:6 comm:7 deptno:8!null deptno:11!null dname:12 loc:13 + ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more) + ├── key: (1) + ├── fd: ()-->(3), (1)-->(2,4-8), (11)-->(12,13), (8)==(11), (11)==(8) + ├── select + │ ├── columns: empno:1!null ename:2 job:3!null mgr:4 hiredate:5 sal:6 comm:7 emp.deptno:8 + │ ├── key: (1) + │ ├── fd: ()-->(3), (1)-->(2,4-8) + │ ├── scan emp + │ │ ├── columns: empno:1!null ename:2 job:3 mgr:4 hiredate:5 sal:6 comm:7 emp.deptno:8 + │ │ ├── key: (1) + │ │ └── fd: (1)-->(2-8) + │ └── filters + │ └── job:3 = 'MANAGER' [outer=(3), constraints=(/3: [/'MANAGER' - /'MANAGER']; tight), fd=()-->(3)] + ├── scan dept + │ ├── columns: dept.deptno:11!null dname:12 loc:13 + │ ├── key: (11) + │ └── fd: (11)-->(12,13) + └── filters + └── emp.deptno:8 = dept.deptno:11 [outer=(8,11), constraints=(/8: (/NULL - ]; /11: (/NULL - ]), fd=(8)==(11), (11)==(8)] + diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateExtractProject.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateExtractProject.opt new file mode 100644 index 0000000..58dbc8e --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateExtractProject.opt @@ -0,0 +1,19 @@ +[AggregateExtractProject, Normalize] +(GroupBy + $input_5:* + $aggregations_6:* + $groupingPrivate_7:* +) +=> +(GroupBy + (Project + $input_5 + [] + (UnionCols + (GroupingCols $groupingPrivate_7) + (AggregationOuterCols $aggregations_6) + ) + ) + $aggregations_6 + $groupingPrivate_7 +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateJoinJoinRemove.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateJoinJoinRemove.opt new file mode 100644 index 0000000..e914ddc --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateJoinJoinRemove.opt @@ -0,0 +1,54 @@ +[AggregateJoinJoinRemove, Normalize] +(DistinctOn + $topJoin_0:(LeftJoin + $bottomJoin_1:(LeftJoin $left_2:* $middle_3:* * *) & + (JoinPreservesLeftRows $bottomJoin_1) & + (JoinDoesNotDuplicateLeftRows $bottomJoin_1) + $right_4:* + $topOn_5:* + $topPrivate_6:* + ) & + (JoinPreservesLeftRows $topJoin_0) & + (JoinDoesNotDuplicateLeftRows $topJoin_0) + $aggregations_7:[] + $groupingPrivate_8:(GroupingPrivate $groupingCols_9:* $ordering_10:*) & + (ColsAreSubset + (UnionCols + $groupingCols_9 + (AggregationOuterCols $aggregations_7) + ) + (UnionCols + (OutputCols $left_2) + (OutputCols $right_4) + ) + ) & + ^(ColsIntersect + (UnionCols + $groupingCols_9 + (AggregationOuterCols $aggregations_7) + ) + (OutputCols $middle_3) + ) & + (OrderingCanProjectCols + $ordering_10 + (UnionCols + (OutputCols $left_2) + (OutputCols $right_4) + ) + ) +) +=> +(DistinctOn + (LeftJoin $left_2 $right_4 $topOn_5 $topPrivate_6) + $aggregations_7 + (MakeGrouping + $groupingCols_9 + (PruneOrdering + $ordering_10 + (UnionCols + (OutputCols $left_2) + (OutputCols $right_4) + ) + ) + ) +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateJoinRemove.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateJoinRemove.opt new file mode 100644 index 0000000..a237ce9 --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateJoinRemove.opt @@ -0,0 +1,28 @@ +[AggregateJoinRemove, Normalize] +(DistinctOn + $input_0:(LeftJoin $left_1:* * * *) & + (JoinPreservesLeftRows $input_0) & + (JoinDoesNotDuplicateLeftRows $input_0) + $aggregations_2:[] + $groupingPrivate_3:(GroupingPrivate $groupingCols_4:* $ordering_5:*) & + (ColsAreSubset + (UnionCols + $groupingCols_4 + (AggregationOuterCols $aggregations_2) + ) + (OutputCols $left_1) + ) & + (OrderingCanProjectCols + $ordering_5 + (OutputCols $left_1) + ) +) +=> +(DistinctOn + $left_1 + $aggregations_2 + (MakeGrouping + $groupingCols_4 + (PruneOrdering $ordering_5 (OutputCols $left_1)) + ) +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateProjectMerge.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateProjectMerge.opt new file mode 100644 index 0000000..0bfb8af --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/AggregateProjectMerge.opt @@ -0,0 +1,16 @@ +[AggregateProjectMerge, Normalize] +(GroupBy + (Project + $input_0:* + $projections_1:* + $passthrough_2:* + ) + $aggregations_3:* + $groupingPrivate_4:* & (CanRemapGroupingColsThroughProject $groupingPrivate_4 $projections_1 $passthrough_2) +) +=> +(GroupBy + $input_0 + (RemapAggregationsThroughProject $aggregations_3 $projections_1) + (RemapGroupingColsThroughProject $groupingPrivate_4 $projections_1 $passthrough_2) +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/FilterReduceFalse.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/FilterReduceFalse.opt new file mode 100644 index 0000000..4421882 --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/FilterReduceFalse.opt @@ -0,0 +1,11 @@ +[FilterReduceFalse, Normalize] +(Select + $input_0:* + $on_1:[ + ... + $item_2:(FiltersItem (False)) + ... + ] +) +=> +(ConstructEmptyValues (OutputCols $input_0)) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/JoinConditionPush.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinConditionPush.opt new file mode 100644 index 0000000..82bcdea --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinConditionPush.opt @@ -0,0 +1,17 @@ +[JoinConditionPush, Normalize] +(InnerJoin + $left_0:* & ^(HasOuterCols $left_0) + $right_1:* & ^(HasOuterCols $right_1) + $on_2:* + $private_3:* +) +=> +(InnerJoin + (Select $left_0 (ExtractBoundConditions $on_2 (OutputCols $left_0))) + (Select $right_1 (ExtractBoundConditions $on_2 (OutputCols $right_1))) + (ExtractUnboundConditions + (ExtractUnboundConditions $on_2 (OutputCols $left_0)) + (OutputCols $right_1) + ) + $private_3 +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/JoinExtractFilter.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinExtractFilter.opt new file mode 100644 index 0000000..cce0a04 --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinExtractFilter.opt @@ -0,0 +1,17 @@ +[JoinExtractFilter, Normalize] +(InnerJoin + $input_0:* + $input_1:* + $cond_2:* + $private_3:* +) +=> +(Select + (InnerJoin + $input_0 + $input_1 + $true + $private_3 +) + $cond_2 +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/JoinReduceFalse.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinReduceFalse.opt new file mode 100644 index 0000000..94b4b6d --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinReduceFalse.opt @@ -0,0 +1,18 @@ +[JoinReduceFalse, Normalize] +(InnerJoin + $input_0:* + $input_1:* + $on_2:[ + ... + $item_3:(FiltersItem (False)) + ... + ] + $private_4:* +) +=> +(InnerJoin + $input_0 + $input_1 + [ (FiltersItem (False)) ] + $private_4 +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/JoinReduceTrue.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinReduceTrue.opt new file mode 100644 index 0000000..c894d23 --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/JoinReduceTrue.opt @@ -0,0 +1,18 @@ +[JoinReduceTrue, Normalize] +(InnerJoin + $input_0:* + $input_1:* + $on_2:[ + ... + $item_3:(FiltersItem (True)) + ... + ] + $private_4:* +) +=> +(InnerJoin + $input_0 + $input_1 + (RemoveFiltersItem $on_2 $item_3) + $private_4 +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/MinusMerge.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/MinusMerge.opt new file mode 100644 index 0000000..5e251b0 --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/MinusMerge.opt @@ -0,0 +1,20 @@ +[MinusMerge, Normalize] +(Except + (Except + $left_0:* + $rightB_1:* + $pInner_2:* + ) + $rightC_3:* + $pOuter_4:* +) +=> +(Except + $left_0 + (Union + $rightB_1 + $rightC_3 + (MakeUnionPrivateForExcept $pInner_2 $pOuter_4) + ) + $pOuter_4 +) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyFilter.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyFilter.opt new file mode 100644 index 0000000..c7a31cb --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyFilter.opt @@ -0,0 +1,7 @@ +[PruneEmptyFilter, Normalize] +(Select + $input_2:* & (HasZeroRows $input_2) + $filters:* +) +=> +$input_2 diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyIntersect.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyIntersect.opt new file mode 100644 index 0000000..43bed9c --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyIntersect.opt @@ -0,0 +1,7 @@ +[PruneEmptyIntersect, Normalize] +(Intersect + $left_0:* + $right_1:* & (HasZeroRows $right_1) +) +=> +(ConstructEmptyValues (OutputCols $left_0)) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyMinus.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyMinus.opt new file mode 100644 index 0000000..285acce --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyMinus.opt @@ -0,0 +1,8 @@ +[PruneEmptyMinus, Normalize] +(Except + $empty_0:(Values) + $input_1:* + $private_2:* +) +=> +(ConstructEmptyValues (OutputCols $input_0)) diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyProject.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyProject.opt new file mode 100644 index 0000000..3c263ac --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyProject.opt @@ -0,0 +1,8 @@ +[PruneEmptyProject, Normalize] +(Project + $input_0:* & (HasZeroRows $input_0) + $projections:* + $passthrough:* +) +=> +$input_0 diff --git a/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyUnion.opt b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyUnion.opt new file mode 100644 index 0000000..bedccbf --- /dev/null +++ b/src/main/java/org/qed/Backends/Cockroach/Generated/PruneEmptyUnion.opt @@ -0,0 +1,7 @@ +[PruneEmptyUnion, Normalize] +(Union + $left_0:* & (HasZeroRows $left_0) + $right_1:* & (HasZeroRows $right_1) +) +=> +(ConstructEmptyValues (OutputCols $left_0)) diff --git a/src/main/java/org/qed/RRuleInstances/PruneZeroRowsTable.java b/src/main/java/org/qed/RRuleInstances/PruneZeroRowsTable.java deleted file mode 100644 index a60fe9b..0000000 --- a/src/main/java/org/qed/RRuleInstances/PruneZeroRowsTable.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.qed.RRuleInstances; - -import org.qed.RRule; -import org.qed.RelRN; - -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; - } -}