diff src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java @ 0:a5a898f6886c

Copy of MonetDB java directory changeset e6e32756ad31.
author Sjoerd Mullender <sjoerd@acm.org>
date Wed, 21 Sep 2016 09:34:48 +0200 (2016-09-21)
parents
children a27ee2cb14a0
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
@@ -0,0 +1,3746 @@
+/*
+ * 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 - 2016 MonetDB B.V.
+ */
+
+package nl.cwi.monetdb.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+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.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.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+import nl.cwi.monetdb.mcl.parser.MCLParseException;
+import nl.cwi.monetdb.mcl.parser.TupleLineParser;
+
+/**
+ * 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 following have default access modifier for the MonetVirtualResultSet subclass
+	/** The current line of the buffer split in columns */
+	final TupleLineParser tlp;
+	/** 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 MonetConnection.ResultSetResponse header;
+	/** The names of the columns in this ResultSet */
+	private final String[] columns;
+	/** The MonetDB types of the columns in this ResultSet */
+	private final String[] types;
+	/** The JDBC SQL types of the columns in this ResultSet. The content will be derived from the MonetDB types[] */
+	private final int[] JdbcSQLTypes;
+	/** The number of rows in this ResultSet */
+	final int tupleCount;	// default for the MonetVirtualResultSet
+
+	/** 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;
+
+	/**
+	 * 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,
+		MonetConnection.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
+		columns = header.getNames();
+		types = header.getTypes();
+		tupleCount = header.tuplecount;
+
+		// create result array
+		tlp = new TupleLineParser(columns.length);
+
+		JdbcSQLTypes = new int[types.length];
+		populateJdbcSQLtypesArray();
+	}
+
+	/**
+	 * Constructor used by MonetVirtualResultSet.
+	 * DO NOT USE THIS CONSTRUCTOR IF YOU ARE NOT EXTENDING THIS OBJECT!
+	 *
+	 * @param statement the statement which created this ResultSet
+	 * @param columns the column names
+	 * @param types the column types
+	 * @param results the number of rows in the ResultSet
+	 * @throws IOException if communicating with monet failed
+	 * @throws SQLException is a protocol error occurs
+	 */
+	MonetResultSet(
+		Statement statement,
+		String[] columns,
+		String[] types,
+		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.columns = columns;
+		this.types = types;
+		this.tupleCount = results;
+
+		this.tlp = new TupleLineParser(columns.length);
+
+		JdbcSQLTypes = new int[types.length];
+		populateJdbcSQLtypesArray();
+	}
+
+	/**
+	 * Internal utility method to fill the JdbcSQLTypes array with derivable values.
+	 * By doing it once (in the constructor) we can avoid doing this in many getXyz() methods again and again
+	 * thereby improving getXyz() method performance.
+	 */
+	private void populateJdbcSQLtypesArray() {
+		for (int i = 0; i < types.length; i++) {
+			int javaSQLtype = MonetDriver.getJavaType(types[i]);
+			JdbcSQLTypes[i] = javaSQLtype;
+			if (javaSQLtype == Types.BLOB) {
+				try {
+					if (((MonetConnection)statement.getConnection()).getBlobAsBinary())
+						JdbcSQLTypes[i] = Types.BINARY;
+				} catch (SQLException se) { /* ignore it */ }
+			}
+		}
+	}
+
+
+	//== 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
+
+		String tmpLine = header.getLine(row - 1);
+
+		// store it
+		curRow = row;
+
+		if (tmpLine == null) return false;
+		try {
+			tlp.parse(tmpLine);
+		} catch (MCLParseException e) {
+			throw new SQLException(e.getMessage(), "M0M10");
+		}
+
+		return true;
+	}
+
+	/**
+	 * Moves the cursor to the end of this ResultSet object, just after the last
+	 * row. This method has no effect if the result set contains no rows.
+	 *
+	 * @throws SQLException if a database access error occurs or the result set
+	 *         type is TYPE_FORWARD_ONLY
+	 */
+	@Override
+	public void afterLast() throws SQLException {
+		absolute(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 && columns != null) {
+			for (int i = 0; i < columns.length; 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 < columns.length; 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");
+	}
+
+	/* Mapi doesn't allow something for streams at the moment, thus all not implemented for now */
+	@Override
+	public InputStream getAsciiStream(int columnIndex) throws SQLException {
+		throw newSQLFeatureNotSupportedException("getAsciiStream");
+	}
+	@Override
+	public InputStream getAsciiStream(String columnName) throws SQLException {
+		throw newSQLFeatureNotSupportedException("getAsciiStream");
+	}
+
+	@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 {
+			switch (JdbcSQLTypes[columnIndex - 1]) {
+				case Types.BLOB:
+					Blob blob = getBlob(columnIndex);
+					if (blob == null)
+						return null;
+					return blob.getBinaryStream();
+				case Types.BINARY:
+				case Types.VARBINARY:
+				case Types.LONGVARBINARY:
+					byte[] bte = getBytes(columnIndex);
+					if (bte == null)
+						return null;
+					return new ByteArrayInputStream(bte);
+			}
+			throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05");
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+	
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object as a stream of uninterpreted bytes. The
+	 * value can then be read in chunks from the stream. This method is
+	 * particularly suitable for retrieving large LONGVARBINARY  values.
+	 * <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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			return new StringReader(val);
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object as a java.io.Reader object.
+	 *
+	 * @param 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 i 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			return MonetBlob.create(val);
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object as a Blob object in the Java programming
+	 * language.
+	 *
+	 * @param 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 i 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			return new MonetClob(val);
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object as a Clob object in the
+	 * Java programming language.
+	 *
+	 * @param 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			try {
+				return new BigDecimal(val);
+			} catch (NumberFormatException e) {
+				return BigDecimal.ZERO;
+			}
+		} 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			try {
+				BigDecimal bd = new BigDecimal(val);
+				bd.setScale(scale);
+				return bd;
+			} catch (NumberFormatException e) {
+				return BigDecimal.ZERO;
+			}
+		} 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return false;	// if the value is SQL NULL, the value returned is false
+			}
+			lastReadWasNull = false;
+
+			// match common cases first
+			if ("false".equalsIgnoreCase(val) || "0".equals(val))
+				return false;
+			if ("true".equalsIgnoreCase(val) || "1".equals(val))
+				return true;
+
+			// match type specific values
+			switch (JdbcSQLTypes[columnIndex - 1]) {
+				case Types.BOOLEAN:
+				case Types.CHAR:
+				case Types.VARCHAR:
+				case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
+				case Types.CLOB:
+					// check if string value equals "true" (case insensitive) or not
+					return Boolean.parseBoolean(val);
+				case Types.BIT: // MonetDB doesn't use type BinaryDigit, it's here for completeness
+				case Types.TINYINT:
+				case Types.SMALLINT:
+				case Types.INTEGER:
+					if (getInt(columnIndex) == 0) {
+						return false;
+					}
+					return true;
+				case Types.BIGINT:
+					if (getLong(columnIndex) == 0L) {
+						return false;
+					}
+					return true;
+				case Types.DOUBLE:
+				case Types.FLOAT:
+				case Types.REAL:
+					if (getDouble(columnIndex) == 0.0) {
+						return false;
+					}
+					return true;
+				case Types.DECIMAL:
+				case Types.NUMERIC:
+					if (getBigDecimal(columnIndex).compareTo(BigDecimal.ZERO) == 0) {
+						return false;
+					}
+					return true;
+				default:
+					throw new SQLException("Conversion from " + types[columnIndex - 1] + " to boolean type not supported", "M1M05");
+			}
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row of this
+	 * ResultSet object as a boolean in the Java programming language.
+	 *
+	 * @param 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return (byte) 0;
+			}
+			lastReadWasNull = false;
+			try {
+				return Byte.parseByte(val);
+			} catch (NumberFormatException e) {
+				// ignore parse error, return the default: 0
+				return (byte) 0;
+			}
+		} 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+
+			// According to Table B-6, getBytes() only operates on BINARY types
+			switch (JdbcSQLTypes[columnIndex - 1]) {
+				case Types.BLOB:
+				case Types.BINARY:
+				case Types.VARBINARY:
+				case Types.LONGVARBINARY:
+					// unpack the HEX (BLOB) notation to real bytes
+					int len = val.length() / 2;
+					byte[] buf = new byte[len];
+					int offset;
+					for (int j = 0; j < len; j++) {
+						offset = j * 2;
+						buf[j] = (byte)Integer.parseInt(val.substring(offset, offset + 2), 16);
+					}
+					return buf;
+				default:
+					throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05");
+			}
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row of this
+	 * ResultSet object as a byte array in the Java programming language. The
+	 * bytes represent the raw values returned by the driver.
+	 *
+	 * NOTE: Since the mapi protocol is ASCII-based, this method only returns
+	 *       Java byte representations of Strings, which is nothing more than
+	 *       an encoding into a sequence of bytes using the platform's default
+	 *       charset.
+	 *
+	 * @param 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.id : "") + ")", "0AM21");
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row of this
+	 * ResultSet object as a double in the Java programming language.
+	 *
+	 * @param columnIndex the first column is 1, the second is 2, ...
+	 * @return the column value; if the value is SQL NULL, the value returned
+	 *         is 0
+	 * @throws SQLException if there is no such column
+	 */
+	@Override
+	public double getDouble(int columnIndex) throws SQLException {
+		try {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return 0;
+			}
+			lastReadWasNull = false;
+			try {
+				return Double.parseDouble(val);
+			} catch (NumberFormatException e) {
+				// ignore conversion error, return the default: 0
+				return 0;
+			}
+		} 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 &lt;= 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return 0;
+			}
+			lastReadWasNull = false;
+			try {
+				return Float.parseFloat(val);
+			} catch (NumberFormatException e) {
+				// ignore conversion error, return the default: 0
+				return 0;
+			}
+		} 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return 0;
+			}
+			lastReadWasNull = false;
+			try {
+				return Integer.parseInt(val);
+			} catch (NumberFormatException e) {
+				// ignore conversion error, return the default: 0
+				return 0;
+			}
+		} 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return 0;
+			}
+			lastReadWasNull = false;
+
+			// The oid datatype values (as string) have a  @0  suffix in the string value.
+			// To allow succesful parsing and conversion to long, we need to remove it first
+			if ("oid".equals(types[columnIndex - 1])) {
+				int len = val.length();
+				if (len > 2 && val.endsWith("@0"))
+					val = val.substring(0, len-2);
+			}
+			try {
+				return Long.parseLong(val);
+			} catch (NumberFormatException e) {
+				// ignore conversion error, return the default: 0
+				return 0;
+			}
+		} 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()
+			private boolean[] _is_fetched	= new boolean[columns.length +1];
+			private int[] _precision	= new int[columns.length +1];
+			private int[] _scale		= new int[columns.length +1];
+			private int[] _isNullable	= new int[columns.length +1];
+			private boolean[] _isAutoincrement = new boolean[columns.length +1];
+			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 && !"".equals(schName)) {
+					String tblName = getTableName(column);
+					if (tblName != null && !"".equals(tblName)) {
+						String colName = getColumnName(column);
+						if (colName != null && !"".equals(colName)) {
+							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.
+			 *
+			 * @returns 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 {
+				if (_is_fetched[column] != true) {
+					fetchColumnInfo(column);
+				}
+				return _isAutoincrement[column];
+			}
+
+			/**
+			 * Indicates whether a column's case matters.
+			 *
+			 * @param column the first column is 1, the second is 2, ...
+			 * @returns true for all character string columns else false
+			 */
+			@Override
+			public boolean isCaseSensitive(int column) throws SQLException {
+				switch (getColumnType(column)) {
+					case Types.CHAR:
+					case Types.VARCHAR:
+					case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
+					case Types.CLOB:
+						return true;
+				}
+
+				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, ...
+			 * @returns 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, ...
+			 * @returns 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 {
+				String monettype = getColumnTypeName(column);
+				if (monettype != null) {
+					if ("oid".equals(monettype)
+					 || "ptr".equals(monettype))
+						return false;
+				}
+				// we can hardcode this, based on the colum type
+				switch (getColumnType(column)) {
+					case Types.NUMERIC:
+					case Types.DECIMAL:
+					case Types.TINYINT:
+					case Types.SMALLINT:
+					case Types.INTEGER:
+					case Types.BIGINT:
+					case Types.REAL:
+					case Types.FLOAT:
+					case Types.DOUBLE:
+						return true;
+					case Types.BIT: // we don't use type BIT, it's here for completeness
+					case Types.BOOLEAN:
+					case Types.DATE:
+					case Types.TIME:
+					case Types.TIMESTAMP:
+					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)
+					return ret;
+
+				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 {
+				String schema = "";
+
+				if (header == null)
+					return schema;
+
+				// figure the name out
+				try {
+					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 {
+				String table = "";
+
+				if (header == null)
+					return table;
+
+				// figure the name out
+				try {
+					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 {
+				if (_is_fetched[column] != true) {
+					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: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
+						case Types.CLOB:
+						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.FLOAT:
+						case Types.DOUBLE:
+							_precision[column] = 15;
+							break;
+						case Types.BIT: // MonetDB doesn't use type BIT, it's here for completeness
+							_precision[column] = 1;
+							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];
+			}
+
+			/**
+			 * 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 {
+				if (_is_fetched[column] != true) {
+					fetchColumnInfo(column);
+				}
+				return _scale[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 {
+				if (_is_fetched[column] != true) {
+					fetchColumnInfo(column);
+				}
+				return _isNullable[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 {
+					Class<?> type = null;
+					if (conn != null) {
+						Map<String,Class<?>> map = conn.getTypeMap();
+						if (map != null && map.containsKey(types[column - 1])) {
+							type = (Class)map.get(types[column - 1]);
+						}
+					}
+					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: " + types[column - 1], "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().getConnection().getTypeMap()).
+	 *
+	 * @param columnIndex the first column is 1, the second is 2, ...
+	 * @return a java.lang.Object holding the column value or null
+	 * @throws SQLException if a database access error occurs
+	 */
+	@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 String MonetDBType;
+		final int JdbcType;
+		final String val;
+		try {
+			val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			MonetDBType = types[columnIndex - 1];
+			JdbcType = JdbcSQLTypes[columnIndex - 1];
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+
+		switch(JdbcType) {
+			case Types.BIT: // MonetDB doesn't use type BInary digiT, it's here for completeness
+			case Types.TINYINT:
+			case Types.SMALLINT:
+				try {
+					return Short.valueOf(val);
+				} catch (NumberFormatException e) {
+					return val;
+				}
+			case Types.INTEGER:
+				try {
+					return Integer.valueOf(val);
+				} catch (NumberFormatException e) {
+					return val;
+				}
+			case Types.BIGINT:
+				try {
+					return Long.valueOf(val);
+				} catch (NumberFormatException e) {
+					return val;
+				}
+			case Types.DOUBLE:
+			case Types.FLOAT:
+				try {
+					return Double.valueOf(val);
+				} catch (NumberFormatException e) {
+					return val;
+				}
+			case Types.REAL:
+				try {
+					return Float.valueOf(val);
+				} catch (NumberFormatException e) {
+					return val;
+				}
+			case Types.DECIMAL:
+			case Types.NUMERIC:
+				try {
+					return new BigDecimal(val);
+				} catch (NumberFormatException e) {
+					return val;
+				}
+			case Types.BOOLEAN:
+				return Boolean.valueOf(val);
+			case Types.VARCHAR:
+			{
+				// The MonetDB types: inet, json, url and uuid are all mapped to Types.VARCHAR in MonetDriver.typeMap
+				// For these MonetDB types (except json, see comments below) we try to create objects of the corresponding class.
+				// As in most cases when we get here the MonetDBtype will be "varchar". For efficiency we compare
+				// the MonetDBtype first letter to see if we can skip the three .equals(MonetDBType) method calls.
+				char firstletter = MonetDBType.charAt(0);
+				if (firstletter == 'i') {
+					if ("inet".equals(MonetDBType)) {
+						try {
+							nl.cwi.monetdb.jdbc.types.INET inet_obj = new nl.cwi.monetdb.jdbc.types.INET();
+							inet_obj.fromString(val);
+							return inet_obj;
+						} catch (Exception exc) {
+							// ignore exception and just return the val String object
+							return val;
+						}
+					}
+				}
+				else if (firstletter == 'u') {
+					if ("url".equals(MonetDBType)) {
+						try {
+							nl.cwi.monetdb.jdbc.types.URL url_obj = new nl.cwi.monetdb.jdbc.types.URL();
+							url_obj.fromString(val);
+							return url_obj;
+						} catch (MalformedURLException exc) {
+							// ignore exception and just return the val String object
+							return val;
+						} catch (Exception exc) {
+							// ignore exception and just return the val String object
+							return val;
+						}
+					}
+					if ("uuid".equals(MonetDBType)) {
+						try {
+							return java.util.UUID.fromString(val);
+						} catch (IllegalArgumentException exc) {
+							// ignore exception and just return the val String object
+							return val;
+						}
+					}
+				}
+//				else if (firstletter == 'j') {
+//					if ("json".equals(MonetDBType)) {
+						// There is no support for JSON in standard java class libraries.
+						// Possibly we could use org.json.simple.JSONObject or other/faster libs
+						// javax.json.Json is not released yet (see https://json-processing-spec.java.net/)
+						// see also https://github.com/fabienrenaud/java-json-benchmark
+						// Note that it would make our JDBC driver dependent of an external jar
+						// and we don't want that so simply return it as String object
+//						return val;
+//					}
+//				}
+				return val;
+			}
+			case Types.CHAR:
+			case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness
+				return val;
+			case Types.CLOB:
+				return new MonetClob(val);
+			case Types.BLOB:
+				return MonetBlob.create(val);
+			case Types.DATE:
+				return getDate(columnIndex);
+			case Types.TIME:
+				return getTime(columnIndex);
+			case Types.TIMESTAMP:
+				return getTimestamp(columnIndex);
+			case Types.BINARY:
+			case Types.VARBINARY:
+			case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY, it's here for completeness
+				return getBytes(columnIndex);
+			case Types.OTHER:
+			default:
+				// When we get here the column type is a non-standard JDBC SQL type, possibly a User Defined Type.
+				// Just call getObject(int, Map) for those rare cases.
+				return getObject(columnIndex, this.getStatement().getConnection().getTypeMap());
+		}
+	}
+
+	private boolean classImplementsSQLData(Class<?> cl) {
+		Class<?>[] cls = cl.getInterfaces();
+		for (int i = 0; i < cls.length; i++) {
+			if (cls[i] == SQLData.class)
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Gets the value of the designated column in the current row of this
+	 * ResultSet object as an Object in the Java programming language.
+	 *
+	 * This method will return the value of the given column as a Java object.
+	 * The type of the Java object will be the default Java object type corresponding
+	 * to the column's SQL type, following the mapping for built-in types specified
+	 * in the JDBC specification.
+	 * If the value is an SQL NULL, the driver returns a Java null.
+	 *
+	 * This method may also be used to read database-specific abstract data types.
+	 * In the JDBC 2.0 API, the behavior of method getObject is extended to
+	 * materialize data of SQL user-defined types.
+	 *
+	 * If Connection.getTypeMap does not throw a SQLFeatureNotSupportedException, then
+	 * when a column contains a structured or distinct value, the behavior of this
+	 * method is as if it were a call to: getObject(columnIndex,
+	 * this.getStatement().getConnection().getTypeMap()).
+	 * If Connection.getTypeMap does throw a SQLFeatureNotSupportedException, then
+	 * structured values are not supported, and distinct values are mapped to the
+	 * default Java class as determined by the underlying SQL type of the DISTINCT type.
+	 *
+	 * @param i 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
+	@SuppressWarnings("unchecked")
+	public Object getObject(int columnIndex, Map<String,Class<?>> map)
+		throws SQLException
+	{
+		final String val;
+		final String MonetDBtype;
+		try {
+			val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			MonetDBtype = types[columnIndex - 1];
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+
+		Class<?> type = null;
+		if (map != null && map.containsKey(MonetDBtype)) {
+			type = map.get(MonetDBtype);
+		}
+		if (type == null) {
+			// fallback to the standard Class mappings
+			type = getClassForType(JdbcSQLTypes[columnIndex - 1]);
+		}
+
+		if (type == null || type == String.class) {
+			return val;
+		} else if (type == BigDecimal.class) {
+			return getBigDecimal(columnIndex);
+		} else if (type == Boolean.class) {
+			return Boolean.valueOf(getBoolean(columnIndex));
+		} else if (type == Short.class) {
+			return Short.valueOf(getShort(columnIndex));
+		} else if (type == Integer.class) {
+			return Integer.valueOf(getInt(columnIndex));
+		} else if (type == Long.class) {
+			return Long.valueOf(getLong(columnIndex));
+		} else if (type == Float.class) {
+			return Float.valueOf(getFloat(columnIndex));
+		} else if (type == Double.class) {
+			return Double.valueOf(getDouble(columnIndex));
+		} else if (type == byte[].class) {
+			return getBytes(columnIndex);
+		} else if (type == java.sql.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 nsme) {
+				throw new SQLException(nsme.getMessage(), "M0M27");
+			} catch (InstantiationException ie) {
+				throw new SQLException(ie.getMessage(), "M0M27");
+			} catch (IllegalAccessException iae) {
+				throw new SQLException(iae.getMessage(), "M0M27");
+			} catch (InvocationTargetException ite) {
+				throw new SQLException(ite.getMessage(), "M0M27");
+			} catch (SecurityException se) {
+				throw new SQLException(se.getMessage(), "M0M27");
+			}
+			final int colnum = columnIndex;
+			final boolean valwasnull = wasNull();
+			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 java.sql.Date readDate() throws SQLException {
+					return getDate(colnum);
+				}
+
+				@Override
+				public java.sql.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, MonetDBtype);
+			return x;
+		} else {
+			return val;
+		}
+	}
+
+	/**
+	 * 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 i the first column is 1, the second is 2, ...
+	 * @param type Class representing the Java data type to convert the
+	 *        designated column to
+	 * @return an instance of type holding the column value
+	 * @throws SQLException if conversion is not supported, type is
+	 *         null or another error occurs. The getCause() method of
+	 *         the exception may provide a more detailed exception, for
+	 *         example, if a conversion error occurs
+	 */
+	@Override
+	public <T> T getObject(int i, Class<T> type) throws SQLException {
+		if (type == null)
+			throw new SQLException("type is null", "M1M05");
+
+		throw new SQLFeatureNotSupportedException("cannot return a Java generic type based on static types from getXXX methods", "0AM34");
+	}
+
+	/**
+	 * 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
+		 */
+		// keep this switch regarding the returned classes aligned with getObject(int, Map) !
+		switch(type) {
+			case Types.CHAR:
+			case Types.VARCHAR:
+			case Types.LONGVARCHAR:
+				return String.class;
+			case Types.NUMERIC:
+			case Types.DECIMAL:
+				return BigDecimal.class;
+			case Types.BOOLEAN:
+				return Boolean.class;
+			case Types.BIT: // MonetDB doesn't support type BIT, it's here for completeness
+			case Types.TINYINT:
+			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.FLOAT:
+			case Types.DOUBLE:
+				return Double.class;
+			case Types.BINARY:      // MonetDB currently does not support these
+			case Types.VARBINARY:   // see treat_blob_as_binary property
+			case Types.LONGVARBINARY:
+				return byte[].class;
+			case Types.DATE:
+				return java.sql.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;
+
+			// all the rest are currently not implemented and used
+			default:
+				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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return 0;
+			}
+			lastReadWasNull = false;
+			try {
+				return Short.parseShort(val);
+			} catch (NumberFormatException e) {
+				// ignore conversion error, return the default: 0
+				return 0;
+			}
+		} 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			return val;
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row of this
+	 * ResultSet object as a String in the Java programming language.
+	 *
+	 * @param 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");
+	}
+
+	// This behaviour is according table B-6 of Sun JDBC Specification 3.0
+	private SimpleDateFormat ts =
+		new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+	private SimpleDateFormat t =
+		new SimpleDateFormat("HH:mm:ss");
+	private SimpleDateFormat d =
+		new SimpleDateFormat("yyyy-MM-dd");
+	/**
+	 * Helper method which parses the date/time value for columns of type
+	 * TIME, DATE and TIMESTAMP.  For the types CHAR, VARCHAR and
+	 * LONGVARCHAR an attempt is made to parse the date according to the
+	 * given type.  The given Calender object is filled with the parsed
+	 * data.  Optional fractional seconds (nanos) are returned by this
+	 * method.  If the underlying type of the column is none of the
+	 * mentioned six, January 1st 1970 0:00:00 GMT is returned.<br />
+	 * The dates are parsed with the given Calendar.
+	 *
+	 * @param cal the Calendar to use/fill when parsing the date/time
+	 * @param col the column to parse
+	 * @param type the corresponding java.sql.Types type of the calling
+	 *        function
+	 * @return the fractional seconds (nanos) or -1 if the value is NULL
+	 * @throws SQLException if a database error occurs
+	 */
+	private int getJavaDate(Calendar cal, int columnIndex, int type)
+		throws SQLException
+	{
+		if (cal == null) throw
+			new IllegalArgumentException("No Calendar object given!");
+
+		final String monetDate;
+		final String MonetDBType;
+		int JdbcType;
+		try {
+			monetDate = tlp.values[columnIndex - 1];
+			if (monetDate == null) {
+				lastReadWasNull = true;
+				return -1;
+			}
+			lastReadWasNull = false;
+			MonetDBType = types[columnIndex - 1];
+			JdbcType = JdbcSQLTypes[columnIndex - 1];
+			// If we got a string type, set the JdbcType to the given type
+			// so we attempt to parse it as the caller thinks it is.
+			if (JdbcType == Types.CHAR ||
+			    JdbcType == Types.VARCHAR ||
+			    JdbcType == Types.LONGVARCHAR ||
+			    JdbcType == Types.CLOB)
+			{
+				JdbcType = type;
+			}
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+
+		// we know whether we have a time with or without
+		// time zone if the monet type ends with "tz"
+		boolean hasTimeZone = (MonetDBType != null) ? MonetDBType.endsWith("tz") : false;
+		TimeZone ptz = cal.getTimeZone();
+
+		// it is important to parse the time in the given timezone in
+		// order to get a correct (UTC) time value, hence we need to
+		// parse it first
+		if (hasTimeZone) {
+			// MonetDB/SQL99:  Sign TwoDigitHours : Minutes
+			ptz = TimeZone.getTimeZone("GMT" +
+					monetDate.substring(
+						monetDate.length() - 6,
+						monetDate.length()));
+		}
+
+		java.util.Date pdate = null;
+		ParsePosition ppos = new ParsePosition(0);
+		switch(JdbcType) {
+			case Types.DATE:
+				d.setTimeZone(ptz);
+				pdate = d.parse(monetDate, ppos);
+				break;
+			case Types.TIME:
+				t.setTimeZone(ptz);
+				pdate = t.parse(monetDate, ppos);
+				break;
+			case Types.TIMESTAMP:
+				ts.setTimeZone(ptz);
+				pdate = ts.parse(monetDate, ppos);
+				break;
+			default:
+				addWarning("unsupported data type", "01M03");
+				cal.clear();
+				return 0;
+		}
+		if (pdate == null) {
+			// parsing failed
+			int epos = ppos.getErrorIndex();
+			if (epos == -1) {
+				addWarning("parsing '" + monetDate + "' failed", "01M10");
+			} else if (epos < monetDate.length()) {
+				addWarning("parsing failed," +
+						 " found: '" + monetDate.charAt(epos) + "'" +
+						 " in: \"" + monetDate + "\"" +
+						 " at pos: " + ppos.getErrorIndex(), "01M10");
+			} else {
+				addWarning("parsing failed, expected more data after '" +
+						monetDate + "'", "01M10");
+			}
+			// default value
+			cal.clear();
+			return 0;
+		}
+		cal.setTime(pdate);
+
+		int nanos = 0;
+		if (JdbcType == Types.TIME || JdbcType == Types.TIMESTAMP) {
+			// parse additional nanos (if any)
+			int pos = ppos.getIndex();
+			char[] monDate = monetDate.toCharArray();
+			if (pos < monDate.length && monDate[pos] == '.') {
+				pos++;
+				int ctr;
+				try {
+					nanos = getIntrinsicValue(monDate[pos], pos++);
+					for (ctr = 1;
+							pos < monDate.length &&
+							monDate[pos] >= '0' &&
+							monDate[pos] <= '9';
+							ctr++)
+					{
+						if (ctr < 9) {
+							nanos *= 10;
+							nanos += (getIntrinsicValue(monDate[pos], pos));
+						}
+						if (ctr == 2)	// we have three at this point
+							cal.set(Calendar.MILLISECOND, nanos);
+						pos++;
+					}
+					while (ctr++ < 9)
+						nanos *= 10;
+				} catch(MCLParseException e) {
+					addWarning(e.getMessage() +
+							" found: '" + monDate[e.getErrorOffset()] + "'" +
+							" in: \"" + monetDate + "\"" +
+							" at pos: " + e.getErrorOffset(), "01M10");
+					// default value
+					cal.clear();
+					nanos = 0;
+				}
+			}
+		}
+		return nanos;
+	}
+
+	/**
+	 * Small helper method that returns the intrinsic value of a char if
+	 * it represents a digit.  If a non-digit character is encountered
+	 * an MCLParseException is thrown.
+	 *
+	 * @param c the char
+	 * @param pos the position
+	 * @return the intrinsic value of the char
+	 * @throws MCLParseException if c is not a digit
+	 */
+	private final static int getIntrinsicValue(char c, int pos)
+		throws MCLParseException
+	{
+		// note: don't use Character.isDigit() here, because
+		// we only want ISO-LATIN-1 digits
+		if (c >= '0' && c <= '9') {
+			return (int)c - (int)'0';
+		} else {
+			throw new MCLParseException("Expected a digit", pos);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row of this
+	 * ResultSet object as a java.sql.Date object in the Java programming
+	 * language.
+	 *
+	 * @param columnIndex the first column is 1, the second is 2, ...
+	 * @return the column value; 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 java.sql.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 java.sql.Date getDate(int columnIndex, Calendar cal)
+		throws SQLException
+	{
+		int ret = getJavaDate(cal, columnIndex, Types.DATE);
+		return ret == -1 ? null : new java.sql.Date(cal.getTimeInMillis());
+	}
+
+	/**
+	 * 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 java.sql.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 java.sql.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
+	{
+		int ret = getJavaDate(cal, columnIndex, Types.TIME);
+		return ret == -1 ? null : new Time(cal.getTimeInMillis());
+	}
+
+	/**
+	 * 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
+	{
+		int nanos = getJavaDate(cal, columnIndex, Types.TIMESTAMP);
+		if (nanos == -1)
+			return null;
+
+		Timestamp ts = new Timestamp(cal.getTimeInMillis());
+		ts.setNanos(nanos);
+		return ts;
+	}
+
+	/**
+	 * 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 {
+			String val = tlp.values[columnIndex - 1];
+			if (val == null) {
+				lastReadWasNull = true;
+				return null;
+			}
+			lastReadWasNull = false;
+			try {
+				return new URL(val);
+			} catch (MalformedURLException e) {
+				throw new SQLException(e.getMessage(), "M1M05");
+			}
+		} catch (IndexOutOfBoundsException e) {
+			throw newSQLInvalidColumnIndexException(columnIndex);
+		}
+	}
+
+	/**
+	 * Retrieves the value of the designated column in the current row
+	 * of this ResultSet object as a java.net.URL object in the Java
+	 * programming language.
+	 *
+	 * @param 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");
+	}
+
+	public void updateNCharacterStream(int columnIndex, Reader x, int length) 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");
+	}
+
+	public void updateNCharacterStream(String columnName, Reader reader, int length) 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
+
+	/**
+	 * Adds a warning to the pile of warnings this ResultSet object has. If
+	 * there were no warnings (or clearWarnings was called) this warning will
+	 * be the first, otherwise this warning will get appended to the current
+	 * warning.
+	 *
+	 * @param reason the warning message
+	 */
+	private void addWarning(String reason, String sqlstate) {
+		if (warnings == null) {
+			warnings = new SQLWarning(reason, sqlstate);
+		} else {
+			warnings.setNextWarning(new SQLWarning(reason, sqlstate));
+		}
+	}
+
+	/**
+	 * Small helper method that formats the "Invalid Column Index number ..." message
+	 * and creates a new SQLException object whose SQLState is set to "M1M05".
+	 *
+	 * @param name the method name
+	 * @return a new created SQLException object with SQLState M1M05
+	 */
+	private final 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 final static SQLFeatureNotSupportedException newSQLFeatureNotSupportedException(String name) {
+		return new SQLFeatureNotSupportedException("Method " + name + " not implemented", "0A000");
+	}
+}
+