This is an automated email from the ASF dual-hosted git repository.
wyk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git
The following commit(s) were added to refs/heads/master by this push:
new 5f2549e9f3 [ASTERIXDB-3229][COMP] Part 2: Intoduce Pushdown Processor
5f2549e9f3 is described below
commit 5f2549e9f3f99140d88617801cb3b5094b81c662
Author: Wail Alkowaileet <[email protected]>
AuthorDate: Mon Jul 31 08:50:09 2023 -0700
[ASTERIXDB-3229][COMP] Part 2: Intoduce Pushdown Processor
- user model changes: no
- storage format changes: no
- interface changes: no
Details:
This patch adds the pushdown processors for columnar filters.
Change-Id: Id1f1b231a293abb1006ba4e906d64294c882b32d
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/17664
Integration-Tests: Jenkins <[email protected]>
Tested-by: Jenkins <[email protected]>
Reviewed-by: Wail Alkowaileet <[email protected]>
Reviewed-by: Ali Alsuliman <[email protected]>
---
.../ExpressionValueAccessPushdownVisitor.java | 36 +---
.../pushdown/ExpressionValueFilterPushdown.java | 6 +-
.../OperatorValueAccessPushdownVisitor.java | 4 +
.../processor/AbstractFilterPushdownProcessor.java | 218 +++++++++++++++++++++
.../processor/AbstractPushdownProcessor.java | 32 +++
.../processor/ColumnFilterPushdownProcessor.java | 163 +++++++++++++++
.../ColumnRangeFilterPushdownProcessor.java | 147 ++++++++++++++
.../pushdown/processor/IPushdownProcessor.java | 25 +++
.../InlineFilterExpressionsProcessor.java | 95 +++++++++
.../{ => schema}/ExpectedSchemaBuilder.java | 59 +++---
.../pushdown/schema/ObjectExpectedSchemaNode.java | 2 +-
.../ColumnFilterPathBuilderVisitor.java | 13 +-
.../ExpressionToExpectedSchemaNodeVisitor.java | 126 ++++++++++++
.../asterix/metadata/utils/PushdownUtil.java | 179 +++++++++++++++++
.../utils/filter/ArrayPathCheckerVisitor.java | 2 +-
15 files changed, 1034 insertions(+), 73 deletions(-)
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
index b71dcb4307..c57f653c7f 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
@@ -18,11 +18,12 @@
*/
package org.apache.asterix.optimizer.rules.pushdown;
-import java.util.HashSet;
+import static org.apache.asterix.metadata.utils.PushdownUtil.ALLOWED_FUNCTIONS;
+import static
org.apache.asterix.metadata.utils.PushdownUtil.SUPPORTED_FUNCTIONS;
+
import java.util.List;
-import java.util.Set;
-import org.apache.asterix.om.functions.BuiltinFunctions;
+import
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaBuilder;
import org.apache.commons.lang3.mutable.Mutable;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
@@ -30,19 +31,10 @@ 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.IVariableTypeEnvironment;
-import
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
import
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionReferenceTransform;
class ExpressionValueAccessPushdownVisitor implements
ILogicalExpressionReferenceTransform {
- //Set of allowed functions that can request a type in its entirety
- static final Set<FunctionIdentifier> ALLOWED_FUNCTIONS =
createAllowedFunctions();
- //Set of supported array functions
- static final Set<FunctionIdentifier> ARRAY_FUNCTIONS =
createSupportedArrayFunctions();
- //Set of supported functions that we can push down
- static final Set<FunctionIdentifier> SUPPORTED_FUNCTIONS =
createSupportedFunctions();
-
private final ExpectedSchemaBuilder builder;
private List<LogicalVariable> producedVariables;
private IVariableTypeEnvironment typeEnv;
@@ -169,24 +161,4 @@ class ExpressionValueAccessPushdownVisitor implements
ILogicalExpressionReferenc
builder.unregisterVariable(variable);
}
}
-
- private static Set<FunctionIdentifier> createSupportedArrayFunctions() {
- return Set.of(BuiltinFunctions.GET_ITEM, BuiltinFunctions.ARRAY_STAR,
BuiltinFunctions.SCAN_COLLECTION);
- }
-
- private static Set<FunctionIdentifier> createSupportedFunctions() {
- Set<FunctionIdentifier> supportedFunctions = new HashSet<>();
- supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_NAME);
- supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_INDEX);
- supportedFunctions.addAll(ARRAY_FUNCTIONS);
- return supportedFunctions;
- }
-
- private static Set<FunctionIdentifier> createAllowedFunctions() {
- return Set.of(BuiltinFunctions.IS_ARRAY, BuiltinFunctions.IS_OBJECT,
BuiltinFunctions.IS_ATOMIC,
- BuiltinFunctions.IS_NUMBER, BuiltinFunctions.IS_BOOLEAN,
BuiltinFunctions.IS_STRING,
- AlgebricksBuiltinFunctions.IS_MISSING,
AlgebricksBuiltinFunctions.IS_NULL, BuiltinFunctions.IS_UNKNOWN,
- BuiltinFunctions.LT, BuiltinFunctions.LE, BuiltinFunctions.EQ,
BuiltinFunctions.GT, BuiltinFunctions.GE,
- BuiltinFunctions.SCALAR_SQL_COUNT);
- }
}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
index bff8bfffa9..29a2465836 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
@@ -37,10 +37,12 @@ import org.apache.asterix.om.base.IAObject;
import org.apache.asterix.om.constants.AsterixConstantValue;
import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.asterix.om.types.ARecordType;
+import
org.apache.asterix.optimizer.rules.pushdown.processor.ColumnRangeFilterPushdownProcessor;
import
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
-import
org.apache.asterix.optimizer.rules.pushdown.schema.ColumnFilterPathBuilderVisitor;
+import
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaBuilder;
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.visitor.ColumnFilterPathBuilderVisitor;
import org.apache.asterix.runtime.projection.FunctionCallInformation;
import
org.apache.asterix.runtime.projection.ProjectionFiltrationWarningFactoryProvider;
import org.apache.commons.lang3.mutable.Mutable;
@@ -66,6 +68,8 @@ import org.apache.hyracks.api.exceptions.SourceLocation;
* TODO Filter could prevent REPLICATE (i.e., we can scan a dataset twice due
to the fact one scan is filtered and
* TODO the other is not or both have different filters)
* TODO part of this class could potentially be used for external data dynamic
prefixes
+ *
+ * @deprecated use {@link ColumnRangeFilterPushdownProcessor} and {@link
ColumnFilterPathBuilderVisitor}
*/
class ExpressionValueFilterPushdown {
private final ExpectedSchemaBuilder builder;
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
index 9eb25d09da..8e49bbb2a9 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
@@ -38,6 +38,7 @@ import
org.apache.asterix.metadata.entities.ExternalDatasetDetails;
import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.asterix.om.types.ARecordType;
import org.apache.asterix.om.utils.ConstantExpressionUtil;
+import
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaBuilder;
import
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
import org.apache.asterix.runtime.projection.FunctionCallInformation;
import org.apache.commons.lang3.mutable.Mutable;
@@ -93,7 +94,10 @@ import
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisit
/**
* This visitor visits the entire plan and tries to build the information of
the required values from all dataset
+ *
+ * @deprecated use {@link
org.apache.asterix.optimizer.rules.pushdown.visitor.PushdownOperatorVisitor}
*/
+@Deprecated
public class OperatorValueAccessPushdownVisitor implements
ILogicalOperatorVisitor<Void, Void> {
private static final List<LogicalVariable> EMPTY_VARIABLES =
Collections.emptyList();
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractFilterPushdownProcessor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractFilterPushdownProcessor.java
new file mode 100644
index 0000000000..4dbb06d754
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractFilterPushdownProcessor.java
@@ -0,0 +1,218 @@
+/*
+ * 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.processor;
+
+import static org.apache.asterix.metadata.utils.PushdownUtil.getConstant;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isAnd;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isCompare;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isConstant;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isFilterPath;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import org.apache.asterix.optimizer.rules.pushdown.descriptor.DefineDescriptor;
+import
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+import org.apache.asterix.optimizer.rules.pushdown.descriptor.UseDescriptor;
+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.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+
+abstract class AbstractFilterPushdownProcessor extends
AbstractPushdownProcessor {
+ private final Set<ILogicalOperator> visitedOperators;
+
+ public AbstractFilterPushdownProcessor(PushdownContext pushdownContext,
IOptimizationContext context) {
+ super(pushdownContext, context);
+ visitedOperators = new HashSet<>();
+ }
+
+ @Override
+ public final void process() throws AlgebricksException {
+ List<ScanDefineDescriptor> scanDefineDescriptors =
pushdownContext.getRegisteredScans();
+ for (ScanDefineDescriptor scanDefineDescriptor :
scanDefineDescriptors) {
+ if (skip(scanDefineDescriptor)) {
+ continue;
+ }
+ prepareScan(scanDefineDescriptor);
+ pushdownFilter(scanDefineDescriptor, scanDefineDescriptor);
+ }
+ }
+
+ /**
+ * Should skip pushing down a filter for the given data-scan
+ *
+ * @param scanDefineDescriptor data-scan descriptor
+ * @return true to skip, false otherwise
+ */
+ protected abstract boolean skip(ScanDefineDescriptor scanDefineDescriptor);
+
+ /**
+ * Prepare data-scan for a pushdown
+ *
+ * @param scanDefineDescriptor data-scan descriptor
+ */
+ protected abstract void prepareScan(ScanDefineDescriptor
scanDefineDescriptor);
+
+ /**
+ * Prepare to pushdown a SELECT expression in the use-descriptor
+ *
+ * @param useDescriptor contains the SELECT operator and its expression
+ */
+ protected abstract void preparePushdown(UseDescriptor useDescriptor)
throws AlgebricksException;
+
+ /**
+ * Is an expression pushable
+ *
+ * @param expression the expression to push down
+ * @return true if it is pushable, false otherwise
+ */
+ protected abstract boolean isPushable(AbstractFunctionCallExpression
expression);
+
+ /**
+ * Handle a compare function
+ *
+ * @param expression compare expression
+ * @return true if the pushdown should continue, false otherwise
+ */
+ protected abstract boolean handleCompare(AbstractFunctionCallExpression
expression) throws AlgebricksException;
+
+ /**
+ * Handle a value access path expression
+ *
+ * @param expression path expression
+ * @return true if the pushdown should continue, false otherwise
+ */
+ protected abstract boolean handlePath(AbstractFunctionCallExpression
expression) throws AlgebricksException;
+
+ /**
+ * Put the filter expression to data-scan
+ *
+ * @param scanDefineDescriptor data-scan descriptor
+ * @param inlinedExpr inlined filter expression
+ * @return true if the filter expression was set to data-scan, false
otherwise
+ */
+ protected abstract boolean putFilterInformation(ScanDefineDescriptor
scanDefineDescriptor,
+ ILogicalExpression inlinedExpr) throws AlgebricksException;
+
+ private boolean pushdownFilter(DefineDescriptor defineDescriptor,
ScanDefineDescriptor scanDefineDescriptor)
+ throws AlgebricksException {
+ List<UseDescriptor> useDescriptors =
pushdownContext.getUseDescriptors(defineDescriptor);
+ for (UseDescriptor useDescriptor : useDescriptors) {
+ /*
+ * Pushdown works only if the scope(use) and scope(scan) are the
same, as we cannot pushdown when
+ * scope(use) > scope(scan) (e.g., after join or group-by)
+ */
+ if (useDescriptor.getScope() == scanDefineDescriptor.getScope()
+ && useDescriptor.getOperator().getOperatorTag() ==
LogicalOperatorTag.SELECT) {
+ if (!inlineAndPushdownFilter(useDescriptor,
scanDefineDescriptor)) {
+ return false;
+ }
+ }
+ }
+
+ for (UseDescriptor useDescriptor : useDescriptors) {
+ DefineDescriptor nextDefineDescriptor =
pushdownContext.getDefineDescriptor(useDescriptor);
+ if (useDescriptor.getScope() == scanDefineDescriptor.getScope() &&
nextDefineDescriptor != null) {
+ if (!pushdownFilter(nextDefineDescriptor,
scanDefineDescriptor)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private boolean inlineAndPushdownFilter(UseDescriptor useDescriptor,
ScanDefineDescriptor scanDefineDescriptor)
+ throws AlgebricksException {
+ ILogicalOperator selectOp = useDescriptor.getOperator();
+ if (visitedOperators.contains(selectOp)) {
+ // Skip and follow through to find any other selects that can be
pushed down
+ return true;
+ }
+
+ // Get a clone of the SELECT expression and inline it
+ ILogicalExpression inlinedExpr =
pushdownContext.cloneAndInlineExpression(useDescriptor);
+ // Prepare for pushdown
+ preparePushdown(useDescriptor);
+ boolean pushdown =
+ pushdownFilterExpression(inlinedExpr) &&
putFilterInformation(scanDefineDescriptor, inlinedExpr);
+
+ // Do not push down a select twice.
+ visitedOperators.add(selectOp);
+ return pushdown;
+ }
+
+ protected final boolean pushdownFilterExpression(ILogicalExpression
expression) throws AlgebricksException {
+ boolean pushdown = false;
+ if (isConstant(expression)) {
+ IAObject constantValue = getConstant(expression);
+ // Only non-derived types are allowed
+ pushdown = !constantValue.getType().getTypeTag().isDerivedType();
+ } else if (isAnd(expression)) {
+ pushdown = handleAnd((AbstractFunctionCallExpression) expression);
+ } else if (isCompare(expression)) {
+ pushdown = handleCompare((AbstractFunctionCallExpression)
expression);
+ } else if (isFilterPath(expression)) {
+ pushdown = handlePath((AbstractFunctionCallExpression) expression);
+ } else if (expression.getExpressionTag() ==
LogicalExpressionTag.FUNCTION_CALL) {
+ // All functions including OR
+ pushdown = handleFunction((AbstractFunctionCallExpression)
expression);
+ }
+ // PK variable should have (pushdown = false) as we should not involve
the PK (at least currently)
+ return pushdown;
+ }
+
+ private boolean handleAnd(AbstractFunctionCallExpression expression)
throws AlgebricksException {
+ List<Mutable<ILogicalExpression>> args = expression.getArguments();
+ Iterator<Mutable<ILogicalExpression>> argIter = args.iterator();
+ while (argIter.hasNext()) {
+ ILogicalExpression arg = argIter.next().getValue();
+ // Allow for partial pushdown of AND operands
+ if (!pushdownFilterExpression(arg)) {
+ // Remove the expression that cannot be pushed down
+ argIter.remove();
+ }
+ }
+ return !args.isEmpty();
+ }
+
+ private boolean handleFunction(AbstractFunctionCallExpression expression)
throws AlgebricksException {
+ if (!expression.getFunctionInfo().isFunctional() ||
!isPushable(expression)) {
+ return false;
+ }
+
+ for (Mutable<ILogicalExpression> argRef : expression.getArguments()) {
+ ILogicalExpression arg = argRef.getValue();
+ // Either all arguments are pushable or none
+ if (!pushdownFilterExpression(arg)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractPushdownProcessor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractPushdownProcessor.java
new file mode 100644
index 0000000000..75917658a7
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractPushdownProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.processor;
+
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+
+abstract class AbstractPushdownProcessor implements IPushdownProcessor {
+ protected final PushdownContext pushdownContext;
+ protected final IOptimizationContext context;
+
+ AbstractPushdownProcessor(PushdownContext pushdownContext,
IOptimizationContext context) {
+ this.pushdownContext = pushdownContext;
+ this.context = context;
+ }
+}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnFilterPushdownProcessor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnFilterPushdownProcessor.java
new file mode 100644
index 0000000000..8db7969e59
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnFilterPushdownProcessor.java
@@ -0,0 +1,163 @@
+/*
+ * 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.processor;
+
+import static
org.apache.asterix.metadata.utils.PushdownUtil.RANGE_FILTER_PUSHABLE_FUNCTIONS;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isNestedFunction;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isTypeFunction;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.common.config.DatasetConfig;
+import org.apache.asterix.metadata.entities.Dataset;
+import org.apache.asterix.metadata.utils.DatasetUtil;
+import org.apache.asterix.metadata.utils.filter.ArrayPathCheckerVisitor;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+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.descriptor.UseDescriptor;
+import
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+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.visitor.ColumnFilterPathBuilderVisitor;
+import
org.apache.asterix.optimizer.rules.pushdown.visitor.ExpressionToExpectedSchemaNodeVisitor;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.commons.lang3.mutable.MutableObject;
+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.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
+
+/**
+ * Computes a filter expression that can be pushed down to {@link
DatasetConfig.DatasetFormat#COLUMN} datasets.
+ * The computed filter expression then will be evaluated to determine if a
record should be assembled and returned as
+ * a result of a data-scan or not.
+ */
+public class ColumnFilterPushdownProcessor extends
AbstractFilterPushdownProcessor {
+ protected final ExpressionToExpectedSchemaNodeVisitor exprToNodeVisitor;
+ protected final ColumnFilterPathBuilderVisitor pathBuilderVisitor;
+ protected final Map<ILogicalExpression, ARecordType> paths;
+ private final ArrayPathCheckerVisitor checkerVisitor;
+
+ public ColumnFilterPushdownProcessor(PushdownContext pushdownContext,
IOptimizationContext context) {
+ super(pushdownContext, context);
+ exprToNodeVisitor = new ExpressionToExpectedSchemaNodeVisitor();
+ pathBuilderVisitor = new ColumnFilterPathBuilderVisitor();
+ paths = new HashMap<>();
+ checkerVisitor = new ArrayPathCheckerVisitor();
+ }
+
+ @Override
+ protected boolean skip(ScanDefineDescriptor scanDefineDescriptor) {
+ Dataset dataset = scanDefineDescriptor.getDataset();
+ LogicalOperatorTag scanOpTag =
scanDefineDescriptor.getOperator().getOperatorTag();
+ /*
+ * Only use the filter with data-scan. For index-search (unnest-map),
this could be expensive as this
+ * requires to rewind the columnar readers for each point-lookup --
decoding 1000s of values for each
+ * point-lookup. Hence, the query should rely on the secondary-index
filtration and not the columnar filter.
+ */
+ return scanOpTag != LogicalOperatorTag.DATASOURCESCAN
+ || dataset.getDatasetFormatInfo().getFormat() !=
DatasetConfig.DatasetFormat.COLUMN
+ || !DatasetUtil.isFilterPushdownSupported(dataset);
+ }
+
+ @Override
+ protected void prepareScan(ScanDefineDescriptor scanDefineDescriptor) {
+ exprToNodeVisitor.reset(scanDefineDescriptor);
+ }
+
+ @Override
+ protected void preparePushdown(UseDescriptor useDescriptor) throws
AlgebricksException {
+
exprToNodeVisitor.setTypeEnv(useDescriptor.getOperator().computeOutputTypeEnvironment(context));
+ paths.clear();
+ }
+
+ @Override
+ protected boolean isPushable(AbstractFunctionCallExpression expression) {
+ FunctionIdentifier fid = expression.getFunctionIdentifier();
+ return RANGE_FILTER_PUSHABLE_FUNCTIONS.contains(fid) ||
!isNestedFunction(fid) && !isTypeFunction(fid);
+ }
+
+ @Override
+ protected boolean handleCompare(AbstractFunctionCallExpression expression)
throws AlgebricksException {
+ List<Mutable<ILogicalExpression>> args = expression.getArguments();
+
+ Mutable<ILogicalExpression> leftRef = args.get(0);
+ Mutable<ILogicalExpression> rightRef = args.get(1);
+
+ ILogicalExpression left = leftRef.getValue();
+ ILogicalExpression right = rightRef.getValue();
+
+ return pushdownFilterExpression(left) &&
pushdownFilterExpression(right);
+ }
+
+ @Override
+ protected boolean handlePath(AbstractFunctionCallExpression expression)
throws AlgebricksException {
+ IExpectedSchemaNode node = expression.accept(exprToNodeVisitor, null);
+ if (node == null || node.getType() != ExpectedSchemaNodeType.ANY) {
+ return false;
+ }
+ paths.put(expression,
pathBuilderVisitor.buildPath((AnyExpectedSchemaNode) node));
+ return true;
+ }
+
+ @Override
+ protected boolean putFilterInformation(ScanDefineDescriptor
scanDefineDescriptor, ILogicalExpression inlinedExpr)
+ throws AlgebricksException {
+ ILogicalExpression filterExpr =
scanDefineDescriptor.getFilterExpression();
+ if (filterExpr != null) {
+ filterExpr = orExpression(filterExpr, inlinedExpr);
+ scanDefineDescriptor.setFilterExpression(filterExpr);
+ } else {
+ scanDefineDescriptor.setFilterExpression(inlinedExpr);
+ }
+
+ if (checkerVisitor.containsMultipleArrayPaths(paths.values())) {
+ // Cannot pushdown a filter with multiple unnest
+ // TODO allow rewindable column readers for filters
+ // TODO this is a bit conservative (maybe too conservative) as we
can push part of expression down
+ return false;
+ }
+
+ scanDefineDescriptor.getFilterPaths().putAll(paths);
+ return true;
+ }
+
+ protected final AbstractFunctionCallExpression
orExpression(ILogicalExpression filterExpr,
+ ILogicalExpression inlinedExpr) {
+ AbstractFunctionCallExpression funcExpr =
(AbstractFunctionCallExpression) filterExpr;
+ if (!BuiltinFunctions.AND.equals(funcExpr.getFunctionIdentifier())) {
+ IFunctionInfo fInfo =
context.getMetadataProvider().lookupFunction(AlgebricksBuiltinFunctions.OR);
+ List<Mutable<ILogicalExpression>> args = new ArrayList<>();
+ args.add(new MutableObject<>(filterExpr));
+ funcExpr = new ScalarFunctionCallExpression(fInfo, args);
+ }
+ funcExpr.getArguments().add(new MutableObject<>(inlinedExpr));
+ return funcExpr;
+ }
+}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnRangeFilterPushdownProcessor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnRangeFilterPushdownProcessor.java
new file mode 100644
index 0000000000..47db154818
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnRangeFilterPushdownProcessor.java
@@ -0,0 +1,147 @@
+/*
+ * 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.processor;
+
+import static
org.apache.asterix.metadata.utils.PushdownUtil.RANGE_FILTER_PUSHABLE_FUNCTIONS;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isConstant;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isFilterPath;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.common.config.DatasetConfig;
+import org.apache.asterix.metadata.entities.Dataset;
+import org.apache.asterix.metadata.utils.DatasetUtil;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+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.descriptor.UseDescriptor;
+import
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+import
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import org.apache.asterix.runtime.projection.FunctionCallInformation;
+import
org.apache.asterix.runtime.projection.ProjectionFiltrationWarningFactoryProvider;
+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.IOptimizationContext;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+
+/**
+ * Computes a range-filter expression for {@link
DatasetConfig.DatasetFormat#COLUMN} datasets. Each column is such
+ * dataset contains a pair of normalized min-max values. The range filter
expression can be utilized to filter out
+ * mega leaf nodes that do not satisfy the range filter expression.
+ */
+public class ColumnRangeFilterPushdownProcessor extends
ColumnFilterPushdownProcessor {
+ private final Map<String, FunctionCallInformation> sourceInformationMap;
+
+ public ColumnRangeFilterPushdownProcessor(PushdownContext pushdownContext,
IOptimizationContext context) {
+ super(pushdownContext, context);
+ sourceInformationMap = new HashMap<>();
+ }
+
+ @Override
+ protected boolean skip(ScanDefineDescriptor scanDefineDescriptor) {
+ Dataset dataset = scanDefineDescriptor.getDataset();
+ return dataset.getDatasetFormatInfo().getFormat() !=
DatasetConfig.DatasetFormat.COLUMN
+ || !DatasetUtil.isRangeFilterPushdownSupported(dataset);
+ }
+
+ @Override
+ protected void preparePushdown(UseDescriptor useDescriptor) throws
AlgebricksException {
+ super.preparePushdown(useDescriptor);
+ sourceInformationMap.clear();
+ }
+
+ @Override
+ protected boolean isPushable(AbstractFunctionCallExpression expression) {
+ return
RANGE_FILTER_PUSHABLE_FUNCTIONS.contains(expression.getFunctionIdentifier());
+ }
+
+ @Override
+ protected boolean handleCompare(AbstractFunctionCallExpression expression)
throws AlgebricksException {
+ List<Mutable<ILogicalExpression>> args = expression.getArguments();
+
+ Mutable<ILogicalExpression> leftRef = args.get(0);
+ Mutable<ILogicalExpression> rightRef = args.get(1);
+
+ ILogicalExpression left = leftRef.getValue();
+ ILogicalExpression right = rightRef.getValue();
+
+ if (isConstant(left) && isFilterPath(right)) {
+ return pushdownRangeFilter(right, left, expression, true);
+ } else if (isConstant(right) && isFilterPath(left)) {
+ return pushdownRangeFilter(left, right, expression, false);
+ }
+ // Either it is a compare that doesn't involve a constant there's a
function that wraps the value access path
+ return false;
+ }
+
+ @Override
+ protected boolean handlePath(AbstractFunctionCallExpression expression) {
+ // This means we got something like WHERE $r.getField("isVerified") --
where isVerified is a boolean field.
+ // Boolean range filters are not supported currently
+ return false;
+ }
+
+ @Override
+ protected boolean putFilterInformation(ScanDefineDescriptor
scanDefineDescriptor, ILogicalExpression inlinedExpr) {
+ ILogicalExpression filterExpr =
scanDefineDescriptor.getRangeFilterExpression();
+ if (filterExpr != null) {
+ filterExpr = orExpression(filterExpr, inlinedExpr);
+ scanDefineDescriptor.setRangeFilterExpression(filterExpr);
+ } else {
+ scanDefineDescriptor.setRangeFilterExpression(inlinedExpr);
+ }
+ scanDefineDescriptor.getFilterPaths().putAll(paths);
+ scanDefineDescriptor.getPathLocations().putAll(sourceInformationMap);
+
+ return true;
+ }
+
+ private boolean pushdownRangeFilter(ILogicalExpression pathExpr,
ILogicalExpression constExpr,
+ AbstractFunctionCallExpression funcExpr, boolean leftConstant)
throws AlgebricksException {
+ AnyExpectedSchemaNode node = getNode(pathExpr);
+ IAObject constantValue = ((AsterixConstantValue) ((ConstantExpression)
constExpr).getValue()).getObject();
+ if (node == null ||
constantValue.getType().getTypeTag().isDerivedType()) {
+ return false;
+ }
+ String functionName = funcExpr.getFunctionIdentifier().getName();
+ SourceLocation sourceLocation = funcExpr.getSourceLocation();
+ FunctionCallInformation functionCallInfo = new
FunctionCallInformation(functionName, sourceLocation,
+
ProjectionFiltrationWarningFactoryProvider.getIncomparableTypesFactory(leftConstant));
+ ARecordType path = pathBuilderVisitor.buildPath(node, constantValue,
sourceInformationMap, functionCallInfo);
+ paths.put(pathExpr, path);
+ return true;
+ }
+
+ private AnyExpectedSchemaNode getNode(ILogicalExpression expression)
throws AlgebricksException {
+ IExpectedSchemaNode node = expression.accept(exprToNodeVisitor, null);
+ if (node == null || node.getType() != ExpectedSchemaNodeType.ANY) {
+ return null;
+ }
+ return (AnyExpectedSchemaNode) node;
+ }
+
+}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/IPushdownProcessor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/IPushdownProcessor.java
new file mode 100644
index 0000000000..d7c7a4042c
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/IPushdownProcessor.java
@@ -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.
+ */
+package org.apache.asterix.optimizer.rules.pushdown.processor;
+
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+
+public interface IPushdownProcessor {
+ void process() throws AlgebricksException;
+}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/InlineFilterExpressionsProcessor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/InlineFilterExpressionsProcessor.java
new file mode 100644
index 0000000000..6cec455450
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/InlineFilterExpressionsProcessor.java
@@ -0,0 +1,95 @@
+/*
+ * 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.processor;
+
+import static org.apache.asterix.metadata.utils.PushdownUtil.isAnd;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isOr;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isSameFunction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+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.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+
+/**
+ * Inline filter expressions of a scan.
+ * E.g.,
+ * and(a > 2) --> a > 2
+ * and(a > 2, and(b > 2)) --> and(a > 2, b > 2)
+ */
+public class InlineFilterExpressionsProcessor extends
AbstractPushdownProcessor {
+ public InlineFilterExpressionsProcessor(PushdownContext pushdownContext,
IOptimizationContext context) {
+ super(pushdownContext, context);
+ }
+
+ @Override
+ public void process() throws AlgebricksException {
+ List<ScanDefineDescriptor> scanDefineDescriptors =
pushdownContext.getRegisteredScans();
+ for (ScanDefineDescriptor scanDefineDescriptor :
scanDefineDescriptors) {
+
scanDefineDescriptor.setFilterExpression(inline(scanDefineDescriptor.getFilterExpression()));
+
scanDefineDescriptor.setRangeFilterExpression(inline(scanDefineDescriptor.getRangeFilterExpression()));
+ }
+ }
+
+ private ILogicalExpression inline(ILogicalExpression expression) {
+ if (expression == null || expression.getExpressionTag() !=
LogicalExpressionTag.FUNCTION_CALL) {
+ return expression;
+ }
+
+ AbstractFunctionCallExpression funcExpr =
(AbstractFunctionCallExpression) expression;
+ List<Mutable<ILogicalExpression>> args = funcExpr.getArguments();
+ if ((isAnd(funcExpr) || isOr(funcExpr)) && args.size() == 1) {
+ // Fix the incorrect AND with a single argument. This could happen
if the AND expression is partially pushed
+ return inline(args.get(0).getValue());
+ }
+
+ if (isAnd(funcExpr) || isOr(funcExpr)) {
+ List<Mutable<ILogicalExpression>> inlinedArgs = new ArrayList<>();
+ for (Mutable<ILogicalExpression> argRef : args) {
+ // inline arg first
+ ILogicalExpression arg = inline(argRef.getValue());
+
+ if (isSameFunction(funcExpr, arg)) {
+ // funcExpr is AND/OR and arg is also the same. Inline
AND/OR by adding arg arguments to funcExpr
+ AbstractFunctionCallExpression argFuncExpr =
(AbstractFunctionCallExpression) arg;
+ inlinedArgs.addAll(argFuncExpr.getArguments());
+ } else {
+ // funcExpr and arg are different. Set the inlined arg to
argRef (argRef has un-inlined version).
+ argRef.setValue(arg);
+ inlinedArgs.add(argRef);
+ }
+ }
+
+ // Clear the original argument
+ args.clear();
+ // Add the new inlined arguments
+ args.addAll(inlinedArgs);
+ }
+
+ // either the original expression or the inlined AND/OR expression
+ return expression;
+ }
+}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpectedSchemaBuilder.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaBuilder.java
similarity index 83%
rename from
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpectedSchemaBuilder.java
rename to
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaBuilder.java
index edebed94b6..345b545146 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpectedSchemaBuilder.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaBuilder.java
@@ -16,27 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.asterix.optimizer.rules.pushdown;
+package org.apache.asterix.optimizer.rules.pushdown.schema;
-import static
org.apache.asterix.optimizer.rules.pushdown.ExpressionValueAccessPushdownVisitor.ARRAY_FUNCTIONS;
-import static
org.apache.asterix.optimizer.rules.pushdown.ExpressionValueAccessPushdownVisitor.SUPPORTED_FUNCTIONS;
+import static org.apache.asterix.metadata.utils.PushdownUtil.ARRAY_FUNCTIONS;
+import static
org.apache.asterix.metadata.utils.PushdownUtil.SUPPORTED_FUNCTIONS;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.asterix.metadata.utils.PushdownUtil;
import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.asterix.om.types.ARecordType;
-import org.apache.asterix.om.utils.ConstantExpressionUtil;
-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.ObjectExpectedSchemaNode;
-import
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
-import
org.apache.asterix.optimizer.rules.pushdown.schema.UnionExpectedSchemaNode;
import
org.apache.asterix.optimizer.rules.pushdown.visitor.ExpectedSchemaNodeToIATypeTranslatorVisitor;
import org.apache.asterix.runtime.projection.DataProjectionFiltrationInfo;
import org.apache.asterix.runtime.projection.FunctionCallInformation;
@@ -140,7 +132,7 @@ public class ExpectedSchemaBuilder {
return varToNode.get(variable);
}
- IExpectedSchemaNode getNode(ILogicalExpression expr) {
+ public IExpectedSchemaNode getNode(ILogicalExpression expr) {
if (expr.getExpressionTag() == LogicalExpressionTag.VARIABLE) {
return getNode(VariableUtilities.getVariable(expr));
}
@@ -210,7 +202,7 @@ public class ExpectedSchemaBuilder {
return newNode;
}
- private void addChild(AbstractFunctionCallExpression parentExpr,
IVariableTypeEnvironment typeEnv,
+ public static void addChild(AbstractFunctionCallExpression parentExpr,
IVariableTypeEnvironment typeEnv,
AbstractComplexExpectedSchemaNode parent, IExpectedSchemaNode
child) throws AlgebricksException {
switch (parent.getType()) {
case OBJECT:
@@ -228,42 +220,35 @@ public class ExpectedSchemaBuilder {
}
}
- private void handleObject(AbstractFunctionCallExpression parentExpr,
IVariableTypeEnvironment typeEnv,
- AbstractComplexExpectedSchemaNode parent, IExpectedSchemaNode
child) throws AlgebricksException {
- if
(BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(parentExpr.getFunctionIdentifier()))
{
- ObjectExpectedSchemaNode objectNode = (ObjectExpectedSchemaNode)
parent;
-
objectNode.addChild(ConstantExpressionUtil.getStringArgument(parentExpr, 1),
child);
- } else {
- //FIELD_ACCESS_BY_INDEX
- ARecordType recordType = (ARecordType)
typeEnv.getType(parentExpr.getArguments().get(0).getValue());
- ObjectExpectedSchemaNode objectNode = (ObjectExpectedSchemaNode)
parent;
- int fieldIdx = ConstantExpressionUtil.getIntArgument(parentExpr,
1);
- objectNode.addChild(recordType.getFieldNames()[fieldIdx], child);
+ public static ExpectedSchemaNodeType
getExpectedNestedNodeType(AbstractFunctionCallExpression funcExpr) {
+ FunctionIdentifier fid = funcExpr.getFunctionIdentifier();
+ if (BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(fid) ||
BuiltinFunctions.FIELD_ACCESS_BY_INDEX.equals(fid)) {
+ return ExpectedSchemaNodeType.OBJECT;
+ } else if (ARRAY_FUNCTIONS.contains(fid)) {
+ return ExpectedSchemaNodeType.ARRAY;
}
+ throw new IllegalStateException("Function " + fid + " should not be
pushed down");
+ }
+
+ private static void handleObject(AbstractFunctionCallExpression
parentExpr, IVariableTypeEnvironment typeEnv,
+ AbstractComplexExpectedSchemaNode parent, IExpectedSchemaNode
child) throws AlgebricksException {
+ String fieldName = PushdownUtil.getFieldName(parentExpr, typeEnv);
+ ObjectExpectedSchemaNode objectNode = (ObjectExpectedSchemaNode)
parent;
+ objectNode.addChild(fieldName, child);
}
- private void handleArray(AbstractComplexExpectedSchemaNode parent,
IExpectedSchemaNode child) {
+ private static void handleArray(AbstractComplexExpectedSchemaNode parent,
IExpectedSchemaNode child) {
ArrayExpectedSchemaNode arrayNode = (ArrayExpectedSchemaNode) parent;
arrayNode.addChild(child);
}
- private void handleUnion(AbstractFunctionCallExpression parentExpr,
AbstractComplexExpectedSchemaNode parent,
+ private static void handleUnion(AbstractFunctionCallExpression parentExpr,
AbstractComplexExpectedSchemaNode parent,
IExpectedSchemaNode child) throws AlgebricksException {
UnionExpectedSchemaNode unionNode = (UnionExpectedSchemaNode) parent;
ExpectedSchemaNodeType parentType =
getExpectedNestedNodeType(parentExpr);
addChild(parentExpr, null, unionNode.getChild(parentType), child);
}
- private static ExpectedSchemaNodeType
getExpectedNestedNodeType(AbstractFunctionCallExpression funcExpr) {
- FunctionIdentifier fid = funcExpr.getFunctionIdentifier();
- if (BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(fid) ||
BuiltinFunctions.FIELD_ACCESS_BY_INDEX.equals(fid)) {
- return ExpectedSchemaNodeType.OBJECT;
- } else if (ARRAY_FUNCTIONS.contains(fid)) {
- return ExpectedSchemaNodeType.ARRAY;
- }
- throw new IllegalStateException("Function " + fid + " should not be
pushed down");
- }
-
private static boolean isVariable(ILogicalExpression expr) {
return expr.getExpressionTag() == LogicalExpressionTag.VARIABLE;
}
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
index 69812a305e..82a72146a0 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
@@ -67,7 +67,7 @@ public class ObjectExpectedSchemaNode extends
AbstractComplexExpectedSchemaNode
return new ARecordType("typeName", fieldNames, fieldTypes, false);
}
- protected String getChildFieldName(IExpectedSchemaNode requestedChild) {
+ public String getChildFieldName(IExpectedSchemaNode requestedChild) {
String key = null;
for (Map.Entry<String, IExpectedSchemaNode> child :
children.entrySet()) {
if (child.getValue() == requestedChild) {
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ColumnFilterPathBuilderVisitor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ColumnFilterPathBuilderVisitor.java
similarity index 88%
rename from
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ColumnFilterPathBuilderVisitor.java
rename to
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ColumnFilterPathBuilderVisitor.java
index 492aea76d3..94fff57f77 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ColumnFilterPathBuilderVisitor.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ColumnFilterPathBuilderVisitor.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.asterix.optimizer.rules.pushdown.schema;
+package org.apache.asterix.optimizer.rules.pushdown.visitor;
import java.util.Map;
@@ -27,6 +27,13 @@ import org.apache.asterix.om.types.ATypeTag;
import org.apache.asterix.om.types.BuiltinType;
import org.apache.asterix.om.types.IAType;
import org.apache.asterix.om.types.IATypeVisitor;
+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.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.asterix.runtime.projection.FunctionCallInformation;
import
org.apache.asterix.runtime.projection.ProjectionFiltrationWarningFactoryProvider;
@@ -38,6 +45,10 @@ public class ColumnFilterPathBuilderVisitor implements
IExpectedSchemaNodeVisito
private Map<String, FunctionCallInformation> sourceInformationMap;
private int counter = 0;
+ public ARecordType buildPath(AnyExpectedSchemaNode anyNode) {
+ return buildPath(anyNode, null, null, null);
+ }
+
public ARecordType buildPath(AnyExpectedSchemaNode anyNode, IAObject
constant,
Map<String, FunctionCallInformation> sourceInformationMap,
FunctionCallInformation compareFunctionInfo) {
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpressionToExpectedSchemaNodeVisitor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpressionToExpectedSchemaNodeVisitor.java
new file mode 100644
index 0000000000..5168505d0b
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpressionToExpectedSchemaNodeVisitor.java
@@ -0,0 +1,126 @@
+/*
+ * 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 static
org.apache.asterix.metadata.utils.PushdownUtil.SUPPORTED_FUNCTIONS;
+
+import
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+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.ExpectedSchemaBuilder;
+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.RootExpectedSchemaNode;
+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.AbstractFunctionCallExpression;
+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.IVariableTypeEnvironment;
+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.functions.FunctionIdentifier;
+import
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
+import
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor;
+
+public class ExpressionToExpectedSchemaNodeVisitor implements
ILogicalExpressionVisitor<IExpectedSchemaNode, Void> {
+ private IVariableTypeEnvironment typeEnv;
+
+ private ScanDefineDescriptor scanDefineDescriptor;
+
+ public void reset(ScanDefineDescriptor scanDefineDescriptor) {
+ this.scanDefineDescriptor = scanDefineDescriptor;
+ }
+
+ public void setTypeEnv(IVariableTypeEnvironment typeEnv) {
+ this.typeEnv = typeEnv;
+ }
+
+ @Override
+ public IExpectedSchemaNode visitConstantExpression(ConstantExpression
expr, Void arg) throws AlgebricksException {
+ return null;
+ }
+
+ @Override
+ public IExpectedSchemaNode
visitVariableReferenceExpression(VariableReferenceExpression expr, Void arg)
+ throws AlgebricksException {
+ LogicalVariable variable = VariableUtilities.getVariable(expr);
+ if (scanDefineDescriptor.getVariable() == variable
+ || scanDefineDescriptor.getMetaRecordVariable() == variable) {
+ return RootExpectedSchemaNode.ALL_FIELDS_ROOT_NODE;
+ }
+ return null;
+ }
+
+ @Override
+ public IExpectedSchemaNode
visitScalarFunctionCallExpression(ScalarFunctionCallExpression expr, Void arg)
+ throws AlgebricksException {
+ return handleFunction(expr);
+ }
+
+ // Disabled expressions
+ @Override
+ public IExpectedSchemaNode
visitAggregateFunctionCallExpression(AggregateFunctionCallExpression expr, Void
arg)
+ throws AlgebricksException {
+ return null;
+ }
+
+ @Override
+ public IExpectedSchemaNode
visitStatefulFunctionCallExpression(StatefulFunctionCallExpression expr, Void
arg)
+ throws AlgebricksException {
+ return null;
+ }
+
+ @Override
+ public IExpectedSchemaNode
visitUnnestingFunctionCallExpression(UnnestingFunctionCallExpression expr, Void
arg)
+ throws AlgebricksException {
+ return handleFunction(expr);
+ }
+
+ private IExpectedSchemaNode handleFunction(AbstractFunctionCallExpression
expr) throws AlgebricksException {
+ FunctionIdentifier fid = expr.getFunctionIdentifier();
+ if (!SUPPORTED_FUNCTIONS.contains(fid)) {
+ // If not a supported function, return null
+ return null;
+ }
+
+ // All supported functions have the node at their first argument
+ ILogicalExpression parentExpr = expr.getArguments().get(0).getValue();
+ IExpectedSchemaNode parent = parentExpr.accept(this, null);
+ if (parent == null) {
+ return null;
+ }
+
+ AbstractComplexExpectedSchemaNode newParent = replaceIfNeeded(parent,
expr);
+ IExpectedSchemaNode myNode =
+ new AnyExpectedSchemaNode(newParent, expr.getSourceLocation(),
expr.getFunctionIdentifier().getName());
+ ExpectedSchemaBuilder.addChild(expr, typeEnv, newParent, myNode);
+ return myNode;
+ }
+
+ private AbstractComplexExpectedSchemaNode
replaceIfNeeded(IExpectedSchemaNode parent,
+ AbstractFunctionCallExpression funcExpr) {
+ ExpectedSchemaNodeType expectedType =
ExpectedSchemaBuilder.getExpectedNestedNodeType(funcExpr);
+ return (AbstractComplexExpectedSchemaNode)
parent.replaceIfNeeded(expectedType, parent.getSourceLocation(),
+ parent.getFunctionName());
+ }
+}
diff --git
a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/PushdownUtil.java
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/PushdownUtil.java
new file mode 100644
index 0000000000..3bdaca3bcd
--- /dev/null
+++
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/PushdownUtil.java
@@ -0,0 +1,179 @@
+/*
+ * 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.metadata.utils;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.asterix.om.base.ABoolean;
+import org.apache.asterix.om.base.AMissing;
+import org.apache.asterix.om.base.ANull;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.utils.ConstantExpressionUtil;
+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.expressions.AbstractFunctionCallExpression;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.IAlgebricksConstantValue;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class PushdownUtil {
+ //Set of allowed functions that can request a type in its entirety without
marking it as leaf (i.e., ANY)
+ public static final Set<FunctionIdentifier> ALLOWED_FUNCTIONS =
createAllowedFunctions();
+ //Set of supported array functions
+ public static final Set<FunctionIdentifier> ARRAY_FUNCTIONS =
createSupportedArrayFunctions();
+ //Set of supported functions that we can push down (a.k.a. path functions)
+ public static final Set<FunctionIdentifier> SUPPORTED_FUNCTIONS =
createSupportedFunctions();
+
+ public static final Set<FunctionIdentifier> FILTER_PUSHABLE_PATH_FUNCTIONS
= createFilterPushablePathFunctions();
+ public static final Set<FunctionIdentifier> COMPARE_FUNCTIONS =
createCompareFunctions();
+ public static final Set<FunctionIdentifier>
RANGE_FILTER_PUSHABLE_FUNCTIONS = createRangeFilterPushableFunctions();
+
+ private PushdownUtil() {
+ }
+
+ public static String getFieldName(AbstractFunctionCallExpression
fieldAccessExpr, IVariableTypeEnvironment typeEnv)
+ throws AlgebricksException {
+ if
(BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(fieldAccessExpr.getFunctionIdentifier()))
{
+ return ConstantExpressionUtil.getStringArgument(fieldAccessExpr,
1);
+ } else {
+ //FIELD_ACCESS_BY_INDEX
+ ARecordType recordType = (ARecordType)
typeEnv.getType(fieldAccessExpr.getArguments().get(0).getValue());
+ int fieldIdx =
ConstantExpressionUtil.getIntArgument(fieldAccessExpr, 1);
+ return recordType.getFieldNames()[fieldIdx];
+ }
+ }
+
+ public static boolean isConstant(ILogicalExpression expression) {
+ return expression.getExpressionTag() == LogicalExpressionTag.CONSTANT;
+ }
+
+ public static boolean isFilterPath(ILogicalExpression expression) {
+ FunctionIdentifier fid = getFunctionIdentifier(expression);
+ return fid != null && FILTER_PUSHABLE_PATH_FUNCTIONS.contains(fid);
+ }
+
+ public static boolean isCompare(ILogicalExpression expression) {
+ FunctionIdentifier fid = getFunctionIdentifier(expression);
+ return fid != null && COMPARE_FUNCTIONS.contains(fid);
+ }
+
+ public static boolean isAnd(ILogicalExpression expression) {
+ FunctionIdentifier fid = getFunctionIdentifier(expression);
+ return BuiltinFunctions.AND.equals(fid);
+ }
+
+ public static boolean isOr(ILogicalExpression expression) {
+ FunctionIdentifier fid = getFunctionIdentifier(expression);
+ return BuiltinFunctions.OR.equals(fid);
+ }
+
+ public static boolean isTypeFunction(FunctionIdentifier fid) {
+ return fid.getName().startsWith("is");
+ }
+
+ public static boolean isNestedFunction(FunctionIdentifier fid) {
+ return isObjectFunction(fid) || isArrayFunction(fid) ||
BuiltinFunctions.DEEP_EQUAL.equals(fid);
+ }
+
+ public static boolean isObjectFunction(FunctionIdentifier fid) {
+ String functionName = fid.getName();
+ return functionName.contains("object") ||
BuiltinFunctions.PAIRS.equals(fid);
+ }
+
+ public static boolean isArrayFunction(FunctionIdentifier fid) {
+ String functionName = fid.getName();
+ return functionName.startsWith("array") ||
functionName.startsWith("strict") || functionName.startsWith("sql")
+ || BuiltinFunctions.GET_ITEM.equals(fid);
+ }
+
+ public static boolean isSameFunction(ILogicalExpression expr1,
ILogicalExpression expr2) {
+ FunctionIdentifier fid1 = getFunctionIdentifier(expr1);
+ FunctionIdentifier fid2 = getFunctionIdentifier(expr2);
+ return fid1 != null && fid1.equals(fid2);
+ }
+
+ public static IAObject getConstant(ILogicalExpression expr) {
+ IAlgebricksConstantValue algebricksConstant = ((ConstantExpression)
expr).getValue();
+ if (algebricksConstant.isTrue()) {
+ return ABoolean.TRUE;
+ } else if (algebricksConstant.isFalse()) {
+ return ABoolean.FALSE;
+ } else if (algebricksConstant.isMissing()) {
+ return AMissing.MISSING;
+ } else if (algebricksConstant.isNull()) {
+ return ANull.NULL;
+ }
+
+ return ((AsterixConstantValue) algebricksConstant).getObject();
+ }
+
+ private static FunctionIdentifier getFunctionIdentifier(ILogicalExpression
expression) {
+ if (expression.getExpressionTag() !=
LogicalExpressionTag.FUNCTION_CALL) {
+ return null;
+ }
+ AbstractFunctionCallExpression funcExpr =
(AbstractFunctionCallExpression) expression;
+ return funcExpr.getFunctionIdentifier();
+ }
+
+ private static Set<FunctionIdentifier> createSupportedArrayFunctions() {
+ return Set.of(BuiltinFunctions.GET_ITEM, BuiltinFunctions.ARRAY_STAR,
BuiltinFunctions.SCAN_COLLECTION);
+ }
+
+ private static Set<FunctionIdentifier> createSupportedFunctions() {
+ Set<FunctionIdentifier> supportedFunctions = new HashSet<>();
+ supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_NAME);
+ supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_INDEX);
+ supportedFunctions.addAll(ARRAY_FUNCTIONS);
+ return supportedFunctions;
+ }
+
+ private static Set<FunctionIdentifier> createAllowedFunctions() {
+ return Set.of(BuiltinFunctions.IS_ARRAY, BuiltinFunctions.IS_OBJECT,
BuiltinFunctions.IS_ATOMIC,
+ BuiltinFunctions.IS_NUMBER, BuiltinFunctions.IS_BOOLEAN,
BuiltinFunctions.IS_STRING,
+ AlgebricksBuiltinFunctions.IS_MISSING,
AlgebricksBuiltinFunctions.IS_NULL, BuiltinFunctions.IS_UNKNOWN,
+ BuiltinFunctions.LT, BuiltinFunctions.LE, BuiltinFunctions.EQ,
BuiltinFunctions.GT, BuiltinFunctions.GE,
+ BuiltinFunctions.SCALAR_SQL_COUNT);
+ }
+
+ private static Set<FunctionIdentifier> createFilterPushablePathFunctions()
{
+ Set<FunctionIdentifier> pushablePathFunctions = new
HashSet<>(SUPPORTED_FUNCTIONS);
+ // TODO Add support for GET_ITEM.
+ pushablePathFunctions.remove(BuiltinFunctions.GET_ITEM);
+ return pushablePathFunctions;
+ }
+
+ private static Set<FunctionIdentifier> createCompareFunctions() {
+ return Set.of(AlgebricksBuiltinFunctions.LE,
AlgebricksBuiltinFunctions.GE, AlgebricksBuiltinFunctions.LT,
+ AlgebricksBuiltinFunctions.GT, AlgebricksBuiltinFunctions.EQ);
+ }
+
+ private static Set<FunctionIdentifier>
createRangeFilterPushableFunctions() {
+ Set<FunctionIdentifier> pushableFunctions = new
HashSet<>(COMPARE_FUNCTIONS);
+ pushableFunctions.add(AlgebricksBuiltinFunctions.AND);
+ pushableFunctions.add(AlgebricksBuiltinFunctions.OR);
+ return pushableFunctions;
+ }
+}
diff --git
a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
index 594cf9be4f..b1ed40b622 100644
---
a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
+++
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
@@ -42,7 +42,7 @@ public class ArrayPathCheckerVisitor implements
IATypeVisitor<Boolean, AbstractC
private final Set<Object> seenCollections;
private boolean firstPath;
- ArrayPathCheckerVisitor() {
+ public ArrayPathCheckerVisitor() {
seenCollections = new HashSet<>();
}