package org.yamcs.yarch.streamsql; import java.util.ArrayList; import java.util.List; import org.yamcs.yarch.AbstractStream; import org.yamcs.yarch.ColumnDefinition; import org.yamcs.yarch.CompiledAggregateExpression; import org.yamcs.yarch.CompiledExpression; import org.yamcs.yarch.DbReaderStream; import org.yamcs.yarch.SelectStream; import org.yamcs.yarch.TupleDefinition; import org.yamcs.yarch.WindowProcessor; import org.yamcs.yarch.YarchDatabase; import org.yamcs.yarch.streamsql.StreamSqlException.ErrCode; /** * Corresponds to a queries like * "select 2*x, x+sum(y+3) from t[...] where x>5 group by x" * chain of data/processing (D=Data, P=Processing, agg=aggregate): * * (D1) inputDef (x,y) * * (P1.1) where filter (x>5) * (P1.2) aggInputList * * (D2) aggInputDef (x,y+3) * * (P2) aggSelectList * * (D3) aggOutputDef (x,sum(y+3)) * * (P3) selectList * * (D4) outputDef (2*x,x+sum(y+3)) * * * P1.2 is performed by the WindowProcessor * If there is no aggregate, then P3 follows directly after P1.1 * * * @author nm * */ public class SelectExpression implements StreamExpression { List<SelectItem> selectList; // a,b+4,c TupleSourceExpression tupleSourceExpression; //t,u,v (but only one table/stream supported for the moment) Expression whereClause; //x and y WindowSpecification windowSpec;//[SIZE 1000 ADVANCE 1000 ON a] TupleDefinition inputDef, outputDef, minOutputDef, aggInputDef=null, aggOutputDef=null; List<AggregateExpression> aggList=null; List<Expression> aggInputList=null; boolean ascending=true; //only for table-selects boolean follow=true; //only for table-selects private boolean selectStar; //in case of select * public void setSelectList(List<SelectItem> selectList) { this.selectList=selectList; } public void setFirstSource(TupleSourceExpression tsrc) { this.tupleSourceExpression=tsrc; } public void setWhereClause(Expression whereClause) { this.whereClause=whereClause; } public void setWindow(WindowSpecification windowSpec) { this.windowSpec=windowSpec; } public void setAscending(boolean ascending) { this.ascending=ascending; tupleSourceExpression.setAscending(ascending); } public void setFollow(boolean follow) { this.follow=follow; tupleSourceExpression.setFollow(follow); } @Override public void bind(ExecutionContext c) throws StreamSqlException { tupleSourceExpression.bind(c); inputDef=tupleSourceExpression.getDefinition(); if(whereClause!=null) whereClause.bind(inputDef); if(windowSpec!=null) windowSpec.bind(inputDef); if(selectList.size()==1 && selectList.get(0)==SelectItem.STAR) { selectStar=true; } /*expand the * if together with something else if(!selectStar) { for(int i=0;i<selectList.size();i++) { if(selectList.get(i)==SelectItem.STAR) { selectList.remove(i); for(ColumnDefinition cd:inputDef.getColumnDefinitions()) { try { selectList.add(i, new SelectItem(new ColumnExpression(cd.getName()))); } catch (ParseException e) { e.printStackTrace(); } i++; } } } } */ //bind the other expressions if(selectStar) { outputDef=inputDef; minOutputDef=inputDef; } else { bindAggregates(c); outputDef=new TupleDefinition(); minOutputDef=new TupleDefinition(); for(SelectItem item:selectList) { if(item!=SelectItem.STAR) { item.expr.bind((aggOutputDef==null)?inputDef:aggOutputDef); outputDef.addColumn(item.getName(), item.expr.getType()); } } } } private void bindAggregates(ExecutionContext c) throws StreamSqlException { //collect aggregates aggList=new ArrayList<AggregateExpression>(); for(SelectItem item:selectList) { if(item!=SelectItem.STAR) item.expr.collectAggregates(aggList); } //bind aggregates if(!aggList.isEmpty()) { if(windowSpec==null) throw new StreamSqlException(ErrCode.AGGREGATE_WITHOUT_WINDOW,"Cannot use aggregates unless windows are also used"); //build aggInput aggInputDef=new TupleDefinition(); aggInputList=new ArrayList<Expression>(); boolean hasStars=false; for(AggregateExpression aggExpr:aggList) { if(aggExpr.star) { hasStars=true; break; } } if(hasStars) {//add all the columns from inputDef for(ColumnDefinition cd:inputDef.getColumnDefinitions()) { aggInputDef.addColumn(cd); try { aggInputList.add(new ColumnExpression(cd.getName())); } catch (ParseException e) { throw new StreamSqlException(ErrCode.ERROR, e.toString()); } } } else if(windowSpec.type==WindowSpecification.Type.FIELD) {//add all the fields from the windowSpec aggInputDef.addColumn(inputDef.getColumn(windowSpec.field)); try { aggInputList.add(new ColumnExpression(windowSpec.field)); } catch (ParseException e) { throw new StreamSqlException(ErrCode.ERROR, e.toString()); } } //add all the fields from the groupBy TODO boolean hasComputations=false; //add all children of the aggregate expressions for(AggregateExpression aggExpr:aggList) { if(aggExpr.children==null)continue; for(Expression expr:aggExpr.children) { expr.bind(inputDef); if(aggInputDef.getColumn(expr.getColName())==null){ aggInputDef.addColumn(expr.getColName(), expr.getType()); aggInputList.add(expr); } if(!(expr instanceof ColumnExpression)) hasComputations=true; } } if(!hasComputations) { aggInputDef=null; aggInputList=null; } aggOutputDef=new TupleDefinition(); for(AggregateExpression aggExpr:aggList) { aggExpr.bindAggregate((aggInputDef==null)?inputDef:aggInputDef); aggOutputDef.addColumn(aggExpr.getColName(), aggExpr.getType()); } } } @Override public TupleDefinition getOutputDefinition() { return outputDef; } @Override public AbstractStream execute(ExecutionContext c) throws StreamSqlException { AbstractStream stream=tupleSourceExpression.execute(c); if((stream instanceof DbReaderStream) && (whereClause!=null)) { DbReaderStream dbStream=(DbReaderStream) stream; whereClause=whereClause.addFilter(dbStream); } CompiledExpression cWhereClause=(whereClause==null)?null:whereClause.compile(); List<CompiledExpression> caggInputList=null; if(aggInputList!=null) { caggInputList=new ArrayList<CompiledExpression>(); for(Expression expr:aggInputList) { caggInputList.add(expr.compile()); } } List<CompiledAggregateExpression> caggList=null; if(aggOutputDef!=null) { caggList=new ArrayList<CompiledAggregateExpression>(); for(AggregateExpression aexpr:aggList) { caggList.add(aexpr.getCompiledAggregate()); } } List<CompiledExpression> cselectList=null; if(!selectStar) { cselectList=new ArrayList<CompiledExpression>(); for(SelectItem item:selectList) { if(item!=SelectItem.STAR) { cselectList.add(item.expr.compile()); } else { cselectList.add(SelectStream.STAR); } } } WindowProcessor windowProc=null; if(windowSpec!=null) windowProc=WindowProcessor.getInstance(windowSpec, aggInputDef, caggList, aggOutputDef); if(cWhereClause!=null || caggInputList!=null || windowProc!=null || cselectList!=null) { stream=new SelectStream(YarchDatabase.getInstance(c.getDbName()), stream, cWhereClause, caggInputList, windowProc, cselectList, outputDef, minOutputDef); } return stream; } }