forked from duckdb/duckdb-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDuckDBDriver.java
More file actions
132 lines (111 loc) · 4.56 KB
/
DuckDBDriver.java
File metadata and controls
132 lines (111 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package org.duckdb;
import java.sql.*;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import java.util.regex.Pattern;
public class DuckDBDriver implements java.sql.Driver {
public static final String DUCKDB_READONLY_PROPERTY = "duckdb.read_only";
public static final String DUCKDB_USER_AGENT_PROPERTY = "custom_user_agent";
public static final String JDBC_STREAM_RESULTS = "jdbc_stream_results";
private static final String DUCKDB_URL_PREFIX = "jdbc:duckdb:";
private static final String DUCKLAKE_OPTION = "ducklake";
private static final String DUCKLAKE_ALIAS_OPTION = "ducklake_alias";
private static final Pattern DUCKLAKE_ALIAS_OPTION_PATTERN = Pattern.compile("[a-zA-Z0-9_]+");
private static final String DUCKLAKE_URL_PREFIX = "ducklake:";
private static final ReentrantLock DUCKLAKE_INIT_LOCK = new ReentrantLock();
static {
try {
DriverManager.registerDriver(new DuckDBDriver());
} catch (SQLException e) {
e.printStackTrace();
}
}
public Connection connect(String url, Properties info) throws SQLException {
if (!acceptsURL(url)) {
return null;
}
boolean read_only = false;
if (info == null) {
info = new Properties();
} else { // make a copy because we're removing the read only property below
info = (Properties) info.clone();
}
String prop_val = (String) info.remove(DUCKDB_READONLY_PROPERTY);
if (prop_val != null) {
String prop_clean = prop_val.trim().toLowerCase();
read_only = prop_clean.equals("1") || prop_clean.equals("true") || prop_clean.equals("yes");
}
info.put("duckdb_api", "jdbc");
// Apache Spark passes this option when SELECT on a JDBC DataSource
// table is performed. It is the internal Spark option and is likely
// passed by mistake, so we need to ignore it to allow the connection
// to be established.
info.remove("path");
String ducklake = removeOption(info, DUCKLAKE_OPTION);
String ducklakeAlias = removeOption(info, DUCKLAKE_ALIAS_OPTION);
Connection conn = DuckDBConnection.newConnection(url, read_only, info);
initDucklake(conn, url, ducklake, ducklakeAlias);
return conn;
}
public boolean acceptsURL(String url) throws SQLException {
return url.startsWith(DUCKDB_URL_PREFIX);
}
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
DriverPropertyInfo[] ret = {};
return ret; // no properties
}
public int getMajorVersion() {
return 1;
}
public int getMinorVersion() {
return 0;
}
public boolean jdbcCompliant() {
return true; // of course!
}
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException("no logger");
}
private static void initDucklake(Connection conn, String url, String ducklake, String ducklakeAlias)
throws SQLException {
if (null == ducklake) {
return;
}
DUCKLAKE_INIT_LOCK.lock();
try {
String attachQuery = createAttachQuery(ducklake, ducklakeAlias);
try (Statement stmt = conn.createStatement()) {
stmt.execute("INSTALL ducklake");
stmt.execute("LOAD ducklake");
stmt.execute(attachQuery);
if (null != ducklakeAlias) {
stmt.execute("USE " + ducklakeAlias);
}
}
} finally {
DUCKLAKE_INIT_LOCK.unlock();
}
}
private static String createAttachQuery(String ducklake, String ducklakeAlias) throws SQLException {
ducklake = ducklake.replaceAll("'", "''");
if (!ducklake.startsWith(DUCKLAKE_URL_PREFIX)) {
ducklake = DUCKLAKE_URL_PREFIX + ducklake;
}
String query = "ATTACH IF NOT EXISTS '" + ducklake + "'";
if (null != ducklakeAlias) {
if (!DUCKLAKE_ALIAS_OPTION_PATTERN.matcher(ducklakeAlias).matches()) {
throw new SQLException("Invalid DuckLake alias specified: " + ducklakeAlias);
}
query += " AS " + ducklakeAlias;
}
return query;
}
private static String removeOption(Properties props, String opt) {
Object obj = props.remove(opt);
if (null != obj) {
return obj.toString();
}
return null;
}
}