blob: fdb56bea53fbfff4236488904e14dd14e1358b21 [file] [log] [blame]
package edu.uci.ics.asterix.aql.rewrites;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.uci.ics.asterix.aql.base.Clause;
import edu.uci.ics.asterix.aql.base.Expression;
import edu.uci.ics.asterix.aql.base.Statement;
import edu.uci.ics.asterix.aql.base.Expression.Kind;
import edu.uci.ics.asterix.aql.expression.BeginFeedStatement;
import edu.uci.ics.asterix.aql.expression.CallExpr;
import edu.uci.ics.asterix.aql.expression.ControlFeedStatement;
import edu.uci.ics.asterix.aql.expression.CreateDataverseStatement;
import edu.uci.ics.asterix.aql.expression.CreateFunctionStatement;
import edu.uci.ics.asterix.aql.expression.CreateIndexStatement;
import edu.uci.ics.asterix.aql.expression.DatasetDecl;
import edu.uci.ics.asterix.aql.expression.DataverseDecl;
import edu.uci.ics.asterix.aql.expression.DataverseDropStatement;
import edu.uci.ics.asterix.aql.expression.DeleteStatement;
import edu.uci.ics.asterix.aql.expression.DieClause;
import edu.uci.ics.asterix.aql.expression.DistinctClause;
import edu.uci.ics.asterix.aql.expression.DropStatement;
import edu.uci.ics.asterix.aql.expression.FLWOGRExpression;
import edu.uci.ics.asterix.aql.expression.FieldAccessor;
import edu.uci.ics.asterix.aql.expression.FieldBinding;
import edu.uci.ics.asterix.aql.expression.ForClause;
import edu.uci.ics.asterix.aql.expression.FunctionDecl;
import edu.uci.ics.asterix.aql.expression.FunctionDropStatement;
import edu.uci.ics.asterix.aql.expression.GbyVariableExpressionPair;
import edu.uci.ics.asterix.aql.expression.GroupbyClause;
import edu.uci.ics.asterix.aql.expression.IfExpr;
import edu.uci.ics.asterix.aql.expression.IndexAccessor;
import edu.uci.ics.asterix.aql.expression.IndexDropStatement;
import edu.uci.ics.asterix.aql.expression.InsertStatement;
import edu.uci.ics.asterix.aql.expression.LetClause;
import edu.uci.ics.asterix.aql.expression.LimitClause;
import edu.uci.ics.asterix.aql.expression.ListConstructor;
import edu.uci.ics.asterix.aql.expression.LiteralExpr;
import edu.uci.ics.asterix.aql.expression.LoadFromFileStatement;
import edu.uci.ics.asterix.aql.expression.NodeGroupDropStatement;
import edu.uci.ics.asterix.aql.expression.NodegroupDecl;
import edu.uci.ics.asterix.aql.expression.OperatorExpr;
import edu.uci.ics.asterix.aql.expression.OrderbyClause;
import edu.uci.ics.asterix.aql.expression.OrderedListTypeDefinition;
import edu.uci.ics.asterix.aql.expression.QuantifiedExpression;
import edu.uci.ics.asterix.aql.expression.QuantifiedPair;
import edu.uci.ics.asterix.aql.expression.Query;
import edu.uci.ics.asterix.aql.expression.RecordConstructor;
import edu.uci.ics.asterix.aql.expression.RecordTypeDefinition;
import edu.uci.ics.asterix.aql.expression.SetStatement;
import edu.uci.ics.asterix.aql.expression.TypeDecl;
import edu.uci.ics.asterix.aql.expression.TypeDropStatement;
import edu.uci.ics.asterix.aql.expression.TypeReferenceExpression;
import edu.uci.ics.asterix.aql.expression.UnaryExpr;
import edu.uci.ics.asterix.aql.expression.UnionExpr;
import edu.uci.ics.asterix.aql.expression.UnorderedListTypeDefinition;
import edu.uci.ics.asterix.aql.expression.UpdateClause;
import edu.uci.ics.asterix.aql.expression.UpdateStatement;
import edu.uci.ics.asterix.aql.expression.VarIdentifier;
import edu.uci.ics.asterix.aql.expression.VariableExpr;
import edu.uci.ics.asterix.aql.expression.WhereClause;
import edu.uci.ics.asterix.aql.expression.WriteFromQueryResultStatement;
import edu.uci.ics.asterix.aql.expression.WriteStatement;
import edu.uci.ics.asterix.aql.expression.visitor.IAqlExpressionVisitor;
import edu.uci.ics.asterix.aql.util.FunctionUtils;
import edu.uci.ics.asterix.common.exceptions.AsterixException;
import edu.uci.ics.asterix.common.functions.FunctionConstants;
import edu.uci.ics.asterix.metadata.MetadataManager;
import edu.uci.ics.asterix.metadata.MetadataTransactionContext;
import edu.uci.ics.asterix.metadata.entities.Function;
import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
import edu.uci.ics.asterix.om.functions.AsterixFunction;
import edu.uci.ics.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
public final class AqlRewriter {
private Query topExpr;
private AqlRewritingContext context;
private MetadataTransactionContext mdTxnCtx;
private String dataverseName;
private enum DfsColor {
WHITE, GRAY, BLACK
}
public AqlRewriter(Query topExpr, int varCounter, MetadataTransactionContext txnContext, String dataverseName) {
this.topExpr = topExpr;
context = new AqlRewritingContext(varCounter);
mdTxnCtx = txnContext;
this.dataverseName = dataverseName;
}
public Query getExpr() {
return topExpr;
}
public int getVarCounter() {
return context.getVarCounter();
}
public void rewrite() throws AsterixException {
wrapInLets();
inlineDeclaredUdfs();
}
private void wrapInLets() {
// If the top expression of the main statement is not a FLWOR, it wraps
// it into a let clause.
if (topExpr == null) {
return;
}
Expression body = topExpr.getBody();
if (body.getKind() != Kind.FLWOGR_EXPRESSION) {
VarIdentifier var = context.newVariable();
VariableExpr v = new VariableExpr(var);
LetClause c1 = new LetClause(v, body);
ArrayList<Clause> clauseList = new ArrayList<Clause>(1);
clauseList.add(c1);
FLWOGRExpression newBody = new FLWOGRExpression(clauseList, new VariableExpr(var));
topExpr.setBody(newBody);
}
}
private void inlineDeclaredUdfs() throws AsterixException {
if (topExpr == null) {
return;
}
List<FunctionDecl> fdecls = buildFunctionDeclList(topExpr);
List<AsterixFunction> funIds = new ArrayList<AsterixFunction>();
for (FunctionDecl fdecl : fdecls) {
funIds.add(fdecl.getIdent());
}
List<FunctionDecl> otherFDecls = new ArrayList<FunctionDecl>();
buildOtherUdfs(topExpr.getBody(), otherFDecls, funIds);
fdecls.addAll(otherFDecls);
if (!fdecls.isEmpty()) {
checkRecursivity(fdecls);
InlineUdfsVisitor visitor = new InlineUdfsVisitor(context);
while (topExpr.accept(visitor, fdecls)) {
// loop until no more changes
}
}
}
private void buildOtherUdfs(Expression expression, List<FunctionDecl> functionDecls,
List<AsterixFunction> declaredFunctions) throws AsterixException {
if (expression == null) {
return;
}
List<AsterixFunction> functionCalls = getFunctionCalls(expression);
for (AsterixFunction funId : functionCalls) {
if (AsterixBuiltinFunctions.isBuiltinCompilerFunction(new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
funId.getFunctionName(), false))) {
continue;
}
if (AsterixBuiltinFunctions.isBuiltinCompilerFunction(new FunctionIdentifier(AlgebricksBuiltinFunctions.ALGEBRICKS_NS,
funId.getFunctionName(), false))) {
continue;
}
if (declaredFunctions != null && declaredFunctions.contains(funId)) {
continue;
}
FunctionDecl functionDecl = getFunctionDecl(funId);
if (functionDecls.contains(functionDecl)) {
throw new AsterixException(" Detected recursvity!");
}
functionDecls.add(functionDecl);
buildOtherUdfs(functionDecl.getFuncBody(), functionDecls, declaredFunctions);
}
}
private FunctionDecl getFunctionDecl(AsterixFunction funId) throws AsterixException {
Function function = MetadataManager.INSTANCE.getFunction(mdTxnCtx, dataverseName, funId.getFunctionName(), funId
.getArity());
if (function == null) {
throw new AsterixException(" unknown function " + funId);
}
return FunctionUtils.getFunctionDecl(function);
}
private List<AsterixFunction> getFunctionCalls(Expression expression) throws AsterixException {
Map<AsterixFunction, DfsColor> color = new HashMap<AsterixFunction, DfsColor>();
Map<AsterixFunction, List<AsterixFunction>> arcs = new HashMap<AsterixFunction, List<AsterixFunction>>();
GatherFunctionCalls gfc = new GatherFunctionCalls();
expression.accept(gfc, null);
List<AsterixFunction> calls = gfc.getCalls();
return calls;
}
private void checkRecursivity(List<FunctionDecl> fdecls) throws AsterixException {
Map<AsterixFunction, DfsColor> color = new HashMap<AsterixFunction, DfsColor>();
Map<AsterixFunction, List<AsterixFunction>> arcs = new HashMap<AsterixFunction, List<AsterixFunction>>();
for (FunctionDecl fd : fdecls) {
GatherFunctionCalls gfc = new GatherFunctionCalls();
fd.getFuncBody().accept(gfc, null);
List<AsterixFunction> calls = gfc.getCalls();
arcs.put(fd.getIdent(), calls);
color.put(fd.getIdent(), DfsColor.WHITE);
}
for (AsterixFunction a : arcs.keySet()) {
if (color.get(a) == DfsColor.WHITE) {
checkRecursivityDfs(a, arcs, color);
}
}
}
private void checkRecursivityDfs(AsterixFunction a, Map<AsterixFunction, List<AsterixFunction>> arcs,
Map<AsterixFunction, DfsColor> color) throws AsterixException {
color.put(a, DfsColor.GRAY);
List<AsterixFunction> next = arcs.get(a);
if (next != null) {
for (AsterixFunction f : next) {
DfsColor dc = color.get(f);
if (dc == DfsColor.GRAY) {
throw new AsterixException("Recursive function calls, created by calling " + f + " starting from "
+ a);
}
if (dc == DfsColor.WHITE) {
checkRecursivityDfs(f, arcs, color);
}
}
}
color.put(a, DfsColor.BLACK);
}
private List<FunctionDecl> buildFunctionDeclList(Query q) {
ArrayList<FunctionDecl> fdecls = new ArrayList<FunctionDecl>();
for (Statement s : q.getPrologDeclList()) {
if (s.getKind() == Statement.Kind.FUNCTION_DECL) {
fdecls.add((FunctionDecl) s);
}
}
return fdecls;
}
private static class GatherFunctionCalls implements IAqlExpressionVisitor<Void, Void> {
private final List<AsterixFunction> calls = new ArrayList<AsterixFunction>();
public GatherFunctionCalls() {
}
@Override
public Void visitCallExpr(CallExpr pf, Void arg) throws AsterixException {
calls.add(pf.getIdent());
for (Expression e : pf.getExprList()) {
e.accept(this, arg);
}
return null;
}
@Override
public Void visitCreateIndexStatement(CreateIndexStatement cis, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitDataverseDecl(DataverseDecl dv, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitDeleteStatement(DeleteStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitDistinctClause(DistinctClause dc, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitDropStatement(DropStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitDatasetDecl(DatasetDecl dd, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitFieldAccessor(FieldAccessor fa, Void arg) throws AsterixException {
fa.getExpr().accept(this, arg);
return null;
}
@Override
public Void visitFlworExpression(FLWOGRExpression flwor, Void arg) throws AsterixException {
for (Clause c : flwor.getClauseList()) {
c.accept(this, arg);
}
flwor.getReturnExpr().accept(this, arg);
return null;
}
@Override
public Void visitForClause(ForClause fc, Void arg) throws AsterixException {
fc.getInExpr().accept(this, arg);
if (fc.getPosVarExpr() != null) {
fc.getPosVarExpr().accept(this, arg);
}
return null;
}
@Override
public Void visitFunctionDecl(FunctionDecl fd, Void arg) throws AsterixException {
fd.getFuncBody().accept(this, arg);
return null;
}
@Override
public Void visitGroupbyClause(GroupbyClause gc, Void arg) throws AsterixException {
for (GbyVariableExpressionPair p : gc.getGbyPairList()) {
p.getExpr().accept(this, arg);
}
for (GbyVariableExpressionPair p : gc.getDecorPairList()) {
p.getExpr().accept(this, arg);
}
return null;
}
@Override
public Void visitIfExpr(IfExpr ifexpr, Void arg) throws AsterixException {
ifexpr.getCondExpr().accept(this, arg);
ifexpr.getThenExpr().accept(this, arg);
ifexpr.getElseExpr().accept(this, arg);
return null;
}
@Override
public Void visitIndexAccessor(IndexAccessor ia, Void arg) throws AsterixException {
ia.getExpr().accept(this, arg);
return null;
}
@Override
public Void visitInsertStatement(InsertStatement insert, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitLetClause(LetClause lc, Void arg) throws AsterixException {
lc.getBindingExpr().accept(this, arg);
return null;
}
@Override
public Void visitLimitClause(LimitClause lc, Void arg) throws AsterixException {
lc.getLimitExpr().accept(this, arg);
if (lc.getOffset() != null) {
lc.getOffset().accept(this, arg);
}
return null;
}
@Override
public Void visitDieClause(DieClause lc, Void arg) throws AsterixException {
lc.getDieExpr().accept(this, arg);
return null;
}
@Override
public Void visitListConstructor(ListConstructor lc, Void arg) throws AsterixException {
for (Expression e : lc.getExprList()) {
e.accept(this, arg);
}
return null;
}
@Override
public Void visitLiteralExpr(LiteralExpr l, Void arg) throws AsterixException {
// do nothing
return null;
}
@Override
public Void visitLoadFromFileStatement(LoadFromFileStatement stmtLoad, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitNodegroupDecl(NodegroupDecl ngd, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitOperatorExpr(OperatorExpr op, Void arg) throws AsterixException {
for (Expression e : op.getExprList()) {
e.accept(this, arg);
}
return null;
}
@Override
public Void visitOrderbyClause(OrderbyClause oc, Void arg) throws AsterixException {
for (Expression e : oc.getOrderbyList()) {
e.accept(this, arg);
}
return null;
}
@Override
public Void visitOrderedListTypeDefiniton(OrderedListTypeDefinition olte, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitQuantifiedExpression(QuantifiedExpression qe, Void arg) throws AsterixException {
for (QuantifiedPair qp : qe.getQuantifiedList()) {
qp.getExpr().accept(this, arg);
}
qe.getSatisfiesExpr().accept(this, arg);
return null;
}
@Override
public Void visitQuery(Query q, Void arg) throws AsterixException {
q.getBody().accept(this, arg);
return null;
}
@Override
public Void visitRecordConstructor(RecordConstructor rc, Void arg) throws AsterixException {
for (FieldBinding fb : rc.getFbList()) {
fb.getLeftExpr().accept(this, arg);
fb.getRightExpr().accept(this, arg);
}
return null;
}
@Override
public Void visitRecordTypeDefiniton(RecordTypeDefinition tre, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitSetStatement(SetStatement ss, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitTypeDecl(TypeDecl td, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitTypeReferenceExpression(TypeReferenceExpression tre, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitUnaryExpr(UnaryExpr u, Void arg) throws AsterixException {
u.getExpr().accept(this, arg);
return null;
}
@Override
public Void visitUnionExpr(UnionExpr u, Void arg) throws AsterixException {
for (Expression e : u.getExprs()) {
e.accept(this, arg);
}
return null;
}
@Override
public Void visitUnorderedListTypeDefiniton(UnorderedListTypeDefinition ulte, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitUpdateClause(UpdateClause del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitUpdateStatement(UpdateStatement update, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitVariableExpr(VariableExpr v, Void arg) throws AsterixException {
// do nothing
return null;
}
@Override
public Void visitWhereClause(WhereClause wc, Void arg) throws AsterixException {
wc.getWhereExpr().accept(this, arg);
return null;
}
@Override
public Void visitWriteFromQueryResultStatement(WriteFromQueryResultStatement stmtLoad, Void arg)
throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitWriteStatement(WriteStatement ws, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
public List<AsterixFunction> getCalls() {
return calls;
}
@Override
public Void visitLoadFromQueryResultStatement(WriteFromQueryResultStatement stmtLoad, Void arg)
throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitCreateDataverseStatement(CreateDataverseStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitIndexDropStatement(IndexDropStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitNodeGroupDropStatement(NodeGroupDropStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitDataverseDropStatement(DataverseDropStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitTypeDropStatement(TypeDropStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitControlFeedStatement(ControlFeedStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visit(CreateFunctionStatement cfs, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitFunctionDropStatement(FunctionDropStatement del, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
@Override
public Void visitBeginFeedStatement(BeginFeedStatement bf, Void arg) throws AsterixException {
// TODO Auto-generated method stub
return null;
}
}
}