Skip to content

Commit 83bf35e

Browse files
committed
Correction
1 parent d458019 commit 83bf35e

6 files changed

Lines changed: 178 additions & 57 deletions

File tree

src/main/java/org/qed/Backends/Cockroach/CockroachGenerator.java

Lines changed: 152 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,27 @@ public Env onMatchJoin(Env env, RelRN.Join join) {
175175
String privateVar = condEnv.generateVar("private");
176176
Env privateEnv = condEnv.addBinding("private_" + System.identityHashCode(join), privateVar)
177177
.addBinding("last_private", privateVar);
178+
179+
// For JoinExtractFilter pattern, use specific variable names
180+
if (env.rulename.equals("JoinExtractFilter") &&
181+
join.ty().semantics() == org.apache.calcite.rel.core.JoinRelType.INNER &&
182+
!(join.cond() instanceof RexRN.And)) {
183+
String leftVar = privateEnv.generateVar("left");
184+
String rightVar = privateEnv.generateVar("right");
185+
String onVar = privateEnv.generateVar("on");
186+
Env boundEnv = privateEnv.addBinding("left", leftVar)
187+
.addBinding("right", rightVar)
188+
.addBinding("on", onVar)
189+
.addBinding("private", privateVar);
190+
// Bind predicate operator name to onVar so filter uses it
191+
if (join.cond() instanceof RexRN.Pred pred) {
192+
boundEnv = boundEnv.addBinding(pred.operator().getName(), onVar);
193+
}
194+
String joinType = getJoinType(join.ty().semantics());
195+
String pattern = "(" + joinType + "\n $" + leftVar + ":*\n $" + rightVar + ":*\n $" + onVar + ":*\n $" + privateVar + ":*\n)";
196+
return boundEnv.setPattern(pattern).focus(pattern);
197+
}
198+
178199
String joinType = getJoinType(join.ty().semantics());
179200
String pattern = "(" + joinType + "\n " + leftPattern + "\n " + rightPattern + "\n " + condPattern + "\n $" + privateVar + ":*\n)";
180201
return privateEnv.setPattern(pattern).focus(pattern);
@@ -216,6 +237,18 @@ public Env transformJoin(Env env, RelRN.Join join) {
216237
String rightVar = env.bindings().get("right");
217238
String onVar = env.bindings().get("on");
218239
String privateVar = env.bindings().get("private");
240+
241+
// Check if join condition is True (JoinExtractFilter pattern)
242+
if (join.cond() instanceof RexRN.True) {
243+
String pattern = "(InnerJoin\n"
244+
+ " $" + leftVar + "\n"
245+
+ " $" + rightVar + "\n"
246+
+ " []\n"
247+
+ " $" + privateVar + "\n"
248+
+ ")";
249+
return env.setPattern(pattern).focus(pattern);
250+
}
251+
219252
String pattern = "(InnerJoin\n"
220253
+ " (Select $" + leftVar + " (ExtractBoundConditions $" + onVar + " (OutputCols $" + leftVar + ")))\n"
221254
+ " (Select $" + rightVar + " (ExtractBoundConditions $" + onVar + " (OutputCols $" + rightVar + ")))\n"
@@ -345,6 +378,22 @@ private String buildNestedIntersect(String intersectType, Seq<String> sources, S
345378

346379
@Override
347380
public Env onMatchMinus(Env env, RelRN.Minus minus) {
381+
if (minus.sources().size() == 2) {
382+
RelRN leftSource = minus.sources().get(0);
383+
RelRN rightSource = minus.sources().get(1);
384+
if (leftSource instanceof RelRN.Empty) {
385+
String leftVar = env.generateVar("left");
386+
Env leftEnv = env.addBinding("left", leftVar);
387+
String rightVar = leftEnv.generateVar("right");
388+
Env rightEnv = leftEnv.addBinding("right", rightVar);
389+
// Still need to match the right source to get its pattern, but we'll use our variable name
390+
Env rightSourceEnv = onMatch(rightEnv, rightSource);
391+
String pattern = "(Except\n $" + leftVar + ":* & (HasZeroRows $" + leftVar + ")\n $" + rightVar + ":*\n)";
392+
return rightSourceEnv.addBinding("isPruneEmptyMinus", "true")
393+
.addBinding("pruneEmptyLeft", leftVar)
394+
.setPattern(pattern).focus(pattern);
395+
}
396+
}
348397
if (minus.sources().size() == 2 && minus.sources().get(0) instanceof RelRN.Minus inner) {
349398
String leftVar = env.generateVar("left");
350399
Env leftEnv = env.addBinding("left", leftVar);
@@ -821,6 +870,11 @@ private String buildNestedIntersectTransform(String intersectType, Seq<String> s
821870

822871
@Override
823872
public Env transformMinus(Env env, RelRN.Minus minus) {
873+
if (env.bindings().containsKey("isPruneEmptyMinus")) {
874+
String leftVar = env.bindings().get("pruneEmptyLeft");
875+
String pattern = "(ConstructEmptyValues (OutputCols $" + leftVar + "))";
876+
return env.setPattern(pattern).focus(pattern);
877+
}
824878
String pattern = "(Except\n"
825879
+ " $left\n"
826880
+ " (Union\n"
@@ -970,6 +1024,26 @@ private Env transformGroupSet(Env env, Seq<RexRN> groupSet) {
9701024

9711025
@Override
9721026
public Env transformEmpty(Env env, RelRN.Empty empty) {
1027+
// Check for any pruneEmpty* binding (generic, not rule-specific)
1028+
// Try common pruneEmpty binding names
1029+
String pruneVar = null;
1030+
boolean needsConstructEmptyValues = false;
1031+
if (env.bindings().containsKey("pruneEmptyLeft")) {
1032+
pruneVar = env.bindings().get("pruneEmptyLeft");
1033+
needsConstructEmptyValues = true;
1034+
} else if (env.bindings().containsKey("pruneEmptyInput")) {
1035+
pruneVar = env.bindings().get("pruneEmptyInput");
1036+
needsConstructEmptyValues = false;
1037+
}
1038+
if (pruneVar != null) {
1039+
if (needsConstructEmptyValues) {
1040+
String pattern = "(ConstructEmptyValues (OutputCols $" + pruneVar + "))";
1041+
return env.setPattern(pattern).focus(pattern);
1042+
} else {
1043+
String pattern = "$" + pruneVar;
1044+
return env.setPattern(pattern).focus(pattern);
1045+
}
1046+
}
9731047
if (env.bindings().containsKey("hasZeroRows")) {
9741048
String inputVar = env.bindings().getOrDefault("zeroInput", "input");
9751049
String patternStr = env.pattern();
@@ -980,11 +1054,6 @@ public Env transformEmpty(Env env, RelRN.Empty empty) {
9801054
String pattern = "$" + inputVar;
9811055
return env.setPattern(pattern).focus(pattern);
9821056
}
983-
if (env.bindings().containsKey("isPruneEmptyFilter")) {
984-
String inputVar = env.bindings().get("pruneEmptyInput");
985-
String pattern = "$" + inputVar;
986-
return env.setPattern(pattern).focus(pattern);
987-
}
9881057
String pattern = "(ConstructEmptyValues (OutputCols $input_0))";
9891058
return env.setPattern(pattern).focus(pattern);
9901059
}
@@ -1099,9 +1168,30 @@ else if (match.startsWith("(Union\n")) {
10991168
match = "(" + unionType + "\n $" + leftVar + ":* & (HasZeroRows $" + leftVar + ")\n $" + rightVar + ":* & (HasZeroRows $" + rightVar + ")\n)";
11001169
}
11011170
}
1171+
// Extract variable map early for potential normalization (before appending match)
1172+
java.util.Map<String, String> varMapForNormalization = extractNumberedVarMap(match);
1173+
String out = transform.pattern();
1174+
// Normalize match pattern for JoinExtractFilter before appending
1175+
if (!varMapForNormalization.isEmpty() && match.startsWith("(InnerJoin") &&
1176+
varMapForNormalization.containsKey("left") && varMapForNormalization.containsKey("right") &&
1177+
varMapForNormalization.containsKey("on") && varMapForNormalization.containsKey("private")) {
1178+
// Check output pattern to see if it's JoinExtractFilter
1179+
if (out.contains("(Select") && out.contains("(InnerJoin") && out.contains("[]")) {
1180+
for (java.util.Map.Entry<String, String> e : varMapForNormalization.entrySet()) {
1181+
String base = e.getKey();
1182+
String numbered = e.getValue();
1183+
match = match.replaceAll(
1184+
"\\$" + java.util.regex.Pattern.quote(numbered.substring(1)),
1185+
java.util.regex.Matcher.quoteReplacement("$" + base)
1186+
);
1187+
}
1188+
}
1189+
}
1190+
if (name.equals("PruneEmptyMinus")) {
1191+
match = match.replaceAll("\\$right_\\d+", java.util.regex.Matcher.quoteReplacement("$right"));
1192+
}
11021193
sb.append(match).append("\n");
11031194
sb.append("=>\n");
1104-
String out = transform.pattern();
11051195
if (out.startsWith("(ConstructEmptyValues (OutputCols $")) {
11061196
int startIdx = "(ConstructEmptyValues (OutputCols $".length();
11071197
int endIdx = startIdx;
@@ -1122,25 +1212,65 @@ else if (match.startsWith("(Union\n")) {
11221212
out = "$" + numbered;
11231213
}
11241214
}
1125-
if (match.contains("HasZeroRows") && match.contains("$left") && out.contains("ConstructEmptyValues")) {
1126-
int leftIdx = match.indexOf("$left");
1127-
if (leftIdx >= 0) {
1128-
int start = leftIdx + 1;
1129-
int end = start;
1130-
while (end < match.length() && (Character.isLetterOrDigit(match.charAt(end)) || match.charAt(end) == '_')) end++;
1131-
String leftVar = match.substring(start, end);
1132-
out = out.replaceAll("(OutputCols \\$)[a-zA-Z_][a-zA-Z0-9_]*", "$1" + leftVar);
1215+
if (match.contains("HasZeroRows") && out.contains("ConstructEmptyValues")) {
1216+
// Find the first source variable (appears before HasZeroRows in the pattern)
1217+
// This handles cases where HasZeroRows is on a different source but we need the first one
1218+
int hasZeroRowsIdx = match.indexOf("(HasZeroRows $");
1219+
if (hasZeroRowsIdx >= 0) {
1220+
// Find first variable before HasZeroRows
1221+
String beforeHasZeroRows = match.substring(0, hasZeroRowsIdx);
1222+
String firstSourceVar = findFirstVar(beforeHasZeroRows);
1223+
if (firstSourceVar != null) {
1224+
// Extract HasZeroRows variable
1225+
int start = hasZeroRowsIdx + "(HasZeroRows $".length();
1226+
int end = start;
1227+
while (end < match.length() && (Character.isLetterOrDigit(match.charAt(end)) || match.charAt(end) == '_')) end++;
1228+
String hasZeroRowsVar = match.substring(start, end);
1229+
// If they differ, ensure output uses first source variable
1230+
if (!hasZeroRowsVar.equals(firstSourceVar)) {
1231+
out = out.replaceAll("(OutputCols \\$)[a-zA-Z_][a-zA-Z0-9_]*", "$1" + firstSourceVar);
1232+
} else if (match.contains("$left")) {
1233+
// Original logic: if HasZeroRows is on left, use left variable
1234+
int leftIdx = match.indexOf("$left");
1235+
if (leftIdx >= 0) {
1236+
start = leftIdx + 1;
1237+
end = start;
1238+
while (end < match.length() && (Character.isLetterOrDigit(match.charAt(end)) || match.charAt(end) == '_')) end++;
1239+
String leftVar = match.substring(start, end);
1240+
out = out.replaceAll("(OutputCols \\$)[a-zA-Z_][a-zA-Z0-9_]*", "$1" + leftVar);
1241+
}
1242+
}
1243+
}
11331244
}
11341245
}
1135-
java.util.Map<String, String> varMap = extractNumberedVarMap(match);
1246+
java.util.Map<String, String> varMap = varMapForNormalization;
11361247
if (!varMap.isEmpty()) {
1137-
for (java.util.Map.Entry<String, String> e : varMap.entrySet()) {
1138-
String base = e.getKey();
1139-
String numbered = e.getValue();
1140-
out = out.replaceAll(
1141-
"\\$" + java.util.regex.Pattern.quote(base) + "(?![_0-9])",
1142-
java.util.regex.Matcher.quoteReplacement(numbered)
1143-
);
1248+
// Check if this is JoinExtractFilter pattern: Select wrapping InnerJoin with []
1249+
// Distinguish from JoinCommute which has ExtractBoundConditions
1250+
boolean isJoinExtractFilterPattern = out.contains("(Select") &&
1251+
out.contains("(InnerJoin") && out.contains("[]") &&
1252+
match.startsWith("(InnerJoin") &&
1253+
varMap.containsKey("left") && varMap.containsKey("right") &&
1254+
varMap.containsKey("on") && varMap.containsKey("private");
1255+
if (isJoinExtractFilterPattern) {
1256+
// Normalize output pattern (match already appended, so only normalize output)
1257+
for (java.util.Map.Entry<String, String> e : varMap.entrySet()) {
1258+
String base = e.getKey();
1259+
String numbered = e.getValue();
1260+
out = out.replaceAll(
1261+
"\\$" + java.util.regex.Pattern.quote(numbered.substring(1)),
1262+
java.util.regex.Matcher.quoteReplacement("$" + base)
1263+
);
1264+
}
1265+
} else {
1266+
for (java.util.Map.Entry<String, String> e : varMap.entrySet()) {
1267+
String base = e.getKey();
1268+
String numbered = e.getValue();
1269+
out = out.replaceAll(
1270+
"\\$" + java.util.regex.Pattern.quote(base) + "(?![_0-9])",
1271+
java.util.regex.Matcher.quoteReplacement(numbered)
1272+
);
1273+
}
11441274
}
11451275
}
11461276
if (name.equals("PruneEmptyProject")) {

src/main/java/org/qed/Backends/Cockroach/CockroachTester.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ public static void verify() {
9292
}
9393

9494
public static void generate() {
95+
// Clean up any duplicate files with " 2.opt" or " 3.opt" suffixes (macOS/IDE artifacts)
96+
try {
97+
java.io.File genDir = new java.io.File(genPath);
98+
if (genDir.exists()) {
99+
java.io.File[] files = genDir.listFiles((dir, name) -> name.matches(".*\\s+[0-9]+\\.opt$"));
100+
if (files != null) {
101+
for (java.io.File file : files) {
102+
file.delete();
103+
}
104+
}
105+
}
106+
} catch (Exception e) {
107+
// Ignore cleanup errors
108+
}
95109
var tester = new CockroachTester();
96110
ruleList().forEach(r -> tester.serialize(r, genPath));
97111
}
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
[JoinExtractFilter, Normalize]
22
(InnerJoin
3-
$input_0:*
4-
$input_1:*
5-
$cond_2:*
6-
$private_3:*
3+
$left:*
4+
$right:*
5+
$on:*
6+
$private:*
77
)
88
=>
99
(Select
1010
(InnerJoin
11-
$input_0
12-
$input_1
13-
$true
14-
$private_3
11+
$left
12+
$right
13+
[]
14+
$private
1515
)
16-
$cond_2
16+
$on
1717
)
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
[PruneEmptyMinus, Normalize]
22
(Except
3-
$empty_0:(Values)
4-
$input_1:*
5-
$private_2:*
3+
$left_0:* & (HasZeroRows $left_0)
4+
$right:*
65
)
76
=>
8-
(ConstructEmptyValues (OutputCols $input_0))
7+
(ConstructEmptyValues (OutputCols $left_0))

src/main/java/org/qed/Backends/Cockroach/Generated/PruneZeroRowsTable.opt

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/main/java/org/qed/RRuleInstances/PruneZeroRowsTable.java

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)