mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-03 17:48:17 +00:00
Implement basic getXX methods for JDBC4ResultSet
This commit is contained in:
parent
ac188808b6
commit
2e62abe6df
5 changed files with 375 additions and 27 deletions
|
@ -117,6 +117,25 @@ public class LimboResultSet {
|
|||
this.open = false;
|
||||
}
|
||||
|
||||
// Note that columnIndex starts from 1
|
||||
@Nullable
|
||||
public Object get(int columnIndex) throws SQLException {
|
||||
if (!this.isOpen()) {
|
||||
throw new SQLException("ResultSet is not open");
|
||||
}
|
||||
|
||||
if (this.lastStepResult == null || this.lastStepResult.getResult() == null) {
|
||||
throw new SQLException("ResultSet is null");
|
||||
}
|
||||
|
||||
final Object[] resultSet = this.lastStepResult.getResult();
|
||||
if (columnIndex > resultSet.length) {
|
||||
throw new SQLException("columnIndex out of bound");
|
||||
}
|
||||
|
||||
return resultSet[columnIndex - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboResultSet{"
|
||||
|
|
|
@ -55,6 +55,11 @@ public class LimboStatement {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because Limbo supports async I/O, it is possible to return a {@link LimboStepResult} with
|
||||
* {@link LimboStepResult#STEP_RESULT_ID_ROW}. However, this is handled by the native side, so you
|
||||
* can expect that this method will not return a {@link LimboStepResult#STEP_RESULT_ID_ROW}.
|
||||
*/
|
||||
@Nullable
|
||||
private native LimboStepResult step(long stmtPointer) throws SQLException;
|
||||
|
||||
|
|
|
@ -47,6 +47,11 @@ public class LimboStepResult {
|
|||
|| stepResultId == STEP_RESULT_ID_ERROR;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object[] getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboStepResult{"
|
||||
|
|
|
@ -3,10 +3,26 @@ package org.github.tursodatabase.jdbc4;
|
|||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.net.URL;
|
||||
import java.sql.*;
|
||||
import java.sql.Array;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Date;
|
||||
import java.sql.NClob;
|
||||
import java.sql.Ref;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.RowId;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.SQLXML;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.Map;
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.github.tursodatabase.annotations.SkipNullableCheck;
|
||||
import org.github.tursodatabase.core.LimboResultSet;
|
||||
|
||||
|
@ -35,64 +51,99 @@ public class JDBC4ResultSet implements ResultSet {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getString(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return wrapTypeConversion(() -> (String) result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
return wrapTypeConversion(() -> (Long) result != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
}
|
||||
return wrapTypeConversion(() -> ((Long) result).byteValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
}
|
||||
return wrapTypeConversion(() -> ((Long) result).shortValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
}
|
||||
return wrapTypeConversion(() -> ((Long) result).intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
}
|
||||
return wrapTypeConversion(() -> (long) result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
}
|
||||
return wrapTypeConversion(() -> ((Double) result).floatValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
}
|
||||
return wrapTypeConversion(() -> (double) result);
|
||||
}
|
||||
|
||||
// TODO: customize rounding mode?
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
@Nullable
|
||||
public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
final double doubleResult = wrapTypeConversion(() -> (double) result);
|
||||
final BigDecimal bigDecimalResult = BigDecimal.valueOf(doubleResult);
|
||||
return bigDecimalResult.setScale(scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public byte[] getBytes(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return new byte[0];
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return wrapTypeConversion(() -> (byte[]) result);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -300,10 +351,14 @@ public class JDBC4ResultSet implements ResultSet {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
@Nullable
|
||||
public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
final Object result = resultSet.get(columnIndex);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
final double doubleResult = wrapTypeConversion(() -> (double) result);
|
||||
return BigDecimal.valueOf(doubleResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1126,7 +1181,16 @@ public class JDBC4ResultSet implements ResultSet {
|
|||
return false;
|
||||
}
|
||||
|
||||
private SQLException throwNotSupportedException() {
|
||||
return new SQLFeatureNotSupportedException("Not implemented by the driver");
|
||||
@FunctionalInterface
|
||||
public interface ResultSetSupplier<T> {
|
||||
T get() throws Exception;
|
||||
}
|
||||
|
||||
private <T> T wrapTypeConversion(ResultSetSupplier<T> supplier) throws SQLException {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Exception e) {
|
||||
throw new SQLException("Type conversion failed: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
package org.github.tursodatabase.jdbc4;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
class JDBC4ResultSetTest {
|
||||
|
||||
|
@ -79,4 +87,251 @@ class JDBC4ResultSetTest {
|
|||
|
||||
assertThrows(SQLException.class, resultSet::next);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getString() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_string (string_col TEXT);");
|
||||
stmt.executeUpdate("INSERT INTO test_string (string_col) VALUES ('test');");
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_string");
|
||||
assertEquals("test", resultSet.getString(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getBoolean_true() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_boolean (boolean_col INTEGER);");
|
||||
stmt.executeUpdate("INSERT INTO test_boolean (boolean_col) VALUES (1);");
|
||||
stmt.executeUpdate("INSERT INTO test_boolean (boolean_col) VALUES (2);");
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_boolean");
|
||||
|
||||
assertTrue(resultSet.getBoolean(1));
|
||||
|
||||
resultSet.next();
|
||||
assertTrue(resultSet.getBoolean(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getBoolean_false() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_boolean (boolean_col INTEGER);");
|
||||
stmt.executeUpdate("INSERT INTO test_boolean (boolean_col) VALUES (0);");
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_boolean");
|
||||
assertFalse(resultSet.getBoolean(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getByte() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_byte (byte_col INTEGER);");
|
||||
stmt.executeUpdate("INSERT INTO test_byte (byte_col) VALUES (1);");
|
||||
stmt.executeUpdate("INSERT INTO test_byte (byte_col) VALUES (128);"); // Exceeds byte size
|
||||
stmt.executeUpdate("INSERT INTO test_byte (byte_col) VALUES (-129);"); // Exceeds byte size
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_byte");
|
||||
|
||||
// Test value that fits within byte size
|
||||
assertEquals(1, resultSet.getByte(1));
|
||||
|
||||
// Test value that exceeds byte size (positive overflow)
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(-128, resultSet.getByte(1)); // 128 overflows to -128
|
||||
|
||||
// Test value that exceeds byte size (negative overflow)
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(127, resultSet.getByte(1)); // -129 overflows to 127
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getShort() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_short (short_col SMALLINT);");
|
||||
stmt.executeUpdate("INSERT INTO test_short (short_col) VALUES (123);");
|
||||
stmt.executeUpdate("INSERT INTO test_short (short_col) VALUES (32767);"); // Max short value
|
||||
stmt.executeUpdate("INSERT INTO test_short (short_col) VALUES (-32768);"); // Min short value
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_short");
|
||||
|
||||
// Test typical short value
|
||||
assertEquals(123, resultSet.getShort(1));
|
||||
|
||||
// Test maximum short value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(32767, resultSet.getShort(1));
|
||||
|
||||
// Test minimum short value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(-32768, resultSet.getShort(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getInt() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_int (int_col INT);");
|
||||
stmt.executeUpdate("INSERT INTO test_int (int_col) VALUES (12345);");
|
||||
stmt.executeUpdate("INSERT INTO test_int (int_col) VALUES (2147483647);"); // Max int value
|
||||
stmt.executeUpdate("INSERT INTO test_int (int_col) VALUES (-2147483648);"); // Min int value
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_int");
|
||||
|
||||
// Test typical int value
|
||||
assertEquals(12345, resultSet.getInt(1));
|
||||
|
||||
// Test maximum int value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(2147483647, resultSet.getInt(1));
|
||||
|
||||
// Test minimum int value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(-2147483648, resultSet.getInt(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("limbo has a bug which sees -9223372036854775808 as double")
|
||||
void test_getLong() throws Exception {
|
||||
Long l1 = Long.MIN_VALUE;
|
||||
Long l2 = Long.MAX_VALUE;
|
||||
stmt.executeUpdate("CREATE TABLE test_long (long_col BIGINT);");
|
||||
stmt.executeUpdate("INSERT INTO test_long (long_col) VALUES (1234567890);");
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_long (long_col) VALUES (9223372036854775807);"); // Max long value
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_long (long_col) VALUES (-9223372036854775808);"); // Min long value
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_long");
|
||||
|
||||
// Test typical long value
|
||||
assertEquals(1234567890L, resultSet.getLong(1));
|
||||
|
||||
// Test maximum long value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(9223372036854775807L, resultSet.getLong(1));
|
||||
|
||||
// Test minimum long value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(-9223372036854775808L, resultSet.getLong(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getFloat() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_float (float_col REAL);");
|
||||
stmt.executeUpdate("INSERT INTO test_float (float_col) VALUES (1.23);");
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_float (float_col) VALUES (3.4028235E38);"); // Max float value
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_float (float_col) VALUES (1.4E-45);"); // Min positive float value
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_float (float_col) VALUES (-3.4028235E38);"); // Min negative float value
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_float");
|
||||
|
||||
// Test typical float value
|
||||
assertEquals(1.23f, resultSet.getFloat(1), 0.0001);
|
||||
|
||||
// Test maximum float value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(3.4028235E38f, resultSet.getFloat(1), 0.0001);
|
||||
|
||||
// Test minimum positive float value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(1.4E-45f, resultSet.getFloat(1), 0.0001);
|
||||
|
||||
// Test minimum negative float value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(-3.4028235E38f, resultSet.getFloat(1), 0.0001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getDouble() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_double (double_col DOUBLE);");
|
||||
stmt.executeUpdate("INSERT INTO test_double (double_col) VALUES (1.234567);");
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_double (double_col) VALUES (1.7976931348623157E308);"); // Max double
|
||||
// value
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_double (double_col) VALUES (4.9E-324);"); // Min positive double value
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_double (double_col) VALUES (-1.7976931348623157E308);"); // Min negative
|
||||
// double value
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_double");
|
||||
|
||||
// Test typical double value
|
||||
assertEquals(1.234567, resultSet.getDouble(1), 0.0001);
|
||||
|
||||
// Test maximum double value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(1.7976931348623157E308, resultSet.getDouble(1), 0.0001);
|
||||
|
||||
// Test minimum positive double value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(4.9E-324, resultSet.getDouble(1), 0.0001);
|
||||
|
||||
// Test minimum negative double value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(-1.7976931348623157E308, resultSet.getDouble(1), 0.0001);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getBigDecimal() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_bigdecimal (bigdecimal_col REAL);");
|
||||
stmt.executeUpdate("INSERT INTO test_bigdecimal (bigdecimal_col) VALUES (12345.67);");
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_bigdecimal (bigdecimal_col) VALUES (1.7976931348623157E308);"); // Max
|
||||
// double
|
||||
// value
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_bigdecimal (bigdecimal_col) VALUES (4.9E-324);"); // Min positive double
|
||||
// value
|
||||
stmt.executeUpdate(
|
||||
"INSERT INTO test_bigdecimal (bigdecimal_col) VALUES (-12345.67);"); // Negative value
|
||||
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_bigdecimal");
|
||||
|
||||
// Test typical BigDecimal value
|
||||
assertEquals(
|
||||
new BigDecimal("12345.67").setScale(2, RoundingMode.HALF_UP),
|
||||
resultSet.getBigDecimal(1, 2));
|
||||
|
||||
// Test maximum double value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(
|
||||
new BigDecimal("1.7976931348623157E308").setScale(10, RoundingMode.HALF_UP),
|
||||
resultSet.getBigDecimal(1, 10));
|
||||
|
||||
// Test minimum positive double value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(
|
||||
new BigDecimal("4.9E-324").setScale(10, RoundingMode.HALF_UP),
|
||||
resultSet.getBigDecimal(1, 10));
|
||||
|
||||
// Test negative BigDecimal value
|
||||
assertTrue(resultSet.next());
|
||||
assertEquals(
|
||||
new BigDecimal("-12345.67").setScale(2, RoundingMode.HALF_UP),
|
||||
resultSet.getBigDecimal(1, 2));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("byteArrayProvider")
|
||||
void test_getBytes(byte[] data) throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE test_bytes (bytes_col BLOB);");
|
||||
executeDMLAndAssert(data);
|
||||
}
|
||||
|
||||
private static Stream<byte[]> byteArrayProvider() {
|
||||
return Stream.of(
|
||||
"Hello".getBytes(), "world".getBytes(), new byte[0], new byte[] {0x00, (byte) 0xFF});
|
||||
}
|
||||
|
||||
private void executeDMLAndAssert(byte[] data) throws SQLException {
|
||||
// Convert byte array to hexadecimal string
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : data) {
|
||||
hexString.append(String.format("%02X", b));
|
||||
}
|
||||
// Execute DML statement
|
||||
stmt.executeUpdate("INSERT INTO test_bytes (bytes_col) VALUES (X'" + hexString + "');");
|
||||
|
||||
// Assert the inserted data
|
||||
ResultSet resultSet = stmt.executeQuery("SELECT bytes_col FROM test_bytes");
|
||||
assertArrayEquals(data, resultSet.getBytes(1));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue