Skip to content

Commit

Permalink
MONDRIAN: LER-6530. Optimize mondrian parsing to not validate the las…
Browse files Browse the repository at this point in the history
…t token in a calculated member name. Extended SQL validation framework to include testing SQLs generated during MDX query parse.

[git-p4: depot-paths = "//open/mondrian/": change = 9950]
  • Loading branch information
Benny Chow committed Oct 4, 2007
1 parent 73ebcd3 commit 5df7e00
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 32 deletions.
10 changes: 8 additions & 2 deletions src/main/mondrian/olap/Formula.java
Expand Up @@ -181,9 +181,15 @@ void createElement(Query q) {
OlapElement mdxElement = q.getCube();
final SchemaReader schemaReader = q.getSchemaReader(true);
for (int i = 0; i < id.getSegments().size(); i++) {
Id.Segment segment = id.getSegments().get(i);
Id.Segment segment = id.getSegments().get(i);
OlapElement parent = mdxElement;
mdxElement = schemaReader.getElementChild(parent, segment);
mdxElement = null;
// BCHOW: The last segment of the id is the name of the calculated member
// so no need to look for a pre-existing child. This avoids
// unnecessarily executing SQL and loading children into cache.
if (i != id.getSegments().size() - 1)
mdxElement = schemaReader.getElementChild(parent, segment);

// Don't try to look up the member which the formula is
// defining. We would only find one if the member is overriding
// a member at the cube or schema level, and we don't want to
Expand Down
53 changes: 29 additions & 24 deletions testsrc/main/mondrian/rolap/BatchTestCase.java
Expand Up @@ -197,7 +197,7 @@ private RolapCube lookupCube(String cubeName) {
* @param patterns Set of patterns for expected SQL statements
*/
protected void assertQuerySql(String mdxQuery, SqlPattern[] patterns) {
assertQuerySqlOrNot(getTestContext(), mdxQuery, patterns, false, true);
assertQuerySqlOrNot(getTestContext(), mdxQuery, patterns, false, false, true);
}

/**
Expand All @@ -210,7 +210,7 @@ protected void assertQuerySql(String mdxQuery, SqlPattern[] patterns) {
*/
protected void assertQuerySql(
TestContext testContext, String mdxQuery, SqlPattern[] patterns) {
assertQuerySqlOrNot(testContext, mdxQuery, patterns, false, true);
assertQuerySqlOrNot(testContext, mdxQuery, patterns, false, false, true);
}

/**
Expand All @@ -221,7 +221,7 @@ protected void assertQuerySql(
* @param patterns Set of patterns for expected SQL statements
*/
protected void assertNoQuerySql(String mdxQuery, SqlPattern[] patterns) {
assertQuerySqlOrNot(getTestContext(), mdxQuery, patterns, true, true);
assertQuerySqlOrNot(getTestContext(), mdxQuery, patterns, true, false, true);
}

/**
Expand All @@ -236,36 +236,37 @@ protected void assertQuerySql(
String mdxQuery,
SqlPattern[] patterns,
boolean clearCache) {
assertQuerySqlOrNot(getTestContext(), mdxQuery, patterns, false, clearCache);
assertQuerySqlOrNot(getTestContext(), mdxQuery, patterns, false, false, clearCache);
}

/**
* Checks that a given MDX query results (or does not result) in a
* particular SQL statement being generated.
* During MDX query parse and execution, checks that the query results
* (or does not result) in a particular SQL statement being generated.
*
* <p>Runs the MDX query once for each SQL pattern in the current
* dialect. If there are multiple patterns, runs the MDX query multiple
* times, and expects to see each SQL statement appear. If there are no
* patterns in this dialect, the test trivially succeeds.
* <p>Parses and executes the MDX query once for each SQL
* pattern in the current dialect. If there are multiple patterns, runs the
* MDX query multiple times, and expects to see each SQL statement appear.
* If there are no patterns in this dialect, the test trivially succeeds.
*
* @param testContext non-default test context if required
* @param mdxQuery MDX query
* @param patterns Set of patterns
* @param negative false to assert if SQL is generated;
* true to assert if SQL is NOT generated
* @param bypassSchemaCache whether to grab a new connection and bypass the
* schema cache before parsing the MDX query
* @param clearCache whether to clear cache before executing the MDX query
*/
protected void assertQuerySqlOrNot(
TestContext testContext,
String mdxQuery,
SqlPattern[] patterns,
boolean negative,
boolean clearCache)
TestContext testContext,
String mdxQuery,
SqlPattern[] patterns,
boolean negative,
boolean bypassSchemaCache,
boolean clearCache)
{
final Connection connection = testContext.getConnection();
final Query query = connection.parseQuery(mdxQuery);
final RolapCube cube = (RolapCube) query.getCube();
RolapSchema schema = cube.getSchema();
Connection connection = testContext.getConnection();
RolapSchema schema = (RolapSchema)connection.getSchema();

// Run the test once for each pattern in this dialect.
// (We could optimize and run it once, collecting multiple queries, and
Expand All @@ -282,17 +283,20 @@ protected void assertQuerySqlOrNot(
String sql = sqlPattern.getSql();
String trigger = sqlPattern.getTriggerSql();

if (clearCache) {
clearCache(cube);
}

// Create a dummy DataSource which will throw a 'bomb' if it is
// asked to execute a particular SQL statement, but will otherwise
// behave exactly the same as the current DataSource.
RolapUtil.threadHooks.set(new TriggerHook(trigger));

Bomb bomb;
try {
if (bypassSchemaCache) {
connection = testContext.getFoodMartConnection(false);
}
final Query query = connection.parseQuery(mdxQuery);
if (clearCache) {
clearCache((RolapCube)query.getCube());
}
final Result result = connection.execute(query);
Util.discard(result);
bomb = null;
Expand All @@ -313,7 +317,8 @@ protected void assertQuerySqlOrNot(
}
}
}



private void clearCache(RolapCube cube) {
// Clear the cache for the Sales cube, so the query runs as if
// for the first time. (TODO: Cleaner way to do this.)
Expand Down
10 changes: 5 additions & 5 deletions testsrc/main/mondrian/rolap/TestAggregationManager.java
Expand Up @@ -254,7 +254,7 @@ public void testUniqueMembers() {
" and `agg_c_14_sales_fact_1997`.`the_year` = 1997 " +
"group by `store`.`store_state`," +
" `agg_c_14_sales_fact_1997`.`the_year`",
26
50
)
};
} else {
Expand All @@ -272,7 +272,7 @@ public void testUniqueMembers() {
" and `sales_fact_1997`.`time_id` = `time_by_day`.`time_id`" +
" and `time_by_day`.`the_year` = 1997 " +
"group by `store`.`store_state`, `time_by_day`.`the_year`",
26
50
)
};
}
Expand Down Expand Up @@ -726,7 +726,7 @@ public void testColumnCadinalityCache() {
// This MDX should be able to reuse the cardinality for
// [Product].[Product Family]; and should not issue a SQL to fetch
// that from DB again.
assertQuerySqlOrNot(context, query2, patterns, true, false);
assertQuerySqlOrNot(context, query2, patterns, true, false, false);
}

public void testKeyExpressionCardinalityCache() {
Expand Down Expand Up @@ -829,10 +829,10 @@ public void testKeyExpressionCardinalityCache() {
testContext.executeQuery(query);

// Query1a will find the "store"."store_country" cardinality in cache.
assertQuerySqlOrNot(testContext, query1, patterns1, true, false);
assertQuerySqlOrNot(testContext, query1, patterns1, true, false, false);

// Query2 again will not find the "store_ragged"."store_country" cardinality in cache.
assertQuerySqlOrNot(testContext, query2, patterns2, false, false);
assertQuerySqlOrNot(testContext, query2, patterns2, false, false, false);
}
}

Expand Down
24 changes: 23 additions & 1 deletion testsrc/main/mondrian/test/TestCalculatedMembers.java
Expand Up @@ -15,6 +15,7 @@
import junit.framework.Assert;
import junit.framework.AssertionFailedError;
import mondrian.olap.*;
import mondrian.rolap.BatchTestCase;

/**
* Tests the expressions used for calculated members. Please keep in sync
Expand All @@ -24,7 +25,7 @@
* @since 5 October, 2002
* @version $Id$
*/
public class TestCalculatedMembers extends FoodMartTestCase {
public class TestCalculatedMembers extends BatchTestCase {
public TestCalculatedMembers() {
super();
}
Expand Down Expand Up @@ -992,6 +993,27 @@ public void testStrToSetInCubeCalcMember() {
+ "select {[Measures].[My Tuple]} on 0 from [Sales]",
desiredResult);
}

public void testCreateCalculatedMember() {

String query = "WITH MEMBER [Product].[Calculated Member] as 'AGGREGATE({})'\n"
+ "SELECT {[Measures].[Unit Sales]} on 0\n"
+ "FROM [Sales]\n"
+ "WHERE ([Product].[Calculated Member])";

String derbySQL =
"select \"product_class\".\"product_family\" from \"product\" as \"product\", \"product_class\" as \"product_class\" where \"product\".\"product_class_id\" = \"product_class\".\"product_class_id\" and UPPER(\"product_class\".\"product_family\") = UPPER('Calculated Member') group by \"product_class\".\"product_family\" order by \"product_class\".\"product_family\" ASC";

String mysqlSQL =
"select `product_class`.`product_family` as `c0` from `product` as `product`, `product_class` as `product_class` where `product`.`product_class_id` = `product_class`.`product_class_id` and UPPER(`product_class`.`product_family`) = UPPER('Calculated Member') group by `product_class`.`product_family` order by ISNULL(`product_class`.`product_family`), `product_class`.`product_family` ASC";

SqlPattern[] patterns = {
new SqlPattern(SqlPattern.Dialect.DERBY, derbySQL, derbySQL),
new SqlPattern(SqlPattern.Dialect.MYSQL, mysqlSQL, mysqlSQL)
};

assertQuerySqlOrNot(this.getTestContext(), query, patterns, true, true, true);
}
}

// End TestCalculatedMembers.java

0 comments on commit 5df7e00

Please sign in to comment.