/*
* This software is subject to the terms of the Eclipse Public License v1.0
* Agreement, available at the following URL:
* http://www.eclipse.org/legal/epl-v10.html.
* You must accept the terms of that agreement to use this software.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package mondrian.rolap;
import mondrian.calc.ResultStyle;
import mondrian.olap.*;
import mondrian.test.FoodMartTestCase;
import mondrian.test.TestContext;
import mondrian.util.Bug;
import junit.framework.Assert;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
/**
* Unit-test for non cacheable elementos of high dimensions.
*
* @author jlopez, lcanals
* @since May, 2008
*/
public class HighDimensionsTest extends FoodMartTestCase {
public HighDimensionsTest() {
}
public HighDimensionsTest(String name) {
super(name);
}
public void testBug1971406() throws Exception {
if (!MondrianProperties.instance().EnableNativeCrossJoin.get()) {
return;
}
final Connection connection = TestContext.instance()
.getConnection();
Query query = connection.parseQuery(
"with set necj as "
+ "NonEmptyCrossJoin(NonEmptyCrossJoin("
+ "[Customers].[Name].members,[Store].[Store Name].members),"
+ "[Product].[Product Name].members) "
+ "select {[Measures].[Unit Sales]} on columns,"
+ "tail(intersect(necj,necj,ALL),5) on rows from sales");
final long t0 = System.currentTimeMillis();
Result result = connection.execute(query);
for (final Position o : result.getAxes()[0].getPositions()) {
assertNotNull(o.get(0));
}
final long t1 = System.currentTimeMillis();
final long elapsed = t1 - t0;
// scale up for slower CPUs
double scaleFactor = computeCpuScaleFactor();
final long target = (long) (90000 * scaleFactor);
assertTrue(
"Query execution took " + elapsed + " milliseconds, "
+ "which is outside target of " + target + " milliseconds",
elapsed <= target);
}
/**
* Computes a scale factor indicating the relative system speed. This is
* necessary for environments that might run code coverage, etc.
*
* <p>The method performs a benchmark computation, and returns is the ratio
* of the elapsed time for the current system versus the baseline. If the
* current system takes, say, 2 seconds to perform the benchmark
* computation, the method returns 2.0, meaning that the system would be
* expected to take twice as long to do a typical CPU-intensive task.
*
* @return Multiplier for how long this system would require to do a typical
* CPU-intensive task versus the baseline system
*/
private static double computeCpuScaleFactor() {
final long nt0 = System.currentTimeMillis();
int t = 0;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
t += i;
}
final long nt1 = System.currentTimeMillis();
long benchmarkTime = 0;
// this check is here to make sure the compiler doesn't optimize
// out the above loop
if (t > 0) {
benchmarkTime = nt1 - nt0;
}
// The benchmark takes takes about 1 second on the baseline system, a
// Dell Latitude D820 Laptop.
double scaleFactor = (double) benchmarkTime / 1000d;
if (false) {
System.out.println("scale factor = " + scaleFactor);
}
return scaleFactor;
}
public void testPromotionsTwoDimensions() throws Exception {
if (!Bug.BugMondrian486Fixed) {
return;
}
execHighCardTest(
"select {[Measures].[Unit Sales]} on columns,\n"
+ "{[Promotions].[Promotion Name].Members} on rows\n"
+ "from [Sales Ragged]",
1,
"Promotions",
highCardResults, null, true, 51);
}
public void testHead() throws Exception {
if (!Bug.BugMondrian486Fixed) {
return;
}
execHighCardTest(
"select {[Measures].[Unit Sales]} on columns,\n"
+ "head({[Promotions].[Promotion Name].Members},40) "
+ "on rows from [Sales Ragged]",
1,
"Promotions",
first40HighCardResults, null, true, 40);
}
// disabled pending fix of bug MONDRIAN-527
public void _testTopCount() throws Exception {
final Connection connection = TestContext.instance()
.getConnection();
final StringBuffer buffer = new StringBuffer();
Query query =
connection.parseQuery(
"select {[Measures].[Unit Sales]} on columns,\n"
+ "TopCount({[Promotions].[Promotion Name].Members},41, "
+ "[Measures].[Unit Sales]) "
+ "on rows from [Sales Ragged]");
Result result = connection.execute(query);
int i = 0;
String topcount40HighCardResults = null;
String topcount41HighCardResults = null;
for (final Position o : result.getAxes()[1].getPositions()) {
buffer.append(o.get(0));
i++;
if (i == 40) {
topcount40HighCardResults = buffer.toString();
}
}
topcount41HighCardResults = buffer.toString();
execHighCardTest(
"select {[Measures].[Unit Sales]} on columns,\n"
+ "TopCount({[Promotions].[Promotion Name].Members},40, "
+ "[Measures].[Unit Sales]) "
+ "on rows from [Sales Ragged]",
1,
"Promotions",
topcount40HighCardResults, topcount40Cells, false, 40);
execHighCardTest(
"select {[Measures].[Unit Sales]} on columns,\n"
+ "TopCount({[Promotions].[Promotion Name].Members},41, "
+ "[Measures].[Unit Sales]) "
+ "on rows from [Sales Ragged]",
1,
"Promotions",
topcount41HighCardResults, topcount41Cells, false, 41);
execHighCardTest(
"select {[Measures].[Unit Sales]} on columns,\n"
+ "TopCount({[Promotions].[Promotion Name].Members},40, "
+ "[Measures].[Unit Sales]) "
+ "on rows from [Sales Ragged]",
1,
"Promotions",
topcount40HighCardResults, topcount40Cells, false, 40);
}
public void testNonEmpty() throws Exception {
if (!Bug.BugMondrian486Fixed) {
return;
}
execHighCardTest(
"select {[Measures].[Unit Sales]} on columns,\n"
+ "non empty {[Promotions].[Promotion Name].Members} "
+ "on rows from [Sales Ragged]",
1,
"Promotions",
nonEmptyHighCardResults, nonEmptyCells, true, 48);
}
public void testFilter() throws Exception {
if (!Bug.BugMondrian486Fixed) {
return;
}
execHighCardTest(
"select [Measures].[Unit Sales] on columns, "
+ "filter([Promotions].[Promotion Name].Members, "
+ "[Measures].[Unit Sales]>0) "
+ "on rows from [Sales Ragged]",
1,
"Promotions",
nonEmptyHighCardResults, nonEmptyCells, true, 48);
execHighCardTest(
"select [Measures].[Unit Sales] on columns, "
+ "filter([Promotions].[Promotion Name].Members, "
+ "[Measures].[Unit Sales]>4000) "
+ "on rows from [Sales Ragged]",
1,
"Promotions",
moreThan4000highCardResults, moreThan4000Cells, true, 3);
}
public void testMondrian1488() {
// MONDRIAN-1501 / MONDRIAN-1488
// Both involve an attempt to modify the list backing
// HighCardSqlTupleReader when handling null values.
// Requires use of a dim flagged as high card which has null members
TestContext ctx = TestContext.instance().create(
null,
"<Cube name=\"highCard\"> \n"
+ " <Table name=\"sales_fact_1997\"/> \n"
+ "<Dimension name=\"StoreSize\" foreignKey=\"customer_id\" highCardinality=\"true\">\n"
+ " <Hierarchy hasAll=\"true\" primaryKey=\"store_id\">\n"
+ " <Table name=\"store\"/>\n"
+ " <Level name=\"Sqft\" column=\"store_sqft\" type=\"Numeric\" uniqueMembers=\"true\"/>\n"
+ " </Hierarchy>\n"
+ " </Dimension>"
+ " <Measure name=\"Unit Sales\" column=\"unit_sales\" aggregator=\"sum\"/> \n"
+ "</Cube> \n",
null,
null,
null,
null);
// this will throw an exception if .remove is called on the HCSTR list
ctx.executeQuery(
"select NON EMPTY filter([StoreSize].[Sqft].members, 1=1) on 0 from highCard");
}
//
// Private Stuff --------------------------------------------
//
/**
* Executes query test trying to [Promotions].[Promotion Name] elements
* into an axis from the results.
*/
private void execHighCardTest(
final String queryString,
final int axisIndex,
final String highDimensionName,
final String results,
final String results2,
final boolean shouldForget,
final int resultLimit)
throws Exception
{
propSaver.set(MondrianProperties.instance().ResultLimit, resultLimit);
final TestContext testContext =
TestContext.instance().createSubstitutingCube(
"Sales Ragged",
"<Dimension name=\"Promotions\" highCardinality=\"true\" "
+ "foreignKey=\"promotion_id\">"
+ " <Hierarchy hasAll=\"true\" "
+ " allMemberName=\"All Promotions\" "
+ " primaryKey=\"promotion_id\">"
+ " <Table name=\"promotion\"/>"
+ " <Level name=\"Promotion Name\" "
+ " column=\"promotion_name\" "
+ " uniqueMembers=\"true\"/>"
+ " </Hierarchy>"
+ "</Dimension>");
final Connection connection = testContext.getConnection();
final Query query = connection.parseQuery(queryString);
query.setResultStyle(ResultStyle.ITERABLE);
Result result = connection.execute(query);
StringBuffer buffer = new StringBuffer();
StringBuffer buffer2 = new StringBuffer();
final List<SoftReference> softReferences =
new ArrayList<SoftReference>();
// Tests results aren't got from database before this point
int ii = 0;
for (final Position o
: result.getAxes()[axisIndex].getPositions())
{
assertNotNull(o.get(0));
buffer2.append(result.getCell(
new int[]{0, ii}).getValue().toString());
ii++;
softReferences.add(new SoftReference(o.get(0)));
buffer.append(o.get(0).toString());
}
assertEquals(buffer.toString().length(), results.length());
if (results2 != null) {
assertEquals(buffer2.toString().length(), results2.length());
}
buffer2 = null;
buffer = null;
if (!shouldForget) {
return;
}
// Tests that really results over ResultLimit are erased from
// memory
final List overloader = new ArrayList();
try {
for (;;) {
overloader.add(new long[99999999]);
}
} catch (OutOfMemoryError out) {
// OK, outofmemory
}
System.gc();
for (int i = 4; i < ii - 40; i++) {
assertNull(softReferences.get(i).get());
}
for (int i = 4; i < ii - 40; i++) {
try {
result.getAxes()[axisIndex].getPositions().get(i).get(0);
Assert.fail("Expected exception");
} catch (RuntimeException nsee) {
// Everything is ok: RuntimeException of type
// RuntimeException is expected.
}
}
}
private static final String first40HighCardResults =
"[Promotions].[Bag Stuffers]"
+ "[Promotions].[Best Savings]"
+ "[Promotions].[Big Promo]"
+ "[Promotions].[Big Time Discounts]"
+ "[Promotions].[Big Time Savings]"
+ "[Promotions].[Bye Bye Baby]"
+ "[Promotions].[Cash Register Lottery]"
+ "[Promotions].[Coupon Spectacular]"
+ "[Promotions].[Dimes Off]"
+ "[Promotions].[Dollar Cutters]"
+ "[Promotions].[Dollar Days]"
+ "[Promotions].[Double Down Sale]"
+ "[Promotions].[Double Your Savings]"
+ "[Promotions].[Fantastic Discounts]"
+ "[Promotions].[Free For All]"
+ "[Promotions].[Go For It]"
+ "[Promotions].[Green Light Days]"
+ "[Promotions].[Green Light Special]"
+ "[Promotions].[High Roller Savings]"
+ "[Promotions].[I Cant Believe It Sale]"
+ "[Promotions].[Money Grabbers]"
+ "[Promotions].[Money Savers]"
+ "[Promotions].[Mystery Sale]"
+ "[Promotions].[No Promotion]"
+ "[Promotions].[One Day Sale]"
+ "[Promotions].[Pick Your Savings]"
+ "[Promotions].[Price Cutters]"
+ "[Promotions].[Price Destroyers]"
+ "[Promotions].[Price Savers]"
+ "[Promotions].[Price Slashers]"
+ "[Promotions].[Price Smashers]"
+ "[Promotions].[Price Winners]"
+ "[Promotions].[Sale Winners]"
+ "[Promotions].[Sales Days]"
+ "[Promotions].[Sales Galore]"
+ "[Promotions].[Save-It Sale]"
+ "[Promotions].[Saving Days]"
+ "[Promotions].[Savings Galore]"
+ "[Promotions].[Shelf Clearing Days]"
+ "[Promotions].[Shelf Emptiers]";
private static final String nonEmptyHighCardResults =
"[Promotions].[Bag Stuffers]"
+ "[Promotions].[Best Savings]"
+ "[Promotions].[Big Promo]"
+ "[Promotions].[Big Time Discounts]"
+ "[Promotions].[Big Time Savings]"
+ "[Promotions].[Bye Bye Baby]"
+ "[Promotions].[Cash Register Lottery]"
+ "[Promotions].[Dimes Off]"
+ "[Promotions].[Dollar Cutters]"
+ "[Promotions].[Dollar Days]"
+ "[Promotions].[Double Down Sale]"
+ "[Promotions].[Double Your Savings]"
+ "[Promotions].[Free For All]"
+ "[Promotions].[Go For It]"
+ "[Promotions].[Green Light Days]"
+ "[Promotions].[Green Light Special]"
+ "[Promotions].[High Roller Savings]"
+ "[Promotions].[I Cant Believe It Sale]"
+ "[Promotions].[Money Savers]"
+ "[Promotions].[Mystery Sale]"
+ "[Promotions].[No Promotion]"
+ "[Promotions].[One Day Sale]"
+ "[Promotions].[Pick Your Savings]"
+ "[Promotions].[Price Cutters]"
+ "[Promotions].[Price Destroyers]"
+ "[Promotions].[Price Savers]"
+ "[Promotions].[Price Slashers]"
+ "[Promotions].[Price Smashers]"
+ "[Promotions].[Price Winners]"
+ "[Promotions].[Sale Winners]"
+ "[Promotions].[Sales Days]"
+ "[Promotions].[Sales Galore]"
+ "[Promotions].[Save-It Sale]"
+ "[Promotions].[Saving Days]"
+ "[Promotions].[Savings Galore]"
+ "[Promotions].[Shelf Clearing Days]"
+ "[Promotions].[Shelf Emptiers]"
+ "[Promotions].[Super Duper Savers]"
+ "[Promotions].[Super Savers]"
+ "[Promotions].[Super Wallet Savers]"
+ "[Promotions].[Three for One]"
+ "[Promotions].[Tip Top Savings]"
+ "[Promotions].[Two Day Sale]"
+ "[Promotions].[Two for One]"
+ "[Promotions].[Unbeatable Price Savers]"
+ "[Promotions].[Wallet Savers]"
+ "[Promotions].[Weekend Markdown]"
+ "[Promotions].[You Save Days]";
private static final String highCardResults =
"[Promotions].[Bag Stuffers]"
+ "[Promotions].[Best Savings]"
+ "[Promotions].[Big Promo]"
+ "[Promotions].[Big Time Discounts]"
+ "[Promotions].[Big Time Savings]"
+ "[Promotions].[Bye Bye Baby]"
+ "[Promotions].[Cash Register Lottery]"
+ "[Promotions].[Coupon Spectacular]"
+ "[Promotions].[Dimes Off]"
+ "[Promotions].[Dollar Cutters]"
+ "[Promotions].[Dollar Days]"
+ "[Promotions].[Double Down Sale]"
+ "[Promotions].[Double Your Savings]"
+ "[Promotions].[Fantastic Discounts]"
+ "[Promotions].[Free For All]"
+ "[Promotions].[Go For It]"
+ "[Promotions].[Green Light Days]"
+ "[Promotions].[Green Light Special]"
+ "[Promotions].[High Roller Savings]"
+ "[Promotions].[I Cant Believe It Sale]"
+ "[Promotions].[Money Grabbers]"
+ "[Promotions].[Money Savers]"
+ "[Promotions].[Mystery Sale]"
+ "[Promotions].[No Promotion]"
+ "[Promotions].[One Day Sale]"
+ "[Promotions].[Pick Your Savings]"
+ "[Promotions].[Price Cutters]"
+ "[Promotions].[Price Destroyers]"
+ "[Promotions].[Price Savers]"
+ "[Promotions].[Price Slashers]"
+ "[Promotions].[Price Smashers]"
+ "[Promotions].[Price Winners]"
+ "[Promotions].[Sale Winners]"
+ "[Promotions].[Sales Days]"
+ "[Promotions].[Sales Galore]"
+ "[Promotions].[Save-It Sale]"
+ "[Promotions].[Saving Days]"
+ "[Promotions].[Savings Galore]"
+ "[Promotions].[Shelf Clearing Days]"
+ "[Promotions].[Shelf Emptiers]"
+ "[Promotions].[Super Duper Savers]"
+ "[Promotions].[Super Savers]"
+ "[Promotions].[Super Wallet Savers]"
+ "[Promotions].[Three for One]"
+ "[Promotions].[Tip Top Savings]"
+ "[Promotions].[Two Day Sale]"
+ "[Promotions].[Two for One]"
+ "[Promotions].[Unbeatable Price Savers]"
+ "[Promotions].[Wallet Savers]"
+ "[Promotions].[Weekend Markdown]"
+ "[Promotions].[You Save Days]";
private static final String moreThan4000highCardResults =
"[Promotions].[Cash Register Lottery]"
+ "[Promotions].[No Promotion]"
+ "[Promotions].[Price Savers]";
private static final String moreThan4000Cells =
"4792.0195448.04094.0";
private static final String nonEmptyCells =
"901.02081.01789.0932.0700.0921.04792.01219.0"
+ "781.01652.01959.0843.01638.0689.01607.0436.0"
+ "2654.0253.0899.01021.0195448.01973.0323.01624.0"
+ "2173.04094.01148.0504.01294.0444.02055.02572.0"
+ "2203.01446.01382.0754.02118.02628.02497.01183.0"
+ "1155.0525.02053.0335.02100.0916.0914.03145.0";
private static final String topcount40Cells =
"195448.04792.04094.03145.02654.02628.02572.02497.0"
+ "2203.02173.02118.02100.02081.02055.02053.01973.0"
+ "1959.01789.01652.01638.01624.01607.01446.01382.0"
+ "1294.01219.01183.01155.01148.01021.0932.0921.0"
+ "916.0914.0901.0899.0843.0781.0754.0700.0";
private static final String topcount41Cells =
"195448.04792.04094.03145.02654.02628.02572.02497.02203.0"
+ "2173.02118.02100.02081.02055.02053.01973.01959.01789.0"
+ "1652.01638.01624.01607.01446.01382.01294.01219.01183.0"
+ "1155.01148.01021.0932.0921.0916.0914.0901.0899.0843.0781"
+ ".0754.0700.0689.0";
}
// End HighDimensionsTest.java