001    /*
002    // This software is subject to the terms of the Common Public License
003    // Agreement, available at the following URL:
004    // http://www.opensource.org/licenses/cpl.html.
005    // Copyright (C) 2004-2005 TONBELLER AG
006    // All Rights Reserved.
007    // You must accept the terms of that agreement to use this software.
008    */
009    package mondrian.rolap;
010    
011    import java.util.ArrayList;
012    import java.util.List;
013    
014    import javax.sql.DataSource;
015    
016    import mondrian.olap.*;
017    import mondrian.rolap.sql.SqlQuery;
018    import mondrian.rolap.sql.TupleConstraint;
019    import mondrian.rolap.sql.SqlQuery.Dialect;
020    
021    /**
022     * Computes a TopCount in SQL.
023     *
024     * @author av
025     * @since Nov 21, 2005
026     * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapNativeTopCount.java#22 $
027     */
028    public class RolapNativeTopCount extends RolapNativeSet {
029    
030        public RolapNativeTopCount() {
031            super.setEnabled(MondrianProperties.instance().EnableNativeTopCount.get());
032        }
033    
034        static class TopCountConstraint extends SetConstraint {
035            String orderByExpr;
036            boolean ascending;
037    
038            public TopCountConstraint(
039                CrossJoinArg[] args, RolapEvaluator evaluator,
040                String orderByExpr, boolean ascending) {
041                super(args, evaluator, true);
042                this.orderByExpr = orderByExpr;
043                this.ascending = ascending;
044            }
045    
046            /**
047             * we alwas need to join the fact table because we want to evalutate
048             * the top count expression which involves a fact.
049             */
050            protected boolean isJoinRequired() {
051                return true;
052            }
053    
054            public void addConstraint(SqlQuery sqlQuery, RolapCube baseCube) {
055                if (orderByExpr != null) {
056                    Dialect dialect = sqlQuery.getDialect();
057                    if (dialect.requiresOrderByAlias()) {
058                        String alias = sqlQuery.nextColumnAlias();
059                        alias = dialect.quoteIdentifier(alias);
060                        sqlQuery.addSelect(orderByExpr, alias);
061                        sqlQuery.addOrderBy(alias, ascending, true, false);
062                    } else {
063                        sqlQuery.addOrderBy(orderByExpr, ascending, true, false);
064                    }
065                }
066                super.addConstraint(sqlQuery, baseCube);
067            }
068    
069            public Object getCacheKey() {
070                List<Object> key = new ArrayList<Object>();
071                key.add(super.getCacheKey());
072                key.add(orderByExpr);
073                key.add(ascending);
074                return key;
075            }
076        }
077    
078        protected boolean restrictMemberTypes() {
079            return true;
080        }
081    
082        NativeEvaluator createEvaluator(RolapEvaluator evaluator, FunDef fun, Exp[] args) {
083            boolean ascending;
084    
085            if (!isEnabled())
086                return null;
087            if (!TopCountConstraint.isValidContext(evaluator)) {
088                return null;
089            }
090    
091            // is this "TopCount(<set>, <count>, [<numeric expr>])"
092            String funName = fun.getName();
093            if ("TopCount".equalsIgnoreCase(funName)) {
094                ascending = false;
095            } else if ("BottomCount".equalsIgnoreCase(funName)) {
096                ascending = true;
097            } else {
098                return null;
099            }
100            if (args.length < 2 || args.length > 3) {
101                return null;
102            }
103            // extract the set expression
104            CrossJoinArg[] cargs = checkCrossJoinArg(evaluator, args[0]);
105            if (cargs == null) {
106                return null;
107            }
108            if (isPreferInterpreter(cargs, false)) {
109                return null;
110            }
111    
112            // extract count
113            if (!(args[1] instanceof Literal)) {
114                return null;
115            }
116            int count = ((Literal) args[1]).getIntValue();
117    
118            // extract "order by" expression
119            SchemaReader schemaReader = evaluator.getSchemaReader();
120            DataSource ds = schemaReader.getDataSource();
121    
122            // generate the ORDER BY Clause
123            SqlQuery sqlQuery = SqlQuery.newQuery(ds, "NativeTopCount");
124            RolapNativeSql sql = new RolapNativeSql(sqlQuery);
125            String orderByExpr = null;
126            if (args.length == 3) {
127                orderByExpr = sql.generateTopCountOrderBy(args[2]);
128                if (orderByExpr == null) {
129                    return null;
130                }
131            }
132            LOGGER.debug("using native topcount");
133            evaluator = overrideContext(evaluator, cargs, sql.getStoredMeasure());
134    
135            TupleConstraint constraint =
136                new TopCountConstraint(cargs, evaluator, orderByExpr, ascending);
137            SetEvaluator sev = new SetEvaluator(cargs, schemaReader, constraint);
138            sev.setMaxRows(count);
139            return sev;
140        }
141    }
142    
143    // End RolapNativeTopCount.java