Skip to content

Commit

Permalink
MONDRIAN: [MONDRIAN-791] Applied a contributed patch by Jason Edwards…
Browse files Browse the repository at this point in the history
…. Fixes a problem with the drillthrough SQL generation where only the first member of a hierarchy included in the slicer was part of the SQL.

[git-p4: depot-paths = "//open/mondrian/": change = 14411]
  • Loading branch information
lucboudreau committed Jun 28, 2011
1 parent 681abe2 commit e82d6f8
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/main/mondrian/rolap/RolapAggregationManager.java
Expand Up @@ -615,10 +615,14 @@ public abstract Object getCellFromCache(
*
* @param request Cell request
* @param countOnly If true, return a statment which returns only the count
* @param starPredicateSlicer A StarPredicate representing slicer positions
* that could not be represented by the CellRequest, or
* <code>null</code> if no additional predicate is necessary.
* @return SQL statement
*/
public abstract String getDrillThroughSql(
CellRequest request,
StarPredicate starPredicateSlicer,
boolean countOnly);

/**
Expand Down
150 changes: 148 additions & 2 deletions src/main/mondrian/rolap/RolapCell.java
Expand Up @@ -11,12 +11,17 @@

import mondrian.mdx.*;
import mondrian.olap.*;
import mondrian.olap.Axis;
import mondrian.olap.Cell;
import mondrian.olap.Connection;
import mondrian.olap.Position;
import mondrian.olap.fun.AggregateFunDef;
import mondrian.olap.fun.SetFunDef;
import mondrian.rolap.agg.AggregationManager;
import mondrian.rolap.agg.AndPredicate;
import mondrian.rolap.agg.CellRequest;
import mondrian.rolap.agg.MemberColumnPredicate;
import mondrian.rolap.agg.OrPredicate;
import mondrian.spi.Dialect;

import java.sql.*;
Expand Down Expand Up @@ -95,18 +100,39 @@ public boolean isError() {
public String getDrillThroughSQL(boolean extendedContext) {
RolapAggregationManager aggMan = AggregationManager.instance();
final Member[] currentMembers = getMembersForDrillThrough();
// Create a StarPredicate to represent the compound slicer
// (if necessary)
// NOTE: the method buildDrillthroughSlicerPredicate modifies
// the array of members, so it MUST be called before calling
// RolapAggregationManager.makeDrillThroughRequest
StarPredicate starPredicateSlicer =
buildDrillthroughSlicerPredicate(
currentMembers,
result.getSlicerAxis());
CellRequest cellRequest =
RolapAggregationManager.makeDrillThroughRequest(
currentMembers, extendedContext, result.getCube());
return (cellRequest == null)
? null
: aggMan.getDrillThroughSql(cellRequest, false);
: aggMan.getDrillThroughSql(
cellRequest,
starPredicateSlicer,
false);
}


public int getDrillThroughCount() {
RolapAggregationManager aggMan = AggregationManager.instance();
final Member[] currentMembers = getMembersForDrillThrough();
// Create a StarPredicate to represent the compound
// slicer (if necessary)
// NOTE: the method buildDrillthroughSlicerPredicate modifies
// the array of members, so it MUST be called before calling
// RolapAggregationManager.makeDrillThroughRequest
StarPredicate starPredicateSlicer =
buildDrillthroughSlicerPredicate(
currentMembers,
result.getSlicerAxis());
CellRequest cellRequest =
RolapAggregationManager.makeDrillThroughRequest(
currentMembers, false, result.getCube());
Expand All @@ -115,7 +141,11 @@ public int getDrillThroughCount() {
}
RolapConnection connection =
(RolapConnection) result.getQuery().getConnection();
final String sql = aggMan.getDrillThroughSql(cellRequest, true);
final String sql =
aggMan.getDrillThroughSql(
cellRequest,
starPredicateSlicer,
true);
final SqlStatement stmt =
RolapUtil.executeQuery(
connection.getDataSource(),
Expand All @@ -134,6 +164,122 @@ public int getDrillThroughCount() {
}
}

/**
* This method handles the case of a compound slicer with more than one
* {@link Position}. In this case, a simple array of {@link Member}s is not
* sufficient to express the set of drill through rows. If the slicer axis
* does have multiple positions, this method will do two things:
* <ol>
* <li>Modify the passed-in array if any Member is overly restrictive.
* This can happen if the slicer specifies multiple members in the same
* hierarchy. In this scenario, the array of Members will contain an
* element for only the last selected member in the hierarchy. This method
* will replace that Member with the "All" Member from that hierarchy.
* </li>
* <li>Create a {@link StarPredicate} representing the Positions indicated
* by the slicer axis.
* </li>
* </ol>
*
* @param membersForDrillthrough the array of Members returned by
* {@link #getMembersForDrillThrough()}
* @param slicerAxis the slicer {@link Axis}
* @return an instance of <code>StarPredicate</code> representing all
* of the the positions from the slicer if it has more than one,
* or <code>null</code> otherwise.
*/
private StarPredicate buildDrillthroughSlicerPredicate(
Member[] membersForDrillthrough,
Axis slicerAxis)
{
List<Position> listOfPositions = slicerAxis.getPositions();
// If the slicer has zero or one position(s),
// then there is no need to do
// anything; the array of Members is correct as-is
if (listOfPositions.size() <= 1) {
return null;
}
// First, iterate through the positions' members, un-constraining the
// "membersForDrillthrough" array if any position member is not
// in the array
for (Position position : listOfPositions) {
for (Member member : position) {
RolapHierarchy rolapHierarchy =
(RolapHierarchy) member.getHierarchy();
// Check if the membersForDrillthrough constraint is identical
// to that of the position member
if (!membersForDrillthrough[rolapHierarchy.getOrdinalInCube()]
.equals(member))
{
// There is a discrepancy, so un-constrain the
// membersForDrillthrough array
membersForDrillthrough[rolapHierarchy.getOrdinalInCube()] =
rolapHierarchy.getAllMember();
}
}
}
// This is a list containing an AndPredicate for each position in the
// slicer axis
List<StarPredicate> listOfStarPredicatesForSlicerPositions =
new ArrayList<StarPredicate>();
// Now we re-iterate the positions' members,
// creating the slicer constraint
for (Position position : listOfPositions) {
// This is a list of the predicates required to select the
// current position (excluding the members of the position
// that are already constrained in the membersForDrillthrough array)
List<StarPredicate> listOfStarPredicatesForCurrentPosition =
new ArrayList<StarPredicate>();
// Iterate the members of the current position
for (Member member : position) {
RolapHierarchy rolapHierarchy =
(RolapHierarchy) member.getHierarchy();
// If the membersForDrillthrough is already constraining to
// this member, then there is no need to create additional
// predicate(s) for this member
if (!membersForDrillthrough[rolapHierarchy.getOrdinalInCube()]
.equals(member))
{
// Walk up the member's hierarchy, adding a
// predicate for each level
Member memberWalk = member;
Level levelLast = null;
while (memberWalk != null && ! memberWalk.isAll()) {
// Only create a predicate for this member if we
// are at a new level. This is for parent-child levels,
// however it still suffers from the following bug:
// http://jira.pentaho.com/browse/MONDRIAN-318
if (memberWalk.getLevel() != levelLast) {
RolapCubeMember rolapCubeMember =
(RolapCubeMember) memberWalk;
RolapStar.Column column =
rolapCubeMember.getLevel()
.getBaseStarKeyColumn(result.getCube());
// Add a predicate for the member at this level
listOfStarPredicatesForCurrentPosition.add(
new MemberColumnPredicate(
column,
rolapCubeMember));
}
levelLast = memberWalk.getLevel();
// Walk up the hierarchy
memberWalk = memberWalk.getParentMember();
}
}
}
// AND together all of the predicates that specify
// the current position
StarPredicate starPredicateForCurrentSlicerPosition =
new AndPredicate(listOfStarPredicatesForCurrentPosition);
// Add this position's predicate to the list
listOfStarPredicatesForSlicerPositions
.add(starPredicateForCurrentSlicerPosition);
}
// OR together the predicates for all of the slicer's
// positions and return
return new OrPredicate(listOfStarPredicatesForSlicerPositions);
}

/**
* Returns whether it is possible to drill through this cell.
* Drill-through is possible if the measure is a stored measure
Expand Down
4 changes: 3 additions & 1 deletion src/main/mondrian/rolap/agg/AggregationManager.java
Expand Up @@ -123,11 +123,13 @@ public Object getCellFromCache(CellRequest request, PinSet pinSet) {

public String getDrillThroughSql(
final CellRequest request,
boolean countOnly)
final StarPredicate starPrediateSlicer,
final boolean countOnly)
{
DrillThroughQuerySpec spec =
new DrillThroughQuerySpec(
request,
starPrediateSlicer,
countOnly);
Pair<String, List<SqlStatement.Type>> pair = spec.generateSqlQuery();

Expand Down
18 changes: 17 additions & 1 deletion src/main/mondrian/rolap/agg/DrillThroughQuerySpec.java
Expand Up @@ -17,6 +17,7 @@
import mondrian.util.Pair;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
Expand All @@ -31,12 +32,23 @@
*/
class DrillThroughQuerySpec extends AbstractQuerySpec {
private final CellRequest request;
private final List<StarPredicate> listOfStarPredicates;
private final List<String> columnNames;
private final int maxColumnNameLength;

public DrillThroughQuerySpec(CellRequest request, boolean countOnly) {
public DrillThroughQuerySpec(
CellRequest request,
StarPredicate starPredicateSlicer,
boolean countOnly)
{
super(request.getMeasure().getStar(), countOnly);
this.request = request;
if (starPredicateSlicer != null) {
this.listOfStarPredicates =
Collections.singletonList(starPredicateSlicer);
} else {
this.listOfStarPredicates = Collections.emptyList();
}
int tmpMaxColumnNameLength =
getStar().getSqlQueryDialect().getMaxColumnNameLength();
if (tmpMaxColumnNameLength == 0) {
Expand Down Expand Up @@ -152,6 +164,10 @@ protected boolean isAggregate() {
protected boolean isOrdered() {
return true;
}

protected List<StarPredicate> getPredicateList() {
return listOfStarPredicates;
}
}

// End DrillThroughQuerySpec.java

0 comments on commit e82d6f8

Please sign in to comment.