001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2001-2005 Julian Hyde 008// Copyright (C) 2005-2012 Pentaho and others 009// All Rights Reserved. 010// 011// jhyde, 30 August, 2001 012*/ 013package mondrian.rolap; 014 015import mondrian.olap.*; 016import mondrian.olap.fun.VisualTotalsFunDef.VisualTotalMember; 017import mondrian.resource.MondrianResource; 018import mondrian.rolap.agg.*; 019 020import java.util.*; 021 022/** 023 * <code>RolapAggregationManager</code> manages all 024 * {@link mondrian.rolap.agg.Segment}s in the system. 025 * 026 * <p> The bits of the implementation which depend upon dimensional concepts 027 * <code>RolapMember</code>, etc.) live in this class, and the other bits live 028 * in the derived class, {@link mondrian.rolap.agg.AggregationManager}. 029 * 030 * @author jhyde 031 * @since 30 August, 2001 032 */ 033public abstract class RolapAggregationManager { 034 035 /** 036 * Creates the RolapAggregationManager. 037 */ 038 protected RolapAggregationManager() { 039 } 040 041 /** 042 * Creates a request to evaluate the cell identified by 043 * <code>members</code>. 044 * 045 * <p>If any of the members is the null member, returns 046 * null, since there is no cell. If the measure is calculated, returns 047 * null. 048 * 049 * @param members Set of members which constrain the cell 050 * @return Cell request, or null if the requst is unsatisfiable 051 */ 052 public static CellRequest makeRequest(final Member[] members) 053 { 054 return makeCellRequest(members, false, false, null, null); 055 } 056 057 /** 058 * Creates a request for the fact-table rows underlying the cell identified 059 * by <code>members</code>. 060 * 061 * <p>If any of the members is the null member, returns null, since there 062 * is no cell. If the measure is calculated, returns null. 063 * 064 * @param members Set of members which constrain the cell 065 * 066 * @param extendedContext If true, add non-constraining columns to the 067 * query for levels below each current member. 068 * This additional context makes the drill-through 069 * queries easier for humans to understand. 070 * 071 * @param cube Cube 072 * @return Cell request, or null if the requst is unsatisfiable 073 */ 074 public static DrillThroughCellRequest makeDrillThroughRequest( 075 final Member[] members, 076 final boolean extendedContext, 077 RolapCube cube, 078 List<Exp> fieldsList) 079 { 080 assert cube != null; 081 return (DrillThroughCellRequest) makeCellRequest( 082 members, true, extendedContext, cube, fieldsList); 083 } 084 085 /** 086 * Creates a request to evaluate the cell identified by the context 087 * specified in <code>evaluator</code>. 088 * 089 * <p>If any of the members from the context is the null member, returns 090 * null, since there is no cell. If the measure is calculated, returns 091 * null. 092 * 093 * @param evaluator the cell specified by the evaluator context 094 * @return Cell request, or null if the requst is unsatisfiable 095 */ 096 public static CellRequest makeRequest( 097 RolapEvaluator evaluator) 098 { 099 final Member[] currentMembers = evaluator.getNonAllMembers(); 100 final List<List<List<Member>>> aggregationLists = 101 evaluator.getAggregationLists(); 102 103 final RolapStoredMeasure measure = 104 (RolapStoredMeasure) currentMembers[0]; 105 final RolapStar.Measure starMeasure = 106 (RolapStar.Measure) measure.getStarMeasure(); 107 assert starMeasure != null; 108 int starColumnCount = starMeasure.getStar().getColumnCount(); 109 110 CellRequest request = 111 makeCellRequest(currentMembers, false, false, null, null); 112 113 /* 114 * Now setting the compound keys. 115 * First find out the columns referenced in the aggregateMemberList. 116 * Each list defines a compound member. 117 */ 118 if (aggregationLists == null) { 119 return request; 120 } 121 122 BitKey compoundBitKey; 123 StarPredicate compoundPredicate; 124 Map<BitKey, List<RolapCubeMember[]>> compoundGroupMap; 125 boolean unsatisfiable; 126 127 /* 128 * For each aggregationList, generate the optimal form of 129 * compoundPredicate. These compoundPredicates are AND'ed together when 130 * sql is generated for them. 131 */ 132 for (List<List<Member>> aggregationList : aggregationLists) { 133 compoundBitKey = BitKey.Factory.makeBitKey(starColumnCount); 134 compoundBitKey.clear(); 135 compoundGroupMap = 136 new LinkedHashMap<BitKey, List<RolapCubeMember[]>>(); 137 138 // Go through the compound members/tuples once and separate them 139 // into groups. 140 List<List<RolapMember>> rolapAggregationList = 141 new ArrayList<List<RolapMember>>(); 142 for (List<Member> members : aggregationList) { 143 // REVIEW: do we need to copy? 144 List<RolapMember> rolapMembers = Util.cast(members); 145 rolapAggregationList.add(rolapMembers); 146 } 147 148 unsatisfiable = 149 makeCompoundGroup( 150 starColumnCount, 151 measure.getCube(), 152 rolapAggregationList, 153 compoundGroupMap); 154 155 if (unsatisfiable) { 156 return null; 157 } 158 compoundPredicate = 159 makeCompoundPredicate(compoundGroupMap, measure.getCube()); 160 161 if (compoundPredicate != null) { 162 /* 163 * Only add the compound constraint when it is not empty. 164 */ 165 for (BitKey bitKey : compoundGroupMap.keySet()) { 166 compoundBitKey = compoundBitKey.or(bitKey); 167 } 168 request.addAggregateList(compoundBitKey, compoundPredicate); 169 } 170 } 171 172 return request; 173 } 174 175 private static CellRequest makeCellRequest( 176 final Member[] members, 177 boolean drillThrough, 178 final boolean extendedContext, 179 RolapCube cube, 180 List<Exp> fieldsList) 181 { 182 // Need cube for drill-through requests 183 assert drillThrough == (cube != null); 184 185 if (extendedContext) { 186 assert (drillThrough); 187 } 188 189 final RolapStoredMeasure measure; 190 if (drillThrough) { 191 cube = RolapCell.chooseDrillThroughCube(members, cube); 192 if (cube == null) { 193 return null; 194 } 195 if (members.length > 0 196 && members[0] instanceof RolapStoredMeasure) 197 { 198 measure = (RolapStoredMeasure) members[0]; 199 } else { 200 measure = (RolapStoredMeasure) cube.getMeasures().get(0); 201 } 202 } else { 203 if (members.length > 0 204 && members[0] instanceof RolapStoredMeasure) 205 { 206 measure = (RolapStoredMeasure) members[0]; 207 } else { 208 return null; 209 } 210 } 211 212 final RolapStar.Measure starMeasure = 213 (RolapStar.Measure) measure.getStarMeasure(); 214 assert starMeasure != null; 215 final CellRequest request; 216 if (drillThrough) { 217 request = 218 new DrillThroughCellRequest(starMeasure, extendedContext); 219 } else { 220 request = 221 new CellRequest(starMeasure, extendedContext, drillThrough); 222 } 223 224 // Since 'request.extendedContext == false' is a well-worn code path, 225 // we have moved the test outside the loop. 226 if (extendedContext) { 227 if (fieldsList != null) { 228 // If a field list was specified, there will be some columns 229 // to include in the result set, other that we don't. This 230 // happens when the MDX is a DRILLTHROUGH operation and 231 // includes a RETURN clause. 232 final SchemaReader reader = cube.getSchemaReader().withLocus(); 233 for (Exp exp : fieldsList) { 234 final OlapElement member = 235 reader.lookupCompound( 236 cube, 237 Util.parseIdentifier(exp.toString()), 238 true, 239 Category.Unknown); 240 if (member.getHierarchy() instanceof RolapCubeHierarchy 241 && ((RolapCubeHierarchy)member.getHierarchy()) 242 .getRolapHierarchy().closureFor != null) 243 { 244 continue; 245 } 246 addNonConstrainingColumns(member, cube, request); 247 } 248 } 249 for (int i = 1; i < members.length; i++) { 250 final RolapCubeMember member = (RolapCubeMember) members[i]; 251 if (member.getHierarchy().getRolapHierarchy().closureFor 252 != null) 253 { 254 continue; 255 } 256 257 addNonConstrainingColumns(member, cube, request); 258 259 final RolapCubeLevel level = member.getLevel(); 260 final boolean needToReturnNull = 261 level.getLevelReader().constrainRequest( 262 member, measure.getCube(), request); 263 if (needToReturnNull) { 264 return null; 265 } 266 } 267 268 } else { 269 for (int i = 1; i < members.length; i++) { 270 if (!(members[i] instanceof RolapCubeMember)) { 271 continue; 272 } 273 RolapCubeMember member = (RolapCubeMember) members[i]; 274 final RolapCubeLevel level = member.getLevel(); 275 final boolean needToReturnNull = 276 level.getLevelReader().constrainRequest( 277 member, measure.getCube(), request); 278 if (needToReturnNull) { 279 return null; 280 } 281 } 282 } 283 return request; 284 } 285 286 /** 287 * Adds the key columns as non-constraining columns. For 288 * example, if they asked for [Gender].[M], [Store].[USA].[CA] 289 * then the following levels are in play:<ul> 290 * <li>Gender = 'M' 291 * <li>Marital Status not constraining 292 * <li>Nation = 'USA' 293 * <li>State = 'CA' 294 * <li>City not constraining 295 * </ul> 296 * 297 * <p>Note that [Marital Status] column is present by virtue of 298 * the implicit [Marital Status].[All] member. Hence the SQL 299 * 300 * <blockquote><pre> 301 * select [Marital Status], [City] 302 * from [Star] 303 * where [Gender] = 'M' 304 * and [Nation] = 'USA' 305 * and [State] = 'CA' 306 * </pre></blockquote> 307 * 308 * @param member Member to constraint 309 * @param baseCube base cube if virtual 310 * @param request Cell request 311 */ 312 private static void addNonConstrainingColumns( 313 final RolapCubeMember member, 314 final RolapCube baseCube, 315 final CellRequest request) 316 { 317 final RolapCubeHierarchy hierarchy = member.getHierarchy(); 318 final RolapCubeLevel[] levels = hierarchy.getLevels(); 319 for (int j = levels.length - 1, depth = member.getLevel().getDepth(); 320 j > depth; j--) 321 { 322 final RolapCubeLevel level = levels[j]; 323 RolapStar.Column column = level.getBaseStarKeyColumn(baseCube); 324 if (column != null) { 325 request.addConstrainedColumn(column, null); 326 if (request.extendedContext 327 && level.getNameExp() != null) 328 { 329 final RolapStar.Column nameColumn = column.getNameColumn(); 330 Util.assertTrue(nameColumn != null); 331 request.addConstrainedColumn(nameColumn, null); 332 } 333 } 334 } 335 } 336 337 private static void addNonConstrainingColumns( 338 final OlapElement member, 339 final RolapCube baseCube, 340 final CellRequest request) 341 { 342 RolapCubeLevel level; 343 if (member instanceof RolapCubeLevel) { 344 level = (RolapCubeLevel) member; 345 } else if (member instanceof RolapCubeHierarchy 346 || member instanceof RolapCubeDimension) 347 { 348 level = (RolapCubeLevel) member.getHierarchy().getLevels()[0]; 349 if (level.isAll()) { 350 level = level.getChildLevel(); 351 } 352 } else if (member instanceof RolapStar.Measure) { 353 ((DrillThroughCellRequest)request) 354 .addDrillThroughMeasure((RolapStar.Measure)member); 355 return; 356 } else if (member instanceof RolapBaseCubeMeasure) { 357 ((DrillThroughCellRequest)request) 358 .addDrillThroughMeasure( 359 (RolapStar.Measure) 360 ((RolapBaseCubeMeasure)member).getStarMeasure()); 361 return; 362 } else if (member instanceof RolapHierarchy.RolapCalculatedMeasure) { 363 throw MondrianResource.instance().DrillthroughCalculatedMember 364 .ex(member.getUniqueName()); 365 } else { 366 throw new MondrianException( 367 "Unknown member type in DRILLTHROUGH operation."); 368 } 369 RolapStar.Column column = level.getBaseStarKeyColumn(baseCube); 370 if (column != null) { 371 request.addConstrainedColumn(column, null); 372 ((DrillThroughCellRequest)request).addDrillThroughColumn(column); 373 if (request.extendedContext 374 && level.getNameExp() != null) 375 { 376 final RolapStar.Column nameColumn = column.getNameColumn(); 377 Util.assertTrue(nameColumn != null); 378 request.addConstrainedColumn(nameColumn, null); 379 } 380 } 381 } 382 383 /** 384 * Groups members (or tuples) from the same compound (i.e. hierarchy) into 385 * groups that are constrained by the same set of columns. 386 * 387 * <p>E.g. 388 * 389 * <pre>Members 390 * [USA].[CA], 391 * [Canada].[BC], 392 * [USA].[CA].[San Francisco], 393 * [USA].[OR].[Portland]</pre> 394 * 395 * will be grouped into 396 * 397 * <pre>Group 1: 398 * {[USA].[CA], [Canada].[BC]} 399 * Group 2: 400 * {[USA].[CA].[San Francisco], [USA].[OR].[Portland]}</pre> 401 * 402 * <p>This helps with generating optimal form of sql. 403 * 404 * <p>In case of aggregating over a list of tuples, similar logic also 405 * applies. 406 * 407 * <p>For example: 408 * 409 * <pre>Tuples: 410 * ([Gender].[M], [Store].[USA].[CA]) 411 * ([Gender].[F], [Store].[USA].[CA]) 412 * ([Gender].[M], [Store].[USA]) 413 * ([Gender].[F], [Store].[Canada])</pre> 414 * 415 * will be grouped into 416 * 417 * <pre>Group 1: 418 * {([Gender].[M], [Store].[USA].[CA]), 419 * ([Gender].[F], [Store].[USA].[CA])} 420 * Group 2: 421 * {([Gender].[M], [Store].[USA]), 422 * ([Gender].[F], [Store].[Canada])}</pre> 423 * 424 * <p>This function returns a boolean value indicating if any constraint 425 * can be created from the aggregationList. It is possible that only part 426 * of the aggregationList can be applied, which still leads to a (partial) 427 * constraint that is represented by the compoundGroupMap. 428 */ 429 private static boolean makeCompoundGroup( 430 int starColumnCount, 431 RolapCube baseCube, 432 List<List<RolapMember>> aggregationList, 433 Map<BitKey, List<RolapCubeMember[]>> compoundGroupMap) 434 { 435 // The more generalized aggregation as aggregating over tuples. 436 // The special case is a tuple defined by only one member. 437 int unsatisfiableTupleCount = 0; 438 for (List<RolapMember> aggregation : aggregationList) { 439 boolean isTuple; 440 if (aggregation.size() > 0 441 && (aggregation.get(0) instanceof RolapCubeMember 442 || aggregation.get(0) instanceof VisualTotalMember)) 443 { 444 isTuple = true; 445 } else { 446 ++unsatisfiableTupleCount; 447 continue; 448 } 449 450 BitKey bitKey = BitKey.Factory.makeBitKey(starColumnCount); 451 RolapCubeMember[] tuple; 452 453 tuple = new RolapCubeMember[aggregation.size()]; 454 int i = 0; 455 for (Member member : aggregation) { 456 if (member instanceof VisualTotalMember) { 457 tuple[i] = (RolapCubeMember) 458 ((VisualTotalMember) member).getMember(); 459 } else { 460 tuple[i] = (RolapCubeMember)member; 461 } 462 i++; 463 } 464 465 boolean tupleUnsatisfiable = false; 466 for (RolapCubeMember member : tuple) { 467 // Tuple cannot be constrained if any of the member cannot be. 468 tupleUnsatisfiable = 469 makeCompoundGroupForMember(member, baseCube, bitKey); 470 if (tupleUnsatisfiable) { 471 // If this tuple is unsatisfiable, skip it and try to 472 // constrain the next tuple. 473 unsatisfiableTupleCount ++; 474 break; 475 } 476 } 477 478 if (!tupleUnsatisfiable && !bitKey.isEmpty()) { 479 // Found tuple(columns) to constrain, 480 // now add it to the compoundGroupMap 481 addTupleToCompoundGroupMap(tuple, bitKey, compoundGroupMap); 482 } 483 } 484 485 return (unsatisfiableTupleCount == aggregationList.size()); 486 } 487 488 private static void addTupleToCompoundGroupMap( 489 RolapCubeMember[] tuple, 490 BitKey bitKey, 491 Map<BitKey, List<RolapCubeMember[]>> compoundGroupMap) 492 { 493 List<RolapCubeMember[]> compoundGroup = compoundGroupMap.get(bitKey); 494 if (compoundGroup == null) { 495 compoundGroup = new ArrayList<RolapCubeMember[]>(); 496 compoundGroupMap.put(bitKey, compoundGroup); 497 } 498 compoundGroup.add(tuple); 499 } 500 501 private static boolean makeCompoundGroupForMember( 502 RolapCubeMember member, 503 RolapCube baseCube, 504 BitKey bitKey) 505 { 506 RolapCubeMember levelMember = member; 507 boolean memberUnsatisfiable = false; 508 while (levelMember != null) { 509 RolapCubeLevel level = levelMember.getLevel(); 510 // Only need to constrain the nonAll levels 511 if (!level.isAll()) { 512 RolapStar.Column column = level.getBaseStarKeyColumn(baseCube); 513 if (column != null) { 514 bitKey.set(column.getBitPosition()); 515 } else { 516 // One level in a member causes the member to be 517 // unsatisfiable. 518 memberUnsatisfiable = true; 519 break; 520 } 521 } 522 523 levelMember = levelMember.getParentMember(); 524 } 525 return memberUnsatisfiable; 526 } 527 528 /** 529 * Translates a Map<BitKey, List<RolapMember>> of the same 530 * compound member into {@link ListPredicate} by traversing a list of 531 * members or tuples. 532 * 533 * <p>1. The example below is for list of tuples 534 * 535 * <blockquote> 536 * group 1: [Gender].[M], [Store].[USA].[CA]<br/> 537 * group 2: [Gender].[F], [Store].[USA].[CA] 538 * </blockquote> 539 * 540 * is translated into 541 * 542 * <blockquote> 543 * (Gender=M AND Store_State=CA AND Store_Country=USA)<br/> 544 * OR<br/> 545 * (Gender=F AND Store_State=CA AND Store_Country=USA) 546 * </blockquote> 547 * 548 * <p>The caller of this method will translate this representation into 549 * appropriate SQL form as 550 * <blockquote> 551 * where (gender = 'M'<br/> 552 * and Store_State = 'CA'<br/> 553 * AND Store_Country = 'USA')<br/> 554 * OR (Gender = 'F'<br/> 555 * and Store_State = 'CA'<br/> 556 * AND Store_Country = 'USA') 557 * </blockquote> 558 * 559 * <p>2. The example below for a list of members 560 * <blockquote> 561 * group 1: [USA].[CA], [Canada].[BC]<br/> 562 * group 2: [USA].[CA].[San Francisco], [USA].[OR].[Portland] 563 * </blockquote> 564 * 565 * is translated into: 566 * 567 * <blockquote> 568 * (Country=USA AND State=CA)<br/> 569 * OR (Country=Canada AND State=BC)<br/> 570 * OR (Country=USA AND State=CA AND City=San Francisco)<br/> 571 * OR (Country=USA AND State=OR AND City=Portland) 572 * </blockquote> 573 * 574 * <p>The caller of this method will translate this representation into 575 * appropriate SQL form. For exmaple, if the underlying DB supports multi 576 * value IN-list, the second group will turn into this predicate: 577 * 578 * <blockquote> 579 * where (country, state, city) IN ((USA, CA, San Francisco), 580 * (USA, OR, Portland)) 581 * </blockquote> 582 * 583 * or, if the DB does not support multi-value IN list: 584 * 585 * <blockquote> 586 * where country=USA AND 587 * ((state=CA AND city = San Francisco) OR 588 * (state=OR AND city=Portland)) 589 * </blockquote> 590 * 591 * @param compoundGroupMap Map from dimensionality to groups 592 * @param baseCube base cube if virtual 593 * @return compound predicate for a tuple or a member 594 */ 595 private static StarPredicate makeCompoundPredicate( 596 Map<BitKey, List<RolapCubeMember[]>> compoundGroupMap, 597 RolapCube baseCube) 598 { 599 List<StarPredicate> compoundPredicateList = 600 new ArrayList<StarPredicate> (); 601 for (List<RolapCubeMember[]> group : compoundGroupMap.values()) { 602 /* 603 * e.g. 604 * {[USA].[CA], [Canada].[BC]} 605 * or 606 * { 607 */ 608 StarPredicate compoundGroupPredicate = null; 609 for (RolapCubeMember[] tuple : group) { 610 /* 611 * [USA].[CA] 612 */ 613 StarPredicate tuplePredicate = null; 614 615 for (RolapCubeMember member : tuple) { 616 tuplePredicate = makeCompoundPredicateForMember( 617 member, baseCube, tuplePredicate); 618 } 619 if (tuplePredicate != null) { 620 if (compoundGroupPredicate == null) { 621 compoundGroupPredicate = tuplePredicate; 622 } else { 623 compoundGroupPredicate = 624 compoundGroupPredicate.or(tuplePredicate); 625 } 626 } 627 } 628 629 if (compoundGroupPredicate != null) { 630 /* 631 * Sometimes the compound member list does not constrain any 632 * columns; for example, if only AllLevel is present. 633 */ 634 compoundPredicateList.add(compoundGroupPredicate); 635 } 636 } 637 638 StarPredicate compoundPredicate = null; 639 640 if (compoundPredicateList.size() > 1) { 641 compoundPredicate = new OrPredicate(compoundPredicateList); 642 } else if (compoundPredicateList.size() == 1) { 643 compoundPredicate = compoundPredicateList.get(0); 644 } 645 646 return compoundPredicate; 647 } 648 649 private static StarPredicate makeCompoundPredicateForMember( 650 RolapCubeMember member, 651 RolapCube baseCube, 652 StarPredicate memberPredicate) 653 { 654 while (member != null) { 655 RolapCubeLevel level = member.getLevel(); 656 if (!level.isAll()) { 657 RolapStar.Column column = level.getBaseStarKeyColumn(baseCube); 658 if (memberPredicate == null) { 659 memberPredicate = 660 new ValueColumnPredicate(column, member.getKey()); 661 } else { 662 memberPredicate = 663 memberPredicate.and( 664 new ValueColumnPredicate(column, member.getKey())); 665 } 666 } 667 // Don't need to constrain USA if CA is unique 668 if (member.getLevel().isUnique()) { 669 break; 670 } 671 member = member.getParentMember(); 672 } 673 return memberPredicate; 674 } 675 676 /** 677 * Retrieves the value of a cell from the cache. 678 * 679 * @param request Cell request 680 * @pre request != null && !request.isUnsatisfiable() 681 * @return Cell value, or null if cell is not in any aggregation in cache, 682 * or {@link Util#nullValue} if cell's value is null 683 */ 684 public abstract Object getCellFromCache(CellRequest request); 685 686 public abstract Object getCellFromCache( 687 CellRequest request, 688 PinSet pinSet); 689 690 /** 691 * Generates a SQL statement which will return the rows which contribute to 692 * this request. 693 * 694 * @param request Cell request 695 * @param countOnly If true, return a statment which returns only the count 696 * @param starPredicateSlicer A StarPredicate representing slicer positions 697 * that could not be represented by the CellRequest, or 698 * <code>null</code> if no additional predicate is necessary. 699 * @return SQL statement 700 */ 701 public abstract String getDrillThroughSql( 702 DrillThroughCellRequest request, 703 StarPredicate starPredicateSlicer, 704 List<Exp> fields, 705 boolean countOnly); 706 707 public static RolapCacheRegion makeCacheRegion( 708 final RolapStar star, 709 final CacheControl.CellRegion region) 710 { 711 final List<Member> measureList = CacheControlImpl.findMeasures(region); 712 final List<RolapStar.Measure> starMeasureList = 713 new ArrayList<RolapStar.Measure>(); 714 RolapCube baseCube = null; 715 for (Member measure : measureList) { 716 if (!(measure instanceof RolapStoredMeasure)) { 717 continue; 718 } 719 final RolapStoredMeasure storedMeasure = 720 (RolapStoredMeasure) measure; 721 final RolapStar.Measure starMeasure = 722 (RolapStar.Measure) storedMeasure.getStarMeasure(); 723 assert starMeasure != null; 724 if (star != starMeasure.getStar()) { 725 continue; 726 } 727 // TODO: each time this code executes, baseCube is set. 728 // Should there be a 'break' here? Are all of the 729 // storedMeasure cubes the same cube? Is the measureList always 730 // non-empty so that baseCube is always set? 731 baseCube = storedMeasure.getCube(); 732 starMeasureList.add(starMeasure); 733 } 734 final RolapCacheRegion cacheRegion = 735 new RolapCacheRegion(star, starMeasureList); 736 if (region instanceof CacheControlImpl.CrossjoinCellRegion) { 737 final CacheControlImpl.CrossjoinCellRegion crossjoin = 738 (CacheControlImpl.CrossjoinCellRegion) region; 739 for (CacheControl.CellRegion component 740 : crossjoin.getComponents()) 741 { 742 constrainCacheRegion(cacheRegion, baseCube, component); 743 } 744 } else { 745 constrainCacheRegion(cacheRegion, baseCube, region); 746 } 747 return cacheRegion; 748 } 749 750 private static void constrainCacheRegion( 751 final RolapCacheRegion cacheRegion, 752 final RolapCube baseCube, 753 final CacheControl.CellRegion region) 754 { 755 if (region instanceof CacheControlImpl.MemberCellRegion) { 756 final CacheControlImpl.MemberCellRegion memberCellRegion = 757 (CacheControlImpl.MemberCellRegion) region; 758 final List<Member> memberList = memberCellRegion.getMemberList(); 759 for (Member member : memberList) { 760 if (member.isMeasure()) { 761 continue; 762 } 763 final RolapCubeMember rolapMember; 764 if (member instanceof RolapCubeMember) { 765 rolapMember = (RolapCubeMember) member; 766 } else { 767 rolapMember = 768 (RolapCubeMember) baseCube.getSchemaReader() 769 .getMemberByUniqueName( 770 Util.parseIdentifier(member.getUniqueName()), 771 true); 772 } 773 final RolapCubeLevel level = rolapMember.getLevel(); 774 RolapStar.Column column = level.getBaseStarKeyColumn(baseCube); 775 776 level.getLevelReader().constrainRegion( 777 new MemberColumnPredicate(column, rolapMember), 778 baseCube, 779 cacheRegion); 780 } 781 } else if (region instanceof CacheControlImpl.MemberRangeCellRegion) { 782 final CacheControlImpl.MemberRangeCellRegion rangeRegion = 783 (CacheControlImpl.MemberRangeCellRegion) region; 784 final RolapCubeLevel level = (RolapCubeLevel)rangeRegion.getLevel(); 785 RolapStar.Column column = level.getBaseStarKeyColumn(baseCube); 786 787 level.getLevelReader().constrainRegion( 788 new RangeColumnPredicate( 789 column, 790 rangeRegion.getLowerInclusive(), 791 (rangeRegion.getLowerBound() == null 792 ? null 793 : new MemberColumnPredicate( 794 column, rangeRegion.getLowerBound())), 795 rangeRegion.getUpperInclusive(), 796 (rangeRegion.getUpperBound() == null 797 ? null 798 : new MemberColumnPredicate( 799 column, rangeRegion.getUpperBound()))), 800 baseCube, 801 cacheRegion); 802 } else { 803 throw new UnsupportedOperationException(); 804 } 805 } 806 807 /** 808 * Returns a {@link mondrian.rolap.CellReader} which reads cells from cache. 809 */ 810 public CellReader getCacheCellReader() { 811 return new CellReader() { 812 // implement CellReader 813 public Object get(RolapEvaluator evaluator) { 814 CellRequest request = makeRequest(evaluator); 815 if (request == null || request.isUnsatisfiable()) { 816 // request out of bounds 817 return Util.nullValue; 818 } 819 return getCellFromCache(request); 820 } 821 822 public int getMissCount() { 823 return 0; // RolapAggregationManager never lies 824 } 825 826 public boolean isDirty() { 827 return false; 828 } 829 }; 830 } 831 832 /** 833 * Creates a {@link PinSet}. 834 * 835 * @return a new PinSet 836 */ 837 public abstract PinSet createPinSet(); 838 839 /** 840 * A set of segments which are pinned (prevented from garbage collection) 841 * for a short duration as a result of a cache inquiry. 842 */ 843 public interface PinSet { 844 } 845} 846 847// End RolapAggregationManager.java