diff --git a/src/main/java/org/duckdb/DuckDBDriver.java b/src/main/java/org/duckdb/DuckDBDriver.java index d6c71cdb2..c43e9b4b4 100644 --- a/src/main/java/org/duckdb/DuckDBDriver.java +++ b/src/main/java/org/duckdb/DuckDBDriver.java @@ -4,6 +4,8 @@ import static org.duckdb.JdbcUtils.removeOption; import java.sql.*; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Properties; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; @@ -41,6 +43,13 @@ public Connection connect(String url, Properties info) throws SQLException { } else { // make a copy because we're removing the read only property below info = (Properties) info.clone(); } + + ParsedProps pp = parsePropsFromUrl(url); + for (Map.Entry en : pp.props.entrySet()) { + info.put(en.getKey(), en.getValue()); + } + url = pp.shortUrl; + String readOnlyStr = removeOption(info, DUCKDB_READONLY_PROPERTY); boolean readOnly = isStringTruish(readOnlyStr, false); info.put("duckdb_api", "jdbc"); @@ -62,7 +71,7 @@ public Connection connect(String url, Properties info) throws SQLException { } public boolean acceptsURL(String url) throws SQLException { - return url.startsWith(DUCKDB_URL_PREFIX); + return null != url && url.startsWith(DUCKDB_URL_PREFIX); } public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { @@ -121,4 +130,41 @@ private static String createAttachQuery(String ducklake, String ducklakeAlias) t } return query; } + + 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/TestDuckDBJDBC.java b/src/test/java/org/duckdb/TestDuckDBJDBC.java index 3795e16b5..bf8ad8e87 100644 --- a/src/test/java/org/duckdb/TestDuckDBJDBC.java +++ b/src/test/java/org/duckdb/TestDuckDBJDBC.java @@ -3540,6 +3540,58 @@ public static void test_auto_commit_option() 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;