/* * Copyright (c) 2011-2015 EPFL DATA Laboratory * Copyright (c) 2014-2015 The Squall Collaboration (see NOTICE) * * All rights reserved. * * Licensed 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 ch.epfl.data.squall.api.sql.visitors.squall; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import net.sf.jsqlparser.expression.AllComparisonExpression; import net.sf.jsqlparser.expression.AnyComparisonExpression; import net.sf.jsqlparser.expression.BinaryExpression; import net.sf.jsqlparser.expression.CaseExpression; import net.sf.jsqlparser.expression.DateValue; import net.sf.jsqlparser.expression.DoubleValue; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.expression.InverseExpression; import net.sf.jsqlparser.expression.JdbcParameter; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.NullValue; import net.sf.jsqlparser.expression.Parenthesis; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.expression.TimeValue; import net.sf.jsqlparser.expression.TimestampValue; import net.sf.jsqlparser.expression.WhenClause; import net.sf.jsqlparser.expression.operators.arithmetic.Addition; import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr; import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; import net.sf.jsqlparser.expression.operators.arithmetic.Concat; import net.sf.jsqlparser.expression.operators.arithmetic.Division; import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.conditional.OrExpression; import net.sf.jsqlparser.expression.operators.relational.Between; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.GreaterThan; import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; import net.sf.jsqlparser.expression.operators.relational.InExpression; import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; import net.sf.jsqlparser.expression.operators.relational.LikeExpression; import net.sf.jsqlparser.expression.operators.relational.Matches; import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; import net.sf.jsqlparser.statement.select.SelectExpressionItem; import net.sf.jsqlparser.statement.select.SelectItemVisitor; import net.sf.jsqlparser.statement.select.SubSelect; import ch.epfl.data.squall.api.sql.optimizers.index.IndexTranslator; import ch.epfl.data.squall.api.sql.schema.Schema; import ch.epfl.data.squall.api.sql.util.ParserUtil; import ch.epfl.data.squall.api.sql.util.TableAliasName; import ch.epfl.data.squall.components.Component; import ch.epfl.data.squall.expressions.ColumnReference; import ch.epfl.data.squall.expressions.IntegerYearFromDate; import ch.epfl.data.squall.expressions.ValueExpression; import ch.epfl.data.squall.expressions.ValueSpecification; import ch.epfl.data.squall.operators.AggregateCountOperator; import ch.epfl.data.squall.operators.AggregateOperator; import ch.epfl.data.squall.operators.AggregateSumOperator; import ch.epfl.data.squall.operators.DistinctOperator; import ch.epfl.data.squall.query_plans.QueryBuilder; import ch.epfl.data.squall.types.DateType; import ch.epfl.data.squall.types.DoubleType; import ch.epfl.data.squall.types.LongType; import ch.epfl.data.squall.types.StringType; import ch.epfl.data.squall.types.Type; /* * Generates Aggregations and its groupBy projections. * If there is no aggregation, these groupBy projections becomes simple projections */ public class IndexSelectItemsVisitor implements SelectItemVisitor, ExpressionVisitor, ItemsListVisitor { // all of these needed only for visit(Column) method private Schema _schema; private Component _affectedComponent; private TableAliasName _tan; private IndexTranslator _it; // needed only for visit(Function) method private final Map _map; private Stack<ValueExpression> _exprStack; private AggregateOperator _agg = null; // these two are of interest for the invoker private final List<AggregateOperator> _aggOps = new ArrayList<AggregateOperator>(); private final List<ValueExpression> _groupByVEs = new ArrayList<ValueExpression>(); private final List<Expression> _groupByExprs = new ArrayList<Expression>(); public static final int AGG = 0; public static final int NON_AGG = 1; // this will not break any contracts, // even with new DateConversion() on all the places, // we will have a single object per (possibly) multiple spout/bolt threads. // generating plans is done from a single thread, static additionally saves // space private static LongType _lc = new LongType(); private static DoubleType _dblConv = new DoubleType(); private static DateType _dateConv = new DateType(); private static StringType _sc = new StringType(); protected IndexSelectItemsVisitor(Map map) { _map = map; } public IndexSelectItemsVisitor(QueryBuilder queryPlan, Schema schema, TableAliasName tan, Map map) { _schema = schema; _tan = tan; _map = map; _affectedComponent = queryPlan.getLastComponent(); _it = new IndexTranslator(_schema, _tan); } /* * veList used only in DISTINCT mode */ protected void createCount(List<ValueExpression> veList, boolean isDistinct) { // COUNT(R.A) and COUNT(1) have the same semantics as COUNT(*), since we // do not have NULLs in R.A _agg = new AggregateCountOperator(_map); // DISTINCT and agg are stored on the same component. if (isDistinct) { final DistinctOperator distinct = new DistinctOperator(_map, veList); _agg.setDistinct(distinct); } } protected void createSum(ValueExpression ve, boolean isDistinct) { _agg = new AggregateSumOperator(ve, _map); // DISTINCT and agg are stored on the same component. if (isDistinct) { final DistinctOperator distinct = new DistinctOperator(_map, ve); _agg.setDistinct(distinct); } } private void doneSingleItem(Expression expr) { if (_agg == null) { _groupByExprs.add(expr); _groupByVEs.add(_exprStack.peek()); } else _aggOps.add(_agg); } public List<AggregateOperator> getAggOps() { return _aggOps; } public List<Expression> getGroupByExprs() { return _groupByExprs; } public List<ValueExpression> getGroupByVEs() { return _groupByVEs; } protected ValueExpression popFromExprStack() { return _exprStack.pop(); } protected void pushToExprStack(ValueExpression ve) { _exprStack.push(ve); } /* * Each of these operations create a Squall type, that's why so much similar * code */ @Override public void visit(Addition adtn) { visitBinaryExpression(adtn); final ValueExpression right = _exprStack.pop(); final ValueExpression left = _exprStack.pop(); final ValueExpression ve = new ch.epfl.data.squall.expressions.Addition( left, right); _exprStack.push(ve); } // SELECTITEMVISITOR DESIGN PATTERN @Override public void visit(AllColumns ac) { // i.e. SELECT * FROM R join S // we need not to do anything in this case for RuleOptimizer // TODO: support it for Cost-Optimizer (Now, each wanted column has to // explicitly specified) } @Override public void visit(AllComparisonExpression ace) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(AllTableColumns atc) { // i.e. SELECT R.* FROM R join S // this is very rare, so it's not supported throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(AndExpression ae) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(AnyComparisonExpression ace) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(Between btwn) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(BitwiseAnd ba) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(BitwiseOr bo) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(BitwiseXor bx) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(CaseExpression ce) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(Column column) { // extract type for the column final Type tc = _schema.getType(ParserUtil.getFullSchemaColumnName( column, _tan)); // extract the position (index) of the required column final int position = _it.getColumnIndex(column, _affectedComponent); final ValueExpression ve = new ColumnReference(tc, position); _exprStack.push(ve); } @Override public void visit(Concat concat) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(DateValue dv) { final ValueExpression ve = new ValueSpecification(_dateConv, dv.getValue()); _exprStack.push(ve); } @Override public void visit(Division dvsn) { visitBinaryExpression(dvsn); final ValueExpression right = _exprStack.pop(); final ValueExpression left = _exprStack.pop(); final ValueExpression ve = new ch.epfl.data.squall.expressions.Division( left, right); _exprStack.push(ve); } @Override public void visit(DoubleValue dv) { final ValueExpression ve = new ValueSpecification(_dblConv, dv.getValue()); _exprStack.push(ve); } @Override public void visit(EqualsTo et) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(ExistsExpression ee) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(ExpressionList el) { for (final Iterator iter = el.getExpressions().iterator(); iter .hasNext();) { final Expression expression = (Expression) iter.next(); expression.accept(this); } } // my VISITOR methods @Override public void visit(Function function) { // all aggregate functions (SUM, AVG, COUNT, MAX, MIN) have only one // parameter (Expression) // although COUNT(*) has no parameters // EXTRACT_YEAR has one parameter final ExpressionList params = function.getParameters(); int numParams = 0; if (params != null) { params.accept(this); // in order to determine the size final List<Expression> listParams = params.getExpressions(); numParams = listParams.size(); } final List<ValueExpression> expressions = new ArrayList<ValueExpression>(); for (int i = 0; i < numParams; i++) expressions.add(_exprStack.pop()); Collections.reverse(expressions); // at the stack top is the lastly // added VE final String fnName = function.getName(); if (fnName.equalsIgnoreCase("SUM")) { // there must be only one parameter, if not SQL parser will raise an // exception final ValueExpression expr = expressions.get(0); createSum(expr, function.isDistinct()); } else if (fnName.equalsIgnoreCase("COUNT")) createCount(expressions, function.isDistinct()); else if (fnName.equalsIgnoreCase("EXTRACT_YEAR")) { if (numParams != 1) throw new RuntimeException( "EXTRACT_YEAR function has exactly one parameter!"); final ValueExpression expr = expressions.get(0); final ValueExpression ve = new IntegerYearFromDate(expr); _exprStack.push(ve); } } @Override public void visit(GreaterThan gt) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(GreaterThanEquals gte) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(InExpression ie) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(InverseExpression ie) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(IsNullExpression ine) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(JdbcParameter jp) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(LikeExpression le) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(LongValue lv) { final ValueExpression ve = new ValueSpecification(_lc, lv.getValue()); _exprStack.push(ve); } @Override public void visit(Matches mtchs) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(MinorThan mt) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(MinorThanEquals mte) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(Multiplication m) { visitBinaryExpression(m); final ValueExpression right = _exprStack.pop(); final ValueExpression left = _exprStack.pop(); final ValueExpression ve = new ch.epfl.data.squall.expressions.Multiplication( left, right); _exprStack.push(ve); } @Override public void visit(NotEqualsTo net) { throw new UnsupportedOperationException("Not supported yet."); } // EXPRESSIONVISITOR DESIGN PATTERN @Override public void visit(NullValue nv) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(OrExpression oe) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(Parenthesis prnths) { prnths.getExpression().accept(this); } @Override public void visit(SelectExpressionItem sei) { _exprStack = new Stack<ValueExpression>(); _agg = null; final Expression expr = sei.getExpression(); expr.accept(this); doneSingleItem(expr); } @Override public void visit(StringValue sv) { final ValueExpression ve = new ValueSpecification(_sc, sv.getValue()); _exprStack.push(ve); } @Override public void visit(SubSelect ss) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(Subtraction s) { visitBinaryExpression(s); final ValueExpression right = _exprStack.pop(); final ValueExpression left = _exprStack.pop(); final ValueExpression ve = new ch.epfl.data.squall.expressions.Subtraction( left, right); _exprStack.push(ve); } @Override public void visit(TimestampValue tv) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(TimeValue tv) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void visit(WhenClause wc) { throw new UnsupportedOperationException("Not supported yet."); } public void visitBinaryExpression(BinaryExpression binaryExpression) { binaryExpression.getLeftExpression().accept(this); binaryExpression.getRightExpression().accept(this); } }