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-2013 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.rolap; 012 013import mondrian.olap.*; 014import mondrian.rolap.agg.*; 015import mondrian.spi.MemberFormatter; 016 017/** 018 * RolapCubeLevel wraps a RolapLevel for a specific Cube. 019 * 020 * @author Will Gorman, 19 October 2007 021 */ 022public class RolapCubeLevel extends RolapLevel { 023 024 private final RolapLevel rolapLevel; 025 private RolapStar.Column starKeyColumn = null; 026 /** 027 * For a parent-child hierarchy with a closure provided by the schema, 028 * the equivalent level in the closed hierarchy; otherwise null. 029 */ 030 private RolapCubeLevel closedPeerCubeLevel; 031 protected LevelReader levelReader; 032 private final RolapCubeHierarchy cubeHierarchy; 033 private final RolapCubeDimension cubeDimension; 034 private final RolapCube cube; 035 private final RolapCubeLevel parentCubeLevel; 036 private RolapCubeLevel childCubeLevel; 037 038 public RolapCubeLevel(RolapLevel level, RolapCubeHierarchy cubeHierarchy) { 039 super( 040 cubeHierarchy, 041 level.getName(), 042 level.getCaption(), 043 level.isVisible(), 044 level.getDescription(), 045 level.getDepth(), 046 level.getKeyExp(), 047 level.getNameExp(), 048 level.getCaptionExp(), 049 level.getOrdinalExp(), 050 level.getParentExp(), 051 level.getNullParentValue(), 052 null, 053 level.getProperties(), 054 level.getFlags(), 055 level.getDatatype(), 056 level.getInternalType(), 057 level.getHideMemberCondition(), 058 level.getLevelType(), 059 "" + level.getApproxRowCount(), 060 level.getAnnotationMap()); 061 062 this.rolapLevel = level; 063 this.cubeHierarchy = cubeHierarchy; 064 this.cubeDimension = (RolapCubeDimension) cubeHierarchy.getDimension(); 065 cube = cubeDimension.getCube(); 066 parentCubeLevel = (RolapCubeLevel) super.getParentLevel(); 067 if (parentCubeLevel != null) { 068 parentCubeLevel.childCubeLevel = this; 069 } 070 MondrianDef.RelationOrJoin hierarchyRel = cubeHierarchy.getRelation(); 071 keyExp = convertExpression(level.getKeyExp(), hierarchyRel); 072 nameExp = convertExpression(level.getNameExp(), hierarchyRel); 073 captionExp = convertExpression(level.getCaptionExp(), hierarchyRel); 074 ordinalExp = convertExpression(level.getOrdinalExp(), hierarchyRel); 075 parentExp = convertExpression(level.getParentExp(), hierarchyRel); 076 properties = convertProperties(level.getProperties(), hierarchyRel); 077 } 078 079 void init(MondrianDef.CubeDimension xmlDimension) { 080 if (isAll()) { 081 this.levelReader = new AllLevelReaderImpl(); 082 } else if (getLevelType() == LevelType.Null) { 083 this.levelReader = new NullLevelReader(); 084 } else if (rolapLevel.xmlClosure != null) { 085 RolapDimension dimension = 086 (RolapDimension) 087 rolapLevel.getClosedPeer().getHierarchy().getDimension(); 088 089 RolapCubeDimension cubeDimension = 090 new RolapCubeDimension( 091 getCube(), dimension, xmlDimension, 092 getDimension().getName() + "$Closure", 093 -1, 094 getCube().hierarchyList, 095 getDimension().isHighCardinality()); 096 097 // RME HACK 098 // WG: Note that the reason for registering this usage is so that 099 // when registerDimension is called, the hierarchy is registered 100 // successfully to the star. This type of hack will go away once 101 // HierarchyUsage is phased out 102 if (! getCube().isVirtual()) { 103 getCube().createUsage( 104 (RolapCubeHierarchy) cubeDimension.getHierarchies()[0], 105 xmlDimension); 106 } 107 cubeDimension.init(xmlDimension); 108 getCube().registerDimension(cubeDimension); 109 closedPeerCubeLevel = (RolapCubeLevel) 110 cubeDimension.getHierarchies()[0].getLevels()[1]; 111 112 if (!getCube().isVirtual()) { 113 getCube().closureColumnBitKey.set( 114 closedPeerCubeLevel.starKeyColumn.getBitPosition()); 115 } 116 117 this.levelReader = new ParentChildLevelReaderImpl(this); 118 } else { 119 this.levelReader = new RegularLevelReader(this); 120 } 121 } 122 123 private RolapProperty[] convertProperties( 124 RolapProperty[] properties, 125 MondrianDef.RelationOrJoin rel) 126 { 127 if (properties == null) { 128 return null; 129 } 130 131 RolapProperty[] convertedProperties = 132 new RolapProperty[properties.length]; 133 for (int i = 0; i < properties.length; i++) { 134 RolapProperty old = properties[i]; 135 convertedProperties[i] = 136 new RolapProperty( 137 old.getName(), 138 old.getType(), 139 convertExpression(old.getExp(), rel), 140 old.getFormatter(), 141 old.getCaption(), 142 old.dependsOnLevelValue(), 143 old.isInternal(), 144 old.getDescription()); 145 } 146 return convertedProperties; 147 } 148 149 /** 150 * Converts an expression to new aliases if necessary. 151 * 152 * @param exp the expression to convert 153 * @param rel the parent relation 154 * @return returns the converted expression 155 */ 156 private MondrianDef.Expression convertExpression( 157 MondrianDef.Expression exp, 158 MondrianDef.RelationOrJoin rel) 159 { 160 if (getHierarchy().isUsingCubeFact()) { 161 // no conversion necessary 162 return exp; 163 } else if (exp == null || rel == null) { 164 return null; 165 } else if (exp instanceof MondrianDef.Column) { 166 MondrianDef.Column col = (MondrianDef.Column)exp; 167 if (rel instanceof MondrianDef.Table) { 168 return new MondrianDef.Column( 169 ((MondrianDef.Table) rel).getAlias(), 170 col.getColumnName()); 171 } else if (rel instanceof MondrianDef.Join 172 || rel instanceof MondrianDef.Relation) 173 { 174 // need to determine correct name of alias for this level. 175 // this may be defined in level 176 // col.table 177 String alias = getHierarchy().lookupAlias(col.getTableAlias()); 178 return new MondrianDef.Column(alias, col.getColumnName()); 179 } 180 } else if (exp instanceof MondrianDef.ExpressionView) { 181 // this is a limitation, in the future, we may need 182 // to replace the table name in the sql provided 183 // with the new aliased name 184 return exp; 185 } 186 throw new RuntimeException( 187 "conversion of Class " + exp.getClass() 188 + " unsupported at this time"); 189 } 190 191 public void setStarKeyColumn(RolapStar.Column column) { 192 starKeyColumn = column; 193 } 194 195 /** 196 * This is the RolapStar.Column that is related to this RolapCubeLevel 197 * 198 * @return the RolapStar.Column related to this RolapCubeLevel 199 */ 200 public RolapStar.Column getStarKeyColumn() { 201 return starKeyColumn; 202 } 203 204 LevelReader getLevelReader() { 205 return levelReader; 206 } 207 208 /** 209 * this method returns the RolapStar.Column if non-virtual, 210 * if virtual, find the base cube level and return it's 211 * column 212 * 213 * @param baseCube the base cube for the specificed virtual level 214 * @return the RolapStar.Column related to this RolapCubeLevel 215 */ 216 public RolapStar.Column getBaseStarKeyColumn(RolapCube baseCube) { 217 RolapStar.Column column = null; 218 if (getCube().isVirtual() && baseCube != null) { 219 RolapCubeLevel lvl = baseCube.findBaseCubeLevel(this); 220 if (lvl != null) { 221 column = lvl.getStarKeyColumn(); 222 } 223 } else { 224 column = getStarKeyColumn(); 225 } 226 return column; 227 } 228 229 /** 230 * Returns the (non virtual) cube this level belongs to. 231 * 232 * @return cube 233 */ 234 public final RolapCube getCube() { 235 return cube; 236 } 237 238 // override with stricter return type 239 public final RolapCubeDimension getDimension() { 240 return cubeDimension; 241 } 242 243 // override with stricter return type 244 public final RolapCubeHierarchy getHierarchy() { 245 return cubeHierarchy; 246 } 247 248 // override with stricter return type 249 public final RolapCubeLevel getChildLevel() { 250 return childCubeLevel; 251 } 252 253 // override with stricter return type 254 public final RolapCubeLevel getParentLevel() { 255 return parentCubeLevel; 256 } 257 258 public String getCaption() { 259 return rolapLevel.getCaption(); 260 } 261 262 public void setCaption(String caption) { 263 // Cannot set the caption on the underlying level; other cube levels 264 // might be using it. 265 throw new UnsupportedOperationException(); 266 } 267 268 /** 269 * Returns the underlying level. 270 * 271 * @return Underlying level 272 */ 273 public RolapLevel getRolapLevel() { 274 return rolapLevel; 275 } 276 277 public boolean equals(RolapCubeLevel level) { 278 if (this == level) { 279 return true; 280 } 281 // verify the levels are part of the same hierarchy 282 return super.equals(level) 283 && getCube().equals(level.getCube()); 284 } 285 286 boolean hasClosedPeer() { 287 return closedPeerCubeLevel != null; 288 } 289 290 public RolapCubeLevel getClosedPeer() { 291 return closedPeerCubeLevel; 292 } 293 294 public MemberFormatter getMemberFormatter() { 295 return rolapLevel.getMemberFormatter(); 296 } 297 298 299 300 /** 301 * Encapsulation of the difference between levels in terms of how 302 * constraints are generated. There are implementations for 'all' levels, 303 * the 'null' level, parent-child levels and regular levels. 304 */ 305 interface LevelReader { 306 307 /** 308 * Adds constraints to a cell request for a member of this level. 309 * 310 * @param member Member to be constrained 311 * @param baseCube base cube if virtual level 312 * @param request Request to be constrained 313 * 314 * @return true if request is unsatisfiable (e.g. if the member is the 315 * null member) 316 */ 317 boolean constrainRequest( 318 RolapCubeMember member, 319 RolapCube baseCube, 320 CellRequest request); 321 322 /** 323 * Adds constraints to a cache region for a member of this level. 324 * 325 * @param predicate Predicate 326 * @param baseCube base cube if virtual level 327 * @param cacheRegion Cache region to be constrained 328 */ 329 void constrainRegion( 330 StarColumnPredicate predicate, 331 RolapCube baseCube, 332 RolapCacheRegion cacheRegion); 333 } 334 335 /** 336 * Level reader for a regular level. 337 */ 338 static final class RegularLevelReader implements LevelReader { 339 private RolapCubeLevel cubeLevel; 340 341 RegularLevelReader( 342 RolapCubeLevel cubeLevel) 343 { 344 this.cubeLevel = cubeLevel; 345 } 346 347 public boolean constrainRequest( 348 RolapCubeMember member, 349 RolapCube baseCube, 350 CellRequest request) 351 { 352 assert member.getLevel() == cubeLevel; 353 Object memberKey = member.member.getKey(); 354 if (memberKey == null) { 355 if (member == member.getHierarchy().getNullMember()) { 356 // cannot form a request if one of the members is null 357 return true; 358 } else { 359 throw Util.newInternal("why is key null?"); 360 } 361 } 362 363 RolapStar.Column column = cubeLevel.getBaseStarKeyColumn(baseCube); 364 365 if (column == null) { 366 // This hierarchy is not one which qualifies the starMeasure 367 // (this happens in virtual cubes). The starMeasure only has 368 // a value for the 'all' member of the hierarchy (or for the 369 // default member if the hierarchy has no 'all' member) 370 return member != cubeLevel.hierarchy.getDefaultMember() 371 || cubeLevel.hierarchy.hasAll(); 372 } 373 374 boolean isMemberCalculated = member.member.isCalculated(); 375 376 final StarColumnPredicate predicate; 377 if (isMemberCalculated && !member.isParentChildLeaf()) { 378 predicate = null; 379 } else { 380 predicate = new ValueColumnPredicate(column, memberKey); 381 } 382 383 // use the member as constraint; this will give us some 384 // optimization potential 385 request.addConstrainedColumn(column, predicate); 386 387 if (request.extendedContext 388 && cubeLevel.getNameExp() != null) 389 { 390 final RolapStar.Column nameColumn = column.getNameColumn(); 391 392 assert nameColumn != null; 393 request.addConstrainedColumn(nameColumn, null); 394 } 395 396 if (isMemberCalculated) { 397 return false; 398 } 399 400 // If member is unique without reference to its parent, 401 // no further constraint is required. 402 if (cubeLevel.isUnique()) { 403 return false; 404 } 405 406 // Constrain the parent member, if any. 407 RolapCubeMember parent = member.getParentMember(); 408 while (true) { 409 if (parent == null) { 410 return false; 411 } 412 RolapCubeLevel level = parent.getLevel(); 413 final LevelReader levelReader = level.levelReader; 414 if (levelReader == this) { 415 // We are looking at a parent in a parent-child hierarchy, 416 // for example, we have moved from Fred to Fred's boss, 417 // Wilma. We don't want to include Wilma's key in the 418 // request. 419 parent = parent.getParentMember(); 420 continue; 421 } 422 return levelReader.constrainRequest( 423 parent, baseCube, request); 424 } 425 } 426 427 public void constrainRegion( 428 StarColumnPredicate predicate, 429 RolapCube baseCube, 430 RolapCacheRegion cacheRegion) 431 { 432 RolapStar.Column column = cubeLevel.getBaseStarKeyColumn(baseCube); 433 434 if (column == null) { 435 // This hierarchy is not one which qualifies the starMeasure 436 // (this happens in virtual cubes). The starMeasure only has 437 // a value for the 'all' member of the hierarchy (or for the 438 // default member if the hierarchy has no 'all' member) 439 return; 440 } 441 442 if (predicate instanceof MemberColumnPredicate) { 443 MemberColumnPredicate memberColumnPredicate = 444 (MemberColumnPredicate) predicate; 445 RolapMember member = memberColumnPredicate.getMember(); 446 assert member.getLevel() == cubeLevel; 447 assert !member.isCalculated(); 448 assert memberColumnPredicate.getMember().getKey() != null; 449 assert memberColumnPredicate.getMember().getKey() 450 != RolapUtil.sqlNullValue; 451 assert !member.isNull(); 452 453 // use the member as constraint, this will give us some 454 // optimization potential 455 cacheRegion.addPredicate(column, predicate); 456 return; 457 } else if (predicate instanceof RangeColumnPredicate) { 458 RangeColumnPredicate rangeColumnPredicate = 459 (RangeColumnPredicate) predicate; 460 final ValueColumnPredicate lowerBound = 461 rangeColumnPredicate.getLowerBound(); 462 RolapMember lowerMember; 463 if (lowerBound == null) { 464 lowerMember = null; 465 } else if (lowerBound instanceof MemberColumnPredicate) { 466 MemberColumnPredicate memberColumnPredicate = 467 (MemberColumnPredicate) lowerBound; 468 lowerMember = memberColumnPredicate.getMember(); 469 } else { 470 throw new UnsupportedOperationException(); 471 } 472 final ValueColumnPredicate upperBound = 473 rangeColumnPredicate.getUpperBound(); 474 RolapMember upperMember; 475 if (upperBound == null) { 476 upperMember = null; 477 } else if (upperBound instanceof MemberColumnPredicate) { 478 MemberColumnPredicate memberColumnPredicate = 479 (MemberColumnPredicate) upperBound; 480 upperMember = memberColumnPredicate.getMember(); 481 } else { 482 throw new UnsupportedOperationException(); 483 } 484 MemberTuplePredicate predicate2 = 485 new MemberTuplePredicate( 486 baseCube, 487 lowerMember, 488 !rangeColumnPredicate.getLowerInclusive(), 489 upperMember, 490 !rangeColumnPredicate.getUpperInclusive()); 491 // use the member as constraint, this will give us some 492 // optimization potential 493 cacheRegion.addPredicate(predicate2); 494 return; 495 } 496 497 // Unknown type of constraint. 498 throw new UnsupportedOperationException(); 499 } 500 } 501 502 /** 503 * Level reader for a parent-child level which has a closed peer level. 504 */ 505 // final for performance 506 static final class ParentChildLevelReaderImpl implements LevelReader { 507 private final RegularLevelReader regularLevelReader; 508 private final RolapCubeLevel closedPeerCubeLevel; 509 private final RolapLevel closedPeerLevel; 510 private final RolapMember wrappedAllMember; 511 private final RolapCubeMember allMember; 512 513 ParentChildLevelReaderImpl(RolapCubeLevel cubeLevel) 514 { 515 this.regularLevelReader = new RegularLevelReader(cubeLevel); 516 517 // inline a bunch of fields for performance 518 this.closedPeerCubeLevel = cubeLevel.closedPeerCubeLevel; 519 this.closedPeerLevel = cubeLevel.rolapLevel.getClosedPeer(); 520 this.wrappedAllMember = (RolapMember) 521 closedPeerLevel.getHierarchy().getDefaultMember(); 522 this.allMember = 523 closedPeerCubeLevel.getHierarchy().getDefaultMember(); 524 assert allMember.isAll(); 525 } 526 527 public boolean constrainRequest( 528 RolapCubeMember member, 529 RolapCube baseCube, 530 CellRequest request) 531 { 532 // Replace a parent/child level by its closed equivalent, when 533 // available; this is always valid, and improves performance by 534 // enabling the database to compute aggregates. 535 if (member.getDataMember() == null) { 536 // Member has no data member because it IS the data 537 // member of a parent-child hierarchy member. Leave 538 // it be. We don't want to aggregate. 539 return regularLevelReader.constrainRequest( 540 member, baseCube, request); 541 } else if (request.drillThrough) { 542 return regularLevelReader.constrainRequest( 543 member.getDataMember(), baseCube, request); 544 } else { 545 // isn't creating a member on the fly a bad idea? 546 RolapMember wrappedMember = 547 new RolapMemberBase( 548 wrappedAllMember, closedPeerLevel, member.getKey()); 549 member = 550 new RolapCubeMember( 551 allMember, 552 wrappedMember, closedPeerCubeLevel); 553 554 return closedPeerCubeLevel.getLevelReader().constrainRequest( 555 member, baseCube, request); 556 } 557 } 558 559 public void constrainRegion( 560 StarColumnPredicate predicate, 561 RolapCube baseCube, 562 RolapCacheRegion cacheRegion) 563 { 564 throw new UnsupportedOperationException(); 565 } 566 } 567 568 /** 569 * Level reader for the level which contains the 'all' member. 570 */ 571 static final class AllLevelReaderImpl implements LevelReader { 572 public boolean constrainRequest( 573 RolapCubeMember member, 574 RolapCube baseCube, 575 CellRequest request) 576 { 577 // We don't need to apply any constraints. 578 return false; 579 } 580 581 public void constrainRegion( 582 StarColumnPredicate predicate, 583 RolapCube baseCube, 584 RolapCacheRegion cacheRegion) 585 { 586 // We don't need to apply any constraints. 587 } 588 } 589 590 /** 591 * Level reader for the level which contains the null member. 592 */ 593 static final class NullLevelReader implements LevelReader { 594 public boolean constrainRequest( 595 RolapCubeMember member, 596 RolapCube baseCube, 597 CellRequest request) 598 { 599 return true; 600 } 601 602 public void constrainRegion( 603 StarColumnPredicate predicate, 604 RolapCube baseCube, 605 RolapCacheRegion cacheRegion) 606 { 607 } 608 } 609 610} 611 612// End RolapCubeLevel.java