Skip to content

Commit d7c1d91

Browse files
committed
Support connection properties in URL
This change adds support for specifying connection properties as part of the connection string itself. It is intended to be used in cases, when `Properties` map is passed by some tool that does not allow direct access to it, and some of these `Properties` values need to be overridden. Properties in URL are accepted in `key=value` format, `;` is used as a separator, empty properties are ignored. URL example: `jdbc:duckdb:;allow_community_extensions=true;;allow_unsigned_extensions = false;`. Testing: new test added that checks that properties in URL take preference.
1 parent 2897886 commit d7c1d91

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

src/main/java/org/duckdb/DuckDBDriver.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import static org.duckdb.JdbcUtils.removeOption;
55

66
import java.sql.*;
7+
import java.util.LinkedHashMap;
8+
import java.util.Map;
79
import java.util.Properties;
810
import java.util.concurrent.locks.ReentrantLock;
911
import java.util.logging.Logger;
@@ -41,6 +43,13 @@ public Connection connect(String url, Properties info) throws SQLException {
4143
} else { // make a copy because we're removing the read only property below
4244
info = (Properties) info.clone();
4345
}
46+
47+
ParsedProps pp = parsePropsFromUrl(url);
48+
for (Map.Entry<String, String> en : pp.props.entrySet()) {
49+
info.put(en.getKey(), en.getValue());
50+
}
51+
url = pp.shortUrl;
52+
4453
String readOnlyStr = removeOption(info, DUCKDB_READONLY_PROPERTY);
4554
boolean readOnly = isStringTruish(readOnlyStr, false);
4655
info.put("duckdb_api", "jdbc");
@@ -62,7 +71,7 @@ public Connection connect(String url, Properties info) throws SQLException {
6271
}
6372

6473
public boolean acceptsURL(String url) throws SQLException {
65-
return url.startsWith(DUCKDB_URL_PREFIX);
74+
return null != url && url.startsWith(DUCKDB_URL_PREFIX);
6675
}
6776

6877
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
@@ -121,4 +130,41 @@ private static String createAttachQuery(String ducklake, String ducklakeAlias) t
121130
}
122131
return query;
123132
}
133+
134+
private static ParsedProps parsePropsFromUrl(String url) throws SQLException {
135+
if (!url.contains(";")) {
136+
return new ParsedProps(url);
137+
}
138+
String[] parts = url.split(";");
139+
LinkedHashMap<String, String> props = new LinkedHashMap<>();
140+
for (int i = 1; i < parts.length; i++) {
141+
String entry = parts[i].trim();
142+
if (entry.isEmpty()) {
143+
continue;
144+
}
145+
String[] kv = entry.split("=");
146+
if (2 != kv.length) {
147+
throw new SQLException("Invalid URL entry: " + entry);
148+
}
149+
String key = kv[0].trim();
150+
String value = kv[1].trim();
151+
props.put(key, value);
152+
}
153+
String shortUrl = parts[0].trim();
154+
return new ParsedProps(shortUrl, props);
155+
}
156+
157+
private static class ParsedProps {
158+
final String shortUrl;
159+
final LinkedHashMap<String, String> props;
160+
161+
private ParsedProps(String url) {
162+
this(url, new LinkedHashMap<>());
163+
}
164+
165+
private ParsedProps(String shortUrl, LinkedHashMap<String, String> props) {
166+
this.shortUrl = shortUrl;
167+
this.props = props;
168+
}
169+
}
124170
}

src/test/java/org/duckdb/TestDuckDBJDBC.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3540,6 +3540,58 @@ public static void test_auto_commit_option() throws Exception {
35403540
}
35413541
}
35423542

3543+
public static void test_props_from_url() throws Exception {
3544+
Properties config = new Properties();
3545+
config.put("allow_community_extensions", false);
3546+
config.put("allow_unsigned_extensions", true);
3547+
3548+
try (Connection conn = DriverManager.getConnection("jdbc:duckdb:", config);
3549+
Statement stmt = conn.createStatement()) {
3550+
try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_community_extensions')")) {
3551+
rs.next();
3552+
assertEquals(rs.getString(1), "false");
3553+
}
3554+
try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_unsigned_extensions')")) {
3555+
rs.next();
3556+
assertEquals(rs.getString(1), "true");
3557+
}
3558+
}
3559+
3560+
try (Connection conn = DriverManager.getConnection(
3561+
"jdbc:duckdb:;allow_community_extensions=true;;allow_unsigned_extensions = false;", config);
3562+
Statement stmt = conn.createStatement()) {
3563+
try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_community_extensions')")) {
3564+
rs.next();
3565+
assertEquals(rs.getString(1), "true");
3566+
}
3567+
try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_unsigned_extensions')")) {
3568+
rs.next();
3569+
assertEquals(rs.getString(1), "false");
3570+
}
3571+
try (ResultSet rs = stmt.executeQuery("SELECT current_catalog()")) {
3572+
rs.next();
3573+
assertEquals(rs.getString(1), "memory");
3574+
}
3575+
}
3576+
3577+
try (Connection conn =
3578+
DriverManager.getConnection("jdbc:duckdb:test1.db;allow_community_extensions=true;", config);
3579+
Statement stmt = conn.createStatement()) {
3580+
try (ResultSet rs = stmt.executeQuery("SELECT current_setting('allow_community_extensions')")) {
3581+
rs.next();
3582+
assertEquals(rs.getString(1), "true");
3583+
}
3584+
try (ResultSet rs = stmt.executeQuery("SELECT current_catalog()")) {
3585+
rs.next();
3586+
assertEquals(rs.getString(1), "test1");
3587+
}
3588+
}
3589+
3590+
assertThrows(
3591+
() -> { DriverManager.getConnection("jdbc:duckdb:;allow_unsigned_extensions"); }, SQLException.class);
3592+
assertThrows(() -> { DriverManager.getConnection("jdbc:duckdb:;foo=bar"); }, SQLException.class);
3593+
}
3594+
35433595
public static void main(String[] args) throws Exception {
35443596
String arg1 = args.length > 0 ? args[0] : "";
35453597
final int statusCode;

0 commit comments

Comments
 (0)