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