Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 &lt;= rows &lt;= 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