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.spi.impl; 011 012import mondrian.olap.Util; 013import mondrian.server.Execution; 014import mondrian.spi.Dialect; 015import mondrian.spi.StatisticsProvider; 016 017import org.apache.log4j.Logger; 018 019import java.sql.*; 020import javax.sql.DataSource; 021 022/** 023 * Implementation of {@link mondrian.spi.StatisticsProvider} that uses JDBC 024 * metadata calls to count rows and distinct values. 025 */ 026public class JdbcStatisticsProvider implements StatisticsProvider { 027 private static final Logger LOG = 028 Logger.getLogger(JdbcStatisticsProvider.class); 029 public int getTableCardinality( 030 Dialect dialect, 031 DataSource dataSource, 032 String catalog, 033 String schema, 034 String table, 035 Execution execution) 036 { 037 Connection connection = null; 038 ResultSet resultSet = null; 039 try { 040 connection = dataSource.getConnection(); 041 resultSet = 042 connection 043 .getMetaData() 044 .getIndexInfo(catalog, schema, table, false, true); 045 int maxNonUnique = -1; 046 while (resultSet.next()) { 047 final int type = resultSet.getInt(7); // "TYPE" column 048 final int cardinality = resultSet.getInt(11); 049 final boolean unique = 050 !resultSet.getBoolean(4); // "NON_UNIQUE" column 051 switch (type) { 052 case DatabaseMetaData.tableIndexStatistic: 053 return cardinality; // "CARDINALITY" column 054 } 055 if (!unique) { 056 maxNonUnique = Math.max(maxNonUnique, cardinality); 057 } 058 } 059 // The cardinality of each non-unique index will be the number of 060 // non-NULL values in that index. Unless we're unlucky, one of those 061 // columns will cover most of the table. 062 return maxNonUnique; 063 } catch (SQLException e) { 064 // We will have to try a count() operation or some other 065 // statistics provider in the chain. 066 if (LOG.isDebugEnabled()) { 067 LOG.debug( 068 "JdbcStatisticsProvider failed to get the cardinality of the table " 069 + table, 070 e); 071 } 072 return -1; 073 } finally { 074 Util.close(resultSet, null, connection); 075 } 076 } 077 078 public int getQueryCardinality( 079 Dialect dialect, 080 DataSource dataSource, 081 String sql, 082 Execution execution) 083 { 084 // JDBC cannot help with this. Defer to another statistics provider. 085 return -1; 086 } 087 088 public int getColumnCardinality( 089 Dialect dialect, 090 DataSource dataSource, 091 String catalog, 092 String schema, 093 String table, 094 String column, 095 Execution execution) 096 { 097 Connection connection = null; 098 ResultSet resultSet = null; 099 try { 100 connection = dataSource.getConnection(); 101 resultSet = 102 connection 103 .getMetaData() 104 .getIndexInfo(catalog, schema, table, false, true); 105 while (resultSet.next()) { 106 int type = resultSet.getInt(7); // "TYPE" column 107 switch (type) { 108 case DatabaseMetaData.tableIndexStatistic: 109 continue; 110 default: 111 String columnName = resultSet.getString(9); //COLUMN_NAME 112 if (columnName != null && columnName.equals(column)) { 113 return resultSet.getInt(11); // "CARDINALITY" column 114 } 115 } 116 } 117 return -1; // information not available, apparently 118 } catch (SQLException e) { 119 // We will have to try a count() operation or some other 120 // statistics provider in the chain. 121 if (LOG.isDebugEnabled()) { 122 LOG.debug( 123 "JdbcStatisticsProvider failed to get the cardinality of the table " 124 + table, 125 e); 126 } 127 return -1; 128 } finally { 129 Util.close(resultSet, null, connection); 130 } 131 } 132} 133 134// End JdbcStatisticsProvider.java