001 /* 002 // $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/ExplicitRecognizer.java#18 $ 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.Hierarchy; 014 import mondrian.olap.Util; 015 import mondrian.olap.Id; 016 import mondrian.recorder.MessageRecorder; 017 import mondrian.rolap.*; 018 019 import java.util.Iterator; 020 import java.util.List; 021 022 /** 023 * This is the Recognizer for the aggregate table descriptions that appear in 024 * the catalog schema files; the user explicitly defines the aggregate. 025 * 026 * @author Richard M. Emberson 027 * @version $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/ExplicitRecognizer.java#18 $ 028 */ 029 class ExplicitRecognizer extends Recognizer { 030 private ExplicitRules.TableDef tableDef; 031 private RolapCube cube; 032 033 ExplicitRecognizer( 034 final ExplicitRules.TableDef tableDef, 035 final RolapStar star, 036 RolapCube cube, 037 final JdbcSchema.Table dbFactTable, 038 final JdbcSchema.Table aggTable, 039 final MessageRecorder msgRecorder) 040 { 041 super(star, dbFactTable, aggTable, msgRecorder); 042 this.tableDef = tableDef; 043 this.cube = cube; 044 } 045 046 /** 047 * Get the ExplicitRules.TableDef associated with this instance. 048 */ 049 protected ExplicitRules.TableDef getTableDef() { 050 return tableDef; 051 } 052 053 /** 054 * Get the Matcher to be used to match columns to be ignored. 055 */ 056 protected Recognizer.Matcher getIgnoreMatcher() { 057 return getTableDef().getIgnoreMatcher(); 058 } 059 060 /** 061 * Get the Matcher to be used to match the column which is the fact count 062 * column. 063 */ 064 protected Recognizer.Matcher getFactCountMatcher() { 065 return getTableDef().getFactCountMatcher(); 066 } 067 068 /** 069 * Make the measures for this aggregate table. 070 * <p> 071 * First, iterate through all of the columns in the table. 072 * For each column, iterate through all of the tableDef measures, the 073 * explicit definitions of a measure. 074 * If the table's column name matches the column name in the measure 075 * definition, then make a measure. 076 * Next, look through all of the fact table column usage measures. 077 * For each such measure usage that has a sibling foreign key usage 078 * see if the tableDef has a foreign key defined with the same name. 079 * If so, then, for free, we can make a measure for the aggregate using 080 * its foreign key. 081 * <p> 082 * 083 * @return number of measures created. 084 */ 085 protected int checkMeasures() { 086 msgRecorder.pushContextName("ExplicitRecognizer.checkMeasures"); 087 try { 088 089 int measureColumnCounts = 0; 090 // Look at each aggregate table column. For each measure defined, 091 // see if the measure's column name equals the column's name. 092 // If so, make the aggregate measure usage for that column. 093 for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) { 094 // if marked as ignore, then do not consider 095 if (aggColumn.hasUsage(JdbcSchema.UsageType.IGNORE)) { 096 continue; 097 } 098 099 String aggColumnName = aggColumn.getName(); 100 101 for (ExplicitRules.TableDef.Measure measure : 102 getTableDef().getMeasures()) 103 { 104 // Column name match is case insensitive 105 if (measure.getColumnName().equalsIgnoreCase(aggColumnName)) { 106 String name = measure.getName(); 107 List<Id.Segment> parts = Util.parseIdentifier(name); 108 String nameLast = parts.get(parts.size() - 1).name; 109 110 RolapStar.Measure m = 111 star.getFactTable().lookupMeasureByName( 112 cube.getName(), nameLast); 113 RolapAggregator agg = null; 114 if (m != null) { 115 agg = m.getAggregator(); 116 } 117 // Ok, got a match, so now make a measure 118 makeMeasure(measure, agg, aggColumn); 119 measureColumnCounts++; 120 } 121 } 122 } 123 // Ok, now look at all of the fact table columns with measure usage 124 // that have a sibling foreign key usage. These can be automagically 125 // generated for the aggregate table as long as it still has the 126 // foreign key. 127 for (Iterator<JdbcSchema.Table.Column.Usage> it = 128 dbFactTable.getColumnUsages(JdbcSchema.UsageType.MEASURE); 129 it.hasNext();) { 130 JdbcSchema.Table.Column.Usage factUsage = it.next(); 131 JdbcSchema.Table.Column factColumn = factUsage.getColumn(); 132 133 if (factColumn.hasUsage(JdbcSchema.UsageType.FOREIGN_KEY)) { 134 // What we've got here is a measure based upon a foreign key 135 String aggFK = 136 getTableDef().getAggregateFK(factColumn.getName()); 137 // OK, not a lost dimension 138 if (aggFK != null) { 139 JdbcSchema.Table.Column aggColumn = 140 aggTable.getColumn(aggFK); 141 142 // Column name match is case insensitive 143 if (aggColumn == null) { 144 aggColumn = aggTable.getColumn(aggFK.toLowerCase()); 145 } 146 if (aggColumn == null) { 147 aggColumn = aggTable.getColumn(aggFK.toUpperCase()); 148 } 149 150 if (aggColumn != null) { 151 makeMeasure(factUsage, aggColumn); 152 measureColumnCounts++; 153 } 154 } 155 } 156 } 157 return measureColumnCounts; 158 159 } finally { 160 msgRecorder.popContextName(); 161 } 162 } 163 164 /** 165 * Make a measure. This makes a measure usage using the Aggregator found in 166 * the RolapStar.Measure associated with the ExplicitRules.TableDef.Measure. 167 * 168 * @param measure 169 * @param aggColumn 170 */ 171 protected void makeMeasure( 172 final ExplicitRules.TableDef.Measure measure, 173 RolapAggregator factAgg, 174 final JdbcSchema.Table.Column aggColumn) { 175 RolapStar.Measure rm = measure.getRolapStarMeasure(); 176 177 JdbcSchema.Table.Column.Usage aggUsage = 178 aggColumn.newUsage(JdbcSchema.UsageType.MEASURE); 179 180 aggUsage.setSymbolicName(measure.getSymbolicName()); 181 RolapAggregator ra = (factAgg == null) 182 ? convertAggregator(aggUsage, rm.getAggregator()) 183 : convertAggregator(aggUsage, factAgg, rm.getAggregator()); 184 aggUsage.setAggregator(ra); 185 186 aggUsage.rMeasure = rm; 187 } 188 189 /** 190 * This creates a foreign key usage. 191 * 192 * <p> First the column name of the fact usage which is a foreign key is used to 193 * search for a foreign key definition in the ExplicitRules.tableDef. 194 * If not found, thats ok, it is just a lost dimension. 195 * If found, look for a column in the aggregate table with that name and 196 * make a foreign key usage. 197 */ 198 protected int matchForeignKey( 199 final JdbcSchema.Table.Column.Usage factUsage) { 200 JdbcSchema.Table.Column factColumn = factUsage.getColumn(); 201 String aggFK = getTableDef().getAggregateFK(factColumn.getName()); 202 203 // OK, a lost dimension 204 if (aggFK == null) { 205 return 0; 206 } 207 208 int matchCount = 0; 209 for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) { 210 // if marked as ignore, then do not consider 211 if (aggColumn.hasUsage(JdbcSchema.UsageType.IGNORE)) { 212 continue; 213 } 214 215 if (aggFK.equals(aggColumn.getName())) { 216 makeForeignKey(factUsage, aggColumn, aggFK); 217 matchCount++; 218 } 219 } 220 return matchCount; 221 222 } 223 224 /** 225 * This creates a level usage. A level usage is a column that is used in a 226 * collapsed dimension aggregate table. 227 * 228 * <p> First, iterate through the ExplicitRules.TableDef's level 229 * definitions for one with a name equal to the RolapLevel unique name, 230 * i.e., [Time].[Quarter]. Now, using the level's column name, search 231 * through the aggregate table's columns for one with that name and make a 232 * level usage for the column. Return true if the aggregate table's column 233 * was found. 234 */ 235 protected boolean matchLevel( 236 final Hierarchy hierarchy, 237 final HierarchyUsage hierarchyUsage, 238 final RolapLevel rlevel) { 239 msgRecorder.pushContextName("ExplicitRecognizer.matchLevel"); 240 try { 241 242 // Try to match a Level's name against the RolapLevel unique name. 243 String levelUniqueName = rlevel.getUniqueName(); 244 for (ExplicitRules.TableDef.Level level : getTableDef().getLevels()) { 245 if (level.getName().equals(levelUniqueName)) { 246 // Ok, got a match, so now make a measue 247 //makeLevel(level, xxxxolumn); 248 // Now can we find a column in the aggTable that matches the 249 // Level's column 250 String columnName = level.getColumnName(); 251 for (JdbcSchema.Table.Column aggColumn : aggTable.getColumns()) { 252 if (aggColumn.getName().equals(columnName)) { 253 makeLevel( 254 aggColumn, 255 hierarchy, 256 hierarchyUsage, 257 getColumnName(rlevel.getKeyExp()), 258 columnName, 259 rlevel.getName()); 260 return true; 261 } 262 } 263 264 } 265 } 266 return false; 267 268 } finally { 269 msgRecorder.popContextName(); 270 } 271 } 272 } 273 274 // End ExplicitRecognizer.java