1 13 14 package mondrian.rolap; 15 import mondrian.olap.*; 16 import mondrian.rolap.agg.*; 17 import mondrian.rolap.aggmatcher.AggStar; 18 import mondrian.rolap.sql.SqlQuery; 19 import mondrian.spi.DataSourceChangeListener; 20 21 import org.apache.log4j.Logger; 22 import org.eigenbase.util.property.Property; 23 import org.eigenbase.util.property.TriggerBase; 24 25 import javax.sql.DataSource ; 26 import java.io.PrintWriter ; 27 import java.io.StringWriter ; 28 import java.sql.Connection ; 29 import java.sql.*; 30 import java.util.*; 31 32 43 public class RolapStar { 44 private static final Logger LOGGER = Logger.getLogger(RolapStar.class); 45 46 52 private static boolean disableCaching = 53 MondrianProperties.instance().DisableCaching.get(); 54 55 static { 56 MondrianProperties.instance().DisableCaching.addTrigger( 60 new TriggerBase(true) { 61 public void execute(Property property, String value) { 62 disableCaching = property.booleanValue(); 63 if (disableCaching) { 65 RolapSchema.flushAllRolapStarCachedAggregations(); 66 } 67 } 68 } 69 ); 70 } 71 72 73 private final RolapSchema schema; 74 75 private DataSource dataSource; 77 78 private final Table factTable; 79 80 86 private final Map<RolapCube, Map<RolapLevel, Column>> 87 cubeToLevelToColumnMapMap; 88 89 90 private Map<BitKey,Aggregation> aggregations; 91 92 93 private final ThreadLocal <Map<BitKey, Aggregation>> 94 localAggregations = 95 new ThreadLocal <Map<BitKey, Aggregation>>() { 96 97 protected Map<BitKey, Aggregation> initialValue() { 98 return new HashMap<BitKey, Aggregation>(); 99 } 100 }; 101 102 107 private final Map<BitKey, Aggregation> pendingAggregations; 108 109 112 private final List<BitKey> aggregationRequests; 113 114 117 private final ThreadLocal <List<BitKey>> localAggregationRequests = 118 new ThreadLocal <List<BitKey>>() { 119 protected List<BitKey> initialValue() { 120 return new ArrayList<BitKey>(); 121 } 122 }; 123 124 127 private int columnCount; 128 129 private final SqlQuery.Dialect sqlQueryDialect; 130 131 135 private boolean cacheAggregations; 136 137 141 private List<AggStar> aggStars; 142 143 private DataSourceChangeListener changeListener; 144 145 150 RolapStar( 151 final RolapSchema schema, 152 final DataSource dataSource, 153 final MondrianDef.Relation fact) { 154 this.cacheAggregations = true; 155 this.schema = schema; 156 this.dataSource = dataSource; 157 this.factTable = new RolapStar.Table(this, fact, null, null); 158 159 this.cubeToLevelToColumnMapMap = 160 new HashMap<RolapCube, Map<RolapLevel, Column>>(); 161 this.aggregations = new HashMap<BitKey, Aggregation>(); 162 this.pendingAggregations = new HashMap<BitKey, Aggregation>(); 163 164 this.aggregationRequests = new ArrayList<BitKey>(); 165 166 clearAggStarList(); 167 168 sqlQueryDialect = schema.getDialect(); 169 170 changeListener = schema.getDataSourceChangeListener(); 171 } 172 173 177 public int getColumnCount() { 178 return columnCount; 179 } 180 181 185 private int nextColumnCount() { 186 return columnCount++; 187 } 188 189 193 private int decrementColumnCount() { 194 return columnCount--; 195 } 196 197 202 public void prepareToLoadAggregates() { 203 aggStars = Collections.emptyList(); 204 } 205 206 213 public void addAggStar(AggStar aggStar) { 214 if (aggStars == Collections.EMPTY_LIST) { 215 aggStars = new LinkedList<AggStar>(); 217 } 218 219 int size = aggStar.getSize(); 221 ListIterator<AggStar> lit = aggStars.listIterator(); 222 while (lit.hasNext()) { 223 AggStar as = lit.next(); 224 if (as.getSize() >= size) { 225 lit.previous(); 226 lit.add(aggStar); 227 return; 228 } 229 } 230 231 aggStars.add(aggStar); 233 } 234 235 238 void clearAggStarList() { 239 aggStars = Collections.emptyList(); 240 } 241 242 246 public void reOrderAggStarList() { 247 List<AggStar> l = aggStars; 249 clearAggStarList(); 250 251 for (AggStar aggStar : l) { 252 addAggStar(aggStar); 253 } 254 } 255 256 260 public List<AggStar> getAggStars() { 261 return aggStars; 262 } 263 264 269 public Table getFactTable() { 270 return factTable; 271 } 272 273 277 public SqlQuery getSqlQuery() { 278 return new SqlQuery(getSqlQueryDialect()); 279 } 280 281 284 public SqlQuery.Dialect getSqlQueryDialect() { 285 return sqlQueryDialect; 286 } 287 288 295 Map<RolapLevel, Column> getLevelToColumnMap(RolapCube cube) { 296 Map<RolapLevel, Column> levelToColumnMap = 297 this.cubeToLevelToColumnMapMap.get(cube); 298 if (levelToColumnMap == null) { 299 levelToColumnMap = new HashMap<RolapLevel, Column>(); 300 this.cubeToLevelToColumnMapMap.put(cube, levelToColumnMap); 301 } 302 return levelToColumnMap; 303 } 304 305 306 317 void setCacheAggregations(boolean cacheAggregations) { 318 this.cacheAggregations = cacheAggregations; 320 clearCachedAggregations(false); 321 } 322 323 328 boolean isCacheAggregations() { 329 return this.cacheAggregations; 330 } 331 332 339 void clearCachedAggregations(boolean forced) { 340 if (forced || !cacheAggregations || RolapStar.disableCaching) { 341 if (LOGGER.isDebugEnabled()) { 342 StringBuilder buf = new StringBuilder (100); 343 buf.append("RolapStar.clearCachedAggregations: schema="); 344 buf.append(schema.getName()); 345 buf.append(", star="); 346 buf.append(getFactTable().getAlias()); 347 LOGGER.debug(buf.toString()); 348 } 349 350 if (forced) { 351 synchronized (aggregations) { 352 aggregations.clear(); 353 } 354 localAggregations.get().clear(); 355 } else { 356 localAggregations.get().clear(); 358 } 359 } 360 361 } 362 363 371 public Aggregation lookupOrCreateAggregation(final BitKey bitKey) { 372 373 Aggregation aggregation = lookupAggregation(bitKey); 374 375 if (aggregation == null) { 376 aggregation = new Aggregation(this, bitKey); 377 378 this.localAggregations.get().put(bitKey, aggregation); 379 380 if ((this.cacheAggregations) && (!RolapStar.disableCaching)) { 383 if (changeListener != null) { 384 Util.discard(changeListener.isAggregationChanged(aggregation)); 385 } 386 } 387 } 388 return aggregation; 389 } 390 391 392 400 public Aggregation lookupAggregation(BitKey bitKey) { 401 Aggregation aggregation = localAggregations.get().get(bitKey); 403 if (aggregation != null) { 404 return aggregation; 405 } 406 407 if (cacheAggregations && !RolapStar.disableCaching) { 408 synchronized (aggregations) { 410 aggregation = aggregations.get(bitKey); 411 if (aggregation != null) { 412 recordAggregationRequest(bitKey); 414 } 415 } 416 } 417 418 return aggregation; 419 } 420 421 432 public void checkAggregateModifications() { 433 434 clearAggregationRequests(); 438 439 if (changeListener != null) { 440 if (cacheAggregations && !RolapStar.disableCaching) { 441 synchronized (aggregations) { 442 for (Map.Entry<BitKey, Aggregation> e : 443 aggregations.entrySet()) 444 { 445 BitKey bitKey = e.getKey(); 446 447 Aggregation aggregation = e.getValue(); 448 if (changeListener.isAggregationChanged(aggregation)) { 449 aggregation = new Aggregation(this, bitKey); 455 456 localAggregations.get().put(bitKey, aggregation); 457 } 458 } 459 } 460 } 461 } 462 } 463 464 471 public void pushAggregateModificationsToGlobalCache() { 472 synchronized (this) { 475 if (cacheAggregations && !RolapStar.disableCaching) { 476 477 Iterator<Map.Entry<BitKey, Aggregation>> 480 it = pendingAggregations.entrySet().iterator(); 481 while (it.hasNext()) { 482 Map.Entry<BitKey, Aggregation> e = it.next(); 483 BitKey bitKey = e.getKey(); 484 Aggregation aggregation = e.getValue(); 485 if (!isAggregationRequested(bitKey)) { 490 pushAggregateModification( 491 bitKey, aggregation,aggregations); 492 it.remove(); 493 } 494 } 495 it = localAggregations.get().entrySet().iterator(); 497 while (it.hasNext()) { 498 Map.Entry<BitKey, Aggregation> e = it.next(); 499 BitKey bitKey = e.getKey(); 500 Aggregation aggregation = e.getValue(); 501 if (!isAggregationRequested(bitKey)) { 506 pushAggregateModification( 507 bitKey, aggregation, aggregations); 508 } else { 509 pushAggregateModification( 510 bitKey, aggregation, pendingAggregations); 511 } 512 } 513 localAggregations.get().clear(); 514 } 515 clearAggregationRequests(); 517 } 518 } 519 520 524 private void pushAggregateModification( 525 BitKey localBitKey, 526 Aggregation localAggregation, 527 Map<BitKey,Aggregation> destAggregations) 528 { 529 if (cacheAggregations && !RolapStar.disableCaching) { 530 synchronized (destAggregations) { 531 532 boolean found = false; 533 Iterator<Map.Entry<BitKey, Aggregation>> 534 it = destAggregations.entrySet().iterator(); 535 while (it.hasNext()) { 536 Map.Entry<BitKey, Aggregation> e = 537 it.next(); 538 BitKey bitKey = e.getKey(); 539 Aggregation aggregation = e.getValue(); 540 541 if (localBitKey.equals(bitKey)) { 542 543 if (localAggregation.getCreationTimestamp().after( 544 aggregation.getCreationTimestamp())) { 545 it.remove(); 546 } else { 547 found = true; 549 } 550 break; 551 } 552 } 553 if (!found) { 554 destAggregations.put(localBitKey, localAggregation); 555 } 556 } 557 } 558 } 559 560 563 private void recordAggregationRequest(BitKey bitKey) { 564 if (!localAggregationRequests.get().contains(bitKey)) { 565 synchronized(aggregationRequests) { 566 aggregationRequests.add(bitKey); 567 } 568 localAggregationRequests.get().add(bitKey); 570 } 571 } 572 573 576 private boolean isAggregationRequested(BitKey bitKey) { 577 synchronized (aggregationRequests) { 578 return aggregationRequests.contains(bitKey); 579 } 580 } 581 582 585 private void clearAggregationRequests() { 586 synchronized (aggregationRequests) { 587 if (localAggregationRequests.get().isEmpty()) { 588 return; 589 } 590 Set<BitKey> localAggregationRequestSet = 594 new HashSet<BitKey>(localAggregationRequests.get()); 595 Iterator<BitKey> iter = aggregationRequests.iterator(); 596 while (iter.hasNext()) { 597 BitKey bitKey = iter.next(); 598 if (localAggregationRequestSet.contains(bitKey)) { 599 iter.remove(); 600 localAggregationRequestSet.remove(bitKey); 603 if (localAggregationRequestSet.isEmpty()) { 604 break; 606 } 607 } 608 } 609 localAggregationRequests.get().clear(); 610 } 611 } 612 613 614 public void setDataSource(DataSource dataSource) { 615 this.dataSource = dataSource; 616 } 617 618 623 public DataSource getDataSource() { 624 return dataSource; 625 } 626 627 630 public static Measure getStarMeasure(Member member) { 631 return (Measure) ((RolapStoredMeasure) member).getStarMeasure(); 632 } 633 634 637 public Column[] lookupColumns(String tableAlias, String columnName) { 638 final Table table = factTable.findDescendant(tableAlias); 639 return (table == null) ? null : table.lookupColumns(columnName); 640 } 641 642 645 public Column lookupColumn(String tableAlias, String columnName) { 646 final Table table = factTable.findDescendant(tableAlias); 647 return (table == null) ? null : table.lookupColumn(columnName); 648 } 649 650 653 public List<String > getAliasList() { 654 List<String > aliasList = new ArrayList<String >(); 655 if (factTable != null) { 656 collectAliases(aliasList, factTable); 657 } 658 return aliasList; 659 } 660 661 664 private static void collectAliases(List<String > aliasList, Table table) { 665 aliasList.add(table.getAlias()); 666 for (Table child : table.children) { 667 collectAliases(aliasList, child); 668 } 669 } 670 671 676 public static void collectColumns( 677 Collection<Column> columnList, 678 Table table, 679 MondrianDef.Column joinColumn) 680 { 681 if (joinColumn == null) { 682 columnList.addAll(table.columnList); 683 } 684 for (Table child : table.children) { 685 if (joinColumn == null || 686 child.getJoinCondition().left.equals(joinColumn)) { 687 collectColumns(columnList, child, null); 688 } 689 } 690 } 691 692 697 Object getCell(CellRequest request) { 698 return getCell(request, dataSource); 699 } 700 701 private Object getCell(CellRequest request, DataSource dataSource) { 702 Measure measure = request.getMeasure(); 703 Column[] columns = request.getConstrainedColumns(); 704 Object [] values = request.getSingleValues(); 705 Util.assertTrue(columns.length == values.length); 706 SqlQuery sqlQuery = getSqlQuery(); 707 Util.assertTrue(measure.getTable() == factTable); 709 factTable.addToFrom(sqlQuery, true, true); 710 sqlQuery.addSelect( 711 measure.aggregator.getExpression( 712 measure.generateExprString(sqlQuery))); 713 for (int i = 0; i < columns.length; i++) { 715 Object value = values[i]; 716 if (value == null) { 717 continue; } 719 Column column = columns[i]; 720 Table table = column.getTable(); 721 if (table.isFunky()) { 722 continue; 724 } 725 table.addToFrom(sqlQuery, true, true); 726 } 727 String sql = sqlQuery.toString(); 728 final SqlStatement stmt = 729 RolapUtil.executeQuery( 730 dataSource, sql, "RolapStar.getCell", 731 "while computing single cell"); 732 try { 733 ResultSet resultSet = stmt.getResultSet(); 734 Object o = null; 735 if (resultSet.next()) { 736 o = resultSet.getObject(1); 737 ++stmt.rowCount; 738 } 739 if (o == null) { 740 o = Util.nullValue; } 742 return o; 743 } catch (SQLException e) { 744 throw stmt.handle(e); 745 } finally { 746 stmt.close(); 747 } 748 } 749 750 private boolean containsColumn(String tableName, String columnName) { 751 Connection jdbcConnection; 752 try { 753 jdbcConnection = dataSource.getConnection(); 754 } catch (SQLException e1) { 755 throw Util.newInternal( 756 e1, "Error while creating connection from data source"); 757 } 758 try { 759 final DatabaseMetaData metaData = jdbcConnection.getMetaData(); 760 final ResultSet columns = 761 metaData.getColumns(null, null, tableName, columnName); 762 return columns.next(); 763 } catch (SQLException e) { 764 throw Util.newInternal("Error while retrieving metadata for table '" + 765 tableName + "', column '" + columnName + "'"); 766 } finally { 767 try { 768 jdbcConnection.close(); 769 } catch (SQLException e) { 770 } 772 } 773 } 774 775 public RolapSchema getSchema() { 776 return schema; 777 } 778 779 public String toString() { 780 StringWriter sw = new StringWriter (256); 781 PrintWriter pw = new PrintWriter (sw); 782 print(pw, "", true); 783 pw.flush(); 784 return sw.toString(); 785 } 786 787 794 public void print(PrintWriter pw, String prefix, boolean structure) { 795 if (structure) { 796 pw.print(prefix); 797 pw.println("RolapStar:"); 798 String subprefix = prefix + " "; 799 factTable.print(pw, subprefix); 800 801 for (AggStar aggStar : getAggStars()) { 802 aggStar.print(pw, subprefix); 803 } 804 } 805 806 List<Aggregation> aggregationList = 807 new ArrayList<Aggregation>(aggregations.values()); 808 Collections.sort( 809 aggregationList, 810 new Comparator<Aggregation>() { 811 public int compare(Aggregation o1, Aggregation o2) { 812 return o1.getConstrainedColumnsBitKey().compareTo( 813 o2.getConstrainedColumnsBitKey()); 814 } 815 } 816 ); 817 818 for (Aggregation aggregation : aggregationList) { 819 aggregation.print(pw); 820 } 821 } 822 823 829 public void flush( 830 CacheControl cacheControl, 831 CacheControl.CellRegion region) 832 { 833 final RolapCacheRegion cacheRegion = 835 RolapAggregationManager.makeCacheRegion(this, region); 836 for (Aggregation aggregation : aggregations.values()) { 837 aggregation.flush(cacheControl, cacheRegion); 838 } 839 } 840 841 842 847 public DataSourceChangeListener getChangeListener() { 848 return changeListener; 849 } 850 851 856 public void setChangeListener(DataSourceChangeListener changeListener) { 857 this.changeListener = changeListener; 858 } 859 860 862 865 public static class Column { 866 private final Table table; 867 private final MondrianDef.Expression expression; 868 private final SqlQuery.Datatype datatype; 869 private final String name; 870 874 private final Column parentColumn; 875 876 882 private final String usagePrefix; 883 888 private final Column nameColumn; 889 private boolean isNameColumn; 890 891 892 private final int bitPosition; 893 894 private int cardinality = -1; 895 896 private Column( 897 String name, 898 Table table, 899 MondrianDef.Expression expression, 900 SqlQuery.Datatype datatype) 901 { 902 this(name, table, expression, datatype, null, null, null); 903 } 904 905 private Column( 906 String name, 907 Table table, 908 MondrianDef.Expression expression, 909 SqlQuery.Datatype datatype, 910 Column nameColumn, 911 Column parentColumn, 912 String usagePrefix) 913 { 914 this.name = name; 915 this.table = table; 916 this.expression = expression; 917 this.datatype = datatype; 918 this.bitPosition = table.star.nextColumnCount(); 919 this.nameColumn = nameColumn; 920 this.parentColumn = parentColumn; 921 this.usagePrefix = usagePrefix; 922 if (nameColumn != null) { 923 nameColumn.isNameColumn = true; 924 } 925 } 926 927 public boolean equals(Object obj) { 928 if (! (obj instanceof RolapStar.Column)) { 929 return false; 930 } 931 RolapStar.Column other = (RolapStar.Column) obj; 932 return (other.table == this.table) && 934 other.expression.equals(this.expression) && 935 (other.datatype == this.datatype) && 936 other.name.equals(this.name); 937 } 938 939 public String getName() { 940 return name; 941 } 942 943 public int getBitPosition() { 944 return bitPosition; 945 } 946 947 public RolapStar getStar() { 948 return table.star; 949 } 950 951 public RolapStar.Table getTable() { 952 return table; 953 } 954 955 public SqlQuery getSqlQuery() { 956 return getTable().getStar().getSqlQuery(); 957 } 958 959 public RolapStar.Column getNameColumn() { 960 return nameColumn; 961 } 962 963 public RolapStar.Column getParentColumn() { 964 return parentColumn; 965 } 966 967 public String getUsagePrefix() { 968 return usagePrefix; 969 } 970 971 public boolean isNameColumn() { 972 return isNameColumn; 973 } 974 975 public MondrianDef.Expression getExpression() { 976 return expression; 977 } 978 979 983 public String generateExprString(SqlQuery query) { 984 return getExpression().getExpression(query); 985 } 986 987 public int getCardinality() { 988 if (cardinality == -1) { 989 cardinality = getCardinality(getStar().getDataSource()); 990 } 991 return cardinality; 992 } 993 994 private int getCardinality(DataSource dataSource) { 995 SqlQuery sqlQuery = getSqlQuery(); 996 if (sqlQuery.getDialect().allowsCountDistinct()) { 997 sqlQuery.addSelect("count(distinct " 999 + generateExprString(sqlQuery) + ")"); 1000 1001 table.addToFrom(sqlQuery, true, false); 1003 } else if (sqlQuery.getDialect().allowsFromQuery()) { 1004 SqlQuery inner = sqlQuery.cloneEmpty(); 1008 inner.setDistinct(true); 1009 inner.addSelect(generateExprString(inner)); 1010 boolean failIfExists = true, 1011 joinToParent = false; 1012 table.addToFrom(inner, failIfExists, joinToParent); 1013 sqlQuery.addSelect("count(*)"); 1014 sqlQuery.addFrom(inner, "init", failIfExists); 1015 } else { 1016 throw Util.newInternal("Cannot compute cardinality: this " + 1017 "database neither supports COUNT DISTINCT nor SELECT in " + 1018 "the FROM clause."); 1019 } 1020 String sql = sqlQuery.toString(); 1021 final SqlStatement stmt = 1022 RolapUtil.executeQuery( 1023 dataSource, sql, 1024 "RolapStar.Column.getCardinality", 1025 "while counting distinct values of column '" + 1026 expression.getGenericExpression()); 1027 try { 1028 ResultSet resultSet = stmt.getResultSet(); 1029 Util.assertTrue(resultSet.next()); 1030 ++stmt.rowCount; 1031 return resultSet.getInt(1); 1032 } catch (SQLException e) { 1033 throw stmt.handle(e); 1034 } finally { 1035 stmt.close(); 1036 } 1037 } 1038 1039 1060 public static String createInExpr( 1061 String expr, 1062 StarColumnPredicate predicate, 1063 SqlQuery.Datatype datatype, 1064 SqlQuery.Dialect dialect) 1065 { 1066 if (predicate instanceof ValueColumnPredicate) { 1067 final ValueColumnPredicate valuePredicate = 1068 (ValueColumnPredicate) predicate; 1069 Object key = valuePredicate.getValue(); 1070 if (key == RolapUtil.sqlNullValue) { 1071 return expr + " is null"; 1072 } else { 1073 StringBuilder buf = new StringBuilder (64); 1074 buf.append(expr); 1075 buf.append(" = "); 1076 dialect.quote(buf, key, datatype); 1077 return buf.toString(); 1078 } 1079 } else if (predicate instanceof ListColumnPredicate) { 1080 ListColumnPredicate valueListColumnPredicate = 1081 (ListColumnPredicate) predicate; 1082 List<StarColumnPredicate> predicates = 1083 valueListColumnPredicate.getPredicates(); 1084 if (predicates.size() == 1) { 1085 return createInExpr( 1086 expr, predicates.get(0), datatype, dialect); 1087 } 1088 1089 int notNullCount = 0; 1090 StringBuilder sb = new StringBuilder (expr); 1091 ValueColumnPredicate firstNotNull = null; 1092 sb.append(" in ("); 1093 for (StarColumnPredicate predicate1 : predicates) { 1094 final ValueColumnPredicate predicate2 = 1095 (ValueColumnPredicate) predicate1; 1096 Object key = predicate2.getValue(); 1097 if (key == RolapUtil.sqlNullValue) { 1098 continue; 1099 } 1100 if (notNullCount > 0) { 1101 sb.append(", "); 1102 } else { 1103 firstNotNull = predicate2; 1104 } 1105 ++notNullCount; 1106 dialect.quote(sb, key, datatype); 1107 } 1108 sb.append(')'); 1109 if (notNullCount < predicates.size()) { 1110 StringBuilder buf; 1112 switch (notNullCount) { 1113 case 0: 1114 return expr + " is null"; 1117 case 1: 1118 assert firstNotNull != null; 1121 buf = new StringBuilder (64); 1122 buf.append('('); 1123 buf.append(expr); 1124 buf.append(" = "); 1125 dialect.quote( 1126 buf, 1127 firstNotNull.getValue(), 1128 datatype); 1129 buf.append(" or "); 1130 buf.append(expr); 1131 buf.append(" is null)"); 1132 return buf.toString(); 1133 default: 1134 buf = new StringBuilder (64); 1137 buf.append('('); 1138 buf.append(sb.toString()); 1139 buf.append(" or "); 1140 buf.append(expr); 1141 buf.append(" is null)"); 1142 return buf.toString(); 1143 } 1144 } else { 1145 return sb.toString(); 1147 } 1148 } else if (predicate instanceof LiteralStarPredicate) { 1149 return predicate.toString(); 1150 } else { 1151 throw Util.newInternal( 1152 "Unexpected constraint type: " + predicate); 1153 } 1154 } 1155 1156 public String toString() { 1157 StringWriter sw = new StringWriter (256); 1158 PrintWriter pw = new PrintWriter (sw); 1159 print(pw, ""); 1160 pw.flush(); 1161 return sw.toString(); 1162 } 1163 1164 1167 public void print(PrintWriter pw, String prefix) { 1168 SqlQuery sqlQuery = getSqlQuery(); 1169 pw.print(prefix); 1170 pw.print(getName()); 1171 pw.print(" ("); 1172 pw.print(getBitPosition()); 1173 pw.print("): "); 1174 pw.print(generateExprString(sqlQuery)); 1175 } 1176 1177 public SqlQuery.Datatype getDatatype() { 1178 return datatype; 1179 } 1180 } 1181 1182 1188 public static class Measure extends Column { 1189 private final RolapAggregator aggregator; 1190 1191 private Measure( 1192 String name, 1193 RolapAggregator aggregator, 1194 Table table, 1195 MondrianDef.Expression expression, 1196 SqlQuery.Datatype datatype) { 1197 super(name, table, expression, datatype); 1198 this.aggregator = aggregator; 1199 } 1200 1201 public RolapAggregator getAggregator() { 1202 return aggregator; 1203 } 1204 1205 public boolean equals(Object obj) { 1206 if (! super.equals(obj)) { 1207 return false; 1208 } 1209 if (! (obj instanceof RolapStar.Measure)) { 1210 return false; 1211 } 1212 RolapStar.Measure other = (RolapStar.Measure) obj; 1213 return (other.aggregator == this.aggregator); 1215 } 1216 1217 1220 public void print(PrintWriter pw, String prefix) { 1221 SqlQuery sqlQuery = getSqlQuery(); 1222 pw.print(prefix); 1223 pw.print(getName()); 1224 pw.print(" ("); 1225 pw.print(getBitPosition()); 1226 pw.print("): "); 1227 pw.print(aggregator.getExpression(generateExprString(sqlQuery))); 1228 } 1229 } 1230 1231 1242 public static class Table { 1243 private final RolapStar star; 1244 private final MondrianDef.Relation relation; 1245 private final List<Column> columnList; 1246 private final Table parent; 1247 private List<Table> children; 1248 private final Condition joinCondition; 1249 private final String alias; 1250 1251 private Table( 1252 RolapStar star, 1253 MondrianDef.Relation relation, 1254 Table parent, 1255 Condition joinCondition) { 1256 this.star = star; 1257 this.relation = relation; 1258 Util.assertTrue( 1259 relation instanceof MondrianDef.Table || 1260 relation instanceof MondrianDef.View, 1261 "todo: allow dimension which is not a Table or View, [" + 1262 relation + "]"); 1263 this.alias = chooseAlias(); 1264 this.parent = parent; 1265 final AliasReplacer aliasReplacer = 1266 new AliasReplacer(relation.getAlias(), this.alias); 1267 this.joinCondition = aliasReplacer.visit(joinCondition); 1268 if (this.joinCondition != null) { 1269 this.joinCondition.table = this; 1270 } 1271 this.columnList = new ArrayList<Column>(); 1272 this.children = Collections.emptyList(); 1273 Util.assertTrue((parent == null) == (joinCondition == null)); 1274 } 1275 1276 1280 public Condition getJoinCondition() { 1281 return joinCondition; 1282 } 1283 1284 1288 public Table getParentTable() { 1289 return parent; 1290 } 1291 1292 private void addColumn(Column column) { 1293 columnList.add(column); 1294 } 1295 1296 1304 private void collectColumns(BitKey bitKey, List<Column> list) { 1305 for (Column column : getColumns()) { 1306 if (bitKey.get(column.getBitPosition())) { 1307 list.add(column); 1308 } 1309 } 1310 for (Table table : getChildren()) { 1311 table.collectColumns(bitKey, list); 1312 } 1313 } 1314 1315 1318 public Column[] lookupColumns(String columnName) { 1319 List<Column> l = new ArrayList<Column>(); 1320 for (Iterator<Column> it = getColumns().iterator(); it.hasNext(); ) { 1321 Column column = it.next(); 1322 if (column.getExpression() instanceof MondrianDef.Column) { 1323 MondrianDef.Column columnExpr = 1324 (MondrianDef.Column) column.getExpression(); 1325 if (columnExpr.name.equals(columnName)) { 1326 l.add(column); 1327 } 1328 } 1329 } 1330 return (Column[]) l.toArray(new Column[0]); 1331 } 1332 1333 public Column lookupColumn(String columnName) { 1334 for (Iterator<Column> it = getColumns().iterator(); it.hasNext(); ) { 1335 Column column = it.next(); 1336 if (column.getExpression() instanceof MondrianDef.Column) { 1337 MondrianDef.Column columnExpr = 1338 (MondrianDef.Column) column.getExpression(); 1339 if (columnExpr.name.equals(columnName)) { 1340 return column; 1341 } 1342 } 1343 } 1344 return null; 1345 } 1346 1347 1351 public Column lookupColumnByExpression(MondrianDef.Expression xmlExpr) { 1352 for (Iterator<Column> it = getColumns().iterator(); it.hasNext(); ) { 1353 Column column = it.next(); 1354 if (column instanceof RolapStar.Measure) { 1355 continue; 1356 } 1357 if (column.getExpression().equals(xmlExpr)) { 1358 return column; 1359 } 1360 } 1361 return null; 1362 } 1363 1364 public boolean containsColumn(Column column) { 1365 for (Iterator<Column> it = getColumns().iterator(); it.hasNext(); ) { 1366 Column other = it.next(); 1367 if (column.equals(other)) { 1368 return true; 1369 } 1370 } 1371 return false; 1372 } 1373 1374 1378 public Measure lookupMeasureByName(String name) { 1379 for (Iterator<Column> it = getColumns().iterator(); it.hasNext(); ) { 1380 Column column = it.next(); 1381 if (column instanceof Measure) { 1382 Measure measure = (Measure) column; 1383 if (measure.getName().equals(name)) { 1384 return measure; 1385 } 1386 } 1387 } 1388 return null; 1389 } 1390 1391 RolapStar getStar() { 1392 return star; 1393 } 1394 private SqlQuery getSqlQuery() { 1395 return getStar().getSqlQuery(); 1396 } 1397 public MondrianDef.Relation getRelation() { 1398 return relation; 1399 } 1400 1401 1402 private String chooseAlias() { 1403 List<String > aliasList = star.getAliasList(); 1404 for (int i = 0;; ++i) { 1405 String candidateAlias = relation.getAlias(); 1406 if (i > 0) { 1407 candidateAlias += "_" + i; 1408 } 1409 if (!aliasList.contains(candidateAlias)) { 1410 return candidateAlias; 1411 } 1412 } 1413 } 1414 1415 public String getAlias() { 1416 return alias; 1417 } 1418 1419 1423 public String getTableName() { 1424 if (relation instanceof MondrianDef.Table) { 1425 MondrianDef.Table t = (MondrianDef.Table) relation; 1426 return t.name; 1427 } else { 1428 return null; 1429 } 1430 } 1431 synchronized void makeMeasure(RolapBaseCubeMeasure measure) { 1432 RolapStar.Measure starMeasure = new RolapStar.Measure( 1433 measure.getName(), 1434 measure.getAggregator(), 1435 this, 1436 measure.getMondrianDefExpression(), 1437 measure.getDatatype()); 1438 1439 measure.setStarMeasure(starMeasure); 1441 if (containsColumn(starMeasure)) { 1442 star.decrementColumnCount(); 1443 } else { 1444 addColumn(starMeasure); 1445 } 1446 } 1447 1448 1456 synchronized Column makeColumns( 1457 RolapCube cube, 1458 RolapLevel level, 1459 Column parentColumn, 1460 String usagePrefix) { 1461 1462 Column nameColumn = null; 1463 if (level.getNameExp() != null) { 1464 nameColumn = makeColumnForLevelExpr( 1466 cube, 1467 level, 1468 level.getName(), 1469 level.getNameExp(), 1470 SqlQuery.Datatype.String, 1471 null, 1472 null, 1473 null); 1474 } 1475 1476 String name = (level.getNameExp() == null) 1479 ? level.getName() 1480 : level.getName() + " (Key)"; 1481 1482 Column column = makeColumnForLevelExpr( 1485 cube, 1486 level, 1487 name, 1488 level.getKeyExp(), 1489 level.getDatatype(), 1490 nameColumn, 1491 parentColumn, 1492 usagePrefix); 1493 1494 if (column != null) { 1495 Map<RolapLevel, Column> map = star.getLevelToColumnMap(cube); 1496 map.put(level, column); 1497 } 1498 1499 return column; 1500 } 1501 1502 private Column makeColumnForLevelExpr( 1503 RolapCube cube, 1504 RolapLevel level, 1505 String name, 1506 MondrianDef.Expression xmlExpr, 1507 SqlQuery.Datatype datatype, 1508 Column nameColumn, 1509 Column parentColumn, 1510 String usagePrefix) { 1511 Table table = this; 1512 if (xmlExpr instanceof MondrianDef.Column) { 1513 final MondrianDef.Column xmlColumn = 1514 (MondrianDef.Column) xmlExpr; 1515 1516 String tableName = xmlColumn.table; 1517 table = findAncestor(tableName); 1518 if (table == null) { 1519 throw Util.newError( 1520 "Level '" + level.getUniqueName() 1521 + "' of cube '" 1522 + this 1523 + "' is invalid: table '" + tableName 1524 + "' is not found in current scope" 1525 + Util.nl 1526 + ", star:" 1527 + Util.nl 1528 + getStar()); 1529 } 1530 RolapStar.AliasReplacer aliasReplacer = 1531 new RolapStar.AliasReplacer(tableName, table.getAlias()); 1532 xmlExpr = aliasReplacer.visit(xmlExpr); 1533 } 1534 Column c = lookupColumnByExpression(xmlExpr); 1536 1537 RolapStar.Column column = null; 1538 if (c != null) { 1539 column = c; 1546 } else { 1547 column = new RolapStar.Column( 1549 name, 1550 table, 1551 xmlExpr, 1552 datatype, 1553 nameColumn, 1554 parentColumn, 1555 usagePrefix); 1556 addColumn(column); 1557 } 1558 return column; 1559 } 1560 1561 1562 1567 synchronized Table addJoin(MondrianDef.Relation relation, 1568 RolapStar.Condition joinCondition) { 1569 if (relation instanceof MondrianDef.Table || 1570 relation instanceof MondrianDef.View) { 1571 RolapStar.Table starTable = findChild(relation, joinCondition); 1572 if (starTable == null) { 1573 starTable = new RolapStar.Table(star, relation, this, 1574 joinCondition); 1575 if (this.children.isEmpty()) { 1576 this.children = new ArrayList<Table>(); 1577 } 1578 this.children.add(starTable); 1579 } 1580 return starTable; 1581 1582 } else if (relation instanceof MondrianDef.Join) { 1583 MondrianDef.Join join = (MondrianDef.Join) relation; 1584 RolapStar.Table leftTable = addJoin(join.left, joinCondition); 1585 String leftAlias = join.leftAlias; 1586 if (leftAlias == null) { 1587 leftAlias = join.left.getAlias(); 1588 if (leftAlias == null) { 1589 throw Util.newError( 1590 "missing leftKeyAlias in " + relation); 1591 } 1592 } 1593 assert leftTable.findAncestor(leftAlias) == leftTable; 1594 leftAlias = leftTable.getAlias(); 1596 1597 String rightAlias = join.rightAlias; 1598 if (rightAlias == null) { 1599 rightAlias = join.right.getAlias(); 1600 if (rightAlias == null) { 1601 throw Util.newError( 1602 "missing rightKeyAlias in " + relation); 1603 } 1604 } 1605 joinCondition = new RolapStar.Condition( 1606 new MondrianDef.Column(leftAlias, join.leftKey), 1607 new MondrianDef.Column(rightAlias, join.rightKey)); 1608 RolapStar.Table rightTable = leftTable.addJoin( 1609 join.right, joinCondition); 1610 return rightTable; 1611 1612 } else { 1613 throw Util.newInternal("bad relation type " + relation); 1614 } 1615 } 1616 1617 1621 public Table findChild( 1622 MondrianDef.Relation relation, 1623 Condition joinCondition) { 1624 for (Table child : getChildren()) { 1625 if (child.relation.equals(relation)) { 1626 Condition condition = joinCondition; 1627 if (!Util.equalName(relation.getAlias(), child.alias)) { 1628 AliasReplacer aliasReplacer = new AliasReplacer( 1632 relation.getAlias(), child.alias); 1633 condition = aliasReplacer.visit(joinCondition); 1634 } 1635 if (child.joinCondition.equals(condition)) { 1636 return child; 1637 } 1638 } 1639 } 1640 return null; 1641 } 1642 1643 1646 public Table findDescendant(String seekAlias) { 1647 if (getAlias().equals(seekAlias)) { 1648 return this; 1649 } 1650 for (Table child : getChildren()) { 1651 Table found = child.findDescendant(seekAlias); 1652 if (found != null) { 1653 return found; 1654 } 1655 } 1656 return null; 1657 } 1658 1659 1662 public Table findAncestor(String tableName) { 1663 for (Table t = this; t != null; t = t.parent) { 1664 if (t.relation.getAlias().equals(tableName)) { 1665 return t; 1666 } 1667 } 1668 return null; 1669 } 1670 1671 public boolean equalsTableName(String tableName) { 1672 if (this.relation instanceof MondrianDef.Table) { 1673 MondrianDef.Table mt = (MondrianDef.Table) this.relation; 1674 if (mt.name.equals(tableName)) { 1675 return true; 1676 } 1677 } 1678 return false; 1679 } 1680 1681 1691 public void addToFrom( 1692 SqlQuery query, 1693 boolean failIfExists, 1694 boolean joinToParent) { 1695 query.addFrom(relation, alias, failIfExists); 1696 Util.assertTrue((parent == null) == (joinCondition == null)); 1697 if (joinToParent) { 1698 if (parent != null) { 1699 parent.addToFrom(query, failIfExists, joinToParent); 1700 } 1701 if (joinCondition != null) { 1702 query.addWhere(joinCondition.toString(query)); 1703 } 1704 } 1705 } 1706 1707 1710 public List<Table> getChildren() { 1711 return children; 1712 } 1713 1714 1717 public List<Column> getColumns() { 1718 return columnList; 1719 } 1720 1721 1726 public RolapStar.Table findTableWithLeftJoinCondition( 1727 final String columnName) { 1728 for (Table child : getChildren()) { 1729 Condition condition = child.joinCondition; 1730 if (condition != null) { 1731 if (condition.left instanceof MondrianDef.Column) { 1732 MondrianDef.Column mcolumn = 1733 (MondrianDef.Column) condition.left; 1734 if (mcolumn.name.equals(columnName)) { 1735 return child; 1736 } 1737 } 1738 } 1739 1740 } 1741 return null; 1742 } 1743 1744 1749 public RolapStar.Table findTableWithLeftCondition( 1750 final MondrianDef.Expression left) { 1751 for (Table child : getChildren()) { 1752 Condition condition = child.joinCondition; 1753 if (condition != null) { 1754 if (condition.left instanceof MondrianDef.Column) { 1755 MondrianDef.Column mcolumn = 1756 (MondrianDef.Column) condition.left; 1757 if (mcolumn.equals(left)) { 1758 return child; 1759 } 1760 } 1761 } 1762 1763 } 1764 return null; 1765 } 1766 1767 1770 public boolean isFunky() { 1771 return (relation == null); 1772 } 1773 public boolean equals(Object obj) { 1774 if (!(obj instanceof Table)) { 1775 return false; 1776 } 1777 Table other = (Table) obj; 1778 return getAlias().equals(other.getAlias()); 1779 } 1780 public int hashCode() { 1781 return getAlias().hashCode(); 1782 } 1783 1784 public String toString() { 1785 StringWriter sw = new StringWriter (256); 1786 PrintWriter pw = new PrintWriter (sw); 1787 print(pw, ""); 1788 pw.flush(); 1789 return sw.toString(); 1790 } 1791 1792 1795 public void print(PrintWriter pw, String prefix) { 1796 pw.print(prefix); 1797 pw.println("Table:"); 1798 String subprefix = prefix + " "; 1799 1800 pw.print(subprefix); 1801 pw.print("alias="); 1802 pw.println(getAlias()); 1803 1804 if (this.relation != null) { 1805 pw.print(subprefix); 1806 pw.print("relation="); 1807 pw.println(relation); 1808 } 1809 1810 pw.print(subprefix); 1811 pw.println("Columns:"); 1812 String subsubprefix = subprefix + " "; 1813 1814 for (Iterator<Column> it = getColumns().iterator(); it.hasNext(); ) { 1815 Column column = it.next(); 1816 column.print(pw, subsubprefix); 1817 pw.println(); 1818 } 1819 1820 if (this.joinCondition != null) { 1821 this.joinCondition.print(pw, subprefix); 1822 } 1823 for (Table child : getChildren()) { 1824 child.print(pw, subprefix); 1825 } 1826 } 1827 1828 1831 public boolean containsColumn(String columnName) { 1832 if (relation instanceof MondrianDef.Table) { 1833 return star.containsColumn(((MondrianDef.Table) relation).name, 1834 columnName); 1835 } else { 1836 return false; 1838 } 1839 } 1840 } 1841 1842 public static class Condition { 1843 private static final Logger LOGGER = Logger.getLogger(Condition.class); 1844 1845 private final MondrianDef.Expression left; 1846 private final MondrianDef.Expression right; 1847 Table table; 1849 1850 Condition(MondrianDef.Expression left, 1851 MondrianDef.Expression right) { 1852 assert left != null; 1853 assert right != null; 1854 1855 if (!(left instanceof MondrianDef.Column)) { 1856 LOGGER.debug("Condition.left NOT Column: " 1859 + left.getClass().getName()); 1860 } 1861 this.left = left; 1862 this.right = right; 1863 } 1864 public MondrianDef.Expression getLeft() { 1865 return left; 1866 } 1867 public MondrianDef.Expression getRight() { 1868 return right; 1869 } 1870 public String toString(SqlQuery query) { 1871 return left.getExpression(query) + " = " + right.getExpression(query); 1872 } 1873 public int hashCode() { 1874 return left.hashCode() ^ right.hashCode(); 1875 } 1876 1877 public boolean equals(Object obj) { 1878 if (!(obj instanceof Condition)) { 1879 return false; 1880 } 1881 Condition that = (Condition) obj; 1882 return (this.left.equals(that.left) && 1883 this.right.equals(that.right)); 1884 } 1885 1886 public String toString() { 1887 StringWriter sw = new StringWriter (256); 1888 PrintWriter pw = new PrintWriter (sw); 1889 print(pw, ""); 1890 pw.flush(); 1891 return sw.toString(); 1892 } 1893 1894 1897 public void print(PrintWriter pw, String prefix) { 1898 SqlQuery sqlQueuy = table.getSqlQuery(); 1899 pw.print(prefix); 1900 pw.println("Condition:"); 1901 String subprefix = prefix + " "; 1902 1903 pw.print(subprefix); 1904 pw.print("left="); 1905 if (left instanceof MondrianDef.Column) { 1907 MondrianDef.Column c = (MondrianDef.Column) left; 1908 Column col = table.star.getFactTable().lookupColumn(c.name); 1909 if (col != null) { 1910 pw.print(" ("); 1911 pw.print(col.getBitPosition()); 1912 pw.print(") "); 1913 } 1914 } 1915 pw.println(left.getExpression(sqlQueuy)); 1916 1917 pw.print(subprefix); 1918 pw.print("right="); 1919 pw.println(right.getExpression(sqlQueuy)); 1920 } 1921 } 1922 1923 1927 public static class AliasReplacer { 1928 private final String oldAlias; 1929 private final String newAlias; 1930 1931 public AliasReplacer(String oldAlias, String newAlias) { 1932 this.oldAlias = oldAlias; 1933 this.newAlias = newAlias; 1934 } 1935 1936 private Condition visit(Condition condition) { 1937 if (condition == null) { 1938 return null; 1939 } 1940 if (newAlias.equals(oldAlias)) { 1941 return condition; 1942 } 1943 return new Condition( 1944 visit(condition.left), 1945 visit(condition.right)); 1946 } 1947 1948 public MondrianDef.Expression visit(MondrianDef.Expression expression) { 1949 if (expression == null) { 1950 return null; 1951 } 1952 if (newAlias.equals(oldAlias)) { 1953 return expression; 1954 } 1955 if (expression instanceof MondrianDef.Column) { 1956 MondrianDef.Column column = (MondrianDef.Column) expression; 1957 return new MondrianDef.Column(visit(column.table), column.name); 1958 } else { 1959 throw Util.newInternal("need to implement " + expression); 1960 } 1961 } 1962 1963 private String visit(String table) { 1964 return table.equals(oldAlias) 1965 ? newAlias 1966 : table; 1967 } 1968 } 1969} 1970 1971 | Popular Tags |