1 //
2 // Copyright 1998, 1999 CDS Networks, Inc., Medford Oregon
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 1. Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // 3. All advertising materials mentioning features or use of this software
14 // must display the following acknowledgement:
15 // This product includes software developed by CDS Networks, Inc.
16 // 4. The name of CDS Networks, Inc. may not be used to endorse or promote
17 // products derived from this software without specific prior
18 // written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY CDS NETWORKS, INC. ``AS IS'' AND
21 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 // ARE DISCLAIMED. IN NO EVENT SHALL CDS NETWORKS, INC. BE LIABLE
24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 // SUCH DAMAGE.
31 //
32
33
34 package com.internetcds.jdbc.tds;
35
36 import java.sql.*;
37 import java.math.BigDecimal;
38 import java.util.Vector;
39 // import java.util.Calendar;
40 import java.util.GregorianCalendar;
41 import java.util.Calendar;
42 import java.io.*;
43
44 /***
45 * <P>A ResultSet provides access to a table of data generated by
46 * executing a Statement. The table rows are retrieved in
47 * sequence. Within a row its column values can be accessed in any
48 * order.
49 *
50 * <P>A ResultSet maintains a cursor pointing to its current row of
51 * data. Initially the cursor is positioned before the first row.
52 * The 'next' method moves the cursor to the next row.
53 *
54 * <P>The getXXX methods retrieve column values for the current
55 * row. You can retrieve values either using the index number of the
56 * column, or by using the name of the column. In general using the
57 * column index will be more efficient. Columns are numbered from 1.
58 *
59 * <P>For maximum portability, ResultSet columns within each row should be
60 * read in left-to-right order and each column should be read only once.
61 *
62 * <P>For the getXXX methods, the JDBC driver attempts to convert the
63 * underlying data to the specified Java type and returns a suitable
64 * Java value. See the JDBC specification for allowable mappings
65 * from SQL types to Java types with the ResultSet.getXXX methods.
66 *
67 * <P>Column names used as input to getXXX methods are case
68 * insensitive. When performing a getXXX using a column name, if
69 * several columns have the same name, then the value of the first
70 * matching column will be returned. The column name option is
71 * designed to be used when column names are used in the SQL
72 * query. For columns that are NOT explicitly named in the query, it
73 * is best to use column numbers. If column names were used there is
74 * no way for the programmer to guarantee that they actually refer to
75 * the intended columns.
76 *
77 * <P>A ResultSet is automatically closed by the Statement that
78 * generated it when that Statement is closed, re-executed, or is used
79 * to retrieve the next result from a sequence of multiple results.
80 *
81 * <P>The number, types and properties of a ResultSet's columns are
82 * provided by the ResulSetMetaData object returned by the getMetaData
83 * method.
84 *
85 * @author Craig Spannring
86 * @author The FreeTDS project
87 * @version $Id: ResultSet_base.java,v 1.1 2003/04/29 18:07:50 sinisa Exp $
88 *
89 * @see Statement#executeQuery
90 * @see Statement#getResultSet
91 * @see ResultSetMetaData
92 @ @see Tds#getRow
93 */
94
95 public class ResultSet_base
96 {
97 public static final String cvsVersion = "$Id: ResultSet_base.java,v 1.1 2003/04/29 18:07:50 sinisa Exp $";
98
99
100 Tds tds = null;
101 Statement stmt = null;
102 Columns columnsInfo = null;
103 ResultSetMetaData metaData = null;
104 PacketRowResult currentRow = null;
105 boolean lastGetWasNull = false;
106
107 boolean hitEndOfData = false;
108 boolean isClosed = false;
109
110 private SQLWarningChain warningChain = null; // The warnings chain.
111
112 public ResultSet_base(Tds tds_, Statement stmt_, Columns columns_)
113 {
114 tds = tds_;
115 stmt = stmt_;
116 columnsInfo = columns_;
117
118 hitEndOfData = false;
119 warningChain = new SQLWarningChain();
120 }
121
122
123 protected void NotImplemented() throws java.sql.SQLException
124 {
125 throw new SQLException("Not implemented");
126 }
127
128
129 /***
130 * After this call getWarnings returns null until a new warning is
131 * reported for this ResultSet.
132 *
133 * @exception SQLException if a database-access error occurs.
134 */
135 public void clearWarnings() throws SQLException
136 {
137 warningChain.clearWarnings();
138 }
139
140 /***
141 * In some cases, it is desirable to immediately release a
142 * ResultSet's database and JDBC resources instead of waiting for
143 * this to happen when it is automatically closed; the close
144 * method provides this immediate release.
145 *
146 * <P><B>Note:</B> A ResultSet is automatically closed by the
147 * Statement that generated it when that Statement is closed,
148 * re-executed, or is used to retrieve the next result from a
149 * sequence of multiple results. A ResultSet is also automatically
150 * closed when it is garbage collected.
151 *
152 * @exception SQLException if a database-access error occurs.
153 */
154 public void close() throws SQLException
155 {
156 Exception exception = null;
157
158 if (isClosed)
159 {
160 // nop ???
161 }
162 else
163 {
164 isClosed = true;
165 try
166 {
167 if (!hitEndOfData)
168 {
169 tds.discardResultSet(columnsInfo);
170 hitEndOfData = true;
171 }
172 else
173 {
174 // nop
175 }
176 }
177 catch(com.internetcds.jdbc.tds.TdsException e)
178 {
179 e.printStackTrace();
180 exception = e;
181 }
182 catch(java.io.IOException e)
183 {
184 e.printStackTrace();
185 exception = e;
186 }
187
188 currentRow = null;
189 metaData = null;
190 columnsInfo = null;
191 stmt = null;
192
193 if (exception != null)
194 {
195 throw new SQLException(exception.getMessage());
196 }
197 }
198 }
199
200
201 //----------------------------------------------------------------
202
203 /***
204 * Map a Resultset column name to a ResultSet column index.
205 *
206 * @param columnName the name of the column
207 * @return the column index
208 * @exception SQLException if a database-access error occurs.
209 */
210 public int findColumn(String columnName) throws SQLException
211 {
212 int i;
213
214 for(i=1; i<=columnsInfo.getColumnCount(); i++)
215 {
216 if (columnsInfo.getName(i).equalsIgnoreCase(columnName))
217 {
218 return i;
219 }
220 // XXX also need to look at the fully qualified name ie. table.column
221 }
222 throw new SQLException("No such column " + columnName);
223 }
224
225
226 /***
227 * A column value can be retrieved as a stream of ASCII characters
228 * and then read in chunks from the stream. This method is particularly
229 * suitable for retrieving large LONGVARCHAR values. The JDBC driver will
230 * do any necessary conversion from the database format into ASCII.
231 *
232 * <P><B>Note:</B> All the data in the returned stream must be
233 * read prior to getting the value of any other column. The next
234 * call to a get method implicitly closes the stream. . Also, a
235 * stream may return 0 for available() whether there is data
236 * available or not.
237 *
238 * @param columnIndex the first column is 1, the second is 2, ...
239 * @return a Java input stream that delivers the database column value
240 * as a stream of one byte ASCII characters. If the value is SQL NULL
241 * then the result is null.
242 * @exception SQLException if a database-access error occurs.
243 */
244 public java.io.InputStream getAsciiStream(int columnIndex) throws SQLException
245 {
246 String val = getString(columnIndex);
247 if (val == null)
248 return null;
249 try {
250 return new ByteArrayInputStream(val.getBytes("ASCII"));
251 } catch (UnsupportedEncodingException ue) {
252 // plain impossible with encoding ASCII
253 return null;
254 }
255 }
256
257
258 /***
259 * A column value can be retrieved as a stream of ASCII characters
260 * and then read in chunks from the stream. This method is particularly
261 * suitable for retrieving large LONGVARCHAR values. The JDBC driver will
262 * do any necessary conversion from the database format into ASCII.
263 *
264 * <P><B>Note:</B> All the data in the returned stream must
265 * be read prior to getting the value of any other column. The
266 * next call to a get method implicitly closes the stream.
267 *
268 * @param columnName is the SQL name of the column
269 * @return a Java input stream that delivers the database column value
270 * as a stream of one byte ASCII characters. If the value is SQL NULL
271 * then the result is null.
272 * @exception SQLException if a database-access error occurs.
273 */
274 public java.io.InputStream getAsciiStream(String columnName) throws SQLException
275 {
276 return getAsciiStream(findColumn(columnName));
277 }
278
279
280 /***
281 * Get the value of a column in the current row as a
282 * java.lang.BigDecimal object.
283 *
284 * @param columnIndex the first column is 1, the second is 2, ...
285 * @param scale the number of digits to the right of the decimal
286 * @return the column value; if the value is SQL NULL, the result is null
287 * @exception SQLException if a database-access error occurs.
288 */
289 public BigDecimal getBigDecimal(int columnIndex, int scale)
290 throws SQLException
291 {
292 Object tmp = getObject(columnIndex);
293 BigDecimal result = null;
294
295
296 if (tmp == null)
297 {
298 result = null;
299 }
300 else if (tmp instanceof java.lang.Double)
301 {
302 result = new BigDecimal(((Double)tmp).doubleValue());
303 result = result.setScale(scale, BigDecimal.ROUND_HALF_UP);
304 }
305 else if (tmp instanceof java.lang.Float)
306 {
307 result = new BigDecimal(((Float)tmp).doubleValue());
308 result = result.setScale(scale, BigDecimal.ROUND_HALF_UP);
309 }
310 else if (tmp instanceof java.lang.Number)
311 {
312 // This handles Byte, Short, Integer, and Long
313 result = BigDecimal.valueOf(((Number)tmp).longValue(), scale);
314 }
315 else if (tmp instanceof BigDecimal)
316 {
317 result = (BigDecimal)tmp;
318 }
319 else if (tmp instanceof java.lang.String)
320 {
321 try
322 {
323 result = new BigDecimal((String)tmp);
324 }
325 catch (NumberFormatException e)
326 {
327 throw new SQLException(e.getMessage());
328 }
329 }
330 return result;
331 }
332
333 /***
334 * Get the value of a column in the current row as a
335 * java.lang.BigDecimal object.
336 *
337 * @param columnName is the SQL name of the column
338 * @param scale the number of digits to the right of the decimal
339 * @return the column value; if the value is SQL NULL, the result is null
340 * @exception SQLException if a database-access error occurs.
341 */
342 public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException
343 {
344 return getBigDecimal(findColumn(columnName), scale);
345 }
346
347
348 /***
349 * A column value can be retrieved as a stream of uninterpreted bytes
350 * and then read in chunks from the stream. This method is particularly
351 * suitable for retrieving large LONGVARBINARY values.
352 *
353 * <P><B>Note:</B> All the data in the returned stream must be
354 * read prior to getting the value of any other column. The next
355 * call to a get method implicitly closes the stream. Also, a
356 * stream may return 0 for available() whether there is data
357 * available or not.
358 *
359 * @param columnIndex the first column is 1, the second is 2, ...
360 * @return a Java input stream that delivers the database column value
361 * as a stream of uninterpreted bytes. If the value is SQL NULL
362 * then the result is null.
363 * @exception SQLException if a database-access error occurs.
364 */
365 public java.io.InputStream getBinaryStream(int columnIndex)
366 throws SQLException
367 {
368 byte[] bytes = getBytes(columnIndex);
369 if (bytes != null)
370 return new ByteArrayInputStream(bytes);
371 return null;
372 }
373
374
375 /***
376 * A column value can be retrieved as a stream of uninterpreted bytes
377 * and then read in chunks from the stream. This method is particularly
378 * suitable for retrieving large LONGVARBINARY values.
379 *
380 * <P><B>Note:</B> All the data in the returned stream must
381 * be read prior to getting the value of any other column. The
382 * next call to a get method implicitly closes the stream.
383 *
384 * @param columnName is the SQL name of the column
385 * @return a Java input stream that delivers the database column value
386 * as a stream of uninterpreted bytes. If the value is SQL NULL
387 * then the result is null.
388 * @exception SQLException if a database-access error occurs.
389 */
390 public java.io.InputStream getBinaryStream(String columnName)
391 throws SQLException
392 {
393 return getBinaryStream(findColumn(columnName));
394 }
395
396
397 /***
398 * Get the value of a column in the current row as a Java boolean.
399 *
400 * @param columnIndex the first column is 1, the second is 2, ...
401 * @return the column value; if the value is SQL NULL, the result is false
402 * @exception SQLException if a database-access error occurs.
403 */
404 public boolean getBoolean(int columnIndex) throws SQLException
405 {
406 Object obj = getObject(columnIndex);
407 boolean result;
408
409 if (obj == null)
410 {
411 result = false;
412 }
413 else
414 {
415 switch(getMetaData().getColumnType(columnIndex))
416 {
417 case java.sql.Types.TINYINT:
418 case java.sql.Types.SMALLINT:
419 case java.sql.Types.INTEGER:
420 case java.sql.Types.BIGINT:
421 case java.sql.Types.REAL:
422 case java.sql.Types.FLOAT:
423 case java.sql.Types.DOUBLE:
424 case java.sql.Types.DECIMAL:
425 case java.sql.Types.NUMERIC:
426 {
427 if (! (obj instanceof java.lang.Number))
428 {
429 // Must be out of sync with the implementation of
430 // Tds.getRow() for this to happen.
431 throw new SQLException("Internal error");
432 }
433 // Would somebody like to tell what a true/false has
434 // to do with a double?
435 result = ((java.lang.Number)obj).intValue()!=0;
436 break;
437 }
438 case java.sql.Types.BIT:
439 {
440 if (! (obj instanceof Boolean))
441 {
442 // Must be out of sync with the implementation of
443 // Tds.getRow() for this to happen.
444 throw new SQLException("Internal error");
445 }
446 result = ((Boolean)obj).booleanValue();
447 break;
448 }
449 case java.sql.Types.CHAR:
450 case java.sql.Types.VARCHAR:
451 case java.sql.Types.LONGVARCHAR:
452 {
453 // Okay, I'm really confused as to what you mean
454 // by a character string being true or false. What
455 // is the boolean value for "Let the wookie win"?
456 // But since the spec says I have to convert from
457 // character to boolean data...
458
459 if (! (obj instanceof String))
460 {
461 // Must be out of sync with the implementation of
462 // Tds.getRow() for this to happen.
463 throw new SQLException("Internal error");
464 }
465 char ch = (((String)obj) + "n").charAt(0);
466
467 result = (ch=='Y')||(ch=='y')||(ch=='t')||(ch=='T');
468 break;
469 }
470 default:
471 {
472 throw new SQLException("Can't convert column " + columnIndex
473 + " from "
474 + obj.getClass().getName()
475 + " to boolean");
476 }
477 }
478 }
479 return result;
480 } // getBoolean()
481
482
483 /***
484 * Get the value of a column in the current row as a Java boolean.
485 *
486 * @param columnName is the SQL name of the column
487 * @return the column value; if the value is SQL NULL, the result is false
488 * @exception SQLException if a database-access error occurs.
489 */
490 public boolean getBoolean(String columnName) throws SQLException
491 {
492 return getBoolean(findColumn(columnName));
493 } // getBoolean()
494
495
496 /***
497 * Get the value of a column in the current row as a Java byte.
498 *
499 * @param columnIndex the first column is 1, the second is 2, ...
500 * @return the column value; if the value is SQL NULL, the result is 0
501 * @exception SQLException if a database-access error occurs.
502 */
503 public byte getByte(int columnIndex) throws SQLException
504 {
505 return (byte) getLong(columnIndex);
506 }
507
508
509 /***
510 * Get the value of a column in the current row as a Java byte.
511 *
512 * @param columnName is the SQL name of the column
513 * @return the column value; if the value is SQL NULL, the result is 0
514 * @exception SQLException if a database-access error occurs.
515 */
516 public byte getByte(String columnName) throws SQLException
517 {
518 return getByte(findColumn(columnName));
519 }
520
521
522 /***
523 * Get the value of a column in the current row as a Java byte array.
524 * The bytes represent the raw values returned by the driver.
525 *
526 * @param columnIndex the first column is 1, the second is 2, ...
527 * @return the column value; if the value is SQL NULL, the result is null
528 * @exception SQLException if a database-access error occurs.
529 */
530 public byte[] getBytes(int columnIndex) throws SQLException
531 {
532 byte result[];
533
534 try
535 {
536 Object tmp = currentRow.getElementAt(columnIndex);
537 lastGetWasNull = false;
538 if (tmp == null)
539 {
540 lastGetWasNull = true;
541 result = null;
542 }
543 else if (tmp instanceof byte[])
544 {
545 result = (byte[])tmp;
546 }
547 else if (tmp instanceof String)
548 {
549 result = tds.getEncoder().getBytes((String)tmp);
550 }
551 else
552 {
553 throw new SQLException("Can't convert column " + columnIndex
554 + " from "
555 + tmp.getClass().getName()
556 + " to byte[]");
557 }
558 }
559 catch (TdsException e)
560 {
561 e.printStackTrace();
562 throw new SQLException(e.getMessage());
563 }
564 return result;
565 }
566
567
568 /***
569 * Get the value of a column in the current row as a Java byte array.
570 * The bytes represent the raw values returned by the driver.
571 *
572 * @param columnName is the SQL name of the column
573 * @return the column value; if the value is SQL NULL, the result is null
574 * @exception SQLException if a database-access error occurs.
575 */
576 public byte[] getBytes(String columnName) throws SQLException
577 {
578 return getBytes(findColumn(columnName));
579 }
580
581
582 /***
583 * Get the name of the SQL cursor used by this ResultSet.
584 *
585 * <P>In SQL, a result table is retrieved through a cursor that is
586 * named. The current row of a result can be updated or deleted
587 * using a positioned update/delete statement that references the
588 * cursor name.
589 *
590 * <P>JDBC supports this SQL feature by providing the name of the
591 * SQL cursor used by a ResultSet. The current row of a ResultSet
592 * is also the current row of this SQL cursor.
593 *
594 * <P><B>Note:</B> If positioned update is not supported a
595 * SQLException is thrown
596 *
597 * @return the ResultSet's SQL cursor name
598 * @exception SQLException if a database-access error occurs.
599 */
600 public String getCursorName() throws SQLException
601 {
602 throw new SQLException("Not implemented (getCursorName)");
603 }
604
605
606 /***
607 * Get the value of a column in the current row as a java.sql.Date object.
608 *
609 * @param columnIndex the first column is 1, the second is 2, ...
610 * @return the column value; if the value is SQL NULL, the result is null
611 * @exception SQLException if a database-access error occurs.
612 */
613 public java.sql.Date getDate(int columnIndex) throws SQLException
614 {
615 java.sql.Date result = null;
616 java.sql.Timestamp tmp = getTimestamp(columnIndex);
617
618 if (tmp != null)
619 {
620 result = new java.sql.Date(tmp.getTime());
621 }
622 return result;
623 }
624
625
626 /***
627 * Get the value of a column in the current row as a java.sql.Date object.
628 *
629 * @param columnName is the SQL name of the column
630 * @return the column value; if the value is SQL NULL, the result is null
631 * @exception SQLException if a database-access error occurs.
632 */
633 public java.sql.Date getDate(String columnName) throws SQLException
634 {
635 return getDate(findColumn(columnName));
636 }
637
638
639 /***
640 * Get the value of a column in the current row as a Java double.
641 *
642 * @param columnIndex the first column is 1, the second is 2, ...
643 * @return the column value; if the value is SQL NULL, the result is 0
644 * @exception SQLException if a database-access error occurs.
645 */
646 public double getDouble(int columnIndex) throws SQLException
647 {
648 double result;
649 Object obj = getObject(columnIndex);
650
651 if (obj == null)
652 {
653 result = 0.0;
654 }
655 else
656 {
657 try
658 {
659 switch(getMetaData().getColumnType(columnIndex))
660 {
661 case java.sql.Types.TINYINT:
662 case java.sql.Types.SMALLINT:
663 case java.sql.Types.INTEGER:
664 {
665 result = ((Number)obj).doubleValue();
666 break;
667 }
668 case java.sql.Types.BIGINT:
669 {
670 result = ((Number)obj).doubleValue();
671 break;
672 }
673 case java.sql.Types.REAL:
674 {
675 result = ((Float)obj).doubleValue();
676 break;
677 }
678 case java.sql.Types.FLOAT:
679 case java.sql.Types.DOUBLE:
680 {
681 result = ((Number)obj).doubleValue();
682 break;
683 }
684 case java.sql.Types.CHAR:
685 case java.sql.Types.VARCHAR:
686 case java.sql.Types.LONGVARCHAR:
687 {
688 try
689 {
690 Double d = new Double((String)obj);
691 result = d.doubleValue();
692 }
693 catch (NumberFormatException e)
694 {
695 throw new SQLException(e.getMessage());
696 }
697 break;
698 }
699 case java.sql.Types.DECIMAL:
700 case java.sql.Types.NUMERIC:
701 {
702 result = ((BigDecimal)obj).doubleValue();
703 break;
704 }
705 case java.sql.Types.BIT:
706 {
707 // XXX according to JDBC spec we need to handle these
708 // for now just fall through
709 }
710 default:
711 {
712 throw new SQLException("Internal error. "
713 + "Don't know how to convert from "
714 + "java.sql.Types." +
715 TdsUtil.javaSqlTypeToString(getMetaData().getColumnType(columnIndex))
716 + " to an Dboule");
717 }
718 }
719 }
720 catch(ClassCastException e)
721 {
722 throw new SQLException("Couldn't convert column " + columnIndex
723 + " to an long. "
724 + e.getMessage());
725 }
726 }
727 return result;
728 } /* getDouble() */
729
730
731 /***
732 * Get the value of a column in the current row as a Java double.
733 *
734 * @param columnName is the SQL name of the column
735 * @return the column value; if the value is SQL NULL, the result is 0
736 * @exception SQLException if a database-access error occurs.
737 */
738 public double getDouble(String columnName) throws SQLException
739 {
740 return getDouble(findColumn(columnName));
741 }
742
743
744 /***
745 * Get the value of a column in the current row as a Java float.
746 *
747 * @param columnIndex the first column is 1, the second is 2, ...
748 * @return the column value; if the value is SQL NULL, the result is 0
749 * @exception SQLException if a database-access error occurs.
750 */
751 public float getFloat(int columnIndex) throws SQLException
752 {
753 return (float)getDouble(columnIndex);
754 }
755
756
757 /***
758 * Get the value of a column in the current row as a Java float.
759 *
760 * @param columnName is the SQL name of the column
761 * @return the column value; if the value is SQL NULL, the result is 0
762 * @exception SQLException if a database-access error occurs.
763 */
764 public float getFloat(String columnName) throws SQLException
765 {
766 return getFloat(findColumn(columnName));
767 }
768
769
770 /***
771 * Get the value of a column in the current row as a Java int.
772 *
773 * @param columnIndex the first column is 1, the second is 2, ...
774 * @return the column value; if the value is SQL NULL, the result is 0
775 * @exception SQLException if a database-access error occurs.
776 */
777 public int getInt(int columnIndex) throws SQLException
778 {
779 return (int) getLong(columnIndex);
780 }
781
782
783 /***
784 * Get the value of a column in the current row as a Java int.
785 *
786 * @param columnName is the SQL name of the column
787 * @return the column value; if the value is SQL NULL, the result is 0
788 * @exception SQLException if a database-access error occurs.
789 */
790 public int getInt(String columnName) throws SQLException
791 {
792 return getInt(findColumn(columnName));
793 }
794
795
796 /***
797 * Get the value of a column in the current row as a Java long.
798 *
799 * @param columnIndex the first column is 1, the second is 2, ...
800 * @return the column value; if the value is SQL NULL, the result is 0
801 * @exception SQLException if a database-access error occurs.
802 */
803 public long getLong(int columnIndex) throws SQLException
804 {
805 long result = 0;
806 Object obj = getObject(columnIndex);
807
808 if (obj == null)
809 {
810 result = 0;
811 }
812 else
813 {
814 try
815 {
816 switch(getMetaData().getColumnType(columnIndex))
817 {
818 case java.sql.Types.TINYINT:
819 case java.sql.Types.SMALLINT:
820 case java.sql.Types.INTEGER:
821 {
822 result = ((Number)obj).longValue();
823 break;
824 }
825 case java.sql.Types.BIGINT:
826 {
827 result = ((Number)obj).longValue();
828 break;
829 }
830 case java.sql.Types.REAL:
831 case java.sql.Types.FLOAT:
832 case java.sql.Types.DOUBLE:
833 {
834 result = ((Number)obj).longValue();
835 break;
836 }
837 case java.sql.Types.CHAR:
838 case java.sql.Types.VARCHAR:
839 case java.sql.Types.LONGVARCHAR:
840 {
841 try
842 {
843 Long i = new Long((String)obj);
844 result = i.longValue();
845 }
846 catch (NumberFormatException e)
847 {
848 throw new SQLException(e.getMessage());
849 }
850 break;
851 }
852 case java.sql.Types.NUMERIC:
853 {
854 result = ((Number)obj).longValue();
855 break;
856 }
857 case java.sql.Types.DECIMAL:
858 {
859 result = ((Number)obj).longValue();
860 break;
861 }
862 case java.sql.Types.BIT:
863 {
864 // XXX according to JDBC spec we need to handle these
865 // for now just fall through
866 }
867 default:
868 {
869 throw new SQLException("Internal error. "
870 + "Don't know how to convert from "
871 + "java.sql.Types " +
872 TdsUtil.javaSqlTypeToString(getMetaData().getColumnType(columnIndex))
873 + " to an long");
874 }
875 }
876 }
877 catch(ClassCastException e)
878 {
879 throw new SQLException("Couldn't convert column " + columnIndex
880 + " to an long. "
881 + e.getMessage());
882 }
883 }
884 return result;
885 } /* getLong() */
886
887
888 /***
889 * Get the value of a column in the current row as a Java long.
890 *
891 * @param columnName is the SQL name of the column
892 * @return the column value; if the value is SQL NULL, the result is 0
893 * @exception SQLException if a database-access error occurs.
894 */
895 public long getLong(String columnName) throws SQLException
896 {
897 return getLong(findColumn(columnName));
898 }
899
900
901 /***
902 * The number, types and properties of a ResultSet's columns
903 * are provided by the getMetaData method.
904 *
905 * @return the description of a ResultSet's columns
906 * @exception SQLException if a database-access error occurs.
907 */
908 public java.sql.ResultSetMetaData getMetaData() throws SQLException
909 {
910 if (metaData == null)
911 {
912 metaData = new ResultSetMetaData(columnsInfo);
913 }
914 return metaData;
915 }
916
917
918 /***
919 * <p>Get the value of a column in the current row as a Java object.
920 *
921 * <p>This method will return the value of the given column as a
922 * Java object. The type of the Java object will be the default
923 * Java Object type corresponding to the column's SQL type,
924 * following the mapping specified in the JDBC spec.
925 *
926 * <p>This method may also be used to read datatabase specific abstract
927 * data types.
928 *
929 * JDBC 2.0
930 *
931 * In the JDBC 2.0 API, the behavior of method
932 * <code>getObject</code> is extended to materialize
933 * data of SQL user-defined types. When the a column contains
934 * a structured or distinct value, the behavior of this method is as
935 * if it were a call to: getObject(columnIndex,
936 * this.getStatement().getConnection().getTypeMap()).
937 *
938 * @param columnIndex the first column is 1, the second is 2, ...
939 * @return A java.lang.Object holding the column value.
940 * @exception SQLException if a database-access error occurs.
941 */
942 public Object getObject(int columnIndex) throws SQLException
943 {
944 // This method is implicitly coupled to the getRow() method in the
945 // Tds class. Every type that getRow() could return must
946 // be handled in this method.
947 //
948 // The object type returned by getRow() must correspond with the
949 // jdbc SQL type in the switch statement below.
950 //
951 // Note- The JDBC spec (version 1.20) does not define the type
952 // of the Object returned for LONGVARCHAR data.
953
954 // XXX- Needs modifications for JDBC 2.0
955
956 Object result = null;
957
958 if (currentRow == null)
959 {
960 throw new SQLException("No current row in the result set. " +
961 "Did you call ResultSet.next()?");
962 }
963
964 try
965 {
966 Object tmp = currentRow.getElementAt(columnIndex);
967 lastGetWasNull = false;
968 if (tmp == null)
969 {
970 lastGetWasNull = true;
971
972 result = null;
973 }
974 else
975 {
976 switch(getMetaData().getColumnType(columnIndex))
977 {
978 case java.sql.Types.CHAR:
979 case java.sql.Types.VARCHAR:
980 {
981 if (tmp instanceof String)
982 {
983 result = tmp;
984 }
985 else
986 {
987 throw new SQLException("Was expecting CHAR data. Got"
988 + tmp.getClass().getName());
989 }
990 break;
991 }
992 case java.sql.Types.TINYINT:
993 {
994 if (! (tmp instanceof Long))
995 {
996 throw new SQLException("Internal error");
997 }
998
999 result = new Byte((byte) ((Long)tmp).intValue());
1000 break;
1001 }
1002 case java.sql.Types.SMALLINT:
1003 {
1004 if (! (tmp instanceof Long))
1005 {
1006 throw new SQLException("Internal error");
1007 }
1008
1009 result = new Short((short) ((Long)tmp).intValue());
1010 break;
1011 }
1012 case java.sql.Types.INTEGER:
1013 {
1014 if (! (tmp instanceof Long))
1015 {
1016 throw new SQLException("Internal error");
1017 }
1018
1019 result = new Integer(((Long)tmp).intValue());
1020 break;
1021 }
1022 case java.sql.Types.BIGINT:
1023 {
1024 if (! (tmp instanceof Long))
1025 {
1026 throw new SQLException("Internal error");
1027 }
1028
1029 result = (Long)tmp;
1030 break;
1031 }
1032 case java.sql.Types.REAL:
1033 {
1034 if (! (tmp instanceof Float))
1035 {
1036 throw new SQLException("Internal error");
1037 }
1038
1039 result = (Float)tmp;
1040 break;
1041 }
1042 case java.sql.Types.FLOAT:
1043 case java.sql.Types.DOUBLE:
1044 {
1045 if (tmp instanceof Double)
1046 {
1047 result = (Double)tmp;
1048 }
1049 else if (tmp instanceof Float)
1050 {
1051 result = new Double(((Float)tmp).doubleValue());
1052 }
1053 else
1054 {
1055 throw new SQLException("Was expecting Double data. Got"
1056 + tmp.getClass().getName());
1057 }
1058
1059 break;
1060 }
1061 case java.sql.Types.DATE:
1062 {
1063 // XXX How do the time types hold up with timezones?
1064 if (! (tmp instanceof Timestamp))
1065 {
1066 throw new SQLException("Internal error");
1067 }
1068
1069 // java.util.Calendar cal = new java.util.GregorianCalendar();
1070 // cal.setTime(getTimestamp(columnIndex));
1071 // result = cal.getTime();
1072 result = new Date(((Timestamp)tmp).getTime());
1073 break;
1074 }
1075 case java.sql.Types.TIME:
1076 {
1077 if (! (tmp instanceof Timestamp))
1078 {
1079 throw new SQLException("Internal error");
1080 }
1081
1082 result = new Time(((Timestamp)tmp).getTime());
1083 break;
1084 }
1085 case java.sql.Types.TIMESTAMP:
1086 {
1087 if (! (tmp instanceof Timestamp))
1088 {
1089 throw new SQLException("Internal error");
1090 }
1091
1092 result = (Timestamp) tmp;
1093 break;
1094 }
1095 case java.sql.Types.BINARY:
1096 case java.sql.Types.VARBINARY:
1097 {
1098 result = getBytes(columnIndex);
1099 break;
1100 }
1101 case java.sql.Types.DECIMAL:
1102 case java.sql.Types.NUMERIC:
1103 {
1104 if (tmp instanceof BigDecimal)
1105 {
1106 result = ((BigDecimal)tmp);
1107 }
1108 else
1109 {
1110 throw new SQLException("Was expecting NUMERIC data. Got"
1111 + tmp.getClass().getName());
1112 }
1113 break;
1114 }
1115 case java.sql.Types.LONGVARCHAR:
1116 {
1117 if (tmp instanceof TdsAsciiInputStream)
1118 {
1119 result = ((TdsAsciiInputStream)tmp).toString();
1120 }
1121 else if (tmp instanceof java.lang.String)
1122 {
1123 result = tmp;
1124 }
1125 else
1126 {
1127 throw new SQLException("Was expecting LONGVARCHAR data. "
1128 + "Got "
1129 + tmp.getClass().getName());
1130 }
1131 break;
1132 }
1133 case java.sql.Types.LONGVARBINARY:
1134 {
1135 throw new SQLException("Not implemented");
1136 }
1137 case java.sql.Types.NULL:
1138 {
1139 throw new SQLException("Not implemented");
1140 }
1141 case java.sql.Types.OTHER:
1142 {
1143 throw new SQLException("Not implemented");
1144 }
1145 case java.sql.Types.BIT:
1146 {
1147 if (tmp instanceof Boolean)
1148 {
1149 result = ((Boolean)tmp);
1150 }
1151 else
1152 {
1153 throw new SQLException("Was expecting BIT data. "
1154 + "Got"
1155 + tmp.getClass().getName());
1156 }
1157 break;
1158 }
1159 default:
1160 {
1161 String msg = ""
1162 + "Unknown datatype "
1163 + getMetaData().getColumnType(columnIndex);
1164 throw new SQLException(msg);
1165 }
1166 }
1167 }
1168 }
1169 catch (com.internetcds.jdbc.tds.TdsException e)
1170 {
1171 e.printStackTrace();
1172 throw new SQLException(e.getMessage());
1173 }
1174 return result;
1175 } // getObject()
1176
1177
1178 /***
1179 * <p>Get the value of a column in the current row as a Java object.
1180 *
1181 * <p>This method will return the value of the given column as a
1182 * Java object. The type of the Java object will be the default
1183 * Java Object type corresponding to the column's SQL type,
1184 * following the mapping specified in the JDBC spec.
1185 *
1186 * JDBC 2.0
1187 *
1188 *
1189 * In the JDBC 2.0 API, the behavior of method
1190 * <code>getObject</code> is extended to materialize
1191 * data of SQL user-defined types. When the a column contains
1192 * a structured or distinct value, the behavior of this method is as
1193 * if it were a call to: getObject(columnIndex,
1194 * this.getStatement().getConnection().getTypeMap()).
1195 *
1196 * <p>This method may also be used to read datatabase specific abstract
1197 * data types.
1198 *
1199 * @param columnName is the SQL name of the column
1200 * @return A java.lang.Object holding the column value.
1201 * @exception SQLException if a database-access error occurs.
1202 */
1203 public Object getObject(String columnName) throws SQLException
1204 {
1205 return getObject(findColumn(columnName));
1206 }
1207
1208
1209 /***
1210 * Get the value of a column in the current row as a Java short.
1211 *
1212 * @param columnIndex the first column is 1, the second is 2, ...
1213 * @return the column value; if the value is SQL NULL, the result is 0
1214 * @exception SQLException if a database-access error occurs.
1215 */
1216 public short getShort(int columnIndex) throws SQLException
1217 {
1218 return (short) getLong(columnIndex);
1219 }
1220
1221
1222 /***
1223 * Get the value of a column in the current row as a Java short.
1224 *
1225 * @param columnName is the SQL name of the column
1226 * @return the column value; if the value is SQL NULL, the result is 0
1227 * @exception SQLException if a database-access error occurs.
1228 */
1229 public short getShort(String columnName) throws SQLException
1230 {
1231 return getShort(findColumn(columnName));
1232 }
1233
1234
1235 //======================================================================
1236 // Methods for accessing results by column index
1237 //======================================================================
1238
1239 /***
1240 * Get the value of a column in the current row as a Java String.
1241 *
1242 * @param columnIndex the first column is 1, the second is 2, ...
1243 * @return the column value; if the value is SQL NULL, the result is null
1244 * @exception SQLException if a database-access error occurs.
1245 */
1246 public String getString(int columnIndex) throws SQLException
1247 {
1248 Object tmp = getObject(columnIndex);
1249
1250 if (tmp == null)
1251 {
1252 return null;
1253 }
1254 else if (tmp instanceof byte[])
1255 {
1256 return new String((byte[])tmp);
1257 }
1258 else
1259 {
1260 return tmp.toString();
1261 }
1262 }
1263
1264
1265 //======================================================================
1266 // Methods for accessing results by column name
1267 //======================================================================
1268
1269 /***
1270 * Get the value of a column in the current row as a Java String.
1271 *
1272 * @param columnName is the SQL name of the column
1273 * @return the column value; if the value is SQL NULL, the result is null
1274 * @exception SQLException if a database-access error occurs.
1275 */
1276 public String getString(String columnName) throws SQLException
1277 {
1278 return getString(findColumn(columnName));
1279 }
1280
1281
1282 /***
1283 * Get the value of a column in the current row as a java.sql.Time object.
1284 *
1285 * @param columnIndex the first column is 1, the second is 2, ...
1286 * @return the column value; if the value is SQL NULL, the result is null
1287 * @exception SQLException if a database-access error occurs.
1288 */
1289 public java.sql.Time getTime(int columnIndex) throws SQLException
1290 {
1291 java.sql.Time result = null;
1292 java.sql.Timestamp tmp = getTimestamp(columnIndex);
1293
1294 if (tmp != null)
1295 {
1296 result = new java.sql.Time(tmp.getTime());
1297 }
1298 return result;
1299 }
1300
1301
1302 /***
1303 * Get the value of a column in the current row as a java.sql.Time object.
1304 *
1305 * @param columnName is the SQL name of the column
1306 * @return the column value; if the value is SQL NULL, the result is null
1307 * @exception SQLException if a database-access error occurs.
1308 */
1309 public java.sql.Time getTime(String columnName) throws SQLException
1310 {
1311 return getTime(findColumn(columnName));
1312 }
1313
1314
1315 /***
1316 * Get the value of a column in the current row as a java.sql.Timestamp object.
1317 *
1318 * @param columnIndex the first column is 1, the second is 2, ...
1319 * @return the column value; if the value is SQL NULL, the result is null
1320 * @exception SQLException if a database-access error occurs.
1321 */
1322 public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLException
1323 {
1324 Timestamp result;
1325
1326 try
1327 {
1328 Object tmp = currentRow.getElementAt(columnIndex);
1329
1330 lastGetWasNull = false;
1331 if (tmp == null)
1332 {
1333 lastGetWasNull = true;
1334 result = null;
1335 }
1336 else if (tmp instanceof Timestamp)
1337 {
1338 result = (Timestamp)tmp;
1339 }
1340 else
1341 {
1342 throw new SQLException("Can't convert column " + columnIndex
1343 + " from "
1344 + tmp.getClass().getName()
1345 + " to Timestamp");
1346 }
1347 }
1348 catch (TdsException e)
1349 {
1350 throw new SQLException(e.getMessage());
1351 }
1352 return result;
1353 }
1354
1355
1356 /***
1357 * Get the value of a column in the current row as a java.sql.Timestamp object.
1358 *
1359 * @param columnName is the SQL name of the column
1360 * @return the column value; if the value is SQL NULL, the result is null
1361 * @exception SQLException if a database-access error occurs.
1362 */
1363 public java.sql.Timestamp getTimestamp(String columnName) throws SQLException
1364 {
1365 return getTimestamp(findColumn(columnName));
1366 }
1367
1368
1369 /***
1370 * A column value can be retrieved as a stream of Unicode characters
1371 * and then read in chunks from the stream. This method is particularly
1372 * suitable for retrieving large LONGVARCHAR values. The JDBC driver will
1373 * do any necessary conversion from the database format into Unicode.
1374 *
1375 * <P><B>Note:</B> All the data in the returned stream must be
1376 * read prior to getting the value of any other column. The next
1377 * call to a get method implicitly closes the stream. . Also, a
1378 * stream may return 0 for available() whether there is data
1379 * available or not.
1380 *
1381 * @param columnIndex the first column is 1, the second is 2, ...
1382 * @return a Java input stream that delivers the database column value
1383 * as a stream of two byte Unicode characters. If the value is SQL NULL
1384 * then the result is null.
1385 * @exception SQLException if a database-access error occurs.
1386 */
1387 public java.io.InputStream getUnicodeStream(int columnIndex) throws SQLException
1388 {
1389 String val = getString(columnIndex);
1390 if (val == null)
1391 return null;
1392 try {
1393 return new ByteArrayInputStream(val.getBytes("UTF8"));
1394 } catch (UnsupportedEncodingException e) {
1395 // plain impossible with UTF-8
1396 return null;
1397 }
1398 }
1399
1400 /***
1401 * A column value can be retrieved as a stream of Unicode characters
1402 * and then read in chunks from the stream. This method is particularly
1403 * suitable for retrieving large LONGVARCHAR values. The JDBC driver will
1404 * do any necessary conversion from the database format into Unicode.
1405 *
1406 * <P><B>Note:</B> All the data in the returned stream must
1407 * be read prior to getting the value of any other column. The
1408 * next call to a get method implicitly closes the stream.
1409 *
1410 * @param columnName is the SQL name of the column
1411 * @return a Java input stream that delivers the database column value
1412 * as a stream of two byte Unicode characters. If the value is SQL NULL
1413 * then the result is null.
1414 * @exception SQLException if a database-access error occurs.
1415 */
1416 public java.io.InputStream getUnicodeStream(String columnName) throws SQLException
1417 {
1418 return getUnicodeStream(findColumn(columnName));
1419 }
1420
1421
1422 //=====================================================================
1423 // Advanced features:
1424 //=====================================================================
1425
1426 /***
1427 * <p>The first warning reported by calls on this ResultSet is
1428 * returned. Subsequent ResultSet warnings will be chained to this
1429 * SQLWarning.
1430 *
1431 * <P>The warning chain is automatically cleared each time a new
1432 * row is read.
1433 *
1434 * <P><B>Note:</B> This warning chain only covers warnings caused
1435 * by ResultSet methods. Any warning caused by statement methods
1436 * (such as reading OUT parameters) will be chained on the
1437 * Statement object.
1438 *
1439 * @return the first SQLWarning or null
1440 * @exception SQLException if a database-access error occurs.
1441 */
1442 public SQLWarning getWarnings() throws SQLException
1443 {
1444 return warningChain.getWarnings();
1445 }
1446
1447
1448 /***
1449 * A ResultSet is initially positioned before its first row; the
1450 * first call to next makes the first row the current row; the
1451 * second call makes the second row the current row, etc.
1452 *
1453 * <P>If an input stream from the previous row is open, it is
1454 * implicitly closed. The ResultSet's warning chain is cleared
1455 * when a new row is read.
1456 *
1457 * @return true if the new current row is valid; false if there
1458 * are no more rows
1459 * @exception SQLException if a database-access error occurs.
1460 */
1461 public boolean next() throws SQLException
1462 {
1463 boolean result = false;
1464 SQLException exception = null;
1465 boolean done = false;
1466 boolean wasCanceled = false;
1467
1468 if (isClosed)
1469 {
1470 throw new SQLException("result set is closed");
1471 }
1472 if(!hitEndOfData) {
1473 try
1474 {
1475 clearWarnings();
1476
1477 Context context = new Context();
1478 context.setColumnInfo(columnsInfo);
1479
1480
1481 // Keep eating garbage and warnings until we reach the next result
1482 while (!tds.isResultSet() &&
1483 !tds.isEndOfResults() &&
1484 !tds.isResultRow())
1485 {
1486 // RMK 2000-06-08: don't choke on RET_STAT package.
1487 if (tds.isProcId() || tds.peek() == Tds.TDS_RET_STAT_TOKEN)
1488 {
1489 tds.processSubPacket();
1490 }
1491 else if (tds.isDoneInProc())
1492 {
1493 PacketDoneInProcResult tmp =
1494 (PacketDoneInProcResult)tds.processSubPacket();
1495 }
1496 else if (tds.isTextUpdate())
1497 {
1498 PacketResult tmp1 =
1499 (PacketResult)tds.processSubPacket();
1500 }
1501 else if (tds.isMessagePacket() || tds.isErrorPacket())
1502 {
1503 PacketMsgResult tmp = (PacketMsgResult)tds.processSubPacket();
1504 exception = warningChain.addOrReturn(tmp);
1505 }
1506 else
1507 {
1508 throw new SQLException("Protocol confusion. "
1509 + "Got a 0x"
1510 + Integer.toHexString((tds.peek() & 0xff))
1511 + " packet");
1512 }
1513 } // end while
1514
1515 if (exception != null)
1516 {
1517 throw exception;
1518 }
1519
1520 if (tds.isResultRow())
1521 {
1522 currentRow = (PacketRowResult)tds.processSubPacket(context);
1523 result = true;
1524 done = true;
1525 }
1526 else if (tds.isEndOfResults())
1527 {
1528 PacketResult tmp = tds.processSubPacket(context);
1529 currentRow = null;
1530 done = true;
1531 hitEndOfData = true;
1532 wasCanceled = wasCanceled
1533 || ((PacketEndTokenResult)tmp).wasCanceled();
1534 }
1535 else if (!tds.isResultSet())
1536 {
1537 throw new SQLException("Protocol confusion. "
1538 + "Got a 0x"
1539 + Integer.toHexString((tds.peek() & 0xff))
1540 + " packet");
1541 }
1542
1543
1544 if (exception != null)
1545 {
1546 throw exception;
1547 }
1548 }
1549 catch(java.io.IOException e)
1550 {
1551 throw new SQLException(e.getMessage());
1552 }
1553 catch(TdsException e)
1554 {
1555 e.printStackTrace();
1556 throw new SQLException(e.getMessage());
1557 }
1558 if (wasCanceled)
1559 {
1560 throw new SQLException("Query was canceled or timed out.");
1561 }
1562 }
1563 return result;
1564 }
1565
1566
1567 /***
1568 * A column may have the value of SQL NULL; wasNull reports whether
1569 * the last column read had this special value.
1570 * Note that you must first call getXXX on a column to try to read
1571 * its value and then call wasNull() to find if the value was
1572 * the SQL NULL.
1573 *
1574 * @return true if last column read was SQL NULL
1575 * @exception SQLException if a database-access error occurs.
1576 */
1577 public boolean wasNull() throws SQLException
1578 {
1579 return lastGetWasNull;
1580 }
1581 }
This page was automatically generated by Maven