>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