diff --git a/src/main/java/org/duckdb/DuckDBDriver.java b/src/main/java/org/duckdb/DuckDBDriver.java index a027bc119..fc4bdff5b 100644 --- a/src/main/java/org/duckdb/DuckDBDriver.java +++ b/src/main/java/org/duckdb/DuckDBDriver.java @@ -5,6 +5,8 @@ import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Properties; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; @@ -44,6 +46,13 @@ public Connection connect(String url, Properties info) throws SQLException { String prop_clean = prop_val.trim().toLowerCase(); read_only = prop_clean.equals("1") || prop_clean.equals("true") || prop_clean.equals("yes"); } + + ParsedProps pp = parsePropsFromUrl(url); + for (Map.Entry en : pp.props.entrySet()) { + info.put(en.getKey(), en.getValue()); + } + url = pp.shortUrl; + info.put("duckdb_api", "jdbc"); // Apache Spark passes this option when SELECT on a JDBC DataSource @@ -56,7 +65,7 @@ public Connection connect(String url, Properties info) throws SQLException { } public boolean acceptsURL(String url) throws SQLException { - return url.startsWith("jdbc:duckdb:"); + return null != url && url.startsWith("jdbc:duckdb:"); } public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { @@ -79,4 +88,41 @@ public boolean jdbcCompliant() { public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException("no logger"); } + + private static ParsedProps parsePropsFromUrl(String url) throws SQLException { + if (!url.contains(";")) { + return new ParsedProps(url); + } + String[] parts = url.split(";"); + LinkedHashMap props = new LinkedHashMap<>(); + for (int i = 1; i < parts.length; i++) { + String entry = parts[i].trim(); + if (entry.isEmpty()) { + continue; + } + String[] kv = entry.split("="); + if (2 != kv.length) { + throw new SQLException("Invalid URL entry: " + entry); + } + String key = kv[0].trim(); + String value = kv[1].trim(); + props.put(key, value); + } + String shortUrl = parts[0].trim(); + return new ParsedProps(shortUrl, props); + } + + private static class ParsedProps { + final String shortUrl; + final LinkedHashMap props; + + private ParsedProps(String url) { + this(url, new LinkedHashMap<>()); + } + + private ParsedProps(String shortUrl, LinkedHashMap props) { + this.shortUrl = shortUrl; + this.props = props; + } + } } diff --git a/src/test/java/org/duckdb/TestBatch.java b/src/test/java/org/duckdb/TestBatch.java index 51e1d781e..17d55d1ac 100644 --- a/src/test/java/org/duckdb/TestBatch.java +++ b/src/test/java/org/duckdb/TestBatch.java @@ -249,9 +249,9 @@ public static void test_prepared_statement_batch_autocommit_constraint_violation } public static void test_statement_batch_constraint_violation() throws Exception { - Properties config = new Properties(); - config.put(DuckDBDriver.JDBC_AUTO_COMMIT, false); - try (Connection conn = DriverManager.getConnection(JDBC_URL, config)) { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + conn.setAutoCommit(false); + assertFalse(conn.getAutoCommit()); assertFalse(conn.getAutoCommit()); try (Statement stmt = conn.createStatement()) { stmt.execute("CREATE TABLE tab1 (col1 VARCHAR NOT NULL)"); @@ -277,9 +277,8 @@ public static void test_statement_batch_constraint_violation() throws Exception } public static void test_prepared_statement_batch_constraint_violation() throws Exception { - Properties config = new Properties(); - config.put(DuckDBDriver.JDBC_AUTO_COMMIT, false); - try (Connection conn = DriverManager.getConnection(JDBC_URL, config)) { + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + conn.setAutoCommit(false); assertFalse(conn.getAutoCommit()); try (Statement stmt = conn.createStatement()) { stmt.execute("CREATE TABLE tab1 (col1 VARCHAR NOT NULL)"); diff --git a/src/test/java/org/duckdb/TestDuckDBJDBC.java b/src/test/java/org/duckdb/TestDuckDBJDBC.java index 08908cb79..6a4e630bd 100644 --- a/src/test/java/org/duckdb/TestDuckDBJDBC.java +++ b/src/test/java/org/duckdb/TestDuckDBJDBC.java @@ -3504,6 +3504,58 @@ public static void test_memory_colon() throws Exception { } } + public static void test_props_from_url() throws Exception { + Properties config = new Properties(); + config.put("allow_community_extensions", false); + config.put("allow_unsigned_extensions", true); + + try (Connection conn = DriverManager.getConnection("jdbc:duckdb:", config); + Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_community_extensions')")) { + rs.next(); + assertEquals(rs.getString(1), "false"); + } + try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_unsigned_extensions')")) { + rs.next(); + assertEquals(rs.getString(1), "true"); + } + } + + try (Connection conn = DriverManager.getConnection( + "jdbc:duckdb:;allow_community_extensions=true;;allow_unsigned_extensions = false;", config); + Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_community_extensions')")) { + rs.next(); + assertEquals(rs.getString(1), "true"); + } + try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_unsigned_extensions')")) { + rs.next(); + assertEquals(rs.getString(1), "false"); + } + try (ResultSet rs = stmt.executeQuery("SELECT current_catalog()")) { + rs.next(); + assertEquals(rs.getString(1), "memory"); + } + } + + try (Connection conn = + DriverManager.getConnection("jdbc:duckdb:test1.db;allow_community_extensions=true;", config); + Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_community_extensions')")) { + rs.next(); + assertEquals(rs.getString(1), "true"); + } + try (ResultSet rs = stmt.executeQuery("SELECT current_catalog()")) { + rs.next(); + assertEquals(rs.getString(1), "test1"); + } + } + + assertThrows( + () -> { DriverManager.getConnection("jdbc:duckdb:;allow_unsigned_extensions"); }, SQLException.class); + assertThrows(() -> { DriverManager.getConnection("jdbc:duckdb:;foo=bar"); }, SQLException.class); + } + public static void main(String[] args) throws Exception { String arg1 = args.length > 0 ? args[0] : ""; final int statusCode;