1 | /* |
2 | Copyright (C) 2002-2004 MySQL AB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of version 2 of the GNU General Public License as |
6 | published by the Free Software Foundation. |
7 | |
8 | There are special exceptions to the terms and conditions of the GPL |
9 | as it is applied to this software. View the full text of the |
10 | exception in file EXCEPTIONS-CONNECTOR-J in the directory of this |
11 | software distribution. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | |
22 | |
23 | |
24 | */ |
25 | package com.mysql.jdbc; |
26 | |
27 | import java.sql.SQLException; |
28 | |
29 | import com.mysql.jdbc.profiler.ProfileEventSink; |
30 | import com.mysql.jdbc.profiler.ProfilerEvent; |
31 | |
32 | /** |
33 | * Allows streaming of MySQL data. |
34 | * |
35 | * @author dgan |
36 | * @version $Id: RowDataDynamic.java 3726 2005-05-19 15:52:24Z mmatthews $ |
37 | */ |
38 | public class RowDataDynamic implements RowData { |
39 | // ~ Instance fields |
40 | // -------------------------------------------------------- |
41 | |
42 | class OperationNotSupportedException extends SQLException { |
43 | OperationNotSupportedException() { |
44 | super( |
45 | Messages.getString("RowDataDynamic.10"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ |
46 | } |
47 | } |
48 | |
49 | private int columnCount; |
50 | |
51 | private Field[] fields; |
52 | |
53 | private int index = -1; |
54 | |
55 | private MysqlIO io; |
56 | |
57 | private boolean isAfterEnd = false; |
58 | |
59 | private boolean isAtEnd = false; |
60 | |
61 | private boolean isBinaryEncoded = false; |
62 | |
63 | private Object[] nextRow; |
64 | |
65 | private ResultSet owner; |
66 | |
67 | private boolean streamerClosed = false; |
68 | |
69 | // ~ Methods |
70 | // ---------------------------------------------------------------- |
71 | |
72 | /** |
73 | * Creates a new RowDataDynamic object. |
74 | * |
75 | * @param io |
76 | * the connection to MySQL that this data is coming from |
77 | * @param fields |
78 | * the fields that describe this data |
79 | * @param isBinaryEncoded |
80 | * is this data in native format? |
81 | * @param colCount |
82 | * the number of columns |
83 | * @throws SQLException |
84 | * if the next record can not be found |
85 | */ |
86 | public RowDataDynamic(MysqlIO io, int colCount, Field[] fields, |
87 | boolean isBinaryEncoded) throws SQLException { |
88 | this.io = io; |
89 | this.columnCount = colCount; |
90 | this.isBinaryEncoded = isBinaryEncoded; |
91 | this.fields = fields; |
92 | nextRecord(); |
93 | } |
94 | |
95 | /** |
96 | * Adds a row to this row data. |
97 | * |
98 | * @param row |
99 | * the row to add |
100 | * @throws SQLException |
101 | * if a database error occurs |
102 | */ |
103 | public void addRow(byte[][] row) throws SQLException { |
104 | notSupported(); |
105 | } |
106 | |
107 | /** |
108 | * Moves to after last. |
109 | * |
110 | * @throws SQLException |
111 | * if a database error occurs |
112 | */ |
113 | public void afterLast() throws SQLException { |
114 | notSupported(); |
115 | } |
116 | |
117 | /** |
118 | * Moves to before first. |
119 | * |
120 | * @throws SQLException |
121 | * if a database error occurs |
122 | */ |
123 | public void beforeFirst() throws SQLException { |
124 | notSupported(); |
125 | } |
126 | |
127 | /** |
128 | * Moves to before last so next el is the last el. |
129 | * |
130 | * @throws SQLException |
131 | * if a database error occurs |
132 | */ |
133 | public void beforeLast() throws SQLException { |
134 | notSupported(); |
135 | } |
136 | |
137 | /** |
138 | * We're done. |
139 | * |
140 | * @throws SQLException |
141 | * if a database error occurs |
142 | */ |
143 | public void close() throws SQLException { |
144 | |
145 | boolean hadMore = false; |
146 | int howMuchMore = 0; |
147 | |
148 | // drain the rest of the records. |
149 | while (this.hasNext()) { |
150 | this.next(); |
151 | hadMore = true; |
152 | howMuchMore++; |
153 | |
154 | if (howMuchMore % 100 == 0) { |
155 | Thread.yield(); |
156 | } |
157 | } |
158 | |
159 | if (this.owner != null) { |
160 | Connection conn = this.owner.connection; |
161 | |
162 | if (conn != null && conn.getUseUsageAdvisor()) { |
163 | if (hadMore) { |
164 | |
165 | ProfileEventSink eventSink = ProfileEventSink |
166 | .getInstance(conn); |
167 | |
168 | eventSink |
169 | .consumeEvent(new ProfilerEvent( |
170 | ProfilerEvent.TYPE_WARN, |
171 | "", //$NON-NLS-1$ |
172 | this.owner.owningStatement == null ? "N/A" : this.owner.owningStatement.currentCatalog, //$NON-NLS-1$ |
173 | conn.getId(), |
174 | this.owner.owningStatement == null ? -1 |
175 | : this.owner.owningStatement |
176 | .getId(), |
177 | -1, |
178 | System.currentTimeMillis(), |
179 | 0, |
180 | null, |
181 | null, |
182 | Messages.getString("RowDataDynamic.2") //$NON-NLS-1$ |
183 | + howMuchMore |
184 | + Messages |
185 | .getString("RowDataDynamic.3") //$NON-NLS-1$ |
186 | + Messages |
187 | .getString("RowDataDynamic.4") //$NON-NLS-1$ |
188 | + Messages |
189 | .getString("RowDataDynamic.5") //$NON-NLS-1$ |
190 | + Messages |
191 | .getString("RowDataDynamic.6") //$NON-NLS-1$ |
192 | + this.owner.pointOfOrigin)); |
193 | } |
194 | } |
195 | } |
196 | |
197 | this.fields = null; |
198 | this.owner = null; |
199 | } |
200 | |
201 | /** |
202 | * Only works on non dynamic result sets. |
203 | * |
204 | * @param index |
205 | * row number to get at |
206 | * @return row data at index |
207 | * @throws SQLException |
208 | * if a database error occurs |
209 | */ |
210 | public Object[] getAt(int ind) throws SQLException { |
211 | notSupported(); |
212 | |
213 | return null; |
214 | } |
215 | |
216 | /** |
217 | * Returns the current position in the result set as a row number. |
218 | * |
219 | * @return the current row number |
220 | * @throws SQLException |
221 | * if a database error occurs |
222 | */ |
223 | public int getCurrentRowNumber() throws SQLException { |
224 | notSupported(); |
225 | |
226 | return -1; |
227 | } |
228 | |
229 | /** |
230 | * @see com.mysql.jdbc.RowData#getOwner() |
231 | */ |
232 | public ResultSet getOwner() { |
233 | return this.owner; |
234 | } |
235 | |
236 | /** |
237 | * Returns true if another row exsists. |
238 | * |
239 | * @return true if more rows |
240 | * @throws SQLException |
241 | * if a database error occurs |
242 | */ |
243 | public boolean hasNext() throws SQLException { |
244 | boolean hasNext = (this.nextRow != null); |
245 | |
246 | if (!hasNext && !this.streamerClosed) { |
247 | this.io.closeStreamer(this); |
248 | this.streamerClosed = true; |
249 | } |
250 | |
251 | return hasNext; |
252 | } |
253 | |
254 | /** |
255 | * Returns true if we got the last element. |
256 | * |
257 | * @return true if after last row |
258 | * @throws SQLException |
259 | * if a database error occurs |
260 | */ |
261 | public boolean isAfterLast() throws SQLException { |
262 | return this.isAfterEnd; |
263 | } |
264 | |
265 | /** |
266 | * Returns if iteration has not occured yet. |
267 | * |
268 | * @return true if before first row |
269 | * @throws SQLException |
270 | * if a database error occurs |
271 | */ |
272 | public boolean isBeforeFirst() throws SQLException { |
273 | return this.index < 0; |
274 | } |
275 | |
276 | /** |
277 | * Returns true if the result set is dynamic. |
278 | * |
279 | * This means that move back and move forward won't work because we do not |
280 | * hold on to the records. |
281 | * |
282 | * @return true if this result set is streaming from the server |
283 | */ |
284 | public boolean isDynamic() { |
285 | return true; |
286 | } |
287 | |
288 | /** |
289 | * Has no records. |
290 | * |
291 | * @return true if no records |
292 | * @throws SQLException |
293 | * if a database error occurs |
294 | */ |
295 | public boolean isEmpty() throws SQLException { |
296 | notSupported(); |
297 | |
298 | return false; |
299 | } |
300 | |
301 | /** |
302 | * Are we on the first row of the result set? |
303 | * |
304 | * @return true if on first row |
305 | * @throws SQLException |
306 | * if a database error occurs |
307 | */ |
308 | public boolean isFirst() throws SQLException { |
309 | notSupported(); |
310 | |
311 | return false; |
312 | } |
313 | |
314 | /** |
315 | * Are we on the last row of the result set? |
316 | * |
317 | * @return true if on last row |
318 | * @throws SQLException |
319 | * if a database error occurs |
320 | */ |
321 | public boolean isLast() throws SQLException { |
322 | notSupported(); |
323 | |
324 | return false; |
325 | } |
326 | |
327 | /** |
328 | * Moves the current position relative 'rows' from the current position. |
329 | * |
330 | * @param rows |
331 | * the relative number of rows to move |
332 | * @throws SQLException |
333 | * if a database error occurs |
334 | */ |
335 | public void moveRowRelative(int rows) throws SQLException { |
336 | if (rows < 0) { |
337 | notSupported(); |
338 | } |
339 | |
340 | for (int i = 0; i < rows; i++) { |
341 | if (hasNext()) { |
342 | next(); |
343 | } // after end of result set checked by ResultSet.checkRowPos() |
344 | } |
345 | } |
346 | |
347 | /** |
348 | * Returns the next row. |
349 | * |
350 | * @return the next row value |
351 | * @throws SQLException |
352 | * if a database error occurs |
353 | */ |
354 | public Object[] next() throws SQLException { |
355 | if (this.index != Integer.MAX_VALUE) { |
356 | this.index++; |
357 | } |
358 | |
359 | Object[] ret = this.nextRow; |
360 | nextRecord(); |
361 | |
362 | return ret; |
363 | } |
364 | |
365 | private void nextRecord() throws SQLException { |
366 | |
367 | try { |
368 | if (!this.isAtEnd) { |
369 | |
370 | this.nextRow = this.io.nextRow(this.fields, this.columnCount, |
371 | this.isBinaryEncoded, |
372 | java.sql.ResultSet.CONCUR_READ_ONLY); |
373 | |
374 | if (this.nextRow == null) { |
375 | this.isAtEnd = true; |
376 | } |
377 | } else { |
378 | this.isAfterEnd = true; |
379 | } |
380 | } catch (SQLException sqlEx) { |
381 | // don't wrap SQLExceptions |
382 | throw sqlEx; |
383 | } catch (Exception ex) { |
384 | String exceptionType = ex.getClass().getName(); |
385 | String exceptionMessage = ex.getMessage(); |
386 | |
387 | exceptionMessage += Messages.getString("RowDataDynamic.7"); //$NON-NLS-1$ |
388 | exceptionMessage += Util.stackTraceToString(ex); |
389 | |
390 | throw new java.sql.SQLException( |
391 | Messages.getString("RowDataDynamic.8") //$NON-NLS-1$ |
392 | + exceptionType |
393 | + Messages.getString("RowDataDynamic.9") + exceptionMessage, SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$ |
394 | } |
395 | } |
396 | |
397 | private void notSupported() throws SQLException { |
398 | throw new OperationNotSupportedException(); |
399 | } |
400 | |
401 | /** |
402 | * Removes the row at the given index. |
403 | * |
404 | * @param index |
405 | * the row to move to |
406 | * @throws SQLException |
407 | * if a database error occurs |
408 | */ |
409 | public void removeRow(int ind) throws SQLException { |
410 | notSupported(); |
411 | } |
412 | |
413 | // ~ Inner Classes |
414 | // ---------------------------------------------------------- |
415 | |
416 | /** |
417 | * Moves the current position in the result set to the given row number. |
418 | * |
419 | * @param rowNumber |
420 | * row to move to |
421 | * @throws SQLException |
422 | * if a database error occurs |
423 | */ |
424 | public void setCurrentRow(int rowNumber) throws SQLException { |
425 | notSupported(); |
426 | } |
427 | |
428 | /** |
429 | * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSet) |
430 | */ |
431 | public void setOwner(ResultSet rs) { |
432 | this.owner = rs; |
433 | } |
434 | |
435 | /** |
436 | * Only works on non dynamic result sets. |
437 | * |
438 | * @return the size of this row data |
439 | */ |
440 | public int size() { |
441 | return RESULT_SET_SIZE_UNKNOWN; |
442 | } |
443 | |
444 | } |