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) 2003-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho
009// All Rights Reserved.
010//
011// jhyde, Feb 21, 2003
012*/
013package mondrian.rolap;
014
015import mondrian.calc.TupleList;
016import mondrian.olap.Util;
017
018import java.sql.SQLException;
019import java.util.ArrayList;
020import java.util.List;
021
022/**
023 * Loader to be iterated to load all results from database.
024 *
025 * @author luis f. canals
026 */
027public class ResultLoader {
028    private final List<TargetBase> targets;
029    private final int enumTargetCount;
030    private final boolean execQuery;
031    private final String message;
032    private final TupleList partialResult;
033    private final List<List<RolapMember>> newPartialResult;
034    private final SqlStatement stmt;
035
036    private final int[] srcMemberIdxes;
037
038    int currPartialResultIdx = 0;
039
040    public ResultLoader(
041        final int enumTargetCount,
042        final List<TargetBase> targets,
043        final SqlStatement stmt,
044        final boolean execQuery,
045        final TupleList partialResult,
046        final List<List<RolapMember>> newPartialResult)
047        throws SQLException
048    {
049        assert (stmt != null) == execQuery;
050        this.targets = targets;
051        this.enumTargetCount = enumTargetCount;
052        this.stmt = stmt;
053        this.execQuery = execQuery;
054        this.partialResult = partialResult;
055        this.newPartialResult = newPartialResult;
056        this.srcMemberIdxes =
057            enumTargetCount > 0
058                ? new int[enumTargetCount]
059                : null;
060        this.message = "Populating member cache with members for " + targets;
061    }
062
063
064    public boolean loadResult() throws SQLException {
065/*
066        if (limit > 0 && limit < ++fetchCount) {
067            throw MondrianResource.instance().MemberFetchLimitExceeded
068                    .ex((long) limit);
069        }
070*/
071        if (enumTargetCount == 0) {
072            int column = 0;
073            for (TargetBase target : targets) {
074                target.removeCurrMember();
075                column = target.addRow(stmt, column);
076            }
077        } else {
078            int firstEnumTarget = 0;
079            for (; firstEnumTarget < targets.size(); firstEnumTarget++) {
080                if (targets.get(firstEnumTarget).getSrcMembers() != null) {
081                    break;
082                }
083            }
084            List<RolapMember> partialRow;
085            if (execQuery) {
086                partialRow = null;
087            } else {
088                partialRow = Util.cast(partialResult.get(currPartialResultIdx));
089            }
090            resetCurrMembers(partialRow);
091            addTargets(
092                0, firstEnumTarget, enumTargetCount, srcMemberIdxes, message);
093            if (newPartialResult != null) {
094                savePartialResult(newPartialResult);
095            }
096        }
097
098        boolean moreRows;
099        if (execQuery) {
100            moreRows = stmt.getResultSet().next();
101            if (moreRows) {
102                ++stmt.rowCount;
103            }
104        } else {
105            currPartialResultIdx++;
106            moreRows = currPartialResultIdx < partialResult.size();
107        }
108        return moreRows;
109    }
110
111
112    /**
113     * Closes internal statement.
114     */
115    public void close() {
116        if (this.stmt != null) {
117            this.stmt.close();
118        }
119    }
120
121
122    /**
123     * Handles an error, and returns an exception that the caller should then
124     * throw.
125     *
126     * @param e Exception
127     * @return Wrapper exception
128     */
129    public RuntimeException handle(Exception e) {
130        if (stmt != null) {
131            return stmt.handle(e);
132        } else {
133            return Util.newError(e, message);
134        }
135    }
136
137    //
138    // Private stuff -------------------------------
139    //
140
141    /**
142     * Sets the current member for those targets that retrieve their column
143     * values from native sql.
144     *
145     * @param partialRow if set, previously cached result set
146     */
147    private void resetCurrMembers(List<RolapMember> partialRow) {
148        int nativeTarget = 0;
149        for (TargetBase target : targets) {
150            if (target.getSrcMembers() == null) {
151                if (partialRow != null) {
152                    target.setCurrMember(partialRow.get(nativeTarget++));
153                } else {
154                    target.removeCurrMember();
155                }
156            }
157        }
158    }
159
160    /**
161     * Recursively forms the cross product of a row retrieved through sql
162     * with each of the targets that contains an enumerated set of members.
163     *
164     * @param currEnumTargetIdx current enum target that recursion
165     * is being applied on
166     * @param currTargetIdx index within the list of a targets that
167     * currEnumTargetIdx corresponds to
168     * @param nEnumTargets number of targets that have enumerated members
169     * @param srcMemberIdxes for each enumerated target, the current member
170     * to be retrieved to form the current cross product row
171     * @param message Message to issue on failure
172     */
173    private void addTargets(
174        int currEnumTargetIdx,
175        int currTargetIdx,
176        int nEnumTargets,
177        int[] srcMemberIdxes,
178        String message)
179    {
180        TargetBase currTarget = targets.get(currTargetIdx);
181        for (int i = 0; i < currTarget.getSrcMembers().size(); i++) {
182            srcMemberIdxes[currEnumTargetIdx] = i;
183            if (currEnumTargetIdx < nEnumTargets - 1) {
184                int nextTargetIdx = currTargetIdx + 1;
185                for (; nextTargetIdx < targets.size(); nextTargetIdx++) {
186                    if (targets.get(nextTargetIdx).getSrcMembers() != null) {
187                        break;
188                    }
189                }
190                addTargets(
191                    currEnumTargetIdx + 1, nextTargetIdx, nEnumTargets,
192                    srcMemberIdxes, message);
193            } else {
194                int column = 0;
195                int enumTargetIdx = 0;
196                for (TargetBase target : targets) {
197                    if (target.getSrcMembers() == null) {
198                        try {
199                            column = target.addRow(stmt, column);
200                        } catch (Throwable e) {
201                            throw Util.newError(e, message);
202                        }
203                    } else {
204                        RolapMember member =
205                            target.getSrcMembers().get(
206                                srcMemberIdxes[enumTargetIdx++]);
207                        target.add(member);
208                    }
209                }
210            }
211        }
212    }
213
214    /**
215     * Retrieves the current members fetched from the targets executed
216     * through sql and form tuples, adding them to partialResult
217     *
218     * @param partialResult list containing the columns and rows corresponding
219     * to data fetched through sql
220     */
221    private void savePartialResult(List<List<RolapMember>> partialResult) {
222        List<RolapMember> row = new ArrayList<RolapMember>();
223        for (TargetBase target : targets) {
224            if (target.getSrcMembers() == null) {
225                row.add(target.getCurrMember());
226            }
227        }
228        partialResult.add(row);
229    }
230
231}
232
233// End ResultLoader.java