Mercurial > hg > monetdb-java
view src/main/java/org/monetdb/jdbc/MonetResultSet.java @ 820:4c35009cd59c
In ResultSet.getObject() method added support for retrieving
TIMESTAMP WITH TIME ZONE data as java.time.OffsetDateTime object and
TIME WITH TIME ZONE as java.time.OffsetTime object.
Also methods ResultSetMetaData.getColumnClassName() and ParameterMetaData.getParameterClassName() now return
java.time.OffsetDateTime.class for columns of type TIMESTAMP WITH TIME ZONE and
java.time.OffsetTime.class for columns of type TIME WITH TIME ZONE.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 14 Dec 2023 14:58:21 +0100 (16 months ago) |
parents | e029af7551b7 |
children | 7eb05cbf67dc |
line wrap: on
line source
/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 1997 - July 2008 CWI, August 2008 - 2023 MonetDB B.V. */ package org.monetdb.jdbc; import org.monetdb.mcl.parser.MCLParseException; import org.monetdb.mcl.parser.TupleLineParser; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.NClob; import java.sql.Ref; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLData; import java.sql.SQLDataException; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLInput; import java.sql.SQLType; // new as of Java 1.8 import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.text.SimpleDateFormat; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.util.Calendar; import java.util.Map; import java.util.TimeZone; /** *<pre> * A {@link ResultSet} suitable for the MonetDB database. * * A table of data representing a database result set, which is usually * generated by executing a statement that queries the database. * * A ResultSet object maintains a cursor pointing to its current row of data. * Initially the cursor is positioned before the first row. The next method * moves the cursor to the next row, and because it returns false when there * are no more rows in the ResultSet object, it can be used in a while loop to * iterate through the result set. * * The current state of this ResultSet is that it supports positioning in the * result set, absolute and relative. A slight performance difference between * FORWARD_ONLY or result sets scrollable in both directions can be noticed as * for FORWARD_ONLY result sets the memory usage will be likely lower for large * result sets. *</pre> * * @author Fabian Groffen * @author Martin van Dinther * @version 1.0 */ public class MonetResultSet extends MonetWrapper implements ResultSet, AutoCloseable { static final int DEF_RESULTSETTYPE = ResultSet.TYPE_FORWARD_ONLY; static final int DEF_FETCHDIRECTION = ResultSet.FETCH_FORWARD; static final int DEF_CONCURRENCY = ResultSet.CONCUR_READ_ONLY; static final int DEF_HOLDABILITY = ResultSet.HOLD_CURSORS_OVER_COMMIT; /** The parental Statement object */ private final Statement statement; /** A Header to retrieve lines from. Note: it will be null in case of a MonetVirtualResultSet ! */ private final MonetConnection.ResultSetResponse header; /** The names of the columns in this ResultSet */ private final String[] columns; /** The MonetDB types of the columns in this ResultSet */ private final String[] types; /** The JDBC SQL types of the columns in this ResultSet. * The content will be derived once from the MonetDB String[] types */ private final int[] JdbcSQLTypes; /** A cache to reduce the number of ResultSetMetaData objects created by getMetaData() to maximum 1 per ResultSet */ private ResultSetMetaData rsmd; // the following have protected access modifier for the MonetVirtualResultSet subclass // they are accessed from MonetVirtualResultSet.absolute() /** The current line of the buffer split in columns */ protected final TupleLineParser tlp; /** The number of rows in this ResultSet */ protected final long tupleCount; /** The current position of the cursor for this ResultSet object */ protected int curRow; /** The type of this ResultSet (forward or scrollable) */ private int type = DEF_RESULTSETTYPE; /** The concurrency of this ResultSet (currently only read-only) */ private int concurrency = DEF_CONCURRENCY; /** The warnings for this ResultSet object */ private SQLWarning warnings; /** whether the last read field (via some getXyz() method) was NULL */ private boolean lastReadWasNull = true; /** to store the fetchsize set. */ private int fetchSize; /** * Main constructor backed by the given Header. * * @param statement the statement which created this ResultSet * @param header a header containing the query, resultset type, etc. * @throws IllegalArgumentException if called with null or invalid value for one of the arguments */ MonetResultSet( final Statement statement, final MonetConnection.ResultSetResponse header) throws IllegalArgumentException { if (statement == null) { throw new IllegalArgumentException("Statement may not be null!"); } if (header == null) { throw new IllegalArgumentException("Header may not be null!"); } this.statement = statement; this.header = header; type = header.getRSType(); concurrency = header.getRSConcur(); /* the fetchSize used for this result set is the header's cacheSize */ fetchSize = header.getCacheSize(); columns = header.getNames(); types = header.getTypes(); if (columns == null || types == null) { throw new IllegalArgumentException("Missing Header metadata"); } if (columns.length != types.length) { throw new IllegalArgumentException("Inconsistent Header metadata"); } tupleCount = header.tuplecount; // create result array tlp = new TupleLineParser(columns.length); // for efficiency derive the JDBC SQL type codes from the types[] names once JdbcSQLTypes = new int[types.length]; populateJdbcSQLtypesArray(); } /** * Constructor used by MonetVirtualResultSet. * DO NOT USE THIS CONSTRUCTOR IF YOU ARE NOT EXTENDING THIS OBJECT! * * @param statement the statement which created this ResultSet * @param columns the column names * @param types the column types * @param results the number of rows in the ResultSet * @throws IllegalArgumentException if called with null or invalid value for one of the arguments */ MonetResultSet( final Statement statement, final String[] columns, final String[] types, final int results) throws IllegalArgumentException { if (statement == null || columns == null || types == null) { throw new IllegalArgumentException("One of the given arguments is null"); } if (columns.length != types.length) { throw new IllegalArgumentException("Given arrays are not the same size"); } if (results < 0) { throw new IllegalArgumentException("Negative rowcount not allowed"); } this.statement = statement; header = null; fetchSize = 0; this.columns = columns; this.types = types; tupleCount = results; tlp = new TupleLineParser(columns.length); // for efficiency derive the JDBC SQL type codes from the types[] names once JdbcSQLTypes = new int[types.length]; populateJdbcSQLtypesArray(); } /** * Internal utility method to fill the JdbcSQLTypes array with derivable values. * By doing it once (in the constructor) we can avoid doing this in many getXyz() * methods again and again thereby improving getXyz() method performance. */ private final void populateJdbcSQLtypesArray() { MonetConnection connection = null; try { connection = (MonetConnection) statement.getConnection(); } catch (SQLException se) { /* ignore it */ } for (int i = 0; i < types.length; i++) { int javaSQLtype = MonetDriver.getJdbcSQLType(types[i]); if (javaSQLtype == Types.CLOB) { if (connection != null && connection.mapClobAsVarChar()) javaSQLtype = Types.VARCHAR; } else if (javaSQLtype == Types.BLOB) { if (connection != null && connection.mapBlobAsVarBinary()) javaSQLtype = Types.VARBINARY; } JdbcSQLTypes[i] = javaSQLtype; } } //== methods of interface ResultSet // Chapter 14.2.2 Sun JDBC 3.0 Specification /** * Moves the cursor to the given row number in this ResultSet object. * * If the row number is positive, the cursor moves to the given row number * with respect to the beginning of the result set. The first row is row 1, * the second is row 2, and so on. * * If the given row number is negative, the cursor moves to an absolute row * position with respect to the end of the result set. For example, calling * the method absolute(-1) positions the cursor on the last row; calling the * method absolute(-2) moves the cursor to the next-to-last row, and so on. * * An attempt to position the cursor beyond the first/last row in the result * set leaves the cursor before the first row or after the last row. * Note: calling absolute(1) is the same as calling first(). Calling * absolute(-1) is the same as calling last(). * * @param row the number of the row to which the cursor should move. A * positive number indicates the row number counting from the * beginning of the result set; a negative number indicates the row * number counting from the end of the result set * @return true if the cursor is on the result set; false otherwise * @throws SQLException if a database access error occurs, or the result set * type is TYPE_FORWARD_ONLY */ @Override public boolean absolute(int row) throws SQLException { checkNotClosed(); if (row != curRow + 1 && type == TYPE_FORWARD_ONLY) throw new SQLException("(Absolute) positioning not allowed on forward only result sets!", "M1M05"); // first calculate what the JDBC row is if (row < 0) { // calculate the negatives... row = (int) tupleCount + row + 1; } // now place the row not farther than just before or after the result if (row < 0) row = 0; // before first else if (row > tupleCount + 1) row = (int) tupleCount + 1; // after last // store it curRow = row; if (header == null) return false; final String tmpLine = header.getLine(row - 1); if (tmpLine == null) return false; try { tlp.parse(tmpLine); } catch (MCLParseException e) { throw new SQLException(e.getMessage(), "M0M10"); } return true; } /** * Moves the cursor to the end of this ResultSet object, just after the last * row. This method has no effect if the result set contains no rows. * * @throws SQLException if a database access error occurs or the result set * type is TYPE_FORWARD_ONLY */ @Override public void afterLast() throws SQLException { absolute((int)tupleCount + 1); } /** * Moves the cursor to the front of this ResultSet object, just before the * first row. This method has no effect if the result set contains no rows. * * @throws SQLException if a database access error occurs or the result set * type is TYPE_FORWARD_ONLY */ @Override public void beforeFirst() throws SQLException { absolute(0); } /** * Clears all warnings reported for this ResultSet object. After a call to * this method, the method getWarnings returns null until a new warning is * reported for this ResultSet object. */ @Override public void clearWarnings() { warnings = null; } /** * Releases this ResultSet object's database (and JDBC) resources * immediately instead of waiting for this to happen when it is * automatically closed. */ @Override public void close() { clearWarnings(); if (header != null && !header.isClosed()) { header.close(); } rsmd = null; if (statement instanceof MonetStatement) ((MonetStatement)statement).closeIfCompletion(); } // Chapter 14.2.3 from Sun JDBC 3.0 specification /** * Maps the given ResultSet column name to its ResultSet column index. * Column names supplied to getter methods are case insensitive. If a select * list contains the same column more than once, the first instance of the * column will be returned. * * @param columnLabel the name of the column * @return the column index of the given column name * @throws SQLException if the ResultSet object does not contain a column labeled columnLabel, * a database access error occurs or this method is called on a closed result set */ @Override public int findColumn(final String columnLabel) throws SQLException { checkNotClosed(); if (columnLabel != null) { final int array_size = columns.length; for (int i = 0; i < array_size; i++) { if (columnLabel.equals(columns[i])) return i + 1; } /* if an exact match did not succeed try a case insensitive match */ for (int i = 0; i < array_size; i++) { if (columnLabel.equalsIgnoreCase(columns[i])) return i + 1; } } throw new SQLException("No such column name: " + columnLabel, "M1M05"); } /** * Moves the cursor to the first row in this ResultSet object. * * @return true if the cursor is on a valid row; false if there are no rows * in the result set * @throws SQLException - if a database access error occurs or the result * set type is TYPE_FORWARD_ONLY */ @Override public boolean first() throws SQLException { return absolute(1); } @Override public Array getArray(final int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getArray"); } @Override public Array getArray(final String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getArray"); } /* Mapi doesn't allow something for streams at the moment, thus all not implemented for now */ @Override public InputStream getAsciiStream(final int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getAsciiStream"); } @Override public InputStream getAsciiStream(final String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getAsciiStream"); } @Override @Deprecated public InputStream getUnicodeStream(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getUnicodeStream"); } @Override @Deprecated public InputStream getUnicodeStream(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getUnicodeStream"); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a stream of uninterpreted bytes. The * value can then be read in chunks from the stream. This method is * particularly suitable for retrieving large LONGVARBINARY values. * * Note: All the data in the returned stream must be read prior to * getting the value of any other column. The next call to a getter * method implicitly closes the stream. Also, a stream may return 0 * when the method InputStream.available is called whether there is * data available or not. * * @param columnIndex the first column is 1, the second is 2, ... * @return a Java input stream that delivers the database column * value as a stream of uninterpreted bytes; if the value is SQL * NULL, the value returned is null * @throws SQLException if the columnIndex is not valid; if a * database access error occurs or this method is called on a closed result set */ @Override public InputStream getBinaryStream(final int columnIndex) throws SQLException { checkNotClosed(); try { switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: final Blob blob = getBlob(columnIndex); if (blob == null) return null; return blob.getBinaryStream(); case Types.BINARY: case Types.VARBINARY: /* case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY */ final byte[] bte = getBytes(columnIndex); if (bte == null) return null; return new java.io.ByteArrayInputStream(bte); } throw new SQLException("Cannot operate on type: " + types[columnIndex - 1], "M1M05"); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a stream of uninterpreted bytes. The * value can then be read in chunks from the stream. This method is * particularly suitable for retrieving large LONGVARBINARY values. * * Note: All the data in the returned stream must be read prior to * getting the value of any other column. The next call to a getter * method implicitly closes the stream. Also, a stream may return 0 * when the method available is called whether there is data * available or not. * * @param columnLabel the label for the column specified with * the SQL AS clause. If the SQL AS clause was not specified, then * the label is the name of the column * @return a Java input stream that delivers the database column * value as a stream of uninterpreted bytes; if the value is SQL * NULL, the result is null * @throws SQLException if the columnLabel is not valid; if a * database access error occurs or this method is called on a closed result set */ @Override public InputStream getBinaryStream(final String columnLabel) throws SQLException { return getBinaryStream(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.io.Reader object. * * @param columnIndex the first column is 1, the second is 2, ... * @return a java.io.Reader object that contains the column value; * if the value is SQL NULL, the value returned is null in * the Java programming language. * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public Reader getCharacterStream(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; return new java.io.StringReader(val); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.io.Reader object. * * @param columnLabel the name of the column * @return a java.io.Reader object that contains the column value; * if the value is SQL NULL, the value returned is null in * the Java programming language. * @throws SQLException if a database access error occurs */ @Override public Reader getCharacterStream(final String columnLabel) throws SQLException { return getCharacterStream(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.io.Reader object. It is * intended for use when accessing NCHAR,NVARCHAR and LONGNVARCHAR * columns. * * @param columnIndex the first column is 1, the second is 2, ... * @return a java.io.Reader object that contains the column value; * if the value is SQL NULL, the value returned is null in * the Java programming language. * @throws SQLException if a database access error occurs */ @Override public Reader getNCharacterStream(final int columnIndex) throws SQLException { return getCharacterStream(columnIndex); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.io.Reader object. It is * intended for use when accessing NCHAR,NVARCHAR and LONGNVARCHAR * columns. * * @param columnLabel the name of the column * @return a java.io.Reader object that contains the column value; * if the value is SQL NULL, the value returned is null in * the Java programming language. * @throws SQLException if a database access error occurs */ @Override public Reader getNCharacterStream(final String columnLabel) throws SQLException { return getCharacterStream(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a Blob object in the Java programming * language. * * @param columnIndex the first column is 1, the second is 2, ... * @return a Blob object representing the SQL BLOB value in the * specified column * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public Blob getBlob(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; return new MonetBlob(val); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a Blob object in the Java programming * language. * * @param columnLabel the name of the column from which to retrieve * the value * @return a Blob object representing the SQL BLOB value in the * specified column * @throws SQLException if a database access error occurs */ @Override public Blob getBlob(final String columnLabel) throws SQLException { return getBlob(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a Clob object in the * Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return a Clob object representing the SQL CLOB value in the * specified column * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public Clob getClob(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; return new MonetClob(val); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a Clob object in the * Java programming language. * * @param columnLabel the name of the column from which to retrieve * the value * @return a Clob object representing the SQL CLOB value in the * specified column * @throws SQLException if a database access error occurs */ @Override public Clob getClob(final String columnLabel) throws SQLException { return getClob(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a NClob object in the * Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return a NClob object representing the SQL NCLOB value in the * specified column * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override public NClob getNClob(final int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getNClob"); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a NClob object in the * Java programming language. * * @param columnLabel the name of the column from which to retrieve * the value * @return a NClob object representing the SQL NCLOB value in the * specified column * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override public NClob getNClob(final String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getNClob"); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.math.BigDecimal with full precision. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value (full precision); if the value is SQL NULL, * the value returned is null in the Java programming language. * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public BigDecimal getBigDecimal(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; return new BigDecimal(val); } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.math.BigDecimal with full precision. * * @param columnIndex the first column is 1, the second is 2, ... * @param scale the number of digits to the right of the decimal point * @return the column value (full precision); if the value is SQL NULL, * the value returned is null in the Java programming language. * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override @Deprecated public BigDecimal getBigDecimal(final int columnIndex, final int scale) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; return (new BigDecimal(val)).setScale(scale); } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.math.BigDecimal with full precision. * * @param columnLabel the SQL name of the column * @return the column value (full precision); if the value is SQL NULL, * the value returned is null in the Java programming language. * @throws SQLException if a database access error occurs */ @Override public BigDecimal getBigDecimal(final String columnLabel) throws SQLException { return getBigDecimal(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.math.BigDecimal with full precision. * * @param columnLabel the SQL name of the column * @param scale the number of digits to the right of the decimal point * @return the column value (full precision); if the value is SQL NULL, * the value returned is null in the Java programming language. * @throws SQLException if a database access error occurs */ @Override @Deprecated public BigDecimal getBigDecimal(final String columnLabel, final int scale) throws SQLException { return getBigDecimal(findColumn(columnLabel), scale); } // See Sun JDBC Specification 3.0 Table B-6 /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a boolean in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned * is false * @throws SQLException if the columnIndex is not valid; if a database access error occurs * or this method is called on a closed result set */ @Override public boolean getBoolean(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return false; // if the value is SQL NULL, the value returned is false } lastReadWasNull = false; // match common cases first if ("false".equalsIgnoreCase(val) || "0".equals(val)) return false; if ("true".equalsIgnoreCase(val) || "1".equals(val)) return true; // match type specific values switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BOOLEAN: case Types.CHAR: case Types.VARCHAR: /* case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR */ case Types.CLOB: // check if string value equals "true" (case insensitive) or not return Boolean.parseBoolean(val); case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: if (getInt(columnIndex) == 0) { return false; } return true; case Types.BIGINT: if (getLong(columnIndex) == 0L) { return false; } return true; case Types.DOUBLE: case Types.FLOAT: case Types.REAL: if (getDouble(columnIndex) == 0.0) { return false; } return true; case Types.DECIMAL: case Types.NUMERIC: if (getBigDecimal(columnIndex).compareTo(BigDecimal.ZERO) == 0) { return false; } return true; default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to boolean type not supported", "M1M05"); } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a boolean in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned * is false * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public boolean getBoolean(final String columnLabel) throws SQLException { return getBoolean(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a byte in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned * is 0 * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public byte getByte(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return (byte) 0; } lastReadWasNull = false; return Byte.parseByte(val); } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a byte in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned * is 0 * @throws SQLException if a database access error occurs */ @Override public byte getByte(final String columnLabel) throws SQLException { return getByte(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a byte array in the Java programming language. The * bytes represent the raw values returned by the driver. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned * is null * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public byte[] getBytes(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; // According to Table B-6, getBytes() only operates on BINARY types switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: case Types.BINARY: case Types.VARBINARY: /* case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY */ return MonetBlob.hexStrToByteArray(val); default: throw new SQLException("Cannot operate on type: " + types[columnIndex - 1], "M1M05"); } } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a byte array in the Java programming language. The * bytes represent the raw values returned by the driver. * * NOTE: Since the mapi protocol is ASCII-based, this method only returns * Java byte representations of Strings, which is nothing more than * an encoding into a sequence of bytes using the platform's default * charset. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned * is null * @throws SQLException if a database access error occurs */ @Override public byte[] getBytes(final String columnLabel) throws SQLException { return getBytes(findColumn(columnLabel)); } /** * Retrieves the concurrency mode of this ResultSet object. The concurrency * used is determined by the Statement object that created the result set. * * NOTE: MonetDB only supports read-only result sets, and will always return * ResultSet.CONCUR_READ_ONLY * * @return the concurrency type, either ResultSet.CONCUR_READ_ONLY or * ResultSet.CONCUR_UPDATABLE */ @Override public int getConcurrency() { return concurrency; } /** * Retrieves the name of the SQL cursor used by this ResultSet object. * In SQL, a result table is retrieved through a cursor that is named. * For MonetDB this is the header.id returned in a resultset header. The * current row of a result set can be updated or deleted using a positioned * update/delete statement that references the cursor name. To insure that * the cursor has the proper isolation level to support update, the * cursor's SELECT statement should be of the form SELECT FOR UPDATE. If * FOR UPDATE is omitted, the positioned updates may fail. * * The JDBC API supports this SQL feature by providing the name of the SQL * cursor used by a ResultSet object. The current row of a ResultSet object * is also the current row of this SQL cursor. * * Note: If positioned update is not supported, a SQLException is thrown. * MonetDB currently doesn't support updates, so the SQLException is * thrown for now. * * @return the SQL name for this ResultSet object's cursor * @throws SQLException if a database access error occurs */ @Override public String getCursorName() throws SQLException { throw new SQLException("Positioned updates not supported for this cursor (" + (header != null ? header.id + ")" : ")"), "0AM21"); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a double in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if there is no such column or this method is called on a closed result set */ @Override public double getDouble(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return 0; } lastReadWasNull = false; return Double.parseDouble(val); } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a double in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public double getDouble(final String columnLabel) throws SQLException { return getDouble(findColumn(columnLabel)); } /** * Retrieves the holdability of this ResultSet object. * * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or * ResultSet.CLOSE_CURSORS_AT_COMMIT * @throws SQLException if a database access error occurs */ @Override public int getHoldability() throws SQLException { return getStatement().getConnection().getHoldability(); } /** * Retrieves the fetch direction for this ResultSet object. * * @return the current fetch direction for this ResultSet object */ @Override public int getFetchDirection() { return ResultSet.FETCH_FORWARD; } /** * Gives a hint as to the direction in which the rows in this ResultSet * object will be processed. The initial value is determined by the * Statement object that produced this ResultSet object. * The fetch direction may be changed at any time. * * @param direction - an int specifying the suggested fetch direction; * one of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN */ @Override public void setFetchDirection(final int direction) throws SQLException { switch (direction) { case ResultSet.FETCH_FORWARD: break; case ResultSet.FETCH_REVERSE: case ResultSet.FETCH_UNKNOWN: throw new SQLException("Not supported direction: " + direction, "0A000"); default: throw new SQLException("Illegal direction: " + direction, "M1M05"); } } /** * Retrieves the fetch size for this ResultSet object. * * @return the current fetch size for this ResultSet object * @throws SQLException if a database access error occurs */ @Override public int getFetchSize() throws SQLException { return fetchSize; } /** * Gives the JDBC driver a hint as to the number of rows that should be * fetched from the database when more rows are needed. In MonetDB, this is * actually a no-op, because even before a MonetResultSet object is * created, the fetch size is already determined in the * MonetConnection.ResultSetResponse passed to its constructor. Since all * data blocks for this whole result set are already allocated in * MonetConnection.ResultSetResponse, it is too complicated and error-prone * to still change the fetchSize here. If one really needs to overwrite * the default fetchSize, please use MonetStatement.setFetchSize() instead. * * @param rows the number of rows to fetch * @throws SQLException if the condition 0 <= rows is not satisfied */ @Override public void setFetchSize(final int rows) throws SQLException { if (rows >= 0) { fetchSize = rows; } else { throw new SQLException("Illegal fetch size value: " + rows, "M1M05"); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a float in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if there is no such column or this method is called on a closed result set */ @Override public float getFloat(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return 0; } lastReadWasNull = false; return Float.parseFloat(val); } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a float in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public float getFloat(final String columnLabel) throws SQLException { return getFloat(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as an int in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if there is no such column or this method is called on a closed result set */ @Override public int getInt(final int columnIndex) throws SQLException { checkNotClosed(); String val = ""; try { val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return 0; } lastReadWasNull = false; return Integer.parseInt(val); } catch (NumberFormatException e) { // The oid datatype values (as string) have a @0 suffix in the string value. // To allow succesful parsing and conversion to int, we need to remove the suffix first if ("oid".equals(types[columnIndex - 1])) { if (val.endsWith("@0")) { try { return Integer.parseInt(val.substring(0, val.length()-2)); } catch (NumberFormatException nfe) { throw newSQLNumberFormatException(nfe); } } } throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as an int in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public int getInt(final String columnLabel) throws SQLException { return getInt(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a long in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if there is no such column or this method is called on a closed result set */ @Override public long getLong(final int columnIndex) throws SQLException { checkNotClosed(); String val = ""; try { val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return 0; } lastReadWasNull = false; return Long.parseLong(val); } catch (NumberFormatException e) { // The oid datatype values (as string) have a @0 suffix in the string value. // To allow succesful parsing and conversion to long, we need to remove the suffix first if ("oid".equals(types[columnIndex - 1])) { if (val.endsWith("@0")) { try { return Long.parseLong(val.substring(0, val.length()-2)); } catch (NumberFormatException nfe) { throw newSQLNumberFormatException(nfe); } } } throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a long in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public long getLong(final String columnLabel) throws SQLException { return getLong(findColumn(columnLabel)); } /** * Retrieves the number, types and properties of this ResultSet object's * columns. * * @return the description of this ResultSet object's columns * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public ResultSetMetaData getMetaData() throws SQLException { if (rsmd == null) { // first use, construct it once and reuse it for all next calls if (header != null) { rsmd = new MonetResultSetMetaData((MonetConnection) getStatement().getConnection(), header); } else { // this will be a MonetVirtualResultSet object, see MonetStatement.getGeneratedKeys() // create arrays for storing the result columns meta data to pass to the constructor final int array_size = 1; final String[] schemas = new String[array_size]; final String[] tables = new String[array_size]; final String[] columns = new String[array_size]; final String[] types = new String[array_size]; final int[] jdbcTypes = new int[array_size]; final int[] lengths = new int[array_size]; final int[] precisions = new int[array_size]; final int[] scales = new int[array_size]; // fill the arrays with the getGeneratedKeys() resultset columns metadata. It only has 1 column. schemas[0] = null; tables[0] = null; columns[0] = "GENERATED_KEY"; types[0] = "bigint"; jdbcTypes[0] = Types.BIGINT; lengths[0] = 20; precisions[0] = 19; scales[0] = 0; rsmd = new MonetResultSetMetaData((MonetConnection) getStatement().getConnection(), 1, schemas, tables, columns, types, jdbcTypes, lengths, precisions, scales); } } return rsmd; } /** * Gets the value of the designated column in the current row of this * ResultSet object as an Object in the Java programming language. * * This method will return the value of the given column as a Java object. * The type of the Java object will be the default Java object type * corresponding to the column's SQL type, following the mapping for * built-in types specified in the JDBC specification. If the value is * an SQL NULL, the driver returns a Java null. * * This method may also be used to read database-specific abstract data * types. In the JDBC 2.0 API, the behavior of method getObject is extended * to materialize data of SQL user-defined types. When a column contains a * structured or distinct value, the behavior of this method is as if it * were a call to: getObject(columnIndex, this.getStatement().getConnection().getTypeMap()). * * @param columnIndex the first column is 1, the second is 2, ... * @return a java.lang.Object holding the column value or null * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public Object getObject(final int columnIndex) throws SQLException { // Many generic JDBC programs use getObject(colnr) to retrieve value objects from a resultset // For speed the implementation should be as fast as possible, so avoid method calls (by inlining code) where possible checkNotClosed(); final int JdbcType; final String val; try { val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; JdbcType = JdbcSQLTypes[columnIndex - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } switch (JdbcType) { case Types.TINYINT: case Types.SMALLINT: try { return Short.valueOf(val); } catch (NumberFormatException e) { return val; } case Types.INTEGER: try { return Integer.valueOf(val); } catch (NumberFormatException e) { return val; } case Types.BIGINT: try { return Long.valueOf(val); } catch (NumberFormatException e) { return val; } case Types.DOUBLE: case Types.FLOAT: try { return Double.valueOf(val); } catch (NumberFormatException e) { return val; } case Types.REAL: try { return Float.valueOf(val); } catch (NumberFormatException e) { return val; } case Types.DECIMAL: case Types.NUMERIC: try { return new BigDecimal(val); } catch (NumberFormatException e) { return val; } case Types.BOOLEAN: return Boolean.valueOf(val); case Types.VARCHAR: { // The MonetDB types: inet, json, url, uuid and xml are all mapped to Types.VARCHAR in MonetDriver.typeMap // For these MonetDB types (except json and xml, see comments below) we try to create objects of the corresponding class. final String MonetDBType = types[columnIndex - 1]; switch (MonetDBType.length()) { case 3: if ("url".equals(MonetDBType)) { try { final org.monetdb.jdbc.types.URL url_obj = new org.monetdb.jdbc.types.URL(); url_obj.fromString(val); return url_obj; } catch (Exception exc) { // ignore exception and just return the val String object return val; } } break; case 4: if ("inet".equals(MonetDBType)) { try { final org.monetdb.jdbc.types.INET inet_obj = new org.monetdb.jdbc.types.INET(); inet_obj.fromString(val); return inet_obj; } catch (Exception exc) { // ignore exception and just return the val String object return val; } } else if ("uuid".equals(MonetDBType)) { try { return java.util.UUID.fromString(val); } catch (IllegalArgumentException exc) { // ignore exception and just return the val String object return val; } // } else // if ("json".equals(MonetDBType)) { // There is no support for JSON in standard java class libraries. // Possibly we could use org.json.simple.JSONObject or other/faster libs // javax.json.Json is not released yet (see https://json-processing-spec.java.net/) // see also https://github.com/fabienrenaud/java-json-benchmark // Note that it would make our JDBC driver dependent of an external jar // and we don't want that so simply return it as String object // return val; } break; } return val; } case Types.CHAR: /* case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR */ return val; case Types.CLOB: return new MonetClob(val); case Types.BLOB: return new MonetBlob(val); case Types.DATE: return getDate(columnIndex, null); case Types.TIME: return getTime(columnIndex, null); case Types.TIME_WITH_TIMEZONE: return getOffsetTime(columnIndex); case Types.TIMESTAMP: return getTimestamp(columnIndex, null); case Types.TIMESTAMP_WITH_TIMEZONE: return getOffsetDateTime(columnIndex); case Types.BINARY: case Types.VARBINARY: /* case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY */ return getBytes(columnIndex); case Types.OTHER: default: // When we get here the column type is a non-standard JDBC SQL type, possibly a User Defined Type. // Just call getObject(int, Map) for those rare cases. return getObject(columnIndex, this.getStatement().getConnection().getTypeMap()); } } private final boolean classImplementsSQLData(final Class<?> cl) { final Class<?>[] cls = cl.getInterfaces(); for (int i = 0; i < cls.length; i++) { if (cls[i] == SQLData.class) return true; } return false; } /** * Gets the value of the designated column in the current row of this * ResultSet object as an Object in the Java programming language. * * This method will return the value of the given column as a Java object. * The type of the Java object will be the default Java object type corresponding * to the column's SQL type, following the mapping for built-in types specified * in the JDBC specification. * If the value is an SQL NULL, the driver returns a Java null. * * This method may also be used to read database-specific abstract data types. * In the JDBC 2.0 API, the behavior of method getObject is extended to * materialize data of SQL user-defined types. * * If Connection.getTypeMap does not throw a SQLFeatureNotSupportedException, then * when a column contains a structured or distinct value, the behavior of this * method is as if it were a call to: getObject(columnIndex, * this.getStatement().getConnection().getTypeMap()). * If Connection.getTypeMap does throw a SQLFeatureNotSupportedException, then * structured values are not supported, and distinct values are mapped to the * default Java class as determined by the underlying SQL type of the DISTINCT type. * * @param columnIndex the first column is 1, the second is 2, ... * @param map a java.util.Map object that contains the mapping from SQL * type names to classes in the Java programming language * @return an Object in the Java programming language representing the SQL value * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override @SuppressWarnings("unchecked") public Object getObject(final int columnIndex, final Map<String,Class<?>> map) throws SQLException { checkNotClosed(); final String val; final String MonetDBtype; try { val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; MonetDBtype = types[columnIndex - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } Class<?> type = null; if (map != null && map.containsKey(MonetDBtype)) { type = map.get(MonetDBtype); } if (type == null) { // fallback to the standard SQL type Class mappings type = MonetDriver.getClassForType(JdbcSQLTypes[columnIndex - 1]); } if (type == null || type == String.class) { return val; } if (type == BigDecimal.class) { return getBigDecimal(columnIndex); } if (type == Boolean.class) { return Boolean.valueOf(getBoolean(columnIndex)); } if (type == Integer.class) { return Integer.valueOf(getInt(columnIndex)); } if (type == Long.class) { return Long.valueOf(getLong(columnIndex)); } if (type == Short.class) { return Short.valueOf(getShort(columnIndex)); } if (type == Double.class) { return Double.valueOf(getDouble(columnIndex)); } if (type == Float.class) { return Float.valueOf(getFloat(columnIndex)); } if (type == Date.class) { return getDate(columnIndex, null); } if (type == Time.class) { return getTime(columnIndex, null); } if (type == Timestamp.class) { return getTimestamp(columnIndex, null); } if (type == OffsetTime.class) { return getOffsetTime(columnIndex); } if (type == OffsetDateTime.class) { return getOffsetDateTime(columnIndex); } if (type == Clob.class) { return getClob(columnIndex); } if (type == Blob.class) { return getBlob(columnIndex); } if (type == byte[].class) { return getBytes(columnIndex); } if (classImplementsSQLData(type)) { final SQLData x; try { final java.lang.reflect.Constructor<? extends SQLData> ctor = ((Class)type).getConstructor(); x = ctor.newInstance(); } catch (NoSuchMethodException nsme) { throw new SQLException(nsme.getMessage(), "M0M27"); } catch (InstantiationException ie) { throw new SQLException(ie.getMessage(), "M0M27"); } catch (IllegalAccessException iae) { throw new SQLException(iae.getMessage(), "M0M27"); } catch (java.lang.reflect.InvocationTargetException ite) { throw new SQLException(ite.getMessage(), "M0M27"); } catch (SecurityException se) { throw new SQLException(se.getMessage(), "M0M27"); } final int colnum = columnIndex; final boolean valwasnull = wasNull(); final SQLInput input = new SQLInput() { @Override public String readString() throws SQLException { return getString(colnum); } @Override public boolean readBoolean() throws SQLException { return getBoolean(colnum); } @Override public byte readByte() throws SQLException { return getByte(colnum); } @Override public short readShort() throws SQLException { return getShort(colnum); } @Override public int readInt() throws SQLException { return getInt(colnum); } @Override public long readLong() throws SQLException { return getLong(colnum); } @Override public float readFloat() throws SQLException { return getFloat(colnum); } @Override public double readDouble() throws SQLException { return getDouble(colnum); } @Override public BigDecimal readBigDecimal() throws SQLException { return getBigDecimal(colnum); } @Override public byte[] readBytes() throws SQLException { return getBytes(colnum); } @Override public Date readDate() throws SQLException { return getDate(colnum, null); } @Override public Time readTime() throws SQLException { return getTime(colnum, null); } @Override public Timestamp readTimestamp() throws SQLException { return getTimestamp(colnum, null); } @Override public Reader readCharacterStream() throws SQLException { return getCharacterStream(colnum); } @Override public InputStream readAsciiStream() throws SQLException { return getAsciiStream(colnum); } @Override public InputStream readBinaryStream() throws SQLException { return getBinaryStream(colnum); } @Override public Object readObject() throws SQLException { return getObject(colnum); } @Override public Ref readRef() throws SQLException { return getRef(colnum); } @Override public Blob readBlob() throws SQLException { return getBlob(colnum); } @Override public Clob readClob() throws SQLException { return getClob(colnum); } @Override public Array readArray() throws SQLException { return getArray(colnum); } @Override public boolean wasNull() throws SQLException { return valwasnull; } @Override public URL readURL() throws SQLException { return getURL(colnum); } @Override public NClob readNClob() throws SQLException { return getNClob(colnum); } @Override public String readNString() throws SQLException { return getNString(colnum); } @Override public SQLXML readSQLXML() throws SQLException { return getSQLXML(colnum); } @Override public RowId readRowId() throws SQLException { return getRowId(colnum); } }; x.readSQL(input, MonetDBtype); return x; } return val; } /** * Gets the value of the designated column in the current row of this * ResultSet object as an Object in the Java programming language. * * This method will return the value of the given column as a Java object. * The type of the Java object will be the default Java object type * corresponding to the column's SQL type, following the mapping for * built-in types specified in the JDBC specification. If the value is an * SQL NULL, the driver returns a Java null. * * This method may also be used to read database-specific abstract data * types. * * @param columnLabel the SQL name of the column * @return a java.lang.Object holding the column value * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public Object getObject(final String columnLabel) throws SQLException { return getObject(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as an Object in the Java programming language. If the * value is an SQL NULL, the driver returns a Java null. This method uses * the specified Map object for custom mapping if appropriate. * * @param columnLabel the name of the column from which to retrieve the value * @param map a java.util.Map object that contains the mapping from SQL * type names to classes in the Java programming language * @return an Object representing the SQL value in the specified column * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public Object getObject(final String columnLabel, final Map<String,Class<?>> map) throws SQLException { return getObject(findColumn(columnLabel), map); } @Override public Ref getRef(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getRef"); } @Override public Ref getRef(final String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getRef"); } /** * Retrieves the current row number. The first row is number 1, the second * number 2, and so on. * * @return the current row number; 0 if there is no current row */ @Override public int getRow() { return curRow; } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.sql.RowId object in the Java * programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned * is null * @throws SQLException if there is no such column * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override public RowId getRowId(final int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getRowId"); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.sql.RowId object in the Java * programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned * is null * @throws SQLException if the ResultSet object does not contain columnLabel * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override public RowId getRowId(final String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getRowId"); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a short in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if there is no such column or this method is called on a closed result set */ @Override public short getShort(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return 0; } lastReadWasNull = false; return Short.parseShort(val); } catch (NumberFormatException e) { throw newSQLNumberFormatException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a short in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is 0 * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public short getShort(final String columnLabel) throws SQLException { return getShort(findColumn(columnLabel)); } /** * Retrieves the Statement object that produced this ResultSet object. * If the result set was generated some other way, such as by a * DatabaseMetaData method, this method may return null. * * In our implementation we always return a non-null object, see constructors. * Also from subclass MonetVirtualResultSet, see its constructor. * * @return the Statement object that produced this ResultSet object or * null if the result set was produced some other way */ @Override public Statement getStatement() { return statement; } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a String in the Java programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if there is no such column or this method is called on a closed result set */ @Override public String getString(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; return val; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a String in the Java programming language. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public String getString(final String columnLabel) throws SQLException { return getString(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a String in the Java programming * language. It is intended for use when accessing NCHAR,NVARCHAR * and LONGNVARCHAR columns. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if there is no such column */ @Override public String getNString(final int columnIndex) throws SQLException { return getString(columnIndex); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a String in the Java programming * language. It is intended for use when accessing NCHAR,NVARCHAR * and LONGNVARCHAR columns. * * @param columnLabel the SQL name of the column * @return the column value; if the value is SQL NULL, the value returned is null * @throws SQLException if the ResultSet object does not contain columnLabel */ @Override public String getNString(final String columnLabel) throws SQLException { return getString(findColumn(columnLabel)); } /** * Retrieves the value of the designated column in the current row * of this ResultSet as a java.sql.SQLXML object in the Java * programming language. * * @param columnIndex the first column is 1, the second is 2, ... * @return a SQLXML object that maps an SQL XML value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override public SQLXML getSQLXML(final int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getSQLXML"); } /** * Retrieves the value of the designated column in the current row * of this ResultSet as a java.sql.SQLXML object in the Java * programming language. * * @param columnLabel the label for the column specified with the SQL AS * clause. If the SQL AS clause was not specified, then the * label is the name of the column * @return a SQLXML object that maps an SQL XML value * @throws SQLException if a database access error occurs * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override public SQLXML getSQLXML(final String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("getSQLXML"); } // This behaviour is according table B-6 of Sun JDBC Specification 3.0 private SimpleDateFormat dateFormat; private SimpleDateFormat timeFormat; private SimpleDateFormat timestampFormat; /** * Helper method which parses the date/time value for columns of type * TIME, DATE and TIMESTAMP. For the types CHAR, VARCHAR and * LONGVARCHAR an attempt is made to parse the date according to the * given type. The given Calender object is filled with the parsed * data. Optional fractional seconds (nanos) are returned by this * method. If the underlying type of the column is none of the * mentioned six, January 1st 1970 0:00:00 GMT is returned.<br /> * The dates are parsed with the given Calendar. * * @param cal the Calendar to use/fill when parsing the date/time * @param columnIndex the column to parse * @param type the corresponding java.sql.Types type of the calling function * @return the fractional seconds (nanos) or -1 if the value is NULL * @throws SQLException if a database error occurs */ private final int getJavaDate(final Calendar cal, final int columnIndex, int type) throws SQLException { checkNotClosed(); if (cal == null) throw new IllegalArgumentException("No Calendar object given!"); final String monetDateStr; final String monetDate; final String MonetDBType; int JdbcType; boolean negativeYear = false; try { monetDateStr = tlp.values[columnIndex - 1]; if (monetDateStr == null) { lastReadWasNull = true; return -1; } lastReadWasNull = false; MonetDBType = types[columnIndex - 1]; JdbcType = JdbcSQLTypes[columnIndex - 1]; // If we got a string type, set the JdbcType to the given type // so we attempt to parse it as the caller thinks it is. if (JdbcType == Types.CHAR || JdbcType == Types.VARCHAR || /* JdbcType == Types.LONGVARCHAR || // MonetDB doesn't use type LONGVARCHAR */ JdbcType == Types.CLOB) { JdbcType = type; } if ((JdbcType == Types.DATE || JdbcType == Types.TIMESTAMP || JdbcType == Types.TIMESTAMP_WITH_TIMEZONE) && monetDateStr.startsWith("-")) { // the SimpleDateFormat parsers do not support to parse negative year numbers, deal with it separately negativeYear = true; monetDate = monetDateStr.substring(1); } else { monetDate = monetDateStr; } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } TimeZone ptz = cal.getTimeZone(); // it is important to parse the time in the given timezone in // order to get a correct (UTC) time value, hence we need to // parse it first if (MonetDBType != null && ("timetz".equals(MonetDBType) || "timestamptz".equals(MonetDBType))) { int vallen = monetDate.length(); if (vallen >= 6) { // MonetDB/SQL99: Sign TwoDigitHours : Minutes ptz = TimeZone.getTimeZone("GMT" + monetDate.substring(vallen - 6, vallen)); } } java.util.Date pdate = null; final java.text.ParsePosition ppos = new java.text.ParsePosition(0); switch (JdbcType) { case Types.DATE: if (dateFormat == null) { // first time usage, create and keep the dateFormat object for next usage dateFormat = new SimpleDateFormat("yyyy-MM-dd"); } dateFormat.setTimeZone(ptz); pdate = dateFormat.parse(monetDate, ppos); break; case Types.TIME: case Types.TIME_WITH_TIMEZONE: if (timeFormat == null) { // first time usage, create and keep the timeFormat object for next usage timeFormat = new SimpleDateFormat("HH:mm:ss"); } timeFormat.setTimeZone(ptz); pdate = timeFormat.parse(monetDate, ppos); break; case Types.TIMESTAMP: case Types.TIMESTAMP_WITH_TIMEZONE: if (timestampFormat == null) { // first time usage, create and keep the timestampFormat object for next usage timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } timestampFormat.setTimeZone(ptz); pdate = timestampFormat.parse(monetDate, ppos); // if parsing with timestampFormat failed try to parse it in dateFormat if (pdate == null && monetDate.length() <= 10 && monetDate.contains("-")) { if (dateFormat == null) { // first time usage, create and keep the dateFormat object for next usage dateFormat = new SimpleDateFormat("yyyy-MM-dd"); } dateFormat.setTimeZone(ptz); pdate = dateFormat.parse(monetDate, ppos); } break; default: throw new SQLException("Internal error, unsupported data type: " + type, "01M03"); } if (pdate == null) { // parsing failed final StringBuilder errMsg = new StringBuilder(128); final int epos = ppos.getErrorIndex(); if (epos == -1) { errMsg.append("parsing '").append(monetDateStr).append("' failed"); } else if (epos < monetDate.length()) { errMsg.append("parsing failed at pos ").append(epos + (negativeYear ? 2 : 1)) .append(" found: '").append(monetDate.charAt(epos)) .append("' in '").append(monetDateStr).append("'"); } else { errMsg.append("parsing failed, expected more data after '").append(monetDateStr).append("'"); } throw new SQLException(errMsg.toString(), "01M10"); } cal.setTime(pdate); if (negativeYear) { // System.out.println("Current cal: " + cal.toString()); // using cal.set(Calendar.YEAR, -(cal.get(Calendar.YEAR))); does not work. We must set the ERA instead cal.set(Calendar.ERA, java.util.GregorianCalendar.BC); // System.out.println("Corrected cal: " + cal.toString()); } if (JdbcType == Types.TIME || JdbcType == Types.TIME_WITH_TIMEZONE || JdbcType == Types.TIMESTAMP || JdbcType == Types.TIMESTAMP_WITH_TIMEZONE) { // parse additional nanos (if any) int nanos = 0; int pos = ppos.getIndex(); final char[] monDate = monetDate.toCharArray(); if (pos < monDate.length && monDate[pos] == '.') { pos++; try { int ctr; nanos = getIntrinsicValue(monDate[pos], pos++); for (ctr = 1; pos < monDate.length && monDate[pos] >= '0' && monDate[pos] <= '9'; ctr++) { if (ctr < 9) { nanos *= 10; nanos += (getIntrinsicValue(monDate[pos], pos)); } if (ctr == 2) // we have three at this point cal.set(Calendar.MILLISECOND, nanos); pos++; } while (ctr++ < 9) nanos *= 10; } catch (MCLParseException e) { final int offset = e.getErrorOffset(); addWarning(e.getMessage() + " found: '" + monDate[offset] + "' in: \"" + monetDate + "\" at pos: " + offset, "01M10"); // default value nanos = 0; } } return nanos; } return 0; } /** * Small helper method that returns the intrinsic value of a char if * it represents a digit. If a non-digit character is encountered * an MCLParseException is thrown. * * @param c the char * @param pos the position * @return the intrinsic value of the char * @throws MCLParseException if c is not a digit */ private static final int getIntrinsicValue(final char c, final int pos) throws MCLParseException { // note: don't use Character.isDigit() here, because // we only want ISO-LATIN-1 digits if (c >= '0' && c <= '9') { return (int)c - (int)'0'; } else { throw new MCLParseException("Expected a digit", pos); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Date object in the Java programming * language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs * @see #getDate(int col, Calendar cal) */ @Override public Date getDate(final int columnIndex) throws SQLException { return getDate(columnIndex, null); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Date object in the Java programming * language. This method uses the given calendar to construct an appropriate * millisecond value for the date if the underlying database does not store * timezone information. * * @param columnIndex the first column is 1, the second is 2, ... * @param cal the java.util.Calendar object to use in constructing the date * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Date getDate(final int columnIndex, Calendar cal) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; if (cal == null) { // try to convert string directly to a Date object // Note: the string must be in JDBC date escape format: yyyy-[m]m-[d]d try { return Date.valueOf(val); } catch (IllegalArgumentException iae) { // this happens if string doesn't match the format, such as for years < 1000 (including negative years) // in those cases just continue and use slower getJavaDate(cal, columnIndex, Types.DATE) method } cal = Calendar.getInstance(); } if (getJavaDate(cal, columnIndex, Types.DATE) == -1) return null; return new Date(cal.getTimeInMillis()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Date object in the Java programming * language. * * @param columnLabel the SQL name of the column from which to retrieve the value * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Date getDate(final String columnLabel) throws SQLException { return getDate(findColumn(columnLabel), null); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Date object in the Java programming * language. This method uses the given calendar to construct an appropriate * millisecond value for the date if the underlying database does not store * timezone information. * * @param columnLabel the SQL name of the column from which to retrieve the value * @param cal the java.util.Calendar object to use in constructing the date * @return the column value as a java.sql.Date object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Date getDate(final String columnLabel, final Calendar cal) throws SQLException { return getDate(findColumn(columnLabel), cal); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Time object in the Java programming * language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Time getTime(final int columnIndex) throws SQLException { return getTime(columnIndex, null); } /** * Retrieves the value of the designated column in the current row of * this ResultSet object as a java.sql.Time object in the Java programming * language. This method uses the given calendar to construct an appropriate * millisecond value for the time if the underlying database does not store * timezone information. * * @param columnIndex the first column is 1, the second is 2, ... * @param cal the java.util.Calendar object to use in constructing the timestamp * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Time getTime(final int columnIndex, Calendar cal) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; if (cal == null) { // try to convert string directly to a Time object // Note: the string must be in JDBC time escape format: hh:mm:ss try { return Time.valueOf(val); } catch (IllegalArgumentException iae) { // this happens if string doesn't match the format or hh >= 24 or mm >= 60 or ss >= 60 // in those cases just continue and use slower getJavaDate(cal, columnIndex, Types.TIME) method } cal = Calendar.getInstance(); } if (getJavaDate(cal, columnIndex, Types.TIME) == -1) return null; return new Time(cal.getTimeInMillis()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Time object in the Java programming * language. * * @param columnLabel the SQL name of the column * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Time getTime(final String columnLabel) throws SQLException { return getTime(findColumn(columnLabel), null); } /** * Retrieves the value of the designated column in the current row of * this ResultSet object as a java.sql.Time object in the Java programming * language. This method uses the given calendar to construct an appropriate * millisecond value for the time if the underlying database does not store * timezone information. * * @param columnLabel the SQL name of the column * @param cal the java.util.Calendar object to use in constructing the timestamp * @return the column value as a java.sql.Time object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Time getTime(final String columnLabel, final Calendar cal) throws SQLException { return getTime(findColumn(columnLabel), cal); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Timestamp object in the Java programming * language. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Timestamp getTimestamp(final int columnIndex) throws SQLException { return getTimestamp(columnIndex, null); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Timestamp object in the Java programming * language. This method uses the given calendar to construct an appropriate * millisecond value for the timestamp if the underlying database does not * store timezone information. * * @param columnIndex the first column is 1, the second is 2, ... * @param cal the java.util.Calendar object to use in constructing the timestamp * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Timestamp getTimestamp(final int columnIndex, Calendar cal) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; if (cal == null) { // try to convert the string directly to a Timestamp object // Note: the string must be in JDBC timestamp escape format: yyyy-[m]m-[d]d hh:mm:ss[.f...] try { return Timestamp.valueOf(val); } catch (IllegalArgumentException iae) { // this happens if string doesn't match the format, such as for years < 1000 (including negative years) // in those cases just continue and use slower getJavaDate(cal, columnIndex, Types.TIMESTAMP) method } cal = Calendar.getInstance(); } final int nanos = getJavaDate(cal, columnIndex, Types.TIMESTAMP); if (nanos == -1) return null; final Timestamp ts = new Timestamp(cal.getTimeInMillis()); ts.setNanos(nanos); return ts; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Timestamp object in the Java programming * language. * * @param columnLabel the SQL name of the column * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Timestamp getTimestamp(final String columnLabel) throws SQLException { return getTimestamp(findColumn(columnLabel), null); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.sql.Timestamp object in the Java programming * language. This method uses the given calendar to construct an appropriate * millisecond value for the timestamp if the underlying database does not * store timezone information. * * @param columnLabel the SQL name of the column * @param cal the java.util.Calendar object to use in constructing the timestamp * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Timestamp getTimestamp(final String columnLabel, final Calendar cal) throws SQLException { return getTimestamp(findColumn(columnLabel), cal); } /** * Retrieves the type of this ResultSet object. The type is determined by * the Statement object that created the result set. * * @return ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, * or ResultSet.TYPE_SCROLL_SENSITIVE */ @Override public int getType() { return type; } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.net.URL object in the Java * programming language. * * @param columnIndex the index of the column 1 is the first, 2 is the second,... * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs, or if a URL is malformed */ @Override public URL getURL(final int columnIndex) throws SQLException { checkNotClosed(); try { final String val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; try { // Note: as of Java version 20 java.net.URL(String) constructor is deprecated. // https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/net/URL.html#%3Cinit%3E(java.lang.String) return new java.net.URI(val).toURL(); } catch (java.net.URISyntaxException | java.net.MalformedURLException e) { throw new SQLException(e.getMessage(), "22M30"); } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * Retrieves the value of the designated column in the current row * of this ResultSet object as a java.net.URL object in the Java * programming language. * * @param columnLabel the SQL name of the column * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs, or if a URL is malformed */ @Override public URL getURL(final String columnLabel) throws SQLException { return getURL(findColumn(columnLabel)); } /** * Retrieves the first warning reported by calls on this ResultSet object. * If there is more than one warning, subsequent warnings will be chained to * the first one and can be retrieved by calling the method * SQLWarning.getNextWarning on the warning that was retrieved previously. * * This method may not be called on a closed result set; doing so will cause * an SQLException to be thrown. * * Note: Subsequent warnings will be chained to this SQLWarning. * * @return the first SQLWarning object or null if there are none * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public SQLWarning getWarnings() throws SQLException { checkNotClosed(); // if there are no warnings, this will be null, which fits with the // specification. return warnings; } /** * Retrieves whether the cursor is after the last row in this ResultSet * object. * * @return true if the cursor is after the last row; false if the cursor is * at any other position or the result set contains no rows * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public boolean isAfterLast() throws SQLException { checkNotClosed(); return curRow == tupleCount + 1; } /** * Retrieves whether the cursor is before the first row in this ResultSet * object. * * @return true if the cursor is before the first row; false if the cursor * is at any other position or the result set contains no rows * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public boolean isBeforeFirst() throws SQLException { checkNotClosed(); return curRow == 0; } /** * Retrieves whether this ResultSet object has been closed. A * ResultSet is closed if the method close has been called on it, or * if it is automatically closed. * * @return true if this ResultSet object is closed; false if it is still open * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public boolean isClosed() throws SQLException { return header != null && header.isClosed(); } /** * Retrieves whether the cursor is on the first row of this ResultSet * object. * * @return true if the cursor is on the first row; false otherwise * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public boolean isFirst() throws SQLException { checkNotClosed(); return curRow == 1; } /** * Retrieves whether the cursor is on the last row of this ResultSet object. * * @return true if the cursor is on the last row; false otherwise * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public boolean isLast() throws SQLException { checkNotClosed(); return curRow == tupleCount; } /** * Moves the cursor to the last row in this ResultSet object. * * @return true if the cursor is on a valid row; false if there are no rows * in the result set * @throws SQLException if a database access error occurs or the result set * type is TYPE_FORWARD_ONLY * @throws SQLException if a database access error occurs or this method is called on a closed result set */ @Override public boolean last() throws SQLException { return absolute(-1); } /** * Moves the cursor down one row from its current position. A ResultSet * cursor is initially positioned before the first row; the first call to * the method next makes the first row the current row; the second call * makes the second row the current row, and so on. * * If an input stream is open for the current row, a call to the method * next will implicitly close it. A ResultSet object's warning chain is * cleared when a new row is read. * * @return true if the new current row is valid; false if there are no * more rows * @throws SQLException if a database access error occurs or ResultSet is * closed */ @Override public boolean next() throws SQLException { return relative(1); } /** * Moves the cursor to the previous row in this ResultSet object. * * @return true if the cursor is on a valid row; false if it is off * the result set * @throws SQLException if a database access error occurs or ResultSet is * closed or the result set type is TYPE_FORWARD_ONLY */ @Override public boolean previous() throws SQLException { return relative(-1); } /** * Moves the cursor a relative number of rows, either positive or negative. * Attempting to move beyond the first/last row in the result set positions * the cursor before/after the the first/last row. Calling relative(0) is * valid, but does not change the cursor position. * * Note: Calling the method relative(1) is identical to calling the method * next() and calling the method relative(-1) is identical to calling the * method previous(). * * @param rows an int specifying the number of rows to move from the current * row; a positive number moves the cursor forward; a negative number * moves the cursor backward * @return true if the cursor is on a row; false otherwise * @throws SQLException if a database access error occurs, there is no current * row, or the result set type is TYPE_FORWARD_ONLY */ @Override public boolean relative(final int rows) throws SQLException { return absolute(curRow + rows); } /** * Retrieves whether a row has been deleted. A deleted row may leave a visible "hole" in a result set. * This method can be used to detect holes in a result set. * The value returned depends on whether or not this ResultSet object can detect deletions. * * Note: Support for the rowDeleted method is optional with a result set concurrency of CONCUR_READ_ONLY * * Returns: true if the current row is detected to have been deleted by the owner or another; false otherwise * * Throws: * SQLException - if a database access error occurs or this method is called on a closed result set * Since: 1.2 * See Also: DatabaseMetaData.deletesAreDetected(int) */ @Override public boolean rowDeleted() throws SQLException { checkNotClosed(); return false; } /** * Retrieves whether the current row has had an insertion. * The value returned depends on whether or not this ResultSet object can detect visible inserts. * * Note: Support for the rowInserted method is optional with a result set concurrency of CONCUR_READ_ONLY * * Returns: true if the current row is detected to have been inserted; false otherwise * * Throws: * SQLException - if a database access error occurs or this method is called on a closed result set * Since: 1.2 * See Also: DatabaseMetaData.insertsAreDetected(int) */ @Override public boolean rowInserted() throws SQLException { checkNotClosed(); return false; } /** * Retrieves whether the current row has been updated. * The value returned depends on whether or not the result set can detect updates. * * Note: Support for the rowUpdated method is optional with a result set concurrency of CONCUR_READ_ONLY * * Returns: true if the current row is detected to have been visibly updated by the owner or another; false otherwise * * Throws: * SQLException - if a database access error occurs or this method is called on a closed result set * Since: 1.2 * See Also: DatabaseMetaData.updatesAreDetected(int) */ @Override public boolean rowUpdated() throws SQLException { checkNotClosed(); return false; } /* Next methods are all related to updateable result sets, which we do not support. * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method */ @Override public void cancelRowUpdates() throws SQLException { throw newSQLFeatureNotSupportedException("cancelRowUpdates"); } @Override public void deleteRow() throws SQLException { throw newSQLFeatureNotSupportedException("deleteRow"); } @Override public void insertRow() throws SQLException { throw newSQLFeatureNotSupportedException("insertRow"); } @Override public void moveToCurrentRow() throws SQLException { throw newSQLFeatureNotSupportedException("moveToCurrentRow"); } @Override public void moveToInsertRow() throws SQLException { throw newSQLFeatureNotSupportedException("moveToInsertRow"); } @Override public void refreshRow() throws SQLException { throw newSQLFeatureNotSupportedException("refreshRow"); } @Override public void updateArray(int columnIndex, Array x) throws SQLException { throw newSQLFeatureNotSupportedException("updateArray"); } @Override public void updateArray(String columnLabel, Array x) throws SQLException { throw newSQLFeatureNotSupportedException("updateArray"); } @Override public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBigDecimal"); } @Override public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBigDecimal"); } @Override public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBlob(int columnIndex, Blob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBlob(int columnIndex, InputStream s) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBlob(int columnIndex, InputStream s, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBlob(String columnLabel, Blob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBlob(String columnLabel, InputStream s) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBlob(String columnLabel, InputStream s, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBoolean(int columnIndex, boolean x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBoolean"); } @Override public void updateBoolean(String columnLabel, boolean x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBoolean"); } @Override public void updateByte(int columnIndex, byte x) throws SQLException { throw newSQLFeatureNotSupportedException("updateByte"); } @Override public void updateByte(String columnLabel, byte x) throws SQLException { throw newSQLFeatureNotSupportedException("updateByte"); } @Override public void updateBytes(int columnIndex, byte[] x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBytes"); } @Override public void updateBytes(String columnLabel, byte[] x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBytes"); } @Override public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNCharacterStream"); } @Override public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateNCharacterStream"); } @Override public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { throw newSQLFeatureNotSupportedException("updateNCharacterStream"); } @Override public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateNCharacterStream"); } @Override public void updateClob(int columnIndex, Clob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateClob(int columnIndex, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateClob(int columnIndex, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateClob(String columnLabel, Clob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateClob(String columnLabel, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateClob(String columnLabel, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateNClob(int columnIndex, NClob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateNClob(int columnIndex, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateNClob(int columnIndex, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateNClob(String columnLabel, NClob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateNClob(String columnLabel, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateNClob(String columnLabel, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateDate(int columnIndex, Date x) throws SQLException { throw newSQLFeatureNotSupportedException("updateDate"); } @Override public void updateDate(String columnLabel, Date x) throws SQLException { throw newSQLFeatureNotSupportedException("updateDate"); } @Override public void updateDouble(int columnIndex, double x) throws SQLException { throw newSQLFeatureNotSupportedException("updateDouble"); } @Override public void updateDouble(String columnLabel, double x) throws SQLException { throw newSQLFeatureNotSupportedException("updateDouble"); } @Override public void updateFloat(int columnIndex, float x) throws SQLException { throw newSQLFeatureNotSupportedException("updateFloat"); } @Override public void updateFloat(String columnLabel, float x) throws SQLException { throw newSQLFeatureNotSupportedException("updateFloat"); } @Override public void updateInt(int columnIndex, int x) throws SQLException { throw newSQLFeatureNotSupportedException("updateInt"); } @Override public void updateInt(String columnLabel, int x) throws SQLException { throw newSQLFeatureNotSupportedException("updateInt"); } @Override public void updateLong(int columnIndex, long x) throws SQLException { throw newSQLFeatureNotSupportedException("updateLong"); } @Override public void updateLong(String columnLabel, long x) throws SQLException { throw newSQLFeatureNotSupportedException("updateLong"); } @Override public void updateNull(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("updateNull"); } @Override public void updateNull(String columnLabel) throws SQLException { throw newSQLFeatureNotSupportedException("updateNull"); } @Override public void updateObject(int columnIndex, Object x) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateObject(int columnIndex, Object x, int scale) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateObject(String columnLabel, Object x) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateObject(String columnLabel, Object x, int scale) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateRef(int columnIndex, Ref x) throws SQLException { throw newSQLFeatureNotSupportedException("updateRef"); } @Override public void updateRef(String columnLabel, Ref x) throws SQLException { throw newSQLFeatureNotSupportedException("updateRef"); } @Override public void updateRow() throws SQLException { throw newSQLFeatureNotSupportedException("updateRow"); } @Override public void updateRowId(int columnIndex, RowId x) throws SQLException { throw newSQLFeatureNotSupportedException("updateRowId"); } @Override public void updateRowId(String columnLabel, RowId x) throws SQLException { throw newSQLFeatureNotSupportedException("updateRowId"); } @Override public void updateShort(int columnIndex, short x) throws SQLException { throw newSQLFeatureNotSupportedException("updateShort"); } @Override public void updateShort(String columnLabel, short x) throws SQLException { throw newSQLFeatureNotSupportedException("updateShort"); } @Override public void updateString(int columnIndex, String x) throws SQLException { throw newSQLFeatureNotSupportedException("updateString"); } @Override public void updateString(String columnLabel, String x) throws SQLException { throw newSQLFeatureNotSupportedException("updateString"); } @Override public void updateNString(int columnIndex, String x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNString"); } @Override public void updateNString(String columnLabel, String x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNString"); } @Override public void updateSQLXML(int columnIndex, SQLXML x) throws SQLException { throw newSQLFeatureNotSupportedException("updateSQLXML"); } @Override public void updateSQLXML(String columnLabel, SQLXML x) throws SQLException { throw newSQLFeatureNotSupportedException("updateSQLXML"); } @Override public void updateTime(int columnIndex, Time x) throws SQLException { throw newSQLFeatureNotSupportedException("updateTime"); } @Override public void updateTime(String columnLabel, Time x) throws SQLException { throw newSQLFeatureNotSupportedException("updateTime"); } @Override public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { throw newSQLFeatureNotSupportedException("updateTimestamp"); } @Override public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { throw newSQLFeatureNotSupportedException("updateTimestamp"); } // Chapter 14.2.3.3 Sun JDBC 3.0 Specification /** * Reports whether the last column read had a value of SQL NULL. Note that * you must first call one of the getter methods on a column to try to read * its value and then call the method wasNull to see if the value read was * SQL NULL. * * @return true if the last column value read was SQL NULL and false otherwise */ @Override public boolean wasNull() { return lastReadWasNull; } //== Java 1.7 methods (JDBC 4.1) /** * Retrieves the value of the designated column in the current row * of this ResultSet object and will convert from the SQL type of * the column to the requested Java data type, if the conversion is * supported. If the conversion is not supported or null is * specified for the type, a SQLException is thrown. * * At a minimum, an implementation must support the conversions defined * in Appendix B, Table B-3 and conversion of appropriate user defined * SQL types to a Java type which implements SQLData, or Struct. * Additional conversions may be supported and are vendor defined. * * @param columnIndex the first column is 1, the second is 2, ... * @param type Class representing the Java data type to convert the * designated column to * @return an instance of type holding the column value * @throws SQLException if conversion is not supported, type is * null or another error occurs. The getCause() method of * the exception may provide a more detailed exception, for * example, if a conversion error occurs */ @Override public <T> T getObject(final int columnIndex, final Class<T> type) throws SQLException { checkNotClosed(); if (type == null) throw new SQLException("type is null", "M1M05"); final String val; try { val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } if (type == String.class) { return type.cast(val); } if (type == Integer.class) { return type.cast(Integer.valueOf(getInt(columnIndex))); } if (type == Long.class) { return type.cast(Long.valueOf(getLong(columnIndex))); } if (type == Short.class) { return type.cast(Short.valueOf(getShort(columnIndex))); } if (type == Double.class) { return type.cast(Double.valueOf(getDouble(columnIndex))); } if (type == Float.class) { return type.cast(Float.valueOf(getFloat(columnIndex))); } if (type == BigDecimal.class) { return type.cast(getBigDecimal(columnIndex)); } if (type == Boolean.class) { return type.cast(Boolean.valueOf(getBoolean(columnIndex))); } if (type == byte[].class) { return type.cast(getBytes(columnIndex)); } if (type == Blob.class || type == MonetBlob.class) { return type.cast(getBlob(columnIndex)); } if (type == Clob.class || type == MonetClob.class) { return type.cast(getClob(columnIndex)); } if (type == URL.class) { return type.cast(getURL(columnIndex)); } if (type == Date.class) { return type.cast(getDate(columnIndex, null)); } if (type == Time.class) { return type.cast(getTime(columnIndex, null)); } if (type == Timestamp.class) { return type.cast(getTimestamp(columnIndex, null)); } if (type == OffsetTime.class) { return type.cast(getOffsetTime(columnIndex)); } if (type == OffsetDateTime.class) { return type.cast(getOffsetDateTime(columnIndex)); } if (type == java.util.Date.class) { final Timestamp timestamp = getTimestamp(columnIndex, null); return type.cast(new java.util.Date(timestamp.getTime())); } if (type == Calendar.class) { final Calendar cal = Calendar.getInstance(); getJavaDate(cal, columnIndex, Types.TIMESTAMP); return type.cast(cal); } if (type == java.util.UUID.class) { try { return type.cast(java.util.UUID.fromString(val)); } catch (IllegalArgumentException exc) { throw new SQLException("conversion to java.util.UUID object failed: " + exc.getMessage(), "M1M05"); } } if (type == java.net.InetAddress.class) { final int slash = val.indexOf('/'); try { return type.cast(java.net.InetAddress.getByName(slash < 0 ? val : val.substring(0, slash))); } catch (java.net.UnknownHostException exc) { throw new SQLException("conversion to java.net.InetAddress object failed: " + exc.getMessage(), "M1M05"); } } throw new SQLException("conversion to '" + type.getName() + "' is not supported", "M1M05"); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object and will convert from the SQL type of * the column to the requested Java data type, if the conversion is * supported. If the conversion is not supported or null is * specified for the type, a SQLException is thrown. * * @param columnLabel the label for the column specified with the * SQL AS clause. If the SQL AS clause was not specified, * then the label is the name of the column * @param type Class representing the Java data type to convert the * designated column to * @return an instance of type holding the column value * @throws SQLException if conversion is not supported, type is * null or another error occurs. The getCause() method of * the exception may provide a more detailed exception, for * example, if a conversion error occurs */ @Override public <T> T getObject(final String columnLabel, final Class<T> type) throws SQLException { return getObject(findColumn(columnLabel), type); } //== Java 1.8 methods (JDBC 4.2) @Override public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } //== end methods of interface ResultSet //== internal helper methods which do not belong to the JDBC interface /** * Adds a warning to the pile of warnings this ResultSet object has. If * there were no warnings (or clearWarnings was called) this warning will * be the first, otherwise this warning will get appended to the current * warning. * * @param reason the warning message * @param sqlstate the SQLState code (5 characters) */ private void addWarning(final String reason, final String sqlstate) { SQLWarning warng = new SQLWarning(reason, sqlstate); if (warnings == null) { warnings = warng; } else { warnings.setNextWarning(warng); } } /** * Local helper method to test whether the ResultSet object is closed * When closed it throws an SQLException * * @throws SQLException if this ResultSet is closed */ private void checkNotClosed() throws SQLException { if (isClosed()) throw new SQLException("ResultSet is closed", "M1M20"); } /** * Retrieves the value of the designated column in the current row * of this ResultSet object and will convert to OffsetTime. * If the conversion is not supported a SQLException is thrown. * * @param columnIndex the first column is 1, the second is 2, ... * @return OffsetTime object or null * @throws SQLException if conversion is not supported */ private OffsetTime getOffsetTime(final int columnIndex) throws SQLException { final String val; try { val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; return OffsetTime.parse(val, java.time.format.DateTimeFormatter.ISO_TIME); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } catch (java.time.format.DateTimeParseException e) { throw new SQLException("Failed to convert to OffsetTime: " + e.getMessage(), "22M36"); } } /** * Retrieves the value of the designated column in the current row * of this ResultSet object and will convert to OffsetDateTime. * If the conversion is not supported a SQLException is thrown. * * @param columnIndex the first column is 1, the second is 2, ... * @return OffsetDateTime object or null * @throws SQLException if conversion is not supported */ private OffsetDateTime getOffsetDateTime(final int columnIndex) throws SQLException { final String val; try { val = tlp.values[columnIndex - 1]; if (val == null) { lastReadWasNull = true; return null; } lastReadWasNull = false; // ISO_OFFSET_DATE_TIME format expects a 'T' instead of a space between date and time parts // replace the space between date and time parts with 'T' String val_new = val; final int space = val.indexOf(' ', 4); if (space > 4 && space < 16) { val_new = val.substring(0, space) + "T" + val.substring(space + 1); } // System.out.println("getOffsetDateTime() changed " + val + " into " + val_new); return OffsetDateTime.parse(val_new, java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } catch (java.time.format.DateTimeParseException e) { throw new SQLException("Failed to convert to OffsetDateTime: " + e.getMessage(), "22M37"); } } /** * Small helper method that formats the "Invalid Column Index number ..." message * and creates a new SQLDataException object whose SQLState is set * to "22010": invalid indicator parameter value. * * @param colIdx the column index number * @return a new created SQLDataException object with SQLState 22010 */ public static final SQLDataException newSQLInvalidColumnIndexException(final int colIdx) { return new SQLDataException("Invalid Column Index number: " + colIdx, "22010"); } /** * Small helper method that formats the "Could not convert value to a number" message * and creates a new SQLDataException object whose SQLState is set * to "22003": Numeric value out of range. * * @param error the NumberFormatException * @return a new created SQLDataException object with SQLState 22003 */ private static final SQLDataException newSQLNumberFormatException(final NumberFormatException error) { return new SQLDataException("Could not convert value to a number. " + error.getMessage(), "22003"); } }