This is an automated email from the ASF dual-hosted git repository.

nizhikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 95afef05ca5 IGNITE-6141 JDBC: add basic support for BLOB and CLOB 
types (#11492)
95afef05ca5 is described below

commit 95afef05ca5cbd278734813162d10e04a09cdcf3
Author: Sergey Korotkov <[email protected]>
AuthorDate: Wed Sep 11 14:23:20 2024 +0700

    IGNITE-6141 JDBC: add basic support for BLOB and CLOB types (#11492)
    
    Co-authored-by: rkondakov <[email protected]>
---
 .../jdbc2/JdbcAbstractDmlStatementSelfTest.java    |  26 +-
 .../jdbc2/JdbcAbstractUpdateStatementSelfTest.java |   8 +-
 .../apache/ignite/internal/jdbc2/JdbcBlobTest.java |   3 +
 .../apache/ignite/internal/jdbc2/JdbcClobTest.java | 452 +++++++++++++++++++++
 .../jdbc2/JdbcInsertStatementSelfTest.java         |  59 ++-
 .../internal/jdbc2/JdbcMergeStatementSelfTest.java |  48 ++-
 .../jdbc2/JdbcPreparedStatementSelfTest.java       |  54 ++-
 .../internal/jdbc2/JdbcResultSetSelfTest.java      |  51 ++-
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java      |   2 +
 .../thin/JdbcThinAbstractDmlStatementSelfTest.java |  72 +++-
 .../JdbcThinAbstractUpdateStatementSelfTest.java   |   8 +-
 .../jdbc/thin/JdbcThinConnectionSelfTest.java      |  24 +-
 .../jdbc/thin/JdbcThinInsertStatementSelfTest.java |  41 +-
 .../jdbc/thin/JdbcThinMergeStatementSelfTest.java  |  25 +-
 .../thin/JdbcThinPreparedStatementSelfTest.java    |  82 +++-
 .../jdbc/thin/JdbcThinResultSetSelfTest.java       |  76 ++--
 .../internal/jdbc/thin/JdbcThinConnection.java     |   6 +-
 .../jdbc/thin/JdbcThinPreparedStatement.java       |   8 +-
 .../internal/jdbc/thin/JdbcThinResultSet.java      |  18 +-
 .../org/apache/ignite/internal/jdbc2/JdbcBlob.java |   2 +-
 .../org/apache/ignite/internal/jdbc2/JdbcClob.java | 326 +++++++++++++++
 .../ignite/internal/jdbc2/JdbcConnection.java      |   2 +-
 .../internal/jdbc2/JdbcPreparedStatement.java      |   4 +-
 .../ignite/internal/jdbc2/JdbcResultSet.java       |   8 +-
 .../processors/query/h2/IgniteH2Indexing.java      |   5 +-
 .../processors/query/h2/QueryParameters.java       |  11 +
 26 files changed, 1242 insertions(+), 179 deletions(-)

diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
index c1142e19685..d1ddd2ba0ba 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java
@@ -20,9 +20,11 @@ package org.apache.ignite.internal.jdbc2;
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.Collections;
 import org.apache.ignite.cache.QueryEntity;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
@@ -53,7 +55,7 @@ public abstract class JdbcAbstractDmlStatementSelfTest 
extends GridCommonAbstrac
         CFG_URL_PREFIX + "cache=" + DEFAULT_CACHE_NAME + 
"@modules/clients/src/test/config/jdbc-bin-config.xml";
 
     /** SQL SELECT query for verification. */
-    static final String SQL_SELECT = "select _key, id, firstName, lastName, 
age, data from Person";
+    static final String SQL_SELECT = "select _key, id, firstName, lastName, 
age, data, text from Person";
 
     /** Alias for _key */
     private static final String KEY_ALIAS = "key";
@@ -112,6 +114,7 @@ public abstract class JdbcAbstractDmlStatementSelfTest 
extends GridCommonAbstrac
         e.addQueryField("firstName", String.class.getName(), null);
         e.addQueryField("lastName", String.class.getName(), null);
         e.addQueryField("data", byte[].class.getName(), null);
+        e.addQueryField("text", String.class.getName(), null);
 
         cache.setQueryEntities(Collections.singletonList(e));
 
@@ -189,6 +192,18 @@ public abstract class JdbcAbstractDmlStatementSelfTest 
extends GridCommonAbstrac
         }
     }
 
+    /**
+     * @param clob Clob.
+     */
+    static String str(Clob clob) {
+        try {
+            return clob.getSubString(1, (int)clob.length());
+        }
+        catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * Person.
      */
@@ -213,6 +228,10 @@ public abstract class JdbcAbstractDmlStatementSelfTest 
extends GridCommonAbstrac
         @QuerySqlField
         private final byte[] data;
 
+        /** CLOB data. */
+        @QuerySqlField
+        private final String text;
+
         /**
          * @param id ID.
          * @param firstName First name.
@@ -229,6 +248,7 @@ public abstract class JdbcAbstractDmlStatementSelfTest 
extends GridCommonAbstrac
             this.lastName = lastName;
             this.age = age;
             this.data = getBytes(lastName);
+            this.text = firstName + " " + lastName;
         }
 
         /** {@inheritDoc} */
@@ -241,6 +261,8 @@ public abstract class JdbcAbstractDmlStatementSelfTest 
extends GridCommonAbstrac
             if (id != person.id) return false;
             if (age != person.age) return false;
             if (firstName != null ? !firstName.equals(person.firstName) : 
person.firstName != null) return false;
+            if (data != null ? !Arrays.equals(data, person.data) : person.data 
!= null) return false;
+            if (text != null ? !text.equals(person.text) : person.text != 
null) return false;
             return lastName != null ? lastName.equals(person.lastName) : 
person.lastName == null;
 
         }
@@ -251,6 +273,8 @@ public abstract class JdbcAbstractDmlStatementSelfTest 
extends GridCommonAbstrac
             result = 31 * result + (firstName != null ? firstName.hashCode() : 
0);
             result = 31 * result + (lastName != null ? lastName.hashCode() : 
0);
             result = 31 * result + age;
+            result = 31 * result + (data != null ? Arrays.hashCode(data) : 0);
+            result = 31 * result + (text != null ? text.hashCode() : 0);
             return result;
         }
     }
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractUpdateStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractUpdateStatementSelfTest.java
index ace1be66561..66d81a5d0bf 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractUpdateStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractUpdateStatementSelfTest.java
@@ -24,10 +24,10 @@ import java.sql.Statement;
  */
 public abstract class JdbcAbstractUpdateStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest {
     /** SQL query to populate cache. */
-    private static final String ITEMS_SQL = "insert into Person(_key, id, 
firstName, lastName, age, data) values " +
-        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White')), " +
-        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black')), " +
-        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'))";
+    private static final String ITEMS_SQL = "insert into Person(_key, id, 
firstName, lastName, age, data, text) values " +
+        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White'), 'John White'), " +
+        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black'), 'Joe Black'), " +
+        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'), 'Mike Green')";
 
     /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBlobTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBlobTest.java
index 1680161e07a..0b0119e48e9 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBlobTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBlobTest.java
@@ -113,6 +113,9 @@ public class JdbcBlobTest {
         res = blob.getBytes(1, 0);
         assertEquals(0, res.length);
 
+        blob = new JdbcBlob(new byte[0]);
+        assertEquals(0, blob.getBytes(1, 0).length);
+
         blob.free();
 
         try {
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcClobTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcClobTest.java
new file mode 100644
index 00000000000..6dc5826d6e6
--- /dev/null
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcClobTest.java
@@ -0,0 +1,452 @@
+/*
+ * 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.internal.jdbc2;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.sql.Clob;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrows;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for JDBC CLOB.
+ */
+public class JdbcClobTest {
+    /** */
+    static final String ERROR_CLOB_FREE = "Clob instance can't be used after 
free() has been called.";
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testLength() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        assertEquals(10, clob.length());
+
+        clob.free();
+        assertThrows(null, clob::length, SQLException.class, ERROR_CLOB_FREE);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetSubString() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> clob.getSubString(-1, 1), SQLException.class, 
null);
+
+        assertThrows(null, () -> clob.getSubString(0, 1), SQLException.class, 
null);
+
+        assertThrows(null, () -> clob.getSubString(1, -1), SQLException.class, 
null);
+
+        assertThrows(null, () -> clob.getSubString(1, 11), SQLException.class, 
null);
+
+        assertEquals("", clob.getSubString(3, 0));
+
+        assertEquals("1", clob.getSubString(1, 1));
+
+        assertEquals("0", clob.getSubString(10, 1));
+
+        assertEquals("12345", clob.getSubString(1, 5));
+
+        assertEquals("34567", clob.getSubString(3, 5));
+
+        assertEquals("567890", clob.getSubString(5, 6));
+
+        assertEquals("1234567890", clob.getSubString(1, 10));
+
+        clob.free();
+        assertThrows(null, () -> clob.getSubString(1, 10), SQLException.class, 
ERROR_CLOB_FREE);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetCharacterStream() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        Reader cStream = clob.getCharacterStream();
+        String res = IOUtils.toString(cStream);
+        assertEquals("1234567890", res);
+
+        clob.free();
+        assertThrows(null, () -> clob.getCharacterStream(), 
SQLException.class, ERROR_CLOB_FREE);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetCharacterStreamWithParams() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> clob.getCharacterStream(-1, 1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.getCharacterStream(0, 1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.getCharacterStream(1, -1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.getCharacterStream(1, 11), 
SQLException.class, null);
+
+        Reader cStream = clob.getCharacterStream(1, 10);
+        String res = IOUtils.toString(cStream);
+        assertEquals("1234567890", res);
+
+        cStream = clob.getCharacterStream(1, 1);
+        res = IOUtils.toString(cStream);
+        assertEquals("1", res);
+
+        cStream = clob.getCharacterStream(10, 1);
+        res = IOUtils.toString(cStream);
+        assertEquals("0", res);
+
+        cStream = clob.getCharacterStream(3, 5);
+        res = IOUtils.toString(cStream);
+        assertEquals("34567", res);
+
+        cStream = clob.getCharacterStream(3, 0);
+        res = IOUtils.toString(cStream);
+        assertEquals("", res);
+
+        clob.free();
+        assertThrows(null, () -> clob.getCharacterStream(1, 1), 
SQLException.class, ERROR_CLOB_FREE);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetAsciiStream() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+        byte[] bytes = IOUtils.toByteArray(clob.getAsciiStream());
+        Assert.assertArrayEquals("1234567890".getBytes(UTF_8), bytes);
+
+        clob.free();
+        assertThrows(null, clob::getAsciiStream, SQLException.class, 
ERROR_CLOB_FREE);
+
+        Clob emptyClob = new JdbcClob("");
+        bytes = IOUtils.toByteArray(emptyClob.getAsciiStream());
+        Assert.assertArrayEquals("".getBytes(UTF_8), bytes);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetAsciiStreamForNonAsciiDataBufferedRead() throws 
Exception {
+        StringBuilder sb = new StringBuilder();
+
+        // Create string in a way which makes sure that all variants in
+        // JdbcClob.Utf8EncodedStringInputStream.encodeNextChunk() are covered.
+        // In particular the check for the surrogate element.
+        for (int i = 0; i < 3277; i++) {
+            sb.append("aa©😀");
+        }
+
+        Clob clob = new JdbcClob(sb.toString());
+
+        InputStream stream = clob.getAsciiStream();
+
+        assertThrows(null, () -> stream.read(null, 0, 1), 
NullPointerException.class, null);
+
+        assertThrows(null, () -> stream.read(new byte[10], -1, 5), 
IndexOutOfBoundsException.class, null);
+
+        assertThrows(null, () -> stream.read(new byte[10], 5, -1), 
IndexOutOfBoundsException.class, null);
+
+        assertThrows(null, () -> stream.read(new byte[10], 11, 1), 
IndexOutOfBoundsException.class, null);
+
+        assertThrows(null, () -> stream.read(new byte[10], 5, 6), 
IndexOutOfBoundsException.class, null);
+
+        assertEquals(0, stream.read(new byte[10], 5, 0));
+
+        byte[] bytes = IOUtils.toByteArray(stream);
+
+        String reencoded = new String(bytes, UTF_8);
+
+        assertEquals(clob.getSubString(1, (int)clob.length()), reencoded);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetAsciiStreamForNonAsciiDataReadByByte() throws Exception 
{
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < 10; i++) {
+            sb.append("aa©😀");
+        }
+
+        Clob clob = new JdbcClob(sb.toString());
+
+        InputStream stream = clob.getAsciiStream();
+
+        int i = 0;
+        byte[] bytes = new byte[80];
+
+        byte val = (byte)stream.read();
+
+        while (val != -1) {
+            bytes[i++] = val;
+
+            val = (byte)stream.read();
+        }
+
+        String reencoded = new String(bytes, UTF_8);
+
+        assertEquals(clob.getSubString(1, (int)clob.length()), reencoded);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPositionWithStringPattern() throws Exception {
+        JdbcClob clob1 = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> clob1.position("0", 0), SQLException.class, 
null);
+
+        assertThrows(null, () -> clob1.position("0", -1), SQLException.class, 
null);
+
+        assertEquals(1, clob1.position("", 1));
+
+        assertEquals(10, clob1.position("", 10));
+
+        assertEquals(11, clob1.position("", 100));
+
+        assertEquals(-1, clob1.position("a", 11));
+
+        assertEquals(1, clob1.position("1", 1));
+
+        assertEquals(5, clob1.position("56", 1));
+
+        assertEquals(5, clob1.position("56", 5));
+
+        assertEquals(-1, clob1.position("56", 6));
+
+        clob1.free();
+        assertThrows(null, clob1::getAsciiStream, SQLException.class, 
ERROR_CLOB_FREE);
+
+        Clob clob2 = new JdbcClob("abbabab");
+
+        assertEquals(5, clob2.position("b", 4));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPositionWithClobPattern() throws Exception {
+        Clob clob = new JdbcClob("1234567890");
+
+        Clob patternClob = new JdbcClob("567");
+
+        assertThrows(null, () -> clob.position(patternClob, 0), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.position(patternClob, -1), 
SQLException.class, null);
+
+        assertEquals(5, clob.position(patternClob, 1));
+
+        assertEquals(5, clob.position(patternClob, 5));
+
+        assertEquals(-1, clob.position(patternClob, 6));
+
+        Clob patternClob2 = new JdbcClob("a");
+
+        assertEquals(-1, clob.position(patternClob2, 1));
+
+        clob.free();
+        assertThrows(null, () -> clob.position(patternClob2, 5), 
SQLException.class, ERROR_CLOB_FREE);
+
+        Clob clob2 = new JdbcClob("bbabbabba");
+
+        assertEquals(6, clob2.position(patternClob2, 5));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSetString() throws Exception {
+        JdbcClob clob1 = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> clob1.setString(-1, "a"), SQLException.class, 
null);
+
+        assertThrows(null, () -> clob1.setString(0, "a"), SQLException.class, 
null);
+
+        assertThrows(null, () -> clob1.setString(clob1.length() + 2, "a"), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob1.setString(1, null), SQLException.class, 
null);
+
+        int written = clob1.setString(1, "a");
+        assertEquals("a", clob1.getSubString(1, 1));
+        assertEquals(1, written);
+
+        written = clob1.setString(5, "abc");
+        assertEquals("abc", clob1.getSubString(5, 3));
+        assertEquals(3, written);
+
+        written = clob1.setString(10, "def");
+        assertEquals("def", clob1.getSubString(10, 3));
+        assertEquals(3, written);
+
+        clob1.free();
+        assertThrows(null, () -> clob1.setString(1, "a"), SQLException.class, 
ERROR_CLOB_FREE);
+
+        Clob clob2 = new JdbcClob("12345");
+        written = clob2.setString(3, "abcd");
+        assertEquals("12abcd", clob2.getSubString(1, (int)clob2.length()));
+        assertEquals(4, written);
+
+        Clob clob3 = new JdbcClob("12345");
+        written = clob3.setString(3, "ab");
+        assertEquals("12ab5", clob3.getSubString(1, (int)clob3.length()));
+        assertEquals(2, written);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSetStringWithSubString() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> clob.setString(-1, "a", 0, 1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.setString(0, "a", 0, 1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.setString(clob.length() + 2, "a", 0, 1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.setString(1, null, 0, 1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.setString(1, "a", -1, 1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.setString(1, "a", 0, -1), 
SQLException.class, null);
+
+        assertThrows(null, () -> clob.setString(1, "abc", 1, 3), 
SQLException.class, null);
+
+        clob.free();
+        assertThrows(null, () -> clob.setString(1, "a", 0, 1), 
SQLException.class, ERROR_CLOB_FREE);
+
+        Clob clob2 = new JdbcClob("1234567890");
+        int written = clob2.setString(3, "abcd", 0, 1);
+        assertEquals("12a4567890", clob2.getSubString(1, (int)clob2.length()));
+        assertEquals(1, written);
+
+        clob2 = new JdbcClob("1234567890");
+        written = clob2.setString(1, "abcd", 0, 3);
+        assertEquals("abc4567890", clob2.getSubString(1, (int)clob2.length()));
+        assertEquals(3, written);
+
+        clob2 = new JdbcClob("1234567890");
+        written = clob2.setString(5, "abcd", 2, 2);
+        assertEquals("1234cd7890", clob2.getSubString(1, (int)clob2.length()));
+        assertEquals(2, written);
+
+        clob2 = new JdbcClob("1234567890");
+        written = clob2.setString(9, "abcd", 0, 4);
+        assertEquals("12345678abcd", clob2.getSubString(1, 
(int)clob2.length()));
+        assertEquals(4, written);
+
+        clob2 = new JdbcClob("1234567890");
+        written = clob2.setString(11, "abcd", 0, 4);
+        assertEquals("1234567890abcd", clob2.getSubString(1, 
(int)clob2.length()));
+        assertEquals(4, written);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testTruncate() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> {
+            clob.truncate(-1);
+
+            return null;
+        }, SQLException.class, null);
+
+        assertThrows(null, () -> {
+            clob.truncate(clob.length() + 1);
+
+            return null;
+        }, SQLException.class, null);
+
+        clob.truncate(9);
+        assertEquals("123456789", clob.getSubString(1, (int)clob.length()));
+
+        clob.truncate(5);
+        assertEquals("12345", clob.getSubString(1, (int)clob.length()));
+
+        clob.truncate(0);
+        assertEquals("", clob.getSubString(1, (int)clob.length()));
+
+        clob.free();
+        assertThrows(null, () -> {
+            clob.truncate(1);
+
+            return null;
+        }, SQLException.class, ERROR_CLOB_FREE);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSetAsciiStream() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> clob.setAsciiStream(1L), 
SQLFeatureNotSupportedException.class, null);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSetCharacterStream() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        assertThrows(null, () -> clob.setCharacterStream(1L), 
SQLFeatureNotSupportedException.class, null);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testFree() throws Exception {
+        JdbcClob clob = new JdbcClob("1234567890");
+
+        clob.length();
+
+        clob.free();
+
+        clob.free();
+
+        assertThrows(null, clob::length, SQLException.class, ERROR_CLOB_FREE);
+    }
+}
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
index 6b39ccb587d..072155f4d3e 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java
@@ -18,6 +18,8 @@
 package org.apache.ignite.internal.jdbc2;
 
 import java.sql.BatchUpdateException;
+import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -36,14 +38,14 @@ import org.junit.Test;
  */
 public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest {
     /** SQL query. */
-    private static final String SQL = "insert into Person(_key, id, firstName, 
lastName, age, data) values " +
-        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White')), " +
-        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black')), " +
-        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'))";
+    private static final String SQL = "insert into Person(_key, id, firstName, 
lastName, age, data, text) values " +
+        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White'), 'John White'), " +
+        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black'), 'Joe Black'), " +
+        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'), 'Mike Green')";
 
     /** SQL query. */
-    private static final String SQL_PREPARED = "insert into Person(_key, id, 
firstName, lastName, age, data) values " +
-        "(?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)";
+    private static final String SQL_PREPARED = "insert into Person(_key, id, 
firstName, lastName, age, data, text) " +
+        "values (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?)";
 
     /** Statement. */
     private Statement stmt;
@@ -83,6 +85,7 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                         assertEquals("White", rs.getString("lastName"));
                         assertEquals(25, rs.getInt("age"));
                         assertEquals("White", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("John White", str(rs.getClob("text")));
                         break;
 
                     case 2:
@@ -91,6 +94,7 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                         assertEquals("Black", rs.getString("lastName"));
                         assertEquals(35, rs.getInt("age"));
                         assertEquals("Black", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Joe Black", str(rs.getClob("text")));
                         break;
 
                     case 3:
@@ -99,6 +103,7 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                         assertEquals("Green", rs.getString("lastName"));
                         assertEquals(40, rs.getInt("age"));
                         assertEquals("Green", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Mike Green", str(rs.getClob("text")));
                         break;
 
                     case 4:
@@ -107,6 +112,7 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                         assertEquals("Grey", rs.getString("lastName"));
                         assertEquals(22, rs.getInt("age"));
                         assertEquals("Grey", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Leah Grey", str(rs.getClob("text")));
                         break;
 
                     default:
@@ -302,6 +308,9 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
     private void formBatch(int id1, int id2) throws SQLException {
         int[] ids = new int[] { id1, id2 };
 
+        Clob clob;
+        Blob blob;
+
         int arg = 0;
         for (int id: ids) {
             String key = "p" + id;
@@ -313,7 +322,14 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                     prepStmt.setString(arg + 3, "John");
                     prepStmt.setString(arg + 4, "White");
                     prepStmt.setInt(arg + 5, 25);
-                    prepStmt.setBytes(arg + 6, getBytes("White"));
+
+                    blob = conn.createBlob();
+                    blob.setBytes(1, getBytes("White"));
+                    prepStmt.setBlob(arg + 6, blob);
+
+                    clob = conn.createClob();
+                    clob.setString(1, "John White");
+                    prepStmt.setClob(arg + 7, clob);
 
                     break;
 
@@ -323,7 +339,14 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                     prepStmt.setString(arg + 3, "Joe");
                     prepStmt.setString(arg + 4, "Black");
                     prepStmt.setInt(arg + 5, 35);
-                    prepStmt.setBytes(arg + 6, getBytes("Black"));
+
+                    blob = conn.createBlob();
+                    blob.setBytes(1, getBytes("Black"));
+                    prepStmt.setBlob(arg + 6, blob);
+
+                    clob = conn.createClob();
+                    clob.setString(1, "Joe Black");
+                    prepStmt.setClob(arg + 7, clob);
 
                     break;
 
@@ -333,7 +356,14 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                     prepStmt.setString(arg + 3, "Mike");
                     prepStmt.setString(arg + 4, "Green");
                     prepStmt.setInt(arg + 5, 40);
-                    prepStmt.setBytes(arg + 6, getBytes("Green"));
+
+                    blob = conn.createBlob();
+                    blob.setBytes(1, getBytes("Green"));
+                    prepStmt.setBlob(arg + 6, blob);
+
+                    clob = conn.createClob();
+                    clob.setString(1, "Mike Green");
+                    prepStmt.setClob(arg + 7, clob);
 
                     break;
 
@@ -343,7 +373,14 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                     prepStmt.setString(arg + 3, "Leah");
                     prepStmt.setString(arg + 4, "Grey");
                     prepStmt.setInt(arg + 5, 22);
-                    prepStmt.setBytes(arg + 6, getBytes("Grey"));
+
+                    blob = conn.createBlob();
+                    blob.setBytes(1, getBytes("Grey"));
+                    prepStmt.setBlob(arg + 6, blob);
+
+                    clob = conn.createClob();
+                    clob.setString(1, "Leah Grey");
+                    prepStmt.setClob(arg + 7, clob);
 
                     break;
 
@@ -351,7 +388,7 @@ public class JdbcInsertStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTes
                     assert false;
             }
 
-            arg += 6;
+            arg += 7;
         }
 
         prepStmt.addBatch();
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java
index 3923333a4e5..c5da516d957 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java
@@ -30,14 +30,14 @@ import org.junit.Test;
  */
 public class JdbcMergeStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest {
     /** SQL query. */
-    private static final String SQL = "merge into Person(_key, id, firstName, 
lastName, age, data) values " +
-        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White')), " +
-        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black')), " +
-        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'))";
+    private static final String SQL = "merge into Person(_key, id, firstName, 
lastName, age, data, text) values " +
+        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White'), 'John White'), " +
+        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black'), 'Joe Black'), " +
+        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'), 'Mike Green')";
 
     /** SQL query. */
-    protected static final String SQL_PREPARED = "merge into Person(_key, id, 
firstName, lastName, age, data) values " +
-        "(?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)";
+    protected static final String SQL_PREPARED = "merge into Person(_key, id, 
firstName, lastName, age, data, text) " +
+        "values (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?)";
 
     /** Statement. */
     protected Statement stmt;
@@ -77,6 +77,7 @@ public class JdbcMergeStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest
                         assertEquals("White", rs.getString("lastName"));
                         assertEquals(25, rs.getInt("age"));
                         assertEquals("White", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("John White", str(rs.getClob("text")));
                         break;
 
                     case 2:
@@ -85,6 +86,7 @@ public class JdbcMergeStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest
                         assertEquals("Black", rs.getString("lastName"));
                         assertEquals(35, rs.getInt("age"));
                         assertEquals("Black", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Joe Black", str(rs.getClob("text")));
                         break;
 
                     case 3:
@@ -93,6 +95,7 @@ public class JdbcMergeStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest
                         assertEquals("Green", rs.getString("lastName"));
                         assertEquals(40, rs.getInt("age"));
                         assertEquals("Green", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Mike Green", str(rs.getClob("text")));
                         break;
 
                     case 4:
@@ -101,6 +104,7 @@ public class JdbcMergeStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest
                         assertEquals("Grey", rs.getString("lastName"));
                         assertEquals(22, rs.getInt("age"));
                         assertEquals("Grey", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Leah Grey", str(rs.getClob("text")));
                         break;
 
                     default:
@@ -159,13 +163,15 @@ public class JdbcMergeStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest
         prepStmt.setString(4, "White");
         prepStmt.setInt(5, 25);
         prepStmt.setBytes(6, getBytes("White"));
-
-        prepStmt.setString(7, "p2");
-        prepStmt.setInt(8, 2);
-        prepStmt.setString(9, "Joe");
-        prepStmt.setString(10, "Black");
-        prepStmt.setInt(11, 35);
-        prepStmt.setBytes(12, getBytes("Black"));
+        prepStmt.setString(7, "John White");
+
+        prepStmt.setString(8, "p2");
+        prepStmt.setInt(9, 2);
+        prepStmt.setString(10, "Joe");
+        prepStmt.setString(11, "Black");
+        prepStmt.setInt(12, 35);
+        prepStmt.setBytes(13, getBytes("Black"));
+        prepStmt.setString(14, "Joe Black");
         prepStmt.addBatch();
 
         prepStmt.setString(1, "p3");
@@ -174,13 +180,15 @@ public class JdbcMergeStatementSelfTest extends 
JdbcAbstractDmlStatementSelfTest
         prepStmt.setString(4, "Green");
         prepStmt.setInt(5, 40);
         prepStmt.setBytes(6, getBytes("Green"));
-
-        prepStmt.setString(7, "p4");
-        prepStmt.setInt(8, 4);
-        prepStmt.setString(9, "Leah");
-        prepStmt.setString(10, "Grey");
-        prepStmt.setInt(11, 22);
-        prepStmt.setBytes(12, getBytes("Grey"));
+        prepStmt.setString(7, "Mike Green");
+
+        prepStmt.setString(8, "p4");
+        prepStmt.setInt(9, 4);
+        prepStmt.setString(10, "Leah");
+        prepStmt.setString(11, "Grey");
+        prepStmt.setInt(12, 22);
+        prepStmt.setBytes(13, getBytes("Grey"));
+        prepStmt.setString(14, "Leah Grey");
 
         prepStmt.addBatch();
 
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatementSelfTest.java
index 98998d21f54..4cbee28c77b 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatementSelfTest.java
@@ -21,6 +21,7 @@ import java.io.Serializable;
 import java.math.BigDecimal;
 import java.net.URL;
 import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
@@ -38,7 +39,9 @@ import org.junit.Test;
 
 import static java.sql.Types.BIGINT;
 import static java.sql.Types.BINARY;
+import static java.sql.Types.BLOB;
 import static java.sql.Types.BOOLEAN;
+import static java.sql.Types.CLOB;
 import static java.sql.Types.DATALINK;
 import static java.sql.Types.DATE;
 import static java.sql.Types.DOUBLE;
@@ -108,6 +111,7 @@ public class JdbcPreparedStatementSelfTest extends 
GridCommonAbstractTest {
         o.strVal = "str";
         o.arrVal = new byte[] {1};
         o.blobVal = new byte[] {1};
+        o.clobVal = "large str";
         o.dateVal = new Date(1);
         o.timeVal = new Time(1);
         o.tsVal = new Timestamp(1);
@@ -571,31 +575,45 @@ public class JdbcPreparedStatementSelfTest extends 
GridCommonAbstractTest {
 
         ResultSet rs = stmt.executeQuery();
 
-        int cnt = 0;
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt("id"));
+        assertFalse(rs.next());
 
-        while (rs.next()) {
-            if (cnt == 0)
-                assert rs.getInt("id") == 1;
+        stmt.setNull(1, BLOB);
 
-            cnt++;
-        }
+        rs = stmt.executeQuery();
 
-        assertEquals(1, cnt);
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt("id"));
+        assertFalse(rs.next());
+    }
 
-        stmt.setNull(1, BINARY);
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testClob() throws Exception {
+        stmt = conn.prepareStatement("select * from TestObject where clobVal 
is not distinct from ?");
 
-        rs = stmt.executeQuery();
+        Clob clob = conn.createClob();
 
-        cnt = 0;
+        clob.setString(1, "large str");
 
-        while (rs.next()) {
-            if (cnt == 0)
-                assert rs.getInt("id") == 2;
+        stmt.setClob(1, clob);
 
-            cnt++;
-        }
+        ResultSet rs = stmt.executeQuery();
 
-        assert cnt == 1;
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt("id"));
+        assertFalse(rs.next());
+
+        stmt.setNull(1, CLOB);
+
+        rs = stmt.executeQuery();
+
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt("id"));
+        assertFalse(rs.next());
     }
 
     /**
@@ -802,6 +820,10 @@ public class JdbcPreparedStatementSelfTest extends 
GridCommonAbstractTest {
         @QuerySqlField
         private byte[] blobVal;
 
+        /** */
+        @QuerySqlField
+        private String clobVal;
+
         /** */
         @QuerySqlField
         private Date dateVal;
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java
index e65bacdc35c..1470a5cfcf3 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java
@@ -22,6 +22,8 @@ import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.Date;
 import java.sql.DriverManager;
 import java.sql.ResultSet;
@@ -46,6 +48,7 @@ import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.jetbrains.annotations.Nullable;
+import org.junit.Assert;
 import org.junit.Test;
 
 import static org.apache.ignite.IgniteJdbcDriver.CFG_URL_PREFIX;
@@ -64,7 +67,7 @@ public class JdbcResultSetSelfTest extends 
GridCommonAbstractTest {
     private static final String SQL =
         "select id, boolVal, byteVal, shortVal, intVal, longVal, floatVal, " +
             "doubleVal, bigVal, strVal, arrVal, dateVal, timeVal, tsVal, 
urlVal, f1, f2, f3, _val, " +
-            "boolVal2, boolVal3, boolVal4 " +
+            "boolVal2, boolVal3, boolVal4, blobVal, clobVal " +
             "from TestObject where id = 1";
 
     /** Statement. */
@@ -145,6 +148,8 @@ public class JdbcResultSetSelfTest extends 
GridCommonAbstractTest {
         o.bigVal = new BigDecimal(1);
         o.strVal = "1";
         o.arrVal = new byte[] {1};
+        o.blobVal = new byte[] {1};
+        o.clobVal = "str";
         o.dateVal = new Date(1, 1, 1);
         o.timeVal = new Time(1, 1, 1);
         o.tsVal = new Timestamp(1);
@@ -670,6 +675,38 @@ public class JdbcResultSetSelfTest extends 
GridCommonAbstractTest {
         assert cnt == 1;
     }
 
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testBlob() throws Exception {
+        ResultSet rs = stmt.executeQuery(SQL);
+
+        assertTrue(rs.next());
+        Blob blob = rs.getBlob("blobVal");
+        Assert.assertArrayEquals(blob.getBytes(1, (int)blob.length()), new 
byte[] {1});
+
+        blob = rs.getBlob(23);
+        Assert.assertArrayEquals(blob.getBytes(1, (int)blob.length()), new 
byte[] {1});
+        assertFalse(rs.next());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testClob() throws Exception {
+        ResultSet rs = stmt.executeQuery(SQL);
+
+        assertTrue(rs.next());
+        Clob clob = rs.getClob("clobVal");
+        Assert.assertEquals("str", clob.getSubString(1, (int)clob.length()));
+
+        clob = rs.getClob(24);
+        Assert.assertEquals("str", clob.getSubString(1, (int)clob.length()));
+        assertFalse(rs.next());
+    }
+
     /**
      * @throws Exception If failed.
      */
@@ -990,6 +1027,14 @@ public class JdbcResultSetSelfTest extends 
GridCommonAbstractTest {
         @QuerySqlField(index = false)
         private byte[] arrVal;
 
+        /** */
+        @QuerySqlField(index = false)
+        private byte[] blobVal;
+
+        /** */
+        @QuerySqlField(index = false)
+        private String clobVal;
+
         /** */
         @QuerySqlField(index = false)
         private Date dateVal;
@@ -1056,6 +1101,8 @@ public class JdbcResultSetSelfTest extends 
GridCommonAbstractTest {
             if (timeVal != null ? !timeVal.equals(that.timeVal) : that.timeVal 
!= null) return false;
             if (tsVal != null ? !tsVal.equals(that.tsVal) : that.tsVal != 
null) return false;
             if (urlVal != null ? !urlVal.equals(that.urlVal) : that.urlVal != 
null) return false;
+            if (!Arrays.equals(blobVal, that.blobVal)) return false;
+            if (clobVal != null ? !clobVal.equals(that.clobVal) : that.clobVal 
!= null) return false;
 
             return true;
         }
@@ -1082,6 +1129,8 @@ public class JdbcResultSetSelfTest extends 
GridCommonAbstractTest {
             res = 31 * res + (f1 != null ? f1.hashCode() : 0);
             res = 31 * res + (f2 != null ? f2.hashCode() : 0);
             res = 31 * res + (f3 != null ? f3.hashCode() : 0);
+            res = 31 * res + (blobVal != null ? Arrays.hashCode(blobVal) : 0);
+            res = 31 * res + (clobVal != null ? clobVal.hashCode() : 0);
 
             return res;
         }
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index 450db60030d..f5b133e5fad 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -21,6 +21,7 @@ import java.security.Security;
 import org.apache.ignite.common.RunningQueryInfoCheckInitiatorTest;
 import org.apache.ignite.internal.jdbc2.JdbcBlobTest;
 import org.apache.ignite.internal.jdbc2.JdbcBulkLoadSelfTest;
+import org.apache.ignite.internal.jdbc2.JdbcClobTest;
 import org.apache.ignite.internal.jdbc2.JdbcConnectionReopenTest;
 import org.apache.ignite.internal.jdbc2.JdbcDistributedJoinsQueryTest;
 import org.apache.ignite.internal.jdbc2.JdbcSchemaCaseSelfTest;
@@ -125,6 +126,7 @@ import org.junit.runners.Suite;
     JdbcBulkLoadSelfTest.class,
     JdbcSchemaCaseSelfTest.class,
 
+    JdbcClobTest.class,
     JdbcBlobTest.class,
     org.apache.ignite.internal.jdbc2.JdbcStreamingSelfTest.class,
     JdbcThinStreamingNotOrderedSelfTest.class,
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractDmlStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractDmlStatementSelfTest.java
index 522fddd7aa1..a69b7a70d4a 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractDmlStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractDmlStatementSelfTest.java
@@ -18,9 +18,12 @@
 package org.apache.ignite.jdbc.thin;
 
 import java.io.Serializable;
+import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.Collections;
 import org.apache.ignite.cache.QueryEntity;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
@@ -30,6 +33,7 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.util.typedef.F;
 
+import static java.nio.charset.StandardCharsets.UTF_16;
 import static org.apache.ignite.cache.CacheMode.PARTITIONED;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
 
@@ -38,7 +42,7 @@ import static 
org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
  */
 public abstract class JdbcThinAbstractDmlStatementSelfTest extends 
JdbcThinAbstractSelfTest {
     /** SQL SELECT query for verification. */
-    static final String SQL_SELECT = "select _key, id, firstName, lastName, 
age from Person";
+    static final String SQL_SELECT = "select _key, id, firstName, lastName, 
age, data, text from Person";
 
     /** Connection. */
     protected Connection conn;
@@ -117,6 +121,8 @@ public abstract class JdbcThinAbstractDmlStatementSelfTest 
extends JdbcThinAbstr
         e.addQueryField("age", Integer.class.getName(), null);
         e.addQueryField("firstName", String.class.getName(), null);
         e.addQueryField("lastName", String.class.getName(), null);
+        e.addQueryField("data", byte[].class.getName(), null);
+        e.addQueryField("text", String.class.getName(), null);
 
         ccfg.setQueryEntities(Collections.singletonList(e));
 
@@ -158,6 +164,8 @@ public abstract class JdbcThinAbstractDmlStatementSelfTest 
extends JdbcThinAbstr
         e.addQueryField("age", Integer.class.getName(), null);
         e.addQueryField("firstName", String.class.getName(), null);
         e.addQueryField("lastName", String.class.getName(), null);
+        e.addQueryField("data", byte[].class.getName(), null);
+        e.addQueryField("text", String.class.getName(), null);
 
         cache.setQueryEntities(Collections.singletonList(e));
 
@@ -171,6 +179,49 @@ public abstract class JdbcThinAbstractDmlStatementSelfTest 
extends JdbcThinAbstr
         return nonBinCacheConfig();
     }
 
+    /**
+     * Helper to get test binary data as string UTF-16 encoding to be in sync 
with the RAWTOHEX function
+     * which uses UTF-16 for conversion strings to byte arrays.
+     * @param str String.
+     * @return Byte array with the UTF-16 encoding.
+     */
+    static byte[] getBytes(String str) {
+        return str.getBytes(UTF_16);
+    }
+
+    /**
+     * Helper to convert a binary data (which is a string UTF-16 encoding) 
back to string.
+     * @param arr Byte array with the UTF-16 encoding.
+     * @return String.
+     */
+    static String str(byte[] arr) {
+        return new String(arr, UTF_16);
+    }
+
+    /**
+     * @param blob Blob.
+     */
+    static byte[] getBytes(Blob blob) {
+        try {
+            return blob.getBytes(1, (int)blob.length());
+        }
+        catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * @param clob Clob.
+     */
+    static String str(Clob clob) {
+        try {
+            return clob.getSubString(1, (int)clob.length());
+        }
+        catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * Person.
      */
@@ -191,6 +242,14 @@ public abstract class JdbcThinAbstractDmlStatementSelfTest 
extends JdbcThinAbstr
         @QuerySqlField
         private final int age;
 
+        /** Binary data (BLOB). */
+        @QuerySqlField
+        private final byte[] data;
+
+        /** CLOB. */
+        @QuerySqlField
+        private final String text;
+
         /**
          * @param id ID.
          * @param firstName First name.
@@ -206,6 +265,8 @@ public abstract class JdbcThinAbstractDmlStatementSelfTest 
extends JdbcThinAbstr
             this.firstName = firstName;
             this.lastName = lastName;
             this.age = age;
+            this.data = getBytes(lastName);
+            this.text = firstName + " " + lastName;
         }
 
         /** {@inheritDoc} */
@@ -218,16 +279,23 @@ public abstract class 
JdbcThinAbstractDmlStatementSelfTest extends JdbcThinAbstr
             if (id != person.id) return false;
             if (age != person.age) return false;
             if (firstName != null ? !firstName.equals(person.firstName) : 
person.firstName != null) return false;
-            return lastName != null ? lastName.equals(person.lastName) : 
person.lastName == null;
+            if (lastName != null ? !lastName.equals(person.lastName) : 
person.lastName != null) return false;
+            if (data != null ? !Arrays.equals(data, person.data) : person.data 
!= null) return false;
+            if (text != null ? !text.equals(person.text) : person.text != 
null) return false;
 
+            return true;
         }
 
         /** {@inheritDoc} */
         @Override public int hashCode() {
             int result = id;
+
             result = 31 * result + (firstName != null ? firstName.hashCode() : 
0);
             result = 31 * result + (lastName != null ? lastName.hashCode() : 
0);
             result = 31 * result + age;
+            result = 31 * result + (data != null ? Arrays.hashCode(data) : 0);
+            result = 31 * result + (text != null ? text.hashCode() : 0);
+
             return result;
         }
     }
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractUpdateStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractUpdateStatementSelfTest.java
index f71d18a1fa1..76e42d016c9 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractUpdateStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractUpdateStatementSelfTest.java
@@ -24,10 +24,10 @@ import java.sql.Statement;
  */
 public abstract class JdbcThinAbstractUpdateStatementSelfTest extends 
JdbcThinAbstractDmlStatementSelfTest {
     /** SQL query to populate cache. */
-    private static final String ITEMS_SQL = "insert into Person(_key, id, 
firstName, lastName, age) values " +
-        "('p1', 1, 'John', 'White', 25), " +
-        "('p2', 2, 'Joe', 'Black', 35), " +
-        "('p3', 3, 'Mike', 'Green', 40)";
+    private static final String ITEMS_SQL = "insert into Person(_key, id, 
firstName, lastName, age, data, text) values " +
+        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White'), 'John White'), " +
+        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black'), 'Joe Black'), " +
+        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'), 'Mike Green')";
 
     /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
index 6f97da4c8c9..c8abd5ea63d 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
@@ -1742,16 +1742,8 @@ public class JdbcThinConnectionSelfTest extends 
JdbcThinAbstractSelfTest {
     @Test
     public void testCreateClob() throws Exception {
         try (Connection conn = 
DriverManager.getConnection(urlWithPartitionAwarenessProp)) {
-            // Unsupported
-            assertThrows(log,
-                new Callable<Object>() {
-                    @Override public Object call() throws Exception {
-                        return conn.createClob();
-                    }
-                },
-                SQLFeatureNotSupportedException.class,
-                "SQL-specific types are not supported"
-            );
+
+            assertNotNull(conn.createClob());
 
             conn.close();
 
@@ -1773,16 +1765,8 @@ public class JdbcThinConnectionSelfTest extends 
JdbcThinAbstractSelfTest {
     @Test
     public void testCreateBlob() throws Exception {
         try (Connection conn = 
DriverManager.getConnection(urlWithPartitionAwarenessProp)) {
-            // Unsupported
-            assertThrows(log,
-                new Callable<Object>() {
-                    @Override public Object call() throws Exception {
-                        return conn.createBlob();
-                    }
-                },
-                SQLFeatureNotSupportedException.class,
-                "SQL-specific types are not supported"
-            );
+
+            assertNotNull(conn.createBlob());
 
             conn.close();
 
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinInsertStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinInsertStatementSelfTest.java
index c9f46abc2b3..9a787a6269a 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinInsertStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinInsertStatementSelfTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.jdbc.thin;
 
+import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -35,23 +37,23 @@ import org.junit.Test;
  */
 public class JdbcThinInsertStatementSelfTest extends 
JdbcThinAbstractDmlStatementSelfTest {
     /** SQL query. */
-    private static final String SQL = "insert into Person(_key, id, firstName, 
lastName, age) values " +
-        "('p1', 1, 'John', 'White', 25), " +
-        "('p2', 2, 'Joe', 'Black', 35), " +
-        "('p3', 3, 'Mike', 'Green', 40)";
+    private static final String SQL = "insert into Person(_key, id, firstName, 
lastName, age, data, text) values " +
+        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White'), 'John White'), " +
+        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black'), 'Joe Black'), " +
+        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'), 'Mike Green')";
 
     /** SQL query. */
-    private static final String SQL_PREPARED = "insert into Person(_key, id, 
firstName, lastName, age) values " +
-        "(?, ?, ?, ?, ?), (?, ?, ?, ?, ?), (?, ?, ?, ?, ?)";
+    private static final String SQL_PREPARED = "insert into Person(_key, id, 
firstName, lastName, age, data, text) " +
+        "values (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, 
?, ?)";
 
     /** Test logger. */
     private static ListeningTestLogger srvLog;
 
     /** Arguments for prepared statement. */
     private final Object[][] args = new Object[][] {
-        {"p1", 1, "John", "White", 25},
-        {"p3", 3, "Mike", "Green", 40},
-        {"p2", 2, "Joe", "Black", 35}
+        {"p1", 1, "John", "White", 25, getBytes("White"), "John White"},
+        {"p3", 3, "Mike", "Green", 40, getBytes("Green"), "Mike Green"},
+        {"p2", 2, "Joe", "Black", 35, getBytes("Black"), "Joe Black"}
     };
 
     /** Statement. */
@@ -95,6 +97,14 @@ public class JdbcThinInsertStatementSelfTest extends 
JdbcThinAbstractDmlStatemen
             prepStmt.setString(paramCnt++, (String)arg[2]);
             prepStmt.setString(paramCnt++, (String)arg[3]);
             prepStmt.setInt(paramCnt++, (Integer)arg[4]);
+
+            Blob blob = conn.createBlob();
+            blob.setBytes(1, (byte[])arg[5]);
+            prepStmt.setBlob(paramCnt++, blob);
+
+            Clob clob = conn.createClob();
+            clob.setString(1, (String)arg[6]);
+            prepStmt.setClob(paramCnt++, clob);
         }
     }
 
@@ -116,6 +126,8 @@ public class JdbcThinInsertStatementSelfTest extends 
JdbcThinAbstractDmlStatemen
                         assertEquals("John", rs.getString("firstName"));
                         assertEquals("White", rs.getString("lastName"));
                         assertEquals(25, rs.getInt("age"));
+                        assertEquals("White", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("John White", str(rs.getClob("text")));
                         break;
 
                     case 2:
@@ -123,6 +135,8 @@ public class JdbcThinInsertStatementSelfTest extends 
JdbcThinAbstractDmlStatemen
                         assertEquals("Joe", rs.getString("firstName"));
                         assertEquals("Black", rs.getString("lastName"));
                         assertEquals(35, rs.getInt("age"));
+                        assertEquals("Black", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Joe Black", str(rs.getClob("text")));
                         break;
 
                     case 3:
@@ -130,13 +144,8 @@ public class JdbcThinInsertStatementSelfTest extends 
JdbcThinAbstractDmlStatemen
                         assertEquals("Mike", rs.getString("firstName"));
                         assertEquals("Green", rs.getString("lastName"));
                         assertEquals(40, rs.getInt("age"));
-                        break;
-
-                    case 4:
-                        assertEquals("p4", rs.getString("_key"));
-                        assertEquals("Leah", rs.getString("firstName"));
-                        assertEquals("Grey", rs.getString("lastName"));
-                        assertEquals(22, rs.getInt("age"));
+                        assertEquals("Green", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Mike Green", str(rs.getClob("text")));
                         break;
 
                     default:
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMergeStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMergeStatementSelfTest.java
index 065f81417b7..8bdf7c6163b 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMergeStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMergeStatementSelfTest.java
@@ -28,14 +28,14 @@ import org.junit.Test;
  */
 public class JdbcThinMergeStatementSelfTest extends 
JdbcThinAbstractDmlStatementSelfTest {
     /** SQL query. */
-    private static final String SQL = "merge into Person(_key, id, firstName, 
lastName, age) values " +
-        "('p1', 1, 'John', 'White', 25), " +
-        "('p2', 2, 'Joe', 'Black', 35), " +
-        "('p3', 3, 'Mike', 'Green', 40)";
+    private static final String SQL = "merge into Person(_key, id, firstName, 
lastName, age, data, text) values " +
+        "('p1', 1, 'John', 'White', 25, RAWTOHEX('White'), 'John White'), " +
+        "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black'), 'Joe Black'), " +
+        "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'), 'Mike Green')";
 
     /** SQL query. */
-    protected static final String SQL_PREPARED = "merge into Person(_key, id, 
firstName, lastName, age) values " +
-        "(?, ?, ?, ?, ?), (?, ?, ?, ?, ?)";
+    protected static final String SQL_PREPARED = "merge into Person(_key, id, 
firstName, lastName, age, data, text) values " +
+        "(?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?)";
 
     /** Statement. */
     protected Statement stmt;
@@ -74,6 +74,8 @@ public class JdbcThinMergeStatementSelfTest extends 
JdbcThinAbstractDmlStatement
                         assertEquals("John", rs.getString("firstName"));
                         assertEquals("White", rs.getString("lastName"));
                         assertEquals(25, rs.getInt("age"));
+                        assertEquals("White", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("John White", str(rs.getClob("text")));
                         break;
 
                     case 2:
@@ -81,6 +83,8 @@ public class JdbcThinMergeStatementSelfTest extends 
JdbcThinAbstractDmlStatement
                         assertEquals("Joe", rs.getString("firstName"));
                         assertEquals("Black", rs.getString("lastName"));
                         assertEquals(35, rs.getInt("age"));
+                        assertEquals("Black", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Joe Black", str(rs.getClob("text")));
                         break;
 
                     case 3:
@@ -88,13 +92,8 @@ public class JdbcThinMergeStatementSelfTest extends 
JdbcThinAbstractDmlStatement
                         assertEquals("Mike", rs.getString("firstName"));
                         assertEquals("Green", rs.getString("lastName"));
                         assertEquals(40, rs.getInt("age"));
-                        break;
-
-                    case 4:
-                        assertEquals("p4", rs.getString("_key"));
-                        assertEquals("Leah", rs.getString("firstName"));
-                        assertEquals("Grey", rs.getString("lastName"));
-                        assertEquals(22, rs.getInt("age"));
+                        assertEquals("Green", 
str(getBytes(rs.getBlob("data"))));
+                        assertEquals("Mike Green", str(rs.getClob("text")));
                         break;
 
                     default:
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinPreparedStatementSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinPreparedStatementSelfTest.java
index 2becc186082..7982980491d 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinPreparedStatementSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinPreparedStatementSelfTest.java
@@ -52,7 +52,9 @@ import org.junit.Test;
 
 import static java.sql.Types.BIGINT;
 import static java.sql.Types.BINARY;
+import static java.sql.Types.BLOB;
 import static java.sql.Types.BOOLEAN;
+import static java.sql.Types.CLOB;
 import static java.sql.Types.DATE;
 import static java.sql.Types.DOUBLE;
 import static java.sql.Types.FLOAT;
@@ -79,7 +81,7 @@ public class JdbcThinPreparedStatementSelfTest extends 
JdbcThinAbstractSelfTest
     /** SQL query. */
     private static final String SQL_PART =
         "select id, boolVal, byteVal, shortVal, intVal, longVal, floatVal, " +
-            "doubleVal, bigVal, strVal, arrVal, dateVal, timeVal, tsVal, 
objVal " +
+            "doubleVal, bigVal, strVal, arrVal, dateVal, timeVal, tsVal, 
objVal, blobVal, clobVal " +
             "from TestObject ";
 
     /** Connection. */
@@ -128,6 +130,8 @@ public class JdbcThinPreparedStatementSelfTest extends 
JdbcThinAbstractSelfTest
         o.bigVal = new BigDecimal(1);
         o.strVal = "str";
         o.arrVal = new byte[] {1};
+        o.blobVal = new byte[] {1};
+        o.clobVal = "large str";
         o.dateVal = new Date(1);
         o.timeVal = new Time(1);
         o.tsVal = new Timestamp(1);
@@ -854,6 +858,62 @@ public class JdbcThinPreparedStatementSelfTest extends 
JdbcThinAbstractSelfTest
         assert cnt == 1;
     }
 
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testBlob() throws Exception {
+        stmt = conn.prepareStatement(SQL_PART + " where blobVal is not 
distinct from ?");
+
+        Blob blob = conn.createBlob();
+
+        blob.setBytes(1, new byte[] {1});
+
+        stmt.setBlob(1, blob);
+
+        ResultSet rs = stmt.executeQuery();
+
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt("id"));
+        assertFalse(rs.next());
+
+        stmt.setNull(1, BLOB);
+
+        rs = stmt.executeQuery();
+
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt("id"));
+        assertFalse(rs.next());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testClob() throws Exception {
+        stmt = conn.prepareStatement(SQL_PART + " where clobVal is not 
distinct from ?");
+
+        Clob clob = conn.createClob();
+
+        clob.setString(1, "large str");
+
+        stmt.setClob(1, clob);
+
+        ResultSet rs = stmt.executeQuery();
+
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt("id"));
+        assertFalse(rs.next());
+
+        stmt.setNull(1, CLOB);
+
+        rs = stmt.executeQuery();
+
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt("id"));
+        assertFalse(rs.next());
+    }
+
     /**
      * @throws Exception If failed.
      */
@@ -1047,12 +1107,6 @@ public class JdbcThinPreparedStatementSelfTest extends 
JdbcThinAbstractSelfTest
             }
         });
 
-        checkNotSupported(new RunnableX() {
-            @Override public void runx() throws Exception {
-                stmt.setBlob(1, (Blob)null);
-            }
-        });
-
         checkNotSupported(new RunnableX() {
             @Override public void runx() throws Exception {
                 stmt.setBlob(1, (InputStream)null);
@@ -1083,12 +1137,6 @@ public class JdbcThinPreparedStatementSelfTest extends 
JdbcThinAbstractSelfTest
             }
         });
 
-        checkNotSupported(new RunnableX() {
-            @Override public void runx() throws Exception {
-                stmt.setClob(1, (Clob)null);
-            }
-        });
-
         checkNotSupported(new RunnableX() {
             @Override public void runx() throws Exception {
                 stmt.setClob(1, (Reader)null);
@@ -1204,6 +1252,14 @@ public class JdbcThinPreparedStatementSelfTest extends 
JdbcThinAbstractSelfTest
         @QuerySqlField
         private byte[] arrVal;
 
+        /** */
+        @QuerySqlField
+        private byte[] blobVal;
+
+        /** */
+        @QuerySqlField
+        private String clobVal;
+
         /** */
         @QuerySqlField
         private Date dateVal;
diff --git 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java
 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java
index 50f1c1079d1..60210c335db 100644
--- 
a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java
+++ 
b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java
@@ -62,7 +62,7 @@ public class JdbcThinResultSetSelfTest extends 
JdbcThinAbstractSelfTest {
     /** SQL query. */
     private static final String SQL =
         "select id, boolVal, byteVal, shortVal, intVal, longVal, floatVal, " +
-            "doubleVal, bigVal, strVal, arrVal, dateVal, timeVal, tsVal, 
objVal " +
+            "doubleVal, bigVal, strVal, arrVal, dateVal, timeVal, tsVal, 
objVal, blobVal, clobVal " +
             "from TestObject where id = 1";
 
     /** Statement. */
@@ -144,6 +144,8 @@ public class JdbcThinResultSetSelfTest extends 
JdbcThinAbstractSelfTest {
         o.bigVal = new BigDecimal(1);
         o.strVal = "1";
         o.arrVal = new byte[] {1};
+        o.blobVal = new byte[] {1};
+        o.clobVal = "str";
         o.dateVal = new Date(1, 1, 1);
         o.timeVal = new Time(1, 1, 1);
         o.tsVal = new Timestamp(1);
@@ -599,6 +601,42 @@ public class JdbcThinResultSetSelfTest extends 
JdbcThinAbstractSelfTest {
         assert cnt == 1;
     }
 
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testBlob() throws Exception {
+        ResultSet rs = stmt.executeQuery(SQL);
+
+        assertTrue(rs.next());
+
+        Blob blob = rs.getBlob("blobVal");
+        Assert.assertArrayEquals(blob.getBytes(1, (int)blob.length()), new 
byte[] {1});
+
+        blob = rs.getBlob(16);
+        Assert.assertArrayEquals(blob.getBytes(1, (int)blob.length()), new 
byte[] {1});
+
+        assertFalse(rs.next());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testClob() throws Exception {
+        ResultSet rs = stmt.executeQuery(SQL);
+
+        assertTrue(rs.next());
+
+        Clob clob = rs.getClob("clobVal");
+        Assert.assertEquals("str", clob.getSubString(1, (int)clob.length()));
+
+        clob = rs.getClob(17);
+        Assert.assertEquals("str", clob.getSubString(1, (int)clob.length()));
+
+        assertFalse(rs.next());
+    }
+
     /**
      * @throws Exception If failed.
      */
@@ -828,30 +866,6 @@ public class JdbcThinResultSetSelfTest extends 
JdbcThinAbstractSelfTest {
             }
         });
 
-        checkNotSupported(new RunnableX() {
-            @Override public void runx() throws Exception {
-                rs.getBlob(1);
-            }
-        });
-
-        checkNotSupported(new RunnableX() {
-            @Override public void runx() throws Exception {
-                rs.getBlob("id");
-            }
-        });
-
-        checkNotSupported(new RunnableX() {
-            @Override public void runx() throws Exception {
-                rs.getClob(1);
-            }
-        });
-
-        checkNotSupported(new RunnableX() {
-            @Override public void runx() throws Exception {
-                rs.getClob("id");
-            }
-        });
-
         checkNotSupported(new RunnableX() {
             @Override public void runx() throws Exception {
                 rs.getCharacterStream(1);
@@ -1740,6 +1754,14 @@ public class JdbcThinResultSetSelfTest extends 
JdbcThinAbstractSelfTest {
         @QuerySqlField
         private byte[] arrVal;
 
+        /** */
+        @QuerySqlField
+        private byte[] blobVal;
+
+        /** */
+        @QuerySqlField
+        private String clobVal;
+
         /** */
         @QuerySqlField
         private Date dateVal;
@@ -1806,6 +1828,8 @@ public class JdbcThinResultSetSelfTest extends 
JdbcThinAbstractSelfTest {
             if (timeVal != null ? !timeVal.equals(that.timeVal) : that.timeVal 
!= null) return false;
             if (tsVal != null ? !tsVal.equals(that.tsVal) : that.tsVal != 
null) return false;
             if (urlVal != null ? !urlVal.equals(that.urlVal) : that.urlVal != 
null) return false;
+            if (!Arrays.equals(blobVal, that.blobVal)) return false;
+            if (clobVal != null ? !clobVal.equals(that.clobVal) : that.clobVal 
!= null) return false;
 
             return true;
         }
@@ -1832,6 +1856,8 @@ public class JdbcThinResultSetSelfTest extends 
JdbcThinAbstractSelfTest {
             res = 31 * res + (objVal != null ? objVal.hashCode() : 0);
             res = 31 * res + (f2 != null ? f2.hashCode() : 0);
             res = 31 * res + (f3 != null ? f3.hashCode() : 0);
+            res = 31 * res + (blobVal != null ? Arrays.hashCode(blobVal) : 0);
+            res = 31 * res + (clobVal != null ? clobVal.hashCode() : 0);
 
             return res;
         }
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
index 6a4668a0001..c5a905ac064 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
@@ -82,6 +82,8 @@ import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.binary.BinaryMetadata;
 import org.apache.ignite.internal.binary.BinaryMetadataHandler;
 import org.apache.ignite.internal.binary.BinaryTypeImpl;
+import org.apache.ignite.internal.jdbc2.JdbcBlob;
+import org.apache.ignite.internal.jdbc2.JdbcClob;
 import org.apache.ignite.internal.jdbc2.JdbcUtils;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheUtils;
@@ -754,14 +756,14 @@ public class JdbcThinConnection implements Connection {
     @Override public Clob createClob() throws SQLException {
         ensureNotClosed();
 
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcClob("");
     }
 
     /** {@inheritDoc} */
     @Override public Blob createBlob() throws SQLException {
         ensureNotClosed();
 
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcBlob(new byte[0]);
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java
index 6c795ddc4bb..d64aaab423e 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java
@@ -305,16 +305,12 @@ public class JdbcThinPreparedStatement extends 
JdbcThinStatement implements Prep
 
     /** {@inheritDoc} */
     @Override public void setBlob(int paramIdx, Blob x) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        setBytes(paramIdx, x.getBytes(1, (int)x.length()));
     }
 
     /** {@inheritDoc} */
     @Override public void setClob(int paramIdx, Clob x) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        setString(paramIdx, x.getSubString(1, (int)x.length()));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java
index b75734992e4..1ba7affc761 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java
@@ -46,6 +46,8 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import org.apache.ignite.internal.jdbc2.JdbcBlob;
+import org.apache.ignite.internal.jdbc2.JdbcClob;
 import org.apache.ignite.internal.processors.odbc.SqlStateCode;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcColumnMeta;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryCloseRequest;
@@ -1296,16 +1298,12 @@ public class JdbcThinResultSet implements ResultSet {
 
     /** {@inheritDoc} */
     @Override public Blob getBlob(int colIdx) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcBlob(getBytes(colIdx));
     }
 
     /** {@inheritDoc} */
     @Override public Clob getClob(int colIdx) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcClob(getString(colIdx));
     }
 
     /** {@inheritDoc} */
@@ -1329,16 +1327,12 @@ public class JdbcThinResultSet implements ResultSet {
 
     /** {@inheritDoc} */
     @Override public Blob getBlob(String colLb) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcBlob(getBytes(colLb));
     }
 
     /** {@inheritDoc} */
     @Override public Clob getClob(String colLb) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcClob(getString(colLb));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBlob.java 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBlob.java
index 17e9b13d00c..ef2b6b5bd3a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBlob.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBlob.java
@@ -54,7 +54,7 @@ public class JdbcBlob implements Blob {
     @Override public byte[] getBytes(long pos, int len) throws SQLException {
         ensureNotClosed();
 
-        if (pos < 1 || arr.length - pos < 0 || len < 0)
+        if (pos < 1 || (arr.length - pos < 0 && arr.length > 0) || len < 0)
             throw new SQLException("Invalid argument. Position can't be less 
than 1 or " +
                 "greater than size of underlying byte array. Requested length 
also can't be negative " + "" +
                 "[pos=" + pos + ", len=" + len + ']');
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcClob.java 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcClob.java
new file mode 100644
index 00000000000..ecef1abd6ab
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcClob.java
@@ -0,0 +1,326 @@
+/*
+ * 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.internal.jdbc2;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.sql.Clob;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * CLOB implementation for Ignite JDBC driver.
+ */
+public class JdbcClob implements Clob {
+    /** CLOB's character sequence. */
+    private String chars;
+
+    /**
+     * @param chars CLOB's character sequence.
+     */
+    public JdbcClob(String chars) {
+        this.chars = chars;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long length() throws SQLException {
+        ensureNotClosed();
+
+        return chars.length();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getSubString(long pos, int len) throws 
SQLException {
+        ensureNotClosed();
+
+        long zeroBasedPos = pos - 1;
+
+        if (zeroBasedPos < 0 || len < 0 || zeroBasedPos + len > chars.length())
+            throw new SQLException("Invalid argument. Position should be 
greater than 0. Length should not be " +
+                "negative. Position + length should be less than CLOB size 
[pos=" + pos + ", length=" + len + ']');
+
+        return getSubStringInternal((int)zeroBasedPos, len);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Reader getCharacterStream() throws SQLException {
+        ensureNotClosed();
+
+        return new StringReader(chars);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Reader getCharacterStream(long pos, long len) throws 
SQLException {
+        return new StringReader(getSubString(pos, (int)len));
+    }
+
+    /** {@inheritDoc} */
+    @Override public InputStream getAsciiStream() throws SQLException {
+        ensureNotClosed();
+
+        // Encode to UTF-8 since Ignite internally stores strings in UTF-8 by 
default.
+        return new Utf8EncodedStringInputStream(chars);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long position(String searchStr, long start) throws 
SQLException {
+        ensureNotClosed();
+
+        if (start < 1)
+            throw new SQLException("Invalid argument. Start position should be 
greater than zero [start=" +
+                start + ']');
+
+        long zeroBasedIdx = positionInternal(searchStr, start - 1);
+
+        return zeroBasedIdx == -1 ? -1 : zeroBasedIdx + 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long position(Clob searchStr, long start) throws 
SQLException {
+        return position(searchStr.getSubString(1, (int)searchStr.length()), 
start);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int setString(long pos, String str) throws SQLException {
+        ensureNotClosed();
+
+        long zeroBasedPos = pos - 1;
+
+        if (zeroBasedPos < 0 || str == null || zeroBasedPos > chars.length())
+            throw new SQLException("Invalid argument. Position should be 
greater than zero. " +
+                "Position should not exceed CLOB length+1. Source string 
should not be null " +
+                "[pos=" + pos + ", str=" + str + ']');
+
+        return setStringInternal((int)zeroBasedPos, str);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int setString(long pos, String str, int off, int len) 
throws SQLException {
+        ensureNotClosed();
+
+        long zeroBasedPos = pos - 1;
+
+        if (zeroBasedPos < 0 || str == null || zeroBasedPos > chars.length() 
|| off < 0 || len < 0 || off + len > str.length())
+            throw new SQLException("Invalid argument. Position should be 
greater than zero. " +
+                "Position should not exceed CLOB length+1. Source string 
should not be null.  " +
+                "Offset and length shouldn't be negative. Offset + length 
should not exceed source string length " +
+                "[pos=" + pos + ", str=" + str + ", offset=" + off + ", len=" 
+ len + ']');
+
+        return setStringInternal((int)zeroBasedPos, str, off, len);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void truncate(long len) throws SQLException {
+        ensureNotClosed();
+
+        if (len < 0 || len > chars.length())
+            throw new SQLException("Invalid argument. Truncation length should 
not be negative. Truncation length " +
+                "should not exceed data length [len=" + len + ']');
+
+        chars = chars.substring(0, (int)len);
+    }
+
+    /** {@inheritDoc} */
+    @Override public OutputStream setAsciiStream(long pos) throws SQLException 
{
+        throw new SQLFeatureNotSupportedException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Writer setCharacterStream(long pos) throws SQLException {
+        throw new SQLFeatureNotSupportedException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void free() throws SQLException {
+        chars = null;
+    }
+
+    /**
+     * Ensures CLOB hasn't been closed.
+     */
+    private void ensureNotClosed() throws SQLException {
+        if (chars == null)
+            throw new SQLException("Clob instance can't be used after free() 
has been called.");
+    }
+
+    /**
+     * Internal getSubString implementation with zero-based position parameter.
+     */
+    private String getSubStringInternal(int zeroBasedPos, int len) {
+        return chars.substring(zeroBasedPos, zeroBasedPos + len);
+    }
+
+    /**
+     * Internal position implementation with zero-based start parameter.
+     */
+    private long positionInternal(String searchStr, long zeroBasedStart) {
+        return chars.indexOf(searchStr, (int)zeroBasedStart);
+    }
+
+    /**
+     * Internal setString implementation with zero-based position parameter.
+     */
+    private int setStringInternal(int zeroBasedPos, String str) {
+        StringBuilder strBuilder = new StringBuilder(chars);
+
+        // Ensure string buffer capacity
+        if (zeroBasedPos + str.length() > chars.length())
+            strBuilder.setLength(zeroBasedPos + str.length());
+
+        strBuilder.replace(zeroBasedPos, zeroBasedPos + str.length(), str);
+
+        chars = strBuilder.toString();
+
+        return str.length();
+    }
+
+    /**
+     * Internal setString implementation with zero-based position parameter.
+     */
+    private int setStringInternal(int zeroBasedPos, String str, int off, int 
len) {
+        StringBuilder strBuilder = new StringBuilder(chars);
+
+        // Ensure string buffer capacity
+        if (zeroBasedPos + str.length() > chars.length())
+            strBuilder.setLength(zeroBasedPos + str.length());
+
+        String replaceStr = str.substring(off, off + len);
+        strBuilder.replace(zeroBasedPos, zeroBasedPos + replaceStr.length(), 
replaceStr);
+
+        chars = strBuilder.toString();
+
+        return replaceStr.length();
+    }
+
+    /**
+     * Input stream which encodes the given string to UTF-8.
+     * To save memory for large strings it does it by chunks.
+     */
+    private static class Utf8EncodedStringInputStream extends InputStream {
+        /** String to encode. */
+        private final String chars;
+
+        /** String length. */
+        private final int length;
+
+        /** Start index of the next chunk (substring) to be encoded. */
+        private int charsPos;
+
+        /** Default chunk size. */
+        private static final int DEFAULT_CHUNK_SIZE = 8192;
+
+        /** Buffer containing the current chunk encoding. */
+        private byte[] buf;
+
+        /** Current position in the buffer - index of the next byte to be read 
from the input stream. */
+        private int bufPos;
+
+        /**
+         * @param chars String to be encoded.
+         */
+        Utf8EncodedStringInputStream(String chars) {
+            this.chars = chars;
+
+            length = chars.length();
+            charsPos = 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override public synchronized int read() {
+            if (buf == null || buf.length == 0 || bufPos >= buf.length) {
+                if (charsPos >= length)
+                    return -1;
+
+                bufPos = 0;
+
+                encodeNextChunk();
+            }
+
+            return buf[bufPos++] & 0xFF;
+        }
+
+        /** {@inheritDoc} */
+        @Override public synchronized int read(byte[] b, int off, int len) {
+            if (b == null)
+                throw new NullPointerException();
+
+            if (off < 0 || len < 0 || len > b.length - off)
+                throw new IndexOutOfBoundsException(String.format("Range [%s, 
%<s + %s) out of bounds for length %s", off, len, b.length));
+
+            if (len == 0)
+                return 0;
+
+            int i = 0;
+
+            while (i < len) {
+                if (buf == null || buf.length == 0 || bufPos >= buf.length) {
+                    if (charsPos >= length)
+                        return i > 0 ? i : -1;
+
+                    bufPos = 0;
+
+                    encodeNextChunk();
+                }
+
+                int encodedChunkSize = Math.min(len - i, buf.length - bufPos);
+
+                U.arrayCopy(buf, bufPos, b, off + i, encodedChunkSize);
+
+                bufPos += encodedChunkSize;
+                i += encodedChunkSize;
+            }
+
+            return i;
+        }
+
+        /**
+         * Encodes the next chunk of the string.
+         * <p>
+         * Makes sure that chunk doesn't contain the malformed surrogate 
element at the end
+         * (high surrogate that is not followed by a low surrogate).
+         */
+        private void encodeNextChunk() {
+            int remainingSize = chars.length() - charsPos;
+
+            assert remainingSize > 0;
+
+            int chunkSize;
+
+            if (remainingSize <= DEFAULT_CHUNK_SIZE) {
+                chunkSize = remainingSize;
+            }
+            else if (Character.isHighSurrogate(chars.charAt(charsPos + 
DEFAULT_CHUNK_SIZE - 1))) {
+                chunkSize = DEFAULT_CHUNK_SIZE + 1;
+            }
+            else {
+                chunkSize = DEFAULT_CHUNK_SIZE;
+            }
+
+            String subs = chars.substring(charsPos, charsPos + chunkSize);
+            buf = subs.getBytes(UTF_8);
+
+            charsPos += chunkSize;
+        }
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
index e785e4164d7..31ccf655351 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
@@ -702,7 +702,7 @@ public class JdbcConnection implements Connection {
     @Override public Clob createClob() throws SQLException {
         ensureNotClosed();
 
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcClob("");
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java
index 858a1b63f8b..dbb0c031344 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java
@@ -267,9 +267,7 @@ public class JdbcPreparedStatement extends JdbcStatement 
implements PreparedStat
 
     /** {@inheritDoc} */
     @Override public void setClob(int paramIdx, Clob x) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        setString(paramIdx, x.getSubString(1, (int)x.length()));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
index eab488c31c3..53746e50c33 100755
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java
@@ -1299,9 +1299,7 @@ public class JdbcResultSet implements ResultSet {
 
     /** {@inheritDoc} */
     @Override public Clob getClob(int colIdx) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcClob(getString(colIdx));
     }
 
     /** {@inheritDoc} */
@@ -1330,9 +1328,7 @@ public class JdbcResultSet implements ResultSet {
 
     /** {@inheritDoc} */
     @Override public Clob getClob(String colLb) throws SQLException {
-        ensureNotClosed();
-
-        throw new SQLFeatureNotSupportedException("SQL-specific types are not 
supported.");
+        return new JdbcClob(getString(colLb));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 8e91c71a711..65600be66a7 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -122,6 +122,7 @@ import 
org.apache.ignite.internal.util.lang.IgniteSingletonIterator;
 import org.apache.ignite.internal.util.lang.IgniteThrowableSupplier;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiClosure;
 import org.apache.ignite.lang.IgniteBiTuple;
@@ -1090,14 +1091,14 @@ public class IgniteH2Indexing implements 
GridQueryIndexing {
             if (roEx != null) {
                 throw new IgniteSQLException(
                     "Failed to execute DML statement. Cluster in read-only 
mode [stmt=" + qryDesc.sql() +
-                    ", params=" + Arrays.deepToString(qryParams.arguments()) + 
"]",
+                    ", params=" + S.toString(QueryParameters.class, qryParams) 
+ "]",
                     IgniteQueryErrorCode.CLUSTER_READ_ONLY_MODE_ENABLED,
                     e
                 );
             }
 
             throw new IgniteSQLException("Failed to execute DML statement 
[stmt=" + qryDesc.sql() +
-                ", params=" + Arrays.deepToString(qryParams.arguments()) + 
"]", e);
+                    ", params=" + S.toString(QueryParameters.class, qryParams) 
+ "]", e);
         }
         finally {
             runningQueryManager().unregister(qryId, failReason);
diff --git 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParameters.java
 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParameters.java
index e59e512826e..69d46618de1 100644
--- 
a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParameters.java
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParameters.java
@@ -20,16 +20,21 @@ package org.apache.ignite.internal.processors.query.h2;
 
 import java.util.List;
 import org.apache.ignite.internal.processors.query.NestedTxMode;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
 
 /**
  * Query parameters which vary between requests having the same execution 
plan. Essentially, these are the arguments
  * of original {@link org.apache.ignite.cache.query.SqlFieldsQuery} which are 
not part of {@link QueryDescriptor}.
  */
+@GridToStringInclude(sensitive = true)
 public class QueryParameters {
     /** Arguments. */
+    @GridToStringInclude(sensitive = true)
     private final Object[] args;
 
     /** Partitions. */
+    @GridToStringInclude(sensitive = true)
     private final int[] parts;
 
     /** Timeout. */
@@ -51,6 +56,7 @@ public class QueryParameters {
     private final boolean autoCommit;
 
     /** Batched arguments. */
+    @GridToStringInclude(sensitive = true)
     private final List<Object[]> batchedArgs;
 
     /**
@@ -194,4 +200,9 @@ public class QueryParameters {
             this.updateBatchSize
         );
     }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(QueryParameters.class, this);
+    }
 }


Reply via email to