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