Skip to content

Commit

Permalink
Merge pull request duckdb#9435 from Jens-H/main
Browse files Browse the repository at this point in the history
JDBC - Add append method for LocalDateTime
  • Loading branch information
Mytherin authored Nov 3, 2023
2 parents db3d653 + e4008f0 commit 2e6a379
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 1 deletion.
5 changes: 5 additions & 0 deletions tools/jdbc/src/jni/duckdb_java.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,11 @@ void _duckdb_jdbc_appender_append_double(JNIEnv *env, jclass, jobject appender_r
get_appender(env, appender_ref_buf)->Append((double)value);
}

void _duckdb_jdbc_appender_append_timestamp(JNIEnv *env, jclass, jobject appender_ref_buf, jlong value) {
timestamp_t timestamp = timestamp_t((int64_t)value);
get_appender(env, appender_ref_buf)->Append(Value::TIMESTAMP(timestamp));
}

void _duckdb_jdbc_appender_append_string(JNIEnv *env, jclass, jobject appender_ref_buf, jbyteArray value) {
if (env->IsSameObject(value, NULL)) {
get_appender(env, appender_ref_buf)->Append<std::nullptr_t>(nullptr);
Expand Down
11 changes: 11 additions & 0 deletions tools/jdbc/src/jni/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,17 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1appe
}
}

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1timestamp(JNIEnv *env,
jclass param0,
jobject param1,
jlong param2) {
try {
return _duckdb_jdbc_appender_append_timestamp(env, param0, param1, param2);
} catch (const std::exception &e) {
ThrowJNI(env, e.what());
}
}

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1null(JNIEnv *env, jclass param0,
jobject param1) {
try {
Expand Down
7 changes: 7 additions & 0 deletions tools/jdbc/src/jni/functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1appe
jobject param1,
jbyteArray param2);

void _duckdb_jdbc_appender_append_timestamp(JNIEnv *env, jclass param0, jobject param1, jlong param2);

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1timestamp(JNIEnv *env,
jclass param0,
jobject param1,
jlong param2);

void _duckdb_jdbc_appender_append_null(JNIEnv *env, jclass param0, jobject param1);

JNIEXPORT void JNICALL Java_org_duckdb_DuckDBNative_duckdb_1jdbc_1appender_1append_1null(JNIEnv *env, jclass param0,
Expand Down
12 changes: 12 additions & 0 deletions tools/jdbc/src/main/java/org/duckdb/DuckDBAppender.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.time.LocalDateTime;
import org.duckdb.DuckDBTimestamp;

public class DuckDBAppender implements AutoCloseable {

Expand Down Expand Up @@ -48,6 +50,16 @@ public void append(long value) throws SQLException {
DuckDBNative.duckdb_jdbc_appender_append_long(appender_ref, value);
}

// New naming schema for object params to keep compatibility with calling "append(null)"
public void appendLocalDateTime(LocalDateTime value) throws SQLException {
if (value == null) {
DuckDBNative.duckdb_jdbc_appender_append_null(appender_ref);
} else {
long timeInMicros = DuckDBTimestamp.localDateTime2Micros(value);
DuckDBNative.duckdb_jdbc_appender_append_timestamp(appender_ref, timeInMicros);
}
}

public void append(float value) throws SQLException {
DuckDBNative.duckdb_jdbc_appender_append_float(appender_ref, value);
}
Expand Down
3 changes: 3 additions & 0 deletions tools/jdbc/src/main/java/org/duckdb/DuckDBNative.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ protected static native void duckdb_jdbc_appender_append_double(ByteBuffer appen
protected static native void duckdb_jdbc_appender_append_string(ByteBuffer appender_ref, byte[] value)
throws SQLException;

protected static native void duckdb_jdbc_appender_append_timestamp(ByteBuffer appender_ref, long value)
throws SQLException;

protected static native void duckdb_jdbc_appender_append_null(ByteBuffer appender_ref) throws SQLException;

protected static native void duckdb_jdbc_create_extension_type(ByteBuffer conn_ref) throws SQLException;
Expand Down
6 changes: 5 additions & 1 deletion tools/jdbc/src/main/java/org/duckdb/DuckDBTimestamp.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public DuckDBTimestamp(long timeMicros) {
}

public DuckDBTimestamp(LocalDateTime localDateTime) {
this.timeMicros = DuckDBTimestamp.RefLocalDateTime.until(localDateTime, ChronoUnit.MICROS);
this.timeMicros = localDateTime2Micros(localDateTime);
}

public DuckDBTimestamp(OffsetDateTime offsetDateTime) {
Expand Down Expand Up @@ -94,6 +94,10 @@ public static Timestamp fromNanoInstant(long nanos) {
return Timestamp.from(Instant.ofEpochSecond(nanos / 1_000_000_000, nanosPartNanos(nanos)));
}

public static long localDateTime2Micros(LocalDateTime localDateTime) {
return DuckDBTimestamp.RefLocalDateTime.until(localDateTime, ChronoUnit.MICROS);
}

public Timestamp toSqlTimestamp() {
return Timestamp.valueOf(this.toLocalDateTime());
}
Expand Down
54 changes: 54 additions & 0 deletions tools/jdbc/src/test/java/org/duckdb/test/TestDuckDBJDBC.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
Expand Down Expand Up @@ -2264,6 +2265,59 @@ public static void test_appender_numbers() throws Exception {
conn.close();
}

public static void test_appender_date_and_time() throws Exception {
DuckDBConnection conn = DriverManager.getConnection("jdbc:duckdb:").unwrap(DuckDBConnection.class);
Statement stmt = conn.createStatement();

stmt.execute("CREATE TABLE date_and_time (id INT4, a TIMESTAMP)");
DuckDBAppender appender = conn.createAppender(DuckDBConnection.DEFAULT_SCHEMA, "date_and_time");

LocalDateTime ldt1 = LocalDateTime.now().truncatedTo(ChronoUnit.MICROS);
LocalDateTime ldt2 = LocalDateTime.of(-23434, 3, 5, 23, 2);
LocalDateTime ldt3 = LocalDateTime.of(1970, 1, 1, 0, 0);
LocalDateTime ldt4 = LocalDateTime.of(11111, 12, 31, 23, 59, 59, 999999000);

appender.beginRow();
appender.append(1);
appender.appendLocalDateTime(ldt1);
appender.endRow();
appender.beginRow();
appender.append(2);
appender.appendLocalDateTime(ldt2);
appender.endRow();
appender.beginRow();
appender.append(3);
appender.appendLocalDateTime(ldt3);
appender.endRow();
appender.beginRow();
appender.append(4);
appender.appendLocalDateTime(ldt4);
appender.endRow();
appender.close();

ResultSet rs = stmt.executeQuery("SELECT a FROM date_and_time ORDER BY id");
assertFalse(rs.isClosed());
assertTrue(rs.next());

LocalDateTime res1 = (LocalDateTime) rs.getObject(1, LocalDateTime.class);
assertEquals(res1, ldt1);
assertTrue(rs.next());

LocalDateTime res2 = (LocalDateTime) rs.getObject(1, LocalDateTime.class);
assertEquals(res2, ldt2);
assertTrue(rs.next());

LocalDateTime res3 = (LocalDateTime) rs.getObject(1, LocalDateTime.class);
assertEquals(res3, ldt3);
assertTrue(rs.next());

LocalDateTime res4 = (LocalDateTime) rs.getObject(1, LocalDateTime.class);
assertEquals(res4, ldt4);

rs.close();
stmt.close();
conn.close();
}
public static void test_appender_int_string() throws Exception {
DuckDBConnection conn = DriverManager.getConnection("jdbc:duckdb:").unwrap(DuckDBConnection.class);
Statement stmt = conn.createStatement();
Expand Down

0 comments on commit 2e6a379

Please sign in to comment.