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