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