comparison src/main/java/org/monetdb/jdbc/MonetResultSet.java @ 391:f523727db392

Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.* This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12)
parents src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java@cc472ea19b3f
children bf9f6b6ecf40
comparison
equal deleted inserted replaced
390:6199e0be3c6e 391:f523727db392
1 /*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V.
7 */
8
9 package org.monetdb.jdbc;
10
11 import org.monetdb.mcl.parser.MCLParseException;
12 import org.monetdb.mcl.parser.TupleLineParser;
13 import java.io.InputStream;
14 import java.io.Reader;
15 import java.math.BigDecimal;
16 import java.net.URL;
17 import java.sql.Array;
18 import java.sql.Blob;
19 import java.sql.Clob;
20 import java.sql.Connection;
21 import java.sql.DatabaseMetaData;
22 import java.sql.NClob;
23 import java.sql.Ref;
24 import java.sql.ResultSet;
25 import java.sql.ResultSetMetaData;
26 import java.sql.RowId;
27 import java.sql.SQLData;
28 import java.sql.SQLDataException;
29 import java.sql.SQLException;
30 import java.sql.SQLFeatureNotSupportedException;
31 import java.sql.SQLInput;
32 import java.sql.SQLType; // new as of Java 1.8
33 import java.sql.SQLWarning;
34 import java.sql.SQLXML;
35 import java.sql.Statement;
36 import java.sql.Time;
37 import java.sql.Timestamp;
38 import java.sql.Types;
39 import java.text.SimpleDateFormat;
40 import java.util.Calendar;
41 import java.util.Map;
42 import java.util.TimeZone;
43
44 /**
45 * A ResultSet suitable for the MonetDB database.
46 *
47 * A table of data representing a database result set, which is usually
48 * generated by executing a statement that queries the database.
49 *
50 * A ResultSet object maintains a cursor pointing to its current row of data.
51 * Initially the cursor is positioned before the first row. The next method
52 * moves the cursor to the next row, and because it returns false when there
53 * are no more rows in the ResultSet object, it can be used in a while loop to
54 * iterate through the result set.
55 *
56 * The current state of this ResultSet is that it supports positioning in the
57 * result set, absolute and relative. A slight performance difference between
58 * FORWARD_ONLY or result sets scrollable in both directions can be noticed as
59 * for FORWARD_ONLY result sets the memory usage will be likely lower for large
60 * result sets.
61 *
62 * @author Fabian Groffen
63 * @author Martin van Dinther
64 * @version 0.9
65 */
66 public class MonetResultSet
67 extends MonetWrapper
68 implements ResultSet, AutoCloseable
69 {
70 static final int DEF_RESULTSETTYPE = ResultSet.TYPE_FORWARD_ONLY;
71 static final int DEF_FETCHDIRECTION = ResultSet.FETCH_FORWARD;
72 static final int DEF_CONCURRENCY = ResultSet.CONCUR_READ_ONLY;
73 static final int DEF_HOLDABILITY = ResultSet.HOLD_CURSORS_OVER_COMMIT;
74
75 /** The parental Statement object */
76 private final Statement statement;
77 /** A Header to retrieve lines from. Note: it will be null in case of a MonetVirtualResultSet ! */
78 private final MonetConnection.ResultSetResponse header;
79 /** The names of the columns in this ResultSet */
80 private final String[] columns;
81 /** The MonetDB types of the columns in this ResultSet */
82 private final String[] types;
83 /** The JDBC SQL types of the columns in this ResultSet. The content will be derived from the MonetDB types[] */
84 private final int[] JdbcSQLTypes;
85
86 // the following have protected access modifier for the MonetVirtualResultSet subclass
87 // they are accessed from MonetVirtualResultSet.absolute()
88 /** The current line of the buffer split in columns */
89 protected final TupleLineParser tlp;
90 /** The number of rows in this ResultSet */
91 protected final long tupleCount;
92 /** The current position of the cursor for this ResultSet object */
93 protected int curRow = 0;
94
95 /** The type of this ResultSet (forward or scrollable) */
96 private int type = DEF_RESULTSETTYPE;
97 /** The concurrency of this ResultSet (currently only read-only) */
98 private int concurrency = DEF_CONCURRENCY;
99 /** The warnings for this ResultSet object */
100 private SQLWarning warnings;
101 /** whether the last read field (via some getXyz() method) was NULL */
102 private boolean lastReadWasNull = true;
103 /** to store the fetchsize set. */
104 private int fetchSize;
105
106 /**
107 * Main constructor backed by the given Header.
108 *
109 * @param statement the statement which created this ResultSet
110 * @param header a header containing the query, resultset type, etc.
111 * @throws IllegalArgumentException if called with null or invalid value for one of the arguments
112 */
113 MonetResultSet(
114 final Statement statement,
115 final MonetConnection.ResultSetResponse header)
116 throws IllegalArgumentException
117 {
118 if (statement == null) {
119 throw new IllegalArgumentException("Statement may not be null!");
120 }
121 if (header == null) {
122 throw new IllegalArgumentException("Header may not be null!");
123 }
124 this.statement = statement;
125 this.header = header;
126 type = header.getRSType();
127 concurrency = header.getRSConcur();
128 /* the fetchSize used for this result set is the header's cacheSize */
129 fetchSize = header.getCacheSize();
130
131 columns = header.getNames();
132 types = header.getTypes();
133 if (columns == null || types == null) {
134 throw new IllegalArgumentException("Missing Header metadata");
135 }
136 if (columns.length != types.length) {
137 throw new IllegalArgumentException("Inconsistent Header metadata");
138 }
139 tupleCount = header.tuplecount;
140
141 // create result array
142 tlp = new TupleLineParser(columns.length);
143
144 // for efficiency derive the JDBC SQL type codes from the types[] names once
145 JdbcSQLTypes = new int[types.length];
146 populateJdbcSQLtypesArray();
147 }
148
149 /**
150 * Constructor used by MonetVirtualResultSet.
151 * DO NOT USE THIS CONSTRUCTOR IF YOU ARE NOT EXTENDING THIS OBJECT!
152 *
153 * @param statement the statement which created this ResultSet
154 * @param columns the column names
155 * @param types the column types
156 * @param results the number of rows in the ResultSet
157 * @throws IllegalArgumentException if called with null or invalid value for one of the arguments
158 */
159 MonetResultSet(
160 final Statement statement,
161 final String[] columns,
162 final String[] types,
163 final int results)
164 throws IllegalArgumentException
165 {
166 if (statement == null || columns == null || types == null) {
167 throw new IllegalArgumentException("One of the given arguments is null");
168 }
169 if (columns.length != types.length) {
170 throw new IllegalArgumentException("Given arrays are not the same size");
171 }
172 if (results < 0) {
173 throw new IllegalArgumentException("Negative rowcount not allowed");
174 }
175
176 this.statement = statement;
177 header = null;
178 fetchSize = 0;
179
180 this.columns = columns;
181 this.types = types;
182 tupleCount = results;
183
184 tlp = new TupleLineParser(columns.length);
185
186 // for efficiency derive the JDBC SQL type codes from the types[] names once
187 JdbcSQLTypes = new int[types.length];
188 populateJdbcSQLtypesArray();
189 }
190
191 /**
192 * Internal utility method to fill the JdbcSQLTypes array with derivable values.
193 * By doing it once (in the constructor) we can avoid doing this in many getXyz()
194 * methods again and again thereby improving getXyz() method performance.
195 */
196 private final void populateJdbcSQLtypesArray() {
197 MonetConnection connection = null;
198 try {
199 connection = (MonetConnection) statement.getConnection();
200 } catch (SQLException se) { /* ignore it */ }
201
202 for (int i = 0; i < types.length; i++) {
203 int javaSQLtype = MonetDriver.getJdbcSQLType(types[i]);
204 if (javaSQLtype == Types.CLOB) {
205 if (connection != null && connection.mapClobAsVarChar())
206 javaSQLtype = Types.VARCHAR;
207 } else
208 if (javaSQLtype == Types.BLOB) {
209 if (connection != null && connection.mapBlobAsVarBinary())
210 javaSQLtype = Types.VARBINARY;
211 }
212 JdbcSQLTypes[i] = javaSQLtype;
213 }
214 }
215
216
217 //== methods of interface ResultSet
218
219 // Chapter 14.2.2 Sun JDBC 3.0 Specification
220 /**
221 * Moves the cursor to the given row number in this ResultSet object.
222 *
223 * If the row number is positive, the cursor moves to the given row number
224 * with respect to the beginning of the result set. The first row is row 1,
225 * the second is row 2, and so on.
226 *
227 * If the given row number is negative, the cursor moves to an absolute row
228 * position with respect to the end of the result set. For example, calling
229 * the method absolute(-1) positions the cursor on the last row; calling the
230 * method absolute(-2) moves the cursor to the next-to-last row, and so on.
231 *
232 * An attempt to position the cursor beyond the first/last row in the result
233 * set leaves the cursor before the first row or after the last row.
234 * Note: calling absolute(1) is the same as calling first(). Calling
235 * absolute(-1) is the same as calling last().
236 *
237 * @param row the number of the row to which the cursor should move. A
238 * positive number indicates the row number counting from the
239 * beginning of the result set; a negative number indicates the row
240 * number counting from the end of the result set
241 * @return true if the cursor is on the result set; false otherwise
242 * @throws SQLException if a database access error occurs, or the result set
243 * type is TYPE_FORWARD_ONLY
244 */
245 @Override
246 public boolean absolute(int row) throws SQLException {
247 checkNotClosed();
248 if (row != curRow + 1 && type == TYPE_FORWARD_ONLY)
249 throw new SQLException("(Absolute) positioning not allowed on forward " +
250 " only result sets!", "M1M05");
251
252 // first calculate what the JDBC row is
253 if (row < 0) {
254 // calculate the negatives...
255 row = (int) tupleCount + row + 1;
256 }
257 // now place the row not farther than just before or after the result
258 if (row < 0)
259 row = 0; // before first
260 else if (row > tupleCount + 1)
261 row = (int) tupleCount + 1; // after last
262
263 // store it
264 curRow = row;
265
266 final String tmpLine = (header != null) ? header.getLine(row - 1) : null;
267 if (tmpLine == null)
268 return false;
269
270 try {
271 tlp.parse(tmpLine);
272 } catch (MCLParseException e) {
273 throw new SQLException(e.getMessage(), "M0M10");
274 }
275
276 return true;
277 }
278
279 /**
280 * Moves the cursor to the end of this ResultSet object, just after the last
281 * row. This method has no effect if the result set contains no rows.
282 *
283 * @throws SQLException if a database access error occurs or the result set
284 * type is TYPE_FORWARD_ONLY
285 */
286 @Override
287 public void afterLast() throws SQLException {
288 absolute((int)tupleCount + 1);
289 }
290
291 /**
292 * Moves the cursor to the front of this ResultSet object, just before the
293 * first row. This method has no effect if the result set contains no rows.
294 *
295 * @throws SQLException if a database access error occurs or the result set
296 * type is TYPE_FORWARD_ONLY
297 */
298 @Override
299 public void beforeFirst() throws SQLException {
300 absolute(0);
301 }
302
303 /**
304 * Clears all warnings reported for this ResultSet object. After a call to
305 * this method, the method getWarnings returns null until a new warning is
306 * reported for this ResultSet object.
307 */
308 @Override
309 public void clearWarnings() {
310 warnings = null;
311 }
312
313 /**
314 * Releases this ResultSet object's database (and JDBC) resources
315 * immediately instead of waiting for this to happen when it is
316 * automatically closed.
317 */
318 @Override
319 public void close() {
320 if (header != null && !header.isClosed()) {
321 header.close();
322 }
323 if (statement instanceof MonetStatement)
324 ((MonetStatement)statement).closeIfCompletion();
325 }
326
327 // Chapter 14.2.3 from Sun JDBC 3.0 specification
328 /**
329 * Maps the given ResultSet column name to its ResultSet column index.
330 * Column names supplied to getter methods are case insensitive. If a select
331 * list contains the same column more than once, the first instance of the
332 * column will be returned.
333 *
334 * @param columnLabel the name of the column
335 * @return the column index of the given column name
336 * @throws SQLException if the ResultSet object does not contain a column labeled columnLabel,
337 * a database access error occurs or this method is called on a closed result set
338 */
339 @Override
340 public int findColumn(final String columnLabel) throws SQLException {
341 checkNotClosed();
342 if (columnLabel != null) {
343 final int array_size = columns.length;
344 for (int i = 0; i < array_size; i++) {
345 if (columnLabel.equals(columns[i]))
346 return i + 1;
347 }
348 /* if an exact match did not succeed try a case insensitive match */
349 for (int i = 0; i < array_size; i++) {
350 if (columnLabel.equalsIgnoreCase(columns[i]))
351 return i + 1;
352 }
353 }
354 throw new SQLException("No such column name: " + columnLabel, "M1M05");
355 }
356
357 /**
358 * Moves the cursor to the first row in this ResultSet object.
359 *
360 * @return true if the cursor is on a valid row; false if there are no rows
361 * in the result set
362 * @throws SQLException - if a database access error occurs or the result
363 * set type is TYPE_FORWARD_ONLY
364 */
365 @Override
366 public boolean first() throws SQLException {
367 return absolute(1);
368 }
369
370 @Override
371 public Array getArray(final int columnIndex) throws SQLException {
372 throw newSQLFeatureNotSupportedException("getArray");
373 }
374 @Override
375 public Array getArray(final String columnLabel) throws SQLException {
376 throw newSQLFeatureNotSupportedException("getArray");
377 }
378
379 /* Mapi doesn't allow something for streams at the moment, thus all not implemented for now */
380 @Override
381 public InputStream getAsciiStream(final int columnIndex) throws SQLException {
382 throw newSQLFeatureNotSupportedException("getAsciiStream");
383 }
384 @Override
385 public InputStream getAsciiStream(final String columnLabel) throws SQLException {
386 throw newSQLFeatureNotSupportedException("getAsciiStream");
387 }
388
389 @Override
390 @Deprecated
391 public InputStream getUnicodeStream(int columnIndex) throws SQLException {
392 throw newSQLFeatureNotSupportedException("getUnicodeStream");
393 }
394 @Override
395 @Deprecated
396 public InputStream getUnicodeStream(String columnLabel) throws SQLException {
397 throw newSQLFeatureNotSupportedException("getUnicodeStream");
398 }
399
400 /**
401 * Retrieves the value of the designated column in the current row
402 * of this ResultSet object as a stream of uninterpreted bytes. The
403 * value can then be read in chunks from the stream. This method is
404 * particularly suitable for retrieving large LONGVARBINARY values.
405 *
406 * Note: All the data in the returned stream must be read prior to
407 * getting the value of any other column. The next call to a getter
408 * method implicitly closes the stream. Also, a stream may return 0
409 * when the method InputStream.available is called whether there is
410 * data available or not.
411 *
412 * @param columnIndex the first column is 1, the second is 2, ...
413 * @return a Java input stream that delivers the database column
414 * value as a stream of uninterpreted bytes; if the value is SQL
415 * NULL, the value returned is null
416 * @throws SQLException if the columnIndex is not valid; if a
417 * database access error occurs or this method is called on a closed result set
418 */
419 @Override
420 public InputStream getBinaryStream(final int columnIndex) throws SQLException {
421 checkNotClosed();
422 try {
423 switch (JdbcSQLTypes[columnIndex - 1]) {
424 case Types.BLOB:
425 final Blob blob = getBlob(columnIndex);
426 if (blob == null)
427 return null;
428 return blob.getBinaryStream();
429 case Types.BINARY:
430 case Types.VARBINARY:
431 case Types.LONGVARBINARY:
432 final byte[] bte = getBytes(columnIndex);
433 if (bte == null)
434 return null;
435 return new java.io.ByteArrayInputStream(bte);
436 }
437 throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05");
438 } catch (IndexOutOfBoundsException e) {
439 throw newSQLInvalidColumnIndexException(columnIndex);
440 }
441 }
442
443 /**
444 * Retrieves the value of the designated column in the current row
445 * of this ResultSet object as a stream of uninterpreted bytes. The
446 * value can then be read in chunks from the stream. This method is
447 * particularly suitable for retrieving large LONGVARBINARY values.
448 *
449 * Note: All the data in the returned stream must be read prior to
450 * getting the value of any other column. The next call to a getter
451 * method implicitly closes the stream. Also, a stream may return 0
452 * when the method available is called whether there is data
453 * available or not.
454 *
455 * @param columnLabel the label for the column specified with
456 * the SQL AS clause. If the SQL AS clause was not specified, then
457 * the label is the name of the column
458 * @return a Java input stream that delivers the database column
459 * value as a stream of uninterpreted bytes; if the value is SQL
460 * NULL, the result is null
461 * @throws SQLException if the columnLabel is not valid; if a
462 * database access error occurs or this method is called on a closed result set
463 */
464 @Override
465 public InputStream getBinaryStream(final String columnLabel) throws SQLException {
466 return getBinaryStream(findColumn(columnLabel));
467 }
468
469 /**
470 * Retrieves the value of the designated column in the current row
471 * of this ResultSet object as a java.io.Reader object.
472 *
473 * @param columnIndex the first column is 1, the second is 2, ...
474 * @return a java.io.Reader object that contains the column value;
475 * if the value is SQL NULL, the value returned is null in
476 * the Java programming language.
477 * @throws SQLException if a database access error occurs or this method is called on a closed result set
478 */
479 @Override
480 public Reader getCharacterStream(final int columnIndex) throws SQLException {
481 checkNotClosed();
482 try {
483 final String val = tlp.values[columnIndex - 1];
484 if (val == null) {
485 lastReadWasNull = true;
486 return null;
487 }
488 lastReadWasNull = false;
489 return new java.io.StringReader(val);
490 } catch (IndexOutOfBoundsException e) {
491 throw newSQLInvalidColumnIndexException(columnIndex);
492 }
493 }
494
495 /**
496 * Retrieves the value of the designated column in the current row
497 * of this ResultSet object as a java.io.Reader object.
498 *
499 * @param columnLabel the name of the column
500 * @return a java.io.Reader object that contains the column value;
501 * if the value is SQL NULL, the value returned is null in
502 * the Java programming language.
503 * @throws SQLException if a database access error occurs
504 */
505 @Override
506 public Reader getCharacterStream(final String columnLabel) throws SQLException {
507 return getCharacterStream(findColumn(columnLabel));
508 }
509
510 /**
511 * Retrieves the value of the designated column in the current row
512 * of this ResultSet object as a java.io.Reader object. It is
513 * intended for use when accessing NCHAR,NVARCHAR and LONGNVARCHAR
514 * columns.
515 *
516 * @param columnIndex the first column is 1, the second is 2, ...
517 * @return a java.io.Reader object that contains the column value;
518 * if the value is SQL NULL, the value returned is null in
519 * the Java programming language.
520 * @throws SQLException if a database access error occurs
521 */
522 @Override
523 public Reader getNCharacterStream(final int columnIndex) throws SQLException {
524 return getCharacterStream(columnIndex);
525 }
526
527 /**
528 * Retrieves the value of the designated column in the current row
529 * of this ResultSet object as a java.io.Reader object. It is
530 * intended for use when accessing NCHAR,NVARCHAR and LONGNVARCHAR
531 * columns.
532 *
533 * @param columnLabel the name of the column
534 * @return a java.io.Reader object that contains the column value;
535 * if the value is SQL NULL, the value returned is null in
536 * the Java programming language.
537 * @throws SQLException if a database access error occurs
538 */
539 @Override
540 public Reader getNCharacterStream(final String columnLabel) throws SQLException {
541 return getCharacterStream(findColumn(columnLabel));
542 }
543
544 /**
545 * Retrieves the value of the designated column in the current row
546 * of this ResultSet object as a Blob object in the Java programming
547 * language.
548 *
549 * @param columnIndex the first column is 1, the second is 2, ...
550 * @return a Blob object representing the SQL BLOB value in the
551 * specified column
552 * @throws SQLException if a database access error occurs or this method is called on a closed result set
553 */
554 @Override
555 public Blob getBlob(final int columnIndex) throws SQLException {
556 checkNotClosed();
557 try {
558 final String val = tlp.values[columnIndex - 1];
559 if (val == null) {
560 lastReadWasNull = true;
561 return null;
562 }
563 lastReadWasNull = false;
564 return new MonetBlob(val);
565 } catch (IndexOutOfBoundsException e) {
566 throw newSQLInvalidColumnIndexException(columnIndex);
567 }
568 }
569
570 /**
571 * Retrieves the value of the designated column in the current row
572 * of this ResultSet object as a Blob object in the Java programming
573 * language.
574 *
575 * @param columnLabel the name of the column from which to retrieve
576 * the value
577 * @return a Blob object representing the SQL BLOB value in the
578 * specified column
579 * @throws SQLException if a database access error occurs
580 */
581 @Override
582 public Blob getBlob(final String columnLabel) throws SQLException {
583 return getBlob(findColumn(columnLabel));
584 }
585
586 /**
587 * Retrieves the value of the designated column in the current row
588 * of this ResultSet object as a Clob object in the
589 * Java programming language.
590 *
591 * @param columnIndex the first column is 1, the second is 2, ...
592 * @return a Clob object representing the SQL CLOB value in the
593 * specified column
594 * @throws SQLException if a database access error occurs or this method is called on a closed result set
595 */
596 @Override
597 public Clob getClob(final int columnIndex) throws SQLException {
598 checkNotClosed();
599 try {
600 final String val = tlp.values[columnIndex - 1];
601 if (val == null) {
602 lastReadWasNull = true;
603 return null;
604 }
605 lastReadWasNull = false;
606 return new MonetClob(val);
607 } catch (IndexOutOfBoundsException e) {
608 throw newSQLInvalidColumnIndexException(columnIndex);
609 }
610 }
611
612 /**
613 * Retrieves the value of the designated column in the current row
614 * of this ResultSet object as a Clob object in the
615 * Java programming language.
616 *
617 * @param columnLabel the name of the column from which to retrieve
618 * the value
619 * @return a Clob object representing the SQL CLOB value in the
620 * specified column
621 * @throws SQLException if a database access error occurs
622 */
623 @Override
624 public Clob getClob(final String columnLabel) throws SQLException {
625 return getClob(findColumn(columnLabel));
626 }
627
628 /**
629 * Retrieves the value of the designated column in the current row
630 * of this ResultSet object as a NClob object in the
631 * Java programming language.
632 *
633 * @param columnIndex the first column is 1, the second is 2, ...
634 * @return a NClob object representing the SQL NCLOB value in the
635 * specified column
636 * @throws SQLException if a database access error occurs
637 * @throws SQLFeatureNotSupportedException the JDBC driver does
638 * not support this method
639 */
640 @Override
641 public NClob getNClob(final int columnIndex) throws SQLException {
642 throw newSQLFeatureNotSupportedException("getNClob");
643 }
644
645 /**
646 * Retrieves the value of the designated column in the current row
647 * of this ResultSet object as a NClob object in the
648 * Java programming language.
649 *
650 * @param columnLabel the name of the column from which to retrieve
651 * the value
652 * @return a NClob object representing the SQL NCLOB value in the
653 * specified column
654 * @throws SQLException if a database access error occurs
655 * @throws SQLFeatureNotSupportedException the JDBC driver does
656 * not support this method
657 */
658 @Override
659 public NClob getNClob(final String columnLabel) throws SQLException {
660 throw newSQLFeatureNotSupportedException("getNClob");
661 }
662
663 /**
664 * Retrieves the value of the designated column in the current row of this
665 * ResultSet object as a java.math.BigDecimal with full precision.
666 *
667 * @param columnIndex the first column is 1, the second is 2, ...
668 * @return the column value (full precision); if the value is SQL NULL,
669 * the value returned is null in the Java programming language.
670 * @throws SQLException if a database access error occurs or this method is called on a closed result set
671 */
672 @Override
673 public BigDecimal getBigDecimal(final int columnIndex) throws SQLException {
674 checkNotClosed();
675 try {
676 final String val = tlp.values[columnIndex - 1];
677 if (val == null) {
678 lastReadWasNull = true;
679 return null;
680 }
681 lastReadWasNull = false;
682 return new BigDecimal(val);
683 } catch (NumberFormatException e) {
684 throw newSQLNumberFormatException(e);
685 } catch (IndexOutOfBoundsException e) {
686 throw newSQLInvalidColumnIndexException(columnIndex);
687 }
688 }
689
690 /**
691 * Retrieves the value of the designated column in the current row of this
692 * ResultSet object as a java.math.BigDecimal with full precision.
693 *
694 * @param columnIndex the first column is 1, the second is 2, ...
695 * @param scale the number of digits to the right of the decimal point
696 * @return the column value (full precision); if the value is SQL NULL,
697 * the value returned is null in the Java programming language.
698 * @throws SQLException if a database access error occurs or this method is called on a closed result set
699 */
700 @Override
701 @Deprecated
702 public BigDecimal getBigDecimal(final int columnIndex, final int scale)
703 throws SQLException
704 {
705 checkNotClosed();
706 try {
707 final String val = tlp.values[columnIndex - 1];
708 if (val == null) {
709 lastReadWasNull = true;
710 return null;
711 }
712 lastReadWasNull = false;
713 return (new BigDecimal(val)).setScale(scale);
714 } catch (NumberFormatException e) {
715 throw newSQLNumberFormatException(e);
716 } catch (IndexOutOfBoundsException e) {
717 throw newSQLInvalidColumnIndexException(columnIndex);
718 }
719 }
720
721 /**
722 * Retrieves the value of the designated column in the current row of this
723 * ResultSet object as a java.math.BigDecimal with full precision.
724 *
725 * @param columnLabel the SQL name of the column
726 * @return the column value (full precision); if the value is SQL NULL,
727 * the value returned is null in the Java programming language.
728 * @throws SQLException if a database access error occurs
729 */
730 @Override
731 public BigDecimal getBigDecimal(final String columnLabel) throws SQLException {
732 return getBigDecimal(findColumn(columnLabel));
733 }
734
735 /**
736 * Retrieves the value of the designated column in the current row of this
737 * ResultSet object as a java.math.BigDecimal with full precision.
738 *
739 * @param columnLabel the SQL name of the column
740 * @param scale the number of digits to the right of the decimal point
741 * @return the column value (full precision); if the value is SQL NULL,
742 * the value returned is null in the Java programming language.
743 * @throws SQLException if a database access error occurs
744 */
745 @Override
746 @Deprecated
747 public BigDecimal getBigDecimal(final String columnLabel, final int scale)
748 throws SQLException
749 {
750 return getBigDecimal(findColumn(columnLabel), scale);
751 }
752
753 // See Sun JDBC Specification 3.0 Table B-6
754 /**
755 * Retrieves the value of the designated column in the current row of this
756 * ResultSet object as a boolean in the Java programming language.
757 *
758 * @param columnIndex the first column is 1, the second is 2, ...
759 * @return the column value; if the value is SQL NULL, the value returned
760 * is false
761 * @throws SQLException if the columnIndex is not valid; if a database access error occurs
762 * or this method is called on a closed result set
763 */
764 @Override
765 public boolean getBoolean(final int columnIndex) throws SQLException {
766 checkNotClosed();
767 try {
768 final String val = tlp.values[columnIndex - 1];
769 if (val == null) {
770 lastReadWasNull = true;
771 return false; // if the value is SQL NULL, the value returned is false
772 }
773 lastReadWasNull = false;
774
775 // match common cases first
776 if ("false".equalsIgnoreCase(val) || "0".equals(val))
777 return false;
778 if ("true".equalsIgnoreCase(val) || "1".equals(val))
779 return true;
780
781 // match type specific values
782 switch (JdbcSQLTypes[columnIndex - 1]) {
783 case Types.BOOLEAN:
784 case Types.CHAR:
785 case Types.VARCHAR:
786 case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
787 case Types.CLOB:
788 // check if string value equals "true" (case insensitive) or not
789 return Boolean.parseBoolean(val);
790 case Types.TINYINT:
791 case Types.SMALLINT:
792 case Types.INTEGER:
793 if (getInt(columnIndex) == 0) {
794 return false;
795 }
796 return true;
797 case Types.BIGINT:
798 if (getLong(columnIndex) == 0L) {
799 return false;
800 }
801 return true;
802 case Types.DOUBLE:
803 case Types.FLOAT:
804 case Types.REAL:
805 if (getDouble(columnIndex) == 0.0) {
806 return false;
807 }
808 return true;
809 case Types.DECIMAL:
810 case Types.NUMERIC:
811 if (getBigDecimal(columnIndex).compareTo(BigDecimal.ZERO) == 0) {
812 return false;
813 }
814 return true;
815 default:
816 throw new SQLException("Conversion from " + types[columnIndex - 1] + " to boolean type not supported", "M1M05");
817 }
818 } catch (IndexOutOfBoundsException e) {
819 throw newSQLInvalidColumnIndexException(columnIndex);
820 }
821 }
822
823 /**
824 * Retrieves the value of the designated column in the current row of this
825 * ResultSet object as a boolean in the Java programming language.
826 *
827 * @param columnLabel the SQL name of the column
828 * @return the column value; if the value is SQL NULL, the value returned
829 * is false
830 * @throws SQLException if the ResultSet object does not contain columnLabel
831 */
832 @Override
833 public boolean getBoolean(final String columnLabel) throws SQLException {
834 return getBoolean(findColumn(columnLabel));
835 }
836
837 /**
838 * Retrieves the value of the designated column in the current row of this
839 * ResultSet object as a byte in the Java programming language.
840 *
841 * @param columnIndex the first column is 1, the second is 2, ...
842 * @return the column value; if the value is SQL NULL, the value returned
843 * is 0
844 * @throws SQLException if a database access error occurs or this method is called on a closed result set
845 */
846 @Override
847 public byte getByte(final int columnIndex) throws SQLException {
848 checkNotClosed();
849 try {
850 final String val = tlp.values[columnIndex - 1];
851 if (val == null) {
852 lastReadWasNull = true;
853 return (byte) 0;
854 }
855 lastReadWasNull = false;
856 return Byte.parseByte(val);
857 } catch (NumberFormatException e) {
858 throw newSQLNumberFormatException(e);
859 } catch (IndexOutOfBoundsException e) {
860 throw newSQLInvalidColumnIndexException(columnIndex);
861 }
862 }
863
864 /**
865 * Retrieves the value of the designated column in the current row of this
866 * ResultSet object as a byte in the Java programming language.
867 *
868 * @param columnLabel the SQL name of the column
869 * @return the column value; if the value is SQL NULL, the value returned
870 * is 0
871 * @throws SQLException if a database access error occurs
872 */
873 @Override
874 public byte getByte(final String columnLabel) throws SQLException {
875 return getByte(findColumn(columnLabel));
876 }
877
878 /**
879 * Retrieves the value of the designated column in the current row of this
880 * ResultSet object as a byte array in the Java programming language. The
881 * bytes represent the raw values returned by the driver.
882 *
883 * @param columnIndex the first column is 1, the second is 2, ...
884 * @return the column value; if the value is SQL NULL, the value returned
885 * is null
886 * @throws SQLException if a database access error occurs or this method is called on a closed result set
887 */
888 @Override
889 public byte[] getBytes(final int columnIndex) throws SQLException {
890 checkNotClosed();
891 try {
892 final String val = tlp.values[columnIndex - 1];
893 if (val == null) {
894 lastReadWasNull = true;
895 return null;
896 }
897 lastReadWasNull = false;
898
899 // According to Table B-6, getBytes() only operates on BINARY types
900 switch (JdbcSQLTypes[columnIndex - 1]) {
901 case Types.BLOB:
902 case Types.BINARY:
903 case Types.VARBINARY:
904 case Types.LONGVARBINARY:
905 return MonetBlob.hexStrToByteArray(val);
906 default:
907 throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05");
908 }
909 } catch (NumberFormatException e) {
910 throw newSQLNumberFormatException(e);
911 } catch (IndexOutOfBoundsException e) {
912 throw newSQLInvalidColumnIndexException(columnIndex);
913 }
914 }
915
916 /**
917 * Retrieves the value of the designated column in the current row of this
918 * ResultSet object as a byte array in the Java programming language. The
919 * bytes represent the raw values returned by the driver.
920 *
921 * NOTE: Since the mapi protocol is ASCII-based, this method only returns
922 * Java byte representations of Strings, which is nothing more than
923 * an encoding into a sequence of bytes using the platform's default
924 * charset.
925 *
926 * @param columnLabel the SQL name of the column
927 * @return the column value; if the value is SQL NULL, the value returned
928 * is null
929 * @throws SQLException if a database access error occurs
930 */
931 @Override
932 public byte[] getBytes(final String columnLabel) throws SQLException {
933 return getBytes(findColumn(columnLabel));
934 }
935
936 /**
937 * Retrieves the concurrency mode of this ResultSet object. The concurrency
938 * used is determined by the Statement object that created the result set.
939 *
940 * NOTE: MonetDB only supports read-only result sets, and will always return
941 * ResultSet.CONCUR_READ_ONLY
942 *
943 * @return the concurrency type, either ResultSet.CONCUR_READ_ONLY or
944 * ResultSet.CONCUR_UPDATABLE
945 */
946 @Override
947 public int getConcurrency() {
948 return concurrency;
949 }
950
951 /**
952 * Retrieves the name of the SQL cursor used by this ResultSet object.
953 * In SQL, a result table is retrieved through a cursor that is named.
954 * For MonetDB this is the header.id returned in a resultset header. The
955 * current row of a result set can be updated or deleted using a positioned
956 * update/delete statement that references the cursor name. To insure that
957 * the cursor has the proper isolation level to support update, the
958 * cursor's SELECT statement should be of the form SELECT FOR UPDATE. If
959 * FOR UPDATE is omitted, the positioned updates may fail.
960 *
961 * The JDBC API supports this SQL feature by providing the name of the SQL
962 * cursor used by a ResultSet object. The current row of a ResultSet object
963 * is also the current row of this SQL cursor.
964 *
965 * Note: If positioned update is not supported, a SQLException is thrown.
966 * MonetDB currently doesn't support updates, so the SQLException is
967 * thrown for now.
968 *
969 * @return the SQL name for this ResultSet object's cursor
970 * @throws SQLException if a database access error occurs
971 */
972 @Override
973 public String getCursorName() throws SQLException {
974 throw new SQLException("Positioned updates not supported for this " +
975 "cursor (" + (header != null ? header.id : "") + ")", "0AM21");
976 }
977
978 /**
979 * Retrieves the value of the designated column in the current row of this
980 * ResultSet object as a double in the Java programming language.
981 *
982 * @param columnIndex the first column is 1, the second is 2, ...
983 * @return the column value; if the value is SQL NULL, the value returned is 0
984 * @throws SQLException if there is no such column or this method is called on a closed result set
985 */
986 @Override
987 public double getDouble(final int columnIndex) throws SQLException {
988 checkNotClosed();
989 try {
990 final String val = tlp.values[columnIndex - 1];
991 if (val == null) {
992 lastReadWasNull = true;
993 return 0;
994 }
995 lastReadWasNull = false;
996 return Double.parseDouble(val);
997 } catch (NumberFormatException e) {
998 throw newSQLNumberFormatException(e);
999 } catch (IndexOutOfBoundsException e) {
1000 throw newSQLInvalidColumnIndexException(columnIndex);
1001 }
1002 }
1003
1004 /**
1005 * Retrieves the value of the designated column in the current row of this
1006 * ResultSet object as a double in the Java programming language.
1007 *
1008 * @param columnLabel the SQL name of the column
1009 * @return the column value; if the value is SQL NULL, the value returned is 0
1010 * @throws SQLException if the ResultSet object does not contain columnLabel
1011 */
1012 @Override
1013 public double getDouble(final String columnLabel) throws SQLException {
1014 return getDouble(findColumn(columnLabel));
1015 }
1016
1017 /**
1018 * Retrieves the holdability of this ResultSet object.
1019 *
1020 * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or
1021 * ResultSet.CLOSE_CURSORS_AT_COMMIT
1022 * @throws SQLException if a database access error occurs
1023 */
1024 @Override
1025 public int getHoldability() throws SQLException {
1026 return getStatement().getConnection().getHoldability();
1027 }
1028
1029 /**
1030 * Retrieves the fetch direction for this ResultSet object.
1031 *
1032 * @return the current fetch direction for this ResultSet object
1033 */
1034 @Override
1035 public int getFetchDirection() {
1036 return ResultSet.FETCH_FORWARD;
1037 }
1038
1039 /**
1040 * Gives a hint as to the direction in which the rows in this ResultSet
1041 * object will be processed. The initial value is determined by the
1042 * Statement object that produced this ResultSet object.
1043 * The fetch direction may be changed at any time.
1044 *
1045 * @param direction - an int specifying the suggested fetch direction;
1046 * one of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN
1047 */
1048 @Override
1049 public void setFetchDirection(final int direction) throws SQLException {
1050 switch (direction) {
1051 case ResultSet.FETCH_FORWARD:
1052 break;
1053 case ResultSet.FETCH_REVERSE:
1054 case ResultSet.FETCH_UNKNOWN:
1055 throw new SQLException("Not supported direction " + direction, "0A000");
1056 default:
1057 throw new SQLException("Illegal direction: " + direction, "M1M05");
1058 }
1059 }
1060
1061 /**
1062 * Retrieves the fetch size for this ResultSet object.
1063 *
1064 * @return the current fetch size for this ResultSet object
1065 * @throws SQLException if a database access error occurs
1066 */
1067 @Override
1068 public int getFetchSize() throws SQLException {
1069 return fetchSize;
1070 }
1071
1072 /**
1073 * Gives the JDBC driver a hint as to the number of rows that should be
1074 * fetched from the database when more rows are needed. In MonetDB, this is
1075 * actually a no-op, because even before a MonetResultSet object is
1076 * created, the fetch size is already determined in the
1077 * MonetConnection.ResultSetResponse passed to its constructor. Since all
1078 * data blocks for this whole result set are already allocated in
1079 * MonetConnection.ResultSetResponse, it is too complicated and error-prone
1080 * to still change the fetchSize here. If one really needs to overwrite
1081 * the default fetchSize, please use MonetStatement.setFetchSize() instead.
1082 *
1083 * @param rows the number of rows to fetch
1084 * @throws SQLException if the condition 0 &lt;= rows is not satisfied
1085 */
1086 @Override
1087 public void setFetchSize(final int rows) throws SQLException {
1088 if (rows >= 0) {
1089 fetchSize = rows;
1090 } else {
1091 throw new SQLException("Illegal fetch size value: " + rows, "M1M05");
1092 }
1093 }
1094
1095 /**
1096 * Retrieves the value of the designated column in the current row of this
1097 * ResultSet object as a float in the Java programming language.
1098 *
1099 * @param columnIndex the first column is 1, the second is 2, ...
1100 * @return the column value; if the value is SQL NULL, the value returned is 0
1101 * @throws SQLException if there is no such column or this method is called on a closed result set
1102 */
1103 @Override
1104 public float getFloat(final int columnIndex) throws SQLException {
1105 checkNotClosed();
1106 try {
1107 final String val = tlp.values[columnIndex - 1];
1108 if (val == null) {
1109 lastReadWasNull = true;
1110 return 0;
1111 }
1112 lastReadWasNull = false;
1113 return Float.parseFloat(val);
1114 } catch (NumberFormatException e) {
1115 throw newSQLNumberFormatException(e);
1116 } catch (IndexOutOfBoundsException e) {
1117 throw newSQLInvalidColumnIndexException(columnIndex);
1118 }
1119 }
1120
1121 /**
1122 * Retrieves the value of the designated column in the current row of this
1123 * ResultSet object as a float in the Java programming language.
1124 *
1125 * @param columnLabel the SQL name of the column
1126 * @return the column value; if the value is SQL NULL, the value returned is 0
1127 * @throws SQLException if the ResultSet object does not contain columnLabel
1128 */
1129 @Override
1130 public float getFloat(final String columnLabel) throws SQLException {
1131 return getFloat(findColumn(columnLabel));
1132 }
1133
1134 /**
1135 * Retrieves the value of the designated column in the current row of this
1136 * ResultSet object as an int in the Java programming language.
1137 *
1138 * @param columnIndex the first column is 1, the second is 2, ...
1139 * @return the column value; if the value is SQL NULL, the value returned is 0
1140 * @throws SQLException if there is no such column or this method is called on a closed result set
1141 */
1142 @Override
1143 public int getInt(final int columnIndex) throws SQLException {
1144 checkNotClosed();
1145 String val = "";
1146 try {
1147 val = tlp.values[columnIndex - 1];
1148 if (val == null) {
1149 lastReadWasNull = true;
1150 return 0;
1151 }
1152 lastReadWasNull = false;
1153 return Integer.parseInt(val);
1154 } catch (NumberFormatException e) {
1155 // The oid datatype values (as string) have a @0 suffix in the string value.
1156 // To allow succesful parsing and conversion to int, we need to remove the suffix first
1157 if ("oid".equals(types[columnIndex - 1])) {
1158 if (val.endsWith("@0")) {
1159 try {
1160 return Integer.parseInt(val.substring(0, val.length()-2));
1161 } catch (NumberFormatException nfe) {
1162 throw newSQLNumberFormatException(nfe);
1163 }
1164 }
1165 }
1166 throw newSQLNumberFormatException(e);
1167 } catch (IndexOutOfBoundsException e) {
1168 throw newSQLInvalidColumnIndexException(columnIndex);
1169 }
1170 }
1171
1172 /**
1173 * Retrieves the value of the designated column in the current row of this
1174 * ResultSet object as an int in the Java programming language.
1175 *
1176 * @param columnLabel the SQL name of the column
1177 * @return the column value; if the value is SQL NULL, the value returned is 0
1178 * @throws SQLException if the ResultSet object does not contain columnLabel
1179 */
1180 @Override
1181 public int getInt(final String columnLabel) throws SQLException {
1182 return getInt(findColumn(columnLabel));
1183 }
1184
1185 /**
1186 * Retrieves the value of the designated column in the current row of this
1187 * ResultSet object as a long in the Java programming language.
1188 *
1189 * @param columnIndex the first column is 1, the second is 2, ...
1190 * @return the column value; if the value is SQL NULL, the value returned is 0
1191 * @throws SQLException if there is no such column or this method is called on a closed result set
1192 */
1193 @Override
1194 public long getLong(final int columnIndex) throws SQLException {
1195 checkNotClosed();
1196 String val = "";
1197 try {
1198 val = tlp.values[columnIndex - 1];
1199 if (val == null) {
1200 lastReadWasNull = true;
1201 return 0;
1202 }
1203 lastReadWasNull = false;
1204 return Long.parseLong(val);
1205 } catch (NumberFormatException e) {
1206 // The oid datatype values (as string) have a @0 suffix in the string value.
1207 // To allow succesful parsing and conversion to long, we need to remove the suffix first
1208 if ("oid".equals(types[columnIndex - 1])) {
1209 if (val.endsWith("@0")) {
1210 try {
1211 return Long.parseLong(val.substring(0, val.length()-2));
1212 } catch (NumberFormatException nfe) {
1213 throw newSQLNumberFormatException(nfe);
1214 }
1215 }
1216 }
1217 throw newSQLNumberFormatException(e);
1218 } catch (IndexOutOfBoundsException e) {
1219 throw newSQLInvalidColumnIndexException(columnIndex);
1220 }
1221 }
1222
1223 /**
1224 * Retrieves the value of the designated column in the current row of this
1225 * ResultSet object as a long in the Java programming language.
1226 *
1227 * @param columnLabel the SQL name of the column
1228 * @return the column value; if the value is SQL NULL, the value returned is 0
1229 * @throws SQLException if the ResultSet object does not contain columnLabel
1230 */
1231 @Override
1232 public long getLong(final String columnLabel) throws SQLException {
1233 return getLong(findColumn(columnLabel));
1234 }
1235
1236
1237 /* helper for the anonymous class inside getMetaData */
1238 private abstract class rsmdw extends MonetWrapper implements ResultSetMetaData {}
1239
1240 /**
1241 * Retrieves the number, types and properties of this ResultSet object's
1242 * columns.
1243 *
1244 * @return the description of this ResultSet object's columns
1245 */
1246 @Override
1247 public ResultSetMetaData getMetaData() throws SQLException {
1248 // return inner class which implements the ResultSetMetaData interface
1249 return new rsmdw() {
1250 // for the more expensive methods (getPrecision(), getScale(), isNullable(), isAutoIncrement()), we
1251 // use caches to store precision, scale and isNullable values from getColumns() combined per fully qualified column.
1252 private final int array_size = columns.length + 1; // add 1 as in JDBC columns start from 1 (array from 0).
1253 private final boolean[] _is_fetched = new boolean[array_size];
1254 private final int[] _precision = new int[array_size];
1255 private final int[] _scale = new int[array_size];
1256 private final int[] _isNullable = new int[array_size];
1257 private final boolean[] _isAutoincrement = new boolean[array_size];
1258 private final Connection conn = getStatement().getConnection();
1259 private DatabaseMetaData dbmd = null; // it will be assigned at first need and reused for other columns
1260
1261 /**
1262 * A private utility method to check validity of column index number
1263 * @throws an SQLDataException when invalid column index number
1264 */
1265 private final void checkColumnIndexValidity(final int column) throws SQLDataException {
1266 if (column < 1 || column > columns.length)
1267 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1268 }
1269
1270 /**
1271 * A private method to fetch the precision, scale, isNullable and isAutoincrement value for a fully qualified column.
1272 * As md.getColumns() is an expensive method we call it only once per column and store
1273 * the precision, scale, isNullable and isAutoincrement values in the above array caches.
1274 * Also we only call md.getColumns() when we have a non empty schema name and table name and column name.
1275 */
1276 private final void fetchColumnInfo(final int column) throws SQLException
1277 {
1278 checkColumnIndexValidity(column);
1279
1280 _is_fetched[column] = true;
1281 _precision[column] = 0;
1282 _scale[column] = 0;
1283 _isNullable[column] = columnNullableUnknown;
1284 _isAutoincrement[column] = false;
1285
1286 if (dbmd == null) {
1287 // first time usage
1288 dbmd = conn.getMetaData();
1289 if (dbmd == null)
1290 return;
1291 }
1292
1293 // we will only call dbmd.getColumns() when we have a specific schema name and table name and column name
1294 final String schName = getSchemaName(column);
1295 if (schName != null && !schName.isEmpty()) {
1296 final String tblName = getTableName(column);
1297 if (tblName != null && !tblName.isEmpty()) {
1298 final String colName = getColumnName(column);
1299 if (colName != null && !colName.isEmpty()) {
1300 // for precision, scale, isNullable and isAutoincrement we query the information from data dictionary
1301 final ResultSet colInfo = dbmd.getColumns(null, schName, tblName, colName);
1302 if (colInfo != null) {
1303 // we expect exactly one row in the resultset
1304 if (colInfo.next()) {
1305 _precision[column] = colInfo.getInt(7); // col 7 is "COLUMN_SIZE"
1306 _scale[column] = colInfo.getInt(9); // col 9 is "DECIMAL_DIGITS"
1307 _isNullable[column] = colInfo.getInt(11); // col 11 is "NULLABLE"
1308 final String strVal = colInfo.getString(23); // col 23 is "IS_AUTOINCREMENT"
1309 if (strVal != null && "YES".equals(strVal))
1310 _isAutoincrement[column] = true;
1311 }
1312 colInfo.close(); // close the resultset to release resources
1313 }
1314 }
1315 }
1316 }
1317 }
1318
1319 /**
1320 * Returns the number of columns in this ResultSet object.
1321 *
1322 * @return the number of columns
1323 */
1324 @Override
1325 public int getColumnCount() {
1326 return columns.length;
1327 }
1328
1329 /**
1330 * Indicates whether the designated column is automatically numbered.
1331 *
1332 * This method is currently very expensive for BIGINT,
1333 * INTEGER, SMALLINT and TINYINT result column types
1334 * as it needs to retrieve the information from the
1335 * database using an SQL meta data query.
1336 *
1337 * @param column the first column is 1, the second is 2, ...
1338 * @return true if so; false otherwise
1339 * @throws SQLException if a database access error occurs
1340 */
1341 @Override
1342 public boolean isAutoIncrement(final int column) throws SQLException {
1343 // only few integer types can be auto incrementable in MonetDB
1344 // see: https://www.monetdb.org/Documentation/SQLReference/DataTypes/SerialDatatypes
1345 switch (getColumnType(column)) {
1346 case Types.BIGINT:
1347 case Types.INTEGER:
1348 case Types.SMALLINT:
1349 case Types.TINYINT:
1350 try {
1351 if (_is_fetched[column] != true) {
1352 fetchColumnInfo(column);
1353 }
1354 return _isAutoincrement[column];
1355 } catch (IndexOutOfBoundsException e) {
1356 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1357 }
1358 }
1359
1360 return false;
1361 }
1362
1363 /**
1364 * Indicates whether a column's case matters.
1365 *
1366 * @param column the first column is 1, the second is 2, ...
1367 * @return true for all character string columns else false
1368 */
1369 @Override
1370 public boolean isCaseSensitive(final int column) throws SQLException {
1371 switch (getColumnType(column)) {
1372 case Types.CHAR:
1373 case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
1374 case Types.CLOB:
1375 return true;
1376 case Types.VARCHAR:
1377 final String monettype = getColumnTypeName(column);
1378 if (monettype != null && monettype.length() == 4) {
1379 // data of type inet or uuid is not case sensitive
1380 if ("inet".equals(monettype)
1381 || "uuid".equals(monettype))
1382 return false;
1383 }
1384 return true;
1385 }
1386
1387 return false;
1388 }
1389
1390 /**
1391 * Indicates whether the designated column can be used in a
1392 * where clause.
1393 * It is unknown to me what kind ot columns they regard to,
1394 * as I think all columns are useable in a where clause.
1395 * Returning true for all here, for the time being.
1396 * Possible thought; maybe they want to know here if it's a
1397 * real column existing in a table or not...
1398 *
1399 * @param column the first column is 1, the second is 2, ...
1400 * @return true
1401 */
1402 @Override
1403 public boolean isSearchable(final int column) throws SQLException {
1404 checkColumnIndexValidity(column);
1405 return true;
1406 }
1407
1408 /**
1409 * Indicates whether the designated column is a cash value.
1410 * From the MonetDB database perspective it is by definition
1411 * unknown whether the value is a currency, because there are
1412 * no currency datatypes such as MONEY. With this knowledge
1413 * we can always return false here.
1414 *
1415 * @param column the first column is 1, the second is 2, ...
1416 * @return false
1417 */
1418 @Override
1419 public boolean isCurrency(final int column) throws SQLException {
1420 checkColumnIndexValidity(column);
1421 return false;
1422 }
1423
1424 /**
1425 * Indicates whether values in the designated column are signed
1426 * numbers.
1427 * Within MonetDB all numeric types (except oid and ptr) are signed.
1428 *
1429 * @param column the first column is 1, the second is 2, ...
1430 * @return true if so; false otherwise
1431 */
1432 @Override
1433 public boolean isSigned(final int column) throws SQLException {
1434 // we can hardcode this, based on the colum type
1435 switch (getColumnType(column)) {
1436 case Types.TINYINT:
1437 case Types.SMALLINT:
1438 case Types.INTEGER:
1439 case Types.REAL:
1440 case Types.FLOAT:
1441 case Types.DOUBLE:
1442 case Types.DECIMAL:
1443 case Types.NUMERIC:
1444 return true;
1445 case Types.BIGINT:
1446 final String monettype = getColumnTypeName(column);
1447 if (monettype != null && monettype.length() == 3) {
1448 // data of type oid or ptr is not signed
1449 if ("oid".equals(monettype)
1450 || "ptr".equals(monettype))
1451 return false;
1452 }
1453 return true;
1454 // All other types should return false
1455 // case Types.BOOLEAN:
1456 // case Types.DATE: // can year be negative?
1457 // case Types.TIME: // can time be negative?
1458 // case Types.TIME_WITH_TIMEZONE:
1459 // case Types.TIMESTAMP: // can year be negative?
1460 // case Types.TIMESTAMP_WITH_TIMEZONE:
1461 default:
1462 return false;
1463 }
1464 }
1465
1466 /**
1467 * Indicates the designated column's normal maximum width in
1468 * characters.
1469 *
1470 * @param column the first column is 1, the second is 2, ...
1471 * @return the normal maximum number of characters allowed as the
1472 * width of the designated column
1473 * @throws SQLException if there is no such column
1474 */
1475 @Override
1476 public int getColumnDisplaySize(final int column) throws SQLException {
1477 checkColumnIndexValidity(column);
1478 if (header != null) {
1479 try {
1480 return header.getColumnLengths()[column - 1];
1481 } catch (IndexOutOfBoundsException e) {
1482 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1483 }
1484 }
1485 return 1;
1486 }
1487
1488 /**
1489 * Get the designated column's schema name.
1490 *
1491 * @param column the first column is 1, the second is 2, ...
1492 * @return schema name or "" if not applicable
1493 * @throws SQLException if a database access error occurs
1494 */
1495 @Override
1496 public String getSchemaName(final int column) throws SQLException {
1497 checkColumnIndexValidity(column);
1498 if (header != null) {
1499 // figure the name out
1500 try {
1501 final String schema = header.getTableNames()[column - 1];
1502 if (schema != null) {
1503 final int dot = schema.indexOf('.');
1504 return (dot >= 0) ? schema.substring(0, dot) : "";
1505 }
1506 } catch (IndexOutOfBoundsException e) {
1507 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1508 }
1509 }
1510 return "";
1511 }
1512
1513 /**
1514 * Gets the designated column's table name.
1515 *
1516 * @param column the first column is 1, the second is 2, ...
1517 * @return table name or "" if not applicable
1518 */
1519 @Override
1520 public String getTableName(final int column) throws SQLException {
1521 checkColumnIndexValidity(column);
1522 if (header != null) {
1523 // figure the name out
1524 try {
1525 final String table = header.getTableNames()[column - 1];
1526 if (table != null) {
1527 final int dot = table.indexOf('.');
1528 return (dot >= 0) ? table.substring(dot + 1) : table;
1529 }
1530 } catch (IndexOutOfBoundsException e) {
1531 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1532 }
1533 }
1534 return "";
1535 }
1536
1537 /**
1538 * Get the designated column's specified column size.
1539 * For numeric data, this is the maximum precision.
1540 * For character data, this is the length in characters.
1541 * For datetime datatypes, this is the length in characters
1542 * of the String representation (assuming the maximum
1543 * allowed precision of the fractional seconds component).
1544 * For binary data, this is the length in bytes.
1545 * For the ROWID datatype, this is the length in bytes.
1546 * 0 is returned for data types where the column size is not applicable.
1547 *
1548 * This method is currently very expensive for DECIMAL, NUMERIC
1549 * CHAR, VARCHAR, CLOB, BLOB, VARBINARY and BINARY result
1550 * column types as it needs to retrieve the information
1551 * from the database using an SQL meta data query.
1552 *
1553 * @param column the first column is 1, the second is 2, ...
1554 * @return precision
1555 * @throws SQLException if a database access error occurs
1556 */
1557 @Override
1558 public int getPrecision(final int column) throws SQLException {
1559 final int tpe = getColumnType(column);
1560 switch (tpe) {
1561 case Types.BIGINT:
1562 return 19;
1563 case Types.INTEGER:
1564 return 10;
1565 case Types.SMALLINT:
1566 return 5;
1567 case Types.TINYINT:
1568 return 3;
1569 case Types.REAL:
1570 return 7;
1571 case Types.FLOAT:
1572 case Types.DOUBLE:
1573 return 15;
1574
1575 case Types.DECIMAL:
1576 case Types.NUMERIC:
1577 // these data types do not have a fixed precision, max precision however is 38
1578 // we need to fetch the defined precision with an SQL query !
1579 case Types.CHAR:
1580 case Types.VARCHAR:
1581 case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
1582 case Types.CLOB:
1583 case Types.BINARY:
1584 case Types.VARBINARY:
1585 case Types.BLOB:
1586 // these data types also do not have a fixed length
1587 try {
1588 if (_is_fetched[column] != true) {
1589 fetchColumnInfo(column);
1590 }
1591 if (_precision[column] == 0) {
1592 // apparently no precision or max length could be fetched
1593 // use columnDisplaySize() value as alternative
1594 _precision[column] = getColumnDisplaySize(column);
1595 if (tpe == Types.BLOB || tpe == Types.VARBINARY || tpe == Types.BINARY)
1596 // These expect number of bytes, not number of hex chars
1597 _precision[column] = (_precision[column] / 2) +1;
1598 }
1599 return _precision[column];
1600 } catch (IndexOutOfBoundsException e) {
1601 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1602 }
1603 case Types.DATE:
1604 return 10; // 2020-10-08
1605 case Types.TIME:
1606 return 15; // 21:51:34.399753
1607 case Types.TIME_WITH_TIMEZONE:
1608 return 21; // 21:51:34.399753+02:00
1609 case Types.TIMESTAMP:
1610 return 26; // 2020-10-08 21:51:34.399753
1611 case Types.TIMESTAMP_WITH_TIMEZONE:
1612 return 32; // 2020-10-08 21:51:34.399753+02:00
1613 case Types.BOOLEAN:
1614 return 1;
1615 default:
1616 // All other types should return 0
1617 return 0;
1618 }
1619 }
1620
1621 /**
1622 * Gets the designated column's number of digits to right of
1623 * the decimal point.
1624 * 0 is returned for data types where the scale is not applicable.
1625 *
1626 * This method is currently very expensive for DECIMAL and NUMERIC
1627 * result column types as it needs to retrieve the information
1628 * from the database using an SQL meta data query.
1629 *
1630 * @param column the first column is 1, the second is 2, ...
1631 * @return scale
1632 * @throws SQLException if a database access error occurs
1633 */
1634 @Override
1635 public int getScale(final int column) throws SQLException {
1636 switch (getColumnType(column)) {
1637 case Types.DECIMAL:
1638 case Types.NUMERIC:
1639 // these data types may have a scale, max scale is 38
1640 try {
1641 if (_is_fetched[column] != true) {
1642 fetchColumnInfo(column);
1643 }
1644 return _scale[column];
1645 } catch (IndexOutOfBoundsException e) {
1646 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1647 }
1648 case Types.TIME:
1649 case Types.TIME_WITH_TIMEZONE:
1650 case Types.TIMESTAMP:
1651 case Types.TIMESTAMP_WITH_TIMEZONE:
1652 // support microseconds, so scale 6
1653 return 6; // 21:51:34.399753
1654 // All other types should return 0
1655 // case Types.BIGINT:
1656 // case Types.INTEGER:
1657 // case Types.SMALLINT:
1658 // case Types.TINYINT:
1659 // case Types.REAL:
1660 // case Types.FLOAT:
1661 // case Types.DOUBLE:
1662 // case Types.CHAR:
1663 // case Types.VARCHAR:
1664 // case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
1665 // case Types.CLOB:
1666 // case Types.BINARY:
1667 // case Types.VARBINARY:
1668 // case Types.BLOB:
1669 // case Types.DATE:
1670 // case Types.BOOLEAN:
1671 default:
1672 return 0;
1673 }
1674 }
1675
1676 /**
1677 * Indicates the nullability of values in the designated column.
1678 *
1679 * This method is currently very expensive as it needs to
1680 * retrieve the information from the database using an SQL
1681 * meta data query (for each column).
1682 *
1683 * @param column the first column is 1, the second is 2, ...
1684 * @return the nullability status of the given column; one of
1685 * columnNoNulls, columnNullable or columnNullableUnknown
1686 * @throws SQLException if a database access error occurs
1687 */
1688 @Override
1689 public int isNullable(final int column) throws SQLException {
1690 checkColumnIndexValidity(column);
1691 try {
1692 if (_is_fetched[column] != true) {
1693 fetchColumnInfo(column);
1694 }
1695 return _isNullable[column];
1696 } catch (IndexOutOfBoundsException e) {
1697 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1698 }
1699 }
1700
1701 /**
1702 * Gets the designated column's table's catalog name.
1703 * MonetDB does not support the catalog naming concept as in: catalog.schema.table naming scheme
1704 *
1705 * @param column the first column is 1, the second is 2, ...
1706 * @return the name of the catalog for the table in which the given
1707 * column appears or "" if not applicable
1708 */
1709 @Override
1710 public String getCatalogName(final int column) throws SQLException {
1711 checkColumnIndexValidity(column);
1712 return null; // MonetDB does NOT support catalogs
1713
1714 }
1715
1716 /**
1717 * Indicates whether the designated column is definitely not
1718 * writable. MonetDB does not support cursor updates, so
1719 * nothing is writable.
1720 *
1721 * @param column the first column is 1, the second is 2, ...
1722 * @return true if so; false otherwise
1723 */
1724 @Override
1725 public boolean isReadOnly(final int column) throws SQLException {
1726 checkColumnIndexValidity(column);
1727 return true;
1728 }
1729
1730 /**
1731 * Indicates whether it is possible for a write on the
1732 * designated column to succeed.
1733 *
1734 * @param column the first column is 1, the second is 2, ...
1735 * @return true if so; false otherwise
1736 */
1737 @Override
1738 public boolean isWritable(final int column) throws SQLException {
1739 checkColumnIndexValidity(column);
1740 return false;
1741 }
1742
1743 /**
1744 * Indicates whether a write on the designated column will
1745 * definitely succeed.
1746 *
1747 * @param column the first column is 1, the second is 2, ...
1748 * @return true if so; false otherwise
1749 */
1750 @Override
1751 public boolean isDefinitelyWritable(final int column) throws SQLException {
1752 checkColumnIndexValidity(column);
1753 return false;
1754 }
1755
1756 /**
1757 * Returns the fully-qualified name of the Java class whose
1758 * instances are manufactured if the method
1759 * ResultSet.getObject is called to retrieve a value from
1760 * the column. ResultSet.getObject may return a subclass of
1761 * the class returned by this method.
1762 *
1763 * @param column the first column is 1, the second is 2, ...
1764 * @return the fully-qualified name of the class in the Java
1765 * programming language that would be used by the method
1766 * ResultSet.getObject to retrieve the value in the
1767 * specified column. This is the class name used for custom
1768 * mapping.
1769 * @throws SQLException if there is no such column
1770 */
1771 @Override
1772 public String getColumnClassName(final int column) throws SQLException {
1773 checkColumnIndexValidity(column);
1774 try {
1775 final String MonetDBType = types[column - 1];
1776 Class<?> type = null;
1777 if (conn != null) {
1778 final Map<String,Class<?>> map = conn.getTypeMap();
1779 if (map != null && map.containsKey(MonetDBType)) {
1780 type = (Class)map.get(MonetDBType);
1781 }
1782 }
1783 if (type == null) {
1784 // fallback to the standard SQL type Class mappings
1785 type = getClassForType(JdbcSQLTypes[column - 1]);
1786 }
1787 if (type != null) {
1788 return type.getName();
1789 }
1790 throw new SQLException("column type mapping null: " + MonetDBType, "M0M03");
1791 } catch (IndexOutOfBoundsException e) {
1792 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1793 }
1794 }
1795
1796 /**
1797 * Gets the designated column's suggested title for use in
1798 * printouts and displays. This is currently equal to
1799 * getColumnName().
1800 *
1801 * @param column the first column is 1, the second is 2, ...
1802 * @return the suggested column title
1803 * @throws SQLException if there is no such column
1804 */
1805 @Override
1806 public String getColumnLabel(final int column) throws SQLException {
1807 return getColumnName(column);
1808 }
1809
1810 /**
1811 * Gets the designated column's name
1812 *
1813 * @param column the first column is 1, the second is 2, ...
1814 * @return the column name
1815 * @throws SQLException if there is no such column
1816 */
1817 @Override
1818 public String getColumnName(final int column) throws SQLException {
1819 checkColumnIndexValidity(column);
1820 try {
1821 return columns[column - 1];
1822 } catch (IndexOutOfBoundsException e) {
1823 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1824 }
1825 }
1826
1827 /**
1828 * Retrieves the designated column's SQL type.
1829 *
1830 * @param column the first column is 1, the second is 2, ...
1831 * @return SQL type from java.sql.Types
1832 * @throws SQLException if there is no such column
1833 */
1834 @Override
1835 public int getColumnType(final int column) throws SQLException {
1836 checkColumnIndexValidity(column);
1837 try {
1838 return JdbcSQLTypes[column - 1];
1839 } catch (IndexOutOfBoundsException e) {
1840 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1841 }
1842 }
1843
1844 /**
1845 * Retrieves the designated column's database-specific type name.
1846 *
1847 * @param column the first column is 1, the second is 2, ...
1848 * @return type name used by the database. If the column type is a
1849 * user-defined type, then a fully-qualified type name is
1850 * returned.
1851 * @throws SQLException if there is no such column
1852 */
1853 @Override
1854 public String getColumnTypeName(final int column) throws SQLException {
1855 checkColumnIndexValidity(column);
1856 try {
1857 return types[column - 1];
1858 } catch (IndexOutOfBoundsException e) {
1859 throw MonetResultSet.newSQLInvalidColumnIndexException(column);
1860 }
1861 }
1862
1863 }; // end of new rsmdw()
1864 } // end of getMetaData()
1865
1866 /**
1867 * Gets the value of the designated column in the current row of this
1868 * ResultSet object as an Object in the Java programming language.
1869 *
1870 * This method will return the value of the given column as a Java object.
1871 * The type of the Java object will be the default Java object type
1872 * corresponding to the column's SQL type, following the mapping for
1873 * built-in types specified in the JDBC specification. If the value is
1874 * an SQL NULL, the driver returns a Java null.
1875 *
1876 * This method may also be used to read database-specific abstract data
1877 * types. In the JDBC 2.0 API, the behavior of method getObject is extended
1878 * to materialize data of SQL user-defined types. When a column contains a
1879 * structured or distinct value, the behavior of this method is as if it
1880 * were a call to: getObject(columnIndex, this.getStatement().getConnection().getTypeMap()).
1881 *
1882 * @param columnIndex the first column is 1, the second is 2, ...
1883 * @return a java.lang.Object holding the column value or null
1884 * @throws SQLException if a database access error occurs or this method is called on a closed result set
1885 */
1886 @Override
1887 public Object getObject(final int columnIndex) throws SQLException {
1888 // Many generic JDBC programs use getObject(colnr) to retrieve value objects from a resultset
1889 // For speed the implementation should be as fast as possible, so avoid method calls (by inlining code) where possible
1890 checkNotClosed();
1891
1892 final int JdbcType;
1893 final String val;
1894 try {
1895 val = tlp.values[columnIndex - 1];
1896 if (val == null) {
1897 lastReadWasNull = true;
1898 return null;
1899 }
1900 lastReadWasNull = false;
1901 JdbcType = JdbcSQLTypes[columnIndex - 1];
1902 } catch (IndexOutOfBoundsException e) {
1903 throw newSQLInvalidColumnIndexException(columnIndex);
1904 }
1905
1906 switch(JdbcType) {
1907 case Types.TINYINT:
1908 case Types.SMALLINT:
1909 try {
1910 return Short.valueOf(val);
1911 } catch (NumberFormatException e) {
1912 return val;
1913 }
1914 case Types.INTEGER:
1915 try {
1916 return Integer.valueOf(val);
1917 } catch (NumberFormatException e) {
1918 return val;
1919 }
1920 case Types.BIGINT:
1921 try {
1922 return Long.valueOf(val);
1923 } catch (NumberFormatException e) {
1924 return val;
1925 }
1926 case Types.DOUBLE:
1927 case Types.FLOAT:
1928 try {
1929 return Double.valueOf(val);
1930 } catch (NumberFormatException e) {
1931 return val;
1932 }
1933 case Types.REAL:
1934 try {
1935 return Float.valueOf(val);
1936 } catch (NumberFormatException e) {
1937 return val;
1938 }
1939 case Types.DECIMAL:
1940 case Types.NUMERIC:
1941 try {
1942 return new BigDecimal(val);
1943 } catch (NumberFormatException e) {
1944 return val;
1945 }
1946 case Types.BOOLEAN:
1947 return Boolean.valueOf(val);
1948 case Types.VARCHAR:
1949 {
1950 // The MonetDB types: inet, json, url and uuid are all mapped to Types.VARCHAR in MonetDriver.typeMap
1951 // For these MonetDB types (except json, see comments below) we try to create objects of the corresponding class.
1952 final String MonetDBType = types[columnIndex - 1];
1953 switch (MonetDBType.length()) {
1954 case 3:
1955 if ("url".equals(MonetDBType)) {
1956 try {
1957 final org.monetdb.jdbc.types.URL url_obj = new org.monetdb.jdbc.types.URL();
1958 url_obj.fromString(val);
1959 return url_obj;
1960 } catch (Exception exc) {
1961 // ignore exception and just return the val String object
1962 return val;
1963 }
1964 }
1965 break;
1966 case 4:
1967 if ("inet".equals(MonetDBType)) {
1968 try {
1969 final org.monetdb.jdbc.types.INET inet_obj = new org.monetdb.jdbc.types.INET();
1970 inet_obj.fromString(val);
1971 return inet_obj;
1972 } catch (Exception exc) {
1973 // ignore exception and just return the val String object
1974 return val;
1975 }
1976 } else
1977 if ("uuid".equals(MonetDBType)) {
1978 try {
1979 return java.util.UUID.fromString(val);
1980 } catch (IllegalArgumentException exc) {
1981 // ignore exception and just return the val String object
1982 return val;
1983 }
1984 // } else
1985 // if ("json".equals(MonetDBType)) {
1986 // There is no support for JSON in standard java class libraries.
1987 // Possibly we could use org.json.simple.JSONObject or other/faster libs
1988 // javax.json.Json is not released yet (see https://json-processing-spec.java.net/)
1989 // see also https://github.com/fabienrenaud/java-json-benchmark
1990 // Note that it would make our JDBC driver dependent of an external jar
1991 // and we don't want that so simply return it as String object
1992 // return val;
1993 }
1994 break;
1995 }
1996 return val;
1997 }
1998 case Types.CHAR:
1999 case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
2000 return val;
2001 case Types.CLOB:
2002 return new MonetClob(val);
2003 case Types.BLOB:
2004 return new MonetBlob(val);
2005 case Types.DATE:
2006 return getDate(columnIndex, null);
2007 case Types.TIME:
2008 case Types.TIME_WITH_TIMEZONE:
2009 return getTime(columnIndex, null);
2010 case Types.TIMESTAMP:
2011 case Types.TIMESTAMP_WITH_TIMEZONE:
2012 return getTimestamp(columnIndex, null);
2013 case Types.BINARY:
2014 case Types.VARBINARY:
2015 case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY, it's here for completeness
2016 return getBytes(columnIndex);
2017 case Types.OTHER:
2018 default:
2019 // When we get here the column type is a non-standard JDBC SQL type, possibly a User Defined Type.
2020 // Just call getObject(int, Map) for those rare cases.
2021 return getObject(columnIndex, this.getStatement().getConnection().getTypeMap());
2022 }
2023 }
2024
2025 private final boolean classImplementsSQLData(final Class<?> cl) {
2026 final Class<?>[] cls = cl.getInterfaces();
2027 for (int i = 0; i < cls.length; i++) {
2028 if (cls[i] == SQLData.class)
2029 return true;
2030 }
2031 return false;
2032 }
2033
2034 /**
2035 * Gets the value of the designated column in the current row of this
2036 * ResultSet object as an Object in the Java programming language.
2037 *
2038 * This method will return the value of the given column as a Java object.
2039 * The type of the Java object will be the default Java object type corresponding
2040 * to the column's SQL type, following the mapping for built-in types specified
2041 * in the JDBC specification.
2042 * If the value is an SQL NULL, the driver returns a Java null.
2043 *
2044 * This method may also be used to read database-specific abstract data types.
2045 * In the JDBC 2.0 API, the behavior of method getObject is extended to
2046 * materialize data of SQL user-defined types.
2047 *
2048 * If Connection.getTypeMap does not throw a SQLFeatureNotSupportedException, then
2049 * when a column contains a structured or distinct value, the behavior of this
2050 * method is as if it were a call to: getObject(columnIndex,
2051 * this.getStatement().getConnection().getTypeMap()).
2052 * If Connection.getTypeMap does throw a SQLFeatureNotSupportedException, then
2053 * structured values are not supported, and distinct values are mapped to the
2054 * default Java class as determined by the underlying SQL type of the DISTINCT type.
2055 *
2056 * @param columnIndex the first column is 1, the second is 2, ...
2057 * @param map a java.util.Map object that contains the mapping from SQL
2058 * type names to classes in the Java programming language
2059 * @return an Object in the Java programming language representing the SQL value
2060 * @throws SQLException if a database access error occurs or this method is called on a closed result set
2061 */
2062 @Override
2063 @SuppressWarnings("unchecked")
2064 public Object getObject(final int columnIndex, final Map<String,Class<?>> map)
2065 throws SQLException
2066 {
2067 checkNotClosed();
2068 final String val;
2069 final String MonetDBtype;
2070 try {
2071 val = tlp.values[columnIndex - 1];
2072 if (val == null) {
2073 lastReadWasNull = true;
2074 return null;
2075 }
2076 lastReadWasNull = false;
2077 MonetDBtype = types[columnIndex - 1];
2078 } catch (IndexOutOfBoundsException e) {
2079 throw newSQLInvalidColumnIndexException(columnIndex);
2080 }
2081
2082 Class<?> type = null;
2083 if (map != null && map.containsKey(MonetDBtype)) {
2084 type = map.get(MonetDBtype);
2085 }
2086 if (type == null) {
2087 // fallback to the standard SQL type Class mappings
2088 type = getClassForType(JdbcSQLTypes[columnIndex - 1]);
2089 }
2090
2091 if (type == null || type == String.class) {
2092 return val;
2093 } else if (type == BigDecimal.class) {
2094 return getBigDecimal(columnIndex);
2095 } else if (type == Boolean.class) {
2096 return Boolean.valueOf(getBoolean(columnIndex));
2097 } else if (type == Short.class) {
2098 return Short.valueOf(getShort(columnIndex));
2099 } else if (type == Integer.class) {
2100 return Integer.valueOf(getInt(columnIndex));
2101 } else if (type == Long.class) {
2102 return Long.valueOf(getLong(columnIndex));
2103 } else if (type == Float.class) {
2104 return Float.valueOf(getFloat(columnIndex));
2105 } else if (type == Double.class) {
2106 return Double.valueOf(getDouble(columnIndex));
2107 } else if (type == byte[].class) {
2108 return getBytes(columnIndex);
2109 } else if (type == java.sql.Date.class) {
2110 return getDate(columnIndex, null);
2111 } else if (type == Time.class) {
2112 return getTime(columnIndex, null);
2113 } else if (type == Timestamp.class) {
2114 return getTimestamp(columnIndex, null);
2115 } else if (type == Clob.class) {
2116 return getClob(columnIndex);
2117 } else if (type == Blob.class) {
2118 return getBlob(columnIndex);
2119 } else if (classImplementsSQLData(type)) {
2120 final SQLData x;
2121 try {
2122 final java.lang.reflect.Constructor<? extends SQLData> ctor =
2123 ((Class)type).getConstructor();
2124 x = ctor.newInstance();
2125 } catch (NoSuchMethodException nsme) {
2126 throw new SQLException(nsme.getMessage(), "M0M27");
2127 } catch (InstantiationException ie) {
2128 throw new SQLException(ie.getMessage(), "M0M27");
2129 } catch (IllegalAccessException iae) {
2130 throw new SQLException(iae.getMessage(), "M0M27");
2131 } catch (java.lang.reflect.InvocationTargetException ite) {
2132 throw new SQLException(ite.getMessage(), "M0M27");
2133 } catch (SecurityException se) {
2134 throw new SQLException(se.getMessage(), "M0M27");
2135 }
2136 final int colnum = columnIndex;
2137 final boolean valwasnull = wasNull();
2138 final SQLInput input = new SQLInput() {
2139 @Override
2140 public String readString() throws SQLException {
2141 return getString(colnum);
2142 }
2143
2144 @Override
2145 public boolean readBoolean() throws SQLException {
2146 return getBoolean(colnum);
2147 }
2148
2149 @Override
2150 public byte readByte() throws SQLException {
2151 return getByte(colnum);
2152 }
2153
2154 @Override
2155 public short readShort() throws SQLException {
2156 return getShort(colnum);
2157 }
2158
2159 @Override
2160 public int readInt() throws SQLException {
2161 return getInt(colnum);
2162 }
2163
2164 @Override
2165 public long readLong() throws SQLException {
2166 return getLong(colnum);
2167 }
2168
2169 @Override
2170 public float readFloat() throws SQLException {
2171 return getFloat(colnum);
2172 }
2173
2174 @Override
2175 public double readDouble() throws SQLException {
2176 return getDouble(colnum);
2177 }
2178
2179 @Override
2180 public BigDecimal readBigDecimal() throws SQLException {
2181 return getBigDecimal(colnum);
2182 }
2183
2184 @Override
2185 public byte[] readBytes() throws SQLException {
2186 return getBytes(colnum);
2187 }
2188
2189 @Override
2190 public java.sql.Date readDate() throws SQLException {
2191 return getDate(colnum, null);
2192 }
2193
2194 @Override
2195 public java.sql.Time readTime() throws SQLException {
2196 return getTime(colnum, null);
2197 }
2198
2199 @Override
2200 public Timestamp readTimestamp() throws SQLException {
2201 return getTimestamp(colnum, null);
2202 }
2203
2204 @Override
2205 public Reader readCharacterStream() throws SQLException {
2206 return getCharacterStream(colnum);
2207 }
2208
2209 @Override
2210 public InputStream readAsciiStream() throws SQLException {
2211 return getAsciiStream(colnum);
2212 }
2213
2214 @Override
2215 public InputStream readBinaryStream() throws SQLException {
2216 return getBinaryStream(colnum);
2217 }
2218
2219 @Override
2220 public Object readObject() throws SQLException {
2221 return getObject(colnum);
2222 }
2223
2224 @Override
2225 public Ref readRef() throws SQLException {
2226 return getRef(colnum);
2227 }
2228
2229 @Override
2230 public Blob readBlob() throws SQLException {
2231 return getBlob(colnum);
2232 }
2233
2234 @Override
2235 public Clob readClob() throws SQLException {
2236 return getClob(colnum);
2237 }
2238
2239 @Override
2240 public Array readArray() throws SQLException {
2241 return getArray(colnum);
2242 }
2243
2244 @Override
2245 public boolean wasNull() throws SQLException {
2246 return valwasnull;
2247 }
2248
2249 @Override
2250 public URL readURL() throws SQLException {
2251 return getURL(colnum);
2252 }
2253
2254 @Override
2255 public NClob readNClob() throws SQLException {
2256 return getNClob(colnum);
2257 }
2258
2259 @Override
2260 public String readNString() throws SQLException {
2261 return getNString(colnum);
2262 }
2263
2264 @Override
2265 public SQLXML readSQLXML() throws SQLException {
2266 return getSQLXML(colnum);
2267 }
2268
2269 @Override
2270 public RowId readRowId() throws SQLException {
2271 return getRowId(colnum);
2272 }
2273 };
2274 x.readSQL(input, MonetDBtype);
2275 return x;
2276 } else {
2277 return val;
2278 }
2279 }
2280
2281 /**
2282 * Helper method to support the getObject and
2283 * ResultsetMetaData.getColumnClassName JDBC methods.
2284 *
2285 * @param type a value from java.sql.Types
2286 * @return a Class object from which an instance would be returned
2287 */
2288 final static Class<?> getClassForType(final int type) {
2289 /**
2290 * This switch returns the types as objects according to table B-3 from
2291 * Oracle's JDBC specification 4.1
2292 */
2293 // keep this switch regarding the returned classes aligned with getObject(int, Map) !
2294 switch(type) {
2295 case Types.CHAR:
2296 case Types.VARCHAR:
2297 case Types.LONGVARCHAR:
2298 return String.class;
2299 case Types.NUMERIC:
2300 case Types.DECIMAL:
2301 return BigDecimal.class;
2302 case Types.BOOLEAN:
2303 return Boolean.class;
2304 case Types.TINYINT:
2305 case Types.SMALLINT:
2306 return Short.class;
2307 case Types.INTEGER:
2308 return Integer.class;
2309 case Types.BIGINT:
2310 return Long.class;
2311 case Types.REAL:
2312 return Float.class;
2313 case Types.FLOAT:
2314 case Types.DOUBLE:
2315 return Double.class;
2316 case Types.BINARY: // MonetDB currently does not support these
2317 case Types.VARBINARY: // see treat_blob_as_binary property
2318 case Types.LONGVARBINARY:
2319 return byte[].class;
2320 case Types.DATE:
2321 return java.sql.Date.class;
2322 case Types.TIME:
2323 case Types.TIME_WITH_TIMEZONE:
2324 return Time.class;
2325 case Types.TIMESTAMP:
2326 case Types.TIMESTAMP_WITH_TIMEZONE:
2327 return Timestamp.class;
2328 case Types.CLOB:
2329 return Clob.class;
2330 case Types.BLOB:
2331 return Blob.class;
2332
2333 // all the rest are currently not implemented and used
2334 default:
2335 return String.class;
2336 }
2337 }
2338
2339 /**
2340 * Gets the value of the designated column in the current row of this
2341 * ResultSet object as an Object in the Java programming language.
2342 *
2343 * This method will return the value of the given column as a Java object.
2344 * The type of the Java object will be the default Java object type
2345 * corresponding to the column's SQL type, following the mapping for
2346 * built-in types specified in the JDBC specification. If the value is an
2347 * SQL NULL, the driver returns a Java null.
2348 *
2349 * This method may also be used to read database-specific abstract data
2350 * types.
2351 *
2352 * @param columnLabel the SQL name of the column
2353 * @return a java.lang.Object holding the column value
2354 * @throws SQLException if a database access error occurs or this method is called on a closed result set
2355 */
2356 @Override
2357 public Object getObject(final String columnLabel) throws SQLException {
2358 return getObject(findColumn(columnLabel));
2359 }
2360
2361 /**
2362 * Retrieves the value of the designated column in the current row of this
2363 * ResultSet object as an Object in the Java programming language. If the
2364 * value is an SQL NULL, the driver returns a Java null. This method uses
2365 * the specified Map object for custom mapping if appropriate.
2366 *
2367 * @param columnLabel the name of the column from which to retrieve the value
2368 * @param map a java.util.Map object that contains the mapping from SQL
2369 * type names to classes in the Java programming language
2370 * @return an Object representing the SQL value in the specified column
2371 * @throws SQLException if a database access error occurs or this method is called on a closed result set
2372 */
2373 @Override
2374 public Object getObject(final String columnLabel, final Map<String,Class<?>> map) throws SQLException {
2375 return getObject(findColumn(columnLabel), map);
2376 }
2377
2378 @Override
2379 public Ref getRef(int columnIndex) throws SQLException {
2380 throw newSQLFeatureNotSupportedException("getRef");
2381 }
2382
2383 @Override
2384 public Ref getRef(final String columnLabel) throws SQLException {
2385 throw newSQLFeatureNotSupportedException("getRef");
2386 }
2387
2388 /**
2389 * Retrieves the current row number. The first row is number 1, the second
2390 * number 2, and so on.
2391 *
2392 * @return the current row number; 0 if there is no current row
2393 */
2394 @Override
2395 public int getRow() {
2396 return curRow;
2397 }
2398
2399 /**
2400 * Retrieves the value of the designated column in the current row
2401 * of this ResultSet object as a java.sql.RowId object in the Java
2402 * programming language.
2403 *
2404 * @param columnIndex the first column is 1, the second is 2, ...
2405 * @return the column value; if the value is SQL NULL, the value returned
2406 * is null
2407 * @throws SQLException if there is no such column
2408 * @throws SQLFeatureNotSupportedException the JDBC driver does
2409 * not support this method
2410 */
2411 @Override
2412 public RowId getRowId(final int columnIndex) throws SQLException {
2413 throw newSQLFeatureNotSupportedException("getRowId");
2414 }
2415
2416 /**
2417 * Retrieves the value of the designated column in the current row
2418 * of this ResultSet object as a java.sql.RowId object in the Java
2419 * programming language.
2420 *
2421 * @param columnLabel the SQL name of the column
2422 * @return the column value; if the value is SQL NULL, the value returned
2423 * is null
2424 * @throws SQLException if the ResultSet object does not contain columnLabel
2425 * @throws SQLFeatureNotSupportedException the JDBC driver does
2426 * not support this method
2427 */
2428 @Override
2429 public RowId getRowId(final String columnLabel) throws SQLException {
2430 throw newSQLFeatureNotSupportedException("getRowId");
2431 }
2432
2433 /**
2434 * Retrieves the value of the designated column in the current row of this
2435 * ResultSet object as a short in the Java programming language.
2436 *
2437 * @param columnIndex the first column is 1, the second is 2, ...
2438 * @return the column value; if the value is SQL NULL, the value returned is 0
2439 * @throws SQLException if there is no such column or this method is called on a closed result set
2440 */
2441 @Override
2442 public short getShort(final int columnIndex) throws SQLException {
2443 checkNotClosed();
2444 try {
2445 final String val = tlp.values[columnIndex - 1];
2446 if (val == null) {
2447 lastReadWasNull = true;
2448 return 0;
2449 }
2450 lastReadWasNull = false;
2451 return Short.parseShort(val);
2452 } catch (NumberFormatException e) {
2453 throw newSQLNumberFormatException(e);
2454 } catch (IndexOutOfBoundsException e) {
2455 throw newSQLInvalidColumnIndexException(columnIndex);
2456 }
2457 }
2458
2459 /**
2460 * Retrieves the value of the designated column in the current row of this
2461 * ResultSet object as a short in the Java programming language.
2462 *
2463 * @param columnLabel the SQL name of the column
2464 * @return the column value; if the value is SQL NULL, the value returned is 0
2465 * @throws SQLException if the ResultSet object does not contain columnLabel
2466 */
2467 @Override
2468 public short getShort(final String columnLabel) throws SQLException {
2469 return getShort(findColumn(columnLabel));
2470 }
2471
2472 /**
2473 * Retrieves the Statement object that produced this ResultSet object.
2474 * If the result set was generated some other way, such as by a
2475 * DatabaseMetaData method, this method may return null.
2476 *
2477 * In our implementation we always return a non-null object, see constructors.
2478 * Also from subclass MonetVirtualResultSet, see its constructor.
2479 *
2480 * @return the Statement object that produced this ResultSet object or
2481 * null if the result set was produced some other way
2482 */
2483 @Override
2484 public Statement getStatement() {
2485 return statement;
2486 }
2487
2488 /**
2489 * Retrieves the value of the designated column in the current row of this
2490 * ResultSet object as a String in the Java programming language.
2491 *
2492 * @param columnIndex the first column is 1, the second is 2, ...
2493 * @return the column value; if the value is SQL NULL, the value returned is null
2494 * @throws SQLException if there is no such column or this method is called on a closed result set
2495 */
2496 @Override
2497 public String getString(final int columnIndex) throws SQLException {
2498 checkNotClosed();
2499 try {
2500 final String val = tlp.values[columnIndex - 1];
2501 if (val == null) {
2502 lastReadWasNull = true;
2503 return null;
2504 }
2505 lastReadWasNull = false;
2506 return val;
2507 } catch (IndexOutOfBoundsException e) {
2508 throw newSQLInvalidColumnIndexException(columnIndex);
2509 }
2510 }
2511
2512 /**
2513 * Retrieves the value of the designated column in the current row of this
2514 * ResultSet object as a String in the Java programming language.
2515 *
2516 * @param columnLabel the SQL name of the column
2517 * @return the column value; if the value is SQL NULL, the value returned is null
2518 * @throws SQLException if the ResultSet object does not contain columnLabel
2519 */
2520 @Override
2521 public String getString(final String columnLabel) throws SQLException {
2522 return getString(findColumn(columnLabel));
2523 }
2524
2525 /**
2526 * Retrieves the value of the designated column in the current row
2527 * of this ResultSet object as a String in the Java programming
2528 * language. It is intended for use when accessing NCHAR,NVARCHAR
2529 * and LONGNVARCHAR columns.
2530 *
2531 * @param columnIndex the first column is 1, the second is 2, ...
2532 * @return the column value; if the value is SQL NULL, the value returned is null
2533 * @throws SQLException if there is no such column
2534 */
2535 @Override
2536 public String getNString(final int columnIndex) throws SQLException {
2537 return getString(columnIndex);
2538 }
2539
2540 /**
2541 * Retrieves the value of the designated column in the current row
2542 * of this ResultSet object as a String in the Java programming
2543 * language. It is intended for use when accessing NCHAR,NVARCHAR
2544 * and LONGNVARCHAR columns.
2545 *
2546 * @param columnLabel the SQL name of the column
2547 * @return the column value; if the value is SQL NULL, the value returned is null
2548 * @throws SQLException if the ResultSet object does not contain columnLabel
2549 */
2550 @Override
2551 public String getNString(final String columnLabel) throws SQLException {
2552 return getString(findColumn(columnLabel));
2553 }
2554
2555 /**
2556 * Retrieves the value of the designated column in the current row
2557 * of this ResultSet as a java.sql.SQLXML object in the Java
2558 * programming language.
2559 *
2560 * @param columnIndex the first column is 1, the second is 2, ...
2561 * @return a SQLXML object that maps an SQL XML value
2562 * @throws SQLException if a database access error occurs
2563 * @throws SQLFeatureNotSupportedException the JDBC driver does
2564 * not support this method
2565 */
2566 @Override
2567 public SQLXML getSQLXML(final int columnIndex) throws SQLException {
2568 throw newSQLFeatureNotSupportedException("getSQLXML");
2569 }
2570
2571 /**
2572 * Retrieves the value of the designated column in the current row
2573 * of this ResultSet as a java.sql.SQLXML object in the Java
2574 * programming language.
2575 *
2576 * @param columnLabel the label for the column specified with the SQL AS
2577 * clause. If the SQL AS clause was not specified, then the
2578 * label is the name of the column
2579 * @return a SQLXML object that maps an SQL XML value
2580 * @throws SQLException if a database access error occurs
2581 * @throws SQLFeatureNotSupportedException the JDBC driver does
2582 * not support this method
2583 */
2584 @Override
2585 public SQLXML getSQLXML(final String columnLabel) throws SQLException {
2586 throw newSQLFeatureNotSupportedException("getSQLXML");
2587 }
2588
2589 // This behaviour is according table B-6 of Sun JDBC Specification 3.0
2590 private SimpleDateFormat dateFormat;
2591 private SimpleDateFormat timeFormat;
2592 private SimpleDateFormat timestampFormat;
2593 /**
2594 * Helper method which parses the date/time value for columns of type
2595 * TIME, DATE and TIMESTAMP. For the types CHAR, VARCHAR and
2596 * LONGVARCHAR an attempt is made to parse the date according to the
2597 * given type. The given Calender object is filled with the parsed
2598 * data. Optional fractional seconds (nanos) are returned by this
2599 * method. If the underlying type of the column is none of the
2600 * mentioned six, January 1st 1970 0:00:00 GMT is returned.<br />
2601 * The dates are parsed with the given Calendar.
2602 *
2603 * @param cal the Calendar to use/fill when parsing the date/time
2604 * @param col the column to parse
2605 * @param type the corresponding java.sql.Types type of the calling function
2606 * @return the fractional seconds (nanos) or -1 if the value is NULL
2607 * @throws SQLException if a database error occurs
2608 */
2609 private final int getJavaDate(final Calendar cal, final int columnIndex, int type)
2610 throws SQLException
2611 {
2612 checkNotClosed();
2613 if (cal == null)
2614 throw new IllegalArgumentException("No Calendar object given!");
2615
2616 final String monetDateStr;
2617 final String monetDate;
2618 final String MonetDBType;
2619 int JdbcType;
2620 boolean negativeYear = false;
2621 try {
2622 monetDateStr = tlp.values[columnIndex - 1];
2623 if (monetDateStr == null) {
2624 lastReadWasNull = true;
2625 return -1;
2626 }
2627 lastReadWasNull = false;
2628 MonetDBType = types[columnIndex - 1];
2629 JdbcType = JdbcSQLTypes[columnIndex - 1];
2630 // If we got a string type, set the JdbcType to the given type
2631 // so we attempt to parse it as the caller thinks it is.
2632 if (JdbcType == Types.CHAR ||
2633 JdbcType == Types.VARCHAR ||
2634 JdbcType == Types.LONGVARCHAR ||
2635 JdbcType == Types.CLOB)
2636 {
2637 JdbcType = type;
2638 }
2639
2640 if ((JdbcType == Types.DATE || JdbcType == Types.TIMESTAMP || JdbcType == Types.TIMESTAMP_WITH_TIMEZONE)
2641 && monetDateStr.startsWith("-")) {
2642 // the SimpleDateFormat parsers do not support to parse negative year numbers, deal with it separately
2643 negativeYear = true;
2644 monetDate = monetDateStr.substring(1);
2645 } else {
2646 monetDate = monetDateStr;
2647 }
2648 } catch (IndexOutOfBoundsException e) {
2649 throw newSQLInvalidColumnIndexException(columnIndex);
2650 }
2651
2652 TimeZone ptz = cal.getTimeZone();
2653
2654 // it is important to parse the time in the given timezone in
2655 // order to get a correct (UTC) time value, hence we need to
2656 // parse it first
2657 if (MonetDBType != null && ("timetz".equals(MonetDBType) || "timestamptz".equals(MonetDBType))) {
2658 int vallen = monetDate.length();
2659 if (vallen >= 6) {
2660 // MonetDB/SQL99: Sign TwoDigitHours : Minutes
2661 ptz = TimeZone.getTimeZone("GMT" + monetDate.substring(vallen - 6, vallen));
2662 }
2663 }
2664
2665 java.util.Date pdate = null;
2666 final java.text.ParsePosition ppos = new java.text.ParsePosition(0);
2667 switch(JdbcType) {
2668 case Types.DATE:
2669 if (dateFormat == null) {
2670 // first time usage, create and keep the dateFormat object for next usage
2671 dateFormat = new SimpleDateFormat("yyyy-MM-dd");
2672 }
2673 dateFormat.setTimeZone(ptz);
2674 pdate = dateFormat.parse(monetDate, ppos);
2675 break;
2676 case Types.TIME:
2677 case Types.TIME_WITH_TIMEZONE:
2678 if (timeFormat == null) {
2679 // first time usage, create and keep the timeFormat object for next usage
2680 timeFormat = new SimpleDateFormat("HH:mm:ss");
2681 }
2682 timeFormat.setTimeZone(ptz);
2683 pdate = timeFormat.parse(monetDate, ppos);
2684 break;
2685 case Types.TIMESTAMP:
2686 case Types.TIMESTAMP_WITH_TIMEZONE:
2687 if (timestampFormat == null) {
2688 // first time usage, create and keep the timestampFormat object for next usage
2689 timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
2690 }
2691 timestampFormat.setTimeZone(ptz);
2692 pdate = timestampFormat.parse(monetDate, ppos);
2693 // if parsing with timestampFormat failed try to parse it in dateFormat
2694 if (pdate == null && monetDate.length() <= 10 && monetDate.contains("-")) {
2695 if (dateFormat == null) {
2696 // first time usage, create and keep the dateFormat object for next usage
2697 dateFormat = new SimpleDateFormat("yyyy-MM-dd");
2698 }
2699 dateFormat.setTimeZone(ptz);
2700 pdate = dateFormat.parse(monetDate, ppos);
2701 }
2702 break;
2703 default:
2704 throw new SQLException("Internal error, unsupported data type: " + type, "01M03");
2705 }
2706 if (pdate == null) {
2707 // parsing failed
2708 final String errMsg;
2709 final int epos = ppos.getErrorIndex();
2710 if (epos == -1) {
2711 errMsg = "parsing '" + monetDateStr + "' failed";
2712 } else if (epos < monetDate.length()) {
2713 errMsg = "parsing failed," +
2714 " found: '" + monetDate.charAt(epos) + "'" +
2715 " in: \"" + monetDateStr + "\"" +
2716 " at pos: " + (epos + (negativeYear ? 2 : 1));
2717 } else {
2718 errMsg = "parsing failed, expected more data after '" + monetDateStr + "'";
2719 }
2720 throw new SQLException(errMsg, "01M10");
2721 }
2722
2723 cal.setTime(pdate);
2724 if (negativeYear) {
2725 // System.out.println("Current cal: " + cal.toString());
2726 // using cal.set(Calendar.YEAR, -(cal.get(Calendar.YEAR))); does not work. We must set the ERA instead
2727 cal.set(Calendar.ERA, java.util.GregorianCalendar.BC);
2728 // System.out.println("Corrected cal: " + cal.toString());
2729 }
2730
2731 if (JdbcType == Types.TIME
2732 || JdbcType == Types.TIME_WITH_TIMEZONE
2733 || JdbcType == Types.TIMESTAMP
2734 || JdbcType == Types.TIMESTAMP_WITH_TIMEZONE) {
2735 // parse additional nanos (if any)
2736 int nanos = 0;
2737 int pos = ppos.getIndex();
2738 final char[] monDate = monetDate.toCharArray();
2739 if (pos < monDate.length && monDate[pos] == '.') {
2740 pos++;
2741 try {
2742 int ctr;
2743 nanos = getIntrinsicValue(monDate[pos], pos++);
2744 for (ctr = 1;
2745 pos < monDate.length &&
2746 monDate[pos] >= '0' &&
2747 monDate[pos] <= '9';
2748 ctr++)
2749 {
2750 if (ctr < 9) {
2751 nanos *= 10;
2752 nanos += (getIntrinsicValue(monDate[pos], pos));
2753 }
2754 if (ctr == 2) // we have three at this point
2755 cal.set(Calendar.MILLISECOND, nanos);
2756 pos++;
2757 }
2758 while (ctr++ < 9)
2759 nanos *= 10;
2760 } catch (MCLParseException e) {
2761 addWarning(e.getMessage() +
2762 " found: '" + monDate[e.getErrorOffset()] + "'" +
2763 " in: \"" + monetDate + "\"" +
2764 " at pos: " + e.getErrorOffset(), "01M10");
2765 // default value
2766 nanos = 0;
2767 }
2768 }
2769 return nanos;
2770 }
2771
2772 return 0;
2773 }
2774
2775 /**
2776 * Small helper method that returns the intrinsic value of a char if
2777 * it represents a digit. If a non-digit character is encountered
2778 * an MCLParseException is thrown.
2779 *
2780 * @param c the char
2781 * @param pos the position
2782 * @return the intrinsic value of the char
2783 * @throws MCLParseException if c is not a digit
2784 */
2785 private static final int getIntrinsicValue(final char c, final int pos)
2786 throws MCLParseException
2787 {
2788 // note: don't use Character.isDigit() here, because
2789 // we only want ISO-LATIN-1 digits
2790 if (c >= '0' && c <= '9') {
2791 return (int)c - (int)'0';
2792 } else {
2793 throw new MCLParseException("Expected a digit", pos);
2794 }
2795 }
2796
2797 /**
2798 * Retrieves the value of the designated column in the current row of this
2799 * ResultSet object as a java.sql.Date object in the Java programming
2800 * language.
2801 *
2802 * @param columnIndex the first column is 1, the second is 2, ...
2803 * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null
2804 * @throws SQLException if a database access error occurs
2805 * @see #getDate(int col, Calendar cal)
2806 */
2807 @Override
2808 public java.sql.Date getDate(final int columnIndex) throws SQLException {
2809 return getDate(columnIndex, null);
2810 }
2811
2812 /**
2813 * Retrieves the value of the designated column in the current row of this
2814 * ResultSet object as a java.sql.Date object in the Java programming
2815 * language. This method uses the given calendar to construct an appropriate
2816 * millisecond value for the date if the underlying database does not store
2817 * timezone information.
2818 *
2819 * @param columnIndex the first column is 1, the second is 2, ...
2820 * @param cal the java.util.Calendar object to use in constructing the date
2821 * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null
2822 * @throws SQLException if a database access error occurs
2823 */
2824 @Override
2825 public java.sql.Date getDate(final int columnIndex, Calendar cal)
2826 throws SQLException
2827 {
2828 checkNotClosed();
2829 try {
2830 final String val = tlp.values[columnIndex - 1];
2831 if (val == null) {
2832 lastReadWasNull = true;
2833 return null;
2834 }
2835 lastReadWasNull = false;
2836 if (cal == null) {
2837 // try to convert string directly to a Date object
2838 // Note: the string must be in JDBC date escape format: yyyy-[m]m-[d]d
2839 try {
2840 return java.sql.Date.valueOf(val);
2841 } catch (IllegalArgumentException iae) {
2842 // this happens if string doesn't match the format, such as for years < 1000 (including negative years)
2843 // in those cases just continue and use slower getJavaDate(cal, columnIndex, Types.DATE) method
2844 }
2845 cal = Calendar.getInstance();
2846 }
2847 final int ret = getJavaDate(cal, columnIndex, Types.DATE);
2848 return ret == -1 ? null : new java.sql.Date(cal.getTimeInMillis());
2849 } catch (IndexOutOfBoundsException e) {
2850 throw newSQLInvalidColumnIndexException(columnIndex);
2851 }
2852 }
2853
2854 /**
2855 * Retrieves the value of the designated column in the current row of this
2856 * ResultSet object as a java.sql.Date object in the Java programming
2857 * language.
2858 *
2859 * @param columnLabel the SQL name of the column from which to retrieve the value
2860 * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null
2861 * @throws SQLException if a database access error occurs
2862 */
2863 @Override
2864 public java.sql.Date getDate(final String columnLabel) throws SQLException {
2865 return getDate(findColumn(columnLabel), null);
2866 }
2867
2868 /**
2869 * Retrieves the value of the designated column in the current row of this
2870 * ResultSet object as a java.sql.Date object in the Java programming
2871 * language. This method uses the given calendar to construct an appropriate
2872 * millisecond value for the date if the underlying database does not store
2873 * timezone information.
2874 *
2875 * @param columnLabel the SQL name of the column from which to retrieve the value
2876 * @param cal the java.util.Calendar object to use in constructing the date
2877 * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null
2878 * @throws SQLException if a database access error occurs
2879 */
2880 @Override
2881 public java.sql.Date getDate(final String columnLabel, final Calendar cal)
2882 throws SQLException
2883 {
2884 return getDate(findColumn(columnLabel), cal);
2885 }
2886
2887 /**
2888 * Retrieves the value of the designated column in the current row of this
2889 * ResultSet object as a java.sql.Time object in the Java programming
2890 * language.
2891 *
2892 * @param columnIndex the first column is 1, the second is 2, ...
2893 * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null
2894 * @throws SQLException if a database access error occurs
2895 */
2896 @Override
2897 public Time getTime(final int columnIndex) throws SQLException {
2898 return getTime(columnIndex, null);
2899 }
2900
2901 /**
2902 * Retrieves the value of the designated column in the current row of
2903 * this ResultSet object as a java.sql.Time object in the Java programming
2904 * language. This method uses the given calendar to construct an appropriate
2905 * millisecond value for the time if the underlying database does not store
2906 * timezone information.
2907 *
2908 * @param columnIndex the first column is 1, the second is 2, ...
2909 * @param cal the java.util.Calendar object to use in constructing the timestamp
2910 * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null
2911 * @throws SQLException if a database access error occurs
2912 */
2913 @Override
2914 public Time getTime(final int columnIndex, Calendar cal)
2915 throws SQLException
2916 {
2917 checkNotClosed();
2918 try {
2919 final String val = tlp.values[columnIndex - 1];
2920 if (val == null) {
2921 lastReadWasNull = true;
2922 return null;
2923 }
2924 lastReadWasNull = false;
2925 if (cal == null) {
2926 // try to convert string directly to a Time object
2927 // Note: the string must be in JDBC time escape format: hh:mm:ss
2928 try {
2929 return Time.valueOf(val);
2930 } catch (IllegalArgumentException iae) {
2931 // this happens if string doesn't match the format or hh >= 24 or mm >= 60 or ss >= 60
2932 // in those cases just continue and use slower getJavaDate(cal, columnIndex, Types.TIME) method
2933 }
2934 cal = Calendar.getInstance();
2935 }
2936 final int ret = getJavaDate(cal, columnIndex, Types.TIME);
2937 return ret == -1 ? null : new Time(cal.getTimeInMillis());
2938 } catch (IndexOutOfBoundsException e) {
2939 throw newSQLInvalidColumnIndexException(columnIndex);
2940 }
2941 }
2942
2943 /**
2944 * Retrieves the value of the designated column in the current row of this
2945 * ResultSet object as a java.sql.Time object in the Java programming
2946 * language.
2947 *
2948 * @param columnLabel the SQL name of the column
2949 * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null
2950 * @throws SQLException if a database access error occurs
2951 */
2952 @Override
2953 public Time getTime(final String columnLabel) throws SQLException {
2954 return getTime(findColumn(columnLabel), null);
2955 }
2956
2957 /**
2958 * Retrieves the value of the designated column in the current row of
2959 * this ResultSet object as a java.sql.Time object in the Java programming
2960 * language. This method uses the given calendar to construct an appropriate
2961 * millisecond value for the time if the underlying database does not store
2962 * timezone information.
2963 *
2964 * @param columnLabel the SQL name of the column
2965 * @param cal the java.util.Calendar object to use in constructing the timestamp
2966 * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null
2967 * @throws SQLException if a database access error occurs
2968 */
2969 @Override
2970 public Time getTime(final String columnLabel, final Calendar cal)
2971 throws SQLException
2972 {
2973 return getTime(findColumn(columnLabel), cal);
2974 }
2975
2976 /**
2977 * Retrieves the value of the designated column in the current row of this
2978 * ResultSet object as a java.sql.Timestamp object in the Java programming
2979 * language.
2980 *
2981 * @param columnIndex the first column is 1, the second is 2, ...
2982 * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null
2983 * @throws SQLException if a database access error occurs
2984 */
2985 @Override
2986 public Timestamp getTimestamp(final int columnIndex) throws SQLException {
2987 return getTimestamp(columnIndex, null);
2988 }
2989
2990 /**
2991 * Retrieves the value of the designated column in the current row of this
2992 * ResultSet object as a java.sql.Timestamp object in the Java programming
2993 * language. This method uses the given calendar to construct an appropriate
2994 * millisecond value for the timestamp if the underlying database does not
2995 * store timezone information.
2996 *
2997 * @param columnIndex the first column is 1, the second is 2, ...
2998 * @param cal the java.util.Calendar object to use in constructing the timestamp
2999 * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null
3000 * @throws SQLException if a database access error occurs
3001 */
3002 @Override
3003 public Timestamp getTimestamp(final int columnIndex, Calendar cal)
3004 throws SQLException
3005 {
3006 checkNotClosed();
3007 try {
3008 final String val = tlp.values[columnIndex - 1];
3009 if (val == null) {
3010 lastReadWasNull = true;
3011 return null;
3012 }
3013 lastReadWasNull = false;
3014 if (cal == null) {
3015 // try to convert the string directly to a Timestamp object
3016 // Note: the string must be in JDBC timestamp escape format: yyyy-[m]m-[d]d hh:mm:ss[.f...]
3017 try {
3018 return Timestamp.valueOf(val);
3019 } catch (IllegalArgumentException iae) {
3020 // this happens if string doesn't match the format, such as for years < 1000 (including negative years)
3021 // in those cases just continue and use slower getJavaDate(cal, columnIndex, Types.TIMESTAMP) method
3022 }
3023 cal = Calendar.getInstance();
3024 }
3025 final int nanos = getJavaDate(cal, columnIndex, Types.TIMESTAMP);
3026 if (nanos == -1)
3027 return null;
3028
3029 final Timestamp ts = new Timestamp(cal.getTimeInMillis());
3030 ts.setNanos(nanos);
3031 return ts;
3032 } catch (IndexOutOfBoundsException e) {
3033 throw newSQLInvalidColumnIndexException(columnIndex);
3034 }
3035 }
3036
3037 /**
3038 * Retrieves the value of the designated column in the current row of this
3039 * ResultSet object as a java.sql.Timestamp object in the Java programming
3040 * language.
3041 *
3042 * @param columnLabel the SQL name of the column
3043 * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null
3044 * @throws SQLException if a database access error occurs
3045 */
3046 @Override
3047 public Timestamp getTimestamp(final String columnLabel) throws SQLException {
3048 return getTimestamp(findColumn(columnLabel), null);
3049 }
3050
3051 /**
3052 * Retrieves the value of the designated column in the current row of this
3053 * ResultSet object as a java.sql.Timestamp object in the Java programming
3054 * language. This method uses the given calendar to construct an appropriate
3055 * millisecond value for the timestamp if the underlying database does not
3056 * store timezone information.
3057 *
3058 * @param columnLabel the SQL name of the column
3059 * @param cal the java.util.Calendar object to use in constructing the timestamp
3060 * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null
3061 * @throws SQLException if a database access error occurs
3062 */
3063 @Override
3064 public Timestamp getTimestamp(final String columnLabel, final Calendar cal)
3065 throws SQLException
3066 {
3067 return getTimestamp(findColumn(columnLabel), cal);
3068 }
3069
3070 /**
3071 * Retrieves the type of this ResultSet object. The type is determined by
3072 * the Statement object that created the result set.
3073 *
3074 * @return ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE,
3075 * or ResultSet.TYPE_SCROLL_SENSITIVE
3076 */
3077 @Override
3078 public int getType() {
3079 return type;
3080 }
3081
3082 /**
3083 * Retrieves the value of the designated column in the current row
3084 * of this ResultSet object as a java.net.URL object in the Java
3085 * programming language.
3086 *
3087 * @param columnIndex the index of the column 1 is the first, 2 is the second,...
3088 * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null
3089 * @throws SQLException if a database access error occurs, or if a URL is malformed
3090 */
3091 @Override
3092 public URL getURL(final int columnIndex) throws SQLException {
3093 checkNotClosed();
3094 try {
3095 final String val = tlp.values[columnIndex - 1];
3096 if (val == null) {
3097 lastReadWasNull = true;
3098 return null;
3099 }
3100 lastReadWasNull = false;
3101 try {
3102 return new URL(val);
3103 } catch (java.net.MalformedURLException e) {
3104 throw new SQLException(e.getMessage(), "M1M05");
3105 }
3106 } catch (IndexOutOfBoundsException e) {
3107 throw newSQLInvalidColumnIndexException(columnIndex);
3108 }
3109 }
3110
3111 /**
3112 * Retrieves the value of the designated column in the current row
3113 * of this ResultSet object as a java.net.URL object in the Java
3114 * programming language.
3115 *
3116 * @param columnLabel the SQL name of the column
3117 * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null
3118 * @throws SQLException if a database access error occurs, or if a URL is malformed
3119 */
3120 @Override
3121 public URL getURL(final String columnLabel) throws SQLException {
3122 return getURL(findColumn(columnLabel));
3123 }
3124
3125 /**
3126 * Retrieves the first warning reported by calls on this ResultSet object.
3127 * If there is more than one warning, subsequent warnings will be chained to
3128 * the first one and can be retrieved by calling the method
3129 * SQLWarning.getNextWarning on the warning that was retrieved previously.
3130 *
3131 * This method may not be called on a closed result set; doing so will cause
3132 * an SQLException to be thrown.
3133 *
3134 * Note: Subsequent warnings will be chained to this SQLWarning.
3135 *
3136 * @return the first SQLWarning object or null if there are none
3137 * @throws SQLException if a database access error occurs or this method is called on a closed result set
3138 */
3139 @Override
3140 public SQLWarning getWarnings() throws SQLException {
3141 checkNotClosed();
3142 // if there are no warnings, this will be null, which fits with the
3143 // specification.
3144 return warnings;
3145 }
3146
3147 /**
3148 * Retrieves whether the cursor is after the last row in this ResultSet
3149 * object.
3150 *
3151 * @return true if the cursor is after the last row; false if the cursor is
3152 * at any other position or the result set contains no rows
3153 * @throws SQLException if a database access error occurs or this method is called on a closed result set
3154 */
3155 @Override
3156 public boolean isAfterLast() throws SQLException {
3157 checkNotClosed();
3158 return curRow == tupleCount + 1;
3159 }
3160
3161 /**
3162 * Retrieves whether the cursor is before the first row in this ResultSet
3163 * object.
3164 *
3165 * @return true if the cursor is before the first row; false if the cursor
3166 * is at any other position or the result set contains no rows
3167 * @throws SQLException if a database access error occurs or this method is called on a closed result set
3168 */
3169 @Override
3170 public boolean isBeforeFirst() throws SQLException {
3171 checkNotClosed();
3172 return curRow == 0;
3173 }
3174
3175 /**
3176 * Retrieves whether this ResultSet object has been closed. A
3177 * ResultSet is closed if the method close has been called on it, or
3178 * if it is automatically closed.
3179 *
3180 * @return true if this ResultSet object is closed; false if it is still open
3181 * @throws SQLException if a database access error occurs or this method is called on a closed result set
3182 */
3183 @Override
3184 public boolean isClosed() throws SQLException {
3185 return header != null && header.isClosed();
3186 }
3187
3188 /**
3189 * Retrieves whether the cursor is on the first row of this ResultSet
3190 * object.
3191 *
3192 * @return true if the cursor is on the first row; false otherwise
3193 * @throws SQLException if a database access error occurs or this method is called on a closed result set
3194 */
3195 @Override
3196 public boolean isFirst() throws SQLException {
3197 checkNotClosed();
3198 return curRow == 1;
3199 }
3200
3201 /**
3202 * Retrieves whether the cursor is on the last row of this ResultSet object.
3203 *
3204 * @return true if the cursor is on the last row; false otherwise
3205 * @throws SQLException if a database access error occurs or this method is called on a closed result set
3206 */
3207 @Override
3208 public boolean isLast() throws SQLException {
3209 checkNotClosed();
3210 return curRow == tupleCount;
3211 }
3212
3213 /**
3214 * Moves the cursor to the last row in this ResultSet object.
3215 *
3216 * @return true if the cursor is on a valid row; false if there are no rows
3217 * in the result set
3218 * @throws SQLException if a database access error occurs or the result set
3219 * type is TYPE_FORWARD_ONLY
3220 * @throws SQLException if a database access error occurs or this method is called on a closed result set
3221 */
3222 @Override
3223 public boolean last() throws SQLException {
3224 return absolute(-1);
3225 }
3226
3227 /**
3228 * Moves the cursor down one row from its current position. A ResultSet
3229 * cursor is initially positioned before the first row; the first call to
3230 * the method next makes the first row the current row; the second call
3231 * makes the second row the current row, and so on.
3232 *
3233 * If an input stream is open for the current row, a call to the method
3234 * next will implicitly close it. A ResultSet object's warning chain is
3235 * cleared when a new row is read.
3236 *
3237 * @return true if the new current row is valid; false if there are no
3238 * more rows
3239 * @throws SQLException if a database access error occurs or ResultSet is
3240 * closed
3241 */
3242 @Override
3243 public boolean next() throws SQLException {
3244 return relative(1);
3245 }
3246
3247 /**
3248 * Moves the cursor to the previous row in this ResultSet object.
3249 *
3250 * @return true if the cursor is on a valid row; false if it is off
3251 * the result set
3252 * @throws SQLException if a database access error occurs or ResultSet is
3253 * closed or the result set type is TYPE_FORWARD_ONLY
3254 */
3255 @Override
3256 public boolean previous() throws SQLException {
3257 return relative(-1);
3258 }
3259
3260 /**
3261 * Moves the cursor a relative number of rows, either positive or negative.
3262 * Attempting to move beyond the first/last row in the result set positions
3263 * the cursor before/after the the first/last row. Calling relative(0) is
3264 * valid, but does not change the cursor position.
3265 *
3266 * Note: Calling the method relative(1) is identical to calling the method
3267 * next() and calling the method relative(-1) is identical to calling the
3268 * method previous().
3269 *
3270 * @param rows an int specifying the number of rows to move from the current
3271 * row; a positive number moves the cursor forward; a negative number
3272 * moves the cursor backward
3273 * @return true if the cursor is on a row; false otherwise
3274 * @throws SQLException if a database access error occurs, there is no current
3275 * row, or the result set type is TYPE_FORWARD_ONLY
3276 */
3277 @Override
3278 public boolean relative(final int rows) throws SQLException {
3279 return absolute(curRow + rows);
3280 }
3281
3282 /**
3283 * Retrieves whether a row has been deleted. A deleted row may leave a visible "hole" in a result set.
3284 * This method can be used to detect holes in a result set.
3285 * The value returned depends on whether or not this ResultSet object can detect deletions.
3286 *
3287 * Note: Support for the rowDeleted method is optional with a result set concurrency of CONCUR_READ_ONLY
3288 *
3289 * Returns: true if the current row is detected to have been deleted by the owner or another; false otherwise
3290 *
3291 * Throws:
3292 * SQLException - if a database access error occurs or this method is called on a closed result set
3293 * Since: 1.2
3294 * See Also: DatabaseMetaData.deletesAreDetected(int)
3295 */
3296 @Override
3297 public boolean rowDeleted() throws SQLException {
3298 checkNotClosed();
3299 return false;
3300 }
3301
3302 /**
3303 * Retrieves whether the current row has had an insertion.
3304 * The value returned depends on whether or not this ResultSet object can detect visible inserts.
3305 *
3306 * Note: Support for the rowInserted method is optional with a result set concurrency of CONCUR_READ_ONLY
3307 *
3308 * Returns: true if the current row is detected to have been inserted; false otherwise
3309 *
3310 * Throws:
3311 * SQLException - if a database access error occurs or this method is called on a closed result set
3312 * Since: 1.2
3313 * See Also: DatabaseMetaData.insertsAreDetected(int)
3314 */
3315 @Override
3316 public boolean rowInserted() throws SQLException {
3317 checkNotClosed();
3318 return false;
3319 }
3320
3321 /**
3322 * Retrieves whether the current row has been updated.
3323 * The value returned depends on whether or not the result set can detect updates.
3324 *
3325 * Note: Support for the rowUpdated method is optional with a result set concurrency of CONCUR_READ_ONLY
3326 *
3327 * Returns: true if the current row is detected to have been visibly updated by the owner or another; false otherwise
3328 *
3329 * Throws:
3330 * SQLException - if a database access error occurs or this method is called on a closed result set
3331 * Since: 1.2
3332 * See Also: DatabaseMetaData.updatesAreDetected(int)
3333 */
3334 @Override
3335 public boolean rowUpdated() throws SQLException {
3336 checkNotClosed();
3337 return false;
3338 }
3339
3340
3341 /* Next methods are all related to updateable result sets, which we do not support.
3342 * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
3343 */
3344 @Override
3345 public void cancelRowUpdates() throws SQLException {
3346 throw newSQLFeatureNotSupportedException("cancelRowUpdates");
3347 }
3348
3349 @Override
3350 public void deleteRow() throws SQLException {
3351 throw newSQLFeatureNotSupportedException("deleteRow");
3352 }
3353
3354 @Override
3355 public void insertRow() throws SQLException {
3356 throw newSQLFeatureNotSupportedException("insertRow");
3357 }
3358
3359 @Override
3360 public void moveToCurrentRow() throws SQLException {
3361 throw newSQLFeatureNotSupportedException("moveToCurrentRow");
3362 }
3363
3364 @Override
3365 public void moveToInsertRow() throws SQLException {
3366 throw newSQLFeatureNotSupportedException("moveToInsertRow");
3367 }
3368
3369 @Override
3370 public void refreshRow() throws SQLException {
3371 throw newSQLFeatureNotSupportedException("refreshRow");
3372 }
3373
3374
3375 @Override
3376 public void updateArray(int columnIndex, Array x) throws SQLException {
3377 throw newSQLFeatureNotSupportedException("updateArray");
3378 }
3379
3380 @Override
3381 public void updateArray(String columnLabel, Array x) throws SQLException {
3382 throw newSQLFeatureNotSupportedException("updateArray");
3383 }
3384
3385 @Override
3386 public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException {
3387 throw newSQLFeatureNotSupportedException("updateAsciiStream");
3388 }
3389
3390 @Override
3391 public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException {
3392 throw newSQLFeatureNotSupportedException("updateAsciiStream");
3393 }
3394
3395 @Override
3396 public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
3397 throw newSQLFeatureNotSupportedException("updateAsciiStream");
3398 }
3399
3400 @Override
3401 public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
3402 throw newSQLFeatureNotSupportedException("updateAsciiStream");
3403 }
3404
3405 @Override
3406 public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException {
3407 throw newSQLFeatureNotSupportedException("updateAsciiStream");
3408 }
3409
3410 @Override
3411 public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException {
3412 throw newSQLFeatureNotSupportedException("updateAsciiStream");
3413 }
3414
3415 @Override
3416 public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
3417 throw newSQLFeatureNotSupportedException("updateBigDecimal");
3418 }
3419
3420 @Override
3421 public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException {
3422 throw newSQLFeatureNotSupportedException("updateBigDecimal");
3423 }
3424
3425 @Override
3426 public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
3427 throw newSQLFeatureNotSupportedException("updateBinaryStream");
3428 }
3429
3430 @Override
3431 public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException {
3432 throw newSQLFeatureNotSupportedException("updateBinaryStream");
3433 }
3434
3435 @Override
3436 public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
3437 throw newSQLFeatureNotSupportedException("updateBinaryStream");
3438 }
3439
3440 @Override
3441 public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
3442 throw newSQLFeatureNotSupportedException("updateBinaryStream");
3443 }
3444
3445 @Override
3446 public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException {
3447 throw newSQLFeatureNotSupportedException("updateBinaryStream");
3448 }
3449
3450 @Override
3451 public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException {
3452 throw newSQLFeatureNotSupportedException("updateBinaryStream");
3453 }
3454
3455 @Override
3456 public void updateBlob(int columnIndex, Blob x) throws SQLException {
3457 throw newSQLFeatureNotSupportedException("updateBlob");
3458 }
3459
3460 @Override
3461 public void updateBlob(int columnIndex, InputStream s) throws SQLException {
3462 throw newSQLFeatureNotSupportedException("updateBlob");
3463 }
3464
3465 @Override
3466 public void updateBlob(int columnIndex, InputStream s, long length) throws SQLException {
3467 throw newSQLFeatureNotSupportedException("updateBlob");
3468 }
3469
3470 @Override
3471 public void updateBlob(String columnLabel, Blob x) throws SQLException {
3472 throw newSQLFeatureNotSupportedException("updateBlob");
3473 }
3474
3475 @Override
3476 public void updateBlob(String columnLabel, InputStream s) throws SQLException {
3477 throw newSQLFeatureNotSupportedException("updateBlob");
3478 }
3479
3480 @Override
3481 public void updateBlob(String columnLabel, InputStream s, long length) throws SQLException {
3482 throw newSQLFeatureNotSupportedException("updateBlob");
3483 }
3484
3485 @Override
3486 public void updateBoolean(int columnIndex, boolean x) throws SQLException {
3487 throw newSQLFeatureNotSupportedException("updateBoolean");
3488 }
3489
3490 @Override
3491 public void updateBoolean(String columnLabel, boolean x) throws SQLException {
3492 throw newSQLFeatureNotSupportedException("updateBoolean");
3493 }
3494
3495 @Override
3496 public void updateByte(int columnIndex, byte x) throws SQLException {
3497 throw newSQLFeatureNotSupportedException("updateByte");
3498 }
3499
3500 @Override
3501 public void updateByte(String columnLabel, byte x) throws SQLException {
3502 throw newSQLFeatureNotSupportedException("updateByte");
3503 }
3504
3505 @Override
3506 public void updateBytes(int columnIndex, byte[] x) throws SQLException {
3507 throw newSQLFeatureNotSupportedException("updateBytes");
3508 }
3509
3510 @Override
3511 public void updateBytes(String columnLabel, byte[] x) throws SQLException {
3512 throw newSQLFeatureNotSupportedException("updateBytes");
3513 }
3514
3515 @Override
3516 public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
3517 throw newSQLFeatureNotSupportedException("updateCharacterStream");
3518 }
3519
3520 @Override
3521 public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException {
3522 throw newSQLFeatureNotSupportedException("updateCharacterStream");
3523 }
3524
3525 @Override
3526 public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
3527 throw newSQLFeatureNotSupportedException("updateCharacterStream");
3528 }
3529
3530 @Override
3531 public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
3532 throw newSQLFeatureNotSupportedException("updateCharacterStream");
3533 }
3534
3535 @Override
3536 public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException {
3537 throw newSQLFeatureNotSupportedException("updateCharacterStream");
3538 }
3539
3540 @Override
3541 public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
3542 throw newSQLFeatureNotSupportedException("updateCharacterStream");
3543 }
3544
3545 @Override
3546 public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
3547 throw newSQLFeatureNotSupportedException("updateNCharacterStream");
3548 }
3549
3550 @Override
3551 public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
3552 throw newSQLFeatureNotSupportedException("updateNCharacterStream");
3553 }
3554
3555 @Override
3556 public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException {
3557 throw newSQLFeatureNotSupportedException("updateNCharacterStream");
3558 }
3559
3560 @Override
3561 public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
3562 throw newSQLFeatureNotSupportedException("updateNCharacterStream");
3563 }
3564
3565 @Override
3566 public void updateClob(int columnIndex, Clob x) throws SQLException {
3567 throw newSQLFeatureNotSupportedException("updateClob");
3568 }
3569
3570 @Override
3571 public void updateClob(int columnIndex, Reader r) throws SQLException {
3572 throw newSQLFeatureNotSupportedException("updateClob");
3573 }
3574
3575 @Override
3576 public void updateClob(int columnIndex, Reader r, long length) throws SQLException {
3577 throw newSQLFeatureNotSupportedException("updateClob");
3578 }
3579
3580 @Override
3581 public void updateClob(String columnLabel, Clob x) throws SQLException {
3582 throw newSQLFeatureNotSupportedException("updateClob");
3583 }
3584
3585 @Override
3586 public void updateClob(String columnLabel, Reader r) throws SQLException {
3587 throw newSQLFeatureNotSupportedException("updateClob");
3588 }
3589
3590 @Override
3591 public void updateClob(String columnLabel, Reader r, long length) throws SQLException {
3592 throw newSQLFeatureNotSupportedException("updateClob");
3593 }
3594
3595 @Override
3596 public void updateNClob(int columnIndex, NClob x) throws SQLException {
3597 throw newSQLFeatureNotSupportedException("updateNClob");
3598 }
3599
3600 @Override
3601 public void updateNClob(int columnIndex, Reader r) throws SQLException {
3602 throw newSQLFeatureNotSupportedException("updateNClob");
3603 }
3604
3605 @Override
3606 public void updateNClob(int columnIndex, Reader r, long length) throws SQLException {
3607 throw newSQLFeatureNotSupportedException("updateNClob");
3608 }
3609
3610 @Override
3611 public void updateNClob(String columnLabel, NClob x) throws SQLException {
3612 throw newSQLFeatureNotSupportedException("updateNClob");
3613 }
3614
3615 @Override
3616 public void updateNClob(String columnLabel, Reader r) throws SQLException {
3617 throw newSQLFeatureNotSupportedException("updateNClob");
3618 }
3619
3620 @Override
3621 public void updateNClob(String columnLabel, Reader r, long length) throws SQLException {
3622 throw newSQLFeatureNotSupportedException("updateNClob");
3623 }
3624
3625 @Override
3626 public void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
3627 throw newSQLFeatureNotSupportedException("updateDate");
3628 }
3629
3630 @Override
3631 public void updateDate(String columnLabel, java.sql.Date x) throws SQLException {
3632 throw newSQLFeatureNotSupportedException("updateDate");
3633 }
3634
3635 @Override
3636 public void updateDouble(int columnIndex, double x) throws SQLException {
3637 throw newSQLFeatureNotSupportedException("updateDouble");
3638 }
3639
3640 @Override
3641 public void updateDouble(String columnLabel, double x) throws SQLException {
3642 throw newSQLFeatureNotSupportedException("updateDouble");
3643 }
3644
3645 @Override
3646 public void updateFloat(int columnIndex, float x) throws SQLException {
3647 throw newSQLFeatureNotSupportedException("updateFloat");
3648 }
3649
3650 @Override
3651 public void updateFloat(String columnLabel, float x) throws SQLException {
3652 throw newSQLFeatureNotSupportedException("updateFloat");
3653 }
3654
3655 @Override
3656 public void updateInt(int columnIndex, int x) throws SQLException {
3657 throw newSQLFeatureNotSupportedException("updateInt");
3658 }
3659
3660 @Override
3661 public void updateInt(String columnLabel, int x) throws SQLException {
3662 throw newSQLFeatureNotSupportedException("updateInt");
3663 }
3664
3665 @Override
3666 public void updateLong(int columnIndex, long x) throws SQLException {
3667 throw newSQLFeatureNotSupportedException("updateLong");
3668 }
3669
3670 @Override
3671 public void updateLong(String columnLabel, long x) throws SQLException {
3672 throw newSQLFeatureNotSupportedException("updateLong");
3673 }
3674
3675 @Override
3676 public void updateNull(int columnIndex) throws SQLException {
3677 throw newSQLFeatureNotSupportedException("updateNull");
3678 }
3679
3680 @Override
3681 public void updateNull(String columnLabel) throws SQLException {
3682 throw newSQLFeatureNotSupportedException("updateNull");
3683 }
3684
3685 @Override
3686 public void updateObject(int columnIndex, Object x) throws SQLException {
3687 throw newSQLFeatureNotSupportedException("updateObject");
3688 }
3689
3690 @Override
3691 public void updateObject(int columnIndex, Object x, int scale) throws SQLException {
3692 throw newSQLFeatureNotSupportedException("updateObject");
3693 }
3694
3695 @Override
3696 public void updateObject(String columnLabel, Object x) throws SQLException {
3697 throw newSQLFeatureNotSupportedException("updateObject");
3698 }
3699
3700 @Override
3701 public void updateObject(String columnLabel, Object x, int scale) throws SQLException {
3702 throw newSQLFeatureNotSupportedException("updateObject");
3703 }
3704
3705 @Override
3706 public void updateRef(int columnIndex, Ref x) throws SQLException {
3707 throw newSQLFeatureNotSupportedException("updateRef");
3708 }
3709
3710 @Override
3711 public void updateRef(String columnLabel, Ref x) throws SQLException {
3712 throw newSQLFeatureNotSupportedException("updateRef");
3713 }
3714
3715 @Override
3716 public void updateRow() throws SQLException {
3717 throw newSQLFeatureNotSupportedException("updateRow");
3718 }
3719
3720 @Override
3721 public void updateRowId(int columnIndex, RowId x) throws SQLException {
3722 throw newSQLFeatureNotSupportedException("updateRowId");
3723 }
3724
3725 @Override
3726 public void updateRowId(String columnLabel, RowId x) throws SQLException {
3727 throw newSQLFeatureNotSupportedException("updateRowId");
3728 }
3729
3730 @Override
3731 public void updateShort(int columnIndex, short x) throws SQLException {
3732 throw newSQLFeatureNotSupportedException("updateShort");
3733 }
3734
3735 @Override
3736 public void updateShort(String columnLabel, short x) throws SQLException {
3737 throw newSQLFeatureNotSupportedException("updateShort");
3738 }
3739
3740 @Override
3741 public void updateString(int columnIndex, String x) throws SQLException {
3742 throw newSQLFeatureNotSupportedException("updateString");
3743 }
3744
3745 @Override
3746 public void updateString(String columnLabel, String x) throws SQLException {
3747 throw newSQLFeatureNotSupportedException("updateString");
3748 }
3749
3750 @Override
3751 public void updateNString(int columnIndex, String x) throws SQLException {
3752 throw newSQLFeatureNotSupportedException("updateNString");
3753 }
3754
3755 @Override
3756 public void updateNString(String columnLabel, String x) throws SQLException {
3757 throw newSQLFeatureNotSupportedException("updateNString");
3758 }
3759
3760 @Override
3761 public void updateSQLXML(int columnIndex, SQLXML x) throws SQLException {
3762 throw newSQLFeatureNotSupportedException("updateSQLXML");
3763 }
3764
3765 @Override
3766 public void updateSQLXML(String columnLabel, SQLXML x) throws SQLException {
3767 throw newSQLFeatureNotSupportedException("updateSQLXML");
3768 }
3769
3770 @Override
3771 public void updateTime(int columnIndex, Time x) throws SQLException {
3772 throw newSQLFeatureNotSupportedException("updateTime");
3773 }
3774
3775 @Override
3776 public void updateTime(String columnLabel, Time x) throws SQLException {
3777 throw newSQLFeatureNotSupportedException("updateTime");
3778 }
3779
3780 @Override
3781 public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
3782 throw newSQLFeatureNotSupportedException("updateTimestamp");
3783 }
3784
3785 @Override
3786 public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException {
3787 throw newSQLFeatureNotSupportedException("updateTimestamp");
3788 }
3789
3790 // Chapter 14.2.3.3 Sun JDBC 3.0 Specification
3791 /**
3792 * Reports whether the last column read had a value of SQL NULL. Note that
3793 * you must first call one of the getter methods on a column to try to read
3794 * its value and then call the method wasNull to see if the value read was
3795 * SQL NULL.
3796 *
3797 * @return true if the last column value read was SQL NULL and false otherwise
3798 */
3799 @Override
3800 public boolean wasNull() {
3801 return lastReadWasNull;
3802 }
3803
3804 //== Java 1.7 methods (JDBC 4.1)
3805
3806 /**
3807 * Retrieves the value of the designated column in the current row
3808 * of this ResultSet object and will convert from the SQL type of
3809 * the column to the requested Java data type, if the conversion is
3810 * supported. If the conversion is not supported or null is
3811 * specified for the type, a SQLException is thrown.
3812 *
3813 * At a minimum, an implementation must support the conversions defined
3814 * in Appendix B, Table B-3 and conversion of appropriate user defined
3815 * SQL types to a Java type which implements SQLData, or Struct.
3816 * Additional conversions may be supported and are vendor defined.
3817 *
3818 * @param columnIndex the first column is 1, the second is 2, ...
3819 * @param type Class representing the Java data type to convert the
3820 * designated column to
3821 * @return an instance of type holding the column value
3822 * @throws SQLException if conversion is not supported, type is
3823 * null or another error occurs. The getCause() method of
3824 * the exception may provide a more detailed exception, for
3825 * example, if a conversion error occurs
3826 * @throws SQLFeatureNotSupportedException the JDBC driver does
3827 * not support this method
3828 */
3829 @Override
3830 public <T> T getObject(final int columnIndex, final Class<T> type) throws SQLException {
3831 checkNotClosed();
3832 if (type == null)
3833 throw new SQLException("type is null", "M1M05");
3834
3835 throw newSQLFeatureNotSupportedException("getObject(column, Class<T> type)");
3836 }
3837
3838 /**
3839 * Retrieves the value of the designated column in the current row
3840 * of this ResultSet object and will convert from the SQL type of
3841 * the column to the requested Java data type, if the conversion is
3842 * supported. If the conversion is not supported or null is
3843 * specified for the type, a SQLException is thrown.
3844 *
3845 * @param columnLabel the label for the column specified with the
3846 * SQL AS clause. If the SQL AS clause was not specified,
3847 * then the label is the name of the column
3848 * @param type Class representing the Java data type to convert the
3849 * designated column to
3850 * @return an instance of type holding the column value
3851 * @throws SQLException if conversion is not supported, type is
3852 * null or another error occurs. The getCause() method of
3853 * the exception may provide a more detailed exception, for
3854 * example, if a conversion error occurs
3855 * @throws SQLFeatureNotSupportedException the JDBC driver does
3856 * not support this method
3857 */
3858 @Override
3859 public <T> T getObject(final String columnLabel, final Class<T> type) throws SQLException {
3860 return getObject(findColumn(columnLabel), type);
3861 }
3862
3863 //== Java 1.8 methods (JDBC 4.2)
3864
3865 @Override
3866 public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
3867 throw newSQLFeatureNotSupportedException("updateObject");
3868 }
3869
3870 @Override
3871 public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
3872 throw newSQLFeatureNotSupportedException("updateObject");
3873 }
3874
3875 @Override
3876 public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException {
3877 throw newSQLFeatureNotSupportedException("updateObject");
3878 }
3879
3880 @Override
3881 public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException {
3882 throw newSQLFeatureNotSupportedException("updateObject");
3883 }
3884
3885 //== end methods of interface ResultSet
3886
3887
3888 //== internal helper methods which do not belong to the JDBC interface
3889
3890 /**
3891 * Adds a warning to the pile of warnings this ResultSet object has. If
3892 * there were no warnings (or clearWarnings was called) this warning will
3893 * be the first, otherwise this warning will get appended to the current
3894 * warning.
3895 *
3896 * @param reason the warning message
3897 */
3898 private void addWarning(final String reason, final String sqlstate) {
3899 SQLWarning warng = new SQLWarning(reason, sqlstate);
3900 if (warnings == null) {
3901 warnings = warng;
3902 } else {
3903 warnings.setNextWarning(warng);
3904 }
3905 }
3906
3907 /**
3908 * Local helper method to test whether the ResultSet object is closed
3909 * When closed it throws an SQLException
3910 */
3911 private void checkNotClosed() throws SQLException {
3912 if (isClosed())
3913 throw new SQLException("ResultSet is closed", "M1M20");
3914 }
3915
3916 /**
3917 * Small helper method that formats the "Invalid Column Index number ..." message
3918 * and creates a new SQLDataException object whose SQLState is set
3919 * to "22010": invalid indicator parameter value.
3920 *
3921 * @param colIdx the column index number
3922 * @return a new created SQLDataException object with SQLState 22010
3923 */
3924 public static final SQLDataException newSQLInvalidColumnIndexException(final int colIdx) {
3925 return new SQLDataException("Invalid Column Index number: " + colIdx, "22010");
3926 }
3927
3928 /**
3929 * Small helper method that formats the "Could not convert value to a number" message
3930 * and creates a new SQLDataException object whose SQLState is set
3931 * to "22003": Numeric value out of range.
3932 *
3933 * @param error the NumberFormatException
3934 * @return a new created SQLDataException object with SQLState 22003
3935 */
3936 private static final SQLDataException newSQLNumberFormatException(final NumberFormatException error) {
3937 return new SQLDataException("Could not convert value to a number. " + error.getMessage(), "22003");
3938 }
3939 }