package jeql.engine.query.group; import java.util.List; import jeql.api.function.Aggregator; import jeql.api.row.Row; import jeql.api.row.RowList; import jeql.engine.query.QueryScope; import jeql.engine.query.SelectedItemsRowList; import jeql.engine.query.Tuple; import jeql.syntax.ParseTreeNode; import jeql.syntax.SelectItemList; import jeql.syntax.StatementListNode; import jeql.syntax.TableRefNode; /** * Evaluates a SELECT with a GROUP BY and aggregate functions. * * A grouped select is evaluated using the following strategy: * <ul> * <li>Fully evaluate the innerSelList, * using the rows supplied by the FROM/WHERE source * <li>Compute a new rowList which consists * of a column for each of the GROUP BY columns, * and a column for each aggregate function. * There will be one row for each unique key tuple * <li>Evaluate a rowlist of the outerSelList * applied to the grouped rowList. This is the result rowlist. * </ul> * * @author Martin Davis * */ public class GroupByEvaluator { QueryScope baseScope; List aggFunArgList; StatementListNode aliasList; GroupScope groupScope; private SelectItemList outerSelList; private int[] groupKeyAttrIndex; private int groupKeyLen = 0; public GroupByEvaluator( QueryScope baseScope, List aggFunArgList, StatementListNode aliasList, GroupScope groupScope, SelectItemList outerSelList) { this.baseScope = baseScope; this.aggFunArgList = aggFunArgList; this.aliasList = aliasList; this.groupScope = groupScope; this.outerSelList = outerSelList; groupKeyAttrIndex = groupScope.getGroupKeyIndices(); groupKeyLen = groupKeyAttrIndex.length; } public RowList eval(RowList baseRS) { GroupRowsBuilder groupRowListBuilder; if (groupKeyLen > 0) { groupRowListBuilder = new MappedGroupRowsBuilder(); } else { groupRowListBuilder = new SingleGroupRowsBuilder(); } groupRowListBuilder.init(this); // Compute rowlist for the aggregated groups RowList groupedRowList = groupRowListBuilder.eval(baseRS); // Construct rowlist for the outer select expressions RowList rowStrSel = new SelectedItemsRowList(groupedRowList, outerSelList, null, groupScope); return rowStrSel; } void evalAggregators(Aggregator[] agg, QueryScope scope) { for (int i = 0; i < aggFunArgList.size(); i++) { ParseTreeNode expr = (ParseTreeNode) aggFunArgList.get(i); Object value = null; // handle aggregator expressions of form f(*) (e.g. count - but this is more general) if (TableRefNode.isStar(expr)) { value = scope.getRow(); } else { value = expr.eval(scope); } agg[i].addValue(value); } } Tuple extractGroupKey(Row row) { if (groupKeyLen == 0) return null; Tuple tuple = new Tuple(groupKeyAttrIndex.length); for (int i = 0; i < groupKeyAttrIndex.length; i++) { tuple.setValue(i, row.getValue(groupKeyAttrIndex[i])); } return tuple; } }