ignite-5203 Simple BLOB support added
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/64ab5cdf Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/64ab5cdf Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/64ab5cdf Branch: refs/heads/ignite-5075-pds Commit: 64ab5cdfe9297ed819d269166a3d1a3b489d03ec Parents: 618a448 Author: agura <ag...@apache.org> Authored: Thu May 18 19:40:09 2017 +0300 Committer: agura <ag...@apache.org> Committed: Tue May 30 00:33:46 2017 +0300 ---------------------------------------------------------------------- .../jdbc2/JdbcAbstractDmlStatementSelfTest.java | 50 +- .../JdbcAbstractUpdateStatementSelfTest.java | 11 +- .../ignite/internal/jdbc2/JdbcBlobTest.java | 485 +++++++++++++++++++ .../jdbc2/JdbcInsertStatementSelfTest.java | 16 +- .../jdbc2/JdbcMergeStatementSelfTest.java | 16 +- .../jdbc2/JdbcPreparedStatementSelfTest.java | 47 ++ .../jdbc/suite/IgniteJdbcDriverTestSuite.java | 7 +- .../apache/ignite/internal/jdbc2/JdbcBlob.java | 191 ++++++++ .../ignite/internal/jdbc2/JdbcConnection.java | 2 +- .../internal/jdbc2/JdbcPreparedStatement.java | 4 +- .../ignite/internal/jdbc2/JdbcResultSet.java | 8 +- 11 files changed, 808 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractDmlStatementSelfTest.java ---------------------------------------------------------------------- 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 6ef86d3..81c913d 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 @@ -18,8 +18,11 @@ package org.apache.ignite.internal.jdbc2; import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.sql.Blob; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.SQLException; import java.util.Collections; import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.query.annotations.QuerySqlField; @@ -35,6 +38,9 @@ import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; * Statement test. */ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstractTest { + /** UTF 16 character set name. */ + private static final String UTF_16 = "UTF-16"; // RAWTOHEX function use UTF-16 for conversion strings to byte arrays. + /** JDBC URL. */ private static final String BASE_URL = CFG_URL_PREFIX + "cache=" + DEFAULT_CACHE_NAME + "@modules/clients/src/test/config/jdbc-config.xml"; @@ -42,7 +48,7 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac static final String BASE_URL_BIN = 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 from Person"; + static final String SQL_SELECT = "select _key, id, firstName, lastName, age, data from Person"; /** Alias for _key */ private static final String KEY_ALIAS = "key"; @@ -107,6 +113,7 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac 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); cache.setQueryEntities(Collections.singletonList(e)); @@ -136,6 +143,42 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac } /** + * @param str String. + */ + static byte[] getBytes(String str) { + try { + return str.getBytes(UTF_16); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * @param blob Blob. + */ + static byte[] getBytes(Blob blob) { + try { + return blob.getBytes(1, (int)blob.length()); + } + catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * @param arr Array. + */ + static String str(byte[] arr) { + try { + return new String(arr, UTF_16); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** * Person. */ @SuppressWarnings("UnusedDeclaration") @@ -156,6 +199,10 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac @QuerySqlField private final int age; + /** Binary data. */ + @QuerySqlField + private final byte[] data; + /** * @param id ID. * @param firstName First name. @@ -171,6 +218,7 @@ public abstract class JdbcAbstractDmlStatementSelfTest extends GridCommonAbstrac this.firstName = firstName; this.lastName = lastName; this.age = age; + this.data = getBytes(lastName); } /** {@inheritDoc} */ http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcAbstractUpdateStatementSelfTest.java ---------------------------------------------------------------------- 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 a20b815..ace1be6 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 @@ -19,12 +19,15 @@ package org.apache.ignite.internal.jdbc2; 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) 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) values " + + "('p1', 1, 'John', 'White', 25, RAWTOHEX('White')), " + + "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black')), " + + "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('Green'))"; /** {@inheritDoc} */ @Override protected void beforeTest() throws Exception { http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBlobTest.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..9e0e0d2 --- /dev/null +++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcBlobTest.java @@ -0,0 +1,485 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.Arrays; +import junit.framework.TestCase; + +/** + * + */ +public class JdbcBlobTest extends TestCase { + /** + * @throws Exception If failed. + */ + public void testLength() throws Exception { + JdbcBlob blob = new JdbcBlob(new byte[16]); + + assertEquals(16, (int)blob.length()); + + blob.free(); + + try { + blob.length(); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testGetBytes() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + JdbcBlob blob = new JdbcBlob(arr); + + try { + blob.getBytes(0, 16); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + try { + blob.getBytes(17, 16); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + try { + blob.getBytes(1, -1); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + byte[] res = blob.getBytes(1, 0); + assertEquals(0, res.length); + + assertTrue(Arrays.equals(arr, blob.getBytes(1, 16))); + + res = blob.getBytes(1, 20); + assertEquals(16, res.length); + assertTrue(Arrays.equals(arr, res)); + + res = blob.getBytes(1, 10); + assertEquals(10, res.length); + assertEquals(0, res[0]); + assertEquals(9, res[9]); + + res = blob.getBytes(7, 10); + assertEquals(10, res.length); + assertEquals(6, res[0]); + assertEquals(15, res[9]); + + res = blob.getBytes(7, 20); + assertEquals(10, res.length); + assertEquals(6, res[0]); + assertEquals(15, res[9]); + + res = blob.getBytes(1, 0); + assertEquals(0, res.length); + + blob.free(); + + try { + blob.getBytes(1, 16); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testGetBinaryStream() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + JdbcBlob blob = new JdbcBlob(arr); + + InputStream is = blob.getBinaryStream(); + + byte[] res = readBytes(is); + + assertTrue(Arrays.equals(arr, res)); + + blob.free(); + + try { + blob.getBinaryStream(); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testGetBinaryStreamWithParams() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + JdbcBlob blob = new JdbcBlob(arr); + + try { + blob.getBinaryStream(0, arr.length); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + try { + blob.getBinaryStream(1, 0); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + try { + blob.getBinaryStream(17, arr.length); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + try { + blob.getBinaryStream(1, arr.length + 1); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + InputStream is = blob.getBinaryStream(1, arr.length); + byte[] res = readBytes(is); + assertTrue(Arrays.equals(arr, res)); + + is = blob.getBinaryStream(1, 10); + res = readBytes(is); + assertEquals(10, res.length); + assertEquals(0, res[0]); + assertEquals(9, res[9]); + + is = blob.getBinaryStream(6, 10); + res = readBytes(is); + assertEquals(10, res.length); + assertEquals(5, res[0]); + assertEquals(14, res[9]); + + blob.free(); + + try { + blob.getBinaryStream(1, arr.length); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testPositionBytePattern() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + JdbcBlob blob = new JdbcBlob(arr); + + assertEquals(-1, blob.position(new byte[] {1, 2, 3}, 0)); + assertEquals(-1, blob.position(new byte[] {1, 2, 3}, arr.length + 1)); + assertEquals(-1, blob.position(new byte[0], 1)); + assertEquals(-1, blob.position(new byte[17], 1)); + assertEquals(-1, blob.position(new byte[] {3, 2, 1}, 1)); + assertEquals(1, blob.position(new byte[] {0, 1, 2}, 1)); + assertEquals(2, blob.position(new byte[] {1, 2, 3}, 1)); + assertEquals(2, blob.position(new byte[] {1, 2, 3}, 2)); + assertEquals(-1, blob.position(new byte[] {1, 2, 3}, 3)); + assertEquals(14, blob.position(new byte[] {13, 14, 15}, 3)); + assertEquals(-1, blob.position(new byte[] {0, 1, 3}, 1)); + assertEquals(-1, blob.position(new byte[] {0, 2, 3}, 1)); + assertEquals(-1, blob.position(new byte[] {1, 2, 4}, 1)); + + blob.free(); + + try { + blob.position(new byte[] {0, 1, 2}, 1); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testPositionBlobPattern() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + JdbcBlob blob = new JdbcBlob(arr); + + assertEquals(-1, blob.position(new JdbcBlob(new byte[] {1, 2, 3}), 0)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[] {1, 2, 3}), arr.length + 1)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[0]), 1)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[17]), 1)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[] {3, 2, 1}), 1)); + assertEquals(1, blob.position(new JdbcBlob(new byte[] {0, 1, 2}), 1)); + assertEquals(2, blob.position(new JdbcBlob(new byte[] {1, 2, 3}), 1)); + assertEquals(2, blob.position(new JdbcBlob(new byte[] {1, 2, 3}), 2)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[] {1, 2, 3}), 3)); + assertEquals(14, blob.position(new JdbcBlob(new byte[] {13, 14, 15}), 3)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[] {0, 1, 3}), 1)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[] {0, 2, 3}), 1)); + assertEquals(-1, blob.position(new JdbcBlob(new byte[] {1, 2, 4}), 1)); + + blob.free(); + + try { + blob.position(new JdbcBlob(new byte[] {0, 1, 2}), 1); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testSetBytes() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; + + JdbcBlob blob = new JdbcBlob(arr); + + try { + blob.setBytes(0, new byte[4]); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + try { + blob.setBytes(17, new byte[4]); + + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + // No-op. + } + + assertEquals(4, blob.setBytes(1, new byte[] {3, 2, 1, 0})); + assertTrue(Arrays.equals(new byte[] {3, 2, 1, 0, 4, 5, 6, 7}, blob.getBytes(1, arr.length))); + + assertEquals(4, blob.setBytes(5, new byte[] {7, 6, 5, 4})); + assertTrue(Arrays.equals(new byte[] {3, 2, 1, 0, 7, 6, 5, 4}, blob.getBytes(1, arr.length))); + + assertEquals(4, blob.setBytes(7, new byte[] {8, 9, 10, 11})); + assertTrue(Arrays.equals(new byte[] {3, 2, 1, 0, 7, 6, 8, 9, 10, 11}, blob.getBytes(1, (int)blob.length()))); + + blob = new JdbcBlob(new byte[] {15, 16}); + assertEquals(8, blob.setBytes(1, new byte[] {0, 1, 2, 3, 4, 5, 6, 7})); + assertTrue(Arrays.equals(new byte[] {0, 1, 2, 3, 4, 5, 6, 7}, blob.getBytes(1, (int)blob.length()))); + + blob.free(); + + try { + blob.setBytes(1, new byte[] {0, 1, 2}); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testSetBytesWithOffsetAndLength() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; + + JdbcBlob blob = new JdbcBlob(arr); + + try { + blob.setBytes(0, new byte[4], 0, 2); + + fail(); + } + catch (SQLException e) { + // No-op. + } + + try { + blob.setBytes(17, new byte[4], 0, 2); + + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + // No-op. + } + + try { + blob.setBytes(1, new byte[4], -1, 2); + + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + // No-op. + } + + try { + blob.setBytes(1, new byte[4], 0, 5); + + fail(); + } + catch (ArrayIndexOutOfBoundsException e) { + // No-op. + } + + assertEquals(4, blob.setBytes(1, new byte[] {3, 2, 1, 0}, 0, 4)); + assertTrue(Arrays.equals(new byte[] {3, 2, 1, 0, 4, 5, 6, 7}, blob.getBytes(1, arr.length))); + + assertEquals(4, blob.setBytes(5, new byte[] {7, 6, 5, 4}, 0, 4)); + assertTrue(Arrays.equals(new byte[] {3, 2, 1, 0, 7, 6, 5, 4}, blob.getBytes(1, arr.length))); + + assertEquals(4, blob.setBytes(7, new byte[] {8, 9, 10, 11}, 0, 4)); + assertTrue(Arrays.equals(new byte[] {3, 2, 1, 0, 7, 6, 8, 9, 10, 11}, blob.getBytes(1, (int)blob.length()))); + + assertEquals(2, blob.setBytes(1, new byte[] {3, 2, 1, 0}, 2, 2)); + assertTrue(Arrays.equals(new byte[] {1, 0, 1, 0, 7, 6, 8, 9, 10, 11}, blob.getBytes(1, (int)blob.length()))); + + assertEquals(2, blob.setBytes(9, new byte[] {3, 2, 1, 0}, 1, 2)); + assertTrue(Arrays.equals(new byte[] {1, 0, 1, 0, 7, 6, 8, 9, 2, 1}, blob.getBytes(1, (int)blob.length()))); + + assertEquals(3, blob.setBytes(9, new byte[] {3, 2, 1, 0}, 0, 3)); + assertTrue(Arrays.equals(new byte[] {1, 0, 1, 0, 7, 6, 8, 9, 3, 2, 1}, blob.getBytes(1, (int)blob.length()))); + + blob = new JdbcBlob(new byte[] {15, 16}); + assertEquals(8, blob.setBytes(1, new byte[] {0, 1, 2, 3, 4, 5, 6, 7}, 0, 8)); + assertTrue(Arrays.equals(new byte[] {0, 1, 2, 3, 4, 5, 6, 7}, blob.getBytes(1, (int)blob.length()))); + + blob.free(); + + try { + blob.setBytes(1, new byte[] {0, 1, 2}, 0, 2); + + fail(); + } + catch (SQLException e) { + // No-op. + } + } + + /** + * @throws Exception If failed. + */ + public void testTruncate() throws Exception { + byte[] arr = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; + + JdbcBlob blob = new JdbcBlob(arr); + + try { + blob.truncate(-1); + + fail(); + } + catch(SQLException e) { + // No-op. + } + + try { + blob.truncate(arr.length + 1); + + fail(); + } + catch(SQLException e) { + // No-op. + } + + blob.truncate(4); + assertTrue(Arrays.equals(new byte[] {0, 1, 2, 3}, blob.getBytes(1, (int)blob.length()))); + + blob.truncate(0); + assertEquals(0, (int)blob.length()); + + blob.free(); + + try { + blob.truncate(0); + + fail(); + } + catch (SQLException e) { + // No-op. + System.out.println(); + } + } + + /** + * @param is Input stream. + */ + private static byte[] readBytes(InputStream is) throws IOException { + byte[] tmp = new byte[16]; + + int i = 0; + int read; + int cnt = 0; + + while ((read = is.read()) != -1) { + tmp[i++] = (byte)read; + cnt++; + } + + byte[] res = new byte[cnt]; + + System.arraycopy(tmp, 0, res, 0, cnt); + + return res; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java ---------------------------------------------------------------------- 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 b23f947..0e7539f 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 @@ -33,14 +33,14 @@ import org.apache.ignite.testframework.GridTestUtils; */ public class JdbcInsertStatementSelfTest extends JdbcAbstractDmlStatementSelfTest { /** 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) values " + + "('p1', 1, 'John', 'White', 25, RAWTOHEX('White')), " + + "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black')), " + + "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('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) values " + + "(?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)"; /** Statement. */ private Statement stmt; @@ -79,6 +79,7 @@ public class JdbcInsertStatementSelfTest extends JdbcAbstractDmlStatementSelfTes assertEquals("John", rs.getString("firstName")); assertEquals("White", rs.getString("lastName")); assertEquals(25, rs.getInt("age")); + assertEquals("White", str(getBytes(rs.getBlob("data")))); break; case 2: @@ -86,6 +87,7 @@ public class JdbcInsertStatementSelfTest extends JdbcAbstractDmlStatementSelfTes assertEquals("Joe", rs.getString("firstName")); assertEquals("Black", rs.getString("lastName")); assertEquals(35, rs.getInt("age")); + assertEquals("Black", str(getBytes(rs.getBlob("data")))); break; case 3: @@ -93,6 +95,7 @@ public class JdbcInsertStatementSelfTest extends JdbcAbstractDmlStatementSelfTes assertEquals("Mike", rs.getString("firstName")); assertEquals("Green", rs.getString("lastName")); assertEquals(40, rs.getInt("age")); + assertEquals("Green", str(getBytes(rs.getBlob("data")))); break; case 4: @@ -100,6 +103,7 @@ public class JdbcInsertStatementSelfTest extends JdbcAbstractDmlStatementSelfTes assertEquals("Leah", rs.getString("firstName")); assertEquals("Grey", rs.getString("lastName")); assertEquals(22, rs.getInt("age")); + assertEquals("Grey", str(getBytes(rs.getBlob("data")))); break; default: http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java ---------------------------------------------------------------------- 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 f4577f5..1432a78 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 @@ -28,14 +28,14 @@ import org.apache.ignite.cache.CachePeekMode; */ public class JdbcMergeStatementSelfTest extends JdbcAbstractDmlStatementSelfTest { /** 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) values " + + "('p1', 1, 'John', 'White', 25, RAWTOHEX('White')), " + + "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black')), " + + "('p3', 3, 'Mike', 'Green', 40, RAWTOHEX('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) values " + + "(?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)"; /** Statement. */ protected Statement stmt; @@ -74,6 +74,7 @@ public class JdbcMergeStatementSelfTest extends JdbcAbstractDmlStatementSelfTest assertEquals("John", rs.getString("firstName")); assertEquals("White", rs.getString("lastName")); assertEquals(25, rs.getInt("age")); + assertEquals("White", str(getBytes(rs.getBlob("data")))); break; case 2: @@ -81,6 +82,7 @@ public class JdbcMergeStatementSelfTest extends JdbcAbstractDmlStatementSelfTest assertEquals("Joe", rs.getString("firstName")); assertEquals("Black", rs.getString("lastName")); assertEquals(35, rs.getInt("age")); + assertEquals("Black", str(getBytes(rs.getBlob("data")))); break; case 3: @@ -88,6 +90,7 @@ public class JdbcMergeStatementSelfTest extends JdbcAbstractDmlStatementSelfTest assertEquals("Mike", rs.getString("firstName")); assertEquals("Green", rs.getString("lastName")); assertEquals(40, rs.getInt("age")); + assertEquals("Green", str(getBytes(rs.getBlob("data")))); break; case 4: @@ -95,6 +98,7 @@ public class JdbcMergeStatementSelfTest extends JdbcAbstractDmlStatementSelfTest assertEquals("Leah", rs.getString("firstName")); assertEquals("Grey", rs.getString("lastName")); assertEquals(22, rs.getInt("age")); + assertEquals("Grey", str(getBytes(rs.getBlob("data")))); break; default: http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatementSelfTest.java ---------------------------------------------------------------------- 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 5e402ff..30bd018 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 @@ -20,6 +20,7 @@ package org.apache.ignite.internal.jdbc2; import java.io.Serializable; import java.math.BigDecimal; import java.net.URL; +import java.sql.Blob; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -117,6 +118,7 @@ public class JdbcPreparedStatementSelfTest extends GridCommonAbstractTest { o.bigVal = new BigDecimal(1); o.strVal = "str"; o.arrVal = new byte[] {1}; + o.blobVal = new byte[] {1}; o.dateVal = new Date(1); o.timeVal = new Time(1); o.tsVal = new Timestamp(1); @@ -529,6 +531,47 @@ public class JdbcPreparedStatementSelfTest extends GridCommonAbstractTest { /** * @throws Exception If failed. */ + public void testBlob() throws Exception { + stmt = conn.prepareStatement("select * from TestObject where blobVal is not distinct from ?"); + + Blob blob = conn.createBlob(); + + blob.setBytes(1, new byte[] {1}); + + stmt.setBlob(1, blob); + + ResultSet rs = stmt.executeQuery(); + + int cnt = 0; + + while (rs.next()) { + if (cnt == 0) + assert rs.getInt("id") == 1; + + cnt++; + } + + assertEquals(1, cnt); + + stmt.setNull(1, BINARY); + + rs = stmt.executeQuery(); + + cnt = 0; + + while (rs.next()) { + if (cnt == 0) + assert rs.getInt("id") == 2; + + cnt++; + } + + assert cnt == 1; + } + + /** + * @throws Exception If failed. + */ public void testDate() throws Exception { stmt = conn.prepareStatement("select * from TestObject where dateVal is not distinct from ?"); @@ -725,6 +768,10 @@ public class JdbcPreparedStatementSelfTest extends GridCommonAbstractTest { /** */ @QuerySqlField + private byte[] blobVal; + + /** */ + @QuerySqlField private Date dateVal; /** */ http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java ---------------------------------------------------------------------- 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 e2f09ba..abfdf90 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 @@ -18,6 +18,8 @@ package org.apache.ignite.jdbc.suite; import junit.framework.TestSuite; +import org.apache.ignite.internal.jdbc2.JdbcBlobTest; +import org.apache.ignite.internal.jdbc2.JdbcDistributedJoinsQueryTest; import org.apache.ignite.jdbc.JdbcComplexQuerySelfTest; import org.apache.ignite.jdbc.JdbcConnectionSelfTest; import org.apache.ignite.jdbc.JdbcEmptyCacheSelfTest; @@ -41,7 +43,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite { public static TestSuite suite() throws Exception { TestSuite suite = new TestSuite("Ignite JDBC Driver Test Suite"); - // Thin client based driver tests + // Thin client based driver tests. suite.addTest(new TestSuite(JdbcConnectionSelfTest.class)); suite.addTest(new TestSuite(JdbcStatementSelfTest.class)); suite.addTest(new TestSuite(JdbcPreparedStatementSelfTest.class)); @@ -61,7 +63,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite { suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcPreparedStatementSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcResultSetSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcComplexQuerySelfTest.class)); - suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDistributedJoinsQueryTest.class)); + suite.addTest(new TestSuite(JdbcDistributedJoinsQueryTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcMetadataSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcEmptyCacheSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcLocalCachesSelfTest.class)); @@ -71,6 +73,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite { suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcInsertStatementSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcBinaryMarshallerInsertStatementSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDeleteStatementSelfTest.class)); + suite.addTest(new TestSuite(JdbcBlobTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcStreamingSelfTest.class)); // DDL tests. http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBlob.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..d1610d1 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBlob.java @@ -0,0 +1,191 @@ +/* + * 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.ByteArrayInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.Blob; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Arrays; +import org.apache.ignite.internal.util.typedef.internal.U; + +/** + * Simple BLOB implementation. Actually there is no such entity as BLOB in Ignite. So using arrays is preferable way + * to work with binary objects. + * + * This implementation can be useful for reading binary fields of objects through JDBC. + */ +public class JdbcBlob implements Blob { + /** Byte array. */ + private byte[] arr; + + /** + * @param arr Byte array. + */ + public JdbcBlob(byte[] arr) { + this.arr = arr; + } + + /** {@inheritDoc} */ + @Override public long length() throws SQLException { + ensureNotClosed(); + + return arr.length; + } + + /** {@inheritDoc} */ + @Override public byte[] getBytes(long pos, int len) throws SQLException { + ensureNotClosed(); + + if (pos < 1 || arr.length - pos < 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 +']'); + + int idx = (int)(pos - 1); + + int size = len > arr.length - idx ? arr.length - idx : len; + + byte[] res = new byte[size]; + + U.arrayCopy(arr, idx, res, 0, size); + + return res; + } + + /** {@inheritDoc} */ + @Override public InputStream getBinaryStream() throws SQLException { + ensureNotClosed(); + + return new ByteArrayInputStream(arr); + } + + /** {@inheritDoc} */ + @Override public InputStream getBinaryStream(long pos, long len) throws SQLException { + ensureNotClosed(); + + if (pos < 1 || len < 1 || pos > arr.length || len > arr.length - pos + 1) + throw new SQLException("Invalid argument. Position can't be less than 1 or " + + "greater than size of underlying byte array. Requested length can't be negative and can't be " + + "greater than available bytes from given position [pos=" + pos + ", len=" + len +']'); + + + return new ByteArrayInputStream(arr, (int)(pos - 1), (int)len); + } + + /** {@inheritDoc} */ + @Override public long position(byte[] ptrn, long start) throws SQLException { + ensureNotClosed(); + + if (start < 1 || start > arr.length || ptrn.length == 0 || ptrn.length > arr.length) + return -1; + + for(int i = 0, pos = (int)(start - 1); pos < arr.length;) { + if (arr[pos] == ptrn[i]) { + pos++; + + i++; + + if (i == ptrn.length) + return pos - ptrn.length + 1; + } + else { + pos = pos - i + 1; + + i = 0; + } + } + + return -1; + } + + /** {@inheritDoc} */ + @Override public long position(Blob ptrn, long start) throws SQLException { + ensureNotClosed(); + + if (start < 1 || start > arr.length || ptrn.length() == 0 || ptrn.length() > arr.length) + return -1; + + return position(ptrn.getBytes(1, (int)ptrn.length()), start); + } + + /** {@inheritDoc} */ + @Override public int setBytes(long pos, byte[] bytes) throws SQLException { + return setBytes(pos, bytes, 0, bytes.length); + } + + /** {@inheritDoc} */ + @Override public int setBytes(long pos, byte[] bytes, int off, int len) throws SQLException { + ensureNotClosed(); + + if (pos < 1) + throw new SQLException("Invalid argument. Position can't be less than 1 [pos=" + pos + ']'); + + int idx = (int)(pos - 1); + + if (pos - 1 > arr.length || off < 0 || off >= bytes.length || off + len > bytes.length) + throw new ArrayIndexOutOfBoundsException(); + + byte[] dst = arr; + + if (idx + len > arr.length) { + dst = new byte[arr.length + (len - (arr.length - idx))]; + + U.arrayCopy(arr, 0, dst, 0, idx); + + arr = dst; + } + + U.arrayCopy(bytes, off, dst, idx, len); + + return len; + } + + /** {@inheritDoc} */ + @Override public OutputStream setBinaryStream(long pos) throws SQLException { + throw new SQLFeatureNotSupportedException(); + } + + /** {@inheritDoc} */ + @Override public void truncate(long len) throws SQLException { + ensureNotClosed(); + + if (len < 0 || len > arr.length) + throw new SQLException("Invalid argument. Length can't be " + + "less than zero or greater than Blob length [len=" + len + ']'); + + arr = Arrays.copyOf(arr, (int)len); + + } + + /** {@inheritDoc} */ + @Override public void free() throws SQLException { + if (arr != null) + arr = null; + } + + /** + * + */ + private void ensureNotClosed() throws SQLException { + if (arr == null) + throw new SQLException("Blob instance can't be used after free() has been called."); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java ---------------------------------------------------------------------- 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 f6f79fb..ee8b605 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 @@ -597,7 +597,7 @@ public class JdbcConnection implements Connection { @Override public Blob createBlob() throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("SQL-specific types are not supported."); + return new JdbcBlob(new byte[0]); } /** {@inheritDoc} */ http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java ---------------------------------------------------------------------- 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 54e58e9..1a66ced 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 @@ -239,9 +239,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat /** {@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} */ http://git-wip-us.apache.org/repos/asf/ignite/blob/64ab5cdf/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java ---------------------------------------------------------------------- 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 0f91bd8..10cf17a 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 @@ -952,9 +952,7 @@ public class JdbcResultSet 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} */ @@ -985,9 +983,7 @@ public class JdbcResultSet 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} */