Skip to content

Commit

Permalink
MONDRIAN:
Browse files Browse the repository at this point in the history
* Fix WITH SET: set is now evaluated at most once per query, in the context of the slicer.
* Fix use of aggregate tables with distinct-count measures: since distinct-count cannot be rolled up, only use an aggregate table if it is a direct hit.
* SqlQuery was reporting that its DatabaseMetaData was stale (the underlying connection had since been closed), so copy all we need into a Dialect object and release the DatabaseMetaData.
* Move some of SqlQuery's methods onto Dialect.

[git-p4: depot-paths = "//open/mondrian/": change = 3631]
  • Loading branch information
julianhyde committed Jun 1, 2005
1 parent 9323476 commit 04e646f
Show file tree
Hide file tree
Showing 21 changed files with 904 additions and 797 deletions.
5 changes: 5 additions & 0 deletions src/main/mondrian/olap/Evaluator.java
Expand Up @@ -122,6 +122,11 @@ public interface Evaluator {
* exception.
*/
RuntimeException newEvalException(Object context, String s);

/**
* Evaluates a named set.
*/
Object evaluateNamedSet(String name, Exp exp);
}

// End Evaluator.java
4 changes: 2 additions & 2 deletions src/main/mondrian/olap/Mondrian.xml
Expand Up @@ -1113,7 +1113,7 @@ Revision is $Id$
this.name = name;
}
public String getExpression(mondrian.rolap.sql.SqlQuery query) {
return query.quoteIdentifier(table, name);
return query.getDialect().quoteIdentifier(table, name);
}
public String getGenericExpression() {
if (table == null) {
Expand Down Expand Up @@ -1153,7 +1153,7 @@ Revision is $Id$
return expressions[0].cdata;
}
public String getExpression(mondrian.rolap.sql.SqlQuery query) {
return query.chooseQuery(expressions);
return query.getDialect().chooseQuery(expressions);
}
public String getGenericExpression() {
for (int i = 0; i < expressions.length; i++) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/mondrian/olap/SetBase.java
Expand Up @@ -93,7 +93,7 @@ public boolean dependsOn(Dimension dimension) {
}

public Object evaluate(Evaluator evaluator) {
return exp.evaluate(evaluator);
return evaluator.evaluateNamedSet(name, exp);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/mondrian/olap/Syntax.java
Expand Up @@ -137,11 +137,11 @@ public String getSignature(String name, int returnType, int[] argTypes) {
public void unparse(String fun, Exp[] args, PrintWriter pw) {
pw.print("CASE");
int j = 0;
if (fun.equals("CaseTest")) {
if (fun.equals("_CaseTest")) {
pw.print(" ");
args[j++].unparse(pw);
} else {
Util.assertTrue(fun.equals("CaseMatch"));
Util.assertTrue(fun.equals("_CaseMatch"));
}
int clauseCount = (args.length - j) / 2;
for (int i = 0; i < clauseCount; i++) {
Expand Down
51 changes: 40 additions & 11 deletions src/main/mondrian/rolap/FastBatchingCellReader.java
Expand Up @@ -34,16 +34,19 @@
*/
public class FastBatchingCellReader implements CellReader {

private static final Logger LOGGER =
private static final Logger LOGGER =
Logger.getLogger(FastBatchingCellReader.class);


private final RolapCube cube;
private final Set pinnedSegments;
private final Map batches;
private int requestCount;

RolapAggregationManager aggMgr = AggregationManager.instance();
/**
* Indicates that the reader given incorrect results.
*/
private boolean dirty;

public FastBatchingCellReader(RolapCube cube) {
this.cube = cube;
Expand Down Expand Up @@ -83,24 +86,43 @@ void recordCellRequest(CellRequest request) {
Batch batch = (Batch) batches.get(key);
if (batch == null) {
batch = new Batch(request);
/*
System.out.println("FastBatchingCellReader: bitkey=" +request.getBatchKey());
RolapStar star = cube.getStar();
RolapStar.Column[] columns = star.lookupColumns((BitKey) request.getBatchKey());
for (int i = 0; i < columns.length; i++) {
System.out.println(" " +columns[i]);
}
*/

if (LOGGER.isDebugEnabled()) {
LOGGER.debug("FastBatchingCellReader: bitkey=" +
request.getBatchKey());
RolapStar star = cube.getStar();
if (star != null) {
RolapStar.Column[] columns =
star.lookupColumns((BitKey) request.getBatchKey());
for (int i = 0; i < columns.length; i++) {
LOGGER.debug(" " +columns[i]);
}
}
}
batches.put(key, batch);
}
batch.add(request);
}

/**
* Returns whether this reader has told a lie. This is the case if there
* are pending batches to load or if {@link #setDirty(boolean)} has been
* called.
*/
boolean isDirty() {
return dirty || !batches.isEmpty();
}

/**
* Loads pending aggregations, if any.
*
* @return Whether any aggregations were loaded.
*/
boolean loadAggregations() {
long t1 = System.currentTimeMillis();

requestCount = 0;
dirty = false;
if (batches.isEmpty()) {
return false;
}
Expand All @@ -118,6 +140,13 @@ boolean loadAggregations() {
return true;
}

/**
* Sets the flag indicating that the reader has told a lie.
*/
void setDirty(boolean dirty) {
this.dirty = dirty;
}

class Batch {

final RolapStar.Column[] columns;
Expand Down Expand Up @@ -178,7 +207,7 @@ void loadAggregation() {
if (distinctMeasure == null) {
break;
}
final String expr =
final String expr =
distinctMeasure.getExpression().getGenericExpression();
final List distinctMeasuresList = new ArrayList();
for (int i = 0; i < measuresList.size();) {
Expand Down
39 changes: 18 additions & 21 deletions src/main/mondrian/rolap/RolapEvaluator.java
Expand Up @@ -46,24 +46,25 @@ class RolapEvaluator implements Evaluator {
private final Map expResultCache;
private Member expandingMember;
private boolean nonEmpty;
private final RolapResult result;

private RolapEvaluator(
RolapCube cube,
RolapConnection connection,
RolapResult result,
Evaluator parent,
int depth,
Map expResultCache,
CellReader cellReader) {
this.cube = cube;
this.connection = connection;
this.result = result;
this.cube = (RolapCube) result.getQuery().getCube();
this.connection = (RolapConnection) result.getQuery().getConnection();
this.parent = parent;
this.depth = depth;
this.expResultCache = expResultCache;
this.cellReader = cellReader;
}

RolapEvaluator(RolapCube cube, RolapConnection connection) {
this(cube, connection, null, 0, new HashMap(), null);
RolapEvaluator(RolapResult result) {
this(result, null, 0, new HashMap(), null);
// we expect client to set CellReader

SchemaReader scr = connection.getSchemaReader();
Expand All @@ -76,16 +77,11 @@ private RolapEvaluator(

Member member = scr.getHierarchyDefaultMember(hier);

/*
DOES NOT COMPILE
newInvalidHierarchyCondition does not exist
// if there is no member, we cannot go on
// no way to produce a valid result
if (member == null)
// If there is no member, we cannot continue.
if (member == null) {
throw MondrianResource.instance().
newInvalidHierarchyCondition(hier.getUniqueName());
*/

newInvalidHierarchyCondition(hier.getUniqueName());
}

HierarchyUsage[] hierarchyUsages = cube.getUsages(hier);
if (hierarchyUsages.length != 0) {
Expand All @@ -97,12 +93,10 @@ private RolapEvaluator(
}

private RolapEvaluator(
RolapCube cube,
RolapConnection connection,
RolapResult result,
Member[] currentMembers,
RolapEvaluator parent) {
this(cube,
connection,
this(result,
parent,
parent.getDepth() + 1,
parent.expResultCache,
Expand Down Expand Up @@ -158,8 +152,7 @@ public Evaluator push() {
private final RolapEvaluator _push() {
Member[] cloneCurrentMembers = (Member[]) this.currentMembers.clone();
return new RolapEvaluator(
(RolapCube) cube,
(RolapConnection) connection,
result,
cloneCurrentMembers,
this);
}
Expand Down Expand Up @@ -482,6 +475,10 @@ public void setNonEmpty(boolean b) {
public RuntimeException newEvalException(Object context, String s) {
return FunUtil.newEvalException((FunDef) context, s);
}

public Object evaluateNamedSet(String name, Exp exp) {
return result.evaluateNamedSet(name, exp);
}
}

// End RolapEvaluator.java
81 changes: 67 additions & 14 deletions src/main/mondrian/rolap/RolapResult.java
Expand Up @@ -16,10 +16,8 @@
import mondrian.rolap.agg.AggregationManager;

import org.apache.log4j.Logger;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.List;

import java.util.*;

/**
* A <code>RolapResult</code> is the result of running a query.
Expand All @@ -35,19 +33,26 @@ class RolapResult extends ResultBase {
private static final int MAX_AGGREGATION_PASS_COUNT = 5;

private final RolapEvaluator evaluator;
/**
* Evaluator containing context resulting from evaluating the slicer.
*/
private RolapEvaluator slicerEvaluator;
private final CellKey point;
private final Map cellValues;
private final FastBatchingCellReader batchingReader;
AggregatingCellReader aggregatingReader = new AggregatingCellReader();
private final int[] modulos;
/**
* Maps the names of sets to their values. Populated on demand.
*/
private final Map namedSetValues = new HashMap();

RolapResult(Query query) {
super(query, new RolapAxis[query.axes.length]);


this.point = new CellKey(new int[query.axes.length]);
this.evaluator = new RolapEvaluator( (RolapCube) query.getCube(),
(RolapConnection) query.getConnection());
AggregatingCellReader aggregatingReader = new AggregatingCellReader();
this.evaluator = new RolapEvaluator(this);
RolapCube rcube = (RolapCube) query.getCube();
this.batchingReader = new FastBatchingCellReader(rcube);
this.cellValues = new HashMap();
Expand Down Expand Up @@ -102,14 +107,13 @@ class RolapResult extends ResultBase {
for (int j = 0; j < position.members.length; j++) {
evaluator.setContext(position.members[j]);
}
slicerEvaluator = (RolapEvaluator) evaluator.push();
} else {
this.axes[i] = axisResult;
}
}
// now, that the axes are evaluated,
// make sure, that the total number of positions does
// not exceed the result limit
// throw an exeption, if the total numer of positions gets too large
// Now that the axes are evaluated, make sure that the number of
// cells does not exceed the result limit.
int limit = MondrianProperties.instance().getResultLimit();
if (limit > 0) {
// result limit exceeded, throw an exception
Expand Down Expand Up @@ -213,11 +217,10 @@ private void executeBody(Query query) {
// evaluator which collects requests.
int count = 0;
while (true) {
//cellValues = new HashMap();
cellValues.clear();

evaluator.setCellReader(this.batchingReader);
executeStripe(query.axes.length - 1,
executeStripe(query.axes.length - 1,
(RolapEvaluator) evaluator.push());
evaluator.clearExpResultCache();

Expand All @@ -230,7 +233,7 @@ private void executeBody(Query query) {
return;
}
if (count++ > MAX_AGGREGATION_PASS_COUNT) {
throw Util.newInternal("Query required more than "
throw Util.newInternal("Query required more than "
+ count + " iterations");
}
}
Expand All @@ -240,6 +243,56 @@ private void executeBody(Query query) {
}
}

/**
* Evaluates a named set.
*
* <p>A given set is only evaluated once each time a query is executed; the
* result is added to the {@link #namedSetValues} cache on first execution
* and re-used.
*
* <p>Named sets are always evaluated in the context of the slicer.
*/
Object evaluateNamedSet(String name, Exp exp) {
Object value = namedSetValues.get(name);
if (value == null) {
value = evaluateExp(exp, (RolapEvaluator) slicerEvaluator.push());

namedSetValues.put(name, value);
}
return value;
}

private Object evaluateExp(Exp exp, RolapEvaluator evaluator) {
int attempt = 0;
boolean dirty = batchingReader.isDirty();
while (true) {
RolapEvaluator ev = (RolapEvaluator) evaluator.push();

ev.setCellReader(batchingReader);
Object preliminaryValue = exp.evaluate(ev);
Util.discard(preliminaryValue);
if (!batchingReader.loadAggregations()) {
break;
}
if (attempt++ > MAX_AGGREGATION_PASS_COUNT) {
throw Util.newInternal("Failed to load all aggregations after " +
MAX_AGGREGATION_PASS_COUNT +
"passes; there's probably a cycle");
}
}

// If there were pending reads when we entered, some of the other
// expressions may have been evaluated incorrectly. Set the reaader's
// 'dirty' flag so that the caller knows that it must re-evaluate them.
if (dirty) {
batchingReader.setDirty(true);
}
RolapEvaluator ev = (RolapEvaluator) evaluator.push();
ev.setCellReader(aggregatingReader);
Object value = exp.evaluate(ev);
return value;
}

/**
* An <code>AggregatingCellReader</code> reads cell values from the
* {@link RolapAggregationManager}.
Expand Down

0 comments on commit 04e646f

Please sign in to comment.