protected Result run() { getConnection().getCacheControl(null).flushSchemaCache(); IntegerProperty monLimit = MondrianProperties.instance().ResultLimit; int oldLimit = monLimit.get(); try { monLimit.set(this.resultLimit); Result result = executeQuery(query, con); // Check the number of positions on the last axis, which is // the ROWS axis in a 2 axis query. int numAxes = result.getAxes().length; Axis a = result.getAxes()[numAxes - 1]; final int positionCount = a.getPositions().size(); assertEquals(rowCount, positionCount); return result; } finally { monLimit.set(oldLimit); } }
/** * During MDX query parse and execution, checks that the query results (or does not result) in a * particular SQL statement being generated. * * <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 bypassSchemaCache, boolean clearCache) { Connection connection = testContext.getConnection(); mdxQuery = testContext.upgradeQuery(mdxQuery); // Run the test once for each pattern in this dialect. // (We could optimize and run it once, collecting multiple queries, and // comparing all queries at the end.) Dialect dialect = testContext.getDialect(); Dialect.DatabaseProduct d = dialect.getDatabaseProduct(); boolean patternFound = false; for (SqlPattern sqlPattern : patterns) { if (!sqlPattern.hasDatabaseProduct(d)) { // If the dialect is not one in the pattern set, skip the // test. If in the end no pattern is located, print a warning // message if required. continue; } patternFound = true; String sql = sqlPattern.getSql(); String trigger = sqlPattern.getTriggerSql(); sql = dialectize(d, sql); trigger = dialectize(d, trigger); // 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.setHook(new TriggerHook(trigger)); Bomb bomb = null; try { if (bypassSchemaCache) { connection = testContext.withSchemaPool(false).getConnection(); } final Query query = connection.parseQuery(mdxQuery); if (clearCache) { clearCache((RolapCube) query.getCube()); } final Result result = connection.execute(query); Util.discard(result); bomb = null; } catch (Bomb e) { bomb = e; } catch (RuntimeException e) { // Walk up the exception tree and see if the root cause // was a SQL bomb. bomb = Util.getMatchingCause(e, Bomb.class); if (bomb == null) { throw e; } } finally { RolapUtil.setHook(null); } if (negative) { if (bomb != null) { fail("forbidden query [" + sql + "] detected"); } } else { if (bomb == null) { fail("expected query [" + sql + "] did not occur"); } assertEquals( replaceQuotes(sql.replaceAll("\r\n", "\n")), replaceQuotes(bomb.sql.replaceAll("\r\n", "\n"))); } } // Print warning message that no pattern was specified for the current // dialect. if (!patternFound) { String warnDialect = MondrianProperties.instance().WarnIfNoPatternForDialect.get(); if (warnDialect.equals(d.toString())) { System.out.println( "[No expected SQL statements found for dialect \"" + dialect.toString() + "\" and test not run]"); } } }
/** * Runs a query twice, with native crossjoin optimization enabled and disabled. If both results * are equal,and both aggree with the expected result, it is considered correct. * * <p>Optionally the query can be run with fresh connection. This is useful if the test case sets * its certain mondrian properties, e.g. native properties like: mondrian.native.filter.enable * * @param resultLimit Maximum result size of all the MDX operations in this query. This might be * hard to estimate as it is usually larger than the rowCount of the final result. Setting it * to 0 will cause this limit to be ignored. * @param rowCount Number of rows returned. (That is, the number of positions on the last axis of * the query.) * @param mdx Query * @param expectedResult Expected result string * @param freshConnection Whether fresh connection is required */ protected void checkNative( int resultLimit, int rowCount, String mdx, String expectedResult, boolean freshConnection) { // Don't run the test if we're testing expression dependencies. // Expression dependencies cause spurious interval calls to // 'level.getMembers()' which create false negatives in this test. if (MondrianProperties.instance().TestExpDependencies.get() > 0) { return; } getConnection().getCacheControl(null).flushSchemaCache(); try { Logger.getLogger(getClass()).debug("*** Native: " + mdx); boolean reuseConnection = !freshConnection; Connection con = getTestContext().withSchemaPool(reuseConnection).getConnection(); RolapNativeRegistry reg = getRegistry(con); reg.useHardCache(true); TestListener listener = new TestListener(); reg.setListener(listener); reg.setEnabled(true); TestCase c = new TestCase(con, resultLimit, rowCount, mdx); Result result = c.run(); String nativeResult = TestContext.toString(result); if (!listener.isFoundEvaluator()) { fail("expected native execution of " + mdx); } if (!listener.isExecuteSql()) { fail("cache is empty: expected SQL query to be executed"); } if (MondrianProperties.instance().EnableRolapCubeMemberCache.get()) { // run once more to make sure that the result comes from cache // now listener.setExecuteSql(false); c.run(); if (listener.isExecuteSql()) { fail("expected result from cache when query runs twice"); } } con.close(); Logger.getLogger(getClass()).debug("*** Interpreter: " + mdx); getConnection().getCacheControl(null).flushSchemaCache(); con = getTestContext().withSchemaPool(false).getConnection(); reg = getRegistry(con); listener.setFoundEvaluator(false); reg.setListener(listener); // disable RolapNativeSet reg.setEnabled(false); result = executeQuery(mdx, con); String interpretedResult = TestContext.toString(result); if (listener.isFoundEvaluator()) { fail("did not expect native executions of " + mdx); } if (expectedResult != null) { TestContext.assertEqualsVerbose( expectedResult, nativeResult, false, "Native implementation returned different result than " + "expected; MDX=" + mdx); TestContext.assertEqualsVerbose( expectedResult, interpretedResult, false, "Interpreter implementation returned different result than " + "expected; MDX=" + mdx); } if (!nativeResult.equals(interpretedResult)) { TestContext.assertEqualsVerbose( interpretedResult, nativeResult, false, "Native implementation returned different result than " + "interpreter; MDX=" + mdx); } } finally { Connection con = getConnection(); RolapNativeRegistry reg = getRegistry(con); reg.setEnabled(true); reg.useHardCache(false); } }
/** * Checks that a given sequence of cell requests results in a particular SQL statement being * generated. * * <p>Always clears the cache before running the requests. * * <p>Runs the requests 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 requests Sequence of cell requests * @param patterns Set of patterns * @param negative Set to false in order to 'expect' a query or true to 'forbid' a query. */ protected void assertRequestSql(CellRequest[] requests, SqlPattern[] patterns, boolean negative) { final RolapStar star = requests[0].getMeasure().getStar(); final String cubeName = requests[0].getMeasure().getCubeName(); final RolapCube cube = lookupCube(cubeName); final Dialect sqlDialect = star.getSqlQueryDialect(); Dialect.DatabaseProduct d = sqlDialect.getDatabaseProduct(); SqlPattern sqlPattern = SqlPattern.getPattern(d, patterns); if (d == Dialect.DatabaseProduct.UNKNOWN) { // If the dialect is not one in the pattern set, do not run the // test. We do not print any warning message. return; } boolean patternFound = false; for (SqlPattern pattern : patterns) { if (!pattern.hasDatabaseProduct(d)) { continue; } patternFound = true; clearCache(cube); String sql = sqlPattern.getSql(); String trigger = sqlPattern.getTriggerSql(); switch (d) { case ORACLE: sql = sql.replaceAll(" =as= ", " "); trigger = trigger.replaceAll(" =as= ", " "); break; case TERADATA: sql = sql.replaceAll(" =as= ", " as "); trigger = trigger.replaceAll(" =as= ", " as "); break; } // 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.setHook(new TriggerHook(trigger)); Bomb bomb; final Execution execution = new Execution(((RolapConnection) getConnection()).getInternalStatement(), 1000); final AggregationManager aggMgr = execution .getMondrianStatement() .getMondrianConnection() .getServer() .getAggregationManager(); final Locus locus = new Locus(execution, "BatchTestCase", "BatchTestCase"); try { FastBatchingCellReader fbcr = new FastBatchingCellReader(execution, getCube(cubeName), aggMgr); for (CellRequest request : requests) { fbcr.recordCellRequest(request); } // The FBCR will presume there is a current Locus in the stack, // so let's create a mock one. Locus.push(locus); fbcr.loadAggregations(); bomb = null; } catch (Bomb e) { bomb = e; } finally { RolapUtil.setHook(null); Locus.pop(locus); } if (!negative && bomb == null) { fail("expected query [" + sql + "] did not occur"); } else if (negative && bomb != null) { fail("forbidden query [" + sql + "] detected"); } TestContext.assertEqualsVerbose(replaceQuotes(sql), replaceQuotes(bomb.sql)); } // Print warning message that no pattern was specified for the current // dialect. if (!patternFound) { String warnDialect = MondrianProperties.instance().WarnIfNoPatternForDialect.get(); if (warnDialect.equals(d.toString())) { System.out.println( "[No expected SQL statements found for dialect \"" + sqlDialect.toString() + "\" and test not run]"); } } }