Yingyi Bu has uploaded a new change for review.
https://asterix-gerrit.ics.uci.edu/1408
Change subject: ASTERIXDB-1650: fix undefined variable reference resolution.
......................................................................
ASTERIXDB-1650: fix undefined variable reference resolution.
The order of resolving a reference to an undefined variable is:
-- If the reference to an undefined variable is the binding expression for
FROM/JOIN/UNNEST/Quantifier,
then it can only be resolved to a dataset;
-- Otherwise, it is firstly resolved as a field-access. If the system couldn't
find a candidate
field access, it is then resolved to a dataset.
Change-Id: I24e4c1b38e53c97380cfb3e2e9b61cdd05fe7002
---
M
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveVariableRule.java
M
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
A
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckDatasetOnlyResolutionVisitor.java
3 files changed, 284 insertions(+), 33 deletions(-)
git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb
refs/changes/08/1408/1
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveVariableRule.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveVariableRule.java
index 2d36a4e..58f2ac7 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveVariableRule.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveVariableRule.java
@@ -51,10 +51,17 @@
import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
/**
- * This rule resolves references to undefined identifiers as:
+ * This rule resolves references to undefined identifiers with the following
priority:
* 1. expression + field-access paths, or
* 2. datasets
* based on the available type and metadata information.
+ *
+ *
+ * Note that undefined variables that are FROM/JOIN/UNNEST/Quantifier binding
expressions
+ * will be resolved to dataset only, which has been done in
+ *
+ * @see
org.apache.asterix.lang.sqlpp.rewrites.visitor.VariableCheckAndRewriteVisitor
+ *
*/
public class ResolveVariableRule implements IAlgebraicRewriteRule {
@@ -144,12 +151,16 @@
Mutable<ILogicalExpression> parentFuncRef, IOptimizationContext
context) throws AlgebricksException {
AbstractFunctionCallExpression func = (AbstractFunctionCallExpression)
funcRef.getValue();
int numVarCandidates = varAccessCandidates.size();
- boolean hasAmbiguity = hasAmbiguity(hasMatchedDataset,
fullyQualifiedDatasetPathCandidateFromParent,
- numVarCandidates);
- if (hasAmbiguity) {
- // More than one possibilities.
- throw new AlgebricksException("Cannot resolve ambiguous alias
reference for undefined identifier "
- + unresolvedVarName);
+
+ // The resolution order: 1. field-access 2. datasets (standalone-name
or fully-qualified)
+ if (numVarCandidates > 0) {
+ if (numVarCandidates == 1) {
+ resolveAsFieldAccess(funcRef,
varAccessCandidates.iterator().next());
+ } else {
+ // More than one possibilities.
+ throw new AlgebricksException(
+ "Cannot resolve ambiguous alias reference for
undefined identifier " + unresolvedVarName);
+ }
} else if (hasMatchedDataset) {
// Rewrites the "resolve" function to a "dataset" function and
only keep the dataset name argument.
func.setFunctionInfo(FunctionUtil.getFunctionInfo(BuiltinFunctions.DATASET));
@@ -165,8 +176,6 @@
new MutableObject<>(new ConstantExpression(
new AsterixConstantValue(new
AString(fullyQualifiedDatasetPathCandidateFromParent.second
+ "." +
fullyQualifiedDatasetPathCandidateFromParent.third)))));
- } else if (numVarCandidates == 1) {
- resolveAsFieldAccess(funcRef,
varAccessCandidates.iterator().next());
} else {
MetadataProvider metadataProvider = (MetadataProvider)
context.getMetadataProvider();
// Cannot find any resolution.
@@ -174,15 +183,6 @@
+ metadataProvider.getDefaultDataverseName() + " nor an
alias with name " + unresolvedVarName);
}
return true;
- }
-
- // Check whether it is possible to have multiple resolutions for a
"resolve" function.
- private boolean hasAmbiguity(boolean hasMatchedDataset,
- Triple<Boolean, String, String>
fullyQualifiedDatasetPathCandidateFromParent, int numVarCandidates) {
- boolean hasAmbiguity = numVarCandidates > 1 || (numVarCandidates == 1
&& hasMatchedDataset);
- hasAmbiguity = hasAmbiguity || (numVarCandidates == 1 &&
fullyQualifiedDatasetPathCandidateFromParent.first);
- hasAmbiguity = hasAmbiguity || (hasMatchedDataset &&
fullyQualifiedDatasetPathCandidateFromParent.first);
- return hasAmbiguity;
}
// Resolves a "resolve" function call as a field access.
diff --git
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
index a137b2d..a6149f8 100644
---
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
+++
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
@@ -36,6 +36,7 @@
import org.apache.asterix.lang.common.struct.Identifier;
import org.apache.asterix.lang.common.struct.VarIdentifier;
import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.asterix.lang.sqlpp.visitor.CheckDatasetOnlyResolutionVisitor;
import
org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor;
import org.apache.asterix.metadata.declared.MetadataProvider;
import org.apache.asterix.metadata.utils.MetadataConstants;
@@ -108,21 +109,29 @@
// it will lead to ambiguities and the plan is going to be very
complex. An example query is:
//
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/subquery/exists
Set<VariableExpr> liveVars =
SqlppVariableUtil.getLiveVariables(scopeChecker.getCurrentScope(), false);
- boolean resolveAsDataset = resolveDatasetFirst(arg) &&
datasetExists(dataverseName, datasetName);
- if (resolveAsDataset) {
- return wrapWithDatasetFunction(dataverseName, datasetName);
- } else if (liveVars.isEmpty()) {
- String defaultDataverseName =
metadataProvider.getDefaultDataverseName();
- if (dataverseName == null && defaultDataverseName == null) {
- throw new AsterixException("Cannot find dataset " + datasetName
- + " because there is no dataverse declared, nor an
alias with name " + datasetName + "!");
+ boolean resolveToDatasetOnly = resolveToDatasetOnly(arg, varExpr);
+ boolean resolveAsDataset = datasetExists(dataverseName, datasetName);
+
+ if (resolveToDatasetOnly) {
+ if (resolveAsDataset) {
+ return wrapWithDatasetFunction(dataverseName, datasetName);
+ } else {
+ throwUnresolvableError(dataverseName, datasetName);
}
- //If no available dataset nor in-scope variable to resolve to, we
throw an error.
- throw new AsterixException("Cannot find dataset " + datasetName +
" in dataverse "
- + (dataverseName == null ? defaultDataverseName :
dataverseName) + " nor an alias with name "
- + datasetName + "!");
}
return wrapWithResolveFunction(varExpr, liveVars);
+ }
+
+ private void throwUnresolvableError(String dataverseName, String
datasetName) throws AsterixException {
+ String defaultDataverseName =
metadataProvider.getDefaultDataverseName();
+ if (dataverseName == null && defaultDataverseName == null) {
+ throw new AsterixException("Cannot find dataset " + datasetName
+ + " because there is no dataverse declared, nor an alias
with name " + datasetName + "!");
+ }
+ //If no available dataset nor in-scope variable to resolve to, we
throw an error.
+ throw new AsterixException("Cannot find dataset " + datasetName + " in
dataverse "
+ + (dataverseName == null ? defaultDataverseName :
dataverseName) + " nor an alias with name "
+ + datasetName + "!");
}
// Checks whether we need to error the variable reference, e.g., the
variable is referred
@@ -135,9 +144,10 @@
}
}
- // For From/Join/UNNEST/NEST, we resolve the undefined identifier
reference as dataset reference first.
- private boolean resolveDatasetFirst(ILangExpression arg) {
- return arg != null;
+ // For From/Join/UNNEST/Quantifiers, we resolve the undefined identifier
reference as dataset reference only.
+ private boolean resolveToDatasetOnly(ILangExpression arg, VariableExpr
variableExpr) throws AsterixException {
+ CheckDatasetOnlyResolutionVisitor visitor = new
CheckDatasetOnlyResolutionVisitor();
+ return arg.accept(visitor, variableExpr);
}
// Whether a rewrite is needed for a variable reference expression.
diff --git
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckDatasetOnlyResolutionVisitor.java
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckDatasetOnlyResolutionVisitor.java
new file mode 100644
index 0000000..3c8340b
--- /dev/null
+++
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckDatasetOnlyResolutionVisitor.java
@@ -0,0 +1,241 @@
+/*
+ *
+ * * 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.lang.sqlpp.visitor;
+
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.common.functions.FunctionSignature;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.clause.GroupbyClause;
+import org.apache.asterix.lang.common.clause.LetClause;
+import org.apache.asterix.lang.common.clause.LimitClause;
+import org.apache.asterix.lang.common.clause.OrderbyClause;
+import org.apache.asterix.lang.common.clause.WhereClause;
+import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.FieldAccessor;
+import org.apache.asterix.lang.common.expression.FieldBinding;
+import org.apache.asterix.lang.common.expression.IfExpr;
+import org.apache.asterix.lang.common.expression.IndexAccessor;
+import org.apache.asterix.lang.common.expression.ListConstructor;
+import org.apache.asterix.lang.common.expression.LiteralExpr;
+import org.apache.asterix.lang.common.expression.OperatorExpr;
+import org.apache.asterix.lang.common.expression.QuantifiedExpression;
+import org.apache.asterix.lang.common.expression.RecordConstructor;
+import org.apache.asterix.lang.common.expression.UnaryExpr;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.common.struct.QuantifiedPair;
+import org.apache.asterix.lang.sqlpp.clause.FromClause;
+import org.apache.asterix.lang.sqlpp.clause.FromTerm;
+import org.apache.asterix.lang.sqlpp.clause.HavingClause;
+import org.apache.asterix.lang.sqlpp.clause.JoinClause;
+import org.apache.asterix.lang.sqlpp.clause.NestClause;
+import org.apache.asterix.lang.sqlpp.clause.Projection;
+import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
+import org.apache.asterix.lang.sqlpp.clause.SelectClause;
+import org.apache.asterix.lang.sqlpp.clause.SelectElement;
+import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
+import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
+import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
+import org.apache.asterix.lang.sqlpp.expression.CaseExpression;
+import org.apache.asterix.lang.sqlpp.expression.IndependentSubquery;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil;
+import
org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
+
+public class CheckDatasetOnlyResolutionVisitor extends
AbstractSqlppQueryExpressionVisitor<Boolean, ILangExpression> {
+
+ @Override
+ public Boolean visit(Query q, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(FunctionDecl fd, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(LiteralExpr l, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(VariableExpr v, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(ListConstructor lc, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(RecordConstructor rc, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(OperatorExpr ifbo, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(FieldAccessor fa, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(IndexAccessor ia, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(IfExpr ifexpr, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(QuantifiedExpression qe, ILangExpression expr) throws
AsterixException {
+ for (QuantifiedPair qp : qe.getQuantifiedList()) {
+ // If the target reference of undefined variable is a binding
expression in a quantified pair,
+ // then we only resolve it to dataset.
+ if (expr == qp.getExpr()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Boolean visit(UnaryExpr u, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(CallExpr pf, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(LetClause lc, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(WhereClause wc, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(OrderbyClause oc, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(GroupbyClause gc, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(LimitClause lc, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(FromClause fromClause, ILangExpression expr) throws
AsterixException {
+ return true;
+ }
+
+ @Override
+ public Boolean visit(FromTerm fromTerm, ILangExpression expr) throws
AsterixException {
+ return true;
+ }
+
+ @Override
+ public Boolean visit(JoinClause joinClause, ILangExpression expr) throws
AsterixException {
+ return expr == joinClause.getRightExpression();
+ }
+
+ @Override
+ public Boolean visit(NestClause nestClause, ILangExpression expr) throws
AsterixException {
+ return expr == nestClause.getRightExpression();
+ }
+
+ @Override
+ public Boolean visit(Projection projection, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(SelectBlock selectBlock, ILangExpression expr) throws
AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(SelectClause selectClause, ILangExpression expr)
throws AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(SelectElement selectElement, ILangExpression expr)
throws AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(SelectRegular selectRegular, ILangExpression expr)
throws AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(SelectSetOperation selectSetOperation,
ILangExpression expr) throws AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(SelectExpression selectStatement, ILangExpression
expr) throws AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(UnnestClause unnestClause, ILangExpression expr)
throws AsterixException {
+ return true;
+ }
+
+ @Override
+ public Boolean visit(HavingClause havingClause, ILangExpression expr)
throws AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(IndependentSubquery independentSubquery,
ILangExpression arg) throws AsterixException {
+ return false;
+ }
+
+ @Override
+ public Boolean visit(CaseExpression caseExpr, ILangExpression arg) throws
AsterixException {
+ return false;
+ }
+}
--
To view, visit https://asterix-gerrit.ics.uci.edu/1408
To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I24e4c1b38e53c97380cfb3e2e9b61cdd05fe7002
Gerrit-PatchSet: 1
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Owner: Yingyi Bu <[email protected]>