ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sboi...@apache.org
Subject [2/5] incubator-ignite git commit: # ignite-63
Date Fri, 23 Jan 2015 11:34:44 GMT
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/531d9ba3/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java
new file mode 100644
index 0000000..5233a58
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.jdbc;
+
+
+import java.sql.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * JDBC driver implementation for In-Memory Data Grid.
+ * <p>
+ * Driver allows to get distributed data from GridGain cache using standard
+ * SQL queries and standard JDBC API. It will automatically get only fields that
+ * you actually need from objects stored in cache.
+ * <h1 class="header">Limitations</h1>
+ * Data in GridGain cache is usually distributed across several nodes,
+ * so some queries may not work as expected since the query will be sent to each
+ * individual node and results will then be collected and returned as JDBC result set.
+ * Keep in mind following limitations (not applied if data is queried from one node only,
+ * or data is fully co-located or fully replicated on multiple nodes):
+ * <ul>
+ *     <li>
+ *         {@code Group by} and {@code sort by} statements are applied separately
+ *         on each node, so result set will likely be incorrectly grouped or sorted
+ *         after results from multiple remote nodes are grouped together.
+ *     </li>
+ *     <li>
+ *         Aggregation functions like {@code sum}, {@code max}, {@code avg}, etc.
+ *         are also applied on each node. Therefore you will get several results
+ *         containing aggregated values, one for each node.
+ *     </li>
+ *     <li>
+ *         Joins will work correctly only if joined objects are stored in
+ *         collocated mode. Refer to
+ *         {@link org.apache.ignite.cache.affinity.GridCacheAffinityKey}
+ *         javadoc for more details.
+ *     </li>
+ *     <li>
+ *         Note that if you are connected to local or replicated cache, all data will
+ *         be queried only on one node, not depending on what caches participate in
+ *         the query (some data from partitioned cache can be lost). And visa versa,
+ *         if you are connected to partitioned cache, data from replicated caches
+ *         will be duplicated.
+ *     </li>
+ * </ul>
+ * <h1 class="header">SQL Notice</h1>
+ * Driver allows to query data from several caches. Cache that driver is connected to is
+ * treated as default schema in this case. Other caches can be referenced by their names.
+ * <p>
+ * Note that cache name is case sensitive and you have to always specify it in quotes.
+ * <h1 class="header">Dependencies</h1>
+ * JDBC driver is located in main GridGain JAR and depends on all libraries located in
+ * {@code GRIDGAIN_HOME/libs} folder. So if you are using JDBC driver in any external tool,
+ * you have to add main GridGain JAR will all dependencies to its classpath.
+ * <h1 class="header">Configuration</h1>
+ * Internally JDBC driver <b>is based on GridGain Java client</b>. Therefore, all client
+ * configuration properties can be applied to JDBC connection.
+ * <p>
+ * JDBC connection URL has the following pattern:
+ * {@code jdbc:gridgain://<hostname>:<port>/<cache_name>?nodeId=<UUID>}<br>
+ * Note the following:
+ * <ul>
+ *     <li>Hostname is required.</li>
+ *     <li>If port is not defined, {@code 11211} is used (default for GridGain client).</li>
+ *     <li>Leave {@code <cache_name>} empty if you are connecting to default cache.</li>
+ *     <li>
+ *         Provide {@code nodeId} parameter if you want to specify node where to execute
+ *         your queries. Note that local and replicated caches will be queried locally on
+ *         this node while partitioned cache is queried distributively. If node ID is not
+ *         provided, random node is used. If node with provided ID doesn't exist,
+ *         exception is thrown.
+ *     </li>
+ * </ul>
+ * Other properties can be defined in {@link Properties} object passed to
+ * {@link DriverManager#getConnection(String, Properties)} method:
+ * <table class="doctable">
+ *     <tr>
+ *         <th>Name</th>
+ *         <th>Description</th>
+ *         <th>Default</th>
+ *         <th>Optional</th>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.protocol</b></td>
+ *         <td>Communication protocol ({@code TCP} or {@code HTTP}).</td>
+ *         <td>{@code TCP}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.connectTimeout</b></td>
+ *         <td>Socket connection timeout.</td>
+ *         <td>{@code 0} (infinite timeout)</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.tcp.noDelay</b></td>
+ *         <td>Flag indicating whether TCP_NODELAY flag should be enabled for outgoing connections.</td>
+ *         <td>{@code true}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.enabled</b></td>
+ *         <td>Flag indicating that {@code SSL} is needed for connection.</td>
+ *         <td>{@code false}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.protocol</b></td>
+ *         <td>SSL protocol ({@code SSL} or {@code TLS}).</td>
+ *         <td>{@code TLS}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.key.algorithm</b></td>
+ *         <td>Key manager algorithm.</td>
+ *         <td>{@code SunX509}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.keystore.location</b></td>
+ *         <td>Key store to be used by client to connect with GridGain topology.</td>
+ *         <td>&nbsp;</td>
+ *         <td>No (if {@code SSL} is enabled)</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.keystore.password</b></td>
+ *         <td>Key store password.</td>
+ *         <td>&nbsp;</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.keystore.type</b></td>
+ *         <td>Key store type.</td>
+ *         <td>{@code jks}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.truststore.location</b></td>
+ *         <td>Trust store to be used by client to connect with GridGain topology.</td>
+ *         <td>&nbsp;</td>
+ *         <td>No (if {@code SSL} is enabled)</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.truststore.password</b></td>
+ *         <td>Trust store password.</td>
+ *         <td>&nbsp;</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.ssl.truststore.type</b></td>
+ *         <td>Trust store type.</td>
+ *         <td>{@code jks}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.credentials</b></td>
+ *         <td>Client credentials used in authentication process.</td>
+ *         <td>&nbsp;</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.cache.top</b></td>
+ *         <td>
+ *             Flag indicating that topology is cached internally. Cache will be refreshed in
+ *             the background with interval defined by {@code gg.client.topology.refresh}
+ *             property (see below).
+ *         </td>
+ *         <td>{@code false}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.topology.refresh</b></td>
+ *         <td>Topology cache refresh frequency (ms).</td>
+ *         <td>{@code 2000}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ *     <tr>
+ *         <td><b>gg.client.idleTimeout</b></td>
+ *         <td>Maximum amount of time that connection can be idle before it is closed (ms).</td>
+ *         <td>{@code 30000}</td>
+ *         <td>Yes</td>
+ *     </tr>
+ * </table>
+ * <h1 class="header">Example</h1>
+ * <pre name="code" class="java">
+ * // Register JDBC driver.
+ * Class.forName("org.gridgain.jdbc.IgniteJdbcDriver");
+ *
+ * // Open JDBC connection.
+ * Connection conn = DriverManager.getConnection("jdbc:gridgain://localhost/cache");
+ *
+ * // Query persons' names
+ * ResultSet rs = conn.createStatement().executeQuery("select name from Person");
+ *
+ * while (rs.next()) {
+ *     String name = rs.getString(1);
+ *
+ *     ...
+ * }
+ *
+ * // Query persons with specific age
+ * PreparedStatement stmt = conn.prepareStatement("select name, age from Person where age = ?");
+ *
+ * stmt.setInt(1, 30);
+ *
+ * ResultSet rs = stmt.executeQuery();
+ *
+ * while (rs.next()) {
+ *     String name = rs.getString("name");
+ *     int age = rs.getInt("age");
+ *
+ *     ...
+ * }
+ * </pre>
+ */
+@SuppressWarnings("JavadocReference")
+public class IgniteJdbcDriver implements Driver {
+    /** Prefix for property names. */
+    private static final String PROP_PREFIX = "gg.jdbc.";
+
+    /** Hostname property name. */
+    static final String PROP_HOST = PROP_PREFIX + "host";
+
+    /** Port number property name. */
+    static final String PROP_PORT = PROP_PREFIX + "port";
+
+    /** Cache name property name. */
+    static final String PROP_CACHE = PROP_PREFIX + "cache";
+
+    /** Node ID URL parameter name. */
+    static final String PARAM_NODE_ID = "nodeId";
+
+    /** Node ID property name. */
+    static final String PROP_NODE_ID = PROP_PREFIX + PARAM_NODE_ID;
+
+    /** URL prefix. */
+    private static final String URL_PREFIX = "jdbc:gridgain://";
+
+    /** Default port. */
+    private static final int DFLT_PORT = 11211;
+
+    /** Major version. */
+    private static final int MAJOR_VER = 1;
+
+    /** Minor version. */
+    private static final int MINOR_VER = 0;
+
+    /**
+     * Register driver.
+     */
+    static {
+        try {
+            DriverManager.registerDriver(new IgniteJdbcDriver());
+        }
+        catch (SQLException e) {
+            throw new RuntimeException("Failed to register GridGain JDBC driver.", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Connection connect(String url, Properties props) throws SQLException {
+        if (!parseUrl(url, props))
+            throw new SQLException("URL is invalid: " + url);
+
+        return new IgniteJdbcConnection(url, props);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean acceptsURL(String url) throws SQLException {
+        return url.startsWith(URL_PREFIX);
+    }
+
+    /** {@inheritDoc} */
+    @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
+        if (!parseUrl(url, info))
+            throw new SQLException("URL is invalid: " + url);
+
+        DriverPropertyInfo[] props = new DriverPropertyInfo[20];
+
+        props[0] = new PropertyInfo("Hostname", info.getProperty(PROP_HOST), true);
+        props[1] = new PropertyInfo("Port number", info.getProperty(PROP_PORT), "");
+        props[2] = new PropertyInfo("Cache name", info.getProperty(PROP_CACHE), "");
+        props[3] = new PropertyInfo("Node ID", info.getProperty(PROP_NODE_ID, ""));
+        props[4] = new PropertyInfo("gg.client.protocol", info.getProperty("gg.client.protocol", "TCP"),
+            "Communication protocol (TCP or HTTP).");
+        props[5] = new PropertyInfo("gg.client.connectTimeout", info.getProperty("gg.client.connectTimeout", "0"),
+            "Socket connection timeout.");
+        props[6] = new PropertyInfo("gg.client.tcp.noDelay", info.getProperty("gg.client.tcp.noDelay", "true"),
+            "Flag indicating whether TCP_NODELAY flag should be enabled for outgoing connections.");
+        props[7] = new PropertyInfo("gg.client.ssl.enabled", info.getProperty("gg.client.ssl.enabled", "false"),
+            "Flag indicating that SSL is needed for connection.");
+        props[8] = new PropertyInfo("gg.client.ssl.protocol", info.getProperty("gg.client.ssl.protocol", "TLS"),
+            "SSL protocol.");
+        props[9] = new PropertyInfo("gg.client.ssl.key.algorithm", info.getProperty("gg.client.ssl.key.algorithm",
+            "SunX509"), "Key manager algorithm.");
+        props[10] = new PropertyInfo("gg.client.ssl.keystore.location",
+            info.getProperty("gg.client.ssl.keystore.location", ""),
+            "Key store to be used by client to connect with GridGain topology.");
+        props[11] = new PropertyInfo("gg.client.ssl.keystore.password",
+            info.getProperty("gg.client.ssl.keystore.password", ""), "Key store password.");
+        props[12] = new PropertyInfo("gg.client.ssl.keystore.type", info.getProperty("gg.client.ssl.keystore.type",
+            "jks"), "Key store type.");
+        props[13] = new PropertyInfo("gg.client.ssl.truststore.location",
+            info.getProperty("gg.client.ssl.truststore.location", ""),
+            "Trust store to be used by client to connect with GridGain topology.");
+        props[14] = new PropertyInfo("gg.client.ssl.keystore.password",
+            info.getProperty("gg.client.ssl.truststore.password", ""), "Trust store password.");
+        props[15] = new PropertyInfo("gg.client.ssl.truststore.type", info.getProperty("gg.client.ssl.truststore.type",
+            "jks"), "Trust store type.");
+        props[16] = new PropertyInfo("gg.client.credentials", info.getProperty("gg.client.credentials", ""),
+            "Client credentials used in authentication process.");
+        props[17] = new PropertyInfo("gg.client.cache.top", info.getProperty("gg.client.cache.top", "false"),
+            "Flag indicating that topology is cached internally. Cache will be refreshed in the background with " +
+                "interval defined by topologyRefreshFrequency property (see below).");
+        props[18] = new PropertyInfo("gg.client.topology.refresh", info.getProperty("gg.client.topology.refresh",
+            "2000"), "Topology cache refresh frequency (ms).");
+        props[19] = new PropertyInfo("gg.client.idleTimeout", info.getProperty("gg.client.idleTimeout", "30000"),
+            "Maximum amount of time that connection can be idle before it is closed (ms).");
+
+        return props;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getMajorVersion() {
+        return MAJOR_VER;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getMinorVersion() {
+        return MINOR_VER;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean jdbcCompliant() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+        throw new SQLFeatureNotSupportedException("java.util.logging is not used.");
+    }
+
+    /**
+     * Validates and parses connection URL.
+     *
+     * @param props Properties.
+     * @param url URL.
+     * @return Whether URL is valid.
+     */
+    private boolean parseUrl(String url, Properties props) {
+        if (url == null || !url.startsWith(URL_PREFIX) || url.length() == URL_PREFIX.length())
+            return false;
+
+        url = url.substring(URL_PREFIX.length());
+
+        String[] parts = url.split("\\?");
+
+        if (parts.length > 2)
+            return false;
+
+        if (parts.length == 2)
+            if (!parseUrlParameters(parts[1], props))
+                return false;
+
+        parts = parts[0].split("/");
+
+        assert parts.length > 0;
+
+        if (parts.length > 2)
+            return false;
+
+        if (parts.length == 2 && !parts[1].isEmpty())
+            props.setProperty(PROP_CACHE, parts[1]);
+
+        url = parts[0];
+
+        parts = url.split(":");
+
+        assert parts.length > 0;
+
+        if (parts.length > 2)
+            return false;
+
+        props.setProperty(PROP_HOST, parts[0]);
+
+        try {
+            props.setProperty(PROP_PORT, String.valueOf(parts.length == 2 ? Integer.valueOf(parts[1]) : DFLT_PORT));
+        }
+        catch (NumberFormatException ignored) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Validates and parses URL parameters.
+     *
+     * @param urlParams URL parameters string.
+     * @param props Properties.
+     * @return Whether URL parameters string is valid.
+     */
+    private boolean parseUrlParameters(String urlParams, Properties props) {
+        String[] params = urlParams.split("&");
+
+        for (String param : params) {
+            String[] pair = param.split("=");
+
+            if (pair.length != 2 || pair[0].isEmpty() || pair[1].isEmpty())
+                return false;
+
+            props.setProperty(PROP_PREFIX + pair[0], pair[1]);
+        }
+
+        return true;
+    }
+
+    /**
+     * Extension of {@link DriverPropertyInfo} that adds
+     * convenient constructors.
+     */
+    private static class PropertyInfo extends DriverPropertyInfo {
+        /**
+         * @param name Name.
+         * @param val Value.
+         */
+        private PropertyInfo(String name, String val) {
+            super(name, val);
+        }
+
+        /**
+         * @param name Name.
+         * @param val Value.
+         * @param desc Description.
+         */
+        private PropertyInfo(String name, String val, String desc) {
+            super(name, val);
+
+            description = desc;
+        }
+
+        /**
+         * @param name Name.
+         * @param val Value.
+         * @param required Required flag.
+         */
+        private PropertyInfo(String name, String val, boolean required) {
+            super(name, val);
+
+            this.required = required;
+        }
+
+        /**
+         * @param name Name.
+         * @param val Value.
+         * @param desc Description.
+         * @param required Required flag.
+         */
+        private PropertyInfo(String name, String val, String desc, boolean required) {
+            super(name, val);
+
+            description = desc;
+            this.required = required;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/531d9ba3/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcPreparedStatement.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcPreparedStatement.java b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcPreparedStatement.java
new file mode 100644
index 0000000..36cdb3e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcPreparedStatement.java
@@ -0,0 +1,411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.jdbc;
+
+import java.io.*;
+import java.math.*;
+import java.net.*;
+import java.sql.*;
+import java.sql.Date;
+import java.util.*;
+
+/**
+ * JDBC prepared statement implementation.
+ */
+class IgniteJdbcPreparedStatement extends IgniteJdbcStatement implements PreparedStatement {
+    /** SQL query. */
+    private final String sql;
+
+    /** Arguments count. */
+    private final int argsCnt;
+
+    /**
+     * Creates new prepared statement.
+     *
+     * @param conn Connection.
+     * @param sql SQL query.
+     */
+    IgniteJdbcPreparedStatement(IgniteJdbcConnection conn, String sql) {
+        super(conn);
+
+        this.sql = sql;
+
+        argsCnt = sql.replaceAll("[^?]", "").length();
+    }
+
+    /** {@inheritDoc} */
+    @Override public ResultSet executeQuery() throws SQLException {
+        ResultSet rs = executeQuery(sql);
+
+        args = null;
+
+        return rs;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int executeUpdate() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNull(int paramIdx, int sqlType) throws SQLException {
+        setArgument(paramIdx, null);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBoolean(int paramIdx, boolean x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setByte(int paramIdx, byte x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setShort(int paramIdx, short x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setInt(int paramIdx, int x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setLong(int paramIdx, long x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setFloat(int paramIdx, float x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setDouble(int paramIdx, double x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBigDecimal(int paramIdx, BigDecimal x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setString(int paramIdx, String x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBytes(int paramIdx, byte[] x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setDate(int paramIdx, Date x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setTime(int paramIdx, Time x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setTimestamp(int paramIdx, Timestamp x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setAsciiStream(int paramIdx, InputStream x, int length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setUnicodeStream(int paramIdx, InputStream x, int length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBinaryStream(int paramIdx, InputStream x, int length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clearParameters() throws SQLException {
+        ensureNotClosed();
+
+        args = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setObject(int paramIdx, Object x, int targetSqlType) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setObject(int paramIdx, Object x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean execute() throws SQLException {
+        return execute(sql);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void addBatch() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setCharacterStream(int paramIdx, Reader x, int length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setRef(int paramIdx, Ref x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBlob(int paramIdx, Blob x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setClob(int paramIdx, Clob x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setArray(int paramIdx, Array x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public ResultSetMetaData getMetaData() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Meta data for prepared statement is not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setDate(int paramIdx, Date x, Calendar cal) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setTime(int paramIdx, Time x, Calendar cal) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setTimestamp(int paramIdx, Timestamp x, Calendar cal) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNull(int paramIdx, int sqlType, String typeName) throws SQLException {
+        setNull(paramIdx, sqlType);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setURL(int paramIdx, URL x) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public ParameterMetaData getParameterMetaData() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Meta data for prepared statement is not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setRowId(int paramIdx, RowId x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNString(int paramIdx, String val) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNCharacterStream(int paramIdx, Reader val, long length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNClob(int paramIdx, NClob val) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setClob(int paramIdx, Reader reader, long length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBlob(int paramIdx, InputStream inputStream, long length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNClob(int paramIdx, Reader reader, long length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSQLXML(int paramIdx, SQLXML xmlObj) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setObject(int paramIdx, Object x, int targetSqlType,
+        int scaleOrLength) throws SQLException {
+        setArgument(paramIdx, x);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setAsciiStream(int paramIdx, InputStream x, long length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBinaryStream(int paramIdx, InputStream x, long length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setCharacterStream(int paramIdx, Reader x, long length) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setAsciiStream(int paramIdx, InputStream x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBinaryStream(int paramIdx, InputStream x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setCharacterStream(int paramIdx, Reader x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNCharacterStream(int paramIdx, Reader val) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setClob(int paramIdx, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setBlob(int paramIdx, InputStream inputStream) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setNClob(int paramIdx, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /**
+     * Sets query argument value.
+     *
+     * @param paramIdx Index.
+     * @param val Value.
+     * @throws SQLException If index is invalid.
+     */
+    private void setArgument(int paramIdx, Object val) throws SQLException {
+        ensureNotClosed();
+
+        if (paramIdx < 1 || paramIdx > argsCnt)
+            throw new SQLException("Parameter index is invalid: " + paramIdx);
+
+        if (args == null)
+            args = new Object[argsCnt];
+
+        args[paramIdx - 1] = val;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/531d9ba3/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSet.java b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSet.java
new file mode 100644
index 0000000..6d839b8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSet.java
@@ -0,0 +1,1520 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.jdbc;
+
+import org.apache.ignite.client.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.jdbc.typedef.*;
+
+import java.io.*;
+import java.math.*;
+import java.net.*;
+import java.sql.*;
+import java.sql.Date;
+import java.util.*;
+
+/**
+ * JDBC result set implementation.
+ */
+class IgniteJdbcResultSet implements ResultSet {
+    /** Task name. */
+    private static final String TASK_NAME =
+        "org.apache.ignite.internal.processors.cache.query.jdbc.GridCacheQueryJdbcTask";
+
+    /** Statement. */
+    private final IgniteJdbcStatement stmt;
+
+    /** Node ID. */
+    private final UUID nodeId;
+
+    /** Future ID. */
+    private final UUID futId;
+
+    /** Table names. */
+    private final List<String> tbls;
+
+    /** Column names. */
+    private final List<String> cols;
+
+    /** Class names. */
+    private final List<String> types;
+
+    /** Fields iterator. */
+    private Iterator<List<Object>> fields;
+
+    /** Finished flag. */
+    private boolean finished;
+
+    /** Current position. */
+    private int pos;
+
+    /** Current. */
+    private List<Object> curr;
+
+    /** Closed flag. */
+    private boolean closed;
+
+    /** Was {@code NULL} flag. */
+    private boolean wasNull;
+
+    /** Fetch size. */
+    private int fetchSize;
+
+    /**
+     * Creates new result set.
+     *
+     * @param stmt Statement.
+     * @param nodeId Node ID.
+     * @param futId Future ID.
+     * @param tbls Table names.
+     * @param cols Column names.
+     * @param types Types.
+     * @param fields Fields.
+     * @param finished Finished flag.
+     * @param fetchSize Fetch size.
+     */
+    IgniteJdbcResultSet(IgniteJdbcStatement stmt, UUID nodeId, UUID futId,
+                        List<String> tbls, List<String> cols, List<String> types,
+                        Collection<List<Object>> fields, boolean finished, int fetchSize) {
+        assert stmt != null;
+        assert nodeId != null;
+        assert futId != null;
+        assert tbls != null;
+        assert cols != null;
+        assert types != null;
+        assert fields != null;
+        assert fetchSize > 0;
+
+        this.stmt = stmt;
+        this.nodeId = nodeId;
+        this.futId = futId;
+        this.tbls = tbls;
+        this.cols = cols;
+        this.types = types;
+        this.fetchSize = fetchSize;
+        this.fields = fields.iterator();
+        this.finished = finished;
+    }
+
+    /**
+     * Creates new result set with predefined fields.
+     * Result set created with this constructor will
+     * never execute remote tasks.
+     *
+     * @param stmt Statement.
+     * @param tbls Table names.
+     * @param cols Column names.
+     * @param types Types.
+     * @param fields Fields.
+     */
+    IgniteJdbcResultSet(IgniteJdbcStatement stmt, List<String> tbls, List<String> cols,
+                        List<String> types, Collection<List<Object>> fields) {
+        assert stmt != null;
+        assert tbls != null;
+        assert cols != null;
+        assert types != null;
+        assert fields != null;
+
+        this.stmt = stmt;
+        this.tbls = tbls;
+        this.cols = cols;
+        this.types = types;
+        this.fields = fields.iterator();
+
+        nodeId = null;
+        futId = null;
+
+        // Prevent task execution.
+        finished = true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean next() throws SQLException {
+        ensureNotClosed();
+
+        if (fields == null && !finished) {
+            assert nodeId != null;
+            assert futId != null;
+
+            try {
+                GridClientCompute compute = stmt.connection().client().compute();
+
+                GridClientCompute prj = compute.projection(compute.node(nodeId));
+
+                byte[] packet = prj.execute(TASK_NAME, JU.marshalArgument(
+                    JU.taskArgument(nodeId, futId, fetchSize, stmt.getMaxRows())));
+
+                byte status = packet[0];
+                byte[] data = new byte[packet.length - 1];
+
+                U.arrayCopy(packet, 1, data, 0, data.length);
+
+                if (status == 1)
+                    throw JU.unmarshalError(data);
+                else {
+                    List<?> msg = JU.unmarshal(data);
+
+                    assert msg.size() == 2;
+
+                    fields = ((Collection<List<Object>>)msg.get(0)).iterator();
+                    finished = (Boolean)msg.get(1);
+                }
+            }
+            catch (GridClientException e) {
+                throw new SQLException("Failed to query GridGain.", e);
+            }
+        }
+
+        if (fields != null && fields.hasNext()) {
+            curr = fields.next();
+
+            if (!fields.hasNext())
+                fields = null;
+
+            pos++;
+
+            return true;
+        }
+        else {
+            curr = null;
+
+            return false;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void close() throws SQLException {
+        closed = true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean wasNull() throws SQLException {
+        return wasNull;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getString(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, String.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean getBoolean(int colIdx) throws SQLException {
+        Boolean val = getTypedValue(colIdx, Boolean.class);
+
+        return val != null ? val : false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte getByte(int colIdx) throws SQLException {
+        Byte val = getTypedValue(colIdx, Byte.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public short getShort(int colIdx) throws SQLException {
+        Short val = getTypedValue(colIdx, Short.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getInt(int colIdx) throws SQLException {
+        Integer val = getTypedValue(colIdx, Integer.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLong(int colIdx) throws SQLException {
+        Long val = getTypedValue(colIdx, Long.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getFloat(int colIdx) throws SQLException {
+        Float val = getTypedValue(colIdx, Float.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public double getDouble(int colIdx) throws SQLException {
+        Double val = getTypedValue(colIdx, Double.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public BigDecimal getBigDecimal(int colIdx, int scale) throws SQLException {
+        return getTypedValue(colIdx, BigDecimal.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] getBytes(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, byte[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Date getDate(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, Date.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Time getTime(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, Time.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Timestamp getTimestamp(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, Timestamp.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public InputStream getAsciiStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public InputStream getUnicodeStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public InputStream getBinaryStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Stream are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getString(String colLb) throws SQLException {
+        return getTypedValue(colLb, String.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean getBoolean(String colLb) throws SQLException {
+        Boolean val = getTypedValue(colLb, Boolean.class);
+
+        return val != null ? val : false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte getByte(String colLb) throws SQLException {
+        Byte val = getTypedValue(colLb, Byte.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public short getShort(String colLb) throws SQLException {
+        Short val = getTypedValue(colLb, Short.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getInt(String colLb) throws SQLException {
+        Integer val = getTypedValue(colLb, Integer.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLong(String colLb) throws SQLException {
+        Long val = getTypedValue(colLb, Long.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getFloat(String colLb) throws SQLException {
+        Float val = getTypedValue(colLb, Float.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public double getDouble(String colLb) throws SQLException {
+        Double val = getTypedValue(colLb, Double.class);
+
+        return val != null ? val : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public BigDecimal getBigDecimal(String colLb, int scale) throws SQLException {
+        return getTypedValue(colLb, BigDecimal.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] getBytes(String colLb) throws SQLException {
+        return getTypedValue(colLb, byte[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Date getDate(String colLb) throws SQLException {
+        return getTypedValue(colLb, Date.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Time getTime(String colLb) throws SQLException {
+        return getTypedValue(colLb, Time.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Timestamp getTimestamp(String colLb) throws SQLException {
+        return getTypedValue(colLb, Timestamp.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public InputStream getAsciiStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public InputStream getUnicodeStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public InputStream getBinaryStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public SQLWarning getWarnings() throws SQLException {
+        ensureNotClosed();
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clearWarnings() throws SQLException {
+        ensureNotClosed();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getCursorName() throws SQLException {
+        ensureNotClosed();
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public ResultSetMetaData getMetaData() throws SQLException {
+        ensureNotClosed();
+
+        return new IgniteJdbcResultSetMetadata(tbls, cols, types);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getObject(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, Object.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getObject(String colLb) throws SQLException {
+        return getTypedValue(colLb, Object.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int findColumn(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        int idx = cols.indexOf(colLb.toUpperCase());
+
+        if (idx == -1)
+            throw new SQLException("Column not found: " + colLb);
+
+        return idx + 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Reader getCharacterStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Reader getCharacterStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Streams are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public BigDecimal getBigDecimal(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, BigDecimal.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public BigDecimal getBigDecimal(String colLb) throws SQLException {
+        return getTypedValue(colLb, BigDecimal.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isBeforeFirst() throws SQLException {
+        ensureNotClosed();
+
+        return pos < 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isAfterLast() throws SQLException {
+        ensureNotClosed();
+
+        return finished && fields == null && curr == null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isFirst() throws SQLException {
+        ensureNotClosed();
+
+        return pos == 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isLast() throws SQLException {
+        ensureNotClosed();
+
+        return finished && fields == null && curr != null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void beforeFirst() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void afterLast() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean first() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean last() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getRow() throws SQLException {
+        ensureNotClosed();
+
+        return isAfterLast() ? 0 : pos;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean absolute(int row) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean relative(int rows) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean previous() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLException("Result set is forward-only.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setFetchDirection(int direction) throws SQLException {
+        ensureNotClosed();
+
+        if (direction != FETCH_FORWARD)
+            throw new SQLFeatureNotSupportedException("Only forward direction is supported");
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getFetchDirection() throws SQLException {
+        ensureNotClosed();
+
+        return FETCH_FORWARD;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setFetchSize(int fetchSize) throws SQLException {
+        ensureNotClosed();
+
+        if (fetchSize <= 0)
+            throw new SQLException("Fetch size must be greater than zero.");
+
+        this.fetchSize = fetchSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getFetchSize() throws SQLException {
+        ensureNotClosed();
+
+        return fetchSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getType() throws SQLException {
+        ensureNotClosed();
+
+        return stmt.getResultSetType();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getConcurrency() throws SQLException {
+        ensureNotClosed();
+
+        return CONCUR_READ_ONLY;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean rowUpdated() throws SQLException {
+        ensureNotClosed();
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean rowInserted() throws SQLException {
+        ensureNotClosed();
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean rowDeleted() throws SQLException {
+        ensureNotClosed();
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNull(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBoolean(int colIdx, boolean x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateByte(int colIdx, byte x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateShort(int colIdx, short x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateInt(int colIdx, int x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateLong(int colIdx, long x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateFloat(int colIdx, float x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateDouble(int colIdx, double x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBigDecimal(int colIdx, BigDecimal x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateString(int colIdx, String x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBytes(int colIdx, byte[] x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateDate(int colIdx, Date x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateTime(int colIdx, Time x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateTimestamp(int colIdx, Timestamp x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateAsciiStream(int colIdx, InputStream x, int len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBinaryStream(int colIdx, InputStream x, int len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateCharacterStream(int colIdx, Reader x, int len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateObject(int colIdx, Object x, int scaleOrLen) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateObject(int colIdx, Object x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNull(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBoolean(String colLb, boolean x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateByte(String colLb, byte x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateShort(String colLb, short x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateInt(String colLb, int x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateLong(String colLb, long x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateFloat(String colLb, float x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateDouble(String colLb, double x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBigDecimal(String colLb, BigDecimal x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateString(String colLb, String x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBytes(String colLb, byte[] x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateDate(String colLb, Date x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateTime(String colLb, Time x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateTimestamp(String colLb, Timestamp x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateAsciiStream(String colLb, InputStream x, int len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBinaryStream(String colLb, InputStream x, int len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateCharacterStream(String colLb, Reader reader, int len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateObject(String colLb, Object x, int scaleOrLen) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateObject(String colLb, Object x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void insertRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void deleteRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void refreshRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Row refreshing is not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void cancelRowUpdates() throws SQLException {
+        ensureNotClosed();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void moveToInsertRow() throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void moveToCurrentRow() throws SQLException {
+        ensureNotClosed();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Statement getStatement() throws SQLException {
+        ensureNotClosed();
+
+        return stmt;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getObject(int colIdx, Map<String, Class<?>> map) throws SQLException {
+        return getTypedValue(colIdx, Object.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Ref getRef(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Blob getBlob(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Clob getClob(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Array getArray(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getObject(String colLb, Map<String, Class<?>> map) throws SQLException {
+        return getTypedValue(colLb, Object.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Ref getRef(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Blob getBlob(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Clob getClob(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Array getArray(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Date getDate(int colIdx, Calendar cal) throws SQLException {
+        return getTypedValue(colIdx, Date.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Date getDate(String colLb, Calendar cal) throws SQLException {
+        return getTypedValue(colLb, Date.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Time getTime(int colIdx, Calendar cal) throws SQLException {
+        return getTypedValue(colIdx, Time.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Time getTime(String colLb, Calendar cal) throws SQLException {
+        return getTypedValue(colLb, Time.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Timestamp getTimestamp(int colIdx, Calendar cal) throws SQLException {
+        return getTypedValue(colIdx, Timestamp.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Timestamp getTimestamp(String colLb, Calendar cal) throws SQLException {
+        return getTypedValue(colLb, Timestamp.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public URL getURL(int colIdx) throws SQLException {
+        return getTypedValue(colIdx, URL.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public URL getURL(String colLb) throws SQLException {
+        return getTypedValue(colLb, URL.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateRef(int colIdx, Ref x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateRef(String colLb, Ref x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBlob(int colIdx, Blob x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBlob(String colLb, Blob x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateClob(int colIdx, Clob x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateClob(String colLb, Clob x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateArray(int colIdx, Array x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateArray(String colLb, Array x) throws SQLException {
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public RowId getRowId(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public RowId getRowId(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateRowId(int colIdx, RowId x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateRowId(String colLb, RowId x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getHoldability() throws SQLException {
+        ensureNotClosed();
+
+        return HOLD_CURSORS_OVER_COMMIT;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isClosed() throws SQLException {
+        return closed;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNString(int colIdx, String nStr) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNString(String colLb, String nStr) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNClob(int colIdx, NClob nClob) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNClob(String colLb, NClob nClob) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public NClob getNClob(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public NClob getNClob(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public SQLXML getSQLXML(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public SQLXML getSQLXML(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateSQLXML(int colIdx, SQLXML xmlObj) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateSQLXML(String colLb, SQLXML xmlObj) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getNString(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getNString(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Reader getNCharacterStream(int colIdx) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Reader getNCharacterStream(String colLb) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNCharacterStream(int colIdx, Reader x, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNCharacterStream(String colLb, Reader reader, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateAsciiStream(int colIdx, InputStream x, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBinaryStream(int colIdx, InputStream x, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateCharacterStream(int colIdx, Reader x, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateAsciiStream(String colLb, InputStream x, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBinaryStream(String colLb, InputStream x, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateCharacterStream(String colLb, Reader reader, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBlob(int colIdx, InputStream inputStream, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBlob(String colLb, InputStream inputStream, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateClob(int colIdx, Reader reader, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateClob(String colLb, Reader reader, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNClob(int colIdx, Reader reader, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNClob(String colLb, Reader reader, long len) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNCharacterStream(int colIdx, Reader x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNCharacterStream(String colLb, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateAsciiStream(int colIdx, InputStream x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBinaryStream(int colIdx, InputStream x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateCharacterStream(int colIdx, Reader x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateAsciiStream(String colLb, InputStream x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBinaryStream(String colLb, InputStream x) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateCharacterStream(String colLb, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBlob(int colIdx, InputStream inputStream) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateBlob(String colLb, InputStream inputStream) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateClob(int colIdx, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateClob(String colLb, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNClob(int colIdx, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateNClob(String colLb, Reader reader) throws SQLException {
+        ensureNotClosed();
+
+        throw new SQLFeatureNotSupportedException("Updates are not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T unwrap(Class<T> iface) throws SQLException {
+        if (!isWrapperFor(iface))
+            throw new SQLException("Result set is not a wrapper for " + iface.getName());
+
+        return (T)this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isWrapperFor(Class<?> iface) throws SQLException {
+        return iface != null && iface == ResultSet.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T getObject(int colIdx, Class<T> type) throws SQLException {
+        return getTypedValue(colIdx, type);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T getObject(String colLb, Class<T> type) throws SQLException {
+        return getTypedValue(colLb, type);
+    }
+
+    /**
+     * Gets casted field value by label.
+     *
+     * @param colLb Column label.
+     * @param cls Value class.
+     * @return Casted field value.
+     * @throws SQLException In case of error.
+     */
+    private <T> T getTypedValue(String colLb, Class<T> cls) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        int colIdx = cols.indexOf(colLb.toUpperCase()) + 1;
+
+        if (colIdx <= 0)
+            throw new SQLException("Invalid column label: " + colLb);
+
+        return getTypedValue(colIdx, cls);
+    }
+
+    /**
+     * Gets casted field value by index.
+     *
+     * @param colIdx Column index.
+     * @param cls Value class.
+     * @return Casted field value.
+     * @throws SQLException In case of error.
+     */
+    private <T> T getTypedValue(int colIdx, Class<T> cls) throws SQLException {
+        ensureNotClosed();
+        ensureHasCurrentRow();
+
+        try {
+            T val = cls == String.class ? (T)String.valueOf(curr.get(colIdx - 1)) : (T)curr.get(colIdx - 1);
+
+            wasNull = val == null;
+
+            return val;
+        }
+        catch (IndexOutOfBoundsException ignored) {
+            throw new SQLException("Invalid column index: " + colIdx);
+        }
+        catch (ClassCastException ignored) {
+            throw new SQLException("Value is an not instance of " + cls.getName());
+        }
+    }
+
+    /**
+     * Ensures that result set is not closed.
+     *
+     * @throws SQLException If result set is closed.
+     */
+    private void ensureNotClosed() throws SQLException {
+        if (closed)
+            throw new SQLException("Result set is closed.");
+    }
+
+    /**
+     * Ensures that result set is positioned on a row.
+     *
+     * @throws SQLException If result set is not positioned on a row.
+     */
+    private void ensureHasCurrentRow() throws SQLException {
+        if (curr == null)
+            throw new SQLException("Result set is not positioned on a row.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/531d9ba3/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSetMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSetMetadata.java b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSetMetadata.java
new file mode 100644
index 0000000..e95e9aa
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/jdbc/IgniteJdbcResultSetMetadata.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.jdbc;
+
+import org.apache.ignite.jdbc.typedef.*;
+
+import java.sql.*;
+import java.util.*;
+
+/**
+ * JDBC result set metadata implementation.
+ */
+class IgniteJdbcResultSetMetadata implements ResultSetMetaData {
+    /** Column width. */
+    private static final int COL_WIDTH = 30;
+
+    /** Table names. */
+    private final List<String> tbls;
+
+    /** Column names. */
+    private final List<String> cols;
+
+    /** Class names. */
+    private final List<String> types;
+
+    /**
+     * @param tbls Table names.
+     * @param cols Column names.
+     * @param types Types.
+     */
+    IgniteJdbcResultSetMetadata(List<String> tbls, List<String> cols, List<String> types) {
+        assert cols != null;
+        assert types != null;
+
+        this.tbls = tbls;
+        this.cols = cols;
+        this.types = types;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getColumnCount() throws SQLException {
+        return cols.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isAutoIncrement(int col) throws SQLException {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isCaseSensitive(int col) throws SQLException {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isSearchable(int col) throws SQLException {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isCurrency(int col) throws SQLException {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int isNullable(int col) throws SQLException {
+        return columnNullable;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isSigned(int col) throws SQLException {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getColumnDisplaySize(int col) throws SQLException {
+        return COL_WIDTH;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getColumnLabel(int col) throws SQLException {
+        return cols.get(col - 1);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getColumnName(int col) throws SQLException {
+        return cols.get(col - 1);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSchemaName(int col) throws SQLException {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getPrecision(int col) throws SQLException {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getScale(int col) throws SQLException {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getTableName(int col) throws SQLException {
+        return tbls != null ? tbls.get(col - 1) : "";
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getCatalogName(int col) throws SQLException {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getColumnType(int col) throws SQLException {
+        return JU.type(types.get(col - 1));
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getColumnTypeName(int col) throws SQLException {
+        return JU.typeName(types.get(col - 1));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isReadOnly(int col) throws SQLException {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isWritable(int col) throws SQLException {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isDefinitelyWritable(int col) throws SQLException {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getColumnClassName(int col) throws SQLException {
+        return types.get(col - 1);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T unwrap(Class<T> iface) throws SQLException {
+        if (!isWrapperFor(iface))
+            throw new SQLException("Result set meta data is not a wrapper for " + iface.getName());
+
+        return (T)this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isWrapperFor(Class<?> iface) throws SQLException {
+        return iface == ResultSetMetaData.class;
+    }
+}


Mime
View raw message