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) 2002-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho and others
009// All Rights Reserved.
010//
011// jhyde, 21 March, 2002
012*/
013package mondrian.rolap.agg;
014
015import mondrian.rolap.RolapStar;
016import mondrian.rolap.SqlStatement.Type;
017import mondrian.rolap.StarColumnPredicate;
018import mondrian.rolap.aggmatcher.AggStar;
019import mondrian.rolap.sql.SqlQuery;
020import mondrian.util.Pair;
021
022import org.apache.log4j.Logger;
023
024import java.util.ArrayList;
025import java.util.List;
026
027/**
028 * An AggStar's version of the {@link QuerySpec}. <p/>
029 *
030 * When/if the {@link AggStar} code is merged into {@link RolapStar}
031 * (or RolapStar is merged into AggStar}, then this, indeed, can implement the
032 * {@link QuerySpec} interface.
033 *
034 * @author Richard M. Emberson
035 */
036class AggQuerySpec {
037    private static final Logger LOGGER = Logger.getLogger(AggQuerySpec.class);
038
039    private final AggStar aggStar;
040    private final List<Segment> segments;
041    private final Segment segment0;
042    private final boolean rollup;
043    private final GroupingSetsList groupingSetsList;
044
045    AggQuerySpec(
046        final AggStar aggStar,
047        final boolean rollup,
048        GroupingSetsList groupingSetsList)
049    {
050        this.aggStar = aggStar;
051        this.segments = groupingSetsList.getDefaultSegments();
052        this.segment0 = segments.get(0);
053        this.rollup = rollup;
054        this.groupingSetsList = groupingSetsList;
055    }
056
057    protected SqlQuery newSqlQuery() {
058        return getStar().getSqlQuery();
059    }
060
061    public RolapStar getStar() {
062        return aggStar.getStar();
063    }
064
065    public int getMeasureCount() {
066        return segments.size();
067    }
068
069    public AggStar.FactTable.Column getMeasureAsColumn(final int i) {
070        int bitPos = segments.get(i).measure.getBitPosition();
071        return aggStar.lookupColumn(bitPos);
072    }
073
074    public String getMeasureAlias(final int i) {
075        return "m" + Integer.toString(i);
076    }
077
078    public int getColumnCount() {
079        return segment0.getColumns().length;
080    }
081
082    public AggStar.Table.Column getColumn(final int i) {
083        RolapStar.Column[] columns = segment0.getColumns();
084        int bitPos = columns[i].getBitPosition();
085        AggStar.Table.Column column = aggStar.lookupColumn(bitPos);
086
087        // this should never happen
088        if (column == null) {
089            LOGGER.error("column null for bitPos=" + bitPos);
090        }
091        return column;
092    }
093
094    public String getColumnAlias(final int i) {
095        return "c" + Integer.toString(i);
096    }
097
098    /**
099     * Returns the predicate on the <code>i</code>th column.
100     *
101     * <p>If the column is unconstrained, returns
102     * {@link LiteralStarPredicate}(true).
103     *
104     * @param i Column ordinal
105     * @return Constraint on column
106     */
107    public StarColumnPredicate getPredicate(int i) {
108        return segment0.predicates[i];
109    }
110
111    public Pair<String, List<Type>> generateSqlQuery() {
112        SqlQuery sqlQuery = newSqlQuery();
113        generateSql(sqlQuery);
114        return sqlQuery.toSqlAndTypes();
115    }
116
117    private void addGroupingSets(SqlQuery sqlQuery) {
118        List<RolapStar.Column[]> groupingSetsColumns =
119            groupingSetsList.getGroupingSetsColumns();
120        for (RolapStar.Column[] groupingSetColumns : groupingSetsColumns) {
121            ArrayList<String> groupingColumnsExpr = new ArrayList<String>();
122
123            for (RolapStar.Column aColumnArr : groupingSetColumns) {
124                groupingColumnsExpr.add(findColumnExpr(aColumnArr, sqlQuery));
125            }
126            sqlQuery.addGroupingSet(groupingColumnsExpr);
127        }
128    }
129
130    private String findColumnExpr(RolapStar.Column columnj, SqlQuery sqlQuery) {
131        AggStar.Table.Column column =
132            aggStar.lookupColumn(columnj.getBitPosition());
133        return column.generateExprString(sqlQuery);
134    }
135
136    protected void addMeasure(final int i, final SqlQuery query) {
137        AggStar.FactTable.Measure column =
138                (AggStar.FactTable.Measure) getMeasureAsColumn(i);
139
140        column.getTable().addToFrom(query, false, true);
141        String alias = getMeasureAlias(i);
142
143        String expr;
144        if (rollup) {
145            expr = column.generateRollupString(query);
146        } else {
147            expr = column.generateExprString(query);
148        }
149        query.addSelect(expr, null, alias);
150    }
151
152    protected void generateSql(final SqlQuery sqlQuery) {
153        // add constraining dimensions
154        int columnCnt = getColumnCount();
155        for (int i = 0; i < columnCnt; i++) {
156            AggStar.Table.Column column = getColumn(i);
157            AggStar.Table table = column.getTable();
158            table.addToFrom(sqlQuery, false, true);
159
160            String expr = column.generateExprString(sqlQuery);
161
162            StarColumnPredicate predicate = getPredicate(i);
163            final String where = RolapStar.Column.createInExpr(
164                expr,
165                predicate,
166                column.getDatatype(),
167                sqlQuery);
168            if (!where.equals("true")) {
169                sqlQuery.addWhere(where);
170            }
171
172            // some DB2 (AS400) versions throw an error, if a column alias is
173            // there and *not* used in a subsequent order by/group by
174            final String alias0;
175            switch (sqlQuery.getDialect().getDatabaseProduct()) {
176            case DB2_AS400:
177            case DB2_OLD_AS400:
178                alias0 = null;
179                break;
180            default:
181                alias0 = getColumnAlias(i);
182                break;
183            }
184
185            final String alias =
186                sqlQuery.addSelect(expr, column.getInternalType(), alias0);
187            if (rollup) {
188                sqlQuery.addGroupBy(expr, alias);
189            }
190        }
191
192        // Add measures.
193        // This can also add non-shared local dimension columns, which are
194        // not measures.
195        for (int i = 0, count = getMeasureCount(); i < count; i++) {
196            addMeasure(i, sqlQuery);
197        }
198        addGroupingSets(sqlQuery);
199        addGroupingFunction(sqlQuery);
200    }
201
202    private void addGroupingFunction(SqlQuery sqlQuery) {
203        List<RolapStar.Column> list = groupingSetsList.getRollupColumns();
204        for (RolapStar.Column column : list) {
205            sqlQuery.addGroupingFunction(findColumnExpr(column, sqlQuery));
206        }
207    }
208}
209
210// End AggQuerySpec.java