/* This file is part of VoltDB.
 * Copyright (C) 2008-2017 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.voltdb.jdbc;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.RowIdLifetime;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.voltdb.VoltTable;
import org.voltdb.VoltTable.ColumnInfo;
import org.voltdb.VoltType;

public class JDBC4DatabaseMetaData implements java.sql.DatabaseMetaData
{
    /*
     * Two types of tables the metadata generator generates. It has to match the
     * table types generated in JdbcDatabaseMetaDataGenerator.
     */
    static final String tableTypes[] = new String[] {"TABLE",
                                                     "VIEW"};

    private final CallableStatement sysInfo;
    private final CallableStatement sysCatalog;
    private final JDBC4Connection sourceConnection;
    JDBC4DatabaseMetaData(JDBC4Connection connection) throws SQLException
    {
        this.sourceConnection = connection;
        this.sysInfo = connection.prepareCall("{call @SystemInformation}");
        this.sysCatalog = connection.prepareCall("{call @SystemCatalog(?)}");

        // Initialize system information
        loadSystemInformation();
    }

    private String buildString = null;
    private String versionString = null;
    private String catalogString = null;

    private void loadSystemInformation() throws SQLException
    {
        ResultSet res = this.sysInfo.executeQuery();
        while (res.next())
        {
            if (res.getString(2).equals("BUILDSTRING"))
                buildString = res.getString(3);
            else if (res.getString(2).equals("VERSION"))
                versionString = res.getString(3);
            else if (res.getString(2).equals("CATALOG"))
                catalogString = res.getString(3);
        }
        res.close();

    }

    private void checkClosed() throws SQLException
    {
        if (this.sourceConnection.isClosed())
            throw SQLError.get(SQLError.CONNECTION_CLOSED);
    }

    // Retrieves whether the current user can call all the procedures returned by the method getProcedures.
    @Override
    public boolean allProceduresAreCallable() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether the current user can use all the tables returned by the method getTables in a SELECT statement.
    @Override
    public boolean allTablesAreSelectable() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether a SQLException while autoCommit is true indicates that all open ResultSets are closed, even ones that are holdable.
    @Override
    public boolean autoCommitFailureClosesAllResultSets() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether a data definition statement within a transaction forces the transaction to commit.
    @Override
    public boolean dataDefinitionCausesTransactionCommit() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database ignores a data definition statement within a transaction.
    @Override
    public boolean dataDefinitionIgnoredInTransactions() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether or not a visible row delete can be detected by calling the method ResultSet.rowDeleted.
    @Override
    public boolean deletesAreDetected(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether the return value for the method getMaxRowSize includes the SQL data types LONGVARCHAR and LONGVARBINARY.
    @Override
    public boolean doesMaxRowSizeIncludeBlobs() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves a description of the given attribute of the given type for a user-defined type (UDT) that is available in the given schema and catalog.
    @Override
    public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a description of a table's optimal set of columns that uniquely identifies a row.
    @Override
    public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the catalog names available in this database.
    @Override
    public ResultSet getCatalogs() throws SQLException
    {
        checkClosed();
        VoltTable result = new VoltTable(new VoltTable.ColumnInfo("TABLE_CAT",VoltType.STRING));
        result.addRow(new Object[] { catalogString });
        return new JDBC4ResultSet(null, result);
    }

    // Retrieves the String that this database uses as the separator between a catalog and table name.
    @Override
    public String getCatalogSeparator() throws SQLException
    {
        return ".";
    }

    // Retrieves the database vendor's preferred term for "catalog".
    @Override
    public String getCatalogTerm() throws SQLException
    {
        checkClosed();
        return "catalog";
    }

    // Retrieves a list of the client info properties that the driver supports.
    @Override
    public ResultSet getClientInfoProperties() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a description of the access rights for a table's columns.
    @Override
    public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a description of table columns available in the specified catalog.
    @Override
    public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException
    {
        checkClosed();
        this.sysCatalog.setString(1, "COLUMNS");
        JDBC4ResultSet res = (JDBC4ResultSet) this.sysCatalog.executeQuery();
        VoltTable vtable = res.getVoltTable().clone(0);

        // If no pattern is specified, default to matching any/all.
        if (tableNamePattern == null || tableNamePattern.length() == 0)
        {
            tableNamePattern = "%";
        }
        Pattern table_pattern = computeJavaPattern(tableNamePattern);

        if (columnNamePattern == null || columnNamePattern.length() == 0)
        {
            columnNamePattern = "%";
        }
        Pattern column_pattern = computeJavaPattern(columnNamePattern);

        // Filter columns based on table name and column name
        while (res.next()) {
            Matcher table_matcher = table_pattern.matcher(res.getString("TABLE_NAME"));
            if (table_matcher.matches())
            {
                Matcher column_matcher = column_pattern.matcher(res.getString("COLUMN_NAME"));
                if (column_matcher.matches())
                {
                    vtable.addRow(res.getRowData());
                }
            }
        }

        return new JDBC4ResultSet(this.sysCatalog, vtable);
    }

    // Retrieves the connection that produced this metadata object.
    @Override
    public Connection getConnection() throws SQLException
    {
        checkClosed();
        return sourceConnection;
    }

    // Retrieves a description of the foreign key columns in the given foreign key table that reference the primary key or the columns representing a unique constraint of the parent table (could be the same or a different table).
    @Override
    public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the major version number of the underlying database.
    @Override
    public int getDatabaseMajorVersion() throws SQLException
    {
        checkClosed();
        System.out.println("\n\n\nVERSION: " + versionString);
        return Integer.valueOf(versionString.split("\\.")[0]);
    }

    // Retrieves the minor version number of the underlying database.
    @Override
    public int getDatabaseMinorVersion() throws SQLException
    {
        checkClosed();
        return Integer.valueOf(versionString.split("\\.")[1]);
    }

    // Retrieves the name of this database product.
    @Override
    public String getDatabaseProductName() throws SQLException
    {
        checkClosed();
        return "VoltDB";
    }

    // Retrieves the version number of this database product.
    @Override
    public String getDatabaseProductVersion() throws SQLException
    {
        checkClosed();
        return buildString;
    }

    // Retrieves this database's default transaction isolation level.
    @Override
    public int getDefaultTransactionIsolation() throws SQLException
    {
        checkClosed();
        return Connection.TRANSACTION_SERIALIZABLE;
    }

    // Retrieves this JDBC driver's major version number.
    @Override
    public int getDriverMajorVersion()
    {
        return 1;
    }

    // Retrieves this JDBC driver's minor version number.
    @Override
    public int getDriverMinorVersion()
    {
        return 1;
    }

    // Retrieves the name of this JDBC driver.
    @Override
    public String getDriverName() throws SQLException
    {
        checkClosed();
        return "voltdb";
    }

    // Retrieves the version number of this JDBC driver as a String.
    @Override
    public String getDriverVersion() throws SQLException
    {
        checkClosed();
        return new String(getDriverMajorVersion() + "." + getDriverMinorVersion());
    }

    /**
     * Retrieves a description of the foreign key columns that reference the
     * given table's primary key columns (the foreign keys exported by a table).
     */
    @Override
    public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException
    {
        checkClosed();
        VoltTable vtable = new VoltTable(
                new ColumnInfo("PKTABLE_CAT", VoltType.STRING),
                new ColumnInfo("PKTABLE_SCHEM", VoltType.STRING),
                new ColumnInfo("PKTABLE_NAME", VoltType.STRING),
                new ColumnInfo("PKCOLUMN_NAME", VoltType.STRING),
                new ColumnInfo("FKTABLE_CAT", VoltType.STRING),
                new ColumnInfo("FKTABLE_SCHEM", VoltType.STRING),
                new ColumnInfo("FKTABLE_NAME", VoltType.STRING),
                new ColumnInfo("FKCOLUMN_NAME", VoltType.STRING),
                new ColumnInfo("KEY_SEQ", VoltType.SMALLINT),
                new ColumnInfo("UPDATE_RULE", VoltType.SMALLINT),
                new ColumnInfo("DELETE_RULE", VoltType.SMALLINT),
                new ColumnInfo("FK_NAME", VoltType.STRING),
                new ColumnInfo("PK_NAME", VoltType.STRING),
                new ColumnInfo("DEFERRABILITY", VoltType.SMALLINT)
        );
        //NB: @SystemCatalog(?) will need additional support if we want to
        // populate the table.
        JDBC4ResultSet res = new JDBC4ResultSet(this.sysCatalog, vtable);
        return res;
    }

    /**
     * Retrieves all the "extra" characters that can be used in unquoted identifier
     * names (those beyond a-z, A-Z, 0-9 and _).
     */
    @Override
    public String getExtraNameCharacters() throws SQLException
    {
        checkClosed();
        return "";
    }

    // Retrieves a description of the given catalog's system or user function parameters and return type.
    @Override
    public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a description of the system and user functions available in the given catalog.
    @Override
    public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the string used to quote SQL identifiers.
    @Override
    public String getIdentifierQuoteString() throws SQLException
    {
        checkClosed();
        return "\"";
    }

    // Retrieves a description of the primary key columns that are referenced by the given table's foreign key columns (the primary keys imported by a table) throws SQLException.
    @Override
    public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException
    {
        checkClosed();
        VoltTable vtable = new VoltTable(
                new ColumnInfo("PKTABLE_CAT", VoltType.STRING),
                new ColumnInfo("PKTABLE_SCHEM", VoltType.STRING),
                new ColumnInfo("PKTABLE_NAME", VoltType.STRING),
                new ColumnInfo("PKCOLUMN_NAME", VoltType.STRING),
                new ColumnInfo("FKTABLE_CAT", VoltType.STRING),
                new ColumnInfo("FKTABLE_SCHEM", VoltType.STRING),
                new ColumnInfo("FKTABLE_NAME", VoltType.STRING),
                new ColumnInfo("FKCOLUMN_NAME", VoltType.STRING),
                new ColumnInfo("KEY_SEQ", VoltType.SMALLINT),
                new ColumnInfo("UPDATE_RULE", VoltType.SMALLINT),
                new ColumnInfo("DELETE_RULE", VoltType.SMALLINT),
                new ColumnInfo("FK_NAME", VoltType.STRING),
                new ColumnInfo("PK_NAME", VoltType.STRING),
                new ColumnInfo("DEFERRABILITY", VoltType.SMALLINT)
        );
        //NB: @SystemCatalog(?) will need additional support if we want to
        // populate the table.
        JDBC4ResultSet res = new JDBC4ResultSet(this.sysCatalog, vtable);
        return res;
    }

    // Retrieves a description of the given table's indices and statistics.
    // NOTE: currently returns the NON_UNIQUE column as a TINYINT due
    // to lack of boolean support in VoltTable schemas.
    @Override
    public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException
    {
        assert(table != null && !table.isEmpty());
        checkClosed();
        this.sysCatalog.setString(1, "INDEXINFO");
        JDBC4ResultSet res = (JDBC4ResultSet) this.sysCatalog.executeQuery();
        VoltTable vtable = res.getVoltTable().clone(0);

        // Filter the indexes by table name
        while (res.next()) {
            if (res.getString("TABLE_NAME").equals(table)) {
                if (!unique || res.getShort("NON_UNIQUE") == 0) {
                    vtable.addRow(res.getRowData());
                }
            }
        }

        return new JDBC4ResultSet(sysCatalog, vtable);
    }

    // Retrieves the major JDBC version number for this driver.
    @Override
    public int getJDBCMajorVersion() throws SQLException
    {
        checkClosed();
        return 4;
    }

    // Retrieves the minor JDBC version number for this driver.
    @Override
    public int getJDBCMinorVersion() throws SQLException
    {
        checkClosed();
        return 0;
    }

    // Retrieves the maximum number of hex characters this database allows in an inline binary literal.
    @Override
    public int getMaxBinaryLiteralLength() throws SQLException
    {
        checkClosed();
        return VoltType.MAX_VALUE_LENGTH;
    }

    // Retrieves the maximum number of characters that this database allows in a catalog name.
    @Override
    public int getMaxCatalogNameLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of characters this database allows for a character literal.
    @Override
    public int getMaxCharLiteralLength() throws SQLException
    {
        checkClosed();
        return VoltType.MAX_VALUE_LENGTH;
    }

    // Retrieves the maximum number of characters this database allows for a column name.
    @Override
    public int getMaxColumnNameLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of columns this database allows in a GROUP BY clause.
    @Override
    public int getMaxColumnsInGroupBy() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of columns this database allows in an index.
    @Override
    public int getMaxColumnsInIndex() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of columns this database allows in an ORDER BY clause.
    @Override
    public int getMaxColumnsInOrderBy() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of columns this database allows in a SELECT list.
    @Override
    public int getMaxColumnsInSelect() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of columns this database allows in a table.
    @Override
    public int getMaxColumnsInTable() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of concurrent connections to this database that are possible.
    @Override
    public int getMaxConnections() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of characters that this database allows in a cursor name.
    @Override
    public int getMaxCursorNameLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of bytes this database allows for an index, including all of the parts of the index.
    @Override
    public int getMaxIndexLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of characters that this database allows in a procedure name.
    @Override
    public int getMaxProcedureNameLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of bytes this database allows in a single row.
    @Override
    public int getMaxRowSize() throws SQLException
    {
        checkClosed();
        return 2 * 1024 * 1024;  // 2 MB
    }

    // Retrieves the maximum number of characters that this database allows in a schema name.
    @Override
    public int getMaxSchemaNameLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of characters this database allows in an SQL statement.
    @Override
    public int getMaxStatementLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of active statements to this database that can be open at the same time.
    @Override
    public int getMaxStatements() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of characters this database allows in a table name.
    @Override
    public int getMaxTableNameLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of tables this database allows in a SELECT statement.
    @Override
    public int getMaxTablesInSelect() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the maximum number of characters this database allows in a user name.
    @Override
    public int getMaxUserNameLength() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a comma-separated list of math functions available with this database.
    @Override
    public String getNumericFunctions() throws SQLException
    {
        checkClosed();
        return "ABS,BITAND,BITNOT,BITOR,BIT_SHIFT_LEFT,BIT_SHIFT_RIGHT,BITXOR,CEILING,COS,COT,CSC,EXP,FLOOR,LN,LOG,LOG10,PI,POWER,ROUND,SEC,SIN,SQRT,TAN";
    }

    // Retrieves a description of the given table's primary key columns.
    @Override
    public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException
    {
        assert(table != null && !table.isEmpty());
        checkClosed();
        this.sysCatalog.setString(1, "PRIMARYKEYS");
        JDBC4ResultSet res = (JDBC4ResultSet) this.sysCatalog.executeQuery();
        VoltTable vtable = res.getVoltTable().clone(0);

        // Filter the primary keys based on table name
        while (res.next()) {
            if (res.getString("TABLE_NAME").equals(table)) {
                vtable.addRow(res.getRowData());
            }
        }
        return new JDBC4ResultSet(sysCatalog, vtable);
    }

    // Retrieves a description of the given catalog's stored procedure parameter and result columns.
    // TODO: implement pattern filtering somewhere (preferably server-side)
    @Override
    public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException
    {
        assert(procedureNamePattern != null && !procedureNamePattern.isEmpty());
        checkClosed();
        this.sysCatalog.setString(1, "PROCEDURECOLUMNS");
        JDBC4ResultSet res = (JDBC4ResultSet) this.sysCatalog.executeQuery();
        VoltTable vtable = res.getVoltTable().clone(0);

        // Filter the results based on procedure name and column name
        while (res.next()) {
            if (res.getString("PROCEDURE_NAME").equals(procedureNamePattern)) {
                if (columnNamePattern == null || columnNamePattern.equals("%") ||
                    res.getString("COLUMN_NAME").equals(columnNamePattern)) {
                    vtable.addRow(res.getRowData());
                }
            }
        }

        return new JDBC4ResultSet(sysCatalog, vtable);
    }

    // Retrieves a description of the stored procedures available in the given catalog.
    // TODO: implement pattern filtering somewhere (preferably server-side)
    @Override
    public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException
    {
        if (procedureNamePattern != null && !procedureNamePattern.equals("%"))
            throw new SQLException(String.format("getProcedures('%s','%s','%s') does not support pattern filtering", catalog, schemaPattern, procedureNamePattern));

        checkClosed();
        this.sysCatalog.setString(1, "PROCEDURES");
        ResultSet res = this.sysCatalog.executeQuery();
        return res;
    }

    // Retrieves the database vendor's preferred term for "procedure".
    @Override
    public String getProcedureTerm() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves this database's default holdability for ResultSet objects.
    @Override
    public int getResultSetHoldability() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Indicates whether or not this data source supports the SQL ROWID type, and if so the lifetime for which a RowId object remains valid.
    @Override
    public RowIdLifetime getRowIdLifetime() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the schema names available in this database.
    @Override
    public ResultSet getSchemas() throws SQLException
    {
        checkClosed();
        VoltTable vtable = new VoltTable(
                new ColumnInfo("TABLE_SCHEM", VoltType.STRING),
                new ColumnInfo("TABLE_CATALOG", VoltType.STRING));
        JDBC4ResultSet res = new JDBC4ResultSet(this.sysCatalog, vtable);
        return res;
    }

    // Retrieves the schema names available in this database.
    @Override
    public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException
    {
        return getSchemas();    // empty
    }

    // Retrieves the database vendor's preferred term for "schema".
    @Override
    public String getSchemaTerm() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the string that can be used to escape wildcard characters.
    @Override
    public String getSearchStringEscape() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a comma-separated list of all of this database's SQL keywords that are NOT also SQL:2003 keywords.
    @Override
    public String getSQLKeywords() throws SQLException
    {
        checkClosed();
        return "";
    }

    // Indicates whether the SQLSTATE returned by SQLException.getSQLState is X/Open (now known as Open Group) SQL CLI or SQL:2003.
    @Override
    public int getSQLStateType() throws SQLException
    {
        checkClosed();
        return sqlStateXOpen;
    }

    // Retrieves a comma-separated list of string functions available with this database.
    @Override
    public String getStringFunctions() throws SQLException
    {
        checkClosed();
        // TODO: find a more suitable place for COALESCE
        return "BIN,COALESCE,CHAR,CHAR_LENGTH,CONCAT,FORMAT_CURRENCY,HEX,STR,INSERT,LCASE,LEFT,LOWER,LTRIM," +
               "OCTET_LENGTH,OVERLAY,POSITION,REGEXP_POSITION,REPEAT,REPLACE,RIGHT,RTRIM,SPACE,SUBSTRING,SUBSTR,"+
               "TRIM,UCASE,UPPER";
    }

    // Retrieves a description of the table hierarchies defined in a particular schema in this database.
    @Override
    public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a description of the user-defined type (UDT) hierarchies defined in a particular schema in this database.
    @Override
    public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a comma-separated list of system functions available with this database.
    @Override
    public String getSystemFunctions() throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves a description of the access rights for each table available in a catalog.
    @Override
    public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException
    {
        checkClosed();
        VoltTable vtable = new VoltTable(
                new ColumnInfo("TABLE_CAT", VoltType.STRING),
                new ColumnInfo("TABLE_SCHEM", VoltType.STRING),
                new ColumnInfo("TABLE_NAME", VoltType.STRING),
                new ColumnInfo("GRANTOR", VoltType.STRING),
                new ColumnInfo("GRANTEE", VoltType.STRING),
                new ColumnInfo("PRIVILEGE", VoltType.STRING),
                new ColumnInfo("IS_GRANTABLE", VoltType.STRING)
        );
        //NB: @SystemCatalog(?) will need additional support if we want to
        // populate the table.
        JDBC4ResultSet res = new JDBC4ResultSet(this.sysCatalog, vtable);
        return res;
    }

    // Convert the users VoltDB SQL pattern into a regex pattern
    public static Pattern computeJavaPattern(String sqlPattern)
    {
        StringBuffer pattern_buff = new StringBuffer();
        // Replace "_" with "." (match exactly 1 character)
        // Replace "%" with ".*" (match 0 or more characters)
        for (int i=0; i<sqlPattern.length(); i++)
        {
            char c = sqlPattern.charAt(i);
            if (c == '_')
            {
                pattern_buff.append('.');
            }
            else
            if (c == '%')
            {
                pattern_buff.append(".*");
            }
            else
                pattern_buff.append(c);
        }
        return Pattern.compile(pattern_buff.toString());
    }

    // Retrieves a description of the tables available in the given catalog.
    @Override
    public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException
    {
        checkClosed();
        this.sysCatalog.setString(1, "TABLES");
        JDBC4ResultSet res = (JDBC4ResultSet) this.sysCatalog.executeQuery();
        VoltTable vtable = res.getVoltTable().clone(0);

        List<String> typeStrings = null;
        if (types != null) {
            typeStrings = Arrays.asList(types);
        }

        // If no pattern is specified, default to matching any/all.
        if (tableNamePattern == null || tableNamePattern.length() == 0)
        {
            tableNamePattern = "%";
        }

        Pattern table_pattern = computeJavaPattern(tableNamePattern);

        // Filter tables based on type and pattern
        while (res.next()) {
            if (typeStrings == null || typeStrings.contains(res.getString("TABLE_TYPE"))) {
                Matcher table_matcher = table_pattern.matcher(res.getString("TABLE_NAME"));
                if (table_matcher.matches())
                {
                    vtable.addRow(res.getRowData());
                }
            }
        }

        return new JDBC4ResultSet(this.sysCatalog, vtable);
    }

    // Retrieves the table types available in this database.
    @Override
    public ResultSet getTableTypes() throws SQLException
    {
        checkClosed();
        VoltTable vtable = new VoltTable(new ColumnInfo("TABLE_TYPE", VoltType.STRING));
        for (String type : tableTypes) {
            vtable.addRow(type);
        }
        JDBC4ResultSet res = new JDBC4ResultSet(this.sysCatalog, vtable);
        return res;
    }

    // Retrieves a comma-separated list of the time and date functions available with this database.
    @Override
    public String getTimeDateFunctions() throws SQLException
    {
        checkClosed();
        return "CURRENT_TIMESTAMP,DATEADD,DAY,DAYOFMONTH,DAYOFWEEK,DAYOFYEAR,EXTRACT,FROM_UNIXTIME,HOUR," +
               "IS_VALID_TIMESTAMP,MAX_VALID_TIMESTAMP,MIN_VALID_TIMESTAMP," +
               "MINUTE,MONTH,NOW,QUARTER,SECOND,SINCE_EPOCH,TO_TIMESTAMP,TRUNCATE,WEEK,WEEKOFYEAR,"+
               "WEEKDAY,YEAR";
    }

    // Retrieves a description of all the data types supported by this database.
    @Override
    public ResultSet getTypeInfo() throws SQLException
    {
        checkClosed();
        this.sysCatalog.setString(1, "TYPEINFO");
        ResultSet res = this.sysCatalog.executeQuery();
        return res;
    }

    // Retrieves a description of the user-defined types (UDTs) defined in a particular schema.
    @Override
    public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves the URL for this DBMS.
    @Override
    public String getURL() throws SQLException
    {
        checkClosed();
        return "http://voltdb.com/";
    }

    // Retrieves the user name as known to this database.
    @Override
    public String getUserName() throws SQLException
    {
        checkClosed();
        return this.sourceConnection.User;
    }

    // Retrieves a description of a table's columns that are automatically updated when any value in a row is updated.
    @Override
    public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException
    {
        checkClosed();
        throw SQLError.noSupport();
    }

    // Retrieves whether or not a visible row insert can be detected by calling the method ResultSet.rowInserted.
    @Override
    public boolean insertsAreDetected(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a catalog appears at the start of a fully qualified table name.
    @Override
    public boolean isCatalogAtStart() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database is in read-only mode.
    @Override
    public boolean isReadOnly() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Indicates whether updates made to a LOB are made on a copy or directly to the LOB.
    @Override
    public boolean locatorsUpdateCopy() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports concatenations between NULL and non-NULL values being NULL.
    @Override
    public boolean nullPlusNonNullIsNull() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether NULL values are sorted at the end regardless of sort order.
    @Override
    public boolean nullsAreSortedAtEnd() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether NULL values are sorted at the start regardless of sort order.
    @Override
    public boolean nullsAreSortedAtStart() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether NULL values are sorted high.
    @Override
    public boolean nullsAreSortedHigh() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether NULL values are sorted low.
    @Override
    public boolean nullsAreSortedLow() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether deletes made by others are visible.
    @Override
    public boolean othersDeletesAreVisible(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether inserts made by others are visible.
    @Override
    public boolean othersInsertsAreVisible(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether updates made by others are visible.
    @Override
    public boolean othersUpdatesAreVisible(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a result set's own deletes are visible.
    @Override
    public boolean ownDeletesAreVisible(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a result set's own inserts are visible.
    @Override
    public boolean ownInsertsAreVisible(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether for the given type of ResultSet object, the result set's own updates are visible.
    @Override
    public boolean ownUpdatesAreVisible(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database treats mixed case unquoted SQL identifiers as case insensitive and stores them in lower case.
    @Override
    public boolean storesLowerCaseIdentifiers() throws SQLException
    {
        checkClosed();
        return false; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether this database treats mixed case quoted SQL identifiers as case insensitive and stores them in lower case.
    @Override
    public boolean storesLowerCaseQuotedIdentifiers() throws SQLException
    {
        checkClosed();
        return false; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether this database treats mixed case unquoted SQL identifiers as case insensitive and stores them in mixed case.
    @Override
    public boolean storesMixedCaseIdentifiers() throws SQLException
    {
        checkClosed();
        return false; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether this database treats mixed case quoted SQL identifiers as case insensitive and stores them in mixed case.
    @Override
    public boolean storesMixedCaseQuotedIdentifiers() throws SQLException
    {
        checkClosed();
        return false; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether this database treats mixed case unquoted SQL identifiers as case insensitive and stores them in upper case.
    @Override
    public boolean storesUpperCaseIdentifiers() throws SQLException
    {
        checkClosed();
        return true; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether this database treats mixed case quoted SQL identifiers as case insensitive and stores them in upper case.
    @Override
    public boolean storesUpperCaseQuotedIdentifiers() throws SQLException
    {
        checkClosed();
        return true; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether this database supports ALTER TABLE with add column.
    @Override
    public boolean supportsAlterTableWithAddColumn() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports ALTER TABLE with drop column.
    @Override
    public boolean supportsAlterTableWithDropColumn() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the ANSI92 entry level SQL grammar.
    @Override
    public boolean supportsANSI92EntryLevelSQL() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the ANSI92 full SQL grammar supported.
    @Override
    public boolean supportsANSI92FullSQL() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the ANSI92 intermediate SQL grammar supported.
    @Override
    public boolean supportsANSI92IntermediateSQL() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports batch updates.
    @Override
    public boolean supportsBatchUpdates() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether a catalog name can be used in a data manipulation statement.
    @Override
    public boolean supportsCatalogsInDataManipulation() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a catalog name can be used in an index definition statement.
    @Override
    public boolean supportsCatalogsInIndexDefinitions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a catalog name can be used in a privilege definition statement.
    @Override
    public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a catalog name can be used in a procedure call statement.
    @Override
    public boolean supportsCatalogsInProcedureCalls() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a catalog name can be used in a table definition statement.
    @Override
    public boolean supportsCatalogsInTableDefinitions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports column aliasing.
    @Override
    public boolean supportsColumnAliasing() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports the JDBC scalar function CONVERT for the conversion of one JDBC type to another.
    @Override
    public boolean supportsConvert() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the JDBC scalar function CONVERT for conversions between the JDBC types fromType and toType.
    @Override
    public boolean supportsConvert(int fromType, int toType) throws SQLException {
        checkClosed();
        switch (fromType) {
        /*
         * ALL types can be converted to VARCHAR /VoltType.String
         */
        case java.sql.Types.VARCHAR:
        case java.sql.Types.VARBINARY:
        case java.sql.Types.TIMESTAMP:
        case java.sql.Types.OTHER:
            switch (toType) {
            case java.sql.Types.VARCHAR:
                return true;

            default:
                return false;
            }
        case java.sql.Types.TINYINT:
        case java.sql.Types.SMALLINT:
        case java.sql.Types.INTEGER:
        case java.sql.Types.BIGINT:
        case java.sql.Types.FLOAT:
        case java.sql.Types.DECIMAL:
            switch (toType) {
            case java.sql.Types.VARCHAR:
            case java.sql.Types.TINYINT:
            case java.sql.Types.SMALLINT:
            case java.sql.Types.INTEGER:
            case java.sql.Types.BIGINT:
            case java.sql.Types.FLOAT:
            case java.sql.Types.DECIMAL:
                return true;

            default:
                return false;
            }
        default:
            return false;
        }
    }

    // Retrieves whether this database supports the ODBC Core SQL grammar.
    @Override
    public boolean supportsCoreSQLGrammar() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports correlated subqueries.
    @Override
    public boolean supportsCorrelatedSubqueries() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports both data definition and data manipulation statements within a transaction.
    @Override
    public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports only data manipulation statements within a transaction.
    @Override
    public boolean supportsDataManipulationTransactionsOnly() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether, when table correlation names are supported, they are restricted to being different from the names of the tables.
    @Override
    public boolean supportsDifferentTableCorrelationNames() throws SQLException
    {
        checkClosed();
        return false; // Alias may be same as the table name
    }

    // Retrieves whether this database supports expressions in ORDER BY lists.
    @Override
    public boolean supportsExpressionsInOrderBy() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the ODBC Extended SQL grammar.
    @Override
    public boolean supportsExtendedSQLGrammar() throws SQLException
    {
        checkClosed();
        return false;  // Only assuming based on currently limited set
    }

    // Retrieves whether this database supports full nested outer joins.
    @Override
    public boolean supportsFullOuterJoins() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether auto-generated keys can be retrieved after a statement has been executed
    @Override
    public boolean supportsGetGeneratedKeys() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports some form of GROUP BY clause.
    @Override
    public boolean supportsGroupBy() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports using columns not included in the SELECT statement in a GROUP BY clause provided that all of the columns in the SELECT statement are included in the GROUP BY clause.
    @Override
    public boolean supportsGroupByBeyondSelect() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports using a column that is not in the SELECT statement in a GROUP BY clause.
    @Override
    public boolean supportsGroupByUnrelated() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the SQL Integrity Enhancement Facility.
    @Override
    public boolean supportsIntegrityEnhancementFacility() throws SQLException
    {
        checkClosed();
        return false; // Does support DEFAULT, but not CHECK and UNIQUE constraints get violated over partition boundaries!
    }

    // Retrieves whether this database supports specifying a LIKE escape clause.
    @Override
    public boolean supportsLikeEscapeClause() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database provides limited support for outer joins.
    @Override
    public boolean supportsLimitedOuterJoins() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports the ODBC Minimum SQL grammar.
    @Override
    public boolean supportsMinimumSQLGrammar() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database treats mixed case unquoted SQL identifiers as case sensitive and as a result stores them in mixed case.
    @Override
    public boolean supportsMixedCaseIdentifiers() throws SQLException
    {
        checkClosed();
        return false; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether this database treats mixed case quoted SQL identifiers as case sensitive and as a result stores them in mixed case.
    @Override
    public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException
    {
        checkClosed();
        return false; // Note: Proc names are sensitive, but not tables/columns!
    }

    // Retrieves whether it is possible to have multiple ResultSet objects returned from a CallableStatement object simultaneously.
    @Override
    public boolean supportsMultipleOpenResults() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports getting multiple ResultSet objects from a single call to the method execute.
    @Override
    public boolean supportsMultipleResultSets() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database allows having multiple transactions open at once (on different connections) throws SQLException.
    @Override
    public boolean supportsMultipleTransactions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports named parameters to callable statements.
    @Override
    public boolean supportsNamedParameters() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether columns in this database may be defined as non-nullable.
    @Override
    public boolean supportsNonNullableColumns() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports keeping cursors open across commits.
    @Override
    public boolean supportsOpenCursorsAcrossCommit() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports keeping cursors open across rollbacks.
    @Override
    public boolean supportsOpenCursorsAcrossRollback() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports keeping statements open across commits.
    @Override
    public boolean supportsOpenStatementsAcrossCommit() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports keeping statements open across rollbacks.
    @Override
    public boolean supportsOpenStatementsAcrossRollback() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports using a column that is not in the SELECT statement in an ORDER BY clause.
    @Override
    public boolean supportsOrderByUnrelated() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports some form of outer join.
    @Override
    public boolean supportsOuterJoins() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports positioned DELETE statements.
    @Override
    public boolean supportsPositionedDelete() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports positioned UPDATE statements.
    @Override
    public boolean supportsPositionedUpdate() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the given concurrency type in combination with the given result set type.
    @Override
    public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException
    {
        checkClosed();
        if (type == ResultSet.TYPE_SCROLL_INSENSITIVE && concurrency == ResultSet.CONCUR_READ_ONLY)
            return true;
        return false;
    }

    // Retrieves whether this database supports the given result set holdability.
    @Override
    public boolean supportsResultSetHoldability(int holdability) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports the given result set type.
    @Override
    public boolean supportsResultSetType(int type) throws SQLException
    {
        checkClosed();
        if (type == ResultSet.TYPE_SCROLL_INSENSITIVE)
            return true;
        return false;
    }

    // Retrieves whether this database supports savepoints.
    @Override
    public boolean supportsSavepoints() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a schema name can be used in a data manipulation statement.
    @Override
    public boolean supportsSchemasInDataManipulation() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a schema name can be used in an index definition statement.
    @Override
    public boolean supportsSchemasInIndexDefinitions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a schema name can be used in a privilege definition statement.
    @Override
    public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a schema name can be used in a procedure call statement.
    @Override
    public boolean supportsSchemasInProcedureCalls() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether a schema name can be used in a table definition statement.
    @Override
    public boolean supportsSchemasInTableDefinitions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports SELECT FOR UPDATE statements.
    @Override
    public boolean supportsSelectForUpdate() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports statement pooling.
    @Override
    public boolean supportsStatementPooling() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports invoking user-defined or vendor functions using the stored procedure escape syntax.
    @Override
    public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports stored procedure calls that use the stored procedure escape syntax.
    @Override
    public boolean supportsStoredProcedures() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports subqueries in comparison expressions.
    @Override
    public boolean supportsSubqueriesInComparisons() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports subqueries in EXISTS expressions.
    @Override
    public boolean supportsSubqueriesInExists() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports subqueries in IN expressions.
    @Override
    public boolean supportsSubqueriesInIns() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports subqueries in quantified expressions.
    @Override
    public boolean supportsSubqueriesInQuantifieds() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports table correlation names.
    @Override
    public boolean supportsTableCorrelationNames() throws SQLException
    {
        checkClosed();
        return true;
    }

    // Retrieves whether this database supports the given transaction isolation level.
    @Override
    public boolean supportsTransactionIsolationLevel(int level) throws SQLException
    {
        checkClosed();
        if (level == Connection.TRANSACTION_SERIALIZABLE)
            return true;
        return false;
    }

    // Retrieves whether this database supports transactions.
    @Override
    public boolean supportsTransactions() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports SQL UNION.
    @Override
    public boolean supportsUnion() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database supports SQL UNION ALL.
    @Override
    public boolean supportsUnionAll() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether or not a visible row update can be detected by calling the method ResultSet.rowUpdated.
    @Override
    public boolean updatesAreDetected(int type) throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database uses a file for each table.
    @Override
    public boolean usesLocalFilePerTable() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Retrieves whether this database stores tables in a local file.
    @Override
    public boolean usesLocalFiles() throws SQLException
    {
        checkClosed();
        return false;
    }

    // Returns true if this either implements the interface argument or is directly or indirectly a wrapper for an object that does.
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException
    {
        checkClosed();
        return iface.isInstance(this);
    }

    // Returns an object that implements the given interface to allow access to non-standard methods, or standard methods not exposed by the proxy.
    @Override
    public <T> T unwrap(Class<T> iface)    throws SQLException
    {
        checkClosed();
        try
        {
            return iface.cast(this);
        }
         catch (ClassCastException cce)
         {
            throw SQLError.get(SQLError.ILLEGAL_ARGUMENT, iface.toString());
        }
    }

    @Override
    public ResultSet getPseudoColumns(String catalog, String schemaPattern,
            String tableNamePattern, String columnNamePattern)
            throws SQLException {
        checkClosed();
        throw SQLError.noSupport();
    }

    @Override
    public boolean generatedKeyAlwaysReturned() throws SQLException {
        throw SQLError.noSupport();
    }
}
