Source code: com/mysql/jdbc/ResultSet.java
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
9 There are special exceptions to the terms and conditions of the GPL
10 as it is applied to this software. View the full text of the
11 exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
12 software distribution.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 */
24 package com.mysql.jdbc;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.ObjectInputStream;
30 import java.io.StringReader;
31 import java.math.BigDecimal;
32 import java.net.MalformedURLException;
33 import java.net.URL;
34 import java.sql.Array;
35 import java.sql.Date;
36 import java.sql.Ref;
37 import java.sql.SQLException;
38 import java.sql.SQLWarning;
39 import java.sql.Time;
40 import java.sql.Timestamp;
41 import java.sql.Types;
42 import java.util.Calendar;
43 import java.util.GregorianCalendar;
44 import java.util.HashMap;
45 import java.util.Map;
46 import java.util.TimeZone;
47
48
49 /**
50 * A ResultSet provides access to a table of data generated by executing a
51 * Statement. The table rows are retrieved in sequence. Within a row its
52 * column values can be accessed in any order.
53 *
54 * <P>
55 * A ResultSet maintains a cursor pointing to its current row of data.
56 * Initially the cursor is positioned before the first row. The 'next' method
57 * moves the cursor to the next row.
58 * </p>
59 *
60 * <P>
61 * The getXXX methods retrieve column values for the current row. You can
62 * retrieve values either using the index number of the column, or by using
63 * the name of the column. In general using the column index will be more
64 * efficient. Columns are numbered from 1.
65 * </p>
66 *
67 * <P>
68 * For maximum portability, ResultSet columns within each row should be read in
69 * left-to-right order and each column should be read only once.
70 * </p>
71 *
72 * <P>
73 * For the getXXX methods, the JDBC driver attempts to convert the underlying
74 * data to the specified Java type and returns a suitable Java value. See the
75 * JDBC specification for allowable mappings from SQL types to Java types with
76 * the ResultSet getXXX methods.
77 * </p>
78 *
79 * <P>
80 * Column names used as input to getXXX methods are case insenstive. When
81 * performing a getXXX using a column name, if several columns have the same
82 * name, then the value of the first matching column will be returned. The
83 * column name option is designed to be used when column names are used in the
84 * SQL Query. For columns that are NOT explicitly named in the query, it is
85 * best to use column numbers. If column names were used there is no way for
86 * the programmer to guarentee that they actually refer to the intended
87 * columns.
88 * </p>
89 *
90 * <P>
91 * A ResultSet is automatically closed by the Statement that generated it when
92 * that Statement is closed, re-executed, or is used to retrieve the next
93 * result from a sequence of multiple results.
94 * </p>
95 *
96 * <P>
97 * The number, types and properties of a ResultSet's columns are provided by
98 * the ResultSetMetaData object returned by the getMetaData method.
99 * </p>
100 *
101 * @author Mark Matthews
102 * @version $Id: ResultSet.java,v 1.18.2.35 2004/11/05 16:57:46 mmatthew Exp $
103 *
104 * @see ResultSetMetaData
105 * @see java.sql.ResultSet
106 */
107 public class ResultSet implements java.sql.ResultSet {
108 /**
109 * This method ends up being staticly synchronized, so just store our own
110 * copy....
111 */
112 private TimeZone defaultTimeZone;
113
114 /** The Connection instance that created us */
115 protected com.mysql.jdbc.Connection connection; // The connection that created us
116
117 /** Map column names (and all of their permutations) to column indices */
118 protected Map columnNameToIndex = null;
119
120 /** Map of fully-specified column names to column indices */
121 protected Map fullColumnNameToIndex = null;
122
123 /** The actual rows */
124 protected RowData rowData; // The results
125
126 /** The warning chain */
127 protected java.sql.SQLWarning warningChain = null;
128
129 /** The statement that created us */
130 protected com.mysql.jdbc.Statement owningStatement;
131
132 /** The catalog that was in use when we were created */
133 protected String catalog = null;
134
135 /**
136 * Any info message from the server that was created while generating this
137 * result set (if 'info parsing' is enabled for the connection).
138 */
139 protected String serverInfo = null;
140
141 /** The fields for this result set */
142 protected Field[] fields; // The fields
143
144 /** Pointer to current row data */
145 protected byte[][] thisRow; // Values for current row
146
147 /** Are we in the middle of doing updates to the current row? */
148 protected boolean doingUpdates = false;
149
150 /** Has this result set been closed? */
151 protected boolean isClosed = false;
152
153 /** Are we on the insert row? */
154 protected boolean onInsertRow = false;
155
156 /**
157 * Do we actually contain rows, or just information about
158 * UPDATE/INSERT/DELETE?
159 */
160 protected boolean reallyResult = false;
161
162 /** Did the previous value retrieval find a NULL? */
163 protected boolean wasNullFlag = false;
164
165 /**
166 * First character of the query that created this result set...Used to
167 * determine whether or not to parse server info messages in certain
168 * circumstances.
169 */
170 protected char firstCharOfQuery;
171
172 /** The current row #, -1 == before start of result set */
173 protected int currentRow = -1; // Cursor to current row;
174
175 /** The direction to fetch rows (always FETCH_FORWARD) */
176 protected int fetchDirection = FETCH_FORWARD;
177
178 /** The number of rows to fetch in one go... */
179 protected int fetchSize = 0;
180
181 /** Are we read-only or updatable? */
182 protected int resultSetConcurrency = 0;
183
184 /** Are we scroll-sensitive/insensitive? */
185 protected int resultSetType = 0;
186
187 /** How many rows were affected by UPDATE/INSERT/DELETE? */
188 protected long updateCount;
189
190 // These are longs for
191 // recent versions of the MySQL server.
192 //
193 // They get reduced to ints via the JDBC API,
194 // but can be retrieved through a MySQLStatement
195 // in their entirety.
196 //
197
198 /** Value generated for AUTO_INCREMENT columns */
199 protected long updateId = -1;
200 private Calendar fastDateCal = null;
201 private boolean hasBuiltIndexMapping = false;
202 private boolean useStrictFloatingPoint = false;
203
204 /**
205 * Create a result set for an executeUpdate statement.
206 *
207 * @param updateCount the number of rows affected by the update
208 * @param updateID the autoincrement value (if any)
209 */
210 public ResultSet(long updateCount, long updateID) {
211 this.updateCount = updateCount;
212 this.updateId = updateID;
213 reallyResult = false;
214 fields = new Field[0];
215 }
216
217 /**
218 * Create a new ResultSet
219 *
220 * @param catalog the database in use when we were created
221 * @param fields an array of Field objects (basically, the ResultSet
222 * MetaData)
223 * @param tuples actual row data
224 * @param conn the Connection that created us.
225 *
226 * @throws SQLException if an error occurs
227 */
228 public ResultSet(String catalog, Field[] fields, RowData tuples,
229 com.mysql.jdbc.Connection conn) throws SQLException {
230 this(fields, tuples);
231 setConnection(conn);
232 this.catalog = catalog;
233
234 }
235
236 /**
237 * Creates a new ResultSet object.
238 *
239 * @param fields DOCUMENT ME!
240 * @param tuples DOCUMENT ME!
241 *
242 * @throws SQLException DOCUMENT ME!
243 */
244 public ResultSet(Field[] fields, RowData tuples) throws SQLException {
245 //_currentRow = -1;
246 this.fields = fields;
247 this.rowData = tuples;
248 this.updateCount = (long) rowData.size();
249
250 if (Driver.DEBUG) {
251 System.out.println("Retrieved " + updateCount + " rows");
252 }
253
254 this.reallyResult = true;
255
256 // Check for no results
257 if (this.rowData.size() > 0) {
258 //_thisRow = _rows.next();
259 if (this.updateCount == 1) {
260 if (this.thisRow == null) {
261 //_currentRow = -1;
262 this.rowData.close(); // empty result set
263 this.updateCount = -1;
264 }
265 }
266 } else {
267 this.thisRow = null;
268 }
269
270 this.rowData.setOwner(this);
271 }
272
273 /**
274 * JDBC 2.0
275 *
276 * <p>
277 * Determine if the cursor is after the last row in the result set.
278 * </p>
279 *
280 * @return true if after the last row, false otherwise. Returns false when
281 * the result set contains no rows.
282 *
283 * @exception SQLException if a database-access error occurs.
284 */
285 public boolean isAfterLast() throws SQLException {
286 if (Driver.TRACE) {
287 Object[] args = { };
288 Debug.methodCall(this, "isAfterLast", args);
289 }
290
291 checkClosed();
292
293 boolean b = rowData.isAfterLast();
294
295 if (Driver.TRACE) {
296 Debug.returnValue(this, "isAfterLast", new Boolean(b));
297 }
298
299 return b;
300 }
301
302 /**
303 * JDBC 2.0 Get an array column.
304 *
305 * @param i the first column is 1, the second is 2, ...
306 *
307 * @return an object representing an SQL array
308 *
309 * @throws SQLException if a database error occurs
310 * @throws NotImplemented DOCUMENT ME!
311 */
312 public java.sql.Array getArray(int i) throws SQLException {
313 throw new NotImplemented();
314 }
315
316 /**
317 * JDBC 2.0 Get an array column.
318 *
319 * @param colName the column name
320 *
321 * @return an object representing an SQL array
322 *
323 * @throws SQLException if a database error occurs
324 * @throws NotImplemented DOCUMENT ME!
325 */
326 public java.sql.Array getArray(String colName) throws SQLException {
327 throw new NotImplemented();
328 }
329
330 /**
331 * A column value can be retrieved as a stream of ASCII characters and then
332 * read in chunks from the stream. This method is particulary suitable
333 * for retrieving large LONGVARCHAR values. The JDBC driver will do any
334 * necessary conversion from the database format into ASCII.
335 *
336 * <p>
337 * <B>Note:</B> All the data in the returned stream must be read prior to
338 * getting the value of any other column. The next call to a get method
339 * implicitly closes the stream. Also, a stream may return 0 for
340 * available() whether there is data available or not.
341 * </p>
342 *
343 * @param columnIndex the first column is 1, the second is 2, ...
344 *
345 * @return a Java InputStream that delivers the database column value as a
346 * stream of one byte ASCII characters. If the value is SQL NULL
347 * then the result is null
348 *
349 * @exception java.sql.SQLException if a database access error occurs
350 *
351 * @see getBinaryStream
352 */
353 public InputStream getAsciiStream(int columnIndex)
354 throws java.sql.SQLException {
355 checkRowPos();
356
357 return getBinaryStream(columnIndex);
358 }
359
360 /**
361 * DOCUMENT ME!
362 *
363 * @param columnName DOCUMENT ME!
364 *
365 * @return DOCUMENT ME!
366 *
367 * @throws java.sql.SQLException DOCUMENT ME!
368 */
369 public InputStream getAsciiStream(String columnName)
370 throws java.sql.SQLException {
371 return getAsciiStream(findColumn(columnName));
372 }
373
374 //---------------------------------------------------------------------
375 // Traversal/Positioning
376 //---------------------------------------------------------------------
377
378 /**
379 * JDBC 2.0
380 *
381 * <p>
382 * Determine if the cursor is before the first row in the result set.
383 * </p>
384 *
385 * @return true if before the first row, false otherwise. Returns false
386 * when the result set contains no rows.
387 *
388 * @exception SQLException if a database-access error occurs.
389 */
390 public boolean isBeforeFirst() throws SQLException {
391 if (Driver.TRACE) {
392 Object[] args = { };
393 Debug.methodCall(this, "isBeforeFirst", args);
394 }
395
396 checkClosed();
397
398 boolean b = rowData.isBeforeFirst();
399
400 if (Driver.TRACE) {
401 Debug.returnValue(this, "isBeforeFirst", new Boolean(b));
402 }
403
404 return b;
405 }
406
407 /**
408 * Get the value of a column in the current row as a java.math.BigDecimal
409 * object
410 *
411 * @param columnIndex the first column is 1, the second is 2...
412 * @param scale the number of digits to the right of the decimal
413 *
414 * @return the column value; if the value is SQL NULL, null
415 *
416 * @exception java.sql.SQLException if a database access error occurs
417 */
418 public BigDecimal getBigDecimal(int columnIndex, int scale)
419 throws java.sql.SQLException {
420 String stringVal = getString(columnIndex);
421 BigDecimal val;
422
423 if (stringVal != null) {
424 if (stringVal.length() == 0) {
425 val = new BigDecimal(0);
426
427 return val.setScale(scale);
428 }
429
430 try {
431 val = new BigDecimal(stringVal);
432 } catch (NumberFormatException ex) {
433 throw new java.sql.SQLException("Bad format for BigDecimal '"
434 + stringVal + "' in column " + columnIndex + "("
435 + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
436 }
437
438 try {
439 return val.setScale(scale);
440 } catch (ArithmeticException ex) {
441 throw new java.sql.SQLException("Bad format for BigDecimal '"
442 + stringVal + "' in column " + columnIndex + "("
443 + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
444 }
445 }
446
447 return null;
448 }
449
450 /**
451 * DOCUMENT ME!
452 *
453 * @param columnName DOCUMENT ME!
454 * @param scale DOCUMENT ME!
455 *
456 * @return DOCUMENT ME!
457 *
458 * @throws java.sql.SQLException DOCUMENT ME!
459 */
460 public BigDecimal getBigDecimal(String columnName, int scale)
461 throws java.sql.SQLException {
462 return getBigDecimal(findColumn(columnName), scale);
463 }
464
465 /**
466 * JDBC 2.0 Get the value of a column in the current row as a
467 * java.math.BigDecimal object.
468 *
469 * @param columnIndex the first column is 1, the second is 2, ...
470 *
471 * @return the column value (full precision); if the value is SQL NULL, the
472 * result is null
473 *
474 * @exception SQLException if a database-access error occurs.
475 * @throws java.sql.SQLException DOCUMENT ME!
476 */
477 public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
478 String stringVal = getString(columnIndex);
479 BigDecimal val;
480
481 if (stringVal != null) {
482 if (stringVal.length() == 0) {
483 val = new BigDecimal(0);
484
485 return val;
486 }
487
488 try {
489 val = new BigDecimal(stringVal);
490
491 return val;
492 } catch (NumberFormatException ex) {
493 throw new java.sql.SQLException("Bad format for BigDecimal '"
494 + stringVal + "' in column " + columnIndex + "("
495 + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
496 }
497 }
498
499 return null;
500 }
501
502 /**
503 * JDBC 2.0 Get the value of a column in the current row as a
504 * java.math.BigDecimal object.
505 *
506 * @param columnName the name of the column to retrieve the value from
507 *
508 * @return the BigDecimal value in the column
509 *
510 * @throws SQLException if an error occurs
511 */
512 public BigDecimal getBigDecimal(String columnName)
513 throws SQLException {
514 return getBigDecimal(findColumn(columnName));
515 }
516
517 /**
518 * A column value can also be retrieved as a binary strea. This method is
519 * suitable for retrieving LONGVARBINARY values.
520 *
521 * @param columnIndex the first column is 1, the second is 2...
522 *
523 * @return a Java InputStream that delivers the database column value as a
524 * stream of bytes. If the value is SQL NULL, then the result is
525 * null
526 *
527 * @exception java.sql.SQLException if a database access error occurs
528 *
529 * @see getAsciiStream
530 * @see getUnicodeStream
531 */
532 public InputStream getBinaryStream(int columnIndex)
533 throws java.sql.SQLException {
534 checkRowPos();
535
536 byte[] b = getBytes(columnIndex);
537
538 if (b != null) {
539 return new ByteArrayInputStream(b);
540 }
541
542 return null;
543 }
544
545 /**
546 * DOCUMENT ME!
547 *
548 * @param columnName DOCUMENT ME!
549 *
550 * @return DOCUMENT ME!
551 *
552 * @throws java.sql.SQLException DOCUMENT ME!
553 */
554 public InputStream getBinaryStream(String columnName)
555 throws java.sql.SQLException {
556 return getBinaryStream(findColumn(columnName));
557 }
558
559 /**
560 * JDBC 2.0 Get a BLOB column.
561 *
562 * @param columnIndex the first column is 1, the second is 2, ...
563 *
564 * @return an object representing a BLOB
565 *
566 * @throws SQLException if an error occurs.
567 * @throws java.sql.SQLException DOCUMENT ME!
568 */
569 public java.sql.Blob getBlob(int columnIndex) throws SQLException {
570 checkRowPos();
571
572 if ((columnIndex < 1) || (columnIndex > fields.length)) {
573 throw new java.sql.SQLException("Column Index out of range ( "
574 + columnIndex + " > " + fields.length + ").", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER);
575 }
576
577 try {
578 if (thisRow[columnIndex - 1] == null) {
579 wasNullFlag = true;
580 } else {
581 wasNullFlag = false;
582 }
583 } catch (NullPointerException ex) {
584 wasNullFlag = true;
585 }
586
587 if (wasNullFlag) {
588 return null;
589 }
590
591 return new Blob(thisRow[columnIndex - 1]);
592 }
593
594 /**
595 * JDBC 2.0 Get a BLOB column.
596 *
597 * @param colName the column name
598 *
599 * @return an object representing a BLOB
600 *
601 * @throws SQLException if an error occurs.
602 */
603 public java.sql.Blob getBlob(String colName) throws SQLException {
604 return getBlob(findColumn(colName));
605 }
606
607 /**
608 * Get the value of a column in the current row as a Java boolean
609 *
610 * @param columnIndex the first column is 1, the second is 2...
611 *
612 * @return the column value, false for SQL NULL
613 *
614 * @exception java.sql.SQLException if a database access error occurs
615 */
616 public boolean getBoolean(int columnIndex) throws java.sql.SQLException {
617 String stringVal = getString(columnIndex);
618
619 if ((stringVal != null) && (stringVal.length() > 0)) {
620 int c = Character.toLowerCase(stringVal.charAt(0));
621
622 return ((c == 't') || (c == 'y') || (c == '1')
623 || stringVal.equals("-1"));
624 }
625
626 return false;
627 }
628
629 /**
630 * DOCUMENT ME!
631 *
632 * @param columnName DOCUMENT ME!
633 *
634 * @return DOCUMENT ME!
635 *
636 * @throws java.sql.SQLException DOCUMENT ME!
637 */
638 public boolean getBoolean(String columnName) throws java.sql.SQLException {
639 return getBoolean(findColumn(columnName));
640 }
641
642 /**
643 * Get the value of a column in the current row as a Java byte.
644 *
645 * @param columnIndex the first column is 1, the second is 2,...
646 *
647 * @return the column value; 0 if SQL NULL
648 *
649 * @exception java.sql.SQLException if a database access error occurs
650 * @throws SQLException DOCUMENT ME!
651 */
652 public byte getByte(int columnIndex) throws java.sql.SQLException {
653 String stringVal = getString(columnIndex);
654
655 if (this.wasNullFlag || stringVal == null) {
656 return 0;
657 }
658
659 Field field = fields[columnIndex - 1];
660
661 switch (field.getMysqlType()) {
662 case MysqlDefs.FIELD_TYPE_DECIMAL:
663 case MysqlDefs.FIELD_TYPE_TINY:
664 case MysqlDefs.FIELD_TYPE_SHORT:
665 case MysqlDefs.FIELD_TYPE_LONG:
666 case MysqlDefs.FIELD_TYPE_FLOAT:
667 case MysqlDefs.FIELD_TYPE_DOUBLE:
668 case MysqlDefs.FIELD_TYPE_LONGLONG:
669 case MysqlDefs.FIELD_TYPE_INT24:
670
671 try {
672 int decimalIndex = stringVal.indexOf(".");
673
674 // Strip off the decimals
675 if (decimalIndex != -1) {
676 stringVal = stringVal.substring(0, decimalIndex);
677 }
678
679 return Byte.parseByte(stringVal);
680 } catch (NumberFormatException NFE) {
681 throw new SQLException("Value '" + getString(columnIndex)
682 + "' is out of range [-127,127]", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
683 }
684
685 default:
686
687 try {
688 int decimalIndex = stringVal.indexOf(".");
689
690 // Strip off the decimals
691 if (decimalIndex != -1) {
692 stringVal = stringVal.substring(0, decimalIndex);
693 }
694
695 return Byte.parseByte(stringVal);
696 } catch (NumberFormatException NFE) {
697 throw new SQLException("Value '" + getString(columnIndex)
698 + "' is out of range [-127,127]", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
699 }
700 }
701 }
702
703 /**
704 * DOCUMENT ME!
705 *
706 * @param columnName DOCUMENT ME!
707 *
708 * @return DOCUMENT ME!
709 *
710 * @throws java.sql.SQLException DOCUMENT ME!
711 */
712 public byte getByte(String columnName) throws java.sql.SQLException {
713 return getByte(findColumn(columnName));
714 }
715
716 /**
717 * Get the value of a column in the current row as a Java byte array.
718 *
719 * <p>
720 * <b>Be warned</b> If the blob is huge, then you may run out of memory.
721 * </p>
722 *
723 * @param columnIndex the first column is 1, the second is 2, ...
724 *
725 * @return the column value; if the value is SQL NULL, the result is null
726 *
727 * @exception java.sql.SQLException if a database access error occurs
728 */
729 public byte[] getBytes(int columnIndex) throws java.sql.SQLException {
730 checkRowPos();
731
732 try {
733 if (thisRow[columnIndex - 1] == null) {
734 wasNullFlag = true;
735 } else {
736 wasNullFlag = false;
737 }
738 } catch (NullPointerException E) {
739 wasNullFlag = true;
740 } catch (ArrayIndexOutOfBoundsException aioobEx) {
741 throw new java.sql.SQLException("Column Index out of range ( "
742 + columnIndex + " > " + fields.length + ").", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER);
743 }
744
745 if (wasNullFlag) {
746 return null;
747 } else {
748 return thisRow[columnIndex - 1];
749 }
750 }
751
752 /**
753 * DOCUMENT ME!
754 *
755 * @param columnName DOCUMENT ME!
756 *
757 * @return DOCUMENT ME!
758 *
759 * @throws java.sql.SQLException DOCUMENT ME!
760 */
761 public byte[] getBytes(String columnName) throws java.sql.SQLException {
762 return getBytes(findColumn(columnName));
763 }
764
765 //--------------------------JDBC 2.0-----------------------------------
766 //---------------------------------------------------------------------
767 // Getter's and Setter's
768 //---------------------------------------------------------------------
769
770 /**
771 * JDBC 2.0
772 *
773 * <p>
774 * Get the value of a column in the current row as a java.io.Reader.
775 * </p>
776 *
777 * @param columnIndex the column to get the value from
778 *
779 * @return the value in the column as a java.io.Reader.
780 *
781 * @throws SQLException if an error occurs
782 */
783 public java.io.Reader getCharacterStream(int columnIndex)
784 throws SQLException {
785 String stringVal = getString(columnIndex);
786
787 if (stringVal != null) {
788 return new StringReader(stringVal);
789 } else {
790 return null;
791 }
792 }
793
794 /**
795 * JDBC 2.0
796 *
797 * <p>
798 * Get the value of a column in the current row as a java.io.Reader.
799 * </p>
800 *
801 * @param columnName the column name to retrieve the value from
802 *
803 * @return the value as a java.io.Reader
804 *
805 * @throws SQLException if an error occurs
806 */
807 public java.io.Reader getCharacterStream(String columnName)
808 throws SQLException {
809 return getCharacterStream(findColumn(columnName));
810 }
811
812 /**
813 * JDBC 2.0 Get a CLOB column.
814 *
815 * @param i the first column is 1, the second is 2, ...
816 *
817 * @return an object representing a CLOB
818 *
819 * @throws SQLException if an error occurs
820 */
821 public java.sql.Clob getClob(int i) throws SQLException {
822 return new com.mysql.jdbc.Clob(getString(i));
823 }
824
825 /**
826 * JDBC 2.0 Get a CLOB column.
827 *
828 * @param colName the column name
829 *
830 * @return an object representing a CLOB
831 *
832 * @throws SQLException if an error occurs
833 */
834 public java.sql.Clob getClob(String colName) throws SQLException {
835 return getClob(findColumn(colName));
836 }
837
838 /**
839 * JDBC 2.0 Return the concurrency of this result set. The concurrency
840 * used is determined by the statement that created the result set.
841 *
842 * @return the concurrency type, CONCUR_READ_ONLY, etc.
843 *
844 * @throws SQLException if a database-access error occurs
845 */
846 public int getConcurrency() throws SQLException {
847 return this.resultSetConcurrency;
848 }
849
850 /**
851 * DOCUMENT ME!
852 *
853 * @param conn the connection that created this result set.
854 */
855 public synchronized void setConnection(com.mysql.jdbc.Connection conn) {
856 this.connection = conn;
857
858 if (this.connection != null) {
859 this.useStrictFloatingPoint = this.connection.useStrictFloatingPoint();
860 this.defaultTimeZone = this.connection.getDefaultTimeZone();
861 } else {
862 this.defaultTimeZone = TimeZone.getDefault();
863 }
864 }
865
866 /**
867 * Get the name of the SQL cursor used by this ResultSet
868 *
869 * <p>
870 * In SQL, a result table is retrieved though a cursor that is named. The
871 * current row of a result can be updated or deleted using a positioned
872 * update/delete statement that references the cursor name.
873 * </p>
874 *
875 * <p>
876 * JDBC supports this SQL feature by providing the name of the SQL cursor
877 * used by a ResultSet. The current row of a ResulSet is also the current
878 * row of this SQL cursor.
879 * </p>
880 *
881 * <p>
882 * <B>Note:</B> If positioned update is not supported, a
883 * java.sql.SQLException is thrown.
884 * </p>
885 *
886 * @return the ResultSet's SQL cursor name.
887 *
888 * @exception java.sql.SQLException if a database access error occurs
889 */
890 public String getCursorName() throws java.sql.SQLException {
891 throw new java.sql.SQLException("Positioned Update not supported.",
892 "S1C00");
893 }
894
895 /**
896 * Get the value of a column in the current row as a java.sql.Date object
897 *
898 * @param columnIndex the first column is 1, the second is 2...
899 *
900 * @return the column value; null if SQL NULL
901 *
902 * @exception java.sql.SQLException if a database access error occurs
903 */
904 public java.sql.Date getDate(int columnIndex) throws java.sql.SQLException {
905 return getDate(columnIndex, null);
906 }
907
908 /**
909 * DOCUMENT ME!
910 *
911 * @param columnName DOCUMENT ME!
912 *
913 * @return DOCUMENT ME!
914 *
915 * @throws java.sql.SQLException DOCUMENT ME!
916 */
917 public java.sql.Date getDate(String columnName)
918 throws java.sql.SQLException {
919 return getDate(findColumn(columnName));
920 }
921
922 /**
923 * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date
924 * object. Use the calendar to construct an appropriate millisecond value
925 * for the Date, if the underlying database doesn't store timezone
926 * information.
927 *
928 * @param columnIndex the first column is 1, the second is 2, ...
929 * @param cal the calendar to use in constructing the date
930 *
931 * @return the column value; if the value is SQL NULL, the result is null
932 *
933 * @exception SQLException if a database-access error occurs.
934 * @throws java.sql.SQLException DOCUMENT ME!
935 */
936 public java.sql.Date getDate(int columnIndex, Calendar cal)
937 throws SQLException {
938 Integer year = null;
939 Integer month = null;
940 Integer day = null;
941 String stringVal = "";
942
943 try {
944 stringVal = getString(columnIndex);
945
946 if (stringVal == null) {
947 return null;
948 } else {
949 int length = stringVal.length();
950
951 if ((length > 0) && (stringVal.charAt(0) == '0')
952 && (stringVal.equals("0000-00-00")
953 || stringVal.equals("0000-00-00 00:00:00")
954 || stringVal.equals("00000000000000")
955 || stringVal.equals("0"))) {
956 wasNullFlag = true;
957
958 return null;
959 } else if (fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) {
960 // Convert from TIMESTAMP
961 switch (length) {
962 case 14:
963 case 8: {
964 year = new Integer(stringVal.substring(0, 4));
965 month = new Integer(stringVal.substring(4, 6));
966 day = new Integer(stringVal.substring(6, 8));
967
968 return fastDateCreate(cal, year.intValue() - 1900,
969 month.intValue() - 1, day.intValue());
970 }
971
972 case 12:
973 case 10:
974 case 6: {
975 year = new Integer(stringVal.substring(0, 2));
976
977 if (year.intValue() <= 69) {
978 year = new Integer(year.intValue() + 100);
979 }
980
981 month = new Integer(stringVal.substring(2, 4));
982 day = new Integer(stringVal.substring(4, 6));
983
984 return fastDateCreate(cal, year.intValue(),
985 month.intValue() - 1, day.intValue());
986 }
987
988 case 4: {
989 year = new Integer(stringVal.substring(0, 4));
990
991 if (year.intValue() <= 69) {
992 year = new Integer(year.intValue() + 100);
993 }
994
995 month = new Integer(stringVal.substring(2, 4));
996
997 return fastDateCreate(cal, year.intValue(),
998 month.intValue() - 1, 1);
999 }
1000
1001 case 2: {
1002 year = new Integer(stringVal.substring(0, 2));
1003
1004 if (year.intValue() <= 69) {
1005 year = new Integer(year.intValue() + 100);
1006 }
1007
1008 return fastDateCreate(cal, year.intValue(), 0, 1);
1009 }
1010
1011 default:
1012 throw new SQLException("Bad format for Date '"
1013 + stringVal + "' in column " + columnIndex + "("
1014 + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1015 } /* endswitch */
1016 } else if (fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
1017 year = new Integer(stringVal.substring(0, 4));
1018
1019 return fastDateCreate(cal, year.intValue() - 1900, 0, 1);
1020 } else {
1021 if (length < 10) {
1022 throw new SQLException("Bad format for Date '"
1023 + stringVal + "' in column " + columnIndex + "("
1024 + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1025 }
1026
1027 year = new Integer(stringVal.substring(0, 4));
1028 month = new Integer(stringVal.substring(5, 7));
1029 day = new Integer(stringVal.substring(8, 10));
1030 }
1031
1032 return fastDateCreate(cal, year.intValue() - 1900,
1033 month.intValue() - 1, day.intValue());
1034 }
1035 } catch (Exception e) {
1036 throw new java.sql.SQLException("Cannot convert value '"
1037 + stringVal + "' from column " + columnIndex + "(" + stringVal
1038 + " ) to DATE.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1039 }
1040 }
1041
1042 /**
1043 * Get the value of a column in the current row as a java.sql.Date object.
1044 * Use the calendar to construct an appropriate millisecond value for the
1045 * Date, if the underlying database doesn't store timezone information.
1046 *
1047 * @param columnName is the SQL name of the column
1048 * @param cal the calendar to use in constructing the date
1049 *
1050 * @return the column value; if the value is SQL NULL, the result is null
1051 *
1052 * @exception SQLException if a database-access error occurs.
1053 */
1054 public java.sql.Date getDate(String columnName, Calendar cal)
1055 throws SQLException {
1056 return getDate(columnName);
1057 }
1058
1059 /**
1060 * Get the value of a column in the current row as a Java double.
1061 *
1062 * @param columnIndex the first column is 1, the second is 2,...
1063 *
1064 * @return the column value; 0 if SQL NULL
1065 *
1066 * @exception java.sql.SQLException if a database access error occurs
1067 */
1068 public double getDouble(int columnIndex) throws java.sql.SQLException {
1069 try {
1070 return getDoubleInternal(columnIndex);
1071 } catch (NumberFormatException E) {
1072 throw new java.sql.SQLException("Bad format for number '"
1073 + new String(thisRow[columnIndex - 1]) + "' in column "
1074 + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1075 }
1076 }
1077
1078 /**
1079 * DOCUMENT ME!
1080 *
1081 * @param columnName DOCUMENT ME!
1082 *
1083 * @return DOCUMENT ME!
1084 *
1085 * @throws java.sql.SQLException DOCUMENT ME!
1086 */
1087 public double getDouble(String columnName) throws java.sql.SQLException {
1088 return getDouble(findColumn(columnName));
1089 }
1090
1091 /**
1092 * JDBC 2.0 Give a hint as to the direction in which the rows in this
1093 * result set will be processed. The initial value is determined by the
1094 * statement that produced the result set. The fetch direction may be
1095 * changed at any time.
1096 *
1097 * @param direction the direction to fetch rows in.
1098 *
1099 * @exception SQLException if a database-access error occurs, or the result
1100 * set type is TYPE_FORWARD_ONLY and direction is not
1101 * FETCH_FORWARD. MM.MySQL actually ignores this, because it
1102 * has the whole result set anyway, so the direction is
1103 * immaterial.
1104 */
1105 public void setFetchDirection(int direction) throws SQLException {
1106 if ((direction != FETCH_FORWARD) && (direction != FETCH_REVERSE)
1107 && (direction != FETCH_UNKNOWN)) {
1108 throw new SQLException("Illegal value for fetch direction", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1109 } else {
1110 fetchDirection = direction;
1111 }
1112 }
1113
1114 /**
1115 * JDBC 2.0 Returns the fetch direction for this result set.
1116 *
1117 * @return the fetch direction for this result set.
1118 *
1119 * @exception SQLException if a database-access error occurs
1120 */
1121 public int getFetchDirection() throws SQLException {
1122 return fetchDirection;
1123 }
1124
1125 /**
1126 * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that
1127 * should be fetched from the database when more rows are needed for this
1128 * result set. If the fetch size specified is zero, then the JDBC driver
1129 * ignores the value, and is free to make its own best guess as to what
1130 * the fetch size should be. The default value is set by the statement
1131 * that creates the result set. The fetch size may be changed at any
1132 * time.
1133 *
1134 * @param rows the number of rows to fetch
1135 *
1136 * @exception SQLException if a database-access error occurs, or the
1137 * condition 0 <= rows <= this.getMaxRows() is not
1138 * satisfied. Currently ignored by this driver.
1139 */
1140 public void setFetchSize(int rows) throws SQLException {
1141 if (rows < 0) { /* || rows > getMaxRows()*/
1142 throw new SQLException("Value must be between 0 and getMaxRows()",
1143 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1144 }
1145
1146 fetchSize = rows;
1147 }
1148
1149 /**
1150 * JDBC 2.0 Return the fetch size for this result set.
1151 *
1152 * @return the fetch size for this result set.
1153 *
1154 * @exception SQLException if a database-access error occurs
1155 */
1156 public int getFetchSize() throws SQLException {
1157 return fetchSize;
1158 }
1159
1160 /**
1161 * JDBC 2.0
1162 *
1163 * <p>
1164 * Determine if the cursor is on the first row of the result set.
1165 * </p>
1166 *
1167 * @return true if on the first row, false otherwise.
1168 *
1169 * @exception SQLException if a database-access error occurs.
1170 */
1171 public boolean isFirst() throws SQLException {
1172 if (Driver.TRACE) {
1173 Object[] args = { };
1174 Debug.methodCall(this, "isFirst", args);
1175 }
1176
1177 checkClosed();
1178
1179 boolean b = rowData.isFirst();
1180
1181 if (Driver.TRACE) {
1182 Debug.returnValue(this, "isFirst", new Boolean(b));
1183 }
1184
1185 return b;
1186 }
1187
1188 /**
1189 * Get the value of a column in the current row as a Java float.
1190 *
1191 * @param columnIndex the first column is 1, the second is 2,...
1192 *
1193 * @return the column value; 0 if SQL NULL
1194 *
1195 * @exception java.sql.SQLException if a database access error occurs
1196 * @throws SQLException DOCUMENT ME!
1197 */
1198 public float getFloat(int columnIndex) throws java.sql.SQLException {
1199 String val = null;
1200
1201 try {
1202 val = getString(columnIndex);
1203
1204 if ((val != null) && (val.length() != 0)) {
1205 float f = Float.parseFloat(val);
1206
1207 return f;
1208 } else {
1209 return 0;
1210 }
1211 } catch (NumberFormatException nfe) {
1212 try {
1213 // To do: warn on under/overflow?
1214 return (float) Double.parseDouble(val);
1215 } catch (NumberFormatException newNfe) {
1216 ; // ignore, it's not a number
1217 }
1218
1219 throw new SQLException("Invalid value for getFloat() - '" + val
1220 + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1221 }
1222 }
1223
1224 /**
1225 * DOCUMENT ME!
1226 *
1227 * @param columnName DOCUMENT ME!
1228 *
1229 * @return DOCUMENT ME!
1230 *
1231 * @throws java.sql.SQLException DOCUMENT ME!
1232 */
1233 public float getFloat(String columnName) throws java.sql.SQLException {
1234 return getFloat(findColumn(columnName));
1235 }
1236
1237 /**
1238 * Get the value of a column in the current row as a Java int.
1239 *
1240 * @param columnIndex the first column is 1, the second is 2,...
1241 *
1242 * @return the column value; 0 if SQL NULL
1243 *
1244 * @exception java.sql.SQLException if a database access error occurs
1245 * @throws SQLException DOCUMENT ME!
1246 */
1247 public int getInt(int columnIndex) throws java.sql.SQLException {
1248 String val = null;
1249
1250 try {
1251 val = getString(columnIndex);
1252
1253 if ((val != null) && (val.length() != 0)) {
1254 if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
1255 && (val.indexOf(".") == -1)) {
1256 return Integer.parseInt(val);
1257 } else {
1258 // Convert floating point
1259 return (int) (Double.parseDouble(val));
1260 }
1261 } else {
1262 return 0;
1263 }
1264 } catch (NumberFormatException nfe) {
1265 try {
1266 // To do: warn on under/overflow?
1267 return (int) Double.parseDouble(val);
1268 } catch (NumberFormatException newNfe) {
1269 ; // ignore, it's not a number
1270 }
1271
1272 throw new SQLException("Invalid value for getInt() - '" + val + "'",
1273 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1274 }
1275 }
1276
1277 /**
1278 * DOCUMENT ME!
1279 *
1280 * @param columnName DOCUMENT ME!
1281 *
1282 * @return DOCUMENT ME!
1283 *
1284 * @throws java.sql.SQLException DOCUMENT ME!
1285 */
1286 public int getInt(String columnName) throws java.sql.SQLException {
1287 return getInt(findColumn(columnName));
1288 }
1289
1290 /**
1291 * JDBC 2.0
1292 *
1293 * <p>
1294 * Determine if the cursor is on the last row of the result set. Note:
1295 * Calling isLast() may be expensive since the JDBC driver might need to
1296 * fetch ahead one row in order to determine whether the current row is
1297 * the last row in the result set.
1298 * </p>
1299 *
1300 * @return true if on the last row, false otherwise.
1301 *
1302 * @exception SQLException if a database-access error occurs.
1303 */
1304 public boolean isLast() throws SQLException {
1305 if (Driver.TRACE) {
1306 Object[] args = { };
1307 Debug.methodCall(this, "isLast", args);
1308 }
1309
1310 checkClosed();
1311
1312 boolean b = rowData.isLast();
1313
1314 if (Driver.TRACE) {
1315 Debug.returnValue(this, "relative", new Boolean(b));
1316 }
1317
1318 return b;
1319 }
1320
1321 /**
1322 * Get the value of a column in the current row as a Java long.
1323 *
1324 * @param columnIndex the first column is 1, the second is 2,...
1325 *
1326 * @return the column value; 0 if SQL NULL
1327 *
1328 * @exception java.sql.SQLException if a database access error occurs
1329 * @throws SQLException DOCUMENT ME!
1330 */
1331 public long getLong(int columnIndex) throws java.sql.SQLException {
1332 String val = null;
1333
1334 try {
1335 val = getString(columnIndex);
1336
1337 if ((val != null) && (val.length() != 0)) {
1338 if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) {
1339 return Long.parseLong(val);
1340 } else {
1341 // Convert floating point
1342 return Double.doubleToLongBits(Double.parseDouble(val));
1343 }
1344 } else {
1345 return 0;
1346 }
1347 } catch (NumberFormatException nfe) {
1348 try {
1349 // To do: warn on under/overflow?
1350 return (long) Double.parseDouble(val);
1351 } catch (NumberFormatException newNfe) {
1352 ; // ignore, it's not a number
1353 }
1354
1355 throw new SQLException("Invalid value for getLong() - '" + val
1356 + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1357 }
1358 }
1359
1360 /**
1361 * DOCUMENT ME!
1362 *
1363 * @param columnName DOCUMENT ME!
1364 *
1365 * @return DOCUMENT ME!
1366 *
1367 * @throws java.sql.SQLException DOCUMENT ME!
1368 */
1369 public long getLong(String columnName) throws java.sql.SQLException {
1370 return getLong(findColumn(columnName));
1371 }
1372
1373 /**
1374 * The numbers, types and properties of a ResultSet's columns are provided
1375 * by the getMetaData method
1376 *
1377 * @return a description of the ResultSet's columns
1378 *
1379 * @exception java.sql.SQLException if a database access error occurs
1380 */
1381 public java.sql.ResultSetMetaData getMetaData()
1382 throws java.sql.SQLException {
1383 checkClosed();
1384
1385 return new com.mysql.jdbc.ResultSetMetaData(fields);
1386 }
1387
1388 /**
1389 * Get the value of a column in the current row as a Java object
1390 *
1391 * <p>
1392 * This method will return the value of the given column as a Java object.
1393 * The type of the Java object will be the default Java Object type
1394 * corresponding to the column's SQL type, following the mapping specified
1395 * in the JDBC specification.
1396 * </p>
1397 *
1398 * <p>
1399 * This method may also be used to read database specific abstract data
1400 * types.
1401 * </p>
1402 *
1403 * @param columnIndex the first column is 1, the second is 2...
1404 *
1405 * @return a Object holding the column value
1406 *
1407 * @exception java.sql.SQLException if a database access error occurs
1408 * @throws SQLException DOCUMENT ME!
1409 */
1410 public Object getObject(int columnIndex) throws java.sql.SQLException {
1411 checkRowPos();
1412
1413 if (Driver.TRACE) {
1414 Object[] args = { new Integer(columnIndex) };
1415 Debug.methodCall(this, "getObject", args);
1416 }
1417
1418 try {
1419 if (thisRow[columnIndex - 1] == null) {
1420 wasNullFlag = true;
1421
1422 return null;
1423 }
1424 } catch (ArrayIndexOutOfBoundsException aioobEx) {
1425 throw new java.sql.SQLException("Column Index out of range ( "
1426 + columnIndex + " > " + fields.length + ").", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER);
1427 }
1428
1429 wasNullFlag = false;
1430
1431 Field field;
1432 field = fields[columnIndex - 1];
1433
1434 switch (field.getSQLType()) {
1435 case Types.BIT:
1436 return Boolean.valueOf(getBoolean(columnIndex));
1437
1438 case Types.TINYINT:
1439
1440 return new Integer(getInt(columnIndex));
1441
1442 case Types.SMALLINT:
1443
1444 return new Integer(getInt(columnIndex));
1445
1446 case Types.INTEGER:
1447
1448 if (field.isUnsigned()) {
1449 return new Long(getLong(columnIndex));
1450 } else {
1451 return new Integer(getInt(columnIndex));
1452 }
1453
1454 case