view src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java @ 93:eeb71f7d36bf embedded

Fixed a bug on the JDBC MAPI connection from the old code! Fixed the connection properties for an JDBC Embedded connection. To start a JDBC Embedded connection, the user must start the embedded database beforehand with the method MonetDBEmbeddedDatabase.StartDatabase().
author Pedro Ferreira <pedro.ferreira@monetdbsolutions.com>
date Fri, 06 Jan 2017 12:36:33 +0000 (2017-01-06)
parents 6f74e01c57da
children c0ce1ea5075f
line wrap: on
line source
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0.  If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 1997 - July 2008 CWI, August 2008 - 2017 MonetDB B.V.
 */

package nl.cwi.monetdb.jdbc;

import nl.cwi.monetdb.mcl.connection.helpers.GregorianCalendarParser;
import nl.cwi.monetdb.mcl.protocol.ProtocolException;
import nl.cwi.monetdb.mcl.responses.DataBlockResponse;
import nl.cwi.monetdb.mcl.responses.ResultSetResponse;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.NClob;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLInput;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.ParsePosition;
import java.util.*;

/**
 * A ResultSet suitable for the MonetDB database.
 *
 * A table of data representing a database result set, which is usually
 * generated by executing a statement that queries the database.
 *
 * A ResultSet object maintains a cursor pointing to its current row of data.
 * Initially the cursor is positioned before the first row. The next method
 * moves the cursor to the next row, and because it returns false when there
 * are no more rows in the ResultSet object, it can be used in a while loop to
 * iterate through the result set.
 *
 * The current state of this ResultSet is that it supports positioning in the
 * result set, absolute and relative. A slight performance difference between
 * FORWARD_ONLY or result sets scrollable in both directions can be noticed as
 * for FORWARD_ONLY result sets the memory usage will be likely lower for large
 * result sets.
 *
 * @author Fabian Groffen, Martin van Dinther
 * @version 0.8
 */
public class MonetResultSet extends MonetWrapper implements ResultSet {

	/** The current position of the cursor for this ResultSet object */
	int curRow = 0;
	// a blank final is immutable once assigned in the constructor
	/** A Header to retrieve lines from */
	private final ResultSetResponse header;
	/** The names of the columns in this ResultSet */
	private final String[] columns;
	/** The MonetDB types of the columns in this ResultSet */
	private final String[] types;
	/** The JDBC SQL types of the columns in this ResultSet. The content will be derived from the MonetDB types[] */
	private final int[] JdbcSQLTypes;
	/** The number of rows in this ResultSet */
	final int tupleCount;
	/** The parental Statement object */
	private final Statement statement;
	/** The type of this ResultSet (forward or scrollable) */
	private int type = TYPE_FORWARD_ONLY;
	/** The concurrency of this ResultSet (currently only read-only) */
	private int concurrency = CONCUR_READ_ONLY;
	/** The warnings for this ResultSet object */
	private SQLWarning warnings;
	/** whether the last read field (via some getXyz() method) was NULL */
	private boolean lastReadWasNull = true;
	/** Just a dummy variable to keep store the fetchsize set. */
	private int fetchSize;
	/** The current row's values */
	DataBlockResponse currentBlock;

	/**
	 * Main constructor backed by the given Header.
	 *
	 * @param statement the statement which created this ResultSet
	 * @param header a header containing the query, resultset type, etc.
	 * @throws SQLException is a protocol error occurs
	 */
	MonetResultSet(Statement statement, ResultSetResponse header) throws SQLException {
		if (statement == null) {
			throw new IllegalArgumentException("Statement may not be null!");
		}
		if (header == null) {
			throw new IllegalArgumentException("ResultSetResponse may not be null!");
		}
		this.statement = statement;
		this.header = header;
		this.type = header.getRSType();
		this.concurrency = header.getRSConcur();
		/* if we have a header object, the fetchSize used for this result set is the header's cacheSize */
		this.fetchSize = header.getCacheSize();
		// well there is only one supported concurrency, so we don't have to bother about that
		// throws SQLException on getters of Header, so we find out immediately if an error occurred for this query
		this.tupleCount = header.getTuplecount();
		this.columns = header.getNames();
		this.types = header.getTypes();
		this.JdbcSQLTypes = header.getJdbcSQLTypes();
	}

	/**
	 * Constructor used by MonetVirtualResultSet.
	 * DO NOT USE THIS CONSTRUCTOR IF YOU ARE NOT EXTENDING THIS OBJECT!
	 *
	 * @param statement the statement which created this ResultSet
	 * @param columns the column names
	 * @param types the column types
	 * @param results the number of rows in the ResultSet
	 * @throws IllegalArgumentException if communicating with monet failed
	 */
	MonetResultSet(Statement statement, String[] columns, String[] types, int[] JdbcSQLTypes, int results)
			throws IllegalArgumentException {
		if (statement == null) {
			throw new IllegalArgumentException("Statement may not be null!");
		}
		if (columns == null || types == null) {
			throw new IllegalArgumentException("One of the given arguments is null!");
		}
		if (columns.length != types.length) {
			throw new IllegalArgumentException("Given arguments are not the same size!");
		}
		if (results < 0) {
			throw new IllegalArgumentException("Negative rowcount not allowed!");
		}
		this.statement = statement;
		this.header = null;
		this.fetchSize = 0;
		this.tupleCount = results;
		this.columns = columns;
		this.types = types;
		this.JdbcSQLTypes = JdbcSQLTypes;
	}

	private boolean setLastNullValue(int columnIndex) {
		this.lastReadWasNull = currentBlock.checkValueIsNull(columnIndex);
		return this.lastReadWasNull;
	}

	//== methods of interface ResultSet

	// Chapter 14.2.2 Sun JDBC 3.0 Specification

	/**
	 * Moves the cursor to the given row number in this ResultSet object.
	 *
	 * If the row number is positive, the cursor moves to the given row number with respect to the beginning of the
     * result set. The first row is row 1, the second is row 2, and so on.
	 *
	 * If the given row number is negative, the cursor moves to an absolute row position with respect to the end of the
     * result set. For example, calling the method absolute(-1) positions the cursor on the last row; calling the
	 * method absolute(-2) moves the cursor to the next-to-last row, and so on.
	 *
	 * An attempt to position the cursor beyond the first/last row in the result set leaves the cursor before the first
     * row or after the last row.
	 * Note: calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as calling last().
	 *
	 * @param row the number of the row to which the cursor should move. A positive number indicates the row number
	 *        counting from the beginning of the result set; a negative number indicates the row number counting from
	 *        the end of the result set
	 * @return true if the cursor is on the result set; false otherwise
	 * @throws SQLException if a database access error occurs, or the result set type is TYPE_FORWARD_ONLY
	 */
	@Override
	public boolean absolute(int row) throws SQLException {
		if (row != curRow + 1 && type == TYPE_FORWARD_ONLY)
			throw new SQLException("(Absolute) positioning not allowed on forward only result sets!", "M1M05");

		if (header.isClosed())
			throw new SQLException("ResultSet is closed!", "M1M20");

		// first calculate what the JDBC row is
		if (row < 0) {
			// calculate the negatives...
			row = tupleCount + row + 1;
		}
		// now place the row not farther than just before or after the result
		if (row < 0) {
			row = 0; // before first
		} else if (row > tupleCount + 1) {
			row = tupleCount + 1; // after last
		}
		// store it
		this.curRow = row;
		this.currentBlock = header.getDataBlockCorrespondingToLine(row - 1);
		return this.curRow <= this.tupleCount;
	}

	/**
	 * Moves the cursor to the end of this ResultSet object, just after the last row. This method has no effect if the
	 * result set contains no rows.
	 *
	 * @throws SQLException if a database access error occurs or the result set type is TYPE_FORWARD_ONLY
	 */
	@Override
	public void afterLast() throws SQLException {
		absolute(tupleCount + 1);
	}

	/**
	 * Moves the cursor to the front of this ResultSet object, just before the first row. This method has no effect
	 * if the result set contains no rows.
	 *
	 * @throws SQLException if a database access error occurs or the result set type is TYPE_FORWARD_ONLY
	 */
	@Override
	public void beforeFirst() throws SQLException {
		absolute(0);
	}

	/**
	 * Clears all warnings reported for this ResultSet object. After a call to this method, the method getWarnings
	 * returns null until a new warning is reported for this ResultSet object.
	 */
	@Override
	public void clearWarnings() {
		warnings = null;
	}

	/**
	 * Releases this ResultSet object's database (and JDBC) resources immediately instead of waiting for this to happen
	 * when it is automatically closed.
	 */
	@Override
	public void close() {
		if (header != null && !header.isClosed()) {
			header.close();
		}
		if (statement instanceof MonetStatement) {
			((MonetStatement)statement).closeIfCompletion();
		}
	}

	// Chapter 14.2.3 from Sun JDBC 3.0 specification

	/**
	 * Maps the given ResultSet column name to its ResultSet column index. Column names supplied to getter methods are
	 * case insensitive. If a select list contains the same column more than once, the first instance of the
	 * column will be returned.
	 *
	 * @param columnName the name of the column
	 * @return the column index of the given column name
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public int findColumn(String columnName) throws SQLException {
		if (columnName != null) {
			final int array_size = columns.length;
			for (int i = 0; i < array_size; i++) {
				if (columnName.equals(columns[i]))
					return i + 1;
			}
			/* if an exact match did not succeed try a case insensitive match */
			for (int i = 0; i < array_size; i++) {
				if (columnName.equalsIgnoreCase(columns[i]))
					return i + 1;
			}
		}
		throw new SQLException("No such column name: " + columnName, "M1M05");
	}

	/**
	 * Moves the cursor to the first row in this ResultSet object.
	 *
	 * @return true if the cursor is on a valid row; false if there are no rows in the result set
	 * @throws SQLException - if a database access error occurs or the result set type is TYPE_FORWARD_ONLY
	 */
	@Override
	public boolean first() throws SQLException {
		return absolute(1);
	}

	@Override
	public Array getArray(int columnIndex) throws SQLException {
		throw newSQLFeatureNotSupportedException("getArray");
	}

	@Override
	public Array getArray(String colName) throws SQLException {
		throw newSQLFeatureNotSupportedException("getArray");
	}

	@Override
	public InputStream getAsciiStream(int columnIndex) throws SQLException {
        try {
            if(setLastNullValue(columnIndex - 1)) {
                return null;
            }
            switch (JdbcSQLTypes[columnIndex - 1]) {
                case Types.BLOB:
                    return getClob(columnIndex).getAsciiStream();
                case Types.CLOB:
                    return getClob(columnIndex).getAsciiStream();
                case Types.LONGVARBINARY:
                case Types.CHAR:
                case Types.VARCHAR:
                case Types.LONGVARCHAR:
                    return new ByteArrayInputStream(getBytes(columnIndex));
                default:
                    throw new SQLException("Conversion from " + types[columnIndex - 1] +
                            " to ascii stream not supported", "M1M05");
            }
        } catch (ClassCastException ex) {
            throw new SQLException(ex.getMessage());
        } catch (IndexOutOfBoundsException e) {
            throw newSQLInvalidColumnIndexException(columnIndex);
        }
	}

	@Override
	public InputStream getAsciiStream(String columnName) throws SQLException {
        return getAsciiStream(findColumn(columnName));
	}

	@Override
	@Deprecated
	public InputStream getUnicodeStream(int columnIndex) throws SQLException {
		throw newSQLFeatureNotSupportedException("getUnicodeStream");
	}

	@Override
	@Deprecated
	public InputStream getUnicodeStream(String columnName) throws SQLException {
		throw newSQLFeatureNotSupportedException("getUnicodeStream");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of
     * uninterpreted bytes. The value can then be read in chunks from the stream. This method is particularly suitable
     * for retrieving large LONGVARBINARY values.
	 * <br/><br/>
	 * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next
     * call to a getter method implicitly closes the stream. Also, a stream may return 0 when the method
     * InputStream.available  is called whether there is data available or not.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the
     * value is SQL NULL, the value returned is null
	 * @throws SQLException if the columnIndex is not valid; if a database access error occurs or this method is called
     * on a closed result set
	 */
	@Override
	public InputStream getBinaryStream(int columnIndex) throws SQLException {
        try {
            if(setLastNullValue(columnIndex - 1)) {
                return null;
            }
            switch (JdbcSQLTypes[columnIndex - 1]) {
                case Types.BLOB:
                    return getBlob(columnIndex).getBinaryStream();
                case Types.LONGVARBINARY:
                    return new ByteArrayInputStream(getBytes(columnIndex));
                default:
                    throw new SQLException("Conversion from " + types[columnIndex - 1] +
                            " to binary stream not supported", "M1M05");
            }
        } catch (ClassCastException ex) {
            throw new SQLException(ex.getMessage());
        } catch (IndexOutOfBoundsException e) {
            throw newSQLInvalidColumnIndexException(columnIndex);
        }
	}
	
	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a stream of
     * uninterpreted bytes. The value can then be read in chunks from the stream. This method is particularly suitable
     * for retrieving large LONGVARBINARY  values.
     * <br/><br/>
	 * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next
     * call to a getter method implicitly closes the stream. Also, a stream may return 0 when the method available is
     * called whether there is data available or not.
	 *
	 * @param columnName the label for the column specified with the SQL AS clause. If the SQL AS clause was not
     * specified, then the label is the name of the column
	 * @return a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the
     * value is SQL NULL, the result is null
	 * @throws SQLException if the columnLabel is not valid; if a database access error occurs or this method is called
     * on a closed result set
	 */
	@Override
	public InputStream getBinaryStream(String columnName) throws SQLException {
		return getBinaryStream(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader
	 * object.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned
	 * is null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Reader getCharacterStream(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			return new StringReader(currentBlock.getValueAsString(columnIndex - 1));
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader
	 * object.
	 *
	 * @param columnName the name of the column
	 * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is
     * null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Reader getCharacterStream(String columnName) throws SQLException {
		return getCharacterStream(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader
     * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is
     * null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public Reader getNCharacterStream(int columnIndex) throws SQLException {
		throw newSQLFeatureNotSupportedException("getNCharacterStream");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.io.Reader
     * object. It is intended for use when accessing NCHAR, NVARCHAR and LONGNVARCHAR columns.
	 *
	 * @param columnName the name of the column
	 * @return a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is
     * null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 * @throws SQLFeatureNotSupportedException the JDBC driver does
	 *         not support this method
	 */
	@Override
	public Reader getNCharacterStream(String columnName) throws SQLException {
		throw newSQLFeatureNotSupportedException("getNCharacterStream");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a Blob object in
	 * the Java programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return a Blob object representing the SQL BLOB value in the specified column
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Blob getBlob(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			return (MonetBlob) currentBlock.getObjectValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a Blob object in the
     * Java programming language.
	 *
	 * @param colName the name of the column from which to retrieve the value
	 * @return a Blob object representing the SQL BLOB value in the specified column
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Blob getBlob(String colName) throws SQLException {
		return getBlob(findColumn(colName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a Clob object in the
	 * Java programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return a Clob object representing the SQL CLOB value in the specified column
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Clob getClob(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			return (MonetClob) currentBlock.getObjectValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a Clob object in the
	 * Java programming language.
	 *
	 * @param colName the name of the column from which to retrieve the value
	 * @return a Clob object representing the SQL CLOB value in the specified column
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Clob getClob(String colName) throws SQLException {
		return getClob(findColumn(colName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a NClob object in the
	 * Java programming language.
	 *
	 * @param i the first column is 1, the second is 2, ...
	 * @return a NClob object representing the SQL NCLOB value in the specified column
	 * @throws SQLException if a database access error occurs
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public NClob getNClob(int i) throws SQLException {
		throw newSQLFeatureNotSupportedException("getNClob");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a NClob object in the
	 * Java programming language.
	 *
	 * @param colName the name of the column from which to retrieve the value
	 * @return a NClob object representing the SQL NCLOB value in the specified column
	 * @throws SQLException if a database access error occurs
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public NClob getNClob(String colName) throws SQLException {
		throw newSQLFeatureNotSupportedException("getNClob");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.math.BigDecimal with full precision.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value (full precision); if the value is SQL NULL,
	 *         the value returned is null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			return (BigDecimal) currentBlock.getObjectValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.math.BigDecimal with full precision.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @param scale the number of digits to the right of the decimal point
	 * @return the column value (full precision); if the value is SQL NULL,
	 *         the value returned is null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	@Deprecated
	public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			BigDecimal val = (BigDecimal) currentBlock.getObjectValue(columnIndex - 1);
			val.setScale(scale);
			return val;
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.math.BigDecimal with full precision.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value (full precision); if the value is SQL NULL,
	 *         the value returned is null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public BigDecimal getBigDecimal(String columnName) throws SQLException {
		return getBigDecimal(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.math.BigDecimal with full precision.
	 *
	 * @param columnName the SQL name of the column
	 * @param scale the number of digits to the right of the decimal point
	 * @return the column value (full precision); if the value is SQL NULL,
	 *         the value returned is null in the Java programming language.
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	@Deprecated
	public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
		return getBigDecimal(findColumn(columnName), scale);
	}

	// See Sun JDBC Specification 3.0 Table B-6
	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a boolean in the Java
     * programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is false
	 * @throws SQLException if there is no such column
	 */
	@Override
	public boolean getBoolean(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return false; // if the value is SQL NULL, the value returned is false
			}
			// match type specific values
			switch (JdbcSQLTypes[columnIndex - 1]) {
				case Types.BOOLEAN:
					return currentBlock.getBooleanValue(columnIndex - 1);
				case Types.CHAR:
				case Types.VARCHAR:
				case Types.LONGVARCHAR:
				case Types.CLOB:
					String val = currentBlock.getValueAsString(columnIndex - 1);
					if ("false".equalsIgnoreCase(val) || "0".equals(val))
						return false;
					if ("true".equalsIgnoreCase(val) || "1".equals(val))
						return true;
					throw newSQLInvalidColumnIndexException(columnIndex);
				case Types.TINYINT:
					return getByte(columnIndex) != 0;
				case Types.SMALLINT:
					return getShort(columnIndex) != 0;
				case Types.INTEGER:
					return getInt(columnIndex) != 0;
				case Types.BIGINT:
					return getLong(columnIndex) != 0L;
				case Types.REAL:
					return getFloat(columnIndex) != 0.0f;
				case Types.DOUBLE:
					return getDouble(columnIndex) != 0.0d;
				case Types.NUMERIC:
				    BigInteger huge = (BigInteger) currentBlock.getValueAsObject(columnIndex - 1);
                    return huge.compareTo(BigInteger.ZERO) != 0;
				case Types.DECIMAL:
					return getBigDecimal(columnIndex).compareTo(BigDecimal.ZERO) != 0;
				default: //OTHERS, BLOB, LONGVARBINARY, TIME...
					throw new SQLException("Conversion from " + types[columnIndex - 1] +
							" to boolean type not supported", "M1M05");
			}
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a boolean in the Java
	 * programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is false
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public boolean getBoolean(String columnName) throws SQLException {
		return getBoolean(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a byte in the Java
	 * programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public byte getByte(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return 0;
			}
			return currentBlock.getByteValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a byte in the Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned
	 *         is 0
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public byte getByte(String columnName) throws SQLException {
		return getByte(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a byte array in the Java programming language. The
	 * bytes represent the raw values returned by the driver.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public byte[] getBytes(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			// According to Table B-6, getBytes() only operates on BINARY types
			switch (JdbcSQLTypes[columnIndex - 1]) {
				case Types.BLOB:
					return ((MonetBlob) currentBlock.getObjectValue(columnIndex - 1)).getBuffer();
				case Types.LONGVARBINARY:
					// unpack the HEX (BLOB) notation to real bytes
					return (byte[]) currentBlock.getObjectValue(columnIndex - 1);
				default:
					throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05");
			}
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a byte array in the
	 * Java programming language. The bytes represent the raw values returned by the driver.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public byte[] getBytes(String columnName) throws SQLException {
		return getBytes(findColumn(columnName));
	}

	/**
	 * Retrieves the concurrency mode of this ResultSet object. The concurrency used is determined by the Statement
	 * object that created the result set.
	 *
	 * NOTE: MonetDB only supports read-only result sets, and will always return ResultSet.CONCUR_READ_ONLY
	 *
	 * @return the concurrency type, either ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE
	 */
	@Override
	public int getConcurrency() {
		return concurrency;
	}

	/**
	 * Retrieves the name of the SQL cursor used by this ResultSet object.
	 * In SQL, a result table is retrieved through a cursor that is named.
	 * For MonetDB this is the header.id returned in a resultset header. The
	 * current row of a result set can be updated or deleted using a positioned
	 * update/delete statement that references the cursor name. To insure that
	 * the cursor has the proper isolation level to support update, the
	 * cursor's SELECT statement should be of the form SELECT FOR UPDATE. If
	 * FOR UPDATE is omitted, the positioned updates may fail.
	 *
	 * The JDBC API supports this SQL feature by providing the name of the SQL
	 * cursor used by a ResultSet object. The current row of a ResultSet object
	 * is also the current row of this SQL cursor.
	 *
	 * Note: If positioned update is not supported, a SQLException is thrown. MonetDB currently doesn't support updates,
	 * so the SQLException is thrown for now.
	 *
	 * @return the SQL name for this ResultSet object's cursor
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public String getCursorName() throws SQLException {
		throw new SQLException("Positioned updates not supported for this " +
				   "cursor (" + (header != null ? header.getId() : "") + ")", "0AM21");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a double in the Java programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if there is no such column
	 */
	@Override
	public double getDouble(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return 0.0d;
			}
			return currentBlock.getDoubleValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a double in the Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public double getDouble(String columnName) throws SQLException {
		return getDouble(findColumn(columnName));
	}

	/**
	 * Retrieves the holdability of this ResultSet object.
	 *
	 * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public int getHoldability() throws SQLException {
		return getStatement().getConnection().getHoldability();
	}

	/**
	 * Retrieves the fetch direction for this ResultSet object.
	 * <b>currently not implemented</b>
	 *
	 * @return the current fetch direction for this ResultSet object
	 */
	@Override
	public int getFetchDirection() {
		return ResultSet.FETCH_FORWARD;
	}

	/**
	 * Gives a hint as to the direction in which the rows in this ResultSet
	 * object will be processed. The initial value is determined by the
	 * Statement object that produced this ResultSet object.
	 * The fetch direction may be changed at any time.
	 * <b>currently not implemented</b>
	 *
	 * @param direction - an int specifying the suggested fetch direction;
	 * one of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN
	 */
	@Override
	public void setFetchDirection(int direction) throws SQLException {
		switch (direction) {
			case ResultSet.FETCH_FORWARD:
				break;
			case ResultSet.FETCH_REVERSE:
			case ResultSet.FETCH_UNKNOWN:
				throw new SQLException("Not supported direction " + direction, "0A000");
			default:
				throw new SQLException("Illegal direction: " + direction, "M1M05");
		}
	}

	/**
	 * Retrieves the fetch size for this ResultSet object.
	 *
	 * @return the current fetch size for this ResultSet object
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public int getFetchSize() throws SQLException {
		return fetchSize;
	}

	/**
	 * Gives the JDBC driver a hint as to the number of rows that should be
	 * fetched from the database when more rows are needed.  In MonetDB, this is
	 * actually a no-op, because even before a MonetResultSet object is
	 * created, the fetch size is already determined in the
	 * MonetConnection.ResultSetResponse passed to its constructor.  Since all
	 * data blocks for this whole result set are already allocated in
	 * MonetConnection.ResultSetResponse, it is too complicated and error-prone
	 * to still change the fetchSize here.  If one really needs to overwrite
	 * the default fetchSize, please use MonetStatement.setFetchSize() instead.
	 *
	 * @param rows the number of rows to fetch
	 * @throws SQLException if the condition 0 &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 {
			if(setLastNullValue(columnIndex - 1)) {
				return 0.0f;
			}
			return currentBlock.getFloatValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a float in the Java
	 * programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public float getFloat(String columnName) throws SQLException {
		return getFloat(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as an int in the
	 * Java programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if there is no such column
	 */
	@Override
	public int getInt(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return 0;
			}
			return currentBlock.getIntValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as an int in the
	 * Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public int getInt(String columnName) throws SQLException {
		return getInt(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a long in the Java
	 * programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if there is no such column
	 */
	@Override
	public long getLong(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return 0L;
			}
			return currentBlock.getLongValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a long in the Java
	 * programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public long getLong(String columnName) throws SQLException {
		return getLong(findColumn(columnName));
	}

	/* helper for the anonymous class inside getMetaData */
	private abstract class rsmdw extends MonetWrapper implements ResultSetMetaData {}

	/**
	 * Retrieves the number, types and properties of this ResultSet object's columns.
	 *
	 * @return the description of this ResultSet object's columns
	 */
	@Override
	public ResultSetMetaData getMetaData() {
		// return inner class which implements the ResultSetMetaData interface
		return new rsmdw() {
			// for the more expensive methods (getPrecision(), getScale(), isNullable()), we provide a simple cache
			// caches to store precision, scale and isNullable values from getColumns()
			final int array_size = columns.length + 1;  // add 1 as in JDBC columns start from 1 (array from 0).
			private boolean[] _is_fetched	= new boolean[array_size];
			private int[] _precision	= new int[array_size];
			private int[] _scale		= new int[array_size];
			private int[] _isNullable	= new int[array_size];
			private boolean[] _isAutoincrement = new boolean[array_size];
			private Connection conn = null;
			private DatabaseMetaData dbmd = null;

			/**
			 * A private method to fetch the precision, scale, isNullable and isAutoincrement value for a fully qualified column.
			 * As md.getColumns() is an expensive method we call it only once per column
			 * and cache the precision, scale, isNullable and isAutoincrement values in the above array chaches.
			 * Also we only call md.getColumns() when we have a non empty schema name and table name and column name.
			 */
			private void fetchColumnInfo(int column) throws SQLException {
				if (column <= 0 || column > columns.length)
					throw newSQLInvalidColumnIndexException(column);

				_is_fetched[column] = true;
				_precision[column] = 0;
				_scale[column] = 0;
				_isNullable[column] = columnNullableUnknown;
				_isAutoincrement[column] = false;

				// we can only call dbmd.getColumns() when we have a specific schema name and table name and column name
				String schName = getSchemaName(column);
				if (schName != null && !schName.isEmpty()) {
					String tblName = getTableName(column);
					if (tblName != null && !tblName.isEmpty()) {
						String colName = getColumnName(column);
						if (colName != null && !colName.isEmpty()) {
							if (conn == null) {
								// first time, get a Connection object and cache it for all next columns
								conn = getStatement().getConnection();
							}
							if (conn != null && dbmd == null) {
								// first time, get a MetaData object and cache it for all next columns
								dbmd = conn.getMetaData();
							}
							if (dbmd != null) {
								// for precision, scale, isNullable and isAutoincrement we query the information from data dictionary
								ResultSet colInfo = dbmd.getColumns(null, schName, tblName, colName);
								if (colInfo != null) {
									// we expect exactly one row in the resultset
									if (colInfo.next()) {
										_precision[column] = colInfo.getInt(7);  // col 7 is "COLUMN_SIZE"
										_scale[column] = colInfo.getInt(9);  // col 9 is "DECIMAL_DIGITS"
										_isNullable[column] = colInfo.getInt(11);  // col 11 is "NULLABLE"
										String strVal = colInfo.getString(23);  // col 23 is "IS_AUTOINCREMENT"
										if (strVal != null && "YES".equals(strVal))
											_isAutoincrement[column] = true;
									}
									colInfo.close();  // close the resultset to release resources
								}
							}
						}
					}
				}
			}

			/**
			 * Returns the number of columns in this ResultSet object.
			 *
			 * @return the number of columns
			 */
			@Override
			public int getColumnCount() {
				return columns.length;
			}

			/**
			 * Indicates whether the designated column is automatically numbered.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return true if so; false otherwise
			 * @throws SQLException if a database access error occurs
			 */
			@Override
			public boolean isAutoIncrement(int column) throws SQLException {
				try {
					if (!_is_fetched[column]) {
						fetchColumnInfo(column);
					}
					return _isAutoincrement[column];
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

			/**
			 * Indicates whether a column's case matters.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return true for all character string columns else false
			 */
			@Override
			public boolean isCaseSensitive(int column) throws SQLException {
				switch (getColumnType(column)) {
					case Types.CLOB:
					case Types.CHAR:
					case Types.VARCHAR:
					case Types.LONGVARCHAR:
						return true;
					default:
						return false;
				}
			}

			/**
			 * Indicates whether the designated column can be used in a
			 * where clause.
			 * It is unknown to me what kind ot columns they regard to,
			 * as I think all columns are useable in a where clause.
			 * Returning true for all here, for the time being.
			 * Possible thought; maybe they want to know here if it's a
			 * real column existing in a table or not...
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return true
			 */
			@Override
			public boolean isSearchable(int column) {
				return true;
			}

			/**
			 * Indicates whether the designated column is a cash value.
			 * From the MonetDB database perspective it is by definition
			 * unknown whether the value is a currency, because there are
			 * no currency datatypes such as MONEY.  With this knowledge
			 * we can always return false here.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return false
			 */
			@Override
			public boolean isCurrency(int column) {
				return false;
			}
			
			/**
			 * Indicates whether values in the designated column are signed numbers.
			 * Within MonetDB all numeric types (except oid and ptr) are signed.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return true if so; false otherwise
			 */
			@Override
			public boolean isSigned(int column) throws SQLException {
				switch (getColumnType(column)) {
					case Types.TINYINT:
					case Types.SMALLINT:
					case Types.INTEGER:
					case Types.REAL:
					case Types.DOUBLE:
					case Types.BIGINT:
					case Types.NUMERIC:
					case Types.DECIMAL:
						return true;
					default:
						return false;
				}
			}

			/**
			 * Indicates the designated column's normal maximum width in characters.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return the normal maximum number of characters allowed as the width of the designated column
			 * @throws SQLException if there is no such column
			 */
			@Override
			public int getColumnDisplaySize(int column) throws SQLException {
				int ret = 1;
				if (header != null) {
					try {
						ret = header.getColumnLengths()[column - 1];
					} catch (IndexOutOfBoundsException e) {
						throw newSQLInvalidColumnIndexException(column);
					}
				}
				return ret;
			}

			/**
			 * Get the designated column's schema name.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return schema name or "" if not applicable
			 * @throws SQLException if a database access error occurs
			 */
			@Override
			public String getSchemaName(int column) throws SQLException {
				if (header != null) {
					// figure the name out
					try {
						String schema = header.getTableNames()[column - 1];
						if (schema != null) {
							int dot = schema.indexOf('.');
							return (dot >= 0) ? schema.substring(0, dot) : "";
						}
					} catch (IndexOutOfBoundsException e) {
						throw newSQLInvalidColumnIndexException(column);
					}
				}
				return "";
			}

			/**
			 * Gets the designated column's table name.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return table name or "" if not applicable
			 */
			@Override
			public String getTableName(int column) throws SQLException {
				if (header != null) {
					// figure the name out
					try {
						String table = header.getTableNames()[column - 1];
						if (table != null) {
							int dot = table.indexOf('.');
							return (dot >= 0) ? table.substring(dot + 1) : table;
						}
					} catch (IndexOutOfBoundsException e) {
						throw newSQLInvalidColumnIndexException(column);
					}
				}
				return "";
			}

			/**
			 * Get the designated column's number of decimal digits. This method is currently very expensive as it needs
			 * to retrieve the information from the database using an SQL query.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return precision
			 * @throws SQLException if a database access error occurs
			 */
			@Override
			public int getPrecision(int column) throws SQLException {
				try {
					if (!_is_fetched[column]) {
						fetchColumnInfo(column);
					}
					if (_precision[column] == 0) {
						// apparently no precision could be fetched use columnDisplaySize() value for variable
						// length data types
						switch (getColumnType(column)) {
							case Types.CHAR:
							case Types.VARCHAR:
							case Types.LONGVARCHAR:
							case Types.CLOB:
							case Types.LONGVARBINARY:
							case Types.BLOB:
							case Types.NUMERIC:
							case Types.DECIMAL:
								_precision[column] = getColumnDisplaySize(column);
								break;
							case Types.TINYINT:
								_precision[column] = 3;
								break;
							case Types.SMALLINT:
								_precision[column] = 5;
								break;
							case Types.INTEGER:
								_precision[column] = 10;
								break;
							case Types.BIGINT:
								_precision[column] = 19;
								break;
							case Types.REAL:
								_precision[column] = 7;
								break;
							case Types.DOUBLE:
								_precision[column] = 15;
								break;
							case Types.BOOLEAN:
								_precision[column] = 5;
								break;
							case Types.DATE:
								_precision[column] = 10;
								break;
							case Types.TIME:
								_precision[column] = 8;
								break;
							case Types.TIMESTAMP:
								_precision[column] = 19;
								break;
							default:
								_precision[column] = 30;
								break;
						}
					}
					return _precision[column];
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

			/**
			 * Gets the designated column's number of digits to right of
			 * the decimal point.  This method is currently very
			 * expensive as it needs to retrieve the information from
			 * the database using an SQL query.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return scale
			 * @throws SQLException if a database access error occurs
			 */
			@Override
			public int getScale(int column) throws SQLException {
				try {
					if (!_is_fetched[column]) {
						fetchColumnInfo(column);
					}
					return _scale[column];
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

			/**
			 * Indicates the nullability of values in the designated
			 * column.  This method is currently very expensive as it
			 * needs to retrieve the information from the database using
			 * an SQL query.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return the nullability status of the given column; one of columnNoNulls, columnNullable or
			 * columnNullableUnknown
			 * @throws SQLException if a database access error occurs
			 */
			@Override
			public int isNullable(int column) throws SQLException {
				try {
					if (!_is_fetched[column]) {
						fetchColumnInfo(column);
					}
					return _isNullable[column];
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

			/**
			 * Gets the designated column's table's catalog name.
			 * MonetDB does not support the catalog naming concept as in: catalog.schema.table naming scheme
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return the name of the catalog for the table in which the given
			 *         column appears or "" if not applicable
			 */
			@Override
			public String getCatalogName(int column) throws SQLException {
				return null; // MonetDB does NOT support catalogs
			}

			/**
			 * Indicates whether the designated column is definitely not writable.  MonetDB does not support
			 * cursor updates, so nothing is writable.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return true if so; false otherwise
			 */
			@Override
			public boolean isReadOnly(int column) {
				return true;
			}

			/**
			 * Indicates whether it is possible for a write on the designated column to succeed.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return true if so; false otherwise
			 */
			@Override
			public boolean isWritable(int column) {
				return false;
			}

			/**
			 * Indicates whether a write on the designated column will definitely succeed.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return true if so; false otherwise
			 */
			@Override
			public boolean isDefinitelyWritable(int column) {
				return false;
			}

			/**
			 * Returns the fully-qualified name of the Java class whose
			 * instances are manufactured if the method
			 * ResultSet.getObject is called to retrieve a value from
			 * the column.  ResultSet.getObject may return a subclass of
			 * the class returned by this method.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return the fully-qualified name of the class in the Java
			 *         programming language that would be used by the method
			 *         ResultSet.getObject to retrieve the value in the
			 *         specified column. This is the class name used for custom
			 *         mapping.
			 * @throws SQLException if there is no such column
			 */
			@Override
			public String getColumnClassName(int column) throws SQLException {
				if (conn == null) {
					// first time, get a Connection object and cache it for all next columns
					conn = getStatement().getConnection();
				}
				try {
					String MonetDBType = types[column - 1];
					Class<?> type = null;
					if (conn != null) {
						Map<String,Class<?>> map = conn.getTypeMap();
						if (map != null && map.containsKey(MonetDBType)) {
							type = map.get(MonetDBType);
						}
					}
					if (type == null) {
						// fallback to the standard SQL type Class mappings
						type = getClassForType(JdbcSQLTypes[column - 1]);
					}
					if (type != null) {
						return type.getName();
					}
					throw new SQLException("column type mapping null: " + MonetDBType, "M0M03");
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

			/**
			 * Gets the designated column's suggested title for use in printouts and displays. This is currently equal
			 * to getColumnName().
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return the suggested column title
			 * @throws SQLException if there is no such column
			 */
			@Override
			public String getColumnLabel(int column) throws SQLException {
				return getColumnName(column);
			}

			/**
			 * Gets the designated column's name.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return the column name
			 * @throws SQLException if there is no such column
			 */
			@Override
			public String getColumnName(int column) throws SQLException {
				try {
					return columns[column - 1];
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

			/**
			 * Retrieves the designated column's SQL type.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return SQL type from java.sql.Types
			 * @throws SQLException if there is no such column
			 */
			@Override
			public int getColumnType(int column) throws SQLException {
				try {
					return JdbcSQLTypes[column - 1];
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

			/**
			 * Retrieves the designated column's database-specific type name.
			 *
			 * @param column the first column is 1, the second is 2, ...
			 * @return type name used by the database. If the column type is a user-defined type, then a
			 * fully-qualified type name is returned.
			 * @throws SQLException if there is no such column
			 */
			@Override
			public String getColumnTypeName(int column) throws SQLException {
				try {
					return types[column - 1];
				} catch (IndexOutOfBoundsException e) {
					throw newSQLInvalidColumnIndexException(column);
				}
			}

		};	// end of new rsmdw()
	}	// end of getMetaData()

	/**
	 * Gets the value of the designated column in the current row of this
	 * ResultSet object as an Object in the Java programming language.
	 *
	 * This method will return the value of the given column as a Java object.
	 * The type of the Java object will be the default Java object type
	 * corresponding to the column's SQL type, following the mapping for
	 * built-in types specified in the JDBC specification. If the value is
	 * an SQL NULL, the driver returns a Java null.
	 *
	 * This method may also be used to read database-specific abstract data
	 * types. In the JDBC 2.0 API, the behavior of method getObject is extended
	 * to materialize data of SQL user-defined types. When a column contains a
	 * structured or distinct value, the behavior of this method is as if it
	 * were a call to: getObject(columnIndex, this.getStatement().getInternalConnection().getTypeMap()).
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return a java.lang.Object holding the column value or null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Object getObject(int columnIndex) throws SQLException {
		// Many generic JDBC programs use getObject(colnr) to retrieve value objects from a resultset. For speed the
		// implementation should be as fast as possible, so avoid method calls (by inlining code) where possible
		final int JdbcType;
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			JdbcType = JdbcSQLTypes[columnIndex - 1];
			switch(JdbcType) {
				case Types.BOOLEAN:
				case Types.TINYINT:
				case Types.SMALLINT:
				case Types.INTEGER:
				case Types.BIGINT:
				case Types.REAL:
				case Types.DOUBLE:
				case Types.DECIMAL:
				case Types.NUMERIC:
				case Types.CHAR:
				case Types.VARCHAR:
				case Types.LONGVARCHAR:
				case Types.CLOB:
				case Types.LONGVARBINARY:
				case Types.BLOB:
					return currentBlock.getValueAsObject(columnIndex - 1);
				case Types.DATE:
					return getDate(columnIndex);
				case Types.TIME:
					return getTime(columnIndex);
				case Types.TIMESTAMP:
					return getTimestamp(columnIndex);
				case Types.OTHER: {
					// The MonetDB types: inet, json, url and uuid are all mapped to Types.OTHER in MonetDriver.typeMap
					// For these MonetDB types (except json, see comments below) we try to create objects of the corresponding class.
					String MonetDBType = types[columnIndex - 1];
					String val = currentBlock.getValueAsString(columnIndex - 1);
					switch (MonetDBType.length()) {
						case 3:
							if ("url".equals(MonetDBType)) {
								try {
									return new URL(val);
								} catch (Exception exc) {
									// ignore exception and just return the val String object
									return val;
								}
							}
							break;
						case 4:
							if ("inet".equals(MonetDBType)) {
								try {
									return new MonetINET(val);
								} catch (Exception exc) {
									// ignore exception and just return the val String object
									return val;
								}
							} else
							if ("uuid".equals(MonetDBType)) {
								try {
									return UUID.fromString(val);
								} catch (IllegalArgumentException exc) {
									// ignore exception and just return the val String object
									return val;
								}
//							} else if ("json".equals(MonetDBType)) { //the same happens for geometry
								// There is no support for JSON in standard java class libraries.
								// Possibly we could use org.json.simple.JSONObject or other/faster libs
								// javax.json.Json is not released yet (see https://json-processing-spec.java.net/)
								// see also https://github.com/fabienrenaud/java-json-benchmark
								// Note that it would make our JDBC driver dependent of an external jar
								// and we don't want that so simply return it as String object
							}
							break;
					}
					return val;
				}
				default:
					// When we get here the column type is a non-standard JDBC SQL type, possibly a User Defined Type.
					// Just call getObject(int, Map) for those rare cases.
					return getObject(columnIndex, this.getStatement().getConnection().getTypeMap());
			}
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	private boolean classImplementsSQLData(Class<?> cl) {
		Class<?>[] cls = cl.getInterfaces();
		for (Class<?> cl1 : cls) {
			if (cl1 == SQLData.class)
				return true;
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	private Object getObjectFromClass(int columnIndex, Class<?> type) throws SQLException {
		if(setLastNullValue(columnIndex - 1)) {
			return null;
		}
		if (type == null) {
			// fallback to the standard Class mappings
			type = getClassForType(JdbcSQLTypes[columnIndex - 1]);
		}
		if (type == null || type == String.class) {
			return currentBlock.getValueAsString(columnIndex - 1);
		} else if (type == BigInteger.class) {
			return getObject(columnIndex);
		} else if (type == BigDecimal.class) {
			return getBigDecimal(columnIndex);
		} else if (type == Boolean.class) {
			return getBoolean(columnIndex);
		} else if (type == Byte.class) {
			return getByte(columnIndex);
		} else if (type == Short.class) {
			return getShort(columnIndex);
		} else if (type == Integer.class) {
			return getInt(columnIndex);
		} else if (type == Long.class) {
			return getLong(columnIndex);
		} else if (type == Float.class) {
			return getFloat(columnIndex);
		} else if (type == Double.class) {
			return getDouble(columnIndex);
		} else if (type == byte[].class) {
			return getBytes(columnIndex);
		} else if (type == Date.class) {
			return getDate(columnIndex);
		} else if (type == Time.class) {
			return getTime(columnIndex);
		} else if (type == Timestamp.class) {
			return getTimestamp(columnIndex);
		} else if (type == Clob.class) {
			return getClob(columnIndex);
		} else if (type == Blob.class) {
			return getBlob(columnIndex);
		} else if (classImplementsSQLData(type)) {
			SQLData x;
			try {
				Constructor<? extends SQLData> ctor = ((Class)type).getConstructor();
				x = ctor.newInstance();
			} catch (NoSuchMethodException | InstantiationException | InvocationTargetException
					| SecurityException | IllegalAccessException nsme) {
				throw new SQLException(nsme.getMessage(), "M0M27");
			}
			final int colnum = columnIndex;
			final boolean valwasnull = wasNull();
			SQLInput input = new SQLInput() {
				@Override
				public String readString() throws SQLException {
					return getString(colnum);
				}

				@Override
				public boolean readBoolean() throws SQLException {
					return getBoolean(colnum);
				}

				@Override
				public byte readByte() throws SQLException {
					return getByte(colnum);
				}

				@Override
				public short readShort() throws SQLException {
					return getShort(colnum);
				}

				@Override
				public int readInt() throws SQLException {
					return getInt(colnum);
				}

				@Override
				public long readLong() throws SQLException {
					return getLong(colnum);
				}

				@Override
				public float readFloat() throws SQLException {
					return getFloat(colnum);
				}

				@Override
				public double readDouble() throws SQLException {
					return getDouble(colnum);
				}

				@Override
				public BigDecimal readBigDecimal() throws SQLException {
					return getBigDecimal(colnum);
				}

				@Override
				public byte[] readBytes() throws SQLException {
					return getBytes(colnum);
				}

				@Override
				public Date readDate() throws SQLException {
					return getDate(colnum);
				}

				@Override
				public Time readTime() throws SQLException {
					return getTime(colnum);
				}

				@Override
				public Timestamp readTimestamp() throws SQLException {
					return getTimestamp(colnum);
				}

				@Override
				public Reader readCharacterStream() throws SQLException {
					return getCharacterStream(colnum);
				}

				@Override
				public InputStream readAsciiStream() throws SQLException {
					return getAsciiStream(colnum);
				}

				@Override
				public InputStream readBinaryStream() throws SQLException {
					return getBinaryStream(colnum);
				}

				@Override
				public Object readObject() throws SQLException {
					return getObject(colnum);
				}

				@Override
				public Ref readRef() throws SQLException {
					return getRef(colnum);
				}

				@Override
				public Blob readBlob() throws SQLException {
					return getBlob(colnum);
				}

				@Override
				public Clob readClob() throws SQLException {
					return getClob(colnum);
				}

				@Override
				public Array readArray() throws SQLException {
					return getArray(colnum);
				}

				@Override
				public boolean wasNull() throws SQLException {
					return valwasnull;
				}

				@Override
				public URL readURL() throws SQLException {
					return getURL(colnum);
				}

				@Override
				public NClob readNClob() throws SQLException {
					return getNClob(colnum);
				}

				@Override
				public String readNString() throws SQLException {
					return getNString(colnum);
				}

				@Override
				public SQLXML readSQLXML() throws SQLException {
					return getSQLXML(colnum);
				}

				@Override
				public RowId readRowId() throws SQLException {
					return getRowId(colnum);
				}
			};
			x.readSQL(input, types[columnIndex - 1]);
			return x;
		} else {
			return currentBlock.getObjectValue(columnIndex - 1);
		}
	}

	/**
	 * Gets the value of the designated column in the current row of this
	 * ResultSet object as an Object in the Java programming language.
	 *
	 * This method will return the value of the given column as a Java object.
	 * The type of the Java object will be the default Java object type corresponding
	 * to the column's SQL type, following the mapping for built-in types specified
	 * in the JDBC specification.
	 * If the value is an SQL NULL, the driver returns a Java null.
	 *
	 * This method may also be used to read database-specific abstract data types.
	 * In the JDBC 2.0 API, the behavior of method getObject is extended to
	 * materialize data of SQL user-defined types.
	 *
	 * If Connection.getTypeMap does not throw a SQLFeatureNotSupportedException, then
	 * when a column contains a structured or distinct value, the behavior of this
	 * method is as if it were a call to: getObject(columnIndex,
	 * this.getStatement().getInternalConnection().getTypeMap()).
	 * If Connection.getTypeMap does throw a SQLFeatureNotSupportedException, then
	 * structured values are not supported, and distinct values are mapped to the
	 * default Java class as determined by the underlying SQL type of the DISTINCT type.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @param map a java.util.Map object that contains the mapping from SQL
	 *        type names to classes in the Java programming language
	 * @return an Object in the Java programming language representing the SQL value
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
		try {
			String MonetDBtype = types[columnIndex - 1];
			Class<?> type = null;
			if (map != null && map.containsKey(MonetDBtype)) {
				type = map.get(MonetDBtype);
			}
			return getObjectFromClass(columnIndex, type);
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row
	 * of this ResultSet object and will convert from the SQL type of
	 * the column to the requested Java data type, if the conversion is
	 * supported.  If the conversion is not supported or null is
	 * specified for the type, a SQLException is thrown.
	 *
	 * At a minimum, an implementation must support the conversions defined
	 * in Appendix B, Table B-3 and conversion of appropriate user defined
	 * SQL types to a Java type which implements SQLData, or Struct.
	 * Additional conversions may be supported and are vendor defined.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @param type Class representing the Java data type to convert the designated column to
	 * @return an instance of type holding the column value
	 * @throws SQLException if conversion is not supported, type is null or another error occurs. The getCause() method
	 * of the exception may provide a more detailed exception, for example, if a conversion error occurs
	 */
	@Override
	@SuppressWarnings("unchecked")
	public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
		try {
			return (T) getObjectFromClass(columnIndex, type);
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row
	 * of this ResultSet object and will convert from the SQL type of
	 * the column to the requested Java data type, if the conversion is
	 * supported.  If the conversion is not supported or null is
	 * specified for the type, a SQLException is thrown.
	 *
	 * @param columnLabel the label for the column specified with the SQL AS clause. If the SQL AS clause was not
	 * specified, then the label is the name of the column
	 * @param type Class representing the Java data type to convert the designated column to
	 * @return an instance of type holding the column value
	 * @throws SQLException if conversion is not supported, type is null or another error occurs. The getCause() method
	 * of the exception may provide a more detailed exception, for example, if a conversion error occurs
	 */
	@Override
	public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
		return getObject(findColumn(columnLabel), type);
	}

	/**
	 * Helper method to support the getObject and ResultsetMetaData.getColumnClassName JDBC methods.
	 *
	 * @param type a value from java.sql.Types
	 * @return a Class object from which an instance would be returned
	 */
	static Class<?> getClassForType(int type) {
		/**
		 * This switch returns the types as objects according to table B-3 from Oracle's JDBC specification 4.1
		 */
		switch(type) { // keep this switch regarding the returned classes aligned with getObject(int, Map) !
			case Types.CHAR:
			case Types.VARCHAR:
			case Types.LONGVARCHAR:
				return String.class;
			case Types.NUMERIC:
				return BigInteger.class;
			case Types.DECIMAL:
				return BigDecimal.class;
			case Types.BOOLEAN:
				return Boolean.class;
			case Types.TINYINT:
				return Byte.class;
			case Types.SMALLINT:
				return Short.class;
			case Types.INTEGER:
				return Integer.class;
			case Types.BIGINT:
				return Long.class;
			case Types.REAL:
				return Float.class;
			case Types.DOUBLE:
				return Double.class;
			case Types.LONGVARBINARY:
				return byte[].class;
			case Types.DATE:
				return Date.class;
			case Types.TIME:
				return Time.class;
			case Types.TIMESTAMP:
				return Timestamp.class;
			case Types.CLOB:
				return Clob.class;
			case Types.BLOB:
				return Blob.class;
			default: // all the rest are currently not implemented and used
				return String.class;
		}
	}

	/**
	 * Gets the value of the designated column in the current row of this
	 * ResultSet object as an Object in the Java programming language.
	 *
	 * This method will return the value of the given column as a Java object.
	 * The type of the Java object will be the default Java object type
	 * corresponding to the column's SQL type, following the mapping for
	 * built-in types specified in the JDBC specification. If the value is an
	 * SQL NULL, the driver returns a Java null.
	 *
	 * This method may also be used to read database-specific abstract data types.
	 *
	 * @param columnName the SQL name of the column
	 * @return a java.lang.Object holding the column value
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Object getObject(String columnName) throws SQLException {
		return getObject(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as an Object  in the Java programming language. If the
	 * value is an SQL NULL, the driver returns a Java null. This method uses
	 * the specified Map object for custom mapping if appropriate.
	 *
	 * @param colName the name of the column from which to retrieve the value
	 * @param map a java.util.Map object that contains the mapping from SQL
	 *        type names to classes in the Java programming language
	 * @return an Object representing the SQL value in the specified column
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Object getObject(String colName, Map<String,Class<?>> map) throws SQLException {
		return getObject(findColumn(colName), map);
	}

	@Override
	public Ref getRef(int i) throws SQLException {
		throw newSQLFeatureNotSupportedException("getRef");
	}

	@Override
	public Ref getRef(String colName) throws SQLException {
		throw newSQLFeatureNotSupportedException("getRef");
	}

	/**
	 * Retrieves the current row number. The first row is number 1, the second number 2, and so on.
	 *
	 * @return the current row number; 0 if there is no current row
	 */
	@Override
	public int getRow() {
		return curRow;
	}

	/**
	 * Retrieves the value of the designated column in the current row
	 * of this ResultSet object as a java.sql.RowId object in the Java
	 * programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if there is no such column
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public RowId getRowId(int columnIndex) throws SQLException {
		throw newSQLFeatureNotSupportedException("getRowId");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.RowId
	 * object in the Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if the ResultSet object does not contain columnName
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public RowId getRowId(String columnName) throws SQLException {
		throw newSQLFeatureNotSupportedException("getRowId");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a short in the Java
	 * programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if there is no such column
	 */
	@Override
	public short getShort(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return 0;
			}
			return currentBlock.getShortValue(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a short in the Java
	 * programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is 0
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public short getShort(String columnName) throws SQLException {
		return getShort(findColumn(columnName));
	}

	/**
	 * Retrieves the Statement object that produced this ResultSet object. If the result set was generated some other
	 * way, such as by a DatabaseMetaData method, this method returns null.
	 *
	 * @return the Statement object that produced this ResultSet object or null if the result set was produced some
	 * other way
	 */
	@Override
	public Statement getStatement() {
		return statement;
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a String in the Java
	 * programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if there is no such column
	 */
	@Override
	public String getString(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			return currentBlock.getValueAsString(columnIndex - 1);
		} catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a String in the Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if the ResultSet object does not contain columnName
	 */
	@Override
	public String getString(String columnName) throws SQLException {
		return getString(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row
	 * of this ResultSet object as a String in the Java programming
	 * language. It is intended for use when accessing NCHAR,NVARCHAR
	 * and LONGNVARCHAR columns.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if there is no such column
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public String getNString(int columnIndex) throws SQLException {
		return getString(columnIndex);
	}

	/**
	 * Retrieves the value of the designated column in the current row
	 * of this ResultSet object as a String in the Java programming
	 * language. It is intended for use when accessing NCHAR,NVARCHAR
	 * and LONGNVARCHAR columns.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if the ResultSet object does not contain columnName
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public String getNString(String columnName) throws SQLException {
		return getNString(findColumn(columnName));
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet as a java.sql.SQLXML object
	 * in the Java programming language.
	 *
	 * @param i the first column is 1, the second is 2, ...
	 * @return a SQLXML object that maps an SQL XML value
	 * @throws SQLException if a database access error occurs
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public SQLXML getSQLXML(int i) throws SQLException {
		throw newSQLFeatureNotSupportedException("getSQLXML");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet as a java.sql.SQLXML object
	 * in the Java programming language.
	 *
	 * @param colName the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified,
	 * then the label is the name of the column
	 * @return a SQLXML object that maps an SQL XML value
	 * @throws SQLException if a database access error occurs
	 * @throws SQLFeatureNotSupportedException the JDBC driver does not support this method
	 */
	@Override
	public SQLXML getSQLXML(String colName) throws SQLException {
		throw newSQLFeatureNotSupportedException("getSQLXML");
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.sql.Date object in the Java programming
	 * language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 * @see #getDate(int col, Calendar cal)
	 */
	@Override
	public Date getDate(int columnIndex) throws SQLException {
		return getDate(columnIndex, Calendar.getInstance());
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.sql.Date object in the Java programming
	 * language. This method uses the given calendar to construct an appropriate
	 * millisecond value for the date if the underlying database does not store
	 * timezone information.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @param cal the java.util.Calendar object to use in constructing the date
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Date getDate(int columnIndex, Calendar cal) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
            Calendar res;
            switch (JdbcSQLTypes[columnIndex - 1]) {
                case Types.CHAR :
                case Types.VARCHAR:
                case Types.LONGVARCHAR:
                case Types.CLOB:
                    String value = currentBlock.getValueAsString(columnIndex - 1);
                    res = GregorianCalendarParser.ParseDate(value, new ParsePosition(0));
                    break;
                case Types.DATE:
                    res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1);
                    break;
                default:
                    throw new SQLException("Conversion from " + types[columnIndex - 1] +
                            " to Date not supported", "M1M05");
            }
            res.setTimeZone(cal.getTimeZone());
            return new Date(res.getTimeInMillis());
        } catch (ClassCastException ex) {
			throw new SQLException(ex.getMessage());
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		} catch (ProtocolException ep) {
            throw new SQLException(ep.getMessage(), "M1M05");
        }
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.sql.Date object in the Java programming
	 * language.
	 *
	 * @param columnName the SQL name of the column from which to retrieve the value
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Date getDate(String columnName) throws SQLException {
		return getDate(findColumn(columnName), Calendar.getInstance());
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.sql.Date object in the Java programming
	 * language. This method uses the given calendar to construct an appropriate
	 * millisecond value for the date if the underlying database does not store
	 * timezone information.
	 *
	 * @param columnName the SQL name of the column from which to retrieve the value
	 * @param cal the java.util.Calendar object to use in constructing the date
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Date getDate(String columnName, Calendar cal) throws SQLException {
		return getDate(findColumn(columnName), cal);
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Time
	 * object in the Java programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Time getTime(int columnIndex) throws SQLException {
		return getTime(columnIndex, Calendar.getInstance());
	}

	/**
	 * Retrieves the value of the designated column in the current row of
	 * this ResultSet object as a java.sql.Time object in the Java programming
	 * language. This method uses the given calendar to construct an appropriate
	 * millisecond value for the time if the underlying database does not store
	 * timezone information.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @param cal the java.util.Calendar object to use in constructing the timestamp
	 * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null in
	 * the Java programming language
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Time getTime(int columnIndex, Calendar cal) throws SQLException {
        try {
            if(setLastNullValue(columnIndex - 1)) {
                return null;
            }
            Calendar res;
            switch (JdbcSQLTypes[columnIndex - 1]) {
                case Types.CHAR :
                case Types.VARCHAR:
                case Types.LONGVARCHAR:
                case Types.CLOB:
                    String value = currentBlock.getValueAsString(columnIndex - 1);
                    res = GregorianCalendarParser.ParseTime(value, new ParsePosition(0), false);
                    res.setTimeZone(cal.getTimeZone());
                    break;
                case Types.TIME:
                    res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1);
                    res.setTimeZone(cal.getTimeZone());
                    break;
                case Types.TIME_WITH_TIMEZONE:
                    res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1);
                    break;
                default:
                    throw new SQLException("Conversion from " + types[columnIndex - 1] +
                            " to Time not supported", "M1M05");
            }
            return new Time(res.getTimeInMillis());
        } catch (ClassCastException ex) {
            throw new SQLException(ex.getMessage());
        } catch (IndexOutOfBoundsException e) {
            throw newSQLInvalidColumnIndexException(columnIndex);
        } catch (ProtocolException ep) {
            throw new SQLException(ep.getMessage(), "M1M05");
        }
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Time
	 * object in the Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Time getTime(String columnName) throws SQLException {
		return getTime(findColumn(columnName), Calendar.getInstance());
	}

	/**
	 * Retrieves the value of the designated column in the current row of
	 * this ResultSet object as a java.sql.Time object in the Java programming
	 * language. This method uses the given calendar to construct an appropriate
	 * millisecond value for the time if the underlying database does not store
	 * timezone information.
	 *
	 * @param columnName the SQL name of the column
	 * @param cal the java.util.Calendar object to use in constructing the timestamp
	 * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null
	 * in the Java programming language
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Time getTime(String columnName, Calendar cal) throws SQLException {
		return getTime(findColumn(columnName), cal);
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp
	 * object in the Java programming language.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Timestamp getTimestamp(int columnIndex) throws SQLException {
		return getTimestamp(columnIndex, Calendar.getInstance());
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.sql.Timestamp object in the Java programming
	 * language. This method uses the given calendar to construct an appropriate
	 * millisecond value for the timestamp if the underlying database does not
	 * store timezone information.
	 *
	 * @param columnIndex the first column is 1, the second is 2, ...
	 * @param cal the java.util.Calendar object to use in constructing the timestamp
	 * @return the column value as a java.sql.Timestamp object; if the value is SQL NULL, the value returned is null
	 * in the Java programming language
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
        try {
            if(setLastNullValue(columnIndex - 1)) {
                return null;
            }
            Calendar res;
            switch (JdbcSQLTypes[columnIndex - 1]) {
                case Types.CHAR :
                case Types.VARCHAR:
                case Types.LONGVARCHAR:
                case Types.CLOB:
                    String value = currentBlock.getValueAsString(columnIndex - 1);
                    res = GregorianCalendarParser.ParseTimestamp(value, new ParsePosition(0), false);
                    res.setTimeZone(cal.getTimeZone());
                    break;
                case Types.TIMESTAMP:
                    res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1);
                    res.setTimeZone(cal.getTimeZone());
                    break;
                case Types.TIMESTAMP_WITH_TIMEZONE:
                    res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1);
                    break;
                default:
                    throw new SQLException("Conversion from " + types[columnIndex - 1] +
                            " to Timestamp not supported", "M1M05");
            }
            return new Timestamp(res.getTimeInMillis());
        } catch (ClassCastException ex) {
            throw new SQLException(ex.getMessage());
        } catch (IndexOutOfBoundsException e) {
            throw newSQLInvalidColumnIndexException(columnIndex);
        } catch (ProtocolException ep) {
            throw new SQLException(ep.getMessage(), "M1M05");
        }
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp
	 * object in the Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value; if the value is SQL NULL, the value returned is null
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Timestamp getTimestamp(String columnName) throws SQLException {
		return getTimestamp(findColumn(columnName), Calendar.getInstance());
	}

	/**
	 * Retrieves the value of the designated column in the current row of this
	 * ResultSet object as a java.sql.Timestamp object in the Java programming
	 * language. This method uses the given calendar to construct an appropriate
	 * millisecond value for the timestamp if the underlying database does not
	 * store timezone information.
	 *
	 * @param columnName the SQL name of the column
	 * @param cal the java.util.Calendar object to use in constructing the timestamp
	 * @return the column value as a java.sql.Timestamp object; if the value is
	 *         SQL NULL, the value returned is null in the Java programming language
	 * @throws SQLException if a database access error occurs
	 */
	@Override
	public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException {
		return getTimestamp(findColumn(columnName), cal);
	}

	/**
	 * Retrieves the type of this ResultSet object. The type is determined by
	 * the Statement object that created the result set.
	 *
	 * @return ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE or ResultSet.TYPE_SCROLL_SENSITIVE
	 */
	@Override
	public int getType() {
		return type;
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.net.URL
	 * object in the Java programming language.
	 *
	 * @param columnIndex the index of the column 1 is the first, 2 is the second,...
	 * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null in the
	 * Java programming language
	 * @throws SQLException if a database access error occurs, or if a URL is malformed
	 */
	@Override
	public URL getURL(int columnIndex) throws SQLException {
		try {
			if(setLastNullValue(columnIndex - 1)) {
				return null;
			}
			switch(JdbcSQLTypes[columnIndex - 1]) { //if it's a string type, will attempt the conversion
				case Types.CHAR:
				case Types.VARCHAR:
				case Types.LONGVARCHAR:
				case Types.CLOB:
					return new URL(currentBlock.getValueAsString(columnIndex - 1));
				case Types.OTHER:
					if("url".equals(types[columnIndex - 1])) {
						return new URL(currentBlock.getValueAsString(columnIndex - 1));
					}
				default:
					throw new SQLException("Cannot convert " + types[columnIndex - 1] + " to an url", "M1M05");
			}
		} catch (IndexOutOfBoundsException e) {
			throw newSQLInvalidColumnIndexException(columnIndex);
		} catch (MalformedURLException e) {
			throw new SQLException(e.getMessage(), "M1M05");
		}
	}

	/**
	 * Retrieves the value of the designated column in the current row of this ResultSet object as a java.net.URL object
	 * in the Java programming language.
	 *
	 * @param columnName the SQL name of the column
	 * @return the column value as a java.net.URL object; if the value is SQL NULL, the value returned is null in the
	 * Java programming language
	 * @throws SQLException if a database access error occurs, or if a URL is malformed
	 */
	@Override
	public URL getURL(String columnName) throws SQLException {
		return getURL(findColumn(columnName));
	}

	/**
	 * Retrieves the first warning reported by calls on this ResultSet object.
	 * If there is more than one warning, subsequent warnings will be chained to
	 * the first one and can be retrieved by calling the method
	 * SQLWarning.getNextWarning on the warning that was retrieved previously.
	 *
	 * This method may not be called on a closed result set; doing so will cause
	 * an SQLException to be thrown.
	 *
	 * Note: Subsequent warnings will be chained to this SQLWarning.
	 *
	 * @return the first SQLWarning object or null if there are none
	 * @throws SQLException if a database access error occurs or this method is
	 *         called on a closed connection
	 */
	@Override
	public SQLWarning getWarnings() throws SQLException {
		if (header != null && header.isClosed())
			throw new SQLException("Cannot call on closed ResultSet", "M1M20");

		// if there are no warnings, this will be null, which fits with the
		// specification.
		return warnings;
	}

	/**
	 * Retrieves whether the cursor is after the last row in this ResultSet object.
	 *
	 * @return true if the cursor is after the last row; false if the cursor is at any other position or the result set
	 * contains no rows
	 */
	@Override
	public boolean isAfterLast() {
		return curRow == tupleCount + 1;
	}

	/**
	 * Retrieves whether the cursor is before the first row in this ResultSet object.
	 *
	 * @return true if the cursor is before the first row; false if the cursor is at any other position or the result
	 * set contains no rows
	 */
	@Override
	public boolean isBeforeFirst() {
		return curRow == 0;
	}

	/**
	 * Retrieves whether this ResultSet object has been closed. A ResultSet is closed if the method close has been
	 * called on it, or if it is automatically closed.
	 *
	 * @return true if this ResultSet object is closed; false if it is still open
	 */
	@Override
	public boolean isClosed() {
		return header != null && header.isClosed();
	}

	/**
	 * Retrieves whether the cursor is on the first row of this ResultSet object.
	 *
	 * @return true if the cursor is on the first row; false otherwise
	 */
	@Override
	public boolean isFirst() {
		return curRow == 1;
	}

	/**
	 * Retrieves whether the cursor is on the last row of this ResultSet object.
	 *
	 * @return true if the cursor is on the last row; false otherwise
	 */
	@Override
	public boolean isLast() {
		return curRow == tupleCount;
	}

	/**
	 * Moves the cursor to the last row in this ResultSet object.
	 *
	 * @return true if the cursor is on a valid row; false if there are no rows in the result set
	 * @throws SQLException if a database access error occurs or the result set type is TYPE_FORWARD_ONLY
	 */
	@Override
	public boolean last() throws SQLException {
		return absolute(-1);
	}

	/**
	 * Moves the cursor down one row from its current position. A ResultSet
	 * cursor is initially positioned before the first row; the first call to
	 * the method next makes the first row the current row; the second call
	 * makes the second row the current row, and so on.
	 *
	 * If an input stream is open for the current row, a call to the method
	 * next will implicitly close it. A ResultSet object's warning chain is
	 * cleared when a new row is read.
	 *
	 * @return true if the new current row is valid; false if there are no more rows
	 * @throws SQLException if a database access error occurs or ResultSet is closed
	 */
	@Override
	public boolean next() throws SQLException {
		return relative(1);
	}

	/**
	 * Moves the cursor to the previous row in this ResultSet object.
	 *
	 * @return true if the cursor is on a valid row; false if it is off the result set
	 * @throws SQLException if a database access error occurs or ResultSet is closed or the result set type is
	 * TYPE_FORWARD_ONLY
	 */
	@Override
	public boolean previous() throws SQLException {
		return relative(-1);
	}

	/**
	 * Moves the cursor a relative number of rows, either positive or negative.
	 * Attempting to move beyond the first/last row in the result set positions
	 * the cursor before/after the the first/last row. Calling relative(0) is
	 * valid, but does not change the cursor position.
	 *
	 * Note: Calling the method relative(1) is identical to calling the method
	 * next() and calling the method relative(-1) is identical to calling the
	 * method previous().
	 *
	 * @param rows an int specifying the number of rows to move from the current
	 *        row; a positive number moves the cursor forward; a negative number
	 *        moves the cursor backward
	 * @return true if the cursor is on a row; false otherwise
	 * @throws SQLException if a database access error occurs, there is no current
	 *         row, or the result set type is TYPE_FORWARD_ONLY
	 */
	@Override
	public boolean relative(int rows) throws SQLException {
		return absolute(curRow + rows);
	}

	/**
	 * Retrieves whether a row has been deleted. A deleted row may leave a visible "hole" in a result set.
	 * This method can be used to detect holes in a result set.
	 * The value returned depends on whether or not this ResultSet object can detect deletions.
	 *
	 * Note: Support for the rowDeleted method is optional with a result set concurrency of CONCUR_READ_ONLY
	 *
	 * Returns: true if the current row is detected to have been deleted by the owner or another; false otherwise
	 *
	 * Throws:
	 *     SQLException - if a database access error occurs or this method is called on a closed result set
	 *     SQLFeatureNotSupportedException - if the JDBC driver does not support this method
	 * Since: 1.2
	 * See Also: DatabaseMetaData.deletesAreDetected(int)
	 */
	@Override
	public boolean rowDeleted() throws SQLException {
		return false;
	}

	/**
	 * Retrieves whether the current row has had an insertion.
	 * The value returned depends on whether or not this ResultSet object can detect visible inserts.
	 *
	 * Note: Support for the rowInserted method is optional with a result set concurrency of CONCUR_READ_ONLY
	 *
	 * Returns: true if the current row is detected to have been inserted; false otherwise
	 *
	 * Throws:
	 *     SQLException - if a database access error occurs or this method is called on a closed result set
	 *     SQLFeatureNotSupportedException - if the JDBC driver does not support this method
	 * Since: 1.2
	 * See Also: DatabaseMetaData.insertsAreDetected(int)
	 */
	@Override
	public boolean rowInserted() throws SQLException {
		return false;
	}

	/**
	 * Retrieves whether the current row has been updated.
	 * The value returned depends on whether or not the result set can detect updates.
	 *
	 * Note: Support for the rowUpdated method is optional with a result set concurrency of CONCUR_READ_ONLY
	 *
	 * Returns: true if the current row is detected to have been visibly updated by the owner or another; false otherwise
	 *
	 * Throws:
	 *     SQLException - if a database access error occurs or this method is called on a closed result set
	 *     SQLFeatureNotSupportedException - if the JDBC driver does not support this method
	 * Since: 1.2
	 * See Also: DatabaseMetaData.updatesAreDetected(int)
	 */
	@Override
	public boolean rowUpdated() throws SQLException {
		return false;
	}

	/* the next methods are all related to updateable result sets, which we currently do not support */
	@Override
	public void cancelRowUpdates() throws SQLException {
		throw newSQLFeatureNotSupportedException("cancelRowUpdates");
	}

	@Override
	public void deleteRow() throws SQLException {
		throw newSQLFeatureNotSupportedException("deleteRow");
	}

	@Override
	public void insertRow() throws SQLException {
		throw newSQLFeatureNotSupportedException("insertRow");
	}

	@Override
	public void moveToCurrentRow() throws SQLException {
		throw newSQLFeatureNotSupportedException("moveToCurrentRow");
	}

	@Override
	public void moveToInsertRow() throws SQLException {
		throw newSQLFeatureNotSupportedException("moveToInsertRow");
	}

	@Override
	public void refreshRow() throws SQLException {
		throw newSQLFeatureNotSupportedException("refreshRow");
	}

	@Override
	public void updateArray(int columnIndex, Array x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateArray");
	}

	@Override
	public void updateArray(String columnName, Array x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateArray");
	}

	@Override
	public void updateAsciiStream(int columnIndex, InputStream xh) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateAsciiStream");
	}

	@Override
	public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateAsciiStream");
	}

	@Override
	public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateAsciiStream");
	}

	@Override
	public void updateAsciiStream(String columnName, InputStream x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateAsciiStream");
	}

	@Override
	public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateAsciiStream");
	}

	@Override
	public void updateAsciiStream(String columnName, InputStream x, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateAsciiStream");
	}

	@Override
	public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBigDecimal");
	}

	@Override
	public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBigDecimal");
	}

	@Override
	public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBinaryStream");
	}

	@Override
	public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBinaryStream");
	}

	@Override
	public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBinaryStream");
	}

	@Override
	public void updateBinaryStream(String columnName, InputStream x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBinaryStream");
	}

	@Override
	public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBinaryStream");
	}

	@Override
	public void updateBinaryStream(String columnName, InputStream x, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBinaryStream");
	}

	@Override
	public void updateBlob(int columnIndex, Blob x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBlob");
	}

	@Override
	public void updateBlob(int columnIndex, InputStream s) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBlob");
	}

	@Override
	public void updateBlob(int columnIndex, InputStream s, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBlob");
	}

	@Override
	public void updateBlob(String columnName, Blob x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBlob");
	}

	@Override
	public void updateBlob(String columnName, InputStream s) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBlob");
	}

	@Override
	public void updateBlob(String columnName, InputStream s, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBlob");
	}

	@Override
	public void updateBoolean(int columnIndex, boolean x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBoolean");
	}

	@Override
	public void updateBoolean(String columnName, boolean x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBoolean");
	}

	@Override
	public void updateByte(int columnIndex, byte x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateByte");
	}

	@Override
	public void updateByte(String columnName, byte x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateByte");
	}

	@Override
	public void updateBytes(int columnIndex, byte[] x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBytes");
	}

	@Override
	public void updateBytes(String columnName, byte[] x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateBytes");
	}

	@Override
	public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateCharacterStream");
	}

	@Override
	public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateCharacterStream");
	}

	@Override
	public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateCharacterStream");
	}

	@Override
	public void updateCharacterStream(String columnName, Reader reader) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateCharacterStream");
	}

	@Override
	public void updateCharacterStream(String columnName, Reader reader, int length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateCharacterStream");
	}

	@Override
	public void updateCharacterStream(String columnName, Reader reader, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateCharacterStream");
	}

	@Override
	public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNCharacterStream");
	}

	@Override
	public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNCharacterStream");
	}

	@Override
	public void updateNCharacterStream(String columnName, Reader reader) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNCharacterStream");
	}

	@Override
	public void updateNCharacterStream(String columnName, Reader reader, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNCharacterStream");
	}

	@Override
	public void updateClob(int columnIndex, Clob x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateClob");
	}

	@Override
	public void updateClob(int columnIndex, Reader r) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateClob");
	}

	@Override
	public void updateClob(int columnIndex, Reader r, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateClob");
	}

	@Override
	public void updateClob(String columnName, Clob x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateClob");
	}

	@Override
	public void updateClob(String columnName, Reader r) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateClob");
	}

	@Override
	public void updateClob(String columnName, Reader r, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateClob");
	}

	@Override
	public void updateNClob(int columnIndex, NClob x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNClob");
	}

	@Override
	public void updateNClob(int columnIndex, Reader r) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNClob");
	}

	@Override
	public void updateNClob(int columnIndex, Reader r, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNClob");
	}

	@Override
	public void updateNClob(String columnName, NClob x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNClob");
	}

	@Override
	public void updateNClob(String columnName, Reader r) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNClob");
	}

	@Override
	public void updateNClob(String columnName, Reader r, long length) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNClob");
	}

	@Override
	public void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateDate");
	}

	@Override
	public void updateDate(String columnName, java.sql.Date x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateDate");
	}

	@Override
	public void updateDouble(int columnIndex, double x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateDouble");
	}

	@Override
	public void updateDouble(String columnName, double x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateDouble");
	}

	@Override
	public void updateFloat(int columnIndex, float x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateFloat");
	}

	@Override
	public void updateFloat(String columnName, float x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateFloat");
	}

	@Override
	public void updateInt(int columnIndex, int x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateInt");
	}

	@Override
	public void updateInt(String columnName, int x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateInt");
	}

	@Override
	public void updateLong(int columnIndex, long x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateLong");
	}

	@Override
	public void updateLong(String columnName, long x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateLong");
	}

	@Override
	public void updateNull(int columnIndex) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNull");
	}

	@Override
	public void updateNull(String columnName) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNull");
	}

	@Override
	public void updateObject(int columnIndex, Object x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateObject");
	}

	@Override
	public void updateObject(int columnIndex, Object x, int scale) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateObject");
	}

	@Override
	public void updateObject(String columnName, Object x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateObject");
	}

	@Override
	public void updateObject(String columnName, Object x, int scale) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateObject");
	}

	@Override
	public void updateRef(int columnIndex, Ref x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateRef");
	}

	@Override
	public void updateRef(String columnName, Ref x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateRef");
	}

	@Override
	public void updateRow() throws SQLException {
		throw newSQLFeatureNotSupportedException("updateRow");
	}

	@Override
	public void updateRowId(int columnIndex, RowId x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateRowId");
	}

	@Override
	public void updateRowId(String columnLabel, RowId x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateRowId");
	}

	@Override
	public void updateShort(int columnIndex, short x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateShort");
	}

	@Override
	public void updateShort(String columnName, short x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateShort");
	}

	@Override
	public void updateString(int columnIndex, String x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateString");
	}

	@Override
	public void updateString(String columnName, String x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateString");
	}

	@Override
	public void updateNString(int columnIndex, String x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNString");
	}

	@Override
	public void updateNString(String columnName, String x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateNString");
	}

	@Override
	public void updateSQLXML(String columnName, SQLXML x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateSQLXML");
	}

	@Override
	public void updateSQLXML(int columnIndex, SQLXML x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateSQLXML");
	}

	@Override
	public void updateTime(int columnIndex, Time x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateTime");
	}

	@Override
	public void updateTime(String columnName, Time x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateTime");
	}

	@Override
	public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateTimestamp");
	}

	@Override
	public void updateTimestamp(String columnName, Timestamp x) throws SQLException {
		throw newSQLFeatureNotSupportedException("updateTimestamp");
	}

	// Chapter 14.2.3.3 Sun JDBC 3.0 Specification
	/**
	 * Reports whether the last column read had a value of SQL NULL. Note that
	 * you must first call one of the getter methods on a column to try to read
	 * its value and then call the method wasNull to see if the value read was
	 * SQL NULL.
	 *
	 * @return true if the last column value read was SQL NULL and false otherwise
	 */
	@Override
	public boolean wasNull() {
		return lastReadWasNull;
	}

	//== end methods of interface ResultSet

	/**
	 * Small helper method that formats the "Invalid Column Index number ..." message
	 * and creates a new SQLException object whose SQLState is set to "M1M05".
	 *
	 * @param colIdx the column index number
	 * @return a new created SQLException object with SQLState M1M05
	 */
	static SQLException newSQLInvalidColumnIndexException(int colIdx) {
		return new SQLException("Invalid Column Index number: " + colIdx, "M1M05");
	}

	/**
	 * Small helper method that formats the "Method ... not implemented" message
	 * and creates a new SQLFeatureNotSupportedException object
	 * whose SQLState is set to "0A000".
	 *
	 * @param name the method name
	 * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000
	 */
	private static SQLFeatureNotSupportedException newSQLFeatureNotSupportedException(String name) {
		return new SQLFeatureNotSupportedException("Method " + name + " not implemented", "0A000");
	}
}