1 10 11 package mondrian.rolap.aggmatcher; 12 13 import mondrian.olap.Hierarchy; 14 import mondrian.olap.Dimension; 15 import mondrian.olap.MondrianDef; 16 import mondrian.resource.MondrianResource; 17 import mondrian.recorder.MessageRecorder; 18 import mondrian.rolap.RolapStar; 19 import mondrian.rolap.RolapLevel; 20 import mondrian.rolap.RolapSchema; 21 import mondrian.rolap.RolapCube; 22 import mondrian.rolap.RolapAggregator; 23 import mondrian.rolap.HierarchyUsage; 24 import mondrian.rolap.sql.SqlQuery; 25 26 import java.util.*; 27 28 import org.apache.log4j.Logger; 29 30 48 abstract class Recognizer { 49 50 private static final MondrianResource mres = MondrianResource.instance(); 51 private static final Logger LOGGER = Logger.getLogger(Recognizer.class); 52 55 public interface Matcher { 56 57 60 boolean matches(String name); 61 } 62 63 protected final RolapStar star; 64 protected final JdbcSchema.Table dbFactTable; 65 protected final JdbcSchema.Table aggTable; 66 protected final MessageRecorder msgRecorder; 67 protected boolean returnValue; 68 69 protected Recognizer(final RolapStar star, 70 final JdbcSchema.Table dbFactTable, 71 final JdbcSchema.Table aggTable, 72 final MessageRecorder msgRecorder) { 73 this.star = star; 74 this.dbFactTable = dbFactTable; 75 this.aggTable = aggTable; 76 this.msgRecorder = msgRecorder; 77 78 returnValue = true; 79 } 80 81 100 public boolean check() { 101 checkIgnores(); 102 checkFactCount(); 103 104 int nosMeasures = checkMeasures(); 106 checkNosMeasures(nosMeasures); 108 generateImpliedMeasures(); 109 110 List<JdbcSchema.Table.Column.Usage> notSeenForeignKeys = checkForeignKeys(); 112 checkLevels(notSeenForeignKeys); 114 115 if (returnValue) { 116 checkUnusedColumns(); 118 } 119 120 return returnValue; 121 } 122 123 126 protected abstract Matcher getIgnoreMatcher(); 127 128 131 protected void checkIgnores() { 132 Matcher ignoreMatcher = getIgnoreMatcher(); 133 134 for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) { 135 if (ignoreMatcher.matches(aggColumn.getName())) { 136 makeIgnore(aggColumn); 137 } 138 } 139 } 140 141 146 protected void makeIgnore(final JdbcSchema.Table.Column aggColumn) { 147 JdbcSchema.Table.Column.Usage usage = 148 aggColumn.newUsage(JdbcSchema.UsageType.IGNORE); 149 usage.setSymbolicName("Ignore"); 150 } 151 152 153 154 157 protected abstract Matcher getFactCountMatcher(); 158 159 163 protected void checkFactCount() { 164 msgRecorder.pushContextName("Recognizer.checkFactCount"); 165 166 try { 167 168 Matcher factCountMatcher = getFactCountMatcher(); 169 170 int nosOfFactCounts = 0; 171 for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) { 172 if (aggColumn.hasUsage(JdbcSchema.UsageType.IGNORE)) { 174 continue; 175 } 176 if (factCountMatcher.matches(aggColumn.getName())) { 177 if (aggColumn.getDatatype().isNumeric()) { 178 makeFactCount(aggColumn); 179 nosOfFactCounts++; 180 } else { 181 String msg = mres.NonNumericFactCountColumn.str( 182 aggTable.getName(), 183 dbFactTable.getName(), 184 aggColumn.getName(), 185 aggColumn.getTypeName()); 186 msgRecorder.reportError(msg); 187 188 returnValue = false; 189 } 190 } 191 192 } 193 if (nosOfFactCounts == 0) { 194 String msg = mres.NoFactCountColumns.str( 195 aggTable.getName(), 196 dbFactTable.getName()); 197 msgRecorder.reportError(msg); 198 199 returnValue = false; 200 201 } else if (nosOfFactCounts > 1) { 202 String msg = mres.TooManyFactCountColumns.str( 203 aggTable.getName(), 204 dbFactTable.getName(), 205 nosOfFactCounts); 206 msgRecorder.reportError(msg); 207 208 returnValue = false; 209 } 210 211 } finally { 212 msgRecorder.popContextName(); 213 } 214 } 215 216 217 220 protected abstract int checkMeasures(); 221 222 227 protected void makeFactCount(final JdbcSchema.Table.Column aggColumn) { 228 JdbcSchema.Table.Column.Usage usage = 229 aggColumn.newUsage(JdbcSchema.UsageType.FACT_COUNT); 230 usage.setSymbolicName("Fact Count"); 231 } 232 233 234 239 protected void checkNosMeasures(int nosMeasures) { 240 msgRecorder.pushContextName("Recognizer.checkNosMeasures"); 241 242 try { 243 if (nosMeasures == 0) { 244 String msg = mres.NoMeasureColumns.str( 245 aggTable.getName(), 246 dbFactTable.getName() 247 ); 248 msgRecorder.reportError(msg); 249 250 returnValue = false; 251 } 252 253 } finally { 254 msgRecorder.popContextName(); 255 } 256 } 257 258 272 protected void generateImpliedMeasures() { 273 for (JdbcSchema.Table.Column factColumn : aggTable.getColumns()) { 274 JdbcSchema.Table.Column.Usage sumFactUsage = null; 275 JdbcSchema.Table.Column.Usage avgFactUsage = null; 276 277 for (Iterator<JdbcSchema.Table.Column.Usage> mit = 278 factColumn.getUsages(JdbcSchema.UsageType.MEASURE); 279 mit.hasNext(); ) { 280 JdbcSchema.Table.Column.Usage factUsage = mit.next(); 281 if (factUsage.getAggregator() == RolapAggregator.Avg) { 282 avgFactUsage = factUsage; 283 } else if (factUsage.getAggregator() == RolapAggregator.Sum) { 284 sumFactUsage = factUsage; 285 } 286 } 287 288 if (avgFactUsage != null && sumFactUsage != null) { 289 JdbcSchema.Table.Column.Usage sumAggUsage = null; 290 JdbcSchema.Table.Column.Usage avgAggUsage = null; 291 int seenCount = 0; 292 for (Iterator<JdbcSchema.Table.Column.Usage> mit = 293 aggTable.getColumnUsages(JdbcSchema.UsageType.MEASURE); 294 mit.hasNext(); ) { 295 296 JdbcSchema.Table.Column.Usage aggUsage = mit.next(); 297 if (aggUsage.rMeasure == avgFactUsage.rMeasure) { 298 avgAggUsage = aggUsage; 299 seenCount++; 300 } else if (aggUsage.rMeasure == sumFactUsage.rMeasure) { 301 sumAggUsage = aggUsage; 302 seenCount++; 303 } 304 } 305 if (seenCount == 1) { 306 if (avgAggUsage != null) { 307 makeMeasure(sumFactUsage, avgAggUsage); 308 } 309 if (sumAggUsage != null) { 310 makeMeasure(avgFactUsage, sumAggUsage); 311 } 312 } 313 } 314 } 315 } 316 317 325 protected void makeMeasure(final JdbcSchema.Table.Column.Usage factUsage, 326 final JdbcSchema.Table.Column.Usage aggSiblingUsage) { 327 JdbcSchema.Table.Column aggColumn = aggSiblingUsage.getColumn(); 328 329 JdbcSchema.Table.Column.Usage aggUsage = 330 aggColumn.newUsage(JdbcSchema.UsageType.MEASURE); 331 332 aggUsage.setSymbolicName(factUsage.getSymbolicName()); 333 RolapAggregator ra = convertAggregator( 334 aggUsage, 335 factUsage.getAggregator(), 336 aggSiblingUsage.getAggregator()); 337 aggUsage.setAggregator(ra); 338 aggUsage.rMeasure = factUsage.rMeasure; 339 } 340 341 348 protected void makeMeasure(final JdbcSchema.Table.Column.Usage factUsage, 349 final JdbcSchema.Table.Column aggColumn) { 350 JdbcSchema.Table.Column.Usage aggUsage = 351 aggColumn.newUsage(JdbcSchema.UsageType.MEASURE); 352 353 aggUsage.setSymbolicName(factUsage.getSymbolicName()); 354 RolapAggregator ra = 355 convertAggregator(aggUsage, factUsage.getAggregator()); 356 aggUsage.setAggregator(ra); 357 aggUsage.rMeasure = factUsage.rMeasure; 358 } 359 360 365 protected abstract int matchForeignKey(JdbcSchema.Table.Column.Usage factUsage); 366 367 380 protected List<JdbcSchema.Table.Column.Usage> checkForeignKeys() { 381 msgRecorder.pushContextName("Recognizer.checkForeignKeys"); 382 383 try { 384 385 List<JdbcSchema.Table.Column.Usage> notSeenForeignKeys = 386 Collections.emptyList(); 387 388 for (Iterator<JdbcSchema.Table.Column.Usage> it = 389 dbFactTable.getColumnUsages(JdbcSchema.UsageType.FOREIGN_KEY); 390 it.hasNext(); ) { 391 392 JdbcSchema.Table.Column.Usage factUsage = it.next(); 393 394 int matchCount = matchForeignKey(factUsage); 395 396 if (matchCount > 1) { 397 String msg = mres.TooManyMatchingForeignKeyColumns.str( 398 aggTable.getName(), 399 dbFactTable.getName(), 400 matchCount, 401 factUsage.getColumn().getName() 402 ); 403 msgRecorder.reportError(msg); 404 405 returnValue = false; 406 407 } else if (matchCount == 0) { 408 if (notSeenForeignKeys.isEmpty()) { 409 notSeenForeignKeys = new ArrayList<JdbcSchema.Table.Column.Usage>(); 410 } 411 notSeenForeignKeys.add(factUsage); 412 } 413 } 414 return notSeenForeignKeys; 415 416 } finally { 417 msgRecorder.popContextName(); 418 } 419 } 420 421 453 protected void checkLevels(List<JdbcSchema.Table.Column.Usage> notSeenForeignKeys) { 454 455 461 for (RolapCube cube : findCubes()) { 463 Dimension[] dims = cube.getDimensions(); 464 for (int j = 1; j < dims.length; j++) { 466 Dimension dim = dims[j]; 467 String dimName = dim.getName(); 477 478 Hierarchy[] hierarchies = dim.getHierarchies(); 479 for (Hierarchy hierarchy : hierarchies) { 480 HierarchyUsage[] hierarchyUsages = 481 cube.getUsages(hierarchy); 482 for (HierarchyUsage hierarchyUsage : hierarchyUsages) { 483 String foreignKey = hierarchyUsage.getForeignKey(); 487 boolean b = inNotSeenForeignKeys( 488 foreignKey, 489 notSeenForeignKeys); 490 if (!b) { 491 continue; 493 } 494 495 496 RolapLevel[] levels = 497 (RolapLevel[]) hierarchy.getLevels(); 498 mid_level: 505 for (RolapLevel level : levels) { 506 if (level.isAll()) { 507 continue mid_level; 508 } 509 if (matchLevel(hierarchy, hierarchyUsage, level)) { 510 511 continue mid_level; 512 513 } else { 514 break mid_level; 517 } 518 } 519 } 520 } 521 } 522 } 523 } 524 525 529 boolean inNotSeenForeignKeys( 530 String foreignKey, 531 List<JdbcSchema.Table.Column.Usage> notSeenForeignKeys) 532 { 533 for (JdbcSchema.Table.Column.Usage usage : notSeenForeignKeys) { 534 if (usage.getColumn().getName().equals(foreignKey)) { 535 return true; 536 } 537 } 538 return false; 539 } 540 541 546 private void printNotSeenForeignKeys(List notSeenForeignKeys) { 547 LOGGER.debug("Recognizer.printNotSeenForeignKeys: " 548 + aggTable.getName()); 549 for (Iterator it = notSeenForeignKeys.iterator(); it.hasNext(); ) { 550 JdbcSchema.Table.Column.Usage usage = 551 (JdbcSchema.Table.Column.Usage) it.next(); 552 LOGGER.debug(" " + usage.getColumn().getName()); 553 } 554 } 555 556 565 protected void makeForeignKey(final JdbcSchema.Table.Column.Usage factUsage, 566 final JdbcSchema.Table.Column aggColumn, 567 final String rightJoinConditionColumnName) { 568 JdbcSchema.Table.Column.Usage aggUsage = 569 aggColumn.newUsage(JdbcSchema.UsageType.FOREIGN_KEY); 570 aggUsage.setSymbolicName("FOREIGN_KEY"); 571 aggUsage.rTable = factUsage.rTable; 575 aggUsage.rightJoinConditionColumnName = rightJoinConditionColumnName; 576 577 aggUsage.rColumn = factUsage.rColumn; 578 } 579 580 584 protected abstract boolean matchLevel( 585 final Hierarchy hierarchy, 586 final HierarchyUsage hierarchyUsage, 587 final RolapLevel level); 588 589 598 protected void makeLevel( 599 final JdbcSchema.Table.Column aggColumn, 600 final Hierarchy hierarchy, 601 final HierarchyUsage hierarchyUsage, 602 final String factColumnName, 603 final String levelColumnName, 604 final String symbolicName) { 605 606 msgRecorder.pushContextName("Recognizer.makeLevel"); 607 608 try { 609 610 if (aggColumn.hasUsage(JdbcSchema.UsageType.LEVEL)) { 611 for (Iterator<JdbcSchema.Table.Column.Usage> uit = 615 aggColumn.getUsages(JdbcSchema.UsageType.LEVEL); 616 uit.hasNext(); ) { 617 JdbcSchema.Table.Column.Usage aggUsage = uit.next(); 618 619 MondrianDef.Relation rel = hierarchyUsage.getJoinTable(); 620 String cName = levelColumnName; 621 622 if (! aggUsage.relation.equals(rel) || 623 ! aggColumn.column.name.equals(cName)) { 624 625 String msg = mres.DoubleMatchForLevel.str( 627 aggTable.getName(), 628 dbFactTable.getName(), 629 aggColumn.getName(), 630 aggUsage.relation.toString(), 631 aggColumn.column.name, 632 rel.toString(), 633 cName); 634 msgRecorder.reportError(msg); 635 636 returnValue = false; 637 638 msgRecorder.throwRTException(); 639 } 640 } 641 } else { 642 JdbcSchema.Table.Column.Usage aggUsage = 643 aggColumn.newUsage(JdbcSchema.UsageType.LEVEL); 644 aggUsage.relation = hierarchyUsage.getJoinTable(); 647 aggUsage.joinExp = hierarchyUsage.getJoinExp(); 648 aggUsage.levelColumnName = levelColumnName; 649 650 aggUsage.setSymbolicName(symbolicName); 651 652 String tableAlias; 653 if (aggUsage.joinExp instanceof MondrianDef.Column) { 654 MondrianDef.Column mcolumn = 655 (MondrianDef.Column) aggUsage.joinExp; 656 tableAlias = mcolumn.table; 657 } else { 658 tableAlias = aggUsage.relation.getAlias(); 659 } 660 661 662 RolapStar.Table factTable = star.getFactTable(); 663 RolapStar.Table descTable = factTable.findDescendant(tableAlias); 664 665 if (descTable == null) { 666 StringBuilder buf = new StringBuilder (256); 668 buf.append("descendant table is null for factTable="); 669 buf.append(factTable.getAlias()); 670 buf.append(", tableAlias="); 671 buf.append(tableAlias); 672 msgRecorder.reportError(buf.toString()); 673 674 returnValue = false; 675 676 msgRecorder.throwRTException(); 677 } 678 679 RolapStar.Column rc = descTable.lookupColumn(factColumnName); 680 681 if (rc == null) { 682 rc = lookupInChildren(descTable, factColumnName); 683 684 } 685 if (rc == null) { 686 StringBuilder buf = new StringBuilder (256); 687 buf.append("Rolap.Column not found (null) for tableAlias="); 688 buf.append(tableAlias); 689 buf.append(", factColumnName="); 690 buf.append(factColumnName); 691 buf.append(", levelColumnName="); 692 buf.append(levelColumnName); 693 buf.append(", symbolicName="); 694 buf.append(symbolicName); 695 msgRecorder.reportError(buf.toString()); 696 697 returnValue = false; 698 699 msgRecorder.throwRTException(); 700 } else { 701 aggUsage.rColumn = rc; 702 } 703 } 704 } finally { 705 msgRecorder.popContextName(); 706 } 707 } 708 709 protected RolapStar.Column lookupInChildren( 710 final RolapStar.Table table, 711 final String factColumnName) 712 { 713 for (RolapStar.Table child : table.getChildren()) { 718 RolapStar.Column rc = child.lookupColumn(factColumnName); 719 if (rc != null) { 720 return rc; 721 } else { 722 rc = lookupInChildren(child, factColumnName); 723 if (rc != null) { 724 return rc; 725 } 726 } 727 } 728 return null; 729 } 730 731 732 735 736 741 protected void checkUnusedColumns() { 742 msgRecorder.pushContextName("Recognizer.checkUnusedColumns"); 743 for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) { 744 if (! aggColumn.hasUsage()) { 745 746 String msg = mres.AggUnknownColumn.str( 747 aggTable.getName(), 748 dbFactTable.getName(), 749 aggColumn.getName() 750 ); 751 761 msgRecorder.reportWarning(msg); 762 } 763 } 764 msgRecorder.popContextName(); 765 } 766 767 781 protected RolapAggregator convertAggregator( 782 final JdbcSchema.Table.Column.Usage aggUsage, 783 final RolapAggregator factAgg) { 784 785 if (factAgg == RolapAggregator.Avg) { 788 String columnExpr = getFactCountExpr(aggUsage); 789 return new RolapAggregator.AvgFromSum(columnExpr); 790 } else if (factAgg == RolapAggregator.DistinctCount) { 791 return RolapAggregator.DistinctCount; 793 } else { 794 return factAgg; 795 } 796 } 797 798 821 protected RolapAggregator convertAggregator( 822 final JdbcSchema.Table.Column.Usage aggUsage, 823 final RolapAggregator factAgg, 824 final RolapAggregator siblingAgg) { 825 826 msgRecorder.pushContextName("Recognizer.convertAggregator"); 827 RolapAggregator rollupAgg = null; 828 829 String columnExpr = getFactCountExpr(aggUsage); 830 if (factAgg == RolapAggregator.Avg) { 831 if (siblingAgg == RolapAggregator.Avg) { 832 rollupAgg = new RolapAggregator.AvgFromAvg(columnExpr); 833 } else if (siblingAgg == RolapAggregator.Sum) { 834 rollupAgg = new RolapAggregator.AvgFromSum(columnExpr); 835 } 836 } else if (factAgg == RolapAggregator.Sum) { 837 if (siblingAgg == RolapAggregator.Avg) { 838 rollupAgg = new RolapAggregator.SumFromAvg(columnExpr); 839 } else if (siblingAgg instanceof RolapAggregator.AvgFromAvg) { 840 rollupAgg = new RolapAggregator.SumFromAvg(columnExpr); 842 } 843 } 844 845 if (rollupAgg == null) { 846 rollupAgg = (RolapAggregator) factAgg.getRollup(); 847 } 848 849 if (rollupAgg == null) { 850 String msg = mres.NoAggregatorFound.str( 851 aggUsage.getSymbolicName(), 852 factAgg.getName(), 853 siblingAgg.getName()); 854 msgRecorder.reportError(msg); 855 } 856 857 msgRecorder.popContextName(); 858 return rollupAgg; 859 } 860 861 868 private String getFactCountExpr(final JdbcSchema.Table.Column.Usage aggUsage) { 869 JdbcSchema.Table aggTable = aggUsage.getColumn().getTable(); 871 872 Iterator<JdbcSchema.Table.Column.Usage> it = 874 aggTable.getColumnUsages(JdbcSchema.UsageType.FACT_COUNT); 875 it.hasNext(); 876 JdbcSchema.Table.Column.Usage usage = it.next(); 877 878 String factCountColumnName = usage.getColumn().getName(); 880 String tableName = aggTable.getName(); 881 882 MondrianDef.Column column = 884 new MondrianDef.Column(tableName, factCountColumnName); 885 SqlQuery sqlQuery = star.getSqlQuery(); 886 return column.getExpression(sqlQuery); 887 } 888 889 892 protected List<RolapCube> findCubes() { 893 String name = dbFactTable.getName(); 894 895 List<RolapCube> list = new ArrayList<RolapCube>(); 896 RolapSchema schema = star.getSchema(); 897 for (RolapCube cube : schema.getCubeList()) { 898 if (cube.isVirtual()) { 899 continue; 900 } 901 RolapStar cubeStar = cube.getStar(); 902 String factTableName = cubeStar.getFactTable().getAlias(); 903 if (name.equals(factTableName)) { 904 list.add(cube); 905 } 906 } 907 return list; 908 } 909 910 919 protected String getColumnName(MondrianDef.Expression expr) { 920 msgRecorder.pushContextName("Recognizer.getColumnName"); 921 922 try { 923 if (expr instanceof MondrianDef.Column) { 924 MondrianDef.Column column = (MondrianDef.Column) expr; 925 return column.getColumnName(); 926 } else if (expr instanceof MondrianDef.KeyExpression) { 927 MondrianDef.KeyExpression key = (MondrianDef.KeyExpression) expr; 928 return key.toString(); 929 } 930 931 String msg = mres.NoColumnNameFromExpression.str( 932 expr.toString()); 933 msgRecorder.reportError(msg); 934 935 return null; 936 937 } finally { 938 msgRecorder.popContextName(); 939 } 940 } 941 } 942 943 | Popular Tags |