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) 2005-2005 Julian Hyde
008// Copyright (C) 2005-2013 Pentaho
009// All Rights Reserved.
010*/
011package mondrian.rolap.agg;
012
013import mondrian.olap.MondrianDef;
014import mondrian.olap.Util;
015import mondrian.rolap.*;
016import mondrian.rolap.sql.SqlQuery;
017import mondrian.util.Pair;
018
019import java.util.*;
020
021/**
022 * Provides the information necessary to generate SQL for a drill-through
023 * request.
024 *
025 * @author jhyde
026 * @author Richard M. Emberson
027 */
028class DrillThroughQuerySpec extends AbstractQuerySpec {
029    private final DrillThroughCellRequest request;
030    private final List<StarPredicate> listOfStarPredicates;
031    private final List<String> columnNames;
032    private final int maxColumnNameLength;
033
034    public DrillThroughQuerySpec(
035        DrillThroughCellRequest request,
036        StarPredicate starPredicateSlicer,
037        boolean countOnly)
038    {
039        super(request.getMeasure().getStar(), countOnly);
040        this.request = request;
041        if (starPredicateSlicer != null) {
042            this.listOfStarPredicates =
043                Collections.singletonList(starPredicateSlicer);
044        } else {
045            this.listOfStarPredicates = Collections.emptyList();
046        }
047        int tmpMaxColumnNameLength =
048            getStar().getSqlQueryDialect().getMaxColumnNameLength();
049        if (tmpMaxColumnNameLength == 0) {
050            // From java.sql.DatabaseMetaData: "a result of zero means that
051            // there is no limit or the limit is not known"
052            maxColumnNameLength = Integer.MAX_VALUE;
053        } else {
054            maxColumnNameLength = tmpMaxColumnNameLength;
055        }
056        this.columnNames = computeDistinctColumnNames();
057    }
058
059    private List<String> computeDistinctColumnNames() {
060        final List<String> columnNames = new ArrayList<String>();
061        final Set<String> columnNameSet = new HashSet<String>();
062
063        final RolapStar.Column[] columns = getColumns();
064        for (RolapStar.Column column : columns) {
065            addColumnName(column, columnNames, columnNameSet);
066        }
067
068        addColumnName(request.getMeasure(), columnNames, columnNameSet);
069
070        return columnNames;
071    }
072
073    private void addColumnName(
074        final RolapStar.Column column,
075        final List<String> columnNames,
076        final Set<String> columnNameSet)
077    {
078        String columnName = makeAlias(column, columnNames, columnNameSet);
079        columnNames.add(columnName);
080    }
081
082    private String makeAlias(
083        final RolapStar.Column column,
084        final List<String> columnNames,
085        final Set<String> columnNameSet)
086    {
087        String columnName = column.getName();
088        if (columnName != null) {
089            // nothing
090        } else if (column.getExpression() instanceof MondrianDef.Column) {
091            columnName = ((MondrianDef.Column) column.getExpression()).name;
092        } else {
093            columnName = "c" + Integer.toString(columnNames.size());
094        }
095        // Register the column name, and if it's not unique, append numeric
096        // suffixes until it is. Also make sure that it is within the
097        // range allowed by this SQL dialect.
098        String originalColumnName = columnName;
099        if (columnName.length() > maxColumnNameLength) {
100            columnName = columnName.substring(0, maxColumnNameLength);
101        }
102        for (int j = 0; !columnNameSet.add(columnName); j++) {
103            final String suffix = "_" + Integer.toString(j);
104            columnName = originalColumnName;
105            if (originalColumnName.length() + suffix.length()
106                > maxColumnNameLength)
107            {
108                columnName =
109                    originalColumnName.substring(
110                        0, maxColumnNameLength - suffix.length());
111            }
112            columnName += suffix;
113        }
114
115        return columnName;
116    }
117
118    @Override
119    protected boolean isPartOfSelect(RolapStar.Column col) {
120        return request.includeInSelect(col);
121    }
122
123    @Override
124    protected boolean isPartOfSelect(RolapStar.Measure measure) {
125        return request.includeInSelect(measure);
126    }
127
128    public int getMeasureCount() {
129        return request.getDrillThroughMeasures().size() > 0
130            ? request.getDrillThroughMeasures().size()
131            : 1;
132    }
133
134    public RolapStar.Measure getMeasure(final int i) {
135        return request.getDrillThroughMeasures().size() > 0
136            ? request.getDrillThroughMeasures().get(i)
137            : request.getMeasure();
138    }
139
140    public String getMeasureAlias(final int i) {
141        return request.getDrillThroughMeasures().size() > 0
142            ? request.getDrillThroughMeasures().get(i).getName()
143            : columnNames.get(columnNames.size() - 1);
144    }
145
146    public RolapStar.Column[] getColumns() {
147        return request.getConstrainedColumns();
148    }
149
150    public String getColumnAlias(final int i) {
151        return columnNames.get(i);
152    }
153
154    public StarColumnPredicate getColumnPredicate(final int i) {
155        final StarColumnPredicate constr = request.getValueAt(i);
156        return (constr == null)
157            ? LiteralStarPredicate.TRUE
158            : constr;
159    }
160
161    public Pair<String, List<SqlStatement.Type>> generateSqlQuery() {
162        SqlQuery sqlQuery = newSqlQuery();
163        nonDistinctGenerateSql(sqlQuery);
164        return sqlQuery.toSqlAndTypes();
165    }
166
167    protected void addMeasure(final int i, final SqlQuery sqlQuery) {
168        RolapStar.Measure measure = getMeasure(i);
169
170        if (!isPartOfSelect(measure)) {
171            return;
172        }
173
174        Util.assertTrue(measure.getTable() == getStar().getFactTable());
175        measure.getTable().addToFrom(sqlQuery, false, true);
176
177        if (!countOnly) {
178            String expr = measure.generateExprString(sqlQuery);
179            sqlQuery.addSelect(expr, null, getMeasureAlias(i));
180        }
181    }
182
183    protected boolean isAggregate() {
184        return false;
185    }
186
187    protected boolean isOrdered() {
188        return true;
189    }
190
191    protected List<StarPredicate> getPredicateList() {
192        return listOfStarPredicates;
193    }
194
195    protected void extraPredicates(SqlQuery sqlQuery) {
196        super.extraPredicates(sqlQuery);
197
198        if (countOnly) {
199            return;
200        }
201        // generate the select list
202        final Set<String> columnNameSet = new HashSet<String>();
203        columnNameSet.addAll(columnNames);
204
205        List<StarPredicate> predicateList = getPredicateList();
206        for (StarPredicate predicate : predicateList) {
207            for (RolapStar.Column column
208                : predicate.getConstrainedColumnList())
209            {
210                sqlQuery.addSelect(
211                    column.generateExprString(sqlQuery),
212                    column.getInternalType(),
213                    makeAlias(column, columnNames, columnNameSet));
214            }
215        }
216    }
217}
218
219// End DrillThroughQuerySpec.java