Skip to content

Commit

Permalink
MONDRIAN: removed dead code, added package documentation for rolap pa…
Browse files Browse the repository at this point in the history
…ckage, activated tests for FastBatchingCellReader

[git-p4: depot-paths = "//open/mondrian/": change = 2916]
  • Loading branch information
Andreas Voss committed Dec 7, 2004
1 parent cb94324 commit b45890e
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 371 deletions.
26 changes: 21 additions & 5 deletions src/main/mondrian/rolap/FastBatchingCellReader.java
Expand Up @@ -23,18 +23,18 @@
public class FastBatchingCellReader implements CellReader {

RolapCube cube;
HashSet pinnedSegments;
Set pinnedSegments;
Map batches = new HashMap();
RolapAggregationManager aggMgr = AggregationManager.instance();

FastBatchingCellReader(RolapCube cube, HashSet pinnedSegments) {
public FastBatchingCellReader(RolapCube cube, Set pinnedSegments) {
this.cube = cube;
this.pinnedSegments = pinnedSegments;
}

public Object get(Evaluator evaluator) {
RolapMember[] currentMembers = ((RolapEvaluator) evaluator).currentMembers;
CellRequest request = aggMgr.makeRequest(currentMembers, false);
CellRequest request = RolapAggregationManager.makeRequest(currentMembers, false);
if (request == null)
return Util.nullValue; // request out of bounds
// try to retrieve a cell and simultaneously pin the segment which
Expand Down Expand Up @@ -132,8 +132,7 @@ void loadAggregation() {
// distinct aggregations out.
while (true) {
// Scan for a measure based upon a distinct aggregation.
RolapStar.Measure distinctMeasure =
AggregationManager.getFirstDistinctMeasure(measuresList);
RolapStar.Measure distinctMeasure = getFirstDistinctMeasure(measuresList);
if (distinctMeasure == null) {
break;
}
Expand Down Expand Up @@ -162,6 +161,23 @@ void loadAggregation() {
long t2 = System.currentTimeMillis();
//System.out.println("Batch.loadAggregation " + (t2 - t1));
}

/**
* Returns the first measure based upon a distinct aggregation, or null if
* there is none.
* @param measuresList
* @return
*/
RolapStar.Measure getFirstDistinctMeasure(ArrayList measuresList) {
for (int i = 0; i < measuresList.size(); i++) {
RolapStar.Measure measure = (RolapStar.Measure) measuresList.get(i);
if (measure.aggregator.distinct) {
return measure;
}
}
return null;
}

}

}
44 changes: 1 addition & 43 deletions src/main/mondrian/rolap/RolapAggregationManager.java
Expand Up @@ -33,44 +33,6 @@
**/
public abstract class RolapAggregationManager implements CellReader {

/**
* Looks through a set of requests and loads aggregations
* accordingly.
*
* @param keySet A set whose keys are {@link ArrayList}s
* which contain {@link mondrian.olap.Member}s
* @param pinnedSegments Writes each loaded aggregation into here. The client must
* call {@link CachePool#unpin} on this list.
**/
public void loadAggregations(Set keySet, Collection pinnedSegments)
{
RolapMember[] members = RolapUtil.emptyMemberArray;
Hashtable mapColumnSetToBatch = new Hashtable();
ArrayList batches = new ArrayList();
for (Iterator keys = keySet.iterator(); keys.hasNext(); ) {
ArrayList key = (ArrayList) keys.next();
members = (RolapMember[]) key.toArray(members);
CellRequest request = makeRequest(members, false);
if (request == null) {
continue; // invalid location -- ignore it
}
ArrayList columnList = request.getBatchKey();
Batch batch = (Batch) mapColumnSetToBatch.get(columnList);
if (batch == null) {
batch = new Batch();
mapColumnSetToBatch.put(columnList, batch);
batches.add(batch);
}
batch.requests.add(request);
}
loadAggregations(batches, pinnedSegments);
for (Iterator segmentIter = pinnedSegments.iterator(); segmentIter.hasNext();) {
Segment segment = (Segment) segmentIter.next();
segment.waitUntilLoaded();
}
}

public abstract void loadAggregations(ArrayList batches, Collection pinnedSegments);

/**
* Creates a request to evaluate the cell identified by
Expand All @@ -83,7 +45,7 @@ public void loadAggregations(Set keySet, Collection pinnedSegments)
* query for levels below each current member. This additional context
* makes the drill-through queries easier for humans to understand.
**/
CellRequest makeRequest(RolapMember[] members, boolean extendedContext) {
static CellRequest makeRequest(RolapMember[] members, boolean extendedContext) {
if (!(members[0] instanceof RolapStoredMeasure)) {
return null;
}
Expand Down Expand Up @@ -209,10 +171,6 @@ public Object get(Evaluator evaluator) {

public abstract String getDrillThroughSQL(CellRequest request);

public static class Batch
{
public ArrayList requests = new ArrayList();
}
}

// End RolapAggregationManager.java
93 changes: 1 addition & 92 deletions src/main/mondrian/rolap/RolapResult.java
Expand Up @@ -226,97 +226,6 @@ private void executeBody(Query query) {
}
}

/**
* 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;
HashSet pinnedSegments;
ArrayList key; // contains [RolapMember 0, ..., RolapMember n - 1]

BatchingCellReader(RolapCube cube, HashSet pinnedSegments)
{
this.cube = cube;
this.keys = new HashSet();
this.pinnedSegments = pinnedSegments;
int dimensionCount = cube.getDimensions().length;
this.key = new ArrayList(dimensionCount);
for (int i = 0; i < dimensionCount; i++) {
this.key.add(null);
}
}

// 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 == Boolean.TRUE) {
// Aggregation is being loaded. (todo: Use better value, or
// throw special exception)
return RolapUtil.valueNotReadyException;
}
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.set(i, currentMembers[i]);
}
if (!keys.contains(key)) {
ArrayList clone = (ArrayList) key.clone();
keys.add(clone);
}
return RolapUtil.valueNotReadyException;
}

/**
* 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(Collection)}.
*
* @return Whether any aggregations were loaded
*
* <h3>Design discussion</h3>
*
* <p>Do we group them by (a) level, or (b) the underlying columns they
* access. I think the columns.
*
* <p>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).
*
* <p>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.)
*
* <p>For each group, extend the aggregation definition a bit, if it
* will help us roll up later.
**/
boolean loadAggregations() {
if (keys.isEmpty()) {
return false;
}
AggregationManager.instance().loadAggregations(keys, pinnedSegments);
keys.clear();
return true;
}
}

/**
* An <code>AggregatingCellReader</code> reads cell values from the
* {@link RolapAggregationManager}.
Expand Down Expand Up @@ -536,7 +445,7 @@ public boolean isError() {
*/
public String getDrillThroughSQL(boolean extendedContext) {
RolapAggregationManager aggregationManager = AggregationManager.instance();
CellRequest cellRequest = aggregationManager.makeRequest(
CellRequest cellRequest = RolapAggregationManager.makeRequest(
getEvaluator().currentMembers, extendedContext);
if (cellRequest == null) {
return null;
Expand Down
95 changes: 0 additions & 95 deletions src/main/mondrian/rolap/agg/AggregationManager.java
Expand Up @@ -45,101 +45,6 @@ public static synchronized AggregationManager instance()
return instance;
}

public void loadAggregations(ArrayList batches, Collection pinnedSegments) {
for (Iterator batchIter = batches.iterator(); batchIter.hasNext();) {
Batch batch = (Batch) batchIter.next();
ArrayList requests = batch.requests;
CellRequest firstRequest = (CellRequest) requests.get(0);
RolapStar.Column[] columns = firstRequest.getColumns();
ArrayList measuresList = new ArrayList();
HashSet[] valueSets = new HashSet[columns.length];
for (int i = 0; i < valueSets.length; i++) {
valueSets[i] = new HashSet();
}
for (int i = 0, count = requests.size(); i < count; i++) {
CellRequest request = (CellRequest) requests.get(i);
for (int j = 0; j < columns.length; j++) {
Object value = request.getValueList().get(j);
Util.assertTrue(
!(value instanceof Object[]),
"multi-valued key not valid in this cell request");
valueSets[j].add(value);
}
RolapStar.Measure measure = request.getMeasure();
if (!measuresList.contains(measure)) {
if (measuresList.size() > 0) {
Util.assertTrue(
measure.table.star ==
((RolapStar.Measure) measuresList.get(0)).table.star);
}
measuresList.add(measure);
}
}
Object[][] constraintses = new Object[columns.length][];
for (int j = 0; j < columns.length; j++) {
Object[] constraints;
HashSet valueSet = valueSets[j];
if (valueSet == null) {
constraints = null;
} else {
constraints = valueSet.toArray();
}
constraintses[j] = constraints;
}
// todo: optimize key sets; drop a constraint if more than x% of
// the members are requested; whether we should get just the cells
// requested or expand to a n-cube

// If the database cannot execute "count(distinct ...)", split the
// distinct aggregations out.
while (true) {
// Scan for a measure based upon a distinct aggregation.
RolapStar.Measure distinctMeasure = getFirstDistinctMeasure(measuresList);
if (distinctMeasure == null) {
break;
}
final String expr = distinctMeasure.expression.getGenericExpression();
final ArrayList distinctMeasuresList = new ArrayList();
for (int i = 0; i < measuresList.size();) {
RolapStar.Measure measure = (RolapStar.Measure)
measuresList.get(i);
if (measure.aggregator.distinct &&
measure.expression.getGenericExpression().equals(
expr)) {
measuresList.remove(i);
distinctMeasuresList.add(distinctMeasure);
} else {
i++;
}
}
RolapStar.Measure[] measures = (RolapStar.Measure[])
distinctMeasuresList.toArray(new RolapStar.Measure[0]);
loadAggregation(measures, columns, constraintses, pinnedSegments);
}
final int measureCount = measuresList.size();
if (measureCount > 0) {
RolapStar.Measure[] measures = (RolapStar.Measure[])
measuresList.toArray(new RolapStar.Measure[measureCount]);
loadAggregation(measures, columns, constraintses, pinnedSegments);
}
}
}

/**
* Returns the first measure based upon a distinct aggregation, or null if
* there is none.
* @param measuresList
* @return
*/
public static RolapStar.Measure getFirstDistinctMeasure(ArrayList measuresList) {
for (int i = 0; i < measuresList.size(); i++) {
RolapStar.Measure measure = (RolapStar.Measure) measuresList.get(i);
if (measure.aggregator.distinct) {
return measure;
}
}
return null;
}

public void loadAggregation(
RolapStar.Measure[] measures, RolapStar.Column[] columns,
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/mondrian/rolap/doc-files/mondrian.zargo
Binary file not shown.
30 changes: 30 additions & 0 deletions src/main/mondrian/rolap/package.html
@@ -0,0 +1,30 @@
<html>
<body>
Implements the data access layer for the olap package.

<p />


<h2>MemberReader</h2>
<img src="doc-files/MemberReader.gif" />
<p />
When a member expression like member.children is evaluated, the function
requests the SchemaReader from the Evaluator. The RolapEvaluator
uses the RolapSchemaReader which delegates most calls to one
of its MemberReaders (one per dimension). In most cases, a SmartMemberReader
will be used, which returns the requested members immediately.

<h2>CellReader</h2>
<img src="doc-files/CellReader.gif" />
<p />
Cells are evaluated multiple times. For the first time,
a FastBatchingCellReader is used. When a cell is evaluated,
Evaluator.evaluateCurrent() is called. The FastBatchingCellReader
will not compute the cells value but record a CellRequest for that cell
and return (not throw) an exception. After all CellRequests for all cells
have been recorded, the Aggregation will generate SQL to load all cells
with a single SQL query. After that the cells are evaluated again with
an AggregatingCellReader that will return the cell values from the cache.

</body>
</html>

0 comments on commit b45890e

Please sign in to comment.