Return-Path: X-Original-To: apmail-asterixdb-commits-archive@minotaur.apache.org Delivered-To: apmail-asterixdb-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 23787198F5 for ; Tue, 5 Apr 2016 03:40:52 +0000 (UTC) Received: (qmail 74954 invoked by uid 500); 5 Apr 2016 03:40:52 -0000 Delivered-To: apmail-asterixdb-commits-archive@asterixdb.apache.org Received: (qmail 74914 invoked by uid 500); 5 Apr 2016 03:40:52 -0000 Mailing-List: contact commits-help@asterixdb.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@asterixdb.incubator.apache.org Delivered-To: mailing list commits@asterixdb.incubator.apache.org Received: (qmail 74905 invoked by uid 99); 5 Apr 2016 03:40:52 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 05 Apr 2016 03:40:51 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 706501A0661 for ; Tue, 5 Apr 2016 03:40:51 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -3.221 X-Spam-Level: X-Spam-Status: No, score=-3.221 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001] autolearn=disabled Received: from mx2-lw-us.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id e1S7BEPVBDOb for ; Tue, 5 Apr 2016 03:40:42 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx2-lw-us.apache.org (ASF Mail Server at mx2-lw-us.apache.org) with SMTP id 723715FB27 for ; Tue, 5 Apr 2016 03:40:41 +0000 (UTC) Received: (qmail 74607 invoked by uid 99); 5 Apr 2016 03:40:40 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 05 Apr 2016 03:40:40 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 3BF0DE0415; Tue, 5 Apr 2016 03:40:40 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: buyingyi@apache.org To: commits@asterixdb.incubator.apache.org Date: Tue, 05 Apr 2016 03:40:42 -0000 Message-Id: <7abf556d7e4e46a195ce11e18f2cca7e@git.apache.org> In-Reply-To: <27ef614162c746b0ac6fc9dcc0ea73c2@git.apache.org> References: <27ef614162c746b0ac6fc9dcc0ea73c2@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [03/10] incubator-asterixdb git commit: Rewrite SQL++ functions. http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java new file mode 100644 index 0000000..e7832bb --- /dev/null +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java @@ -0,0 +1,237 @@ +/* + * 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.rewrites.visitor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.asterix.common.exceptions.AsterixException; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.IRewriterFactory; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.rewrites.LangRewritingContext; +import org.apache.asterix.lang.common.statement.FunctionDecl; +import org.apache.asterix.lang.common.visitor.AbstractInlineUdfsVisitor; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +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.SelectExpression; +import org.apache.asterix.lang.sqlpp.struct.SetOperationRight; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableSubstitutionUtil; +import org.apache.asterix.lang.sqlpp.visitor.SqlppCloneAndSubstituteVariablesVisitor; +import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor; +import org.apache.asterix.metadata.declared.AqlMetadataProvider; +import org.apache.hyracks.algebricks.common.utils.Pair; + +public class SqlppInlineUdfsVisitor extends AbstractInlineUdfsVisitor + implements ISqlppVisitor> { + + /** + * @param context, + * manages ids of variables and guarantees uniqueness of variables. + * @param rewriterFactory, + * a rewrite factory for rewriting user-defined functions. + * @param declaredFunctions, + * a list of declared functions associated with the query. + * @param metadataProvider, + * providing the definition of created (i.e., stored) user-defined functions. + */ + public SqlppInlineUdfsVisitor(LangRewritingContext context, IRewriterFactory rewriterFactory, + List declaredFunctions, AqlMetadataProvider metadataProvider) { + super(context, rewriterFactory, declaredFunctions, metadataProvider, + new SqlppCloneAndSubstituteVariablesVisitor(context)); + } + + @Override + protected Expression generateQueryExpression(List letClauses, Expression returnExpr) + throws AsterixException { + Map varExprMap = extractLetBindingVariableExpressionMappings(letClauses); + Expression inlinedReturnExpr = (Expression) SqlppVariableSubstitutionUtil + .substituteVariableWithoutContext(returnExpr, varExprMap); + return inlinedReturnExpr; + } + + @Override + public Boolean visit(FromClause fromClause, List func) throws AsterixException { + boolean changed = false; + for (FromTerm fromTerm : fromClause.getFromTerms()) { + changed |= fromTerm.accept(this, func); + } + return changed; + } + + @Override + public Boolean visit(FromTerm fromTerm, List func) throws AsterixException { + boolean changed = false; + Pair p = inlineUdfsInExpr(fromTerm.getLeftExpression(), func); + fromTerm.setLeftExpression(p.second); + changed |= p.first; + for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) { + changed |= correlateClause.accept(this, func); + } + return changed; + } + + @Override + public Boolean visit(JoinClause joinClause, List funcs) throws AsterixException { + Pair p1 = inlineUdfsInExpr(joinClause.getRightExpression(), funcs); + joinClause.setRightExpression(p1.second); + Pair p2 = inlineUdfsInExpr(joinClause.getConditionExpression(), funcs); + joinClause.setConditionExpression(p2.second); + return p1.first || p2.first; + } + + @Override + public Boolean visit(NestClause nestClause, List funcs) throws AsterixException { + Pair p1 = inlineUdfsInExpr(nestClause.getRightExpression(), funcs); + nestClause.setRightExpression(p1.second); + Pair p2 = inlineUdfsInExpr(nestClause.getConditionExpression(), funcs); + nestClause.setConditionExpression(p2.second); + return p1.first || p2.first; + } + + @Override + public Boolean visit(Projection projection, List funcs) throws AsterixException { + Pair p = inlineUdfsInExpr(projection.getExpression(), funcs); + projection.setExpression(p.second); + return p.first; + } + + @Override + public Boolean visit(SelectBlock selectBlock, List funcs) throws AsterixException { + boolean changed = false; + if (selectBlock.hasFromClause()) { + changed |= selectBlock.getFromClause().accept(this, funcs); + } + if (selectBlock.hasLetClauses()) { + for (LetClause letClause : selectBlock.getLetList()) { + changed |= letClause.accept(this, funcs); + } + } + if (selectBlock.hasWhereClause()) { + changed |= selectBlock.getWhereClause().accept(this, funcs); + } + if (selectBlock.hasGroupbyClause()) { + changed |= selectBlock.getGroupbyClause().accept(this, funcs); + } + if (selectBlock.hasLetClausesAfterGroupby()) { + for (LetClause letClause : selectBlock.getLetListAfterGroupby()) { + changed |= letClause.accept(this, funcs); + } + } + if (selectBlock.hasHavingClause()) { + changed |= selectBlock.getHavingClause().accept(this, funcs); + } + changed |= selectBlock.getSelectClause().accept(this, funcs); + return changed; + } + + @Override + public Boolean visit(SelectClause selectClause, List funcs) throws AsterixException { + boolean changed = false; + if (selectClause.selectElement()) { + changed |= selectClause.getSelectElement().accept(this, funcs); + } else { + changed |= selectClause.getSelectRegular().accept(this, funcs); + } + return changed; + } + + @Override + public Boolean visit(SelectElement selectElement, List funcs) throws AsterixException { + Pair p = inlineUdfsInExpr(selectElement.getExpression(), funcs); + selectElement.setExpression(p.second); + return p.first; + } + + @Override + public Boolean visit(SelectRegular selectRegular, List funcs) throws AsterixException { + boolean changed = false; + for (Projection projection : selectRegular.getProjections()) { + changed |= projection.accept(this, funcs); + } + return changed; + } + + @Override + public Boolean visit(SelectSetOperation selectSetOperation, List funcs) throws AsterixException { + boolean changed = false; + changed |= selectSetOperation.getLeftInput().accept(this, funcs); + for (SetOperationRight right : selectSetOperation.getRightInputs()) { + changed |= right.getSetOperationRightInput().accept(this, funcs); + } + return changed; + } + + @Override + public Boolean visit(SelectExpression selectExpression, List funcs) throws AsterixException { + boolean changed = false; + if (selectExpression.hasLetClauses()) { + for (LetClause letClause : selectExpression.getLetList()) { + changed |= letClause.accept(this, funcs); + } + } + changed |= selectExpression.getSelectSetOperation().accept(this, funcs); + if (selectExpression.hasOrderby()) { + changed |= selectExpression.getOrderbyClause().accept(this, funcs); + } + if (selectExpression.hasLimit()) { + changed |= selectExpression.getLimitClause().accept(this, funcs); + } + return changed; + } + + @Override + public Boolean visit(UnnestClause unnestClause, List funcs) throws AsterixException { + Pair p = inlineUdfsInExpr(unnestClause.getRightExpression(), funcs); + unnestClause.setRightExpression(p.second); + return p.first; + } + + @Override + public Boolean visit(HavingClause havingClause, List funcs) throws AsterixException { + Pair p = inlineUdfsInExpr(havingClause.getFilterExpression(), funcs); + havingClause.setFilterExpression(p.second); + return p.first; + } + + private Map extractLetBindingVariableExpressionMappings(List letClauses) + throws AsterixException { + Map varExprMap = new HashMap(); + for (LetClause lc : letClauses) { + // inline let variables one by one iteratively. + lc.setBindingExpr((Expression) SqlppVariableSubstitutionUtil + .substituteVariableWithoutContext(lc.getBindingExpr(), varExprMap)); + varExprMap.put(lc.getVarExpr(), lc.getBindingExpr()); + } + return varExprMap; + } +} http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java new file mode 100644 index 0000000..5ca2533 --- /dev/null +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.lang.sqlpp.rewrites.visitor; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.asterix.common.config.MetadataConstants; +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.expression.CallExpr; +import org.apache.asterix.lang.common.expression.LiteralExpr; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.literal.StringLiteral; +import org.apache.asterix.lang.common.rewrites.LangRewritingContext; +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.base.AbstractSqlppExpressionScopingVisitor; +import org.apache.asterix.metadata.declared.AqlMetadataProvider; + +public class VariableCheckAndRewriteVisitor extends AbstractSqlppExpressionScopingVisitor { + + protected final boolean overwrite; + protected final AqlMetadataProvider metadataProvider; + + /** + * @param context, + * manages ids of variables and guarantees uniqueness of variables. + * @param overwrite, + * whether rewrite unbounded variables to dataset function calls. + * This flag can only be true for rewriting a top-level query. + * It should be false for rewriting the body expression of a user-defined function. + */ + public VariableCheckAndRewriteVisitor(LangRewritingContext context, boolean overwrite, + AqlMetadataProvider metadataProvider) { + super(context); + this.overwrite = overwrite; + this.metadataProvider = metadataProvider; + } + + @Override + public Expression visit(VariableExpr varExpr, Expression arg) throws AsterixException { + String varName = varExpr.getVar().getValue(); + if (scopeChecker.isInForbiddenScopes(varName)) { + throw new AsterixException( + "Inside limit clauses, it is disallowed to reference a variable having the same name as any variable bound in the same scope as the limit clause."); + } + if (rewriteNeeded(varExpr)) { + return datasetRewrite(varExpr); + } else { + return varExpr; + } + } + + // Whether a rewrite is needed for a variable reference expression. + private boolean rewriteNeeded(VariableExpr varExpr) throws AsterixException { + String varName = varExpr.getVar().getValue(); + Identifier ident = scopeChecker.lookupSymbol(varName); + if (ident != null) { + // Exists such an identifier + varExpr.setIsNewVar(false); + varExpr.setVar((VarIdentifier) ident); + return false; + } else { + // Meets a undefined variable + return true; + } + } + + // Rewrites for global variable (e.g., dataset) references. + private Expression datasetRewrite(VariableExpr expr) throws AsterixException { + if (!overwrite) { + return expr; + } + String funcName = "dataset"; + String dataverse = MetadataConstants.METADATA_DATAVERSE_NAME; + FunctionSignature signature = new FunctionSignature(dataverse, funcName, 1); + List argList = new ArrayList(); + //Ignore the parser-generated prefix "$" for a dataset. + String dataset = SqlppVariableUtil.toUserDefinedVariableName(expr.getVar()).getValue(); + argList.add(new LiteralExpr(new StringLiteral(dataset))); + return new CallExpr(signature, argList); + } +} http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java new file mode 100644 index 0000000..cbf05b5 --- /dev/null +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java @@ -0,0 +1,150 @@ +/* + * 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.util; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.asterix.common.exceptions.AsterixException; +import org.apache.asterix.common.functions.FunctionConstants; +import org.apache.asterix.common.functions.FunctionSignature; +import org.apache.asterix.lang.common.util.FunctionUtil; +import org.apache.asterix.om.functions.AsterixBuiltinFunctions; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo; + +public class FunctionMapUtil { + + private final static String CORE_AGGREGATE_PREFIX = "coll_"; + + // Maps from a SQL function name to an AQL function name (i.e., AsterixDB internal name). + private static final Map FUNCTION_NAME_MAP = new HashMap<>(); + + static { + FUNCTION_NAME_MAP.put("ceil", "ceiling"); //SQL: ceil, AQL: ceiling + FUNCTION_NAME_MAP.put("length", "string-length"); // SQL: length, AQL: string-length + FUNCTION_NAME_MAP.put("lower", "lowercase"); // SQL: lower, AQL: lowercase + FUNCTION_NAME_MAP.put("substr", "substring"); // SQL: substr, AQL: substring + FUNCTION_NAME_MAP.put("upper", "uppercase"); //SQL: upper, AQL: uppercase + } + + /** + * Whether a function signature is a SQL-92 core aggregate function. + * + * @param fs, + * the function signature. + * @return true if the function signature is a SQL-92 core aggregate, + * false otherwise. + */ + public static boolean isSql92AggregateFunction(FunctionSignature signature) throws AsterixException { + IFunctionInfo finfo = FunctionUtil.getFunctionInfo(new FunctionIdentifier(FunctionConstants.ASTERIX_NS, + signature.getName().toLowerCase(), signature.getArity())); + if (finfo == null) { + return false; + } + return AsterixBuiltinFunctions.getAggregateFunction(finfo.getFunctionIdentifier()) != null; + } + + /** + * Whether a function signature is a SQL++ core aggregate function. + * + * @param fs, + * the function signature. + * @return true if the function signature is a SQL++ core aggregate, + * false otherwise. + */ + public static boolean isCoreAggregateFunction(FunctionSignature fs) { + String name = fs.getName().toLowerCase(); + if (!name.startsWith(CORE_AGGREGATE_PREFIX)) { + return false; + } + IFunctionInfo finfo = FunctionUtil.getFunctionInfo(new FunctionIdentifier(FunctionConstants.ASTERIX_NS, + name.substring(CORE_AGGREGATE_PREFIX.length()), fs.getArity())); + if (finfo == null) { + return false; + } + return AsterixBuiltinFunctions.getAggregateFunction(finfo.getFunctionIdentifier()) != null; + } + + /** + * Get the corresponding SQL++ core aggregate function from the SQL-92 aggregate function. + * + * @param fs, + * the SQL-92 aggregate function signature. + * @return the SQL++ aggregate function signature. + * @throws AsterixException + */ + public static FunctionSignature sql92ToCoreAggregateFunction(FunctionSignature fs) throws AsterixException { + if (!isSql92AggregateFunction(fs)) { + return fs; + } + return new FunctionSignature(fs.getNamespace(), CORE_AGGREGATE_PREFIX + fs.getName(), fs.getArity()); + } + + /** + * Maps a user invoked function signature to a system internal function signature. + * + * @param fs, + * the user typed function. + * @return the system internal function. + */ + public static FunctionSignature normalizeBuiltinFunctionSignature(FunctionSignature fs, boolean checkSql92Aggregate) + throws AsterixException { + String mappedName = internalizeBuiltinScalarFunctionName(fs.getName()); + if (isCoreAggregateFunction(fs)) { + mappedName = internalizeCoreAggregateFunctionName(mappedName); + } else if (checkSql92Aggregate && isSql92AggregateFunction(fs)) { + throw new AsterixException(fs.getName() + + " is a SQL-92 aggregate function. The SQL++ core aggregate function " + CORE_AGGREGATE_PREFIX + + fs.getName().toLowerCase() + " could potentially express the intent."); + } + return new FunctionSignature(fs.getNamespace(), mappedName, fs.getArity()); + } + + /** + * Removes the "coll_" prefix for user-facing SQL++ core aggregate function names. + * + * @param name, + * the name of a user-facing SQL++ core aggregate function name. + * @return the AsterixDB internal function name for the aggregate function. + * @throws AsterixException + */ + private static String internalizeCoreAggregateFunctionName(String name) throws AsterixException { + String lowerCaseName = name.toLowerCase(); + return lowerCaseName.substring(CORE_AGGREGATE_PREFIX.length()); + } + + /** + * Note: function name normalization can ONLY be called + * after all user-defined functions (by either "DECLARE FUNCTION" or "CREATE FUNCTION") + * are inlined, because user-defined function names are case-sensitive. + * + * @param name + * the user-input function name in the query. + * @return the mapped internal name. + */ + private static String internalizeBuiltinScalarFunctionName(String name) { + String lowerCaseName = name.toLowerCase(); + String mappedName = FUNCTION_NAME_MAP.get(lowerCaseName); + if (mappedName != null) { + return mappedName; + } + return lowerCaseName; + } +} http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java index 0f8488a..6c737d6 100644 --- a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java @@ -27,8 +27,9 @@ import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.rewrites.LangRewritingContext; -import org.apache.asterix.lang.sqlpp.visitor.SqlppGroupBySugarVisitor; -import org.apache.asterix.lang.sqlpp.visitor.UsedVariableVisitor; +import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupBySugarVisitor; +import org.apache.asterix.lang.sqlpp.visitor.DeepCopyVisitor; +import org.apache.asterix.lang.sqlpp.visitor.FreeVariableVisitor; public class SqlppRewriteUtil { @@ -36,15 +37,23 @@ public class SqlppRewriteUtil { public static Expression rewriteExpressionUsingGroupVariable(VariableExpr groupVar, Collection targetVarList, ILangExpression expr, LangRewritingContext context) throws AsterixException { - SqlppGroupBySugarVisitor visitor = new SqlppGroupBySugarVisitor(context, null, groupVar, targetVarList); + SqlppGroupBySugarVisitor visitor = new SqlppGroupBySugarVisitor(context, groupVar, targetVarList); return expr.accept(visitor, null); } - public static Set getUsedVariable(Expression expr) throws AsterixException { + public static Set getFreeVariable(Expression expr) throws AsterixException { Set vars = new HashSet<>(); - UsedVariableVisitor visitor = new UsedVariableVisitor(); + FreeVariableVisitor visitor = new FreeVariableVisitor(); expr.accept(visitor, vars); return vars; } + public static ILangExpression deepCopy(ILangExpression expr) throws AsterixException { + if (expr == null) { + return expr; + } + DeepCopyVisitor visitor = new DeepCopyVisitor(); + return expr.accept(visitor, null); + } + } http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java index 8a63aa5..59e9389 100644 --- a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java @@ -18,7 +18,22 @@ */ package org.apache.asterix.lang.sqlpp.util; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.asterix.common.exceptions.AsterixException; +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.expression.GbyVariableExpressionPair; +import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.struct.VarIdentifier; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.FromClause; +import org.apache.asterix.lang.sqlpp.clause.FromTerm; +import org.apache.asterix.lang.sqlpp.visitor.FreeVariableVisitor; public class SqlppVariableUtil { @@ -44,4 +59,73 @@ public class SqlppVariableUtil { return new VarIdentifier(USER_VAR_PREFIX + idName); } + public static Collection getFreeVariables(ILangExpression langExpr) throws AsterixException { + Collection freeVars = new HashSet<>(); + FreeVariableVisitor visitor = new FreeVariableVisitor(); + langExpr.accept(visitor, freeVars); + return freeVars; + } + + public static Collection getBindingVariables(FromClause fromClause) { + Set bindingVars = new HashSet<>(); + if (fromClause == null) { + return bindingVars; + } + for (FromTerm fromTerm : fromClause.getFromTerms()) { + bindingVars.addAll(getBindingVariables(fromTerm)); + } + return bindingVars; + } + + public static Collection getBindingVariables(FromTerm fromTerm) { + Set bindingVars = new HashSet<>(); + if (fromTerm == null) { + return bindingVars; + } + bindingVars.add(fromTerm.getLeftVariable()); + if (fromTerm.hasPositionalVariable()) { + bindingVars.add(fromTerm.getPositionalVariable()); + } + for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) { + bindingVars.add(correlateClause.getRightVariable()); + if (correlateClause.hasPositionalVariable()) { + bindingVars.add(correlateClause.getPositionalVariable()); + } + } + return bindingVars; + } + + public static Collection getBindingVariables(GroupbyClause gbyClause) { + Set bindingVars = new HashSet<>(); + if (gbyClause == null) { + return bindingVars; + } + for (GbyVariableExpressionPair gbyKey : gbyClause.getGbyPairList()) { + VariableExpr var = gbyKey.getVar(); + if (var != null) { + bindingVars.add(var); + } + } + for (GbyVariableExpressionPair gbyKey : gbyClause.getDecorPairList()) { + VariableExpr var = gbyKey.getVar(); + if (var != null) { + bindingVars.add(var); + } + } + bindingVars.addAll(gbyClause.getWithVarList()); + bindingVars.add(gbyClause.getGroupVar()); + return bindingVars; + } + + public static Collection getBindingVariables(List letClauses) { + Set bindingVars = new HashSet<>(); + if (letClauses == null || letClauses.isEmpty()) { + return bindingVars; + } + for (LetClause letClause : letClauses) { + bindingVars.add(letClause.getVarExpr()); + } + return bindingVars; + } + } http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java new file mode 100644 index 0000000..1bca7ac --- /dev/null +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java @@ -0,0 +1,265 @@ +/* + * 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.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.SelectExpression; +import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil; +import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor; + +/** + * This visitor checks if a language construct contains SQL-92 aggregates. + */ +public class CheckSql92AggregateVisitor extends AbstractSqlppQueryExpressionVisitor { + + @Override + public Boolean visit(Query q, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(FunctionDecl fd, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(LiteralExpr l, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(VariableExpr v, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(ListConstructor lc, ILangExpression parentSelectBlock) throws AsterixException { + return visitExprList(lc.getExprList(), parentSelectBlock); + } + + @Override + public Boolean visit(RecordConstructor rc, ILangExpression parentSelectBlock) throws AsterixException { + for (FieldBinding fieldBinding : rc.getFbList()) { + ILangExpression leftExpr = fieldBinding.getLeftExpr(); + ILangExpression rightExpr = fieldBinding.getRightExpr(); + if (leftExpr.accept(this, parentSelectBlock)) { + return true; + } + if (rightExpr.accept(this, parentSelectBlock)) { + return true; + } + } + return false; + } + + @Override + public Boolean visit(OperatorExpr ifbo, ILangExpression parentSelectBlock) throws AsterixException { + return visitExprList(ifbo.getExprList(), parentSelectBlock); + } + + @Override + public Boolean visit(FieldAccessor fa, ILangExpression parentSelectBlock) throws AsterixException { + return fa.getExpr().accept(this, parentSelectBlock); + } + + @Override + public Boolean visit(IndexAccessor ia, ILangExpression parentSelectBlock) throws AsterixException { + return ia.getExpr().accept(this, parentSelectBlock); + } + + @Override + public Boolean visit(IfExpr ifexpr, ILangExpression parentSelectBlock) throws AsterixException { + if (ifexpr.getCondExpr().accept(this, parentSelectBlock)) { + return true; + } else { + return ifexpr.getThenExpr().accept(this, parentSelectBlock) + || ifexpr.getElseExpr().accept(this, parentSelectBlock); + } + } + + @Override + public Boolean visit(QuantifiedExpression qe, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(UnaryExpr u, ILangExpression parentSelectBlock) throws AsterixException { + return u.getExpr().accept(this, parentSelectBlock); + } + + @Override + public Boolean visit(CallExpr pf, ILangExpression parentSelectBlock) throws AsterixException { + FunctionSignature fs = pf.getFunctionSignature(); + if (FunctionMapUtil.isSql92AggregateFunction(fs)) { + return true; + } + for (Expression parameter : pf.getExprList()) { + if (parameter.accept(this, parentSelectBlock)) { + return true; + } + } + return false; + } + + @Override + public Boolean visit(LetClause lc, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(WhereClause wc, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(OrderbyClause oc, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(GroupbyClause gc, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(LimitClause lc, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(FromClause fromClause, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(FromTerm fromTerm, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(JoinClause joinClause, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(NestClause nestClause, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(Projection projection, ILangExpression parentSelectBlock) throws AsterixException { + return projection.getExpression().accept(this, parentSelectBlock); + } + + @Override + public Boolean visit(SelectBlock selectBlock, ILangExpression parentSelectBlock) throws AsterixException { + return selectBlock.getSelectClause().accept(this, selectBlock); + } + + @Override + public Boolean visit(SelectClause selectClause, ILangExpression parentSelectBlock) throws AsterixException { + if (selectClause.selectElement()) { + return selectClause.getSelectElement().accept(this, parentSelectBlock); + } else { + return selectClause.getSelectRegular().accept(this, parentSelectBlock); + } + } + + @Override + public Boolean visit(SelectElement selectElement, ILangExpression parentSelectBlock) throws AsterixException { + return selectElement.getExpression().accept(this, parentSelectBlock); + } + + @Override + public Boolean visit(SelectRegular selectRegular, ILangExpression parentSelectBlock) throws AsterixException { + for (Projection projection : selectRegular.getProjections()) { + if (projection.accept(this, parentSelectBlock)) { + return true; + } + } + return false; + } + + @Override + public Boolean visit(SelectSetOperation selectSetOperation, ILangExpression parentSelectBlock) + throws AsterixException { + return false; + } + + @Override + public Boolean visit(SelectExpression selectStatement, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(UnnestClause unnestClause, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + @Override + public Boolean visit(HavingClause havingClause, ILangExpression parentSelectBlock) throws AsterixException { + return false; + } + + private Boolean visitExprList(List exprs, ILangExpression parentSelectBlock) throws AsterixException { + for (Expression item : exprs) { + if (item.accept(this, parentSelectBlock)) { + return true; + } + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java new file mode 100644 index 0000000..2d891e0 --- /dev/null +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java @@ -0,0 +1,415 @@ +/* + * 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.ArrayList; +import java.util.List; + +import org.apache.asterix.common.exceptions.AsterixException; +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.GbyVariableExpressionPair; +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.Identifier; +import org.apache.asterix.lang.common.struct.QuantifiedPair; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +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.SelectExpression; +import org.apache.asterix.lang.sqlpp.struct.SetOperationInput; +import org.apache.asterix.lang.sqlpp.struct.SetOperationRight; +import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor; +import org.apache.hyracks.algebricks.common.utils.Pair; + +public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor { + + @Override + public FromClause visit(FromClause fromClause, Void arg) throws AsterixException { + List fromTerms = new ArrayList<>(); + for (FromTerm fromTerm : fromClause.getFromTerms()) { + fromTerms.add((FromTerm) fromTerm.accept(this, arg)); + } + return new FromClause(fromTerms); + } + + @Override + public FromTerm visit(FromTerm fromTerm, Void arg) throws AsterixException { + // Visit the left expression of a from term. + Expression fromExpr = (Expression) fromTerm.getLeftExpression().accept(this, arg); + VariableExpr fromVar = (VariableExpr) fromTerm.getLeftVariable().accept(this, arg); + VariableExpr positionVar = fromTerm.getPositionalVariable() == null ? null + : (VariableExpr) fromTerm.getPositionalVariable().accept(this, arg); + + // Visits join/unnest/nest clauses. + List correlateClauses = new ArrayList<>(); + for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) { + correlateClauses.add((AbstractBinaryCorrelateClause) correlateClause.accept(this, arg)); + } + return new FromTerm(fromExpr, fromVar, positionVar, correlateClauses); + } + + @Override + public JoinClause visit(JoinClause joinClause, Void arg) throws AsterixException { + Expression rightExpression = (Expression) joinClause.getRightExpression().accept(this, arg); + VariableExpr rightVar = (VariableExpr) joinClause.getRightVariable().accept(this, arg); + VariableExpr rightPositionVar = joinClause.getPositionalVariable() == null ? null + : (VariableExpr) joinClause.getPositionalVariable().accept(this, arg); + Expression conditionExpresion = (Expression) joinClause.getConditionExpression().accept(this, arg); + return new JoinClause(joinClause.getJoinType(), rightExpression, rightVar, rightPositionVar, + conditionExpresion); + } + + @Override + public NestClause visit(NestClause nestClause, Void arg) throws AsterixException { + Expression rightExpression = (Expression) nestClause.getRightExpression().accept(this, arg); + VariableExpr rightVar = (VariableExpr) nestClause.getRightVariable().accept(this, arg); + VariableExpr rightPositionVar = nestClause.getPositionalVariable() == null ? null + : (VariableExpr) nestClause.getPositionalVariable().accept(this, arg); + Expression conditionExpresion = (Expression) nestClause.getConditionExpression().accept(this, arg); + return new NestClause(nestClause.getJoinType(), rightExpression, rightVar, rightPositionVar, + conditionExpresion); + } + + @Override + public UnnestClause visit(UnnestClause unnestClause, Void arg) throws AsterixException { + Expression rightExpression = (Expression) unnestClause.getRightExpression().accept(this, arg); + VariableExpr rightVar = (VariableExpr) unnestClause.getRightVariable().accept(this, arg); + VariableExpr rightPositionVar = unnestClause.getPositionalVariable() == null ? null + : (VariableExpr) unnestClause.getPositionalVariable().accept(this, arg); + return new UnnestClause(unnestClause.getJoinType(), rightExpression, rightVar, rightPositionVar); + } + + @Override + public Projection visit(Projection projection, Void arg) throws AsterixException { + return new Projection((Expression) projection.getExpression().accept(this, arg), projection.getName(), + projection.star(), projection.exprStar()); + } + + @Override + public SelectBlock visit(SelectBlock selectBlock, Void arg) throws AsterixException { + FromClause fromClause = null; + List letClauses = new ArrayList<>(); + WhereClause whereClause = null; + GroupbyClause gbyClause = null; + List gbyLetClauses = new ArrayList<>(); + HavingClause havingClause = null; + SelectClause selectCluase = null; + // Traverses the select block in the order of "from", "let"s, "where", + // "group by", "let"s, "having" and "select". + if (selectBlock.hasFromClause()) { + fromClause = (FromClause) selectBlock.getFromClause().accept(this, arg); + } + if (selectBlock.hasLetClauses()) { + List letList = selectBlock.getLetList(); + for (LetClause letClause : letList) { + letClauses.add((LetClause) letClause.accept(this, arg)); + } + } + if (selectBlock.hasWhereClause()) { + whereClause = (WhereClause) selectBlock.getWhereClause().accept(this, arg); + } + if (selectBlock.hasGroupbyClause()) { + gbyClause = (GroupbyClause) selectBlock.getGroupbyClause().accept(this, arg); + } + if (selectBlock.hasLetClausesAfterGroupby()) { + List letListAfterGby = selectBlock.getLetListAfterGroupby(); + for (LetClause letClauseAfterGby : letListAfterGby) { + gbyLetClauses.add((LetClause) letClauseAfterGby.accept(this, arg)); + } + } + if (selectBlock.hasHavingClause()) { + havingClause = (HavingClause) selectBlock.getHavingClause().accept(this, arg); + } + selectCluase = (SelectClause) selectBlock.getSelectClause().accept(this, arg); + return new SelectBlock(selectCluase, fromClause, letClauses, whereClause, gbyClause, gbyLetClauses, + havingClause); + } + + @Override + public SelectClause visit(SelectClause selectClause, Void arg) throws AsterixException { + SelectElement selectElement = null; + SelectRegular selectRegular = null; + if (selectClause.selectElement()) { + selectElement = (SelectElement) selectClause.getSelectElement().accept(this, arg); + } + if (selectClause.selectRegular()) { + selectRegular = (SelectRegular) selectClause.getSelectRegular().accept(this, arg); + } + return new SelectClause(selectElement, selectRegular, selectClause.distinct()); + } + + @Override + public SelectElement visit(SelectElement selectElement, Void arg) throws AsterixException { + return new SelectElement((Expression) selectElement.getExpression().accept(this, arg)); + } + + @Override + public SelectRegular visit(SelectRegular selectRegular, Void arg) throws AsterixException { + List projections = new ArrayList<>(); + for (Projection projection : selectRegular.getProjections()) { + projections.add((Projection) projection.accept(this, arg)); + } + return new SelectRegular(projections); + } + + @Override + public SelectSetOperation visit(SelectSetOperation selectSetOperation, Void arg) throws AsterixException { + SetOperationInput leftInput = selectSetOperation.getLeftInput(); + SetOperationInput newLeftInput = null; + if (leftInput.selectBlock()) { + newLeftInput = new SetOperationInput((SelectBlock) leftInput.accept(this, arg), null); + } else { + newLeftInput = new SetOperationInput(null, (SelectExpression) leftInput.accept(this, arg)); + } + List rightInputs = new ArrayList<>(); + for (SetOperationRight right : selectSetOperation.getRightInputs()) { + SetOperationInput newRightInput = null; + SetOperationInput setOpRightInput = right.getSetOperationRightInput(); + if (setOpRightInput.selectBlock()) { + newRightInput = new SetOperationInput((SelectBlock) leftInput.accept(this, arg), null); + } else { + newRightInput = new SetOperationInput(null, (SelectExpression) leftInput.accept(this, arg)); + } + rightInputs.add(new SetOperationRight(right.getSetOpType(), right.isSetSemantics(), newRightInput)); + } + return new SelectSetOperation(newLeftInput, rightInputs); + } + + @Override + public HavingClause visit(HavingClause havingClause, Void arg) throws AsterixException { + return new HavingClause((Expression) havingClause.getFilterExpression().accept(this, arg)); + } + + @Override + public Query visit(Query q, Void arg) throws AsterixException { + return new Query(q.isTopLevel(), (Expression) q.getBody().accept(this, arg), q.getVarCounter(), + q.getDataverses(), q.getDatasets()); + } + + @Override + public FunctionDecl visit(FunctionDecl fd, Void arg) throws AsterixException { + return new FunctionDecl(fd.getSignature(), fd.getParamList(), (Expression) fd.getFuncBody().accept(this, arg)); + } + + @Override + public WhereClause visit(WhereClause whereClause, Void arg) throws AsterixException { + return new WhereClause((Expression) whereClause.getWhereExpr().accept(this, arg)); + } + + @Override + public OrderbyClause visit(OrderbyClause oc, Void arg) throws AsterixException { + List newOrderbyList = new ArrayList(); + for (Expression orderExpr : oc.getOrderbyList()) { + newOrderbyList.add((Expression) orderExpr.accept(this, arg)); + } + return new OrderbyClause(newOrderbyList, oc.getModifierList()); + } + + @Override + public GroupbyClause visit(GroupbyClause gc, Void arg) throws AsterixException { + List gbyPairList = new ArrayList<>(); + List decorPairList = new ArrayList<>(); + List withVarList = new ArrayList<>(); + VariableExpr groupVarExpr = null; + List> groupFieldList = new ArrayList<>(); + for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) { + gbyPairList.add(new GbyVariableExpressionPair((VariableExpr) gbyVarExpr.getVar().accept(this, arg), + (Expression) gbyVarExpr.getExpr().accept(this, arg))); + } + for (GbyVariableExpressionPair gbyVarExpr : gc.getDecorPairList()) { + decorPairList.add(new GbyVariableExpressionPair((VariableExpr) gbyVarExpr.getVar().accept(this, arg), + (Expression) gbyVarExpr.getExpr().accept(this, arg))); + } + for (VariableExpr withVar : gc.getWithVarList()) { + withVarList.add((VariableExpr) withVar.accept(this, arg)); + } + if (gc.hasGroupVar()) { + groupVarExpr = (VariableExpr) gc.getGroupVar().accept(this, arg); + } + for (Pair field : gc.getGroupFieldList()) { + groupFieldList.add(new Pair<>((Expression) field.first.accept(this, arg), field.second)); + } + return new GroupbyClause(gbyPairList, decorPairList, withVarList, groupVarExpr, groupFieldList, + gc.hasHashGroupByHint(), gc.isGroupAll()); + } + + @Override + public LimitClause visit(LimitClause limitClause, Void arg) throws AsterixException { + Expression limitExpr = (Expression) limitClause.getLimitExpr().accept(this, arg); + Expression offsetExpr = limitClause.hasOffset() ? (Expression) limitClause.getOffset().accept(this, arg) : null; + return new LimitClause(limitExpr, offsetExpr); + } + + @Override + public LetClause visit(LetClause letClause, Void arg) throws AsterixException { + return new LetClause((VariableExpr) letClause.getVarExpr().accept(this, arg), + (Expression) letClause.getBindingExpr().accept(this, arg)); + } + + @Override + public SelectExpression visit(SelectExpression selectExpression, Void arg) throws AsterixException { + List lets = new ArrayList<>(); + SelectSetOperation select = null; + OrderbyClause orderby = null; + LimitClause limit = null; + + // visit let list + if (selectExpression.hasLetClauses()) { + for (LetClause letClause : selectExpression.getLetList()) { + lets.add((LetClause) letClause.accept(this, arg)); + } + } + + // visit the main select. + select = (SelectSetOperation) selectExpression.getSelectSetOperation().accept(this, arg); + + // visit order by + if (selectExpression.hasOrderby()) { + List orderExprs = new ArrayList<>(); + for (Expression orderExpr : selectExpression.getOrderbyClause().getOrderbyList()) { + orderExprs.add((Expression) orderExpr.accept(this, arg)); + } + orderby = new OrderbyClause(orderExprs, selectExpression.getOrderbyClause().getModifierList()); + } + + // visit limit + if (selectExpression.hasLimit()) { + limit = (LimitClause) selectExpression.getLimitClause().accept(this, arg); + } + return new SelectExpression(lets, select, orderby, limit, selectExpression.isSubquery()); + } + + @Override + public LiteralExpr visit(LiteralExpr l, Void arg) throws AsterixException { + return l; + } + + @Override + public ListConstructor visit(ListConstructor lc, Void arg) throws AsterixException { + List newExprList = new ArrayList(); + for (Expression expr : lc.getExprList()) { + newExprList.add((Expression) expr.accept(this, arg)); + } + return new ListConstructor(lc.getType(), newExprList); + } + + @Override + public RecordConstructor visit(RecordConstructor rc, Void arg) throws AsterixException { + List bindings = new ArrayList<>(); + for (FieldBinding binding : rc.getFbList()) { + FieldBinding fb = new FieldBinding((Expression) binding.getLeftExpr().accept(this, arg), + (Expression) binding.getRightExpr().accept(this, arg)); + bindings.add(fb); + } + return new RecordConstructor(bindings); + } + + @Override + public OperatorExpr visit(OperatorExpr operatorExpr, Void arg) throws AsterixException { + List newExprList = new ArrayList(); + for (Expression expr : operatorExpr.getExprList()) { + newExprList.add((Expression) expr.accept(this, arg)); + } + return new OperatorExpr(newExprList, operatorExpr.getExprBroadcastIdx(), operatorExpr.getOpList(), + operatorExpr.isCurrentop()); + } + + @Override + public IfExpr visit(IfExpr ifExpr, Void arg) throws AsterixException { + Expression conditionExpr = (Expression) ifExpr.getCondExpr().accept(this, arg); + Expression thenExpr = (Expression) ifExpr.getThenExpr().accept(this, arg); + Expression elseExpr = (Expression) ifExpr.getElseExpr().accept(this, arg); + return new IfExpr(conditionExpr, thenExpr, elseExpr); + } + + @Override + public QuantifiedExpression visit(QuantifiedExpression qe, Void arg) throws AsterixException { + List quantifiedPairs = new ArrayList<>(); + for (QuantifiedPair pair : qe.getQuantifiedList()) { + Expression expr = (Expression) pair.getExpr().accept(this, arg); + VariableExpr var = (VariableExpr) pair.getVarExpr().accept(this, arg); + quantifiedPairs.add(new QuantifiedPair(var, expr)); + } + Expression condition = (Expression) qe.getSatisfiesExpr().accept(this, arg); + return new QuantifiedExpression(qe.getQuantifier(), quantifiedPairs, condition); + } + + @Override + public CallExpr visit(CallExpr callExpr, Void arg) throws AsterixException { + List newExprList = new ArrayList(); + for (Expression expr : callExpr.getExprList()) { + newExprList.add((Expression) expr.accept(this, arg)); + } + return new CallExpr(callExpr.getFunctionSignature(), newExprList); + } + + @Override + public VariableExpr visit(VariableExpr varExpr, Void arg) throws AsterixException { + return new VariableExpr(varExpr.getVar()); + } + + @Override + public UnaryExpr visit(UnaryExpr u, Void arg) throws AsterixException { + return new UnaryExpr(u.getSign(), (Expression) u.getExpr().accept(this, arg)); + } + + @Override + public FieldAccessor visit(FieldAccessor fa, Void arg) throws AsterixException { + return new FieldAccessor((Expression) fa.getExpr().accept(this, arg), fa.getIdent()); + } + + @Override + public Expression visit(IndexAccessor ia, Void arg) throws AsterixException { + Expression expr = (Expression) ia.getExpr().accept(this, arg); + Expression indexExpr = null; + if (ia.getIndexExpr() != null) { + indexExpr = ia.getIndexExpr(); + } + return new IndexAccessor(expr, indexExpr); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/3dd80ec4/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java ---------------------------------------------------------------------- diff --git a/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java new file mode 100644 index 0000000..6e70455 --- /dev/null +++ b/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java @@ -0,0 +1,471 @@ +/* + * 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.Collection; +import java.util.HashSet; +import java.util.List; + +import org.apache.asterix.common.exceptions.AsterixException; +import org.apache.asterix.lang.common.base.Clause.ClauseType; +import org.apache.asterix.lang.common.base.Expression; +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.GbyVariableExpressionPair; +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.Identifier; +import org.apache.asterix.lang.common.struct.QuantifiedPair; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +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.SelectExpression; +import org.apache.asterix.lang.sqlpp.struct.SetOperationRight; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor; +import org.apache.hyracks.algebricks.common.utils.Pair; + +public class FreeVariableVisitor extends AbstractSqlppQueryExpressionVisitor> { + + @Override + public Void visit(FromClause fromClause, Collection freeVars) throws AsterixException { + Collection bindingVars = new HashSet<>(); + for (FromTerm fromTerm : fromClause.getFromTerms()) { + Collection fromTermFreeVars = new HashSet<>(); + fromTerm.accept(this, fromTermFreeVars); + + // Since a right from term can refer to variables defined in a left from term, + // we remove binding variables from the free variables. + fromTermFreeVars.removeAll(bindingVars); + + // Adds binding variables. + bindingVars.addAll(SqlppVariableUtil.getBindingVariables(fromTerm)); + + // Adds into freeVars. + freeVars.addAll(fromTermFreeVars); + } + return null; + } + + @Override + public Void visit(FromTerm fromTerm, Collection freeVars) throws AsterixException { + // The encountered binding variables so far in the fromterm. + Collection bindingVariables = new HashSet<>(); + + // Visit the left expression of a from term. + fromTerm.getLeftExpression().accept(this, freeVars); + + // Adds binding variables. + bindingVariables.add(fromTerm.getLeftVariable()); + if (fromTerm.hasPositionalVariable()) { + bindingVariables.add(fromTerm.getPositionalVariable()); + } + + // Visits join/unnest/nest clauses. + for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) { + Collection correlateFreeVars = new HashSet<>(); + correlateClause.accept(this, correlateFreeVars); + if (correlateClause.getClauseType() != ClauseType.JOIN_CLAUSE) { + // Correlation is allowed if the clause is not a join clause, + // therefore we remove left-side binding variables for these cases. + correlateFreeVars.removeAll(bindingVariables); + + // Adds binding variables. + bindingVariables.add(correlateClause.getRightVariable()); + if (correlateClause.hasPositionalVariable()) { + bindingVariables.add(correlateClause.getPositionalVariable()); + } + } + freeVars.addAll(correlateFreeVars); + } + return null; + } + + @Override + public Void visit(JoinClause joinClause, Collection freeVars) throws AsterixException { + visitJoinAndNest(joinClause, joinClause.getConditionExpression(), freeVars); + return null; + } + + @Override + public Void visit(NestClause nestClause, Collection freeVars) throws AsterixException { + visitJoinAndNest(nestClause, nestClause.getConditionExpression(), freeVars); + return null; + } + + @Override + public Void visit(UnnestClause unnestClause, Collection freeVars) throws AsterixException { + unnestClause.getRightExpression().accept(this, freeVars); + return null; + } + + @Override + public Void visit(Projection projection, Collection freeVars) throws AsterixException { + projection.getExpression().accept(this, freeVars); + return null; + } + + @Override + public Void visit(SelectBlock selectBlock, Collection freeVars) throws AsterixException { + Collection selectFreeVars = new HashSet<>(); + Collection fromFreeVars = new HashSet<>(); + Collection letsFreeVars = new HashSet<>(); + Collection whereFreeVars = new HashSet<>(); + Collection gbyFreeVars = new HashSet<>(); + Collection gbyLetsFreeVars = new HashSet<>(); + + Collection fromBindingVars = SqlppVariableUtil.getBindingVariables(selectBlock.getFromClause()); + Collection letsBindingVars = SqlppVariableUtil.getBindingVariables(selectBlock.getLetList()); + Collection gbyBindingVars = SqlppVariableUtil.getBindingVariables(selectBlock.getGroupbyClause()); + Collection gbyLetsBindingVars = SqlppVariableUtil + .getBindingVariables(selectBlock.getLetListAfterGroupby()); + + selectBlock.getSelectClause().accept(this, selectFreeVars); + // Removes group-by, from, let, and gby-let binding vars. + removeAllBindingVarsInSelectBlock(selectFreeVars, fromBindingVars, letsBindingVars, gbyLetsBindingVars); + + if (selectBlock.hasFromClause()) { + selectBlock.getFromClause().accept(this, fromFreeVars); + } + if (selectBlock.hasLetClauses()) { + visitLetClauses(selectBlock.getLetList(), letsFreeVars); + letsFreeVars.removeAll(fromBindingVars); + } + if (selectBlock.hasWhereClause()) { + selectBlock.getWhereClause().accept(this, whereFreeVars); + whereFreeVars.removeAll(fromBindingVars); + whereFreeVars.removeAll(letsBindingVars); + } + if (selectBlock.hasGroupbyClause()) { + selectBlock.getGroupbyClause().accept(this, gbyFreeVars); + // Remove group-by and let binding vars. + gbyFreeVars.removeAll(fromBindingVars); + gbyFreeVars.removeAll(letsBindingVars); + if (selectBlock.hasLetClausesAfterGroupby()) { + visitLetClauses(selectBlock.getLetListAfterGroupby(), gbyLetsFreeVars); + gbyLetsFreeVars.removeAll(fromBindingVars); + gbyLetsFreeVars.removeAll(letsBindingVars); + gbyLetsFreeVars.removeAll(gbyBindingVars); + } + if (selectBlock.hasHavingClause()) { + selectBlock.getHavingClause().accept(this, selectFreeVars); + removeAllBindingVarsInSelectBlock(selectFreeVars, fromBindingVars, letsBindingVars, gbyLetsBindingVars); + } + } + + // Removes all binding vars from freeVars, which contains the free + // vars in the order-by and limit. + removeAllBindingVarsInSelectBlock(freeVars, fromBindingVars, letsBindingVars, gbyLetsBindingVars); + + // Adds all free vars. + freeVars.addAll(selectFreeVars); + freeVars.addAll(fromFreeVars); + freeVars.addAll(letsFreeVars); + freeVars.addAll(whereFreeVars); + freeVars.addAll(gbyFreeVars); + freeVars.addAll(gbyLetsFreeVars); + return null; + } + + @Override + public Void visit(SelectClause selectClause, Collection freeVars) throws AsterixException { + if (selectClause.selectElement()) { + selectClause.getSelectElement().accept(this, freeVars); + } + if (selectClause.selectRegular()) { + selectClause.getSelectRegular().accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(SelectElement selectElement, Collection freeVars) throws AsterixException { + selectElement.getExpression().accept(this, freeVars); + return null; + } + + @Override + public Void visit(SelectRegular selectRegular, Collection freeVars) throws AsterixException { + for (Projection projection : selectRegular.getProjections()) { + projection.accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(SelectSetOperation selectSetOperation, Collection freeVars) + throws AsterixException { + selectSetOperation.getLeftInput().accept(this, freeVars); + for (SetOperationRight right : selectSetOperation.getRightInputs()) { + right.getSetOperationRightInput().accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(HavingClause havingClause, Collection freeVars) throws AsterixException { + havingClause.getFilterExpression().accept(this, freeVars); + return null; + } + + @Override + public Void visit(Query q, Collection freeVars) throws AsterixException { + q.getBody().accept(this, freeVars); + return null; + } + + @Override + public Void visit(FunctionDecl fd, Collection freeVars) throws AsterixException { + fd.getFuncBody().accept(this, freeVars); + return null; + } + + @Override + public Void visit(WhereClause whereClause, Collection freeVars) throws AsterixException { + whereClause.getWhereExpr().accept(this, freeVars); + return null; + } + + @Override + public Void visit(OrderbyClause oc, Collection freeVars) throws AsterixException { + for (Expression orderExpr : oc.getOrderbyList()) { + orderExpr.accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(GroupbyClause gc, Collection freeVars) throws AsterixException { + // Puts all group-by variables into the symbol set of the new scope. + for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) { + gbyVarExpr.getExpr().accept(this, freeVars); + } + for (GbyVariableExpressionPair decorVarExpr : gc.getDecorPairList()) { + decorVarExpr.getExpr().accept(this, freeVars); + } + if (gc.hasGroupFieldList()) { + for (Pair groupField : gc.getGroupFieldList()) { + groupField.first.accept(this, freeVars); + } + } + return null; + } + + @Override + public Void visit(LimitClause limitClause, Collection freeVars) throws AsterixException { + limitClause.getLimitExpr().accept(this, freeVars); + return null; + } + + @Override + public Void visit(LetClause letClause, Collection freeVars) throws AsterixException { + letClause.getBindingExpr().accept(this, freeVars); + return null; + } + + @Override + public Void visit(SelectExpression selectExpression, Collection freeVars) throws AsterixException { + Collection letsFreeVars = new HashSet<>(); + Collection selectFreeVars = new HashSet<>(); + visitLetClauses(selectExpression.getLetList(), letsFreeVars); + + // visit order by + if (selectExpression.hasOrderby()) { + for (Expression orderExpr : selectExpression.getOrderbyClause().getOrderbyList()) { + orderExpr.accept(this, selectFreeVars); + } + } + + // visit limit + if (selectExpression.hasLimit()) { + selectExpression.getLimitClause().accept(this, selectFreeVars); + } + + // visit the main select + selectExpression.getSelectSetOperation().accept(this, selectFreeVars); + + // Removed let binding variables. + selectFreeVars.removeAll(SqlppVariableUtil.getBindingVariables(selectExpression.getLetList())); + freeVars.addAll(letsFreeVars); + freeVars.addAll(selectFreeVars); + return null; + } + + @Override + public Void visit(LiteralExpr l, Collection freeVars) throws AsterixException { + return null; + } + + @Override + public Void visit(ListConstructor lc, Collection freeVars) throws AsterixException { + for (Expression expr : lc.getExprList()) { + expr.accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(RecordConstructor rc, Collection freeVars) throws AsterixException { + for (FieldBinding binding : rc.getFbList()) { + binding.getLeftExpr().accept(this, freeVars); + binding.getRightExpr().accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(OperatorExpr operatorExpr, Collection freeVars) throws AsterixException { + for (Expression expr : operatorExpr.getExprList()) { + expr.accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(IfExpr ifExpr, Collection freeVars) throws AsterixException { + ifExpr.getCondExpr().accept(this, freeVars); + ifExpr.getThenExpr().accept(this, freeVars); + ifExpr.getElseExpr().accept(this, freeVars); + return null; + } + + @Override + public Void visit(QuantifiedExpression qe, Collection freeVars) throws AsterixException { + for (QuantifiedPair pair : qe.getQuantifiedList()) { + pair.getExpr().accept(this, freeVars); + } + qe.getSatisfiesExpr().accept(this, freeVars); + return null; + } + + @Override + public Void visit(CallExpr callExpr, Collection freeVars) throws AsterixException { + for (Expression expr : callExpr.getExprList()) { + expr.accept(this, freeVars); + } + return null; + } + + @Override + public Void visit(VariableExpr varExpr, Collection freeVars) throws AsterixException { + freeVars.add(varExpr); + return null; + } + + @Override + public Void visit(UnaryExpr u, Collection freeVars) throws AsterixException { + u.getExpr().accept(this, freeVars); + return null; + } + + @Override + public Void visit(FieldAccessor fa, Collection freeVars) throws AsterixException { + fa.getExpr().accept(this, freeVars); + return null; + } + + @Override + public Void visit(IndexAccessor ia, Collection freeVars) throws AsterixException { + ia.getExpr().accept(this, freeVars); + if (ia.getIndexExpr() != null) { + ia.getIndexExpr(); + } + return null; + } + + private void visitLetClauses(List letClauses, Collection freeVars) + throws AsterixException { + if (letClauses == null || letClauses.isEmpty()) { + return; + } + Collection bindingVars = new HashSet<>(); + for (LetClause letClause : letClauses) { + Collection letFreeVars = new HashSet<>(); + letClause.accept(this, letFreeVars); + + // Removes previous binding variables. + letFreeVars.removeAll(bindingVars); + freeVars.addAll(letFreeVars); + + // Adds let binding variables into the binding variable collection. + bindingVars.add(letClause.getVarExpr()); + } + } + + private void visitJoinAndNest(AbstractBinaryCorrelateClause clause, Expression condition, + Collection freeVars) throws AsterixException { + clause.getRightExpression().accept(this, freeVars); + Collection conditionFreeVars = new HashSet<>(); + condition.accept(this, freeVars); + + // The condition expression can free binding variables defined in the join clause. + conditionFreeVars.remove(clause.getRightVariable()); + if (clause.hasPositionalVariable()) { + conditionFreeVars.remove(clause.getPositionalVariable()); + } + freeVars.addAll(conditionFreeVars); + } + + /** + * Removes all binding variables defined in the select block for a free variable collection. + * + * @param freeVars, + * free variables. + * @param fromBindingVars, + * binding variables defined in the from clause of a select block. + * @param letsBindingVars, + * binding variables defined in the let clauses of the select block. + * @param gbyLetsBindingVars, + * binding variables defined in the let clauses after a group-by in the select block. + */ + private void removeAllBindingVarsInSelectBlock(Collection selectFreeVars, + Collection fromBindingVars, Collection letsBindingVars, + Collection gbyLetsBindingVars) { + selectFreeVars.removeAll(fromBindingVars); + selectFreeVars.removeAll(letsBindingVars); + selectFreeVars.removeAll(gbyLetsBindingVars); + selectFreeVars.removeAll(gbyLetsBindingVars); + } + +}