Skip to content

Commit

Permalink
MONDRIAN: Fix bug 2004202, "Except not working with grouping sets".
Browse files Browse the repository at this point in the history
    Some cosmetic stuff, too.

[git-p4: depot-paths = "//open/mondrian-release/3.0/": change = 11297]
  • Loading branch information
julianhyde committed Jul 16, 2008
1 parent 632dff3 commit 075f4c5
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 103 deletions.
103 changes: 81 additions & 22 deletions src/main/mondrian/rolap/FastBatchingCellReader.java
Expand Up @@ -228,10 +228,11 @@ List<CompositeBatch> groupBatches(List<Batch> batchList) {
}

wrapNonBatchedBatchesWithCompositeBatches(batchList, batchGroups);
ArrayList<CompositeBatch> list =
new ArrayList<CompositeBatch>(batchGroups.values());
Collections.sort(list, CompositeBatchComparator.instance);
return list;
final CompositeBatch[] compositeBatches =
batchGroups.values().toArray(
new CompositeBatch[batchGroups.size()]);
Arrays.sort(compositeBatches, CompositeBatchComparator.instance);
return Arrays.asList(compositeBatches);
}

private void wrapNonBatchedBatchesWithCompositeBatches(
Expand Down Expand Up @@ -337,7 +338,7 @@ public void loadAggregation() {

getSegmentLoader().load(
batchCollector.getGroupingSets(),
pinnedSegments,
pinnedSegments,
detailedBatch.batchKey.getCompoundPredicateList());
}

Expand All @@ -364,6 +365,8 @@ class Batch implements Loadable {
new ArrayList<RolapStar.Measure>();
final Set<StarColumnPredicate>[] valueSets;
final AggregationKey batchKey;
// string representation; for debug; set lazily in toString
private String string;

public Batch(CellRequest request) {
columns = request.getConstrainedColumns();
Expand All @@ -374,6 +377,22 @@ public Batch(CellRequest request) {
batchKey = new AggregationKey(request);
}

public String toString() {
if (string == null) {
final StringBuilder buf = new StringBuilder();
buf.append("Batch {\n")
.append(" columns={").append(Arrays.asList(columns))
.append("}\n")
.append(" measures={").append(measuresList).append("}\n")
.append(" valueSets={").append(Arrays.asList(valueSets))
.append("}\n")
.append(" batchKey=").append(batchKey).append("}\n")
.append("}");
string = buf.toString();
}
return string;
}

public final void add(CellRequest request) {
final List<StarColumnPredicate> values = request.getValueList();
for (int j = 0; j < columns.length; j++) {
Expand Down Expand Up @@ -668,17 +687,48 @@ && hasSameMeasureList(other)
&& haveSameStarAndAggregation(other);
}

/**
* Returns whether the constraints on this Batch subsume the constraints
* on another Batch and therefore the other Batch can be subsumed into
* this one for GROUPING SETS purposes. Not symmetric.
*
* @param other Other batch
* @return Whether other batch can be subsumed into this one
*/
private boolean constraintsMatch(Batch other) {
if(areBothDistinctCountBatches(other)){
if(getConstrainedColumnsBitKey().equals(
if (areBothDistinctCountBatches(other)) {
if (getConstrainedColumnsBitKey().equals(
other.getConstrainedColumnsBitKey())) {
return hasSameCompoundPredicate(other)
&& haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(other);
return hasSameCompoundPredicate(other)
&& haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(other);
} else {
return hasSameCompoundPredicate(other)
|| (other.batchKey.getCompoundPredicateList().isEmpty()
|| equalConstraint(
batchKey.getCompoundPredicateList(),
other.batchKey.getCompoundPredicateList()))
&& haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(other);
}
return hasSameCompoundPredicate(other)
|| haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(other);
} else {
return haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(other);
}
return haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(other);
}

private boolean equalConstraint(
List<StarPredicate> predList1,
List<StarPredicate> predList2)
{
if (predList1.size() != predList2.size()) {
return false;
}
for (int i = 0; i < predList1.size(); i++) {
StarPredicate pred1 = predList1.get(i);
StarPredicate pred2 = predList2.get(i);
if (!pred1.equalConstraint(pred2)) {
return false;
}
}
return true;
}

private boolean areBothDistinctCountBatches(Batch other) {
Expand Down Expand Up @@ -788,8 +838,9 @@ boolean haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(
if (hasSameValues(other.valueSets[i], valueSets[j])) {
isCommonColumn = true;
break;
} else
} else {
return false;
}
}
}
if (!isCommonColumn &&
Expand All @@ -800,30 +851,38 @@ boolean haveSameValuesForOverlappingColumnsOrHasAllChildrenForOthers(
return true;
}

private boolean hasAllValues(RolapStar.Column column,
Set<StarColumnPredicate> valueSet)
private boolean hasAllValues(
RolapStar.Column column,
Set<StarColumnPredicate> valueSet)
{
return column.getCardinality() == valueSet.size();
}

private boolean areSameColumns(RolapStar.Column otherColumn,
RolapStar.Column thisColumn)
private boolean areSameColumns(
RolapStar.Column otherColumn,
RolapStar.Column thisColumn)
{
return otherColumn.equals(thisColumn);
}

private boolean hasSameValues(Set<StarColumnPredicate> otherValueSet,
Set<StarColumnPredicate> thisValueSet)
private boolean hasSameValues(
Set<StarColumnPredicate> otherValueSet,
Set<StarColumnPredicate> thisValueSet)
{
return otherValueSet.equals(thisValueSet);
}
}

private static class CompositeBatchComparator implements Comparator<CompositeBatch> {
static final CompositeBatchComparator instance = new CompositeBatchComparator();
private static class CompositeBatchComparator
implements Comparator<CompositeBatch>
{
static final CompositeBatchComparator instance =
new CompositeBatchComparator();

public int compare(CompositeBatch o1, CompositeBatch o2) {
return BatchComparator.instance.compare(o1.detailedBatch, o2.detailedBatch);
return BatchComparator.instance.compare(
o1.detailedBatch,
o2.detailedBatch);
}
}

Expand Down
13 changes: 0 additions & 13 deletions src/main/mondrian/rolap/RolapAggregationManager.java
Expand Up @@ -720,19 +720,6 @@ public boolean isDirty() {
*/
public abstract PinSet createPinSet();

/**
* Generates a SQL query to retrieve a cell value for an evaluation
* context where some of the dimensions have more than one member.
*
* <p>Returns null if the request is unsatisfiable. This would happen, say,
* if one of the members is null.
*
* @param evaluator Provides evaluation context
* @param aggregationLists List of aggregations, each of which is a set
* of members in the same hierarchy which need to be summed together.
* @return SQL query, or null if request is unsatisfiable
*/

/**
* A set of segments which are pinned for a short duration as a result of a
* cache inquiry.
Expand Down
9 changes: 8 additions & 1 deletion src/main/mondrian/rolap/agg/AbstractColumnPredicate.java
Expand Up @@ -28,7 +28,7 @@
* @version $Id$
*/
public abstract class AbstractColumnPredicate implements StarColumnPredicate {
private final RolapStar.Column constrainedColumn;
protected final RolapStar.Column constrainedColumn;
private final BitKey constrainedColumnBitKey;

/**
Expand All @@ -53,6 +53,13 @@ protected AbstractColumnPredicate(RolapStar.Column constrainedColumn) {
}
}

public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append(constrainedColumn.getExpression().getGenericExpression());
describe(buf);
return buf.toString();
}

public RolapStar.Column getConstrainedColumn() {
return constrainedColumn;
}
Expand Down
48 changes: 29 additions & 19 deletions src/main/mondrian/rolap/agg/Aggregation.java
Expand Up @@ -122,19 +122,22 @@ public Date getCreationTimestamp() {
* each constrained by the same set of column values, and each pinned
* once.
*
* A Column and its constraints are accessed at the same level in their
* <p>A Column and its constraints are accessed at the same level in their
* respective arrays.
*
* For example,
* <p>For example,
* <blockquote><pre>
* measures = {unit_sales, store_sales},
* state = {CA, OR},
* gender = unconstrained
* gender = unconstrained</pre></blockquote>
*/
public void load(
RolapStar.Column[] columns, RolapStar.Measure[] measures,
StarColumnPredicate[] predicates,
RolapAggregationManager.PinSet pinnedSegments,
GroupingSetsCollector groupingSetsCollector) {
RolapStar.Column[] columns,
RolapStar.Measure[] measures,
StarColumnPredicate[] predicates,
RolapAggregationManager.PinSet pinnedSegments,
GroupingSetsCollector groupingSetsCollector)
{
// all constrained columns
if (this.columns == null) {
this.columns = columns;
Expand All @@ -150,35 +153,42 @@ public void load(
for (int i = 0; i < axisCount; i++) {
axes[i] = new Aggregation.Axis(predicates[i]);
}
Segment[] segments = addSegmentsToAggregation(measures,
measureBitKey, axes, pinnedSegments);
Segment[] segments =
addSegmentsToAggregation(
measures, measureBitKey, axes, pinnedSegments);
// The constrained columns are simply the level and foreign columns
BitKey levelBitKey = getConstrainedColumnsBitKey();
GroupingSet groupingSet = new GroupingSet(segments,
levelBitKey, measureBitKey, axes, columns);
GroupingSet groupingSet =
new GroupingSet(
segments, levelBitKey, measureBitKey, axes, columns);
final List<StarPredicate> compoundPredicateList =
aggregationKey.getCompoundPredicateList();
if (groupingSetsCollector.useGroupingSets()) {
groupingSetsCollector.add(groupingSet);
// Segments are loaded using group by grouping sets
// by CompositeBatch.loadAggregation
} else {
ArrayList<GroupingSet> groupingSets = new ArrayList<GroupingSet>();
groupingSets.add(groupingSet);
new SegmentLoader().load(
groupingSets, pinnedSegments,
aggregationKey.getCompoundPredicateList());
Collections.singletonList(groupingSet),
pinnedSegments,
compoundPredicateList);
}
}

private Segment[] addSegmentsToAggregation(
RolapStar.Measure[] measures, BitKey measureBitKey, Axis[] axes,
RolapAggregationManager.PinSet pinnedSegments) {
RolapStar.Measure[] measures,
BitKey measureBitKey,
Axis[] axes,
RolapAggregationManager.PinSet pinnedSegments)
{
Segment[] segments = new Segment[measures.length];
for (int i = 0; i < measures.length; i++) {
RolapStar.Measure measure = measures[i];
measureBitKey.set(measure.getBitPosition());
List<Segment.Region> excludedRegions = Collections.emptyList();
Segment segment =
new Segment(this, measure, axes, excludedRegions);
new Segment(
this, measure, axes,
Collections.<Segment.Region>emptyList());
segments[i] = segment;
SoftReference<Segment> ref = new SoftReference<Segment>(segment);
segmentRefs.add(ref);
Expand Down
25 changes: 14 additions & 11 deletions src/main/mondrian/rolap/agg/AggregationManager.java
Expand Up @@ -32,7 +32,8 @@
*/
public class AggregationManager extends RolapAggregationManager {

private static final MondrianProperties properties = MondrianProperties.instance();
private static final MondrianProperties properties =
MondrianProperties.instance();

private static final Logger LOGGER =
Logger.getLogger(AggregationManager.class);
Expand Down Expand Up @@ -76,15 +77,18 @@ public void loadAggregation(
AggregationKey aggregationKey,
StarColumnPredicate[] predicates,
PinSet pinnedSegments,
GroupingSetsCollector groupingSetsCollector) {
GroupingSetsCollector groupingSetsCollector)
{
RolapStar star = measures[0].getStar();
Aggregation aggregation =
star.lookupOrCreateAggregation(aggregationKey);

// try to eliminate unneccessary constraints
// for Oracle: prevent an IN-clause with more than 1000 elements
predicates = aggregation.optimizePredicates(columns, predicates);
aggregation.load(columns, measures, predicates, pinnedSegments, groupingSetsCollector);
aggregation.load(
columns, measures, predicates, pinnedSegments,
groupingSetsCollector);
}

public Object getCellFromCache(CellRequest request) {
Expand All @@ -99,14 +103,13 @@ public Object getCellFromCache(CellRequest request, PinSet pinSet) {
AggregationKey aggregationKey = new AggregationKey(request);
final Aggregation aggregation =
measure.getStar().lookupAggregation(aggregationKey);

if (aggregation == null) {
// cell is not in any aggregation
return null;
} else {
final Object o = aggregation.getCellValue(
return aggregation.getCellValue(
measure, request.getSingleValues(), pinSet);
return o;
}
}

Expand Down Expand Up @@ -137,8 +140,8 @@ public String getDrillThroughSql(
*/
public String generateSql(
GroupingSetsList groupingSetsList,
List<StarPredicate> compoundPredicateList) {

List<StarPredicate> compoundPredicateList)
{
BitKey levelBitKey = groupingSetsList.getDefaultLevelBitKey();
BitKey measureBitKey = groupingSetsList.getDefaultMeasureBitKey();

Expand Down Expand Up @@ -359,7 +362,6 @@ public AggStar findAgg(
// For each such measure, is it based upon a foreign key.
// Are there any foreign keys left over. No, can use AggStar.
BitKey fkBitKey = aggStar.getForeignKeyBitKey().copy();
Iterator mit = aggStar.getFactTable().getMeasures().iterator();
for (AggStar.FactTable.Measure measure : aggStar.getFactTable()
.getMeasures()) {
if (measure.isDistinct()) {
Expand Down Expand Up @@ -394,8 +396,9 @@ public PinSet createPinSet() {
* Implementation of {@link mondrian.rolap.RolapAggregationManager.PinSet}
* using a {@link HashSet}.
*/
public static class PinSetImpl extends HashSet<Segment>
implements RolapAggregationManager.PinSet {
public static class PinSetImpl
extends HashSet<Segment>
implements RolapAggregationManager.PinSet {
}
}

Expand Down

0 comments on commit 075f4c5

Please sign in to comment.