@@ -18,12 +18,15 @@ public class DuckDBDriver implements java.sql.Driver {
18
18
public static final String JDBC_PIN_DB = "jdbc_pin_db" ;
19
19
20
20
static final String DUCKDB_URL_PREFIX = "jdbc:duckdb:" ;
21
+ static final String MEMORY_DB = ":memory:" ;
21
22
22
23
private static final String DUCKLAKE_OPTION = "ducklake" ;
23
24
private static final String DUCKLAKE_ALIAS_OPTION = "ducklake_alias" ;
24
25
private static final Pattern DUCKLAKE_ALIAS_OPTION_PATTERN = Pattern .compile ("[a-zA-Z0-9_]+" );
25
26
private static final String DUCKLAKE_URL_PREFIX = "ducklake:" ;
26
- private static final ReentrantLock DUCKLAKE_INIT_LOCK = new ReentrantLock ();
27
+ private static final String DUCKLAKE_DEFAULT_DBNAME = MEMORY_DB + "ducklakemem" ;
28
+ private static final LinkedHashSet <String > ducklakeInstances = new LinkedHashSet <>();
29
+ private static final ReentrantLock ducklakeInitLock = new ReentrantLock ();
27
30
28
31
private static final LinkedHashMap <String , ByteBuffer > pinnedDbRefs = new LinkedHashMap <>();
29
32
private static final ReentrantLock pinnedDbRefsLock = new ReentrantLock ();
@@ -42,39 +45,52 @@ public Connection connect(String url, Properties info) throws SQLException {
42
45
if (!acceptsURL (url )) {
43
46
return null ;
44
47
}
48
+ final Properties props ;
45
49
if (info == null ) {
46
- info = new Properties ();
50
+ props = new Properties ();
47
51
} else { // make a copy because we're removing the read only property below
48
- info = (Properties ) info .clone ();
52
+ props = (Properties ) info .clone ();
49
53
}
50
54
51
55
ParsedProps pp = parsePropsFromUrl (url );
52
56
for (Map .Entry <String , String > en : pp .props .entrySet ()) {
53
- info .put (en .getKey (), en .getValue ());
57
+ props .put (en .getKey (), en .getValue ());
54
58
}
55
- url = pp .shortUrl ;
56
59
57
- String readOnlyStr = removeOption (info , DUCKDB_READONLY_PROPERTY );
60
+ String readOnlyStr = removeOption (props , DUCKDB_READONLY_PROPERTY );
58
61
boolean readOnly = isStringTruish (readOnlyStr , false );
59
- info .put ("duckdb_api" , "jdbc" );
62
+ props .put ("duckdb_api" , "jdbc" );
60
63
61
64
// Apache Spark passes this option when SELECT on a JDBC DataSource
62
65
// table is performed. It is the internal Spark option and is likely
63
66
// passed by mistake, so we need to ignore it to allow the connection
64
67
// to be established.
65
- info .remove ("path" );
68
+ props .remove ("path" );
66
69
67
- String pinDbOptStr = removeOption (info , JDBC_PIN_DB );
68
- boolean pinDBOpt = isStringTruish (pinDbOptStr , false );
70
+ String ducklake = removeOption (props , DUCKLAKE_OPTION );
71
+ String ducklakeAlias = removeOption (props , DUCKLAKE_ALIAS_OPTION , DUCKLAKE_OPTION );
72
+ final String shortUrl ;
73
+ if (null != ducklake ) {
74
+ setDefaultOptionValue (props , JDBC_PIN_DB , true );
75
+ setDefaultOptionValue (props , JDBC_STREAM_RESULTS , true );
76
+ String dbName = dbNameFromUrl (pp .shortUrl );
77
+ if (MEMORY_DB .equals (dbName )) {
78
+ shortUrl = DUCKDB_URL_PREFIX + DUCKLAKE_DEFAULT_DBNAME ;
79
+ } else {
80
+ shortUrl = pp .shortUrl ;
81
+ }
82
+ } else {
83
+ shortUrl = pp .shortUrl ;
84
+ }
69
85
70
- String ducklake = removeOption (info , DUCKLAKE_OPTION );
71
- String ducklakeAlias = removeOption ( info , DUCKLAKE_ALIAS_OPTION );
86
+ String pinDbOptStr = removeOption (props , JDBC_PIN_DB );
87
+ boolean pinDBOpt = isStringTruish ( pinDbOptStr , false );
72
88
73
- DuckDBConnection conn = DuckDBConnection .newConnection (url , readOnly , info );
89
+ DuckDBConnection conn = DuckDBConnection .newConnection (shortUrl , readOnly , props );
74
90
75
- pinDB (pinDBOpt , url , conn );
91
+ pinDB (pinDBOpt , shortUrl , conn );
76
92
77
- initDucklake (conn , ducklake , ducklakeAlias );
93
+ initDucklake (conn , shortUrl , ducklake , ducklakeAlias );
78
94
79
95
return conn ;
80
96
}
@@ -104,23 +120,29 @@ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
104
120
throw new SQLFeatureNotSupportedException ("no logger" );
105
121
}
106
122
107
- private static void initDucklake (Connection conn , String ducklake , String ducklakeAlias ) throws SQLException {
123
+ private static void initDucklake (Connection conn , String url , String ducklake , String ducklakeAlias )
124
+ throws SQLException {
108
125
if (null == ducklake ) {
109
126
return ;
110
127
}
111
- DUCKLAKE_INIT_LOCK .lock ();
128
+ ducklakeInitLock .lock ();
112
129
try {
113
- String attachQuery = createAttachQuery (ducklake , ducklakeAlias );
114
- try (Statement stmt = conn .createStatement ()) {
115
- stmt .execute ("INSTALL ducklake" );
116
- stmt .execute ("LOAD ducklake" );
117
- stmt .execute (attachQuery );
118
- if (null != ducklakeAlias ) {
119
- stmt .execute ("USE " + ducklakeAlias );
130
+ String dbName = dbNameFromUrl (url );
131
+ String key = dbName + "#" + ducklake ;
132
+ if (!ducklakeInstances .contains (key )) {
133
+ String attachQuery = createAttachQuery (ducklake , ducklakeAlias );
134
+ try (Statement stmt = conn .createStatement ()) {
135
+ stmt .execute ("INSTALL ducklake" );
136
+ stmt .execute ("LOAD ducklake" );
137
+ stmt .execute (attachQuery );
120
138
}
139
+ ducklakeInstances .add (key );
121
140
}
122
141
} finally {
123
- DUCKLAKE_INIT_LOCK .unlock ();
142
+ ducklakeInitLock .unlock ();
143
+ }
144
+ try (Statement stmt = conn .createStatement ()) {
145
+ stmt .execute ("USE " + ducklakeAlias );
124
146
}
125
147
}
126
148
@@ -129,14 +151,10 @@ private static String createAttachQuery(String ducklake, String ducklakeAlias) t
129
151
if (!ducklake .startsWith (DUCKLAKE_URL_PREFIX )) {
130
152
ducklake = DUCKLAKE_URL_PREFIX + ducklake ;
131
153
}
132
- String query = "ATTACH IF NOT EXISTS '" + ducklake + "'" ;
133
- if (null != ducklakeAlias ) {
134
- if (!DUCKLAKE_ALIAS_OPTION_PATTERN .matcher (ducklakeAlias ).matches ()) {
135
- throw new SQLException ("Invalid DuckLake alias specified: " + ducklakeAlias );
136
- }
137
- query += " AS " + ducklakeAlias ;
154
+ if (!DUCKLAKE_ALIAS_OPTION_PATTERN .matcher (ducklakeAlias ).matches ()) {
155
+ throw new SQLException ("Invalid DuckLake alias specified: " + ducklakeAlias );
138
156
}
139
- return query ;
157
+ return "ATTACH '" + ducklake + "' AS " + ducklakeAlias ;
140
158
}
141
159
142
160
private static ParsedProps parsePropsFromUrl (String url ) throws SQLException {
0 commit comments