/* // $Id: //guest/paul_dymecki/mondrian/src/main/mondrian/rolap/RolapResult.java#1 $ // This software is subject to the terms of the Common Public License // Agreement, available at the following URL: // http://www.opensource.org/licenses/cpl.html. // (C) Copyright 2001-2002 Kana Software, Inc. and others. // All Rights Reserved. // You must accept the terms of that agreement to use this software. // // jhyde, 10 August, 2001 */ package mondrian.rolap; import mondrian.olap.*; import mondrian.rolap.agg.AggregationManager; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.ArrayList; /** * todo: * * @author jhyde * @since 10 August, 2001 * @version $Id: //guest/paul_dymecki/mondrian/src/main/mondrian/rolap/RolapResult.java#1 $ */ class RolapResult extends ResultBase { private RolapEvaluator evaluator; private CellKey point; Hashtable cellValues = new Hashtable(); AggregatingCellReader aggregatingReader; BatchingCellReader batchingReader; RolapResult(Query query) { this.query = query; this.point = new CellKey(new int[query.axes.length]); this.axes = new RolapAxis[query.axes.length]; this.evaluator = new RolapEvaluator((RolapCube) query.getCube()); this.aggregatingReader = new AggregatingCellReader(); HashSet pinnedSegments = new HashSet(); this.batchingReader = new BatchingCellReader( (RolapCube) query.getCube(), pinnedSegments); try { for (int i = -1; i < axes.length; i++) { QueryAxis axis; if (i == -1) { if (query.slicer != null) { axis = new QueryAxis( false, new FunCall( "{}", new Exp[] {query.slicer}, FunDef.TypeBraces), "slicer", QueryAxis.subtotalsUndefined); } else { axis = null; } } else { axis = query.axes[i]; } evaluator.cellReader = batchingReader; RolapAxis axisResult = executeAxis(evaluator, axis); batchingReader.loadAggregations(); batchingReader.clear(); evaluator.cellReader = aggregatingReader; axisResult = executeAxis(evaluator, axis); if (i == -1) { this.slicerAxis = axisResult; // Use the context created by the slicer for the other // axes. For example, "select filter([Customers], [Store // Sales] > 100) on columns from Sales where // ([Time].[1998])" should show customers whose 1998 (not // total) purchases exceeded 100. Position position = this.slicerAxis.positions[0]; for (int j = 0; j < position.members.length; j++) { evaluator.setContext(position.members[j]); } } else { this.axes[i] = axisResult; } } this.cellValues = new Hashtable(); executeBody(query); } finally { CachePool.instance().unpin(pinnedSegments); } } // implement Result public Axis[] getAxes() { return axes; } public Cell getCell(int[] pos) { Object value = cellValues.get(new CellKey(pos)); if (value == null) { value = Util.nullValue; } RolapCube cube = (RolapCube) query.getCube(); RolapMember measure = (RolapMember) getMember( pos, cube.measuresHierarchy.getDimension()); // todo: set up evaluator's context better -- context dependent format // strings are going to be just WRONG Evaluator cellEvaluator = evaluator.push(RolapMember.emptyArray); return new RolapCell(measure,value,cellEvaluator); } private RolapAxis executeAxis(Evaluator evaluator, QueryAxis axis) { Position[] positions; if (axis == null) { // Create an axis containing one position with no members (not // the same as an empty axis). RolapPosition position = new RolapPosition(); position.members = new Member[0]; positions = new Position[] {position}; } else { Exp exp = axis.set; Object value = exp.evaluate(evaluator); if (value == null) { value = new Vector(); } Util.assertTrue(value instanceof Vector); Vector vector = (Vector) value; positions = new Position[vector.size()]; for (int i = 0; i < vector.size(); i++) { RolapPosition position = new RolapPosition(); Object o = vector.elementAt(i); if (o instanceof Object[]) { Object[] a = (Object[]) o; position.members = new Member[a.length]; for (int j = 0; j < a.length; j++) { position.members[j] = (Member) a[j]; } } else { position.members = new Member[] {(Member) o}; } positions[i] = position; } } return new RolapAxis(positions); } private void executeBody(Query query) { cellValues = new Hashtable(); // first time, create a dummy evaluator which collects requests this.evaluator.cellReader = this.batchingReader; executeStripe(query.axes.length - 1); // Retrieve the aggregations collected. // // Do we group them by (a) level, or (b) the underlying columns they // access. I think the columns. // // Maybe some or all of the group can be derived by rolling up. We // should probably roll up if possible (the danger is that we end up // with a fragmented aggregation, which we hit many times). // // If we roll up, do we also store? I think so. Then we can let the // caching policy kick in. (It gets interesting if we roll up, but do // not store, part of an aggregation.) // // For each group, extend the aggregation definition a bit, if it will // help us roll up later. // batchingReader.loadAggregations(); batchingReader.clear(); // second time, really execute this.evaluator.cellReader = this.aggregatingReader; executeStripe(query.axes.length - 1); } /** * A <code>BatchingCellReader</code> doesn't really read cells: when asked * to look up the values of stored measures, it lies, and records the fact * that the value was asked for. Later, we can look over the values which * are required, fetch them in an efficient way, and re-run the evaluation * with a real evaluator. * * <p>NOTE: When it doesn't know the answer, it lies by returning an error * object. The calling code must be able to deal with that.</p> **/ private static class BatchingCellReader implements CellReader { RolapCube cube; HashSet keys; java.util.Set pinnedSegments; HashableVector key; // contains [RolapMember 0, ..., RolapMember n - 1] BatchingCellReader(RolapCube cube, java.util.Set pinnedSegments) { this.cube = cube; this.keys = new HashSet(); this.pinnedSegments = pinnedSegments; this.key = new HashableVector(); this.key.setSize(cube.getDimensions().length); } void clear() { this.keys.clear(); } // implement CellReader public Object get(Evaluator evaluator) { RolapMember[] currentMembers = ((RolapEvaluator) evaluator).currentMembers; // try to retrieve a cell and simultaneously pin the segment which // contains it Object o = AggregationManager.instance().getCellFromCache( currentMembers, pinnedSegments); if (o != null) { return o; } // if there is no such cell, record that we need to fetch it, and // return 'error' for (int i = 0, count = currentMembers.length; i < count; i++) { key.setElementAt(currentMembers[i], i); } if (!keys.contains(key)) { HashableVector clone = (HashableVector) key.clone(); keys.add(clone); } return cube.getErrCellValue(); } /** Loads the aggregations which we will need. Writes the aggregations * it loads (and pins) into <code>pinned</code>; the caller must pass * this to {@link CachePool#unpin(List)}. **/ void loadAggregations() { AggregationManager.instance().loadAggregations( cube.getDimensions().length, keys, pinnedSegments); } }; /** * An <code>AggregatingCellReader</code> reads cell values from the {@link * RolapAggregationManager}. **/ private static class AggregatingCellReader implements CellReader { /** * Overrides {@link CellReader#get}. Returns <code>null</code> if no * aggregation contains the required cell. **/ // implement CellReader public Object get(Evaluator evaluator) { RolapMember[] currentMembers = ((RolapEvaluator) evaluator).currentMembers; return AggregationManager.instance().getCellFromCache( currentMembers); } }; private void executeStripe(int axis) { if (axis < 0) { RolapAxis _axis = (RolapAxis) slicerAxis; int count = _axis.positions.length; for (int i = 0; i < count; i++) { RolapPosition position = (RolapPosition) _axis.positions[i]; for (int j = 0; j < position.members.length; j++) { evaluator.setContext((RolapMember) position.members[j]); } Object o = evaluator.evaluateCurrent(); CellKey key = point.copy(); cellValues.put(key, o); } } else { RolapAxis _axis = (RolapAxis) axes[axis]; int count = _axis.positions.length; for (int i = 0; i < count; i++) { point.ordinals[axis] = i; RolapPosition position = (RolapPosition) _axis.positions[i]; for (int j = 0; j < position.members.length; j++) { evaluator.setContext((RolapMember) position.members[j]); } executeStripe(axis - 1); } } } }; class RolapAxis extends Axis { private Hashtable mapPositionToIndex = new Hashtable(); RolapAxis(Position[] positions) { this.positions = positions; for (int i = 0; i < positions.length; i++) { Position position = positions[i]; mapPositionToIndex.put(position, new Integer(i)); } } int lookupPosition(Position position) { Integer index = (Integer) mapPositionToIndex.get(position); return index == null ? -1 : index.intValue(); } }; class RolapPosition extends Position { // override Object public boolean equals(Object o) { if (o instanceof RolapPosition) { RolapPosition other = (RolapPosition) o; if (other.members.length == this.members.length) { for (int i = 0; i < this.members.length; i++) { if (this.members[i] != other.members[i]) { return false; } } return true; } } return false; } // override Object public int hashCode() { int h = 0; for (int i = 0; i < members.length; i++) { h = (h << 4) ^ members[i].hashCode(); } return h; } }; class RolapCell implements Cell { private Object value; private String formattedValue; RolapCell(RolapMember measure, Object value, Evaluator evaluator) { this.value = value; this.formattedValue = computeFormattedValue(measure, value, evaluator); } private static String computeFormattedValue( RolapMember measure, Object value, Evaluator evaluator) { if (value == Util.nullValue) { return "(null)"; } else { return evaluator.format(value); } } // implement Cell public String getFormattedValue() { return formattedValue; } // implement Cell public boolean isNull() { return value == Util.nullValue; } }; // End RolapResult.java
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 1820 | Paul Robert Dymecki | mondrian: Integrate latest from //guest/julian_hyde | ||
//guest/julian_hyde/mondrian/src/main/mondrian/rolap/RolapResult.java | |||||
#5 | 1609 | Julian Hyde |
mondrian: Forgot a couple of files last change. Unit tests for builtin functions. Don't return null members (e.g. [Gender].[F].PrevMember). |
||
#4 | 1603 | Julian Hyde |
mondrian: Add Andreas' taglib; Rename 'mondrian.rolap.Util.assert' to 'assertTrue', because 'assert' is a keyword in jdk 1.4; Fix 'NON EMPTY'; JUnit framework for tests; Add Oracle dataset. |
||
#3 | 1576 | Julian Hyde |
mondrian: fix dataset (add column customer.ordinal); create dataset for oracle; get queries working on oracle; get format strings working; refactor out new packages mondrian.rolap.agg and mondrian.rolap.sql. |
||
#2 | 1499 | Julian Hyde |
Mondrian: Re-organize functions and type-checking Add mondrian.olap.fun package |
||
#1 | 1453 | Julian Hyde | mondrian: first source check-in |