Merge branch gerrit/mad-hatter
Change-Id: I00783be2d88c4567d4a3feff5bac3f097baa7a8a
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/LoadRecordFieldsRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/LoadRecordFieldsRule.java
index ce1312f..b9d512b 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/LoadRecordFieldsRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/LoadRecordFieldsRule.java
@@ -309,7 +309,17 @@
private static ILogicalExpression findFieldExpression(AbstractLogicalOperator op, LogicalVariable recordVar,
Object accessKey, IVariableTypeEnvironment typeEnvironment, FieldResolver resolver)
throws AlgebricksException {
- for (Mutable<ILogicalOperator> child : op.getInputs()) {
+ List<Mutable<ILogicalOperator>> inputs = op.getInputs();
+ int inputIdxLimit;
+ if (op.getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN) {
+ // do not search in the right branch of a left outer join
+ inputIdxLimit = 1;
+ } else {
+ inputIdxLimit = inputs.size();
+ }
+
+ for (int inputIdx = 0; inputIdx < inputIdxLimit; inputIdx++) {
+ Mutable<ILogicalOperator> child = inputs.get(inputIdx);
AbstractLogicalOperator opChild = (AbstractLogicalOperator) child.getValue();
if (opChild.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
AssignOperator op2 = (AssignOperator) opChild;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/right_branch_opt_1/right_branch_opt_1.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/right_branch_opt_1/right_branch_opt_1.1.query.sqlpp
new file mode 100644
index 0000000..016b065
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/right_branch_opt_1/right_branch_opt_1.1.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+with
+ t1 as (from range(1, 4) v select v, -v nv, {} as x),
+ t2 as (from range(1, 2) v select v, -v nv, {} as x)
+from t1 left outer join t2 on t1.v = t2.v
+select t1.nv t1nv, t2.nv t2nv, t1.x as t1x, t2.x as t2x
+order by t1nv desc;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/right_branch_opt_1/right_branch_opt_1.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/right_branch_opt_1/right_branch_opt_1.1.adm
new file mode 100644
index 0000000..19eb6b6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/right_branch_opt_1/right_branch_opt_1.1.adm
@@ -0,0 +1,4 @@
+{ "t1nv": -1, "t2nv": -1, "t1x": { }, "t2x": { } }
+{ "t1nv": -2, "t2nv": -2, "t1x": { }, "t2x": { } }
+{ "t1nv": -3, "t1x": { } }
+{ "t1nv": -4, "t1x": { } }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 246f2a3..3c383b0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -12751,6 +12751,11 @@
<output-dir compare="Text">query-ASTERIXDB-769</output-dir>
</compilation-unit>
</test-case>
+ <test-case FilePath="leftouterjoin">
+ <compilation-unit name="right_branch_opt_1">
+ <output-dir compare="Text">right_branch_opt_1</output-dir>
+ </compilation-unit>
+ </test-case>
</test-group>
<test-group name="index-leftouterjoin">
<test-case FilePath="index-leftouterjoin">
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonExpressionsRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonExpressionsRule.java
index 2cfa241..9420498 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonExpressionsRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonExpressionsRule.java
@@ -19,6 +19,7 @@
package org.apache.hyracks.algebricks.rewriter.rules;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -82,6 +83,9 @@
private final Map<ILogicalExpression, ExprEquivalenceClass> exprEqClassMap =
new HashMap<ILogicalExpression, ExprEquivalenceClass>();
+ private final List<LogicalVariable> tmpLiveVars = new ArrayList<>();
+ private final List<LogicalVariable> tmpProducedVars = new ArrayList<>();
+
// Set of operators for which common subexpression elimination should not be performed.
private static final Set<LogicalOperatorTag> ignoreOps = new HashSet<LogicalOperatorTag>(6);
@@ -123,6 +127,31 @@
exprEqClass.setVariable(lhs);
}
+ // remove equivalence classes that supply given vars
+ private void pruneEquivalenceClassMap(Collection<LogicalVariable> pruneVars) throws AlgebricksException {
+ for (Iterator<Map.Entry<ILogicalExpression, ExprEquivalenceClass>> i = exprEqClassMap.entrySet().iterator(); i
+ .hasNext();) {
+ Map.Entry<ILogicalExpression, ExprEquivalenceClass> me = i.next();
+ ExprEquivalenceClass eqClass = me.getValue();
+ boolean eqClassProvidesPruneVar = false;
+ if (eqClass.variableIsSet() && pruneVars.contains(eqClass.getVariable())) {
+ eqClassProvidesPruneVar = true;
+ } else {
+ tmpProducedVars.clear();
+ VariableUtilities.getProducedVariables(eqClass.getFirstOperator(), tmpProducedVars);
+ for (LogicalVariable producedVar : tmpProducedVars) {
+ if (pruneVars.contains(producedVar)) {
+ eqClassProvidesPruneVar = true;
+ break;
+ }
+ }
+ }
+ if (eqClassProvidesPruneVar) {
+ i.remove();
+ }
+ }
+ }
+
private boolean removeCommonExpressions(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
throws AlgebricksException {
AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
@@ -187,6 +216,12 @@
// Update equivalence class map with original assign expression.
updateEquivalenceClassMap(lhs, exprRef, originalAssignExprs.get(i), op);
}
+ } else if (op.getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN) {
+ // remove equivalence classes that provide vars that are live on the right branch of a left outer join
+ ILogicalOperator rightBranchOp = op.getInputs().get(1).getValue();
+ tmpLiveVars.clear();
+ VariableUtilities.getLiveVariables(rightBranchOp, tmpLiveVars);
+ pruneEquivalenceClassMap(tmpLiveVars);
}
// TODO: For now do not perform replacement in nested plans