Skip to content

Commit 2cbc516

Browse files
committed
Allow to ignore unsupported connection opts (1.3)
This is a backport of the PR #250 to `v1.3-ossivalis` stable branch. Currently driver performs strict checks on the user-specified connection options. If stray options are passed - exception is thrown on connect. This is considered to be a good feature, to prevent accidental typos in option names. But in some cases user does not have full control over the options passed to driver by other tools. This change adds new boolean connection property `jdbc_ignore_unsupported_options`, when it is enabled - all unsupported connection options are ignored. When used with external tools, it is intended to be set in connection string like this: ``` jdbc:duckdb:;jdbc_ignore_unsupported_options=true; ``` Testing: new test added. Fixes: #232
1 parent c7dba1a commit 2cbc516

File tree

2 files changed

+75
-15
lines changed

2 files changed

+75
-15
lines changed

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

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class DuckDBDriver implements java.sql.Driver {
1616
public static final String DUCKDB_USER_AGENT_PROPERTY = "custom_user_agent";
1717
public static final String JDBC_STREAM_RESULTS = "jdbc_stream_results";
1818
public static final String JDBC_PIN_DB = "jdbc_pin_db";
19+
public static final String JDBC_IGNORE_UNSUPPORTED_OPTIONS = "jdbc_ignore_unsupported_options";
1920

2021
static final String DUCKDB_URL_PREFIX = "jdbc:duckdb:";
2122
static final String MEMORY_DB = ":memory:";
@@ -27,6 +28,9 @@ public class DuckDBDriver implements java.sql.Driver {
2728
private static boolean pinnedDbRefsShutdownHookRegistered = false;
2829
private static boolean pinnedDbRefsShutdownHookRun = false;
2930

31+
private static final Set<String> supportedOptions = new LinkedHashSet<>();
32+
private static final ReentrantLock supportedOptionsLock = new ReentrantLock();
33+
3034
static {
3135
try {
3236
DriverManager.registerDriver(new DuckDBDriver());
@@ -42,38 +46,51 @@ public Connection connect(String url, Properties info) throws SQLException {
4246
if (!acceptsURL(url)) {
4347
return null;
4448
}
45-
boolean read_only = false;
49+
final Properties props;
4650
if (info == null) {
47-
info = new Properties();
51+
props = new Properties();
4852
} else { // make a copy because we're removing the read only property below
49-
info = (Properties) info.clone();
50-
}
51-
String prop_val = (String) info.remove(DUCKDB_READONLY_PROPERTY);
52-
if (prop_val != null) {
53-
String prop_clean = prop_val.trim().toLowerCase();
54-
read_only = prop_clean.equals("1") || prop_clean.equals("true") || prop_clean.equals("yes");
53+
props = (Properties) info.clone();
5554
}
5655

56+
// URL options
5757
ParsedProps pp = parsePropsFromUrl(url);
58+
59+
// Options in URL take preference
5860
for (Map.Entry<String, String> en : pp.props.entrySet()) {
59-
info.put(en.getKey(), en.getValue());
61+
props.put(en.getKey(), en.getValue());
6062
}
61-
url = pp.shortUrl;
6263

63-
info.put("duckdb_api", "jdbc");
64+
// Ignore unsupported
65+
removeUnsupportedOptions(props);
66+
67+
// Read-only option
68+
String readOnlyStr = removeOption(props, DUCKDB_READONLY_PROPERTY);
69+
boolean readOnly = isStringTruish(readOnlyStr, false);
70+
71+
// Client name option
72+
props.put("duckdb_api", "jdbc");
6473

6574
// Apache Spark passes this option when SELECT on a JDBC DataSource
6675
// table is performed. It is the internal Spark option and is likely
6776
// passed by mistake, so we need to ignore it to allow the connection
6877
// to be established.
69-
info.remove("path");
78+
props.remove("path");
7079

71-
String pinDbOptStr = removeOption(info, JDBC_PIN_DB);
80+
// Pin DB option
81+
String pinDbOptStr = removeOption(props, JDBC_PIN_DB);
7282
boolean pinDBOpt = isStringTruish(pinDbOptStr, false);
7383

74-
DuckDBConnection conn = DuckDBConnection.newConnection(url, read_only, info);
84+
// Create connection
85+
DuckDBConnection conn = DuckDBConnection.newConnection(pp.shortUrl, readOnly, props);
7586

76-
pinDB(pinDBOpt, url, conn);
87+
// Run post-init
88+
try {
89+
pinDB(pinDBOpt, pp.shortUrl, conn);
90+
} catch (SQLException e) {
91+
closeQuietly(conn);
92+
throw e;
93+
}
7794

7895
return conn;
7996
}
@@ -98,6 +115,8 @@ public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws
98115
list.add(createDriverPropInfo(JDBC_STREAM_RESULTS, "", "Enable result set streaming"));
99116
list.add(createDriverPropInfo(JDBC_PIN_DB, "",
100117
"Do not close the DB instance after all connections to it are closed"));
118+
list.add(createDriverPropInfo(JDBC_IGNORE_UNSUPPORTED_OPTIONS, "",
119+
"Silently discard unsupported connection options"));
101120
list.sort((o1, o2) -> o1.name.compareToIgnoreCase(o2.name));
102121
return list.toArray(new DriverPropertyInfo[0]);
103122
}
@@ -196,6 +215,38 @@ private static DriverPropertyInfo createDriverPropInfo(String name, String value
196215
return dpi;
197216
}
198217

218+
private static void removeUnsupportedOptions(Properties props) throws SQLException {
219+
String ignoreStr = removeOption(props, JDBC_IGNORE_UNSUPPORTED_OPTIONS);
220+
boolean ignore = isStringTruish(ignoreStr, false);
221+
if (!ignore) {
222+
return;
223+
}
224+
supportedOptionsLock.lock();
225+
try {
226+
if (supportedOptions.isEmpty()) {
227+
Driver driver = DriverManager.getDriver(DUCKDB_URL_PREFIX);
228+
Properties dpiProps = new Properties();
229+
dpiProps.put("threads", 1);
230+
DriverPropertyInfo[] dpis = driver.getPropertyInfo(DUCKDB_URL_PREFIX, dpiProps);
231+
for (DriverPropertyInfo dpi : dpis) {
232+
supportedOptions.add(dpi.name);
233+
}
234+
}
235+
List<String> unsupportedNames = new ArrayList<>();
236+
for (Object nameObj : props.keySet()) {
237+
String name = String.valueOf(nameObj);
238+
if (!supportedOptions.contains(name)) {
239+
unsupportedNames.add(name);
240+
}
241+
}
242+
for (String name : unsupportedNames) {
243+
props.remove(name);
244+
}
245+
} finally {
246+
supportedOptionsLock.unlock();
247+
}
248+
}
249+
199250
private static class ParsedProps {
200251
final String shortUrl;
201252
final LinkedHashMap<String, String> props;

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static java.util.Collections.emptyList;
1111
import static java.util.Collections.singletonList;
1212
import static org.duckdb.DuckDBDriver.DUCKDB_USER_AGENT_PROPERTY;
13+
import static org.duckdb.DuckDBDriver.JDBC_STREAM_RESULTS;
1314
import static org.duckdb.DuckDBTimestamp.localDateTimeFromTimestamp;
1415
import static org.duckdb.test.Assertions.*;
1516
import static org.duckdb.test.Runner.runTests;
@@ -3620,6 +3621,14 @@ public static void test_driver_property_info() throws Exception {
36203621
assertTrue(dpis.length > 0);
36213622
}
36223623

3624+
public static void test_ignore_unsupported_options() throws Exception {
3625+
assertThrows(() -> { DriverManager.getConnection("jdbc:duckdb:;foo=bar;"); }, SQLException.class);
3626+
Properties config = new Properties();
3627+
config.put("boo", "bar");
3628+
config.put(JDBC_STREAM_RESULTS, true);
3629+
DriverManager.getConnection("jdbc:duckdb:;foo=bar;jdbc_ignore_unsupported_options=yes;", config).close();
3630+
}
3631+
36233632
public static void main(String[] args) throws Exception {
36243633
String arg1 = args.length > 0 ? args[0] : "";
36253634
final int statusCode;

0 commit comments

Comments
 (0)