001: /*
002: Copyright (C) 2002-2007 MySQL AB
003:
004: This program is free software; you can redistribute it and/or modify
005: it under the terms of version 2 of the GNU General Public License as
006: published by the Free Software Foundation.
007:
008: There are special exceptions to the terms and conditions of the GPL
009: as it is applied to this software. View the full text of the
010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
011: software distribution.
012:
013: This program is distributed in the hope that it will be useful,
014: but WITHOUT ANY WARRANTY; without even the implied warranty of
015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: GNU General Public License for more details.
017:
018: You should have received a copy of the GNU General Public License
019: along with this program; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021:
022:
023:
024: */
025: package com.mysql.jdbc;
026:
027: import java.sql.SQLException;
028:
029: import com.mysql.jdbc.profiler.ProfileEventSink;
030: import com.mysql.jdbc.profiler.ProfilerEvent;
031:
032: /**
033: * Allows streaming of MySQL data.
034: *
035: * @author dgan
036: * @version $Id: RowDataDynamic.java 6454 2007-06-21 17:03:53Z mmatthews $
037: */
038: public class RowDataDynamic implements RowData {
039:
040: class OperationNotSupportedException extends SQLException {
041: OperationNotSupportedException() {
042: super (
043: Messages.getString("RowDataDynamic.10"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
044: }
045: }
046:
047: private int columnCount;
048:
049: private Field[] metadata;
050:
051: private int index = -1;
052:
053: private MysqlIO io;
054:
055: private boolean isAfterEnd = false;
056:
057: private boolean isAtEnd = false;
058:
059: private boolean isBinaryEncoded = false;
060:
061: private ResultSetRow nextRow;
062:
063: private ResultSetImpl owner;
064:
065: private boolean streamerClosed = false;
066:
067: private boolean wasEmpty = false; // we don't know until we attempt to traverse
068:
069: private boolean useBufferRowExplicit;
070:
071: /**
072: * Creates a new RowDataDynamic object.
073: *
074: * @param io
075: * the connection to MySQL that this data is coming from
076: * @param metadata
077: * the metadata that describe this data
078: * @param isBinaryEncoded
079: * is this data in native format?
080: * @param colCount
081: * the number of columns
082: * @throws SQLException
083: * if the next record can not be found
084: */
085: public RowDataDynamic(MysqlIO io, int colCount, Field[] fields,
086: boolean isBinaryEncoded) throws SQLException {
087: this .io = io;
088: this .columnCount = colCount;
089: this .isBinaryEncoded = isBinaryEncoded;
090: this .metadata = fields;
091:
092: this .useBufferRowExplicit = MysqlIO
093: .useBufferRowExplicit(this .metadata);
094: }
095:
096: /**
097: * Adds a row to this row data.
098: *
099: * @param row
100: * the row to add
101: * @throws SQLException
102: * if a database error occurs
103: */
104: public void addRow(ResultSetRow row) throws SQLException {
105: notSupported();
106: }
107:
108: /**
109: * Moves to after last.
110: *
111: * @throws SQLException
112: * if a database error occurs
113: */
114: public void afterLast() throws SQLException {
115: notSupported();
116: }
117:
118: /**
119: * Moves to before first.
120: *
121: * @throws SQLException
122: * if a database error occurs
123: */
124: public void beforeFirst() throws SQLException {
125: notSupported();
126: }
127:
128: /**
129: * Moves to before last so next el is the last el.
130: *
131: * @throws SQLException
132: * if a database error occurs
133: */
134: public void beforeLast() throws SQLException {
135: notSupported();
136: }
137:
138: /**
139: * We're done.
140: *
141: * @throws SQLException
142: * if a database error occurs
143: */
144: public void close() throws SQLException {
145: // Belt and suspenders here - if we don't
146: // have a reference to the connection
147: // it's more than likely dead/gone and we
148: // won't be able to consume rows anyway
149:
150: Object mutex = this ;
151:
152: ConnectionImpl conn = null;
153:
154: if (this .owner != null) {
155: conn = this .owner.connection;
156:
157: if (conn != null) {
158: mutex = conn.getMutex();
159: }
160: }
161:
162: boolean hadMore = false;
163: int howMuchMore = 0;
164:
165: synchronized (mutex) {
166: // drain the rest of the records.
167: while (next() != null) {
168: hadMore = true;
169: howMuchMore++;
170:
171: if (howMuchMore % 100 == 0) {
172: Thread.yield();
173: }
174: }
175:
176: if (conn != null) {
177: if (!conn.getClobberStreamingResults()
178: && conn.getNetTimeoutForStreamingResults() > 0) {
179: String oldValue = conn
180: .getServerVariable("net_write_timeout");
181:
182: if (oldValue == null || oldValue.length() == 0) {
183: oldValue = "60"; // the current default
184: }
185:
186: this .io.clearInputStream();
187:
188: java.sql.Statement stmt = null;
189:
190: try {
191: stmt = conn.createStatement();
192: stmt.executeUpdate("SET net_write_timeout="
193: + oldValue);
194: } finally {
195: if (stmt != null) {
196: stmt.close();
197: }
198: }
199: }
200:
201: if (conn.getUseUsageAdvisor()) {
202: if (hadMore) {
203:
204: ProfileEventSink eventSink = ProfileEventSink
205: .getInstance(conn);
206:
207: eventSink
208: .consumeEvent(new ProfilerEvent(
209: ProfilerEvent.TYPE_WARN,
210: "", //$NON-NLS-1$
211: this .owner.owningStatement == null ? "N/A" : this .owner.owningStatement.currentCatalog, //$NON-NLS-1$
212: this .owner.connectionId,
213: this .owner.owningStatement == null ? -1
214: : this .owner.owningStatement
215: .getId(),
216: -1,
217: System.currentTimeMillis(),
218: 0,
219: Constants.MILLIS_I18N,
220: null,
221: null,
222: Messages
223: .getString("RowDataDynamic.2") //$NON-NLS-1$
224: + howMuchMore
225: + Messages
226: .getString("RowDataDynamic.3") //$NON-NLS-1$
227: + Messages
228: .getString("RowDataDynamic.4") //$NON-NLS-1$
229: + Messages
230: .getString("RowDataDynamic.5") //$NON-NLS-1$
231: + Messages
232: .getString("RowDataDynamic.6") //$NON-NLS-1$
233: + this .owner.pointOfOrigin));
234: }
235: }
236: }
237: }
238:
239: this .metadata = null;
240: this .owner = null;
241: }
242:
243: /**
244: * Only works on non dynamic result sets.
245: *
246: * @param index
247: * row number to get at
248: * @return row data at index
249: * @throws SQLException
250: * if a database error occurs
251: */
252: public ResultSetRow getAt(int ind) throws SQLException {
253: notSupported();
254:
255: return null;
256: }
257:
258: /**
259: * Returns the current position in the result set as a row number.
260: *
261: * @return the current row number
262: * @throws SQLException
263: * if a database error occurs
264: */
265: public int getCurrentRowNumber() throws SQLException {
266: notSupported();
267:
268: return -1;
269: }
270:
271: /**
272: * @see com.mysql.jdbc.RowData#getOwner()
273: */
274: public ResultSetInternalMethods getOwner() {
275: return this .owner;
276: }
277:
278: /**
279: * Returns true if another row exsists.
280: *
281: * @return true if more rows
282: * @throws SQLException
283: * if a database error occurs
284: */
285: public boolean hasNext() throws SQLException {
286: boolean hasNext = (this .nextRow != null);
287:
288: if (!hasNext && !this .streamerClosed) {
289: this .io.closeStreamer(this );
290: this .streamerClosed = true;
291: }
292:
293: return hasNext;
294: }
295:
296: /**
297: * Returns true if we got the last element.
298: *
299: * @return true if after last row
300: * @throws SQLException
301: * if a database error occurs
302: */
303: public boolean isAfterLast() throws SQLException {
304: return this .isAfterEnd;
305: }
306:
307: /**
308: * Returns if iteration has not occured yet.
309: *
310: * @return true if before first row
311: * @throws SQLException
312: * if a database error occurs
313: */
314: public boolean isBeforeFirst() throws SQLException {
315: return this .index < 0;
316: }
317:
318: /**
319: * Returns true if the result set is dynamic.
320: *
321: * This means that move back and move forward won't work because we do not
322: * hold on to the records.
323: *
324: * @return true if this result set is streaming from the server
325: */
326: public boolean isDynamic() {
327: return true;
328: }
329:
330: /**
331: * Has no records.
332: *
333: * @return true if no records
334: * @throws SQLException
335: * if a database error occurs
336: */
337: public boolean isEmpty() throws SQLException {
338: notSupported();
339:
340: return false;
341: }
342:
343: /**
344: * Are we on the first row of the result set?
345: *
346: * @return true if on first row
347: * @throws SQLException
348: * if a database error occurs
349: */
350: public boolean isFirst() throws SQLException {
351: notSupported();
352:
353: return false;
354: }
355:
356: /**
357: * Are we on the last row of the result set?
358: *
359: * @return true if on last row
360: * @throws SQLException
361: * if a database error occurs
362: */
363: public boolean isLast() throws SQLException {
364: notSupported();
365:
366: return false;
367: }
368:
369: /**
370: * Moves the current position relative 'rows' from the current position.
371: *
372: * @param rows
373: * the relative number of rows to move
374: * @throws SQLException
375: * if a database error occurs
376: */
377: public void moveRowRelative(int rows) throws SQLException {
378: notSupported();
379: }
380:
381: /**
382: * Returns the next row.
383: *
384: * @return the next row value
385: * @throws SQLException
386: * if a database error occurs
387: */
388: public ResultSetRow next() throws SQLException {
389:
390: nextRecord();
391:
392: if (this .nextRow == null && !this .streamerClosed) {
393: this .io.closeStreamer(this );
394: this .streamerClosed = true;
395: }
396:
397: if (this .nextRow != null) {
398: if (this .index != Integer.MAX_VALUE) {
399: this .index++;
400: }
401: }
402:
403: return this .nextRow;
404: }
405:
406: private void nextRecord() throws SQLException {
407:
408: try {
409: if (!this .isAtEnd) {
410: this .nextRow = this .io.nextRow(this .metadata,
411: this .columnCount, this .isBinaryEncoded,
412: java.sql.ResultSet.CONCUR_READ_ONLY, true,
413: this .useBufferRowExplicit, true, null);
414:
415: if (this .nextRow == null) {
416: this .isAtEnd = true;
417:
418: if (this .index == -1) {
419: this .wasEmpty = true;
420: }
421: }
422: } else {
423: this .isAfterEnd = true;
424: }
425: } catch (SQLException sqlEx) {
426: if (sqlEx instanceof StreamingNotifiable) {
427: ((StreamingNotifiable) sqlEx).setWasStreamingResults();
428: }
429:
430: // don't wrap SQLExceptions
431: throw sqlEx;
432: } catch (Exception ex) {
433: String exceptionType = ex.getClass().getName();
434: String exceptionMessage = ex.getMessage();
435:
436: exceptionMessage += Messages.getString("RowDataDynamic.7"); //$NON-NLS-1$
437: exceptionMessage += Util.stackTraceToString(ex);
438:
439: throw new java.sql.SQLException(
440: Messages.getString("RowDataDynamic.8") //$NON-NLS-1$
441: + exceptionType
442: + Messages.getString("RowDataDynamic.9") + exceptionMessage, SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
443: }
444: }
445:
446: private void notSupported() throws SQLException {
447: throw new OperationNotSupportedException();
448: }
449:
450: /**
451: * Removes the row at the given index.
452: *
453: * @param index
454: * the row to move to
455: * @throws SQLException
456: * if a database error occurs
457: */
458: public void removeRow(int ind) throws SQLException {
459: notSupported();
460: }
461:
462: /**
463: * Moves the current position in the result set to the given row number.
464: *
465: * @param rowNumber
466: * row to move to
467: * @throws SQLException
468: * if a database error occurs
469: */
470: public void setCurrentRow(int rowNumber) throws SQLException {
471: notSupported();
472: }
473:
474: /**
475: * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSetInternalMethods)
476: */
477: public void setOwner(ResultSetImpl rs) {
478: this .owner = rs;
479: }
480:
481: /**
482: * Only works on non dynamic result sets.
483: *
484: * @return the size of this row data
485: */
486: public int size() {
487: return RESULT_SET_SIZE_UNKNOWN;
488: }
489:
490: public boolean wasEmpty() {
491: return this .wasEmpty;
492: }
493:
494: public void setMetadata(Field[] metadata) {
495: this.metadata = metadata;
496: }
497: }
|