/* // $Id: //guest/julian_hyde/saffron/src/main/saffron/rel/java/JavaAggregate.java#1 $ // Saffron preprocessor and data engine // Copyright (C) 2002 Julian Hyde <julian.hyde@mail.com> // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. // // See the COPYING file located in the top-level-directory of // the archive of this library for complete text of license. */ package saffron.rel.java; import openjava.mop.OJClass; import openjava.mop.OJField; import openjava.mop.Toolbox; import openjava.ptree.*; import saffron.Planner; import saffron.opt.CallingConvention; import saffron.opt.Cluster; import saffron.opt.Cost; import saffron.opt.Implementor; import saffron.rel.Rel; import saffron.util.Util; /** * <code>JavaAggregate</code> implements the {@link Aggregate} relational * operator by generating code. The code looks like this:<blockquote> * * <pre>HashMap h = new HashMap(); * <i>for each child</i> * HashableArray groups = new HashableArray( * new Object[] { * <i>child</i>.field4, * <i>child</i>.field2}); * Object[] aggs = h.get(groups); * if (aggs == null) { * aggs = new Object[<<aggCount>>]; * aggs[0] = <i>agg start code</i>; * aggs[1] = <i>agg start code</i>; * h.put(groups, aggs); * } * <i>agg inc code {aggs[0], childRow}<i/> * <i>agg inc code {aggs[1], childRow}<i/> * <i>end for</i> * Iterator keys = h.keys(); * while (keys.hasNext()) { * T row = new T(); * HashableArray groups = (HashableArray) keys.next(); * row.c0 = groups[0]; * row.c1 = groups[1]; * Object[] aggs = h.get(groups); * row.c2 = <i>agg result code {aggs[0]}</i> * row.c3 = <i>agg result code {aggs[1]}</i> * <i>emit row to parent</i> * }</pre></blockquote> * * Simplifications:<ul> * <li>If there is onle one group column, the <code>HashableArray</code> is * simplified to <code>Object</code>.</li> * <li>If there is only one aggregate, the <code>Object[]</code> are * simplified to <code>Object</code>.</li> * <li>todo: Write an efficient implementation if there are no group * columns.</li> * <li>If there are no aggregators, {@link JavaDistinct} is * more efficient.</li> * </ul> **/ public class JavaAggregate extends saffron.rel.Aggregate { Variable var_h; public JavaAggregate( Cluster cluster, Rel child, int groupCount, Call[] aggCalls) { super(cluster, child, groupCount, aggCalls); } // implement Rel public Object clone() { return new JavaAggregate(cluster, child, groupCount, aggCalls); } public int getConvention() { return CallingConvention.JAVA; } public Cost computeSelfCost(Planner planner) { double dRows = child.getRows(), // reflects memory cost also dCpu = Util.nLogN(dRows) + aggCalls.length * 4, dIo = 0; return planner.makeCost(dRows, dCpu, dIo); } public Object implement(Implementor implementor, int ordinal) { switch (ordinal) { case -1: { // Generate // HashMap h = new HashMap(); // <<child>> // Iterator keys = h.keySet().iterator(); // while (keys.hasNext()) { // T row = new T(); // HashableArray groups = (HashableArray) keys.next(); // row.c0 = groups[0]; // row.c1 = groups[1]; // (or, if groupCount == 1, // Object groups = keys.next(); // row.c0 = (T1) groups; // ) // Object[] aggs = h.get(groups); // row.c2 = <<agg result code {aggs[0]}>> // row.c3 = <<agg result code {aggs[1]}>> // (or, if aggCount == 1 // Object agg = h.get(groups); // row.c2 = <<agg result code {agg}>> // ) // <<emit row to parent>> // } StatementList stmtList = implementor.getStatementList(); this.var_h = implementor.newVariable(); Variable var_keys = implementor.newVariable(); stmtList.add( new VariableDeclaration( new TypeName("java.util.HashMap"), var_h.toString(), new AllocationExpression( new TypeName("java.util.HashMap"), null))); Object o = implementor.implementChild(this, 0, child); Util.assert(o == null); stmtList.add( new VariableDeclaration( new TypeName("java.util.Iterator"), var_keys.toString(), new MethodCall( new MethodCall( var_h, "keySet", null), "iterator", null))); StatementList stmtList2 = new StatementList(); stmtList.add( new WhileStatement( new MethodCall( var_keys, "hasNext", null), stmtList2)); // T row = new T(); OJClass rowType = getRowType(); Variable var_row = implementor.newVariable(); stmtList2.add( new VariableDeclaration( TypeName.forOJClass(rowType), var_row.toString(), new AllocationExpression( TypeName.forOJClass(rowType), null))); Variable var_groups = implementor.newVariable(); if (groupCount == 1) { // Object groups = keys.next(); // row.c0 = (T0) groups; int i = 0; OJField field = rowType.getDeclaredFields()[i]; stmtList2.add( new VariableDeclaration( TypeName.forOJClass(Toolbox.clazzObject), var_groups.toString(), new MethodCall( var_keys, "next", null))); stmtList2.add( new ExpressionStatement( new AssignmentExpression( new FieldAccess( var_row, field.getName()), AssignmentExpression.EQUALS, Toolbox.castObject( field.getType(), var_groups)))); } else { // HashableArray groups = (HashableArray) keys.next(); // row.c0 = (T0) groups[0]; // row.c1 = (T1) groups[1]; throw Util.newInternal("todo: implement"); } Variable var_aggs = implementor.newVariable(); if (groupCount == 1) { // Object agg = h.get(groups); // row.c2 = <<agg result code {agg}>> stmtList2.add( new VariableDeclaration( TypeName.forOJClass(Toolbox.clazzObject), var_aggs.toString(), new MethodCall( var_h, "get", new ExpressionList( var_groups)))); int i = 0; OJField field = rowType.getDeclaredFields()[groupCount + i]; stmtList2.add( new ExpressionStatement( new AssignmentExpression( new FieldAccess( var_row, field.getName()), AssignmentExpression.EQUALS, aggCalls[i].implementResult(var_aggs)))); } else { // Object[] aggs = h.get(groups); // row.c2 = <<agg result code {aggs[0]}>> // row.c3 = <<agg result code {aggs[1]}>> throw Util.newInternal("todo: implement"); } implementor.bind(this, var_row); implementor.generateParentBody(this, stmtList2); return null; } case 0: // called from child { // Generate // HashableArray groups = new HashableArray( // new Object[] { // <<child variable>>.field4, // <<child variable>>.field2}); // Object[] aggs = h.get(groups); // if (aggs == null) { // aggs = new Object[<<aggCount>>]; // aggs[0] = <<agg start code>>; // aggs[1] = <<agg start code>>; // h.put(groups, aggs); // } // <<agg inc code {aggs[0], childRow}>> // <<agg inc code {aggs[1], childRow}>> StatementList stmtList = implementor.getStatementList(); Variable var_groups = implementor.newVariable(); if (groupCount == 1) { // Object groups = <<child variable>>.field4; int i = 0; stmtList.add( new VariableDeclaration( TypeName.forOJClass(Toolbox.clazzObject), var_groups.toString(), implementor.translateInputField(this, 0, i))); } else { // HashableArray groups = new HashableArray( // new Object[] { // <<child variable>>.field4, // <<child variable>>.field2}); throw Util.newInternal("todo:"); } Variable var_aggs = implementor.newVariable(); if (aggCalls.length == 1) { // Object aggs = h.get(groups); stmtList.add( new VariableDeclaration( TypeName.forOJClass(Toolbox.clazzObject), var_aggs.toString(), new MethodCall( var_h, "get", new ExpressionList( var_groups)))); } else { // Object[] aggs = (Object[]) h.get(groups); stmtList.add( new VariableDeclaration( TypeName.forOJClass(Toolbox.clazzObjectArray), var_aggs.toString(), new CastExpression( Toolbox.clazzObjectArray, new MethodCall( var_h, "get", new ExpressionList( var_groups))))); } // if (aggs == null) ... StatementList ifBlock = new StatementList(); stmtList.add( new IfStatement( new BinaryExpression( var_aggs, BinaryExpression.EQUAL, Literal.constantNull()), ifBlock)); if (aggCalls.length == 1) { // aggs = <<aggs[0] start code>>; int i = 0; ifBlock.add( new ExpressionStatement( new AssignmentExpression( var_aggs, AssignmentExpression.EQUALS, aggCalls[i].implementStart(implementor, this)))); } else { // aggs = new Object[] { // <<aggs[0] start code>>, // <<aggs[1] start code>>}; ExpressionList arrayInitializer = new ExpressionList(); ifBlock.add( new ExpressionStatement( new AssignmentExpression( var_aggs, AssignmentExpression.EQUALS, new ArrayAllocationExpression( TypeName.forOJClass(Toolbox.clazzObjectArray), arrayInitializer)))); for (int i = 0; i < aggCalls.length; i++) { arrayInitializer.add( aggCalls[i].implementStart(implementor, this)); } } // h.put(groups, aggs); ifBlock.add( new ExpressionStatement( new MethodCall( var_h, "put", new ExpressionList( var_groups, var_aggs)))); // <<agg inc code {aggs[0], child row}>> // <<agg inc code {aggs[1], child row}>> for (int i = 0; i < aggCalls.length; i++) { Util.assert(aggCalls[i].getArgs().length == 1); aggCalls[i].implementNext( implementor, this, aggCalls.length == 1 ? (Expression) var_aggs : (Expression) new ArrayAccess( var_aggs, Literal.makeLiteral(i))); } return null; } default: throw Util.newInternal( "implement: ordinal=" + ordinal); } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 1748 | Julian Hyde |
saffron: convert unit tests to JUnit; add CallingConvention.ITERABLE; lots of other stuff; release 0.1. |
||
#2 | 1474 | Julian Hyde |
saffron: Aggregations are working. Renamed 'aggregator' to 'aggregation'. |
||
#1 | 1467 | Julian Hyde |
saffron: First saffron check-in; incorporate my changes to openjava. |