001 /* 002 // $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/AggGen.java#19 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2005-2008 Julian Hyde and others 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 011 package mondrian.rolap.aggmatcher; 012 013 import mondrian.olap.MondrianDef; 014 import mondrian.olap.Util; 015 import mondrian.rolap.RolapStar; 016 import mondrian.rolap.sql.SqlQuery; 017 import mondrian.rolap.RolapAggregator; 018 import org.apache.log4j.Logger; 019 import java.io.PrintWriter; 020 import java.io.StringWriter; 021 import java.util.List; 022 import java.util.ArrayList; 023 import java.util.Set; 024 import java.util.HashSet; 025 import java.util.Map; 026 import java.util.HashMap; 027 import java.util.Iterator; 028 import java.sql.SQLException; 029 import java.sql.Types; 030 031 /** 032 * This class is used to create "lost" and "collapsed" aggregate table 033 * creation sql (creates the rdbms table and inserts into it from the base 034 * fact table). 035 * 036 * @author Richard M. Emberson 037 * @version $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/AggGen.java#19 $ 038 */ 039 public class AggGen { 040 private static final Logger LOGGER = Logger.getLogger(AggGen.class); 041 042 private final String cubeName; 043 private final RolapStar star; 044 private final RolapStar.Column[] columns; 045 046 /** map RolapStar.Table to list of JdbcSchema Column Usages */ 047 private final Map<RolapStar.Table, List<JdbcSchema.Table.Column.Usage>> 048 collapsedColumnUsages = 049 new HashMap<RolapStar.Table, List<JdbcSchema.Table.Column.Usage>>(); 050 051 /** set of JdbcSchema Column Usages */ 052 private final Set<JdbcSchema.Table.Column.Usage> notLostColumnUsages = 053 new HashSet<JdbcSchema.Table.Column.Usage>(); 054 055 /** list of JdbcSchema Column Usages */ 056 private final List<JdbcSchema.Table.Column.Usage> measures = 057 new ArrayList<JdbcSchema.Table.Column.Usage>(); 058 059 private boolean isReady; 060 061 public AggGen( 062 String cubeName, 063 RolapStar star, 064 RolapStar.Column[] columns) 065 { 066 this.cubeName = cubeName; 067 this.star = star; 068 this.columns = columns; 069 init(); 070 } 071 072 private Logger getLogger() { 073 return LOGGER; 074 } 075 076 /** 077 * Return true if this instance is ready to generate the sql. If false, 078 * then something went wrong as it was trying to understand the columns. 079 */ 080 public boolean isReady() { 081 return isReady; 082 } 083 084 protected RolapStar.Table getFactTable() { 085 return star.getFactTable(); 086 } 087 088 protected String getFactTableName() { 089 return getFactTable().getAlias(); 090 } 091 092 protected SqlQuery getSqlQuery() { 093 return star.getSqlQuery(); 094 } 095 096 protected String getFactCount() { 097 return "fact_count"; 098 } 099 100 protected JdbcSchema.Table getTable(JdbcSchema db, RolapStar.Table rt) { 101 JdbcSchema.Table jt = getTable(db, rt.getAlias()); 102 return (jt == null) 103 ? getTable(db, rt.getTableName()) 104 : jt; 105 } 106 107 protected JdbcSchema.Table getTable(JdbcSchema db, String name) { 108 return db.getTable(name); 109 } 110 111 protected JdbcSchema.Table.Column getColumn( 112 JdbcSchema.Table table, 113 String name) { 114 return table.getColumn(name); 115 } 116 117 protected String getRolapStarColumnName(RolapStar.Column rColumn) { 118 MondrianDef.Expression expr = rColumn.getExpression(); 119 if (expr instanceof MondrianDef.Column) { 120 MondrianDef.Column cx = (MondrianDef.Column) expr; 121 return cx.getColumnName(); 122 } 123 return null; 124 } 125 protected void addForeignKeyToNotLostColumnUsages( 126 JdbcSchema.Table.Column column) { 127 128 // first make sure its not already in 129 String cname = column.getName(); 130 for (JdbcSchema.Table.Column.Usage usage : notLostColumnUsages) { 131 JdbcSchema.Table.Column c = usage.getColumn(); 132 if (cname.equals(c.getName())) { 133 return; 134 } 135 } 136 JdbcSchema.Table.Column.Usage usage; 137 if (column.hasUsage(JdbcSchema.UsageType.FOREIGN_KEY)) { 138 Iterator<JdbcSchema.Table.Column.Usage> it = 139 column.getUsages(JdbcSchema.UsageType.FOREIGN_KEY); 140 it.hasNext(); 141 usage = it.next(); 142 } else { 143 usage = column.newUsage(JdbcSchema.UsageType.FOREIGN_KEY); 144 usage.setSymbolicName(JdbcSchema.UsageType.FOREIGN_KEY.name()); 145 } 146 notLostColumnUsages.add(usage); 147 } 148 149 /** 150 * The columns are the RolapStar columns taking part in an aggregation 151 * request. This is what happens. 152 * First, for each column, walk up the column's table until one level below 153 * the base fact table. The left join condition contains the base fact table 154 * and the foreign key column name. This column should not be lost. 155 * Get the base fact table's measure columns. 156 * With a list of columns that should not be lost and measure, one can 157 * create lost create and insert commands. 158 */ 159 private void init() { 160 JdbcSchema db = JdbcSchema.makeDB(star.getDataSource()); 161 try { 162 db.load(); 163 } catch (SQLException ex) { 164 getLogger().error(ex); 165 return; 166 } 167 168 JdbcSchema.Table factTable = getTable(db, getFactTableName()); 169 if (factTable == null) { 170 StringBuilder buf = new StringBuilder(64); 171 buf.append("Init: "); 172 buf.append("No fact table with name \""); 173 buf.append(getFactTableName()); 174 buf.append("\""); 175 getLogger().warn(buf.toString()); 176 return; 177 } 178 try { 179 factTable.load(); 180 } catch (SQLException ex) { 181 getLogger().error(ex); 182 return; 183 } 184 185 if (getLogger().isDebugEnabled()) { 186 StringBuilder buf = new StringBuilder(512); 187 buf.append("Init: "); 188 buf.append("RolapStar:"); 189 buf.append(Util.nl); 190 buf.append(getFactTable()); 191 buf.append(Util.nl); 192 buf.append("FactTable:"); 193 buf.append(Util.nl); 194 buf.append(factTable); 195 getLogger().debug(buf.toString()); 196 } 197 198 // do foreign keys 199 for (RolapStar.Column column : columns) { 200 if (getLogger().isDebugEnabled()) { 201 StringBuilder buf = new StringBuilder(64); 202 buf.append("Init: "); 203 buf.append("Column: "); 204 buf.append(column); 205 getLogger().debug(buf.toString()); 206 } 207 RolapStar.Table table = column.getTable(); 208 209 if (table.getParentTable() == null) { 210 // this is for those crazy dimensions which are in the 211 // fact table, you know, non-shared with no table element 212 213 // How the firetruck to enter information for the 214 // collapsed case. This column is in the base fact table 215 // and can be part of a dimension hierarchy but no where 216 // in the RolapStar is this hiearchy captured - ugg. 217 if (!addSpecialCollapsedColumn(db, column)) { 218 return; 219 } 220 221 222 MondrianDef.Expression expr = column.getExpression(); 223 if (expr instanceof MondrianDef.Column) { 224 MondrianDef.Column exprColumn = (MondrianDef.Column) expr; 225 String name = exprColumn.getColumnName(); 226 JdbcSchema.Table.Column c = getColumn(factTable, name); 227 if (c == null) { 228 StringBuilder buf = new StringBuilder(64); 229 buf.append("Init: "); 230 buf.append("FactTable:"); 231 buf.append(getFactTableName()); 232 buf.append(Util.nl); 233 buf.append("No Column with name \""); 234 buf.append(name); 235 buf.append("\""); 236 getLogger().warn(buf.toString()); 237 return; 238 } 239 if (getLogger().isDebugEnabled()) { 240 getLogger().debug(" Jdbc Column: c=" + c); 241 } 242 addForeignKeyToNotLostColumnUsages(c); 243 } 244 245 } else { 246 247 if (!addCollapsedColumn(db, column)) { 248 return; 249 } 250 251 while (table.getParentTable().getParentTable() != null) { 252 table = table.getParentTable(); 253 } 254 RolapStar.Condition cond = table.getJoinCondition(); 255 if (getLogger().isDebugEnabled()) { 256 getLogger().debug(" RolapStar.Condition: cond=" + cond); 257 } 258 MondrianDef.Expression left = cond.getLeft(); 259 if (left instanceof MondrianDef.Column) { 260 MondrianDef.Column leftColumn = (MondrianDef.Column) left; 261 String name = leftColumn.getColumnName(); 262 JdbcSchema.Table.Column c = getColumn(factTable, name); 263 if (c == null) { 264 StringBuilder buf = new StringBuilder(64); 265 buf.append("Init: "); 266 buf.append("FactTable:"); 267 buf.append(getFactTableName()); 268 buf.append(Util.nl); 269 buf.append("No Column with name \""); 270 buf.append(name); 271 buf.append("\""); 272 getLogger().warn(buf.toString()); 273 return; 274 } 275 if (getLogger().isDebugEnabled()) { 276 getLogger().debug(" Jdbc Column: c=" + c); 277 } 278 addForeignKeyToNotLostColumnUsages(c); 279 } 280 } 281 } 282 283 // do measures 284 for (RolapStar.Column rColumn : getFactTable().getColumns()) { 285 String name = getRolapStarColumnName(rColumn); 286 if (name == null) { 287 StringBuilder buf = new StringBuilder(64); 288 buf.append("Init: "); 289 buf.append("For fact table \""); 290 buf.append(getFactTableName()); 291 buf.append( 292 "\", could not get column name for RolapStar.Column: "); 293 buf.append(rColumn); 294 getLogger().warn(buf.toString()); 295 return; 296 } 297 if (!(rColumn instanceof RolapStar.Measure)) { 298 // TODO: whats the solution to this? 299 // its a funky dimension column in the fact table!!! 300 getLogger().warn("not a measure: " + name); 301 continue; 302 } 303 RolapStar.Measure rMeasure = (RolapStar.Measure) rColumn; 304 if (!rMeasure.getCubeName().equals(cubeName)) { 305 continue; 306 } 307 final RolapAggregator aggregator = rMeasure.getAggregator(); 308 JdbcSchema.Table.Column c = getColumn(factTable, name); 309 if (c == null) { 310 StringBuilder buf = new StringBuilder(64); 311 buf.append("For RolapStar: \""); 312 buf.append(getFactTable().getAlias()); 313 buf.append("\" measure with name, "); 314 buf.append(name); 315 buf.append(", is not a column name. "); 316 buf.append("The measure's column name may be an expression"); 317 buf.append(" and currently AggGen does not handle expressions."); 318 buf.append(" You will have to add this measure to the"); 319 buf.append(" aggregate table definition by hand."); 320 getLogger().warn(buf.toString()); 321 continue; 322 } 323 if (getLogger().isDebugEnabled()) { 324 getLogger().debug(" Jdbc Column m=" + c); 325 } 326 /* 327 JdbcSchema.Table.Column.Usage usage = 328 c.newUsage(JdbcSchema.MEASURE_COLUMN_USAGE); 329 usage.setAggregator(aggregator); 330 usage.setSymbolicName(rColumn.getName()); 331 measures.add(usage); 332 */ 333 334 JdbcSchema.Table.Column.Usage usage = null; 335 if (c.hasUsage(JdbcSchema.UsageType.MEASURE)) { 336 for (Iterator<JdbcSchema.Table.Column.Usage> uit = 337 c.getUsages(JdbcSchema.UsageType.MEASURE); 338 uit.hasNext();) { 339 JdbcSchema.Table.Column.Usage tmpUsage = uit.next(); 340 if ((tmpUsage.getAggregator() == aggregator) && 341 tmpUsage.getSymbolicName().equals(rColumn.getName())) { 342 usage = tmpUsage; 343 break; 344 } 345 } 346 } 347 if (usage == null) { 348 usage = c.newUsage(JdbcSchema.UsageType.MEASURE); 349 usage.setAggregator(aggregator); 350 usage.setSymbolicName(rColumn.getName()); 351 } 352 measures.add(usage); 353 } 354 355 // If we got to here, then everything is ok. 356 isReady = true; 357 } 358 private boolean addSpecialCollapsedColumn(final JdbcSchema db, 359 final RolapStar.Column rColumn) { 360 String rname = getRolapStarColumnName(rColumn); 361 if (rname == null) { 362 StringBuilder buf = new StringBuilder(64); 363 buf.append("Adding Special Collapsed Column: "); 364 buf.append("For fact table \""); 365 buf.append(getFactTableName()); 366 buf.append("\", could not get column name for RolapStar.Column: "); 367 buf.append(rColumn); 368 getLogger().warn(buf.toString()); 369 return false; 370 } 371 // this is in fact the fact table. 372 RolapStar.Table rt = rColumn.getTable(); 373 374 JdbcSchema.Table jt = getTable(db, rt); 375 if (jt == null) { 376 StringBuilder buf = new StringBuilder(64); 377 buf.append("Adding Special Collapsed Column: "); 378 buf.append("For fact table \""); 379 buf.append(getFactTableName()); 380 buf.append("\", could not get jdbc schema table "); 381 buf.append("for RolapStar.Table with alias \""); 382 buf.append(rt.getAlias()); 383 buf.append("\""); 384 getLogger().warn(buf.toString()); 385 return false; 386 } 387 try { 388 jt.load(); 389 } catch (SQLException ex) { 390 getLogger().error(ex); 391 return false; 392 } 393 394 List<JdbcSchema.Table.Column.Usage> list = collapsedColumnUsages.get(rt); 395 if (list == null) { 396 list = new ArrayList<JdbcSchema.Table.Column.Usage>(); 397 collapsedColumnUsages.put(rt, list); 398 } 399 400 JdbcSchema.Table.Column c = getColumn(jt, rname); 401 if (c == null) { 402 StringBuilder buf = new StringBuilder(64); 403 buf.append("Adding Special Collapsed Column: "); 404 buf.append("For fact table \""); 405 buf.append(getFactTableName()); 406 buf.append("\", could not get jdbc schema column "); 407 buf.append("for RolapStar.Table with alias \""); 408 buf.append(rt.getAlias()); 409 buf.append("\" and column name \""); 410 buf.append(rname); 411 buf.append("\""); 412 getLogger().warn(buf.toString()); 413 return false; 414 } 415 // NOTE: this creates a new usage for the fact table 416 // I do not know if this is a problem is AggGen is run before 417 // Mondrian uses aggregate tables. 418 list.add(c.newUsage(JdbcSchema.UsageType.FOREIGN_KEY)); 419 420 RolapStar.Column prColumn = rColumn; 421 while (prColumn.getParentColumn() != null) { 422 prColumn = prColumn.getParentColumn(); 423 rname = getRolapStarColumnName(prColumn); 424 if (rname == null) { 425 StringBuilder buf = new StringBuilder(64); 426 buf.append("Adding Special Collapsed Column: "); 427 buf.append("For fact table \""); 428 buf.append(getFactTableName()); 429 buf.append("\", could not get parent column name"); 430 buf.append("for RolapStar.Column \""); 431 buf.append(prColumn); 432 buf.append("\" for RolapStar.Table with alias \""); 433 buf.append(rt.getAlias()); 434 buf.append("\""); 435 getLogger().warn(buf.toString()); 436 return false; 437 } 438 c = getColumn(jt, rname); 439 if (c == null) { 440 getLogger().warn("Can not find column: " + rname); 441 break; 442 } 443 // NOTE: this creates a new usage for the fact table 444 // I do not know if this is a problem is AggGen is run before 445 // Mondrian uses aggregate tables. 446 list.add(c.newUsage(JdbcSchema.UsageType.FOREIGN_KEY)); 447 } 448 449 return true; 450 } 451 452 private boolean addCollapsedColumn(final JdbcSchema db, 453 final RolapStar.Column rColumn) { 454 // TODO: if column is "id" column, then there is no collapse 455 String rname = getRolapStarColumnName(rColumn); 456 if (rname == null) { 457 StringBuilder buf = new StringBuilder(64); 458 buf.append("Adding Collapsed Column: "); 459 buf.append("For fact table \""); 460 buf.append(getFactTableName()); 461 buf.append("\", could not get column name for RolapStar.Column: "); 462 buf.append(rColumn); 463 getLogger().warn(buf.toString()); 464 return false; 465 } 466 467 RolapStar.Table rt = rColumn.getTable(); 468 469 JdbcSchema.Table jt = getTable(db, rt); 470 if (jt == null) { 471 StringBuilder buf = new StringBuilder(64); 472 buf.append("Adding Collapsed Column: "); 473 buf.append("For fact table \""); 474 buf.append(getFactTableName()); 475 buf.append("\", could not get jdbc schema table "); 476 buf.append("for RolapStar.Table with alias \""); 477 buf.append(rt.getAlias()); 478 buf.append("\""); 479 getLogger().warn(buf.toString()); 480 return false; 481 } 482 try { 483 jt.load(); 484 } catch (SQLException ex) { 485 getLogger().error(ex); 486 return false; 487 } 488 489 //CG guarantee the columns has been loaded before looking up them 490 try { 491 jt.load(); 492 } catch (SQLException sqle) { 493 getLogger().error(sqle); 494 return false; 495 } 496 497 // if this is a dimension table, then walk down the levels until 498 // we hit the current column 499 List<JdbcSchema.Table.Column.Usage> list = 500 new ArrayList<JdbcSchema.Table.Column.Usage>(); 501 for (RolapStar.Column rc : rt.getColumns()) { 502 // do not include name columns 503 if (rc.isNameColumn()) { 504 continue; 505 } 506 String name = getRolapStarColumnName(rc); 507 if (name == null) { 508 StringBuilder buf = new StringBuilder(64); 509 buf.append("Adding Collapsed Column: "); 510 buf.append("For fact table \""); 511 buf.append(getFactTableName()); 512 buf.append("\", could not get column name"); 513 buf.append(" for RolapStar.Column \""); 514 buf.append(rc); 515 buf.append("\" for RolapStar.Table with alias \""); 516 buf.append(rt.getAlias()); 517 buf.append("\""); 518 getLogger().warn(buf.toString()); 519 return false; 520 } 521 JdbcSchema.Table.Column c = getColumn(jt, name); 522 if (c == null) { 523 getLogger().warn("Can not find column: " + name); 524 break; 525 } 526 527 JdbcSchema.Table.Column.Usage usage = 528 c.newUsage(JdbcSchema.UsageType.FOREIGN_KEY); 529 usage.usagePrefix = rc.getUsagePrefix(); 530 531 list.add(usage); 532 533 if (rname.equals(name)) { 534 break; 535 } 536 } 537 // may already be there so only enter if new list is bigger 538 List<JdbcSchema.Table.Column.Usage> l = collapsedColumnUsages.get(rt); 539 if ((l == null) || (l.size() < list.size())) { 540 collapsedColumnUsages.put(rt, list); 541 } 542 543 return true; 544 } 545 546 private static final String AGG_LOST_PREFIX = "agg_l_XXX_"; 547 548 String makeLostAggregateTableName(String factTableName) { 549 StringBuilder buf = new StringBuilder(64); 550 buf.append(AGG_LOST_PREFIX); 551 buf.append(factTableName); 552 return buf.toString(); 553 } 554 555 private static final String AGG_COLLAPSED_PREFIX = "agg_c_XXX_"; 556 557 String makeCollapsedAggregateTableName(String factTableName) { 558 StringBuilder buf = new StringBuilder(64); 559 buf.append(AGG_COLLAPSED_PREFIX); 560 buf.append(factTableName); 561 return buf.toString(); 562 } 563 564 565 566 /** 567 * Return a String containing the sql code to create a lost dimension 568 * table. 569 * 570 * @return lost dimension sql code 571 */ 572 public String createLost() { 573 StringWriter sw = new StringWriter(512); 574 PrintWriter pw = new PrintWriter(sw); 575 String prefix = " "; 576 577 pw.print("CREATE TABLE "); 578 pw.print(makeLostAggregateTableName(getFactTableName())); 579 pw.println(" ("); 580 581 // do foreign keys 582 for (JdbcSchema.Table.Column.Usage usage : notLostColumnUsages) { 583 addColumnCreate(pw, prefix, usage); 584 } 585 586 // do measures 587 for (JdbcSchema.Table.Column.Usage usage : measures) { 588 addColumnCreate(pw, prefix, usage); 589 } 590 // do fact_count 591 pw.print(prefix); 592 pw.print(getFactCount()); 593 pw.println(" INTEGER NOT NULL"); 594 595 pw.println(");"); 596 return sw.toString(); 597 } 598 599 /** 600 * Return the sql code to populate a lost dimension table from the fact 601 * table. 602 */ 603 public String insertIntoLost() { 604 StringWriter sw = new StringWriter(512); 605 PrintWriter pw = new PrintWriter(sw); 606 String prefix = " "; 607 String factTableName = getFactTableName(); 608 SqlQuery sqlQuery = getSqlQuery(); 609 610 pw.print("INSERT INTO "); 611 pw.print(makeLostAggregateTableName(getFactTableName())); 612 pw.println(" ("); 613 614 for (JdbcSchema.Table.Column.Usage usage : notLostColumnUsages) { 615 JdbcSchema.Table.Column c = usage.getColumn(); 616 617 pw.print(prefix); 618 pw.print(c.getName()); 619 pw.println(','); 620 } 621 622 for (JdbcSchema.Table.Column.Usage usage : measures) { 623 JdbcSchema.Table.Column c = usage.getColumn(); 624 625 pw.print(prefix); 626 String name = getUsageName(usage); 627 pw.print(name); 628 pw.println(','); 629 } 630 // do fact_count 631 pw.print(prefix); 632 pw.print(getFactCount()); 633 pw.println(")"); 634 635 pw.println("SELECT"); 636 for (JdbcSchema.Table.Column.Usage usage : notLostColumnUsages) { 637 JdbcSchema.Table.Column c = usage.getColumn(); 638 639 pw.print(prefix); 640 pw.print(sqlQuery.getDialect().quoteIdentifier(factTableName, 641 c.getName())); 642 pw.print(" AS "); 643 pw.print(sqlQuery.getDialect().quoteIdentifier(c.getName())); 644 pw.println(','); 645 } 646 for (JdbcSchema.Table.Column.Usage usage : measures) { 647 JdbcSchema.Table.Column c = usage.getColumn(); 648 RolapAggregator agg = usage.getAggregator(); 649 650 pw.print(prefix); 651 pw.print( 652 agg.getExpression(sqlQuery.getDialect().quoteIdentifier( 653 factTableName, c.getName()))); 654 pw.print(" AS "); 655 pw.print(sqlQuery.getDialect().quoteIdentifier(c.getName())); 656 pw.println(','); 657 } 658 659 // do fact_count 660 pw.print(prefix); 661 pw.print("COUNT(*) AS "); 662 pw.println(sqlQuery.getDialect().quoteIdentifier(getFactCount())); 663 664 pw.println("FROM "); 665 pw.print(prefix); 666 pw.print(sqlQuery.getDialect().quoteIdentifier(factTableName)); 667 pw.print(" "); 668 pw.println(sqlQuery.getDialect().quoteIdentifier(factTableName)); 669 670 pw.println("GROUP BY "); 671 int k = 0; 672 for (JdbcSchema.Table.Column.Usage notLostColumnUsage : notLostColumnUsages) { 673 if (k++ > 0) { 674 pw.println(","); 675 } 676 JdbcSchema.Table.Column.Usage usage = notLostColumnUsage; 677 JdbcSchema.Table.Column c = usage.getColumn(); 678 679 pw.print(prefix); 680 pw.print(sqlQuery.getDialect().quoteIdentifier(factTableName, 681 c.getName())); 682 } 683 684 pw.println(';'); 685 return sw.toString(); 686 } 687 /** 688 * Return a String containing the sql code to create a collapsed dimension 689 * table. 690 * 691 * @return collapsed dimension sql code 692 */ 693 public String createCollapsed() { 694 StringWriter sw = new StringWriter(512); 695 PrintWriter pw = new PrintWriter(sw); 696 String prefix = " "; 697 698 pw.print("CREATE TABLE "); 699 pw.print(makeCollapsedAggregateTableName(getFactTableName())); 700 pw.println(" ("); 701 702 // do foreign keys 703 for (List<JdbcSchema.Table.Column.Usage> list : collapsedColumnUsages.values()) { 704 for (JdbcSchema.Table.Column.Usage usage : list) { 705 addColumnCreate(pw, prefix, usage); 706 } 707 } 708 709 // do measures 710 for (JdbcSchema.Table.Column.Usage usage : measures) { 711 addColumnCreate(pw, prefix, usage); 712 } 713 // do fact_count 714 pw.print(prefix); 715 pw.print(getFactCount()); 716 pw.println(" INTEGER NOT NULL"); 717 718 pw.println(");"); 719 return sw.toString(); 720 } 721 722 /** 723 * Return the sql code to populate a collapsed dimension table from 724 * the fact table. 725 */ 726 public String insertIntoCollapsed() { 727 StringWriter sw = new StringWriter(512); 728 PrintWriter pw = new PrintWriter(sw); 729 String prefix = " "; 730 String factTableName = getFactTableName(); 731 SqlQuery sqlQuery = getSqlQuery(); 732 733 pw.print("INSERT INTO "); 734 pw.print(makeCollapsedAggregateTableName(getFactTableName())); 735 pw.println(" ("); 736 737 738 for (List<JdbcSchema.Table.Column.Usage> list : collapsedColumnUsages.values()) { 739 for (JdbcSchema.Table.Column.Usage usage : list) { 740 JdbcSchema.Table.Column c = usage.getColumn(); 741 pw.print(prefix); 742 if (usage.usagePrefix != null) { 743 pw.print(usage.usagePrefix); 744 } 745 pw.print(c.getName()); 746 pw.println(','); 747 } 748 } 749 750 for (JdbcSchema.Table.Column.Usage usage : measures) { 751 JdbcSchema.Table.Column c = usage.getColumn(); 752 753 pw.print(prefix); 754 String name = getUsageName(usage); 755 pw.print(name); 756 //pw.print(c.getName()); 757 pw.println(','); 758 } 759 // do fact_count 760 pw.print(prefix); 761 pw.print(getFactCount()); 762 pw.println(")"); 763 764 pw.println("SELECT"); 765 for (List<JdbcSchema.Table.Column.Usage> list : collapsedColumnUsages.values()) { 766 for (JdbcSchema.Table.Column.Usage usage : list) { 767 JdbcSchema.Table.Column c = usage.getColumn(); 768 JdbcSchema.Table t = c.getTable(); 769 770 pw.print(prefix); 771 pw.print(sqlQuery.getDialect().quoteIdentifier(t.getName(), 772 c.getName())); 773 pw.print(" AS "); 774 String n = (usage.usagePrefix == null) 775 ? c.getName() : usage.usagePrefix + c.getName(); 776 pw.print(sqlQuery.getDialect().quoteIdentifier(n)); 777 pw.println(','); 778 } 779 } 780 for (JdbcSchema.Table.Column.Usage usage : measures) { 781 JdbcSchema.Table.Column c = usage.getColumn(); 782 JdbcSchema.Table t = c.getTable(); 783 RolapAggregator agg = usage.getAggregator(); 784 785 pw.print(prefix); 786 pw.print( 787 agg.getExpression(sqlQuery.getDialect().quoteIdentifier( 788 t.getName(), c.getName()))); 789 pw.print(" AS "); 790 pw.print(sqlQuery.getDialect().quoteIdentifier(c.getName())); 791 pw.println(','); 792 } 793 794 // do fact_count 795 pw.print(prefix); 796 pw.print("COUNT(*) AS "); 797 pw.println(sqlQuery.getDialect().quoteIdentifier(getFactCount())); 798 799 pw.println("FROM "); 800 pw.print(prefix); 801 pw.print(sqlQuery.getDialect().quoteIdentifier(factTableName)); 802 pw.print(" "); 803 pw.print(sqlQuery.getDialect().quoteIdentifier(factTableName)); 804 pw.println(','); 805 806 // add dimension tables 807 int k = 0; 808 for (RolapStar.Table rt : collapsedColumnUsages.keySet()) { 809 if (k++ > 0) { 810 pw.println(','); 811 } 812 pw.print(prefix); 813 pw.print(sqlQuery.getDialect().quoteIdentifier(rt.getAlias())); 814 pw.print(" AS "); 815 pw.print(sqlQuery.getDialect().quoteIdentifier(rt.getAlias())); 816 817 // walk up tables 818 if (rt.getParentTable() != null) { 819 while (rt.getParentTable().getParentTable() != null) { 820 rt = rt.getParentTable(); 821 822 pw.println(','); 823 824 pw.print(prefix); 825 pw.print(sqlQuery 826 .getDialect().quoteIdentifier(rt.getAlias())); 827 pw.print(" AS "); 828 pw.print(sqlQuery 829 .getDialect().quoteIdentifier(rt.getAlias())); 830 } 831 } 832 } 833 834 pw.println(); 835 pw.println("WHERE "); 836 k = 0; 837 for (RolapStar.Table rt : collapsedColumnUsages.keySet()) { 838 if (k++ > 0) { 839 pw.println(" and"); 840 } 841 842 RolapStar.Condition cond = rt.getJoinCondition(); 843 if (cond == null) { 844 continue; 845 } 846 pw.print(prefix); 847 pw.print(cond.toString(sqlQuery)); 848 849 if (rt.getParentTable() != null) { 850 while (rt.getParentTable().getParentTable() != null) { 851 rt = rt.getParentTable(); 852 cond = rt.getJoinCondition(); 853 854 pw.println(" and"); 855 856 pw.print(prefix); 857 pw.print(cond.toString(sqlQuery)); 858 } 859 } 860 } 861 862 pw.println(); 863 pw.println("GROUP BY "); 864 k = 0; 865 for (List<JdbcSchema.Table.Column.Usage> list : collapsedColumnUsages.values()) { 866 for (JdbcSchema.Table.Column.Usage usage : list) { 867 if (k++ > 0) { 868 pw.println(","); 869 } 870 JdbcSchema.Table.Column c = usage.getColumn(); 871 JdbcSchema.Table t = c.getTable(); 872 873 String n = (usage.usagePrefix == null) 874 ? c.getName() : usage.usagePrefix + c.getName(); 875 pw.print(prefix); 876 pw.print(sqlQuery.getDialect().quoteIdentifier(t.getName(), n)); 877 } 878 } 879 pw.println(';'); 880 881 return sw.toString(); 882 } 883 884 885 886 private String getUsageName(final JdbcSchema.Table.Column.Usage usage) { 887 JdbcSchema.Table.Column c = usage.getColumn(); 888 String name = c.getName(); 889 // if its a measure which is based upon a foreign key, then 890 // the foreign key column name is already used (for the foreign key 891 // column) so we must choose a different name. 892 if (usage.getUsageType() == JdbcSchema.UsageType.MEASURE) { 893 if (c.hasUsage(JdbcSchema.UsageType.FOREIGN_KEY)) { 894 name = usage.getSymbolicName().replace(' ', '_').toUpperCase(); 895 } 896 } 897 return name; 898 } 899 900 private void addColumnCreate(final PrintWriter pw, 901 final String prefix, 902 final JdbcSchema.Table.Column.Usage usage) { 903 JdbcSchema.Table.Column c = usage.getColumn(); 904 String name = getUsageName(usage); 905 906 pw.print(prefix); 907 if (usage.usagePrefix != null) { 908 pw.print(usage.usagePrefix); 909 } 910 pw.print(name); 911 pw.print(' '); 912 pw.print(c.getTypeName().toUpperCase()); 913 switch (c.getType()) { 914 case Types.NUMERIC: 915 case Types.DECIMAL: 916 pw.print('('); 917 pw.print(c.getNumPrecRadix()); 918 pw.print(","); 919 pw.print(c.getDecimalDigits()); 920 pw.print(')'); 921 break; 922 case Types.CHAR: 923 case Types.VARCHAR: 924 pw.print('('); 925 pw.print(c.getCharOctetLength()); 926 pw.print(')'); 927 break; 928 default: 929 } 930 if (! c.isNullable()) { 931 pw.print(" NOT NULL"); 932 } 933 pw.println(','); 934 935 } 936 } 937 938 // End AggGen.java