Mercurial > hg > monetdb-java
view src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java @ 102:08bc9009d190 embedded
Merged with default
author | Pedro Ferreira <pedro.ferreira@monetdbsolutions.com> |
---|---|
date | Fri, 13 Jan 2017 18:16:30 +0100 (2017-01-13) |
parents | c0ce1ea5075f b9b35ca2eec2 |
children | a00241382675 |
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 - 2017 MonetDB B.V. */ package nl.cwi.monetdb.jdbc; import nl.cwi.monetdb.mcl.connection.helpers.GregorianCalendarParser; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.responses.DataBlockResponse; import nl.cwi.monetdb.mcl.responses.ResultSetResponse; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; 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.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLInput; 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.ParsePosition; import java.util.*; /** * A 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. * * @author Fabian Groffen, Martin van Dinther * @version 0.8 */ public class MonetResultSet extends MonetWrapper implements ResultSet { /** The current position of the cursor for this ResultSet object */ int curRow = 0; // a blank final is immutable once assigned in the constructor /** A Header to retrieve lines from */ private final 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 from the MonetDB types[] */ private final int[] JdbcSQLTypes; /** The number of rows in this ResultSet */ final int tupleCount; /** The parental Statement object */ private final Statement statement; /** The type of this ResultSet (forward or scrollable) */ private int type = TYPE_FORWARD_ONLY; /** The concurrency of this ResultSet (currently only read-only) */ private int concurrency = CONCUR_READ_ONLY; /** The warnings for this ResultSet object */ private SQLWarning warnings; /** whether the last read field (via some getXyz() method) was NULL */ private boolean lastReadWasNull = true; /** Just a dummy variable to keep store the fetchsize set. */ private int fetchSize; /** The current row's values */ DataBlockResponse currentBlock; /** * 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 SQLException is a protocol error occurs */ MonetResultSet(Statement statement, ResultSetResponse header) throws SQLException { if (statement == null) { throw new IllegalArgumentException("Statement may not be null!"); } if (header == null) { throw new IllegalArgumentException("ResultSetResponse may not be null!"); } this.statement = statement; this.header = header; this.type = header.getRSType(); this.concurrency = header.getRSConcur(); /* if we have a header object, the fetchSize used for this result set is the header's cacheSize */ this.fetchSize = header.getCacheSize(); // well there is only one supported concurrency, so we don't have to bother about that // throws SQLException on getters of Header, so we find out immediately if an error occurred for this query this.tupleCount = header.getTuplecount(); this.columns = header.getNames(); this.types = header.getTypes(); this.JdbcSQLTypes = header.getJdbcSQLTypes(); } /** * 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 communicating with monet failed */ MonetResultSet(Statement statement, String[] columns, String[] types, int[] JdbcSQLTypes, int results) throws IllegalArgumentException { if (statement == null) { throw new IllegalArgumentException("Statement may not be null!"); } if (columns == null || types == null) { throw new IllegalArgumentException("One of the given arguments is null!"); } if (columns.length != types.length) { throw new IllegalArgumentException("Given arguments are not the same size!"); } if (results < 0) { throw new IllegalArgumentException("Negative rowcount not allowed!"); } this.statement = statement; this.header = null; this.fetchSize = 0; this.tupleCount = results; this.columns = columns; this.types = types; this.JdbcSQLTypes = JdbcSQLTypes; } private boolean setLastNullValue(int columnIndex) { this.lastReadWasNull = currentBlock.checkValueIsNull(columnIndex); return this.lastReadWasNull; } //== 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 { if (row != curRow + 1 && type == TYPE_FORWARD_ONLY) throw new SQLException("(Absolute) positioning not allowed on forward only result sets!", "M1M05"); if (header.isClosed()) throw new SQLException("ResultSet is closed!", "M1M20"); // first calculate what the JDBC row is if (row < 0) { // calculate the negatives... row = 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 = tupleCount + 1; // after last } // store it this.curRow = row; this.currentBlock = header.getDataBlockCorrespondingToLine(row - 1); return this.curRow <= this.tupleCount; } /** * 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(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() { if (header != null && !header.isClosed()) { header.close(); } 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 columnName the name of the column * @return the column index of the given column name * @throws SQLException if the ResultSet object does not contain columnName */ @Override public int findColumn(String columnName) throws SQLException { if (columnName != null) { final int array_size = columns.length; for (int i = 0; i < array_size; i++) { if (columnName.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 (columnName.equalsIgnoreCase(columns[i])) return i + 1; } } throw new SQLException("No such column name: " + columnName, "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(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getArray"); } @Override public Array getArray(String colName) throws SQLException { throw newSQLFeatureNotSupportedException("getArray"); } @Override public InputStream getAsciiStream(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: return getClob(columnIndex).getAsciiStream(); case Types.CLOB: return getClob(columnIndex).getAsciiStream(); case Types.LONGVARBINARY: case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: return new ByteArrayInputStream(getBytes(columnIndex)); default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to ascii stream not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @Override public InputStream getAsciiStream(String columnName) throws SQLException { return getAsciiStream(findColumn(columnName)); } @Override @Deprecated public InputStream getUnicodeStream(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getUnicodeStream"); } @Override @Deprecated public InputStream getUnicodeStream(String columnName) 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. * <br/><br/> * 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(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: return getBlob(columnIndex).getBinaryStream(); case Types.LONGVARBINARY: return new ByteArrayInputStream(getBytes(columnIndex)); default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to binary stream not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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. * <br/><br/> * 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 columnName 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(String columnName) throws SQLException { return getBinaryStream(findColumn(columnName)); } /** * 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 */ @Override public Reader getCharacterStream(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } return new StringReader(currentBlock.getValueAsString(columnIndex - 1)); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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(String columnName) throws SQLException { return getCharacterStream(findColumn(columnName)); } /** * 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 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override public Reader getNCharacterStream(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("getNCharacterStream"); } /** * 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 columnName 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 * @throws SQLFeatureNotSupportedException the JDBC driver does * not support this method */ @Override public Reader getNCharacterStream(String columnName) throws SQLException { throw newSQLFeatureNotSupportedException("getNCharacterStream"); } /** * 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 */ @Override public Blob getBlob(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } return (MonetBlob) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 colName 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(String colName) throws SQLException { return getBlob(findColumn(colName)); } /** * 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 */ @Override public Clob getClob(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } return (MonetClob) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 colName 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(String colName) throws SQLException { return getClob(findColumn(colName)); } /** * 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 i 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(int i) 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 colName 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(String colName) 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 */ @Override public BigDecimal getBigDecimal(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } return (BigDecimal) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 */ @Override @Deprecated public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } BigDecimal val = (BigDecimal) currentBlock.getObjectValue(columnIndex - 1); val.setScale(scale); return val; } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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(String columnName) throws SQLException { return getBigDecimal(findColumn(columnName)); } /** * Retrieves the value of the designated column in the current row of this * ResultSet object as a java.math.BigDecimal with full precision. * * @param columnName 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(String columnName, int scale) throws SQLException { return getBigDecimal(findColumn(columnName), 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 there is no such column */ @Override public boolean getBoolean(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return false; // if the value is SQL NULL, the value returned is false } // match type specific values switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BOOLEAN: return currentBlock.getBooleanValue(columnIndex - 1); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: String val = currentBlock.getValueAsString(columnIndex - 1); if ("false".equalsIgnoreCase(val) || "0".equals(val)) return false; if ("true".equalsIgnoreCase(val) || "1".equals(val)) return true; throw newSQLInvalidColumnIndexException(columnIndex); case Types.TINYINT: return getByte(columnIndex) != 0; case Types.SMALLINT: return getShort(columnIndex) != 0; case Types.INTEGER: return getInt(columnIndex) != 0; case Types.BIGINT: return getLong(columnIndex) != 0L; case Types.REAL: return getFloat(columnIndex) != 0.0f; case Types.DOUBLE: return getDouble(columnIndex) != 0.0d; case Types.NUMERIC: BigInteger huge = (BigInteger) currentBlock.getValueAsObject(columnIndex - 1); return huge.compareTo(BigInteger.ZERO) != 0; case Types.DECIMAL: return getBigDecimal(columnIndex).compareTo(BigDecimal.ZERO) != 0; default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + " to boolean type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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 columnName */ @Override public boolean getBoolean(String columnName) throws SQLException { return getBoolean(findColumn(columnName)); } /** * 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 */ @Override public byte getByte(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return 0; } return currentBlock.getByteValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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(String columnName) throws SQLException { return getByte(findColumn(columnName)); } /** * 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 */ @Override public byte[] getBytes(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } // According to Table B-6, getBytes() only operates on BINARY types switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: return ((MonetBlob) currentBlock.getObjectValue(columnIndex - 1)).getBuffer(); case Types.LONGVARBINARY: // unpack the HEX (BLOB) notation to real bytes return (byte[]) currentBlock.getObjectValue(columnIndex - 1); default: throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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. * * @param columnName 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(String columnName) throws SQLException { return getBytes(findColumn(columnName)); } /** * 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.getId() : "") + ")", "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 */ @Override public double getDouble(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return 0.0d; } return currentBlock.getDoubleValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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 columnName */ @Override public double getDouble(String columnName) throws SQLException { return getDouble(findColumn(columnName)); } /** * 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. * <b>currently not implemented</b> * * @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. * <b>currently not implemented</b> * * @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(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(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 */ @Override public float getFloat(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return 0.0f; } return currentBlock.getFloatValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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 columnName */ @Override public float getFloat(String columnName) throws SQLException { return getFloat(findColumn(columnName)); } /** * 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 */ @Override public int getInt(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return 0; } return currentBlock.getIntValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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 columnName */ @Override public int getInt(String columnName) throws SQLException { return getInt(findColumn(columnName)); } /** * 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 */ @Override public long getLong(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return 0L; } return currentBlock.getLongValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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 columnName */ @Override public long getLong(String columnName) throws SQLException { return getLong(findColumn(columnName)); } /* helper for the anonymous class inside getMetaData */ private abstract class rsmdw extends MonetWrapper implements ResultSetMetaData {} /** * Retrieves the number, types and properties of this ResultSet object's columns. * * @return the description of this ResultSet object's columns */ @Override public ResultSetMetaData getMetaData() { // return inner class which implements the ResultSetMetaData interface return new rsmdw() { // for the more expensive methods (getPrecision(), getScale(), isNullable()), we provide a simple cache // caches to store precision, scale and isNullable values from getColumns() final int array_size = columns.length + 1; // add 1 as in JDBC columns start from 1 (array from 0). private boolean[] _is_fetched = new boolean[array_size]; private int[] _precision = new int[array_size]; private int[] _scale = new int[array_size]; private int[] _isNullable = new int[array_size]; private boolean[] _isAutoincrement = new boolean[array_size]; private Connection conn = null; private DatabaseMetaData dbmd = null; /** * A private method to fetch the precision, scale, isNullable and isAutoincrement value for a fully qualified column. * As md.getColumns() is an expensive method we call it only once per column * and cache the precision, scale, isNullable and isAutoincrement values in the above array chaches. * Also we only call md.getColumns() when we have a non empty schema name and table name and column name. */ private void fetchColumnInfo(int column) throws SQLException { if (column <= 0 || column > columns.length) throw newSQLInvalidColumnIndexException(column); _is_fetched[column] = true; _precision[column] = 0; _scale[column] = 0; _isNullable[column] = columnNullableUnknown; _isAutoincrement[column] = false; // we can only call dbmd.getColumns() when we have a specific schema name and table name and column name String schName = getSchemaName(column); if (schName != null && !schName.isEmpty()) { String tblName = getTableName(column); if (tblName != null && !tblName.isEmpty()) { String colName = getColumnName(column); if (colName != null && !colName.isEmpty()) { if (conn == null) { // first time, get a Connection object and cache it for all next columns conn = getStatement().getConnection(); } if (conn != null && dbmd == null) { // first time, get a MetaData object and cache it for all next columns dbmd = conn.getMetaData(); } if (dbmd != null) { // for precision, scale, isNullable and isAutoincrement we query the information from data dictionary ResultSet colInfo = dbmd.getColumns(null, schName, tblName, colName); if (colInfo != null) { // we expect exactly one row in the resultset if (colInfo.next()) { _precision[column] = colInfo.getInt(7); // col 7 is "COLUMN_SIZE" _scale[column] = colInfo.getInt(9); // col 9 is "DECIMAL_DIGITS" _isNullable[column] = colInfo.getInt(11); // col 11 is "NULLABLE" String strVal = colInfo.getString(23); // col 23 is "IS_AUTOINCREMENT" if (strVal != null && "YES".equals(strVal)) _isAutoincrement[column] = true; } colInfo.close(); // close the resultset to release resources } } } } } } /** * Returns the number of columns in this ResultSet object. * * @return the number of columns */ @Override public int getColumnCount() { return columns.length; } /** * Indicates whether the designated column is automatically numbered. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @throws SQLException if a database access error occurs */ @Override public boolean isAutoIncrement(int column) throws SQLException { try { if (!_is_fetched[column]) { fetchColumnInfo(column); } return _isAutoincrement[column]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } /** * Indicates whether a column's case matters. * * @param column the first column is 1, the second is 2, ... * @return true for all character string columns else false */ @Override public boolean isCaseSensitive(int column) throws SQLException { switch (getColumnType(column)) { case Types.CLOB: case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: return true; default: return false; } } /** * Indicates whether the designated column can be used in a * where clause. * It is unknown to me what kind ot columns they regard to, * as I think all columns are useable in a where clause. * Returning true for all here, for the time being. * Possible thought; maybe they want to know here if it's a * real column existing in a table or not... * * @param column the first column is 1, the second is 2, ... * @return true */ @Override public boolean isSearchable(int column) { return true; } /** * Indicates whether the designated column is a cash value. * From the MonetDB database perspective it is by definition * unknown whether the value is a currency, because there are * no currency datatypes such as MONEY. With this knowledge * we can always return false here. * * @param column the first column is 1, the second is 2, ... * @return false */ @Override public boolean isCurrency(int column) { return false; } /** * Indicates whether values in the designated column are signed numbers. * Within MonetDB all numeric types (except oid and ptr) are signed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise */ @Override public boolean isSigned(int column) throws SQLException { switch (getColumnType(column)) { case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.REAL: case Types.DOUBLE: case Types.BIGINT: case Types.NUMERIC: case Types.DECIMAL: return true; default: return false; } } /** * Indicates the designated column's normal maximum width in characters. * * @param column the first column is 1, the second is 2, ... * @return the normal maximum number of characters allowed as the width of the designated column * @throws SQLException if there is no such column */ @Override public int getColumnDisplaySize(int column) throws SQLException { int ret = 1; if (header != null) { try { ret = header.getColumnLengths()[column - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } return ret; } /** * Get the designated column's schema name. * * @param column the first column is 1, the second is 2, ... * @return schema name or "" if not applicable * @throws SQLException if a database access error occurs */ @Override public String getSchemaName(int column) throws SQLException { if (header != null) { // figure the name out try { String schema = header.getTableNames()[column - 1]; if (schema != null) { int dot = schema.indexOf('.'); return (dot >= 0) ? schema.substring(0, dot) : ""; } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } return ""; } /** * Gets the designated column's table name. * * @param column the first column is 1, the second is 2, ... * @return table name or "" if not applicable */ @Override public String getTableName(int column) throws SQLException { if (header != null) { // figure the name out try { String table = header.getTableNames()[column - 1]; if (table != null) { int dot = table.indexOf('.'); return (dot >= 0) ? table.substring(dot + 1) : table; } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } return ""; } /** * Get the designated column's number of decimal digits. This method is currently very expensive as it needs * to retrieve the information from the database using an SQL query. * * @param column the first column is 1, the second is 2, ... * @return precision * @throws SQLException if a database access error occurs */ @Override public int getPrecision(int column) throws SQLException { try { if (!_is_fetched[column]) { fetchColumnInfo(column); } if (_precision[column] == 0) { // apparently no precision could be fetched use columnDisplaySize() value for variable // length data types switch (getColumnType(column)) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.LONGVARBINARY: case Types.BLOB: case Types.NUMERIC: case Types.DECIMAL: _precision[column] = getColumnDisplaySize(column); break; case Types.TINYINT: _precision[column] = 3; break; case Types.SMALLINT: _precision[column] = 5; break; case Types.INTEGER: _precision[column] = 10; break; case Types.BIGINT: _precision[column] = 19; break; case Types.REAL: _precision[column] = 7; break; case Types.DOUBLE: _precision[column] = 15; break; case Types.BOOLEAN: _precision[column] = 5; break; case Types.DATE: _precision[column] = 10; break; case Types.TIME: _precision[column] = 8; break; case Types.TIMESTAMP: _precision[column] = 19; break; default: _precision[column] = 30; break; } } return _precision[column]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } /** * Gets the designated column's number of digits to right of * the decimal point. This method is currently very * expensive as it needs to retrieve the information from * the database using an SQL query. * * @param column the first column is 1, the second is 2, ... * @return scale * @throws SQLException if a database access error occurs */ @Override public int getScale(int column) throws SQLException { try { if (!_is_fetched[column]) { fetchColumnInfo(column); } return _scale[column]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } /** * Indicates the nullability of values in the designated * column. This method is currently very expensive as it * needs to retrieve the information from the database using * an SQL query. * * @param column the first column is 1, the second is 2, ... * @return the nullability status of the given column; one of columnNoNulls, columnNullable or * columnNullableUnknown * @throws SQLException if a database access error occurs */ @Override public int isNullable(int column) throws SQLException { try { if (!_is_fetched[column]) { fetchColumnInfo(column); } return _isNullable[column]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } /** * Gets the designated column's table's catalog name. * MonetDB does not support the catalog naming concept as in: catalog.schema.table naming scheme * * @param column the first column is 1, the second is 2, ... * @return the name of the catalog for the table in which the given * column appears or "" if not applicable */ @Override public String getCatalogName(int column) throws SQLException { return null; // MonetDB does NOT support catalogs } /** * Indicates whether the designated column is definitely not writable. MonetDB does not support * cursor updates, so nothing is writable. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise */ @Override public boolean isReadOnly(int column) { return true; } /** * Indicates whether it is possible for a write on the designated column to succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise */ @Override public boolean isWritable(int column) { return false; } /** * Indicates whether a write on the designated column will definitely succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise */ @Override public boolean isDefinitelyWritable(int column) { return false; } /** * Returns the fully-qualified name of the Java class whose * instances are manufactured if the method * ResultSet.getObject is called to retrieve a value from * the column. ResultSet.getObject may return a subclass of * the class returned by this method. * * @param column the first column is 1, the second is 2, ... * @return the fully-qualified name of the class in the Java * programming language that would be used by the method * ResultSet.getObject to retrieve the value in the * specified column. This is the class name used for custom * mapping. * @throws SQLException if there is no such column */ @Override public String getColumnClassName(int column) throws SQLException { if (conn == null) { // first time, get a Connection object and cache it for all next columns conn = getStatement().getConnection(); } try { String MonetDBType = types[column - 1]; Class<?> type = null; if (conn != null) { Map<String,Class<?>> map = conn.getTypeMap(); if (map != null && map.containsKey(MonetDBType)) { type = map.get(MonetDBType); } } if (type == null) { // fallback to the standard SQL type Class mappings type = getClassForType(JdbcSQLTypes[column - 1]); } if (type != null) { return type.getName(); } throw new SQLException("column type mapping null: " + MonetDBType, "M0M03"); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } /** * Gets the designated column's suggested title for use in printouts and displays. This is currently equal * to getColumnName(). * * @param column the first column is 1, the second is 2, ... * @return the suggested column title * @throws SQLException if there is no such column */ @Override public String getColumnLabel(int column) throws SQLException { return getColumnName(column); } /** * Gets the designated column's name. * * @param column the first column is 1, the second is 2, ... * @return the column name * @throws SQLException if there is no such column */ @Override public String getColumnName(int column) throws SQLException { try { return columns[column - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } /** * Retrieves the designated column's SQL type. * * @param column the first column is 1, the second is 2, ... * @return SQL type from java.sql.Types * @throws SQLException if there is no such column */ @Override public int getColumnType(int column) throws SQLException { try { return JdbcSQLTypes[column - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } /** * Retrieves the designated column's database-specific type name. * * @param column the first column is 1, the second is 2, ... * @return type name used by the database. If the column type is a user-defined type, then a * fully-qualified type name is returned. * @throws SQLException if there is no such column */ @Override public String getColumnTypeName(int column) throws SQLException { try { return types[column - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(column); } } }; // end of new rsmdw() } // end of getMetaData() /** * 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().getInternalConnection().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 */ @Override public Object getObject(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 final int JdbcType; try { if(setLastNullValue(columnIndex - 1)) { return null; } JdbcType = JdbcSQLTypes[columnIndex - 1]; switch(JdbcType) { case Types.BOOLEAN: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.BIGINT: case Types.REAL: case Types.DOUBLE: case Types.DECIMAL: case Types.NUMERIC: case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.LONGVARBINARY: case Types.BLOB: return currentBlock.getValueAsObject(columnIndex - 1); case Types.DATE: return getDate(columnIndex); case Types.TIME: return getTime(columnIndex); case Types.TIMESTAMP: return getTimestamp(columnIndex); case Types.OTHER: { // The MonetDB types: inet, json, url and uuid are all mapped to Types.OTHER in MonetDriver.typeMap // For these MonetDB types (except json, see comments below) we try to create objects of the corresponding class. String MonetDBType = types[columnIndex - 1]; String val = currentBlock.getValueAsString(columnIndex - 1); switch (MonetDBType.length()) { case 3: if ("url".equals(MonetDBType)) { try { return new URL(val); } catch (Exception exc) { // ignore exception and just return the val String object return val; } } break; case 4: if ("inet".equals(MonetDBType)) { try { return new MonetINET(val); } catch (Exception exc) { // ignore exception and just return the val String object return val; } } else if ("uuid".equals(MonetDBType)) { try { return UUID.fromString(val); } catch (IllegalArgumentException exc) { // ignore exception and just return the val String object return val; } // } else if ("json".equals(MonetDBType)) { //the same happens for geometry // 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 } break; } return val; } 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()); } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } private boolean classImplementsSQLData(Class<?> cl) { Class<?>[] cls = cl.getInterfaces(); for (Class<?> cl1 : cls) { if (cl1 == SQLData.class) return true; } return false; } @SuppressWarnings("unchecked") private Object getObjectFromClass(int columnIndex, Class<?> type) throws SQLException { if(setLastNullValue(columnIndex - 1)) { return null; } if (type == null) { // fallback to the standard Class mappings type = getClassForType(JdbcSQLTypes[columnIndex - 1]); } if (type == null || type == String.class) { return currentBlock.getValueAsString(columnIndex - 1); } else if (type == BigInteger.class) { return getObject(columnIndex); } else if (type == BigDecimal.class) { return getBigDecimal(columnIndex); } else if (type == Boolean.class) { return getBoolean(columnIndex); } else if (type == Byte.class) { return getByte(columnIndex); } else if (type == Short.class) { return getShort(columnIndex); } else if (type == Integer.class) { return getInt(columnIndex); } else if (type == Long.class) { return getLong(columnIndex); } else if (type == Float.class) { return getFloat(columnIndex); } else if (type == Double.class) { return getDouble(columnIndex); } else if (type == byte[].class) { return getBytes(columnIndex); } else if (type == Date.class) { return getDate(columnIndex); } else if (type == Time.class) { return getTime(columnIndex); } else if (type == Timestamp.class) { return getTimestamp(columnIndex); } else if (type == Clob.class) { return getClob(columnIndex); } else if (type == Blob.class) { return getBlob(columnIndex); } else if (classImplementsSQLData(type)) { SQLData x; try { Constructor<? extends SQLData> ctor = ((Class)type).getConstructor(); x = ctor.newInstance(); } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | SecurityException | IllegalAccessException nsme) { throw new SQLException(nsme.getMessage(), "M0M27"); } final int colnum = columnIndex; final boolean valwasnull = wasNull(); 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); } @Override public Time readTime() throws SQLException { return getTime(colnum); } @Override public Timestamp readTimestamp() throws SQLException { return getTimestamp(colnum); } @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, types[columnIndex - 1]); return x; } else { return currentBlock.getObjectValue(columnIndex - 1); } } /** * 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().getInternalConnection().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 */ @Override public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException { try { String MonetDBtype = types[columnIndex - 1]; Class<?> type = null; if (map != null && map.containsKey(MonetDBtype)) { type = map.get(MonetDBtype); } return getObjectFromClass(columnIndex, type); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * 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 @SuppressWarnings("unchecked") public <T> T getObject(int columnIndex, Class<T> type) throws SQLException { try { return (T) getObjectFromClass(columnIndex, type); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } /** * 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(String columnLabel, Class<T> type) throws SQLException { return getObject(findColumn(columnLabel), type); } /** * Helper method to support the getObject and ResultsetMetaData.getColumnClassName JDBC methods. * * @param type a value from java.sql.Types * @return a Class object from which an instance would be returned */ static Class<?> getClassForType(int type) { /** * This switch returns the types as objects according to table B-3 from Oracle's JDBC specification 4.1 */ switch(type) { // keep this switch regarding the returned classes aligned with getObject(int, Map) ! case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: return String.class; case Types.NUMERIC: return BigInteger.class; case Types.DECIMAL: return BigDecimal.class; case Types.BOOLEAN: return Boolean.class; case Types.TINYINT: return Byte.class; case Types.SMALLINT: return Short.class; case Types.INTEGER: return Integer.class; case Types.BIGINT: return Long.class; case Types.REAL: return Float.class; case Types.DOUBLE: return Double.class; case Types.LONGVARBINARY: return byte[].class; case Types.DATE: return Date.class; case Types.TIME: return Time.class; case Types.TIMESTAMP: return Timestamp.class; case Types.CLOB: return Clob.class; case Types.BLOB: return Blob.class; default: // all the rest are currently not implemented and used return String.class; } } /** * 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 columnName the SQL name of the column * @return a java.lang.Object holding the column value * @throws SQLException if a database access error occurs */ @Override public Object getObject(String columnName) throws SQLException { return getObject(findColumn(columnName)); } /** * 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 colName 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 */ @Override public Object getObject(String colName, Map<String,Class<?>> map) throws SQLException { return getObject(findColumn(colName), map); } @Override public Ref getRef(int i) throws SQLException { throw newSQLFeatureNotSupportedException("getRef"); } @Override public Ref getRef(String colName) 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(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 columnName 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 columnName * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override public RowId getRowId(String columnName) 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 */ @Override public short getShort(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return 0; } return currentBlock.getShortValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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 columnName */ @Override public short getShort(String columnName) throws SQLException { return getShort(findColumn(columnName)); } /** * 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 returns null. * * @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 */ @Override public String getString(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } return currentBlock.getValueAsString(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } 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 columnName 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 columnName */ @Override public String getString(String columnName) throws SQLException { return getString(findColumn(columnName)); } /** * 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 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override public String getNString(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 columnName 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 columnName * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method */ @Override public String getNString(String columnName) throws SQLException { return getNString(findColumn(columnName)); } /** * 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 i 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(int i) 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 colName 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(String colName) throws SQLException { throw newSQLFeatureNotSupportedException("getSQLXML"); } /** * 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; 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(int columnIndex) throws SQLException { return getDate(columnIndex, Calendar.getInstance()); } /** * 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; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } Calendar res; switch (JdbcSQLTypes[columnIndex - 1]) { case Types.CHAR : case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: String value = currentBlock.getValueAsString(columnIndex - 1); res = GregorianCalendarParser.ParseDate(value, new ParsePosition(0)); break; case Types.DATE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); break; default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to Date not supported", "M1M05"); } res.setTimeZone(cal.getTimeZone()); return new Date(res.getTimeInMillis()); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } catch (ProtocolException ep) { throw new SQLException(ep.getMessage(), "M1M05"); } } /** * 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 columnName the SQL name of the column from which to retrieve the value * @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 Date getDate(String columnName) throws SQLException { return getDate(findColumn(columnName), Calendar.getInstance()); } /** * 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 columnName 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; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Date getDate(String columnName, Calendar cal) throws SQLException { return getDate(findColumn(columnName), 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; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Time getTime(int columnIndex) throws SQLException { return getTime(columnIndex, Calendar.getInstance()); } /** * 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.Timestamp object; 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 Time getTime(int columnIndex, Calendar cal) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } Calendar res; switch (JdbcSQLTypes[columnIndex - 1]) { case Types.CHAR : case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: String value = currentBlock.getValueAsString(columnIndex - 1); res = GregorianCalendarParser.ParseTime(value, new ParsePosition(0), false); res.setTimeZone(cal.getTimeZone()); break; case Types.TIME: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); res.setTimeZone(cal.getTimeZone()); break; case Types.TIME_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); break; default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to Time not supported", "M1M05"); } return new Time(res.getTimeInMillis()); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } catch (ProtocolException ep) { throw new SQLException(ep.getMessage(), "M1M05"); } } /** * 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 columnName 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 Time getTime(String columnName) throws SQLException { return getTime(findColumn(columnName), Calendar.getInstance()); } /** * 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 columnName 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 * in the Java programming language * @throws SQLException if a database access error occurs */ @Override public Time getTime(String columnName, Calendar cal) throws SQLException { return getTime(findColumn(columnName), 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; if the value is SQL NULL, the value returned is null * @throws SQLException if a database access error occurs */ @Override public Timestamp getTimestamp(int columnIndex) throws SQLException { return getTimestamp(columnIndex, Calendar.getInstance()); } /** * 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 * in the Java programming language * @throws SQLException if a database access error occurs */ @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } Calendar res; switch (JdbcSQLTypes[columnIndex - 1]) { case Types.CHAR : case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: String value = currentBlock.getValueAsString(columnIndex - 1); res = GregorianCalendarParser.ParseTimestamp(value, new ParsePosition(0), false); res.setTimeZone(cal.getTimeZone()); break; case Types.TIMESTAMP: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); res.setTimeZone(cal.getTimeZone()); break; case Types.TIMESTAMP_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); break; default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to Timestamp not supported", "M1M05"); } return new Timestamp(res.getTimeInMillis()); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } catch (ProtocolException ep) { throw new SQLException(ep.getMessage(), "M1M05"); } } /** * 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 columnName 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 Timestamp getTimestamp(String columnName) throws SQLException { return getTimestamp(findColumn(columnName), Calendar.getInstance()); } /** * 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 columnName 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 in the Java programming language * @throws SQLException if a database access error occurs */ @Override public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { return getTimestamp(findColumn(columnName), 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 in the * Java programming language * @throws SQLException if a database access error occurs, or if a URL is malformed */ @Override public URL getURL(int columnIndex) throws SQLException { try { if(setLastNullValue(columnIndex - 1)) { return null; } switch(JdbcSQLTypes[columnIndex - 1]) { //if it's a string type, will attempt the conversion case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: return new URL(currentBlock.getValueAsString(columnIndex - 1)); case Types.OTHER: if("url".equals(types[columnIndex - 1])) { return new URL(currentBlock.getValueAsString(columnIndex - 1)); } default: throw new SQLException("Cannot convert " + types[columnIndex - 1] + " to an url", "M1M05"); } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } catch (MalformedURLException e) { throw new SQLException(e.getMessage(), "M1M05"); } } /** * 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 columnName 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 in the * Java programming language * @throws SQLException if a database access error occurs, or if a URL is malformed */ @Override public URL getURL(String columnName) throws SQLException { return getURL(findColumn(columnName)); } /** * 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 connection */ @Override public SQLWarning getWarnings() throws SQLException { if (header != null && header.isClosed()) throw new SQLException("Cannot call on closed ResultSet", "M1M20"); // 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 */ @Override public boolean isAfterLast() { 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 */ @Override public boolean isBeforeFirst() { 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 */ @Override public boolean isClosed() { 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 */ @Override public boolean isFirst() { 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 */ @Override public boolean isLast() { 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 */ @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(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 * SQLFeatureNotSupportedException - if the JDBC driver does not support this method * Since: 1.2 * See Also: DatabaseMetaData.deletesAreDetected(int) */ @Override public boolean rowDeleted() throws SQLException { 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 * SQLFeatureNotSupportedException - if the JDBC driver does not support this method * Since: 1.2 * See Also: DatabaseMetaData.insertsAreDetected(int) */ @Override public boolean rowInserted() throws SQLException { 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 * SQLFeatureNotSupportedException - if the JDBC driver does not support this method * Since: 1.2 * See Also: DatabaseMetaData.updatesAreDetected(int) */ @Override public boolean rowUpdated() throws SQLException { return false; } /* the next methods are all related to updateable result sets, which we currently do not support */ @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 columnName, Array x) throws SQLException { throw newSQLFeatureNotSupportedException("updateArray"); } @Override public void updateAsciiStream(int columnIndex, InputStream xh) 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 columnName, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateAsciiStream"); } @Override public void updateAsciiStream(String columnName, 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 columnName, 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 columnName, InputStream x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateBinaryStream"); } @Override public void updateBinaryStream(String columnName, 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 columnName, Blob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBlob(String columnName, InputStream s) throws SQLException { throw newSQLFeatureNotSupportedException("updateBlob"); } @Override public void updateBlob(String columnName, 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 columnName, 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 columnName, 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 columnName, 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 columnName, Reader reader) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateCharacterStream(String columnName, Reader reader, int length) throws SQLException { throw newSQLFeatureNotSupportedException("updateCharacterStream"); } @Override public void updateCharacterStream(String columnName, 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 columnName, Reader reader) throws SQLException { throw newSQLFeatureNotSupportedException("updateNCharacterStream"); } @Override public void updateNCharacterStream(String columnName, 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 columnName, Clob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateClob(String columnName, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateClob"); } @Override public void updateClob(String columnName, 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 columnName, NClob x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateNClob(String columnName, Reader r) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateNClob(String columnName, Reader r, long length) throws SQLException { throw newSQLFeatureNotSupportedException("updateNClob"); } @Override public void updateDate(int columnIndex, java.sql.Date x) throws SQLException { throw newSQLFeatureNotSupportedException("updateDate"); } @Override public void updateDate(String columnName, java.sql.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 columnName, 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 columnName, 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 columnName, 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 columnName, long x) throws SQLException { throw newSQLFeatureNotSupportedException("updateLong"); } @Override public void updateNull(int columnIndex) throws SQLException { throw newSQLFeatureNotSupportedException("updateNull"); } @Override public void updateNull(String columnName) 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 columnName, Object x) throws SQLException { throw newSQLFeatureNotSupportedException("updateObject"); } @Override public void updateObject(String columnName, 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 columnName, 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 columnName, 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 columnName, 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 columnName, String x) throws SQLException { throw newSQLFeatureNotSupportedException("updateNString"); } @Override public void updateSQLXML(String columnName, SQLXML x) throws SQLException { throw newSQLFeatureNotSupportedException("updateSQLXML"); } @Override public void updateSQLXML(int columnIndex, 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 columnName, 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 columnName, 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; } //== end methods of interface ResultSet /** * Small helper method that formats the "Invalid Column Index number ..." message * and creates a new SQLException object whose SQLState is set to "M1M05". * * @param colIdx the column index number * @return a new created SQLException object with SQLState M1M05 */ static SQLException newSQLInvalidColumnIndexException(int colIdx) { return new SQLException("Invalid Column Index number: " + colIdx, "M1M05"); } /** * Small helper method that formats the "Method ... not implemented" message * and creates a new SQLFeatureNotSupportedException object * whose SQLState is set to "0A000". * * @param name the method name * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000 */ private static SQLFeatureNotSupportedException newSQLFeatureNotSupportedException(String name) { return new SQLFeatureNotSupportedException("Method " + name + " not implemented", "0A000"); } }