>From Ritik Raj <ritik....@couchbase.com>:

Ritik Raj has uploaded this change for review. ( 
https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/20128 )


Change subject: [ASTERIXDB-3548][COMP] Handled merged expected schema, while 
projection consolidation
......................................................................

[ASTERIXDB-3548][COMP] Handled merged expected schema, while projection 
consolidation

- user model changes: no
- storage format changes: no
- interface changes: yes

Details:
Added SchemaCloner to clone the mergedSchema created after project consolidation

Patch-seq: 3

Ext-ref: MB-63032
Change-Id: I5541a4f21edd50665e91a2f443118d705f0f1724
---
M 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterUnnestMapOperator.java
M 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/PushdownExpressionReplaceVariableVisitor.java
A 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaTreeVisitor.java
A 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaVariablesSubstituteVisitor.java
M 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/IExpectedSchemaNode.java
A 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaCloneVisitor.java
A 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/ExpectedSchemaSubstituteVariablesVisitor.java
M 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnnestMapOperator.java
M 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ConsolidateProjectionAndFilterExpressionsProcessor.java
M 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/AbstractExpectedSchemaNode.java
10 files changed, 596 insertions(+), 6 deletions(-)



  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb 
refs/changes/28/20128/1

diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ConsolidateProjectionAndFilterExpressionsProcessor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ConsolidateProjectionAndFilterExpressionsProcessor.java
index f9136d2..088bb36 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ConsolidateProjectionAndFilterExpressionsProcessor.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ConsolidateProjectionAndFilterExpressionsProcessor.java
@@ -20,6 +20,7 @@

 import static 
org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression.TRUE;

+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -30,8 +31,11 @@
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
 import 
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaCloneVisitor;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
 import 
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
 import 
org.apache.asterix.optimizer.rules.pushdown.visitor.ExpectedSchemaMergerVisitor;
+import 
org.apache.asterix.optimizer.rules.pushdown.visitor.ExpectedSchemaVariablesSubstituteVisitor;
 import org.apache.asterix.runtime.projection.FunctionCallInformation;
 import org.apache.commons.lang3.mutable.MutableObject;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
@@ -48,13 +52,21 @@
  */
 public class ConsolidateProjectionAndFilterExpressionsProcessor extends 
AbstractPushdownProcessor {
     private final AllVariablesSubstituteVisitor substituteVisitor;
+    private final ExpectedSchemaCloneVisitor schemaCloneVisitor;
+    private final ExpectedSchemaVariablesSubstituteVisitor 
schemaSubstituteVisitor;
     private final ExpectedSchemaMergerVisitor schemaMergerVisitor;
+    private final List<LogicalVariable> scanVariables;
+    private final boolean assemblyAvoidanceEnabled;

     public ConsolidateProjectionAndFilterExpressionsProcessor(PushdownContext 
pushdownContext,
             IOptimizationContext context) {
         super(pushdownContext, context);
+        scanVariables = new ArrayList<>();
         substituteVisitor = new AllVariablesSubstituteVisitor();
         schemaMergerVisitor = new ExpectedSchemaMergerVisitor();
+        schemaCloneVisitor = new ExpectedSchemaCloneVisitor();
+        schemaSubstituteVisitor = new 
ExpectedSchemaVariablesSubstituteVisitor(scanVariables);
+        assemblyAvoidanceEnabled = 
context.getPhysicalOptimizationConfig().isColumnAssemblyAvoidanceEnabled();
     }

     @Override
@@ -79,6 +91,7 @@
         RootExpectedSchemaNode mergedRoot = null;
         RootExpectedSchemaNode mergedMetaRoot = null;

+        scanVariables.clear();
         // First combine filters and projected fields
         for (ScanDefineDescriptor descriptor : scanDefineDescriptors) {
             Map<ILogicalExpression, ARecordType> scanPaths = 
descriptor.getFilterPaths();
@@ -90,6 +103,7 @@

             filterExpr = or(filterExpr, descriptor.getFilterExpression());

+            scanVariables.add(descriptor.getVariable());
             mergedRoot = schemaMergerVisitor.merge(mergedRoot, 
descriptor.getRecordNode());
             mergedMetaRoot = schemaMergerVisitor.merge(mergedMetaRoot, 
descriptor.getMetaNode());
         }
@@ -107,8 +121,21 @@
             
descriptor.setRangeFilterExpression(cloneAndSubstituteVariable(scanVariable, 
rangeFilterExpr));

             
descriptor.setFilterExpression(cloneAndSubstituteVariable(scanVariable, 
filterExpr));
-            descriptor.setRecordNode(mergedRoot);
-            descriptor.setMetaNode(mergedMetaRoot);
+
+            if (assemblyAvoidanceEnabled) {
+                IExpectedSchemaNode clonedMergedRoot = 
mergedRoot.accept(schemaCloneVisitor, null);
+                schemaSubstituteVisitor.traverse(scanVariable, 
clonedMergedRoot, descriptor.getRecordNode());
+                descriptor.setRecordNode((RootExpectedSchemaNode) 
clonedMergedRoot);
+
+                if (mergedMetaRoot != null) {
+                    IExpectedSchemaNode clonedMetaMergedRoot = 
mergedMetaRoot.accept(schemaCloneVisitor, null);
+                    schemaSubstituteVisitor.traverse(scanVariable, 
clonedMetaMergedRoot, descriptor.getMetaNode());
+                    descriptor.setMetaNode((RootExpectedSchemaNode) 
clonedMetaMergedRoot);
+                }
+            } else {
+                descriptor.setRecordNode(mergedRoot);
+                descriptor.setMetaNode(mergedMetaRoot);
+            }
         }

         return true;
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/AbstractExpectedSchemaNode.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/AbstractExpectedSchemaNode.java
index c7ee270..3766c4d 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/AbstractExpectedSchemaNode.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/AbstractExpectedSchemaNode.java
@@ -24,8 +24,8 @@
 import org.apache.hyracks.api.exceptions.SourceLocation;

 abstract class AbstractExpectedSchemaNode implements IExpectedSchemaNode {
-    protected final AbstractFunctionCallExpression parentExpression;
-    protected final ILogicalExpression expression;
+    protected AbstractFunctionCallExpression parentExpression;
+    protected ILogicalExpression expression;
     private AbstractComplexExpectedSchemaNode parent;

     // fieldOrdinal determines the strict ordering in which
@@ -49,6 +49,16 @@
     }

     @Override
+    public void setExpression(ILogicalExpression expression) {
+        this.expression = expression;
+    }
+
+    @Override
+    public void setParentExpression(AbstractFunctionCallExpression 
parentExpression) {
+        this.parentExpression = parentExpression;
+    }
+
+    @Override
     public final AbstractComplexExpectedSchemaNode getParent() {
         return parent;
     }
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaCloneVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaCloneVisitor.java
new file mode 100644
index 0000000..787d112
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaCloneVisitor.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+package org.apache.asterix.optimizer.rules.pushdown.schema;
+
+import java.util.Map;
+
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+
+/**
+ * A special case of MergeVisitor, with cloning the pExpr and expr in node
+ */
+public class ExpectedSchemaCloneVisitor implements 
IExpectedSchemaNodeVisitor<IExpectedSchemaNode, Void> {
+    private AbstractComplexExpectedSchemaNode currentParent;
+
+    @Override
+    public IExpectedSchemaNode visit(RootExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        if (node.isAllFields() || node.isEmpty()) {
+            return node;
+        }
+
+        RootExpectedSchemaNode clonedRoot = (RootExpectedSchemaNode) 
RootExpectedSchemaNode.ALL_FIELDS_ROOT_NODE
+                .replaceIfNeeded(ExpectedSchemaNodeType.OBJECT, null, null);
+        cloneObjectFields(clonedRoot, node.getChildren());
+        clonedRoot.addAllFieldNameIds(node);
+        return clonedRoot;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(ObjectExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        ILogicalExpression clonedParentExpr = 
node.getParentExpression().cloneExpression();
+        ILogicalExpression clonedExpr;
+        if (node.getParentExpression() == node.getExpression()) {
+            clonedExpr = clonedParentExpr;
+        } else {
+            clonedExpr = node.getExpression().cloneExpression();
+        }
+        ObjectExpectedSchemaNode clonedObject = new 
ObjectExpectedSchemaNode(currentParent,
+                (AbstractFunctionCallExpression) clonedParentExpr, clonedExpr);
+        clonedObject.addAllFieldNameIds(node);
+        cloneObjectFields(clonedObject, node.getChildren());
+        return clonedObject;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(ArrayExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        ILogicalExpression clonedParentExpr = 
node.getParentExpression().cloneExpression();
+        ILogicalExpression clonedExpr;
+        if (node.getParentExpression() == node.getExpression()) {
+            clonedExpr = clonedParentExpr;
+        } else {
+            clonedExpr = node.getExpression().cloneExpression();
+        }
+        ArrayExpectedSchemaNode clonedArray = new 
ArrayExpectedSchemaNode(currentParent,
+                (AbstractFunctionCallExpression) clonedParentExpr, clonedExpr);
+        AbstractComplexExpectedSchemaNode previousParent = currentParent;
+        currentParent = clonedArray;
+        IExpectedSchemaNode clonedItem = node.getChild().accept(this, null);
+        clonedArray.addChild(clonedItem);
+        currentParent = previousParent;
+
+        return clonedArray;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(UnionExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        ILogicalExpression clonedParentExpr = 
node.getParentExpression().cloneExpression();
+        UnionExpectedSchemaNode clonedUnion =
+                new UnionExpectedSchemaNode(currentParent, 
(AbstractFunctionCallExpression) clonedParentExpr);
+        AbstractComplexExpectedSchemaNode previousParent = currentParent;
+        currentParent = clonedUnion;
+        for (Map.Entry<ExpectedSchemaNodeType, 
AbstractComplexExpectedSchemaNode> nodeChild : node.getChildren()) {
+            IExpectedSchemaNode mergedChild = 
nodeChild.getValue().accept(this, null);
+            clonedUnion.addChild((AbstractComplexExpectedSchemaNode) 
mergedChild);
+        }
+        currentParent = previousParent;
+        return clonedUnion;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(AnyExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        return new AnyExpectedSchemaNode(currentParent,
+                (AbstractFunctionCallExpression) 
node.getParentExpression().cloneExpression());
+    }
+
+    private void cloneObjectFields(ObjectExpectedSchemaNode objectNode, 
Map<String, IExpectedSchemaNode> children)
+            throws AlgebricksException {
+        for (Map.Entry<String, IExpectedSchemaNode> child : 
children.entrySet()) {
+            String fieldName = child.getKey();
+            IExpectedSchemaNode clonedChild = child.getValue().accept(this, 
null);
+            objectNode.addChild(fieldName, -1, clonedChild);
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/IExpectedSchemaNode.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/IExpectedSchemaNode.java
index 78766b0..4c25e86 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/IExpectedSchemaNode.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/IExpectedSchemaNode.java
@@ -59,6 +59,20 @@
     ILogicalExpression getExpression();

     /**
+     * Set expression of a node
+     *
+     * @param expression new expression
+     */
+    void setExpression(ILogicalExpression expression);
+
+    /**
+     * Set parent expression of a node
+     *
+     * @param parentExpression new parent expression
+     */
+    void setParentExpression(AbstractFunctionCallExpression parentExpression);
+
+    /**
      * Set parent of a node
      *
      * @param parent new parent
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaTreeVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaTreeVisitor.java
new file mode 100644
index 0000000..8b40403
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaTreeVisitor.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+package org.apache.asterix.optimizer.rules.pushdown.visitor;
+
+import java.util.List;
+import java.util.Map;
+
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AbstractComplexExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ArrayExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNodeVisitor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ObjectExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.UnionExpectedSchemaNode;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.ExpectedSchemaSubstituteVariablesVisitor;
+
+public class ExpectedSchemaTreeVisitor implements 
IExpectedSchemaNodeVisitor<IExpectedSchemaNode, Void> {
+
+    private final ExpectedSchemaSubstituteVariablesVisitor variablesVisitor;
+
+    public ExpectedSchemaTreeVisitor(Map<LogicalVariable, LogicalVariable> 
mappedVariables,
+            List<LogicalVariable> scanVariables) {
+        this.variablesVisitor = new 
ExpectedSchemaSubstituteVariablesVisitor(mappedVariables, scanVariables);
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(RootExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        replacer(node.getExpression());
+        replacer(node.getParentExpression());
+        for (Map.Entry<String, IExpectedSchemaNode> childNode : 
node.getChildren().entrySet()) {
+            childNode.getValue().accept(this, arg);
+        }
+        return node;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(ObjectExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        replacer(node.getExpression());
+        replacer(node.getParentExpression());
+        for (Map.Entry<String, IExpectedSchemaNode> childNode : 
node.getChildren().entrySet()) {
+            childNode.getValue().accept(this, arg);
+        }
+        return node;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(ArrayExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        replacer(node.getParentExpression());
+        replacer(node.getExpression());
+        node.getChild().accept(this, arg);
+        return node;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(UnionExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        replacer(node.getParentExpression());
+        replacer(node.getExpression());
+        for (Map.Entry<ExpectedSchemaNodeType, 
AbstractComplexExpectedSchemaNode> nodeChild : node.getChildren()) {
+            nodeChild.getValue().accept(this, arg);
+        }
+        return node;
+    }
+
+    @Override
+    public IExpectedSchemaNode visit(AnyExpectedSchemaNode node, Void arg) 
throws AlgebricksException {
+        replacer(node.getParentExpression());
+        replacer(node.getExpression());
+        return node;
+    }
+
+    private void replacer(ILogicalExpression expr) throws AlgebricksException {
+        if (expr == null) {
+            return;
+        }
+        expr.accept(variablesVisitor, null);
+    }
+
+    public void setCurrentScanVariable(LogicalVariable currentScanVariable) {
+        variablesVisitor.setCurrentScanVariable(currentScanVariable);
+    }
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaVariablesSubstituteVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaVariablesSubstituteVisitor.java
new file mode 100644
index 0000000..a1e7a08
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpectedSchemaVariablesSubstituteVisitor.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+package org.apache.asterix.optimizer.rules.pushdown.visitor;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AbstractComplexExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ArrayExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNodeVisitor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ObjectExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.UnionExpectedSchemaNode;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.ExpectedSchemaSubstituteVariablesVisitor;
+
+public class ExpectedSchemaVariablesSubstituteVisitor implements 
IExpectedSchemaNodeVisitor<Void, IExpectedSchemaNode> {
+    private final Map<LogicalVariable, LogicalVariable> mappedVariables;
+    private final ExpectedSchemaTreeVisitor schemaTreeVisitor;
+    private final ExpectedSchemaSubstituteVariablesVisitor 
substituteVariablesVisitor;
+
+    public ExpectedSchemaVariablesSubstituteVisitor(List<LogicalVariable> 
scanVariables) {
+        this.mappedVariables = new HashMap<>();
+        this.schemaTreeVisitor = new 
ExpectedSchemaTreeVisitor(mappedVariables, scanVariables);
+        this.substituteVariablesVisitor = new 
ExpectedSchemaSubstituteVariablesVisitor(mappedVariables, scanVariables);
+    }
+
+    public void traverse(LogicalVariable currentScanVariable, 
IExpectedSchemaNode schemaNode,
+            IExpectedSchemaNode recordNode) throws AlgebricksException {
+        substituteVariablesVisitor.setCurrentScanVariable(currentScanVariable);
+        schemaTreeVisitor.setCurrentScanVariable(currentScanVariable);
+        schemaNode.accept(this, recordNode);
+    }
+
+    @Override
+    public Void visit(RootExpectedSchemaNode mergedNode, IExpectedSchemaNode 
arg) throws AlgebricksException {
+        if (!(arg instanceof RootExpectedSchemaNode)) {
+            throw new IllegalArgumentException("Cannot substitute root node 
with non-root node");
+        }
+
+        RootExpectedSchemaNode argRoot = (RootExpectedSchemaNode) arg;
+        if (mergedNode.isAllFields() || argRoot.isAllFields()) {
+            return null;
+        }
+
+        substitute(mergedNode, argRoot);
+        traverseObjectFields(mergedNode.getChildren(), argRoot.getChildren());
+        return null;
+    }
+
+    @Override
+    public Void visit(ObjectExpectedSchemaNode mergedNode, IExpectedSchemaNode 
arg) throws AlgebricksException {
+        if (arg == null) {
+            // should we traverse down and use the mapped expression
+            mergedNode.accept(schemaTreeVisitor, null);
+            return null;
+        }
+        if (!(arg instanceof ObjectExpectedSchemaNode)) {
+            throw new IllegalArgumentException("Should have been an object 
node");
+        }
+        ObjectExpectedSchemaNode argRoot = (ObjectExpectedSchemaNode) arg;
+        substitute(mergedNode, argRoot);
+        traverseObjectFields(mergedNode.getChildren(), argRoot.getChildren());
+        return null;
+    }
+
+    @Override
+    public Void visit(ArrayExpectedSchemaNode mergedNode, IExpectedSchemaNode 
arg) throws AlgebricksException {
+        if (arg == null) {
+            // should we traverse down and use the mapped expression
+            mergedNode.accept(schemaTreeVisitor, null);
+            return null;
+        }
+        if (!(arg instanceof ArrayExpectedSchemaNode)) {
+            throw new IllegalArgumentException("Should have been an array 
node");
+        }
+        ArrayExpectedSchemaNode argRoot = (ArrayExpectedSchemaNode) arg;
+        substitute(mergedNode, argRoot);
+        mergedNode.getChild().accept(this, argRoot.getChild());
+        return null;
+    }
+
+    @Override
+    public Void visit(UnionExpectedSchemaNode mergedNode, IExpectedSchemaNode 
arg) throws AlgebricksException {
+        if (arg == null) {
+            // should we traverse down and use the mapped expression
+            mergedNode.accept(schemaTreeVisitor, null);
+            return null;
+        }
+        mergedNode.getParentExpression().accept(substituteVariablesVisitor, 
null);
+        mergedNode.getExpression().accept(substituteVariablesVisitor, null);
+        if (arg instanceof UnionExpectedSchemaNode) {
+            UnionExpectedSchemaNode argRoot = (UnionExpectedSchemaNode) arg;
+            substitute(mergedNode, argRoot);
+            for (Map.Entry<ExpectedSchemaNodeType, 
AbstractComplexExpectedSchemaNode> nodeChild : mergedNode
+                    .getChildren()) {
+                IExpectedSchemaNode mergedChild = nodeChild.getValue();
+                IExpectedSchemaNode argChild = 
argRoot.getChild(mergedChild.getType());
+                substitute(mergedChild, argChild);
+                mergedChild.accept(this, argChild);
+            }
+        } else {
+            ExpectedSchemaNodeType nodeType = arg.getType();
+            IExpectedSchemaNode childNode = mergedNode.getChild(nodeType);
+            substitute(childNode, arg);
+            childNode.accept(this, arg);
+            // once this branch has been done, replace all other branches.
+            for (Map.Entry<ExpectedSchemaNodeType, 
AbstractComplexExpectedSchemaNode> nodeChild : mergedNode
+                    .getChildren()) {
+                IExpectedSchemaNode mergedChild = nodeChild.getValue();
+                if (mergedChild.getType() == nodeType) {
+                    continue;
+                }
+                mergedChild.accept(schemaTreeVisitor, null);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(AnyExpectedSchemaNode mergedNode, IExpectedSchemaNode 
arg) throws AlgebricksException {
+        if (arg == null) {
+            // should we traverse down and use the mapped expression
+            
mergedNode.getParentExpression().accept(substituteVariablesVisitor, null);
+            mergedNode.getExpression().accept(substituteVariablesVisitor, 
null);
+            return null;
+        }
+        substitute(mergedNode, arg);
+        return null;
+    }
+
+    private void traverseObjectFields(Map<String, IExpectedSchemaNode> 
mergedFields,
+            Map<String, IExpectedSchemaNode> argFields) throws 
AlgebricksException {
+        for (Map.Entry<String, IExpectedSchemaNode> mergedChild : 
mergedFields.entrySet()) {
+            IExpectedSchemaNode argChild = argFields.get(mergedChild.getKey());
+            mergedChild.getValue().accept(this, argChild);
+        }
+    }
+
+    private void substitute(IExpectedSchemaNode mergedNode, 
IExpectedSchemaNode arg) {
+        mapVariables(mergedNode.getParentExpression(), 
arg.getParentExpression());
+        mergedNode.setParentExpression(arg.getParentExpression());
+        mergedNode.setExpression(arg.getExpression());
+    }
+
+    private void mapVariables(ILogicalExpression left, ILogicalExpression 
right) {
+        if (left == null || right == null) {
+            return;
+        }
+        if (left.getExpressionTag() == LogicalExpressionTag.VARIABLE
+                && right.getExpressionTag() == LogicalExpressionTag.VARIABLE) {
+            LogicalVariable leftVar = ((VariableReferenceExpression) 
left).getVariableReference();
+            LogicalVariable rightVar = ((VariableReferenceExpression) 
right).getVariableReference();
+            if (leftVar != rightVar) {
+                mappedVariables.put(leftVar, rightVar);
+            }
+            return;
+        }
+        if (left.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL
+                && right.getExpressionTag() == 
LogicalExpressionTag.FUNCTION_CALL) {
+            AbstractFunctionCallExpression leftFunction = 
(AbstractFunctionCallExpression) left;
+            AbstractFunctionCallExpression rightFunction = 
(AbstractFunctionCallExpression) right;
+            if (leftFunction.getFunctionIdentifier() == 
rightFunction.getFunctionIdentifier()) {
+                if (leftFunction.getArguments().size() < 2) {
+                    return;
+                }
+                int argSize = leftFunction.getArguments().size();
+                ILogicalExpression leftLastArg = 
leftFunction.getArguments().get(argSize - 1).getValue();
+                ILogicalExpression rightLastArg = 
rightFunction.getArguments().get(argSize - 1).getValue();
+                if (leftLastArg.equals(rightLastArg)) {
+                    mapVariables(leftFunction.getArguments().get(argSize - 
2).getValue(),
+                            rightFunction.getArguments().get(argSize - 
2).getValue());
+                }
+            }
+        }
+    }
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/PushdownExpressionReplaceVariableVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/PushdownExpressionReplaceVariableVisitor.java
index b6b9a3e..c47a222 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/PushdownExpressionReplaceVariableVisitor.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/PushdownExpressionReplaceVariableVisitor.java
@@ -308,8 +308,6 @@
         context.computeAndSetTypeEnvironmentForOperator(op);
         // removing all the projections, and added IntroduceProjectsRule after 
this to add back the necessary ones.
         
parentOperator.getInputs().get(childIndex).setValue(op.getInputs().get(0).getValue());
-        context.computeAndSetTypeEnvironmentForOperator(parentOperator);
-        parentOperator.recomputeSchema();
         return true;
     }

diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterUnnestMapOperator.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterUnnestMapOperator.java
index 5eaecbc..999d167 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterUnnestMapOperator.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterUnnestMapOperator.java
@@ -97,6 +97,7 @@
             env.setVarType(variables.get(i), outVarType);
         }

+        accountNonOutputInOutputTypeEnvironment(env);
         return env;
     }

diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnnestMapOperator.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnnestMapOperator.java
index 2f97c88..070b87d 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnnestMapOperator.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/UnnestMapOperator.java
@@ -87,6 +87,7 @@
         for (int i = 0; i < n; i++) {
             env.setVarType(variables.get(i), variableTypes.get(i));
         }
+        accountNonOutputInOutputTypeEnvironment(env);
         return env;
     }

diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/ExpectedSchemaSubstituteVariablesVisitor.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/ExpectedSchemaSubstituteVariablesVisitor.java
new file mode 100644
index 0000000..1ceaf7f
--- /dev/null
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/ExpectedSchemaSubstituteVariablesVisitor.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+package org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AggregateFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.StatefulFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor;
+
+public class ExpectedSchemaSubstituteVariablesVisitor implements 
ILogicalExpressionVisitor<Void, Void> {
+    private final Map<LogicalVariable, LogicalVariable> mappedVariables;
+    private final List<LogicalVariable> scanVariables;
+    private LogicalVariable currentScanVariable;
+
+    public ExpectedSchemaSubstituteVariablesVisitor(Map<LogicalVariable, 
LogicalVariable> mappedVariables,
+            List<LogicalVariable> scanVariables) {
+        this.mappedVariables = mappedVariables;
+        this.scanVariables = scanVariables;
+    }
+
+    @Override
+    public Void visitConstantExpression(ConstantExpression expr, Void arg) 
throws AlgebricksException {
+        return null;
+    }
+
+    @Override
+    public Void visitVariableReferenceExpression(VariableReferenceExpression 
expr, Void arg)
+            throws AlgebricksException {
+        LogicalVariable var = expr.getVariableReference();
+        // check first for scanVariable
+        if (scanVariables.contains(var)) {
+            expr.setVariable(currentScanVariable);
+            return null;
+        }
+        LogicalVariable mappedVar = mappedVariables.get(var);
+        if (mappedVar != null) {
+            expr.setVariable(mappedVar);
+        }
+        return null;
+    }
+
+    @Override
+    public Void 
visitAggregateFunctionCallExpression(AggregateFunctionCallExpression expr, Void 
arg)
+            throws AlgebricksException {
+        visitArgs(expr.getArguments(), arg);
+        return null;
+    }
+
+    @Override
+    public Void visitScalarFunctionCallExpression(ScalarFunctionCallExpression 
expr, Void arg)
+            throws AlgebricksException {
+        visitArgs(expr.getArguments(), arg);
+        return null;
+    }
+
+    @Override
+    public Void 
visitStatefulFunctionCallExpression(StatefulFunctionCallExpression expr, Void 
arg)
+            throws AlgebricksException {
+        visitArgs(expr.getArguments(), arg);
+        return null;
+    }
+
+    @Override
+    public Void 
visitUnnestingFunctionCallExpression(UnnestingFunctionCallExpression expr, Void 
arg)
+            throws AlgebricksException {
+        visitArgs(expr.getArguments(), arg);
+        return null;
+    }
+
+    private void visitArgs(List<Mutable<ILogicalExpression>> args, Void 
variable) throws AlgebricksException {
+        for (Mutable<ILogicalExpression> funcArg : args) {
+            funcArg.getValue().accept(this, null);
+        }
+    }
+
+    public void setCurrentScanVariable(LogicalVariable currentScanVariable) {
+        this.currentScanVariable = currentScanVariable;
+    }
+}
\ No newline at end of file

--
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/20128
To unsubscribe, or for help writing mail filters, visit 
https://asterix-gerrit.ics.uci.edu/settings

Gerrit-Project: asterixdb
Gerrit-Branch: ionic
Gerrit-Change-Id: I5541a4f21edd50665e91a2f443118d705f0f1724
Gerrit-Change-Number: 20128
Gerrit-PatchSet: 1
Gerrit-Owner: Ritik Raj <ritik....@couchbase.com>
Gerrit-MessageType: newchange

Reply via email to