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]>

Reply via email to