Skip to content

Commit

Permalink
MONDRIAN: Fix bug 1755778, "CrossJoin / Filter query returns null row…
Browse files Browse the repository at this point in the history
… in result set". Problem was that named sets were instantiating the sets using an iterable calc, and the cache misses were being resolved after the result had been returned. Contributed by Robin Tharappel.

[git-p4: depot-paths = "//open/mondrian/": change = 10731]
  • Loading branch information
julianhyde committed Mar 19, 2008
1 parent 01ad2ed commit 027e08d
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 26 deletions.
49 changes: 38 additions & 11 deletions src/main/mondrian/olap/Query.java
Expand Up @@ -413,7 +413,7 @@ public void resolve() {
resolve(validator); // resolve self and children
// Create a dummy result so we can use its evaluator
final Evaluator evaluator = RolapUtil.createEvaluator(this);
ExpCompiler compiler = createCompiler(evaluator, validator);
ExpCompiler compiler = createCompiler(evaluator, validator, Collections.singletonList(resultStyle));
compile(compiler);
}

Expand Down Expand Up @@ -1025,28 +1025,55 @@ public Hierarchy[] getMdxHierarchiesOnAxis(AxisOrdinal axis) {
return collectHierarchies(queryAxis.getSet());
}

public Calc compileExpression(Exp exp, boolean scalar) {
/**
* Compiles an expression, using a cached compiled expression if available.
*
* @param exp Expression
* @param scalar Whether expression is scalar
* @param resultStyle Preferred result style; if null, use query's default
* result style; ignored if expression is scalar
* @return compiled expression
*/
public Calc compileExpression(
Exp exp,
boolean scalar,
ResultStyle resultStyle)
{
Evaluator evaluator = RolapEvaluator.create(this);
final Validator validator = createValidator();
final ExpCompiler compiler = createCompiler(evaluator, validator);
Calc calc = (scalar)
? compiler.compileScalar(exp, false)
: compiler.compile(exp);
return calc;
List<ResultStyle> resultStyleList;
resultStyleList =
Collections.singletonList(
resultStyle != null ? resultStyle : this.resultStyle);
final ExpCompiler compiler =
createCompiler(
evaluator, validator, resultStyleList);
if (scalar) {
return compiler.compileScalar(exp, false);
} else {
return compiler.compile(exp);
}
}

public ExpCompiler createCompiler() {
return createCompiler(RolapEvaluator.create(this), createValidator());
Evaluator evaluator = RolapEvaluator.create(this);
Validator validator = createValidator();
return createCompiler(
evaluator,
validator,
Collections.singletonList(resultStyle));
}

private ExpCompiler createCompiler(
final Evaluator evaluator, final Validator validator) {

final Evaluator evaluator,
final Validator validator,
List<ResultStyle> resultStyleList)
{
ExpCompiler compiler =
ExpCompiler.Factory.getExpCompiler(
evaluator,
validator,
Collections.singletonList(resultStyle));
resultStyleList);

final int expDeps =
MondrianProperties.instance().TestExpDependencies.get();
Expand Down
29 changes: 20 additions & 9 deletions src/main/mondrian/rolap/RolapEvaluator.java
Expand Up @@ -12,8 +12,7 @@
*/

package mondrian.rolap;
import mondrian.calc.Calc;
import mondrian.calc.ParameterSlot;
import mondrian.calc.*;
import mondrian.olap.*;
import mondrian.olap.fun.FunUtil;
import mondrian.rolap.sql.SqlQuery;
Expand Down Expand Up @@ -218,7 +217,8 @@ protected static class RolapEvaluatorRoot {
final RolapCube cube;
final RolapConnection connection;
final SchemaReader schemaReader;
final Map<Exp, Calc> compiledExps = new HashMap<Exp, Calc>();
final Map<List<Object>, Calc> compiledExps =
new HashMap<List<Object>, Calc>();
private final Query query;
private final Date queryStartTime;
final SqlQuery.Dialect currentDialect;
Expand Down Expand Up @@ -251,12 +251,23 @@ public RolapEvaluatorRoot(Query query) {
* expressions.
*
* <p>TODO: Save compiled expressions somewhere better.
*
* @param exp Expression
* @param scalar Whether expression is scalar
* @param resultStyle Preferred result style; if null, use query's default
* result style; ignored if expression is scalar
* @return compiled expression
*/
final Calc getCompiled(Exp exp, boolean scalar) {
Calc calc = compiledExps.get(exp);
final Calc getCompiled(
Exp exp,
boolean scalar,
ResultStyle resultStyle)
{
List<Object> key = Arrays.asList(exp, scalar, resultStyle);
Calc calc = compiledExps.get(key);
if (calc == null) {
calc = query.compileExpression(exp, scalar);
compiledExps.put(exp, calc);
calc = query.compileExpression(exp, scalar, resultStyle);
compiledExps.put(key, calc);
}
return calc;
}
Expand Down Expand Up @@ -564,7 +575,7 @@ public final Object evaluateCurrent() {
final RolapEvaluator evaluator = push(defaultMember);
evaluator.setExpanding(maxSolveMember);
final Exp exp = maxSolveMember.getExpression();
final Calc calc = root.getCompiled(exp, true);
final Calc calc = root.getCompiled(exp, true, null);
final Object o = calc.evaluate(evaluator);
if (o == Util.nullValue) {
return null;
Expand Down Expand Up @@ -722,7 +733,7 @@ public final String getFormatString() {
if (formatExp == null) {
return "Standard";
}
final Calc formatCalc = root.getCompiled(formatExp, true);
final Calc formatCalc = root.getCompiled(formatExp, true, null);
final Object o = formatCalc.evaluate(this);
if (o == null) {
return "Standard";
Expand Down
6 changes: 2 additions & 4 deletions src/main/mondrian/rolap/RolapResult.java
Expand Up @@ -12,8 +12,6 @@
*/

package mondrian.rolap;
import mondrian.calc.Calc;
import mondrian.calc.ParameterSlot;
import mondrian.olap.*;
import mondrian.olap.fun.MondrianEvaluationException;
import mondrian.resource.MondrianResource;
Expand All @@ -23,7 +21,7 @@

import mondrian.olap.fun.FunUtil;
import mondrian.olap.type.ScalarType;
import mondrian.calc.DummyExp;
import mondrian.calc.*;
import mondrian.calc.impl.ValueCalc;

import org.apache.log4j.Logger;
Expand Down Expand Up @@ -1192,7 +1190,7 @@ protected Object evaluateNamedSet(String name, Exp exp) {
if (value == null) {
final RolapEvaluator.RolapEvaluatorRoot root =
slicerEvaluator.root;
final Calc calc = root.getCompiled(exp, false);
final Calc calc = root.getCompiled(exp, false, ResultStyle.LIST);
Object o = result.evaluateExp(calc, slicerEvaluator.push());
List list;
if (o instanceof List) {
Expand Down
65 changes: 65 additions & 0 deletions testsrc/main/mondrian/test/BasicQueryTest.java
Expand Up @@ -4506,6 +4506,71 @@ public void testDependsOn() {
"Row #1: 15,111\n"));
}

/**
* Testcase for bug 1755778, "CrossJoin / Filter query returns null row in
* result set"
*
* @throws Exception on error
*/
public void testFilterWithCrossJoin() throws Exception {
String queryWithFilter =
"WITH SET [#DataSet#] AS 'Filter(Crossjoin({[Store].[All Stores]}, {[Customers].[All Customers]}), " +
"[Measures].[Unit Sales] > 5)' " +
"MEMBER [Customers].[#GT#] as 'Aggregate({[#DataSet#]})' " +
"MEMBER [Store].[#GT#] as 'Aggregate({[#DataSet#]})' " +
"SET [#GrandTotalSet#] as 'Crossjoin({[Store].[#GT#]}, {[Customers].[#GT#]})' " +
"SELECT {[Measures].[Unit Sales]} " +
"on columns, Union([#GrandTotalSet#], Hierarchize({[#DataSet#]})) on rows FROM [Sales]";

String queryWithoutFilter =
"WITH SET [#DataSet#] AS 'Crossjoin({[Store].[All Stores]}, {[Customers].[All Customers]})' " +
"SET [#GrandTotalSet#] as 'Crossjoin({[Store].[All Stores]}, {[Customers].[All Customers]})' " +
"SELECT {[Measures].[Unit Sales]} on columns, Union([#GrandTotalSet#], Hierarchize({[#DataSet#]})) " +
"on rows FROM [Sales]";


String wrongResultWithFilter = "Axis #0:\n" +
"{}\n" +
"Axis #1:\n" +
"{[Measures].[Unit Sales]}\n" +
"Axis #2:\n" +
"{[Store].[#GT#], [Customers].[#GT#]}\n" +
"Row #0: \n";

String expectedResultWithFilter = "Axis #0:\n" +
"{}\n" +
"Axis #1:\n" +
"{[Measures].[Unit Sales]}\n" +
"Axis #2:\n" +
"{[Store].[#GT#], [Customers].[#GT#]}\n" +
"{[Store].[All Stores], [Customers].[All Customers]}\n" +
"Row #0: 266,773\n" +
"Row #1: 266,773\n";

String expectedResultWithoutFilter = "Axis #0:\n" +
"{}\n" +
"Axis #1:\n" +
"{[Measures].[Unit Sales]}\n" +
"Axis #2:\n" +
"{[Store].[All Stores], [Customers].[All Customers]}\n" +
"Row #0: 266,773\n";

// With bug 1755778, the following test below fails because it returns
// only row that have a null value (see "wrongResultWithFilter").
// It should return the "expectedResultWithFilter" value.
assertQueryReturns(queryWithFilter, fold(expectedResultWithFilter));

// To see the test case return the correct result comment out the line
// above and uncomment out the lines below following. If a similar
// query without the filter is executed (queryWithoutFilter) prior to
// running the query with the filter then the correct result set is
// returned
assertQueryReturns(
queryWithoutFilter, fold(expectedResultWithoutFilter));
assertQueryReturns(
queryWithFilter, fold(expectedResultWithFilter));
}

/**
* This resulted in {@link OutOfMemoryError} when the
* BatchingCellReader did not know the values for the tuples that
Expand Down
4 changes: 2 additions & 2 deletions testsrc/main/mondrian/test/TestContext.java
Expand Up @@ -590,7 +590,7 @@ public String compileExpression(String expression, final boolean scalar) {
} else {
exp = query.axes[0].getSet();
}
final Calc calc = query.compileExpression(exp, scalar);
final Calc calc = query.compileExpression(exp, scalar, null);
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
final CalcWriter calcWriter = new CalcWriter(pw);
Expand Down Expand Up @@ -1157,7 +1157,7 @@ private void checkDependsOn(
String expectedDimList,
final boolean scalar)
{
final Calc calc = query.compileExpression(expression, scalar);
final Calc calc = query.compileExpression(expression, scalar, null);
final Dimension[] dimensions = query.getCube().getDimensions();
StringBuilder buf = new StringBuilder("{");
int dependCount = 0;
Expand Down

0 comments on commit 027e08d

Please sign in to comment.