From 5f10e647e3fdc502635dc68c81a053b8a0f4bd1d Mon Sep 17 00:00:00 2001 From: kimminseok Date: Sun, 26 Oct 2025 21:31:11 +0900 Subject: [PATCH 1/7] Fix wasNull not being set in getter methods --- .../java/tech/turso/jdbc4/JDBC4ResultSet.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java index 009e38884..46152c2d1 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java @@ -31,6 +31,7 @@ import tech.turso.core.TursoResultSet; public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { private final TursoResultSet resultSet; + private boolean wasNull = false; public JDBC4ResultSet(TursoResultSet resultSet) { this.resultSet = resultSet; @@ -55,6 +56,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Nullable public String getString(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } @@ -64,6 +66,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public boolean getBoolean(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return false; } @@ -73,6 +76,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public byte getByte(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return 0; } @@ -82,6 +86,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public short getShort(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return 0; } @@ -91,6 +96,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public int getInt(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return 0; } @@ -100,6 +106,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public long getLong(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return 0; } @@ -109,6 +116,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public float getFloat(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return 0; } @@ -118,6 +126,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public double getDouble(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return 0; } @@ -129,6 +138,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Nullable public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } @@ -141,6 +151,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Nullable public byte[] getBytes(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } @@ -151,6 +162,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Nullable public Date getDate(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } @@ -171,6 +183,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @SkipNullableCheck public Time getTime(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } @@ -191,6 +204,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @SkipNullableCheck public Timestamp getTimestamp(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } @@ -285,6 +299,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Nullable public Date getDate(String columnLabel) throws SQLException { final Object result = resultSet.get(columnLabel); + wasNull = result == null; if (result == null) { return null; } @@ -386,6 +401,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @SkipNullableCheck public Reader getCharacterStream(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } @@ -402,6 +418,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Nullable public BigDecimal getBigDecimal(int columnIndex) throws SQLException { final Object result = resultSet.get(columnIndex); + wasNull = result == null; if (result == null) { return null; } From 791e19892d7f914c1dc3f450ab33a279bbdd0724 Mon Sep 17 00:00:00 2001 From: kimminseok Date: Sun, 26 Oct 2025 21:45:54 +0900 Subject: [PATCH 2/7] Refactor columnLabel getters to use delegation pattern --- .../java/tech/turso/jdbc4/JDBC4ResultSet.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java index 46152c2d1..d106fdd7e 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java @@ -240,59 +240,56 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { } @Override + @Nullable public String getString(String columnLabel) throws SQLException { - final Object result = this.resultSet.get(columnLabel); - if (result == null) { - return ""; - } - - return wrapTypeConversion(() -> (String) result); + return getString(findColumn(columnLabel)); } @Override public boolean getBoolean(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getBoolean(findColumn(columnLabel)); } @Override public byte getByte(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getByte(findColumn(columnLabel)); } @Override public short getShort(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getShort(findColumn(columnLabel)); } @Override public int getInt(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getInt(findColumn(columnLabel)); } @Override public long getLong(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getLong(findColumn(columnLabel)); } @Override public float getFloat(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getFloat(findColumn(columnLabel)); } @Override public double getDouble(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getDouble(findColumn(columnLabel)); } @Override @SkipNullableCheck public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getBigDecimal(findColumn(columnLabel), scale); } @Override + @Nullable public byte[] getBytes(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getBytes(findColumn(columnLabel)); } @Override @@ -323,7 +320,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override @SkipNullableCheck public Time getTime(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getTime(findColumn(columnLabel)); } @Override From 7ee339a754e22117459713802d4ba595dbf547b2 Mon Sep 17 00:00:00 2001 From: kimminseok Date: Sun, 26 Oct 2025 22:09:47 +0900 Subject: [PATCH 3/7] Extract timezone offset calculation to helper method --- .../java/tech/turso/jdbc4/JDBC4ResultSet.java | 36 +++++-------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java index d106fdd7e..b10e28be5 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java @@ -838,15 +838,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { if (date == null || cal == null) { return date; } - - final Calendar localCal = Calendar.getInstance(); - localCal.setTime(date); - - final long offset = - cal.getTimeZone().getOffset(date.getTime()) - - localCal.getTimeZone().getOffset(date.getTime()); - - return new Date(date.getTime() + offset); + return new Date(date.getTime() + calculateTimezoneOffset(date.getTime(), cal)); } @Override @@ -862,15 +854,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { if (time == null || cal == null) { return time; } - - final Calendar localCal = Calendar.getInstance(); - localCal.setTime(time); - - final long offset = - cal.getTimeZone().getOffset(time.getTime()) - - localCal.getTimeZone().getOffset(time.getTime()); - - return new Time(time.getTime() + offset); + return new Time(time.getTime() + calculateTimezoneOffset(time.getTime(), cal)); } @Override @@ -886,15 +870,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { if (timestamp == null || cal == null) { return timestamp; } - - final Calendar localCal = Calendar.getInstance(); - localCal.setTime(timestamp); - - final long offset = - cal.getTimeZone().getOffset(timestamp.getTime()) - - localCal.getTimeZone().getOffset(timestamp.getTime()); - - return new Timestamp(timestamp.getTime() + offset); + return new Timestamp(timestamp.getTime() + calculateTimezoneOffset(timestamp.getTime(), cal)); } @Override @@ -1342,6 +1318,12 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { throw new UnsupportedOperationException("not implemented"); } + private long calculateTimezoneOffset(long timeMillis, Calendar targetCal) { + Calendar localCal = Calendar.getInstance(); + return targetCal.getTimeZone().getOffset(timeMillis) + - localCal.getTimeZone().getOffset(timeMillis); + } + @FunctionalInterface public interface ResultSetSupplier { T get() throws Exception; From 27233a947fe34e6b7923b556fd80375ea81ec494 Mon Sep 17 00:00:00 2001 From: kimminseok Date: Sun, 26 Oct 2025 22:34:43 +0900 Subject: [PATCH 4/7] Add consistent wasNull handling for stream getter methods --- .../java/tech/turso/jdbc4/JDBC4ResultSet.java | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java index b10e28be5..8c440177f 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java @@ -1,5 +1,6 @@ package tech.turso.jdbc4; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; @@ -49,7 +50,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public boolean wasNull() throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return wasNull; } @Override @@ -224,19 +225,53 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override @SkipNullableCheck public InputStream getAsciiStream(int columnIndex) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + final Object result = resultSet.get(columnIndex); + wasNull = result == null; + if (result == null) { + return null; + } + return wrapTypeConversion(() -> { + if (result instanceof String) { + return new ByteArrayInputStream(((String) result).getBytes("US-ASCII")); + } else if (result instanceof byte[]) { + return new ByteArrayInputStream((byte[]) result); + } + throw new SQLException("Cannot convert to ASCII stream: " + result.getClass()); + }); } @Override @SkipNullableCheck public InputStream getUnicodeStream(int columnIndex) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + final Object result = resultSet.get(columnIndex); + wasNull = result == null; + if (result == null) { + return null; + } + return wrapTypeConversion(() -> { + if (result instanceof String) { + return new ByteArrayInputStream(((String) result).getBytes("UTF-8")); + } else if (result instanceof byte[]) { + return new ByteArrayInputStream((byte[]) result); + } + throw new SQLException("Cannot convert to Unicode stream: " + result.getClass()); + }); } @Override @SkipNullableCheck public InputStream getBinaryStream(int columnIndex) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + final Object result = resultSet.get(columnIndex); + wasNull = result == null; + if (result == null) { + return null; + } + return wrapTypeConversion(() -> { + if (result instanceof byte[]) { + return new ByteArrayInputStream((byte[]) result); + } + throw new SQLException("Cannot convert to binary stream: " + result.getClass()); + }); } @Override @@ -332,19 +367,19 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override @SkipNullableCheck public InputStream getAsciiStream(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getAsciiStream(findColumn(columnLabel)); } @Override @SkipNullableCheck public InputStream getUnicodeStream(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getUnicodeStream(findColumn(columnLabel)); } @Override @SkipNullableCheck public InputStream getBinaryStream(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getBinaryStream(findColumn(columnLabel)); } @Override From 71f53b58507ae9d55d6c5ad1aedeef91738d69b5 Mon Sep 17 00:00:00 2001 From: kimminseok Date: Sun, 26 Oct 2025 22:36:38 +0900 Subject: [PATCH 5/7] Add wasNull tracking to getObject methods --- .../java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java index 8c440177f..777a84ffa 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java @@ -405,13 +405,15 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override public Object getObject(int columnIndex) throws SQLException { - return resultSet.get(columnIndex); + final Object result = resultSet.get(columnIndex); + wasNull = result == null; + return result; } @Override @SkipNullableCheck public Object getObject(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getObject(findColumn(columnLabel)); } @Override From 17f1a070ed71cdd97960612d7b1ca2c7ec8bf367 Mon Sep 17 00:00:00 2001 From: kimminseok Date: Sun, 26 Oct 2025 22:37:47 +0900 Subject: [PATCH 6/7] Add comprehensive tests for ResultSet getter methods --- .../tech/turso/jdbc4/JDBC4ResultSetTest.java | 282 ++++++++++++++++++ 1 file changed, 282 insertions(+) diff --git a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java index 45fc0f1ee..b9202b30c 100644 --- a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java +++ b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java @@ -2,6 +2,7 @@ package tech.turso.jdbc4; import static org.junit.jupiter.api.Assertions.*; +import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.math.RoundingMode; @@ -869,4 +870,285 @@ class JDBC4ResultSetTest { assertNotNull(timestamp); } + + @Test + void test_wasNull() throws Exception { + stmt.executeUpdate("CREATE TABLE test_was_null (id INTEGER, name TEXT);"); + stmt.executeUpdate("INSERT INTO test_was_null VALUES (1, 'test');"); + stmt.executeUpdate("INSERT INTO test_was_null VALUES (NULL, NULL);"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_was_null"); + + // First row - non-null values + assertTrue(resultSet.next()); + int id = resultSet.getInt(1); + assertFalse(resultSet.wasNull()); + String name = resultSet.getString(2); + assertFalse(resultSet.wasNull()); + + // Second row - null values + assertTrue(resultSet.next()); + int nullInt = resultSet.getInt(1); + assertTrue(resultSet.wasNull()); + assertEquals(0, nullInt); + String nullString = resultSet.getString(2); + assertTrue(resultSet.wasNull()); + assertNull(nullString); + } + + @Test + void test_columnLabel_getters() throws Exception { + stmt.executeUpdate( + "CREATE TABLE test_column_label (" + + "bool_col INTEGER, " + + "byte_col INTEGER, " + + "short_col INTEGER, " + + "int_col INTEGER, " + + "long_col BIGINT, " + + "float_col REAL, " + + "double_col REAL, " + + "bytes_col BLOB);"); + + stmt.executeUpdate( + "INSERT INTO test_column_label VALUES (" + + "1, 127, 32767, 2147483647, 9223372036854775807, 3.14, 2.718281828, X'48656C6C6F');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_column_label"); + assertTrue(resultSet.next()); + + // Test columnLabel-based getters + assertTrue(resultSet.getBoolean("bool_col")); + assertEquals(127, resultSet.getByte("byte_col")); + assertEquals(32767, resultSet.getShort("short_col")); + assertEquals(2147483647, resultSet.getInt("int_col")); + assertEquals(9223372036854775807L, resultSet.getLong("long_col")); + assertEquals(3.14f, resultSet.getFloat("float_col"), 0.001); + assertEquals(2.718281828, resultSet.getDouble("double_col"), 0.000001); + assertArrayEquals("Hello".getBytes(), resultSet.getBytes("bytes_col")); + } + + @Test + void test_getObject_with_columnLabel() throws Exception { + stmt.executeUpdate("CREATE TABLE test_object (id INTEGER, name TEXT);"); + stmt.executeUpdate("INSERT INTO test_object VALUES (42, 'test');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_object"); + assertTrue(resultSet.next()); + + Object idObj = resultSet.getObject("id"); + assertEquals(42L, idObj); + assertFalse(resultSet.wasNull()); + + Object nameObj = resultSet.getObject("name"); + assertEquals("test", nameObj); + assertFalse(resultSet.wasNull()); + } + + @Test + void test_getBigDecimal_with_scale_columnLabel() throws Exception { + stmt.executeUpdate("CREATE TABLE test_decimal (amount REAL);"); + stmt.executeUpdate("INSERT INTO test_decimal VALUES (123.456789);"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_decimal"); + assertTrue(resultSet.next()); + + BigDecimal result = resultSet.getBigDecimal("amount", 2); + assertEquals(new BigDecimal("123.46"), result); // Should be rounded to 2 decimal places + } + + @Test + void test_getTime_with_columnLabel() throws Exception { + stmt.executeUpdate("CREATE TABLE test_time (time_col BLOB);"); + + long timeMillis = System.currentTimeMillis(); + byte[] timeBytes = ByteBuffer.allocate(Long.BYTES).putLong(timeMillis).array(); + StringBuilder hexString = new StringBuilder(); + for (byte b : timeBytes) { + hexString.append(String.format("%02X", b)); + } + stmt.executeUpdate("INSERT INTO test_time VALUES (X'" + hexString + "');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_time"); + assertTrue(resultSet.next()); + + Time time = resultSet.getTime("time_col"); + assertNotNull(time); + assertEquals(timeMillis, time.getTime()); + } + + @Test + void test_getAsciiStream() throws Exception { + stmt.executeUpdate("CREATE TABLE test_ascii (text_col TEXT);"); + stmt.executeUpdate("INSERT INTO test_ascii VALUES ('Hello ASCII');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_ascii"); + assertTrue(resultSet.next()); + + InputStream stream = resultSet.getAsciiStream(1); + assertNotNull(stream); + byte[] buffer = new byte[11]; + int bytesRead = stream.read(buffer); + assertEquals(11, bytesRead); + assertEquals("Hello ASCII", new String(buffer, "US-ASCII")); + assertFalse(resultSet.wasNull()); + } + + @Test + void test_getAsciiStream_with_columnLabel() throws Exception { + stmt.executeUpdate("CREATE TABLE test_ascii (text_col TEXT);"); + stmt.executeUpdate("INSERT INTO test_ascii VALUES ('Test');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_ascii"); + assertTrue(resultSet.next()); + + InputStream stream = resultSet.getAsciiStream("text_col"); + assertNotNull(stream); + byte[] buffer = new byte[4]; + stream.read(buffer); + assertEquals("Test", new String(buffer, "US-ASCII")); + } + + @Test + void test_getBinaryStream() throws Exception { + stmt.executeUpdate("CREATE TABLE test_binary (binary_col BLOB);"); + byte[] data = {0x01, 0x02, 0x03, 0x04, 0x05}; + + StringBuilder hexString = new StringBuilder(); + for (byte b : data) { + hexString.append(String.format("%02X", b)); + } + stmt.executeUpdate("INSERT INTO test_binary VALUES (X'" + hexString + "');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_binary"); + assertTrue(resultSet.next()); + + InputStream stream = resultSet.getBinaryStream(1); + assertNotNull(stream); + byte[] buffer = new byte[5]; + int bytesRead = stream.read(buffer); + assertEquals(5, bytesRead); + assertArrayEquals(data, buffer); + assertFalse(resultSet.wasNull()); + } + + @Test + void test_getBinaryStream_with_columnLabel() throws Exception { + stmt.executeUpdate("CREATE TABLE test_binary (data BLOB);"); + byte[] data = {0x0A, 0x0B, 0x0C}; + + StringBuilder hexString = new StringBuilder(); + for (byte b : data) { + hexString.append(String.format("%02X", b)); + } + stmt.executeUpdate("INSERT INTO test_binary VALUES (X'" + hexString + "');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_binary"); + assertTrue(resultSet.next()); + + InputStream stream = resultSet.getBinaryStream("data"); + assertNotNull(stream); + byte[] buffer = new byte[3]; + stream.read(buffer); + assertArrayEquals(data, buffer); + } + + @Test + void test_getUnicodeStream() throws Exception { + stmt.executeUpdate("CREATE TABLE test_unicode (text_col TEXT);"); + stmt.executeUpdate("INSERT INTO test_unicode VALUES ('Hello minseok');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_unicode"); + assertTrue(resultSet.next()); + + InputStream stream = resultSet.getUnicodeStream(1); + assertNotNull(stream); + byte[] buffer = new byte[1024]; + int bytesRead = stream.read(buffer); + String result = new String(buffer, 0, bytesRead, "UTF-8"); + assertEquals("Hello minseok", result); + assertFalse(resultSet.wasNull()); + } + + @Test + void test_getUnicodeStream_with_columnLabel() throws Exception { + stmt.executeUpdate("CREATE TABLE test_unicode (text_col TEXT);"); + stmt.executeUpdate("INSERT INTO test_unicode VALUES ('Unicode 테스트');"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_unicode"); + assertTrue(resultSet.next()); + + InputStream stream = resultSet.getUnicodeStream("text_col"); + assertNotNull(stream); + byte[] buffer = new byte[1024]; + int bytesRead = stream.read(buffer); + String result = new String(buffer, 0, bytesRead, "UTF-8"); + assertEquals("Unicode 테스트", result); + } + + @Test + void test_stream_methods_return_null_on_null() throws Exception { + stmt.executeUpdate("CREATE TABLE test_null_stream (text_col TEXT, binary_col BLOB);"); + stmt.executeUpdate("INSERT INTO test_null_stream VALUES (NULL, NULL);"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_null_stream"); + assertTrue(resultSet.next()); + + assertNull(resultSet.getAsciiStream(1)); + assertTrue(resultSet.wasNull()); + + assertNull(resultSet.getUnicodeStream(1)); + assertTrue(resultSet.wasNull()); + + assertNull(resultSet.getBinaryStream(2)); + assertTrue(resultSet.wasNull()); + } + + @Test + void test_getMetaData_column_count() throws Exception { + stmt.executeUpdate("CREATE TABLE test_meta (col1 INTEGER, col2 TEXT, col3 REAL);"); + stmt.executeUpdate("INSERT INTO test_meta VALUES (1, 'test', 3.14);"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_meta"); + ResultSetMetaData metaData = resultSet.getMetaData(); + + assertEquals(3, metaData.getColumnCount()); + assertEquals("col1", metaData.getColumnName(1)); + assertEquals("col2", metaData.getColumnName(2)); + assertEquals("col3", metaData.getColumnName(3)); + assertEquals("col1", metaData.getColumnLabel(1)); + assertEquals(Integer.MAX_VALUE, metaData.getColumnDisplaySize(1)); + } + + @Test + void test_wasNull_consistency_across_types() throws Exception { + stmt.executeUpdate( + "CREATE TABLE test_null_types (" + + "int_col INTEGER, " + + "text_col TEXT, " + + "real_col REAL, " + + "blob_col BLOB);"); + stmt.executeUpdate("INSERT INTO test_null_types VALUES (NULL, NULL, NULL, NULL);"); + + ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_null_types"); + assertTrue(resultSet.next()); + + // Test wasNull for various getter methods + resultSet.getInt(1); + assertTrue(resultSet.wasNull()); + + resultSet.getString(2); + assertTrue(resultSet.wasNull()); + + resultSet.getDouble(3); + assertTrue(resultSet.wasNull()); + + resultSet.getBytes(4); + assertTrue(resultSet.wasNull()); + + resultSet.getObject(1); + assertTrue(resultSet.wasNull()); + + resultSet.getBigDecimal(3); + assertTrue(resultSet.wasNull()); + } } From 2b456ec7e4d6a64372d7f7d68568e22517cf0bdd Mon Sep 17 00:00:00 2001 From: kimminseok Date: Mon, 27 Oct 2025 00:31:49 +0900 Subject: [PATCH 7/7] chore: apply spotless formatting --- .../java/tech/turso/jdbc4/JDBC4ResultSet.java | 47 ++++++++++--------- .../tech/turso/jdbc4/JDBC4ResultSetTest.java | 32 ++++++------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java index 8df20ff2c..4070f1445 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java @@ -236,14 +236,15 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { if (result == null) { return null; } - return wrapTypeConversion(() -> { - if (result instanceof String) { - return new ByteArrayInputStream(((String) result).getBytes("US-ASCII")); - } else if (result instanceof byte[]) { - return new ByteArrayInputStream((byte[]) result); - } - throw new SQLException("Cannot convert to ASCII stream: " + result.getClass()); - }); + return wrapTypeConversion( + () -> { + if (result instanceof String) { + return new ByteArrayInputStream(((String) result).getBytes("US-ASCII")); + } else if (result instanceof byte[]) { + return new ByteArrayInputStream((byte[]) result); + } + throw new SQLException("Cannot convert to ASCII stream: " + result.getClass()); + }); } @Override @@ -254,14 +255,15 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { if (result == null) { return null; } - return wrapTypeConversion(() -> { - if (result instanceof String) { - return new ByteArrayInputStream(((String) result).getBytes("UTF-8")); - } else if (result instanceof byte[]) { - return new ByteArrayInputStream((byte[]) result); - } - throw new SQLException("Cannot convert to Unicode stream: " + result.getClass()); - }); + return wrapTypeConversion( + () -> { + if (result instanceof String) { + return new ByteArrayInputStream(((String) result).getBytes("UTF-8")); + } else if (result instanceof byte[]) { + return new ByteArrayInputStream((byte[]) result); + } + throw new SQLException("Cannot convert to Unicode stream: " + result.getClass()); + }); } @Override @@ -272,12 +274,13 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { if (result == null) { return null; } - return wrapTypeConversion(() -> { - if (result instanceof byte[]) { - return new ByteArrayInputStream((byte[]) result); - } - throw new SQLException("Cannot convert to binary stream: " + result.getClass()); - }); + return wrapTypeConversion( + () -> { + if (result instanceof byte[]) { + return new ByteArrayInputStream((byte[]) result); + } + throw new SQLException("Cannot convert to binary stream: " + result.getClass()); + }); } @Override diff --git a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java index b9202b30c..25d993064 100644 --- a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java +++ b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4ResultSetTest.java @@ -899,19 +899,19 @@ class JDBC4ResultSetTest { @Test void test_columnLabel_getters() throws Exception { stmt.executeUpdate( - "CREATE TABLE test_column_label (" + - "bool_col INTEGER, " + - "byte_col INTEGER, " + - "short_col INTEGER, " + - "int_col INTEGER, " + - "long_col BIGINT, " + - "float_col REAL, " + - "double_col REAL, " + - "bytes_col BLOB);"); + "CREATE TABLE test_column_label (" + + "bool_col INTEGER, " + + "byte_col INTEGER, " + + "short_col INTEGER, " + + "int_col INTEGER, " + + "long_col BIGINT, " + + "float_col REAL, " + + "double_col REAL, " + + "bytes_col BLOB);"); stmt.executeUpdate( - "INSERT INTO test_column_label VALUES (" + - "1, 127, 32767, 2147483647, 9223372036854775807, 3.14, 2.718281828, X'48656C6C6F');"); + "INSERT INTO test_column_label VALUES (" + + "1, 127, 32767, 2147483647, 9223372036854775807, 3.14, 2.718281828, X'48656C6C6F');"); ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_column_label"); assertTrue(resultSet.next()); @@ -1122,11 +1122,11 @@ class JDBC4ResultSetTest { @Test void test_wasNull_consistency_across_types() throws Exception { stmt.executeUpdate( - "CREATE TABLE test_null_types (" + - "int_col INTEGER, " + - "text_col TEXT, " + - "real_col REAL, " + - "blob_col BLOB);"); + "CREATE TABLE test_null_types (" + + "int_col INTEGER, " + + "text_col TEXT, " + + "real_col REAL, " + + "blob_col BLOB);"); stmt.executeUpdate("INSERT INTO test_null_types VALUES (NULL, NULL, NULL, NULL);"); ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_null_types");