Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/main/java/org/duckdb/DuckDBConnection.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.duckdb;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.duckdb.DuckDBDriver.JDBC_AUTO_COMMIT;
import static org.duckdb.JdbcUtils.isStringTruish;
import static org.duckdb.JdbcUtils.removeOption;

import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.sql.Array;
import java.sql.Blob;
Expand Down Expand Up @@ -38,9 +40,9 @@ public final class DuckDBConnection implements java.sql.Connection {
ByteBuffer connRef;
final Lock connRefLock = new ReentrantLock();
final LinkedHashSet<DuckDBPreparedStatement> preparedStatements = new LinkedHashSet<>();
volatile boolean closing = false;
volatile boolean closing;

volatile boolean autoCommit = true;
volatile boolean autoCommit;
volatile boolean transactionRunning;
final String url;
private final boolean readOnly;
Expand All @@ -57,14 +59,19 @@ public static DuckDBConnection newConnection(String url, boolean readOnly, Prope
if (db_dir.startsWith("memory:")) {
db_dir = ":" + db_dir;
}
String autoCommitStr = removeOption(properties, JDBC_AUTO_COMMIT);
boolean autoCommit = isStringTruish(autoCommitStr, true);
ByteBuffer nativeReference = DuckDBNative.duckdb_jdbc_startup(db_dir.getBytes(UTF_8), readOnly, properties);
return new DuckDBConnection(nativeReference, url, readOnly);
return new DuckDBConnection(nativeReference, url, readOnly, autoCommit);
}

private DuckDBConnection(ByteBuffer connectionReference, String url, boolean readOnly) throws SQLException {
private DuckDBConnection(ByteBuffer connectionReference, String url, boolean readOnly, boolean autoCommit)
throws SQLException {
this.connRef = connectionReference;
this.url = url;
this.readOnly = readOnly;
this.autoCommit = autoCommit;
// Hardcoded 'true' here is intentional, autocommit is handled in stmt#execute()
DuckDBNative.duckdb_jdbc_set_auto_commit(connectionReference, true);
}

Expand Down Expand Up @@ -95,7 +102,7 @@ public Connection duplicate() throws SQLException {
connRefLock.lock();
try {
checkOpen();
return new DuckDBConnection(DuckDBNative.duckdb_jdbc_connect(connRef), url, readOnly);
return new DuckDBConnection(DuckDBNative.duckdb_jdbc_connect(connRef), url, readOnly, autoCommit);
} finally {
connRefLock.unlock();
}
Expand Down
22 changes: 7 additions & 15 deletions src/main/java/org/duckdb/DuckDBDriver.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.duckdb;

import static org.duckdb.JdbcUtils.isStringTruish;
import static org.duckdb.JdbcUtils.removeOption;

import java.sql.*;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantLock;
Expand All @@ -11,6 +14,7 @@ public class DuckDBDriver implements java.sql.Driver {
public static final String DUCKDB_READONLY_PROPERTY = "duckdb.read_only";
public static final String DUCKDB_USER_AGENT_PROPERTY = "custom_user_agent";
public static final String JDBC_STREAM_RESULTS = "jdbc_stream_results";
public static final String JDBC_AUTO_COMMIT = "jdbc_auto_commit";

private static final String DUCKDB_URL_PREFIX = "jdbc:duckdb:";

Expand All @@ -32,17 +36,13 @@ public Connection connect(String url, Properties info) throws SQLException {
if (!acceptsURL(url)) {
return null;
}
boolean read_only = false;
if (info == null) {
info = new Properties();
} else { // make a copy because we're removing the read only property below
info = (Properties) info.clone();
}
String prop_val = (String) info.remove(DUCKDB_READONLY_PROPERTY);
if (prop_val != null) {
String prop_clean = prop_val.trim().toLowerCase();
read_only = prop_clean.equals("1") || prop_clean.equals("true") || prop_clean.equals("yes");
}
String readOnlyStr = removeOption(info, DUCKDB_READONLY_PROPERTY);
boolean readOnly = isStringTruish(readOnlyStr, false);
info.put("duckdb_api", "jdbc");

// Apache Spark passes this option when SELECT on a JDBC DataSource
Expand All @@ -54,7 +54,7 @@ public Connection connect(String url, Properties info) throws SQLException {
String ducklake = removeOption(info, DUCKLAKE_OPTION);
String ducklakeAlias = removeOption(info, DUCKLAKE_ALIAS_OPTION);

Connection conn = DuckDBConnection.newConnection(url, read_only, info);
Connection conn = DuckDBConnection.newConnection(url, readOnly, info);

initDucklake(conn, url, ducklake, ducklakeAlias);

Expand Down Expand Up @@ -121,12 +121,4 @@ private static String createAttachQuery(String ducklake, String ducklakeAlias) t
}
return query;
}

private static String removeOption(Properties props, String opt) {
Object obj = props.remove(opt);
if (null != obj) {
return obj.toString();
}
return null;
}
}
25 changes: 24 additions & 1 deletion src/main/java/org/duckdb/JdbcUtils.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package org.duckdb;

import java.sql.SQLException;
import java.util.Properties;

final class JdbcUtils {

private JdbcUtils() {
}

@SuppressWarnings("unchecked")
static <T> T unwrap(Object obj, Class<T> iface) throws SQLException {
if (!iface.isInstance(obj)) {
Expand All @@ -12,6 +16,25 @@ static <T> T unwrap(Object obj, Class<T> iface) throws SQLException {
return (T) obj;
}

private JdbcUtils() {
static String removeOption(Properties props, String opt) {
Object obj = props.remove(opt);
if (null != obj) {
return obj.toString().trim();
}
return null;
}

static boolean isStringTruish(String val, boolean defaultVal) throws SQLException {
if (null == val) {
return defaultVal;
}
String valLower = val.toLowerCase().trim();
if (valLower.equals("true") || valLower.equals("1") || valLower.equals("yes") || valLower.equals("on")) {
return true;
}
if (valLower.equals("false") || valLower.equals("0") || valLower.equals("no") || valLower.equals("off")) {
return false;
}
throw new SQLException("Invalid boolean option value: " + val);
}
}
36 changes: 36 additions & 0 deletions src/test/java/org/duckdb/TestDuckDBJDBC.java
Original file line number Diff line number Diff line change
Expand Up @@ -3504,6 +3504,42 @@ public static void test_memory_colon() throws Exception {
}
}

public static void test_auto_commit_option() throws Exception {
Properties config = new Properties();

try (Connection conn = DriverManager.getConnection(JDBC_URL, config)) {
assertTrue(conn.getAutoCommit());
}

config.put(DuckDBDriver.JDBC_AUTO_COMMIT, true);
try (DuckDBConnection conn = DriverManager.getConnection(JDBC_URL, config).unwrap(DuckDBConnection.class)) {
assertTrue(conn.getAutoCommit());

try (Connection dup = conn.duplicate()) {
assertTrue(dup.getAutoCommit());
}
}

config.put(DuckDBDriver.JDBC_AUTO_COMMIT, false);
try (DuckDBConnection conn = DriverManager.getConnection(JDBC_URL, config).unwrap(DuckDBConnection.class)) {
assertFalse(conn.getAutoCommit());

try (Connection dup = conn.duplicate()) {
assertFalse(dup.getAutoCommit());
}
}

config.put(DuckDBDriver.JDBC_AUTO_COMMIT, "on");
try (Connection conn = DriverManager.getConnection(JDBC_URL, config)) {
assertTrue(conn.getAutoCommit());
}

config.put(DuckDBDriver.JDBC_AUTO_COMMIT, "off");
try (Connection conn = DriverManager.getConnection(JDBC_URL, config)) {
assertFalse(conn.getAutoCommit());
}
}

public static void main(String[] args) throws Exception {
String arg1 = args.length > 0 ? args[0] : "";
final int statusCode;
Expand Down