001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2012-2012 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.rolap; 011 012import mondrian.olap.MondrianDef; 013import mondrian.rolap.sql.SqlQuery; 014import mondrian.server.Execution; 015import mondrian.spi.Dialect; 016import mondrian.spi.StatisticsProvider; 017 018import java.util.*; 019import javax.sql.DataSource; 020 021/** 022 * Provides and caches statistics. 023 * 024 * <p>Wrapper around a chain of {@link mondrian.spi.StatisticsProvider}s, 025 * followed by a cache to store the results.</p> 026 */ 027public class RolapStatisticsCache { 028 private final RolapStar star; 029 private final Map<List, Integer> columnMap = new HashMap<List, Integer>(); 030 private final Map<List, Integer> tableMap = new HashMap<List, Integer>(); 031 private final Map<String, Integer> queryMap = 032 new HashMap<String, Integer>(); 033 034 public RolapStatisticsCache(RolapStar star) { 035 this.star = star; 036 } 037 038 public int getRelationCardinality( 039 MondrianDef.Relation relation, 040 String alias, 041 int approxRowCount) 042 { 043 if (approxRowCount >= 0) { 044 return approxRowCount; 045 } 046 if (relation instanceof MondrianDef.Table) { 047 final MondrianDef.Table table = (MondrianDef.Table) relation; 048 return getTableCardinality( 049 null, table.schema, table.name); 050 } else { 051 final SqlQuery sqlQuery = star.getSqlQuery(); 052 sqlQuery.addSelect("*", null); 053 sqlQuery.addFrom(relation, null, true); 054 return getQueryCardinality(sqlQuery.toString()); 055 } 056 } 057 058 private int getTableCardinality( 059 String catalog, 060 String schema, 061 String table) 062 { 063 final List<String> key = Arrays.asList(catalog, schema, table); 064 int rowCount = -1; 065 if (tableMap.containsKey(key)) { 066 rowCount = tableMap.get(key); 067 } else { 068 final Dialect dialect = star.getSqlQueryDialect(); 069 final List<StatisticsProvider> statisticsProviders = 070 dialect.getStatisticsProviders(); 071 final Execution execution = 072 new Execution( 073 star.getSchema().getInternalConnection() 074 .getInternalStatement(), 075 0); 076 for (StatisticsProvider statisticsProvider : statisticsProviders) { 077 rowCount = statisticsProvider.getTableCardinality( 078 dialect, 079 star.getDataSource(), 080 catalog, 081 schema, 082 table, 083 execution); 084 if (rowCount >= 0) { 085 break; 086 } 087 } 088 089 // Note: If all providers fail, we put -1 into the cache, to ensure 090 // that we won't try again. 091 tableMap.put(key, rowCount); 092 } 093 return rowCount; 094 } 095 096 private int getQueryCardinality(String sql) { 097 int rowCount = -1; 098 if (queryMap.containsKey(sql)) { 099 rowCount = queryMap.get(sql); 100 } else { 101 final Dialect dialect = star.getSqlQueryDialect(); 102 final List<StatisticsProvider> statisticsProviders = 103 dialect.getStatisticsProviders(); 104 final Execution execution = 105 new Execution( 106 star.getSchema().getInternalConnection() 107 .getInternalStatement(), 108 0); 109 for (StatisticsProvider statisticsProvider : statisticsProviders) { 110 rowCount = statisticsProvider.getQueryCardinality( 111 dialect, star.getDataSource(), sql, execution); 112 if (rowCount >= 0) { 113 break; 114 } 115 } 116 117 // Note: If all providers fail, we put -1 into the cache, to ensure 118 // that we won't try again. 119 queryMap.put(sql, rowCount); 120 } 121 return rowCount; 122 } 123 124 public int getColumnCardinality( 125 MondrianDef.Relation relation, 126 MondrianDef.Expression expression, 127 int approxCardinality) 128 { 129 if (approxCardinality >= 0) { 130 return approxCardinality; 131 } 132 if (relation instanceof MondrianDef.Table 133 && expression instanceof MondrianDef.Column) 134 { 135 final MondrianDef.Table table = (MondrianDef.Table) relation; 136 final MondrianDef.Column column = (MondrianDef.Column) expression; 137 return getColumnCardinality( 138 null, 139 table.schema, 140 table.name, 141 column.name); 142 } else { 143 final SqlQuery sqlQuery = star.getSqlQuery(); 144 sqlQuery.setDistinct(true); 145 sqlQuery.addSelect(expression.getExpression(sqlQuery), null); 146 sqlQuery.addFrom(relation, null, true); 147 return getQueryCardinality(sqlQuery.toString()); 148 } 149 } 150 151 private int getColumnCardinality( 152 String catalog, 153 String schema, 154 String table, 155 String column) 156 { 157 final List<String> key = Arrays.asList(catalog, schema, table, column); 158 int rowCount = -1; 159 if (columnMap.containsKey(key)) { 160 rowCount = columnMap.get(key); 161 } else { 162 final Dialect dialect = star.getSqlQueryDialect(); 163 final List<StatisticsProvider> statisticsProviders = 164 dialect.getStatisticsProviders(); 165 final Execution execution = 166 new Execution( 167 star.getSchema().getInternalConnection() 168 .getInternalStatement(), 169 0); 170 for (StatisticsProvider statisticsProvider : statisticsProviders) { 171 rowCount = statisticsProvider.getColumnCardinality( 172 dialect, 173 star.getDataSource(), 174 catalog, 175 schema, 176 table, 177 column, 178 execution); 179 if (rowCount >= 0) { 180 break; 181 } 182 } 183 184 // Note: If all providers fail, we put -1 into the cache, to ensure 185 // that we won't try again. 186 columnMap.put(key, rowCount); 187 } 188 return rowCount; 189 } 190 191 public int getColumnCardinality2( 192 DataSource dataSource, 193 Dialect dialect, 194 String catalog, 195 String schema, 196 String table, 197 String column) 198 { 199 return -1; 200 } 201} 202 203// End RolapStatisticsCache.java