diff --git a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java index 60b5316fe..a260d1ef2 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java @@ -6,6 +6,7 @@ import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; +import java.nio.ByteBuffer; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; @@ -121,17 +122,38 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep @Override public void setDate(int parameterIndex, Date x) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + } else { + long time = x.getTime(); + this.statement.bindBlob( + parameterIndex, ByteBuffer.allocate(Long.BYTES).putLong(time).array()); + } } @Override public void setTime(int parameterIndex, Time x) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + } else { + long time = x.getTime(); + this.statement.bindBlob( + parameterIndex, ByteBuffer.allocate(Long.BYTES).putLong(time).array()); + } } @Override public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { - // TODO + requireNonNull(this.statement); + if (x == null) { + this.statement.bindNull(parameterIndex); + } else { + long time = x.getTime(); + this.statement.bindBlob( + parameterIndex, ByteBuffer.allocate(Long.BYTES).putLong(time).array()); + } } @Override @@ -212,17 +234,18 @@ public final class JDBC4PreparedStatement extends JDBC4Statement implements Prep @Override public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { - // TODO + setDate(parameterIndex, x); } @Override public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { - // TODO + setTime(parameterIndex, x); } @Override public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { - // TODO + // TODO: Apply calendar timezone conversion + setTimestamp(parameterIndex, x); } @Override 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 c2cc31a0a..5d9156b88 100644 --- a/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java +++ b/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java @@ -5,6 +5,7 @@ import java.io.Reader; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URL; +import java.nio.ByteBuffer; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; @@ -146,21 +147,63 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { } @Override - @SkipNullableCheck + @Nullable public Date getDate(int columnIndex) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + final Object result = resultSet.get(columnIndex); + if (result == null) { + return null; + } + return wrapTypeConversion( + () -> { + if (result instanceof byte[]) { + byte[] bytes = (byte[]) result; + if (bytes.length == Long.BYTES) { + long time = ByteBuffer.wrap(bytes).getLong(); + return new Date(time); + } + } + throw new SQLException("Cannot convert value to Date: " + result.getClass()); + }); } @Override @SkipNullableCheck public Time getTime(int columnIndex) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + final Object result = resultSet.get(columnIndex); + if (result == null) { + return null; + } + return wrapTypeConversion( + () -> { + if (result instanceof byte[]) { + byte[] bytes = (byte[]) result; + if (bytes.length == Long.BYTES) { + long time = ByteBuffer.wrap(bytes).getLong(); + return new Time(time); + } + } + throw new SQLException("Cannot convert value to Date: " + result.getClass()); + }); } @Override @SkipNullableCheck public Timestamp getTimestamp(int columnIndex) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + final Object result = resultSet.get(columnIndex); + if (result == null) { + return null; + } + return wrapTypeConversion( + () -> { + if (result instanceof byte[]) { + byte[] bytes = (byte[]) result; + if (bytes.length == Long.BYTES) { + long time = ByteBuffer.wrap(bytes).getLong(); + return new Timestamp(time); + } + } + throw new SQLException("Cannot convert value to Timestamp: " + result.getClass()); + }); } @Override @@ -238,9 +281,27 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { } @Override - @SkipNullableCheck + @Nullable public Date getDate(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + final Object result = resultSet.get(columnLabel); + if (result == null) { + return null; + } + return wrapTypeConversion( + () -> { + if (result instanceof byte[]) { + byte[] bytes = (byte[]) result; + if (bytes.length == Long.BYTES) { + long time = ByteBuffer.wrap(bytes).getLong(); + return new Date(time); + } + } + // Try to parse as string if it's stored as TEXT + if (result instanceof String) { + return Date.valueOf((String) result); + } + throw new SQLException("Cannot convert value to Date: " + result.getClass()); + }); } @Override @@ -252,7 +313,7 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { @Override @SkipNullableCheck public Timestamp getTimestamp(String columnLabel) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + return getTimestamp(findColumn(columnLabel)); } @Override @@ -738,39 +799,45 @@ public final class JDBC4ResultSet implements ResultSet, ResultSetMetaData { } @Override - @SkipNullableCheck + @Nullable public Date getDate(int columnIndex, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + // TODO: Properly handle timezone conversion with Calendar + return getDate(columnIndex); } @Override - @SkipNullableCheck + @Nullable public Date getDate(String columnLabel, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + // TODO: Properly handle timezone conversion with Calendar + return getDate(columnLabel); } @Override @SkipNullableCheck public Time getTime(int columnIndex, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + // TODO: Properly handle timezone conversion with Calendar + return getTime(columnIndex); } @Override @SkipNullableCheck public Time getTime(String columnLabel, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + // TODO: Properly handle timezone conversion with Calendar + return getTime(columnLabel); } @Override @SkipNullableCheck public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + // TODO: Apply calendar timezone conversion + return getTimestamp(columnIndex); } @Override @SkipNullableCheck public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("not implemented"); + // TODO: Apply calendar timezone conversion + return getTimestamp(findColumn(columnLabel)); } @Override diff --git a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java index 866a56bda..7d2f0274b 100644 --- a/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java +++ b/bindings/java/src/test/java/tech/turso/jdbc4/JDBC4PreparedStatementTest.java @@ -6,9 +6,12 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; +import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; import java.util.Properties; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -225,6 +228,84 @@ class JDBC4PreparedStatementTest { assertArrayEquals(new byte[] {7, 8, 9}, rs.getBytes(1)); } + @Test + void testSetDate() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + PreparedStatement stmt = + connection.prepareStatement("INSERT INTO test (col) VALUES (?), (?), (?)"); + + Date date1 = new Date(1000000000000L); + Date date2 = new Date(1500000000000L); + Date date3 = new Date(2000000000000L); + + stmt.setDate(1, date1); + stmt.setDate(2, date2); + stmt.setDate(3, date3); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT * FROM test;"); + JDBC4ResultSet rs = (JDBC4ResultSet) stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals(date1, rs.getDate(1)); + assertTrue(rs.next()); + assertEquals(date2, rs.getDate(1)); + assertTrue(rs.next()); + assertEquals(date3, rs.getDate(1)); + } + + @Test + void testSetTime() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + PreparedStatement stmt = + connection.prepareStatement("INSERT INTO test (col) VALUES (?), (?), (?)"); + + Time time1 = new Time(1000000000000L); + Time time2 = new Time(1500000000000L); + Time time3 = new Time(2000000000000L); + + stmt.setTime(1, time1); + stmt.setTime(2, time2); + stmt.setTime(3, time3); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT * FROM test;"); + JDBC4ResultSet rs = (JDBC4ResultSet) stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals(time1, rs.getTime(1)); + assertTrue(rs.next()); + assertEquals(time2, rs.getTime(1)); + assertTrue(rs.next()); + assertEquals(time3, rs.getTime(1)); + } + + @Test + void testSetTimestamp() throws SQLException { + connection.prepareStatement("CREATE TABLE test (col BLOB)").execute(); + PreparedStatement stmt = + connection.prepareStatement("INSERT INTO test (col) VALUES (?), (?), (?)"); + + Timestamp timestamp1 = new Timestamp(1000000000000L); + Timestamp timestamp2 = new Timestamp(1500000000000L); + Timestamp timestamp3 = new Timestamp(2000000000000L); + + stmt.setTimestamp(1, timestamp1); + stmt.setTimestamp(2, timestamp2); + stmt.setTimestamp(3, timestamp3); + stmt.execute(); + + PreparedStatement stmt2 = connection.prepareStatement("SELECT * FROM test;"); + JDBC4ResultSet rs = (JDBC4ResultSet) stmt2.executeQuery(); + + assertTrue(rs.next()); + assertEquals(timestamp1, rs.getTimestamp(1)); + assertTrue(rs.next()); + assertEquals(timestamp2, rs.getTimestamp(1)); + assertTrue(rs.next()); + assertEquals(timestamp3, rs.getTimestamp(1)); + } + @Test void testInsertMultipleTypes() throws SQLException { connection