A JDBC driver for DuckDB's Quack remote protocol.
Lets any JVM tool — DBeaver, IntelliJ DataGrip, dbt, Spark, your own
service — connect to a DuckDB server over the Quack wire protocol with a
familiar jdbc:quack:// URL.
Status: Experimental / alpha. The Quack protocol itself shipped on 2026-05-12 and will stabilize as part of DuckDB v2.0 in September 2026. Expect breaking changes between now and then. As of DuckDB v1.5.3 ("Variegata"),
quackships as a core extension — no morecore_nightlyrepository needed. This driver is tested against thequackextension bundled with DuckDB CLI v1.5.3.
-- in any DuckDB session
INSTALL quack;
LOAD quack;
CALL quack_serve('quack:127.0.0.1:9494', token=>'my-secret-token');The quack extension is signed and lives in the core repository as
of DuckDB v1.5.3, so no -unsigned flag and no core_nightly repository
are required. (Earlier 1.5.x builds need INSTALL quack FROM core_nightly
with duckdb -unsigned.)
Maven:
<dependency>
<groupId>com.gizmodata</groupId>
<artifactId>quack-jdbc</artifactId>
<version>0.1.0-alpha.1</version>
</dependency>Gradle:
implementation "com.gizmodata:quack-jdbc:0.1.0-alpha.1"Direct jar download (for DBeaver, DataGrip, or any tool that takes a .jar):
| Asset | Description |
|---|---|
quack-jdbc.jar |
Latest release — un-versioned filename, always the newest jar |
quack-jdbc-sources.jar |
Latest sources jar |
quack-jdbc-javadoc.jar |
Latest javadoc jar |
| GitHub releases page | All versioned jars + SHA256 checksums for every release |
import java.sql.*;
import java.util.Properties;
public class Demo {
public static void main(String[] args) throws SQLException {
Properties props = new Properties();
props.setProperty("token", "my-secret-token");
try (Connection conn = DriverManager.getConnection(
"jdbc:quack://127.0.0.1:9494", props);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
"SELECT 42 AS answer, 'hello duckdb' AS greeting")) {
while (rs.next()) {
System.out.println(rs.getInt("answer") + " / " + rs.getString("greeting"));
}
}
}
}jdbc:quack://host[:port][/database][?token=…&tls=…]
| Property | Default | Notes |
|---|---|---|
host |
— | Required. |
port |
9494 | Default Quack port. |
database |
(none) | Reserved; passed through to the server when provided. |
token |
(none) | Authentication token. Prefer an indirect token source for shared configs. |
password |
(none) | Alias for token, useful for tools that expose a password field. |
tokenEnv |
(none) | Environment variable containing the authentication token. |
tokenFile |
(none) | Local file containing the authentication token. |
tls |
false | true → use https:// for the underlying HTTP transport. |
useEncryption |
false | Alias for tls (matches the gizmosql-jdbc-driver convention). |
connectTimeout |
10 | HTTP connect timeout, as seconds or an ISO-8601 duration like PT5S. |
requestTimeout |
60 | Per-request HTTP timeout, as seconds or an ISO-8601 duration like PT30S. |
These options can be set on the URL or via java.util.Properties passed
to DriverManager.getConnection. URL values take precedence. Token
resolution checks token, then password, then tokenEnv, then
tokenFile.
tokenEnv and tokenFile are the exception: they are only accepted via
Properties (or a tool's driver-properties panel) and are rejected if
they appear on the URL. A pasted or shared URL must not be able to read
a local secret and send it to whatever host the URL names.
The built-in HTTP transport reads connectTimeout and requestTimeout
directly from the JDBC URL or connection properties:
try (Connection conn = DriverManager.getConnection(
"jdbc:quack://127.0.0.1:9494?token=my-secret-token&connectTimeout=5&requestTimeout=30")) {
// use the connection normally
}The same options can be supplied with Properties:
Properties props = new Properties();
props.setProperty("token", "my-secret-token");
props.setProperty("connectTimeout", "5");
props.setProperty("requestTimeout", "PT30S");
try (Connection conn = DriverManager.getConnection("jdbc:quack://127.0.0.1:9494", props)) {
// use the connection normally
}For desktop tools such as DataGrip, avoid putting token=... directly
in the JDBC URL. URLs and plain driver properties are easy to copy, log,
or commit by accident.
Prefer tokenFile or tokenEnv, set as driver properties (not on the
URL), for shared desktop-tool configurations:
tokenFile=/Users/alice/.config/quack/prod.token
or:
tokenEnv=QUACK_TOKEN
The token is read at connection time and used for the connection handshake, but the token itself does not need to be stored in the data source.
Applications that need to customize the HTTP layer can bypass
DriverManager and pass a transport factory to QuackDriver. The factory
receives the same parsed QuackUri, so URL parameters and connection
properties are available to custom transports too:
QuackDriver driver = new QuackDriver();
try (Connection conn = driver.connect(
"jdbc:quack://127.0.0.1:9494?token=my-secret-token&connectTimeout=5&requestTimeout=30",
new Properties(),
uri -> {
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(uri.connectTimeout())
.build();
return new QuackHttpTransport(uri.httpUri(), httpClient, uri.requestTimeout());
})) {
// use the connection normally
}quack-jdbc implements the full JDBC DatabaseMetaData surface that
DBeaver uses for catalog browsing, modeled query-for-query on DuckDB's
own JDBC driver. That means:
- Catalog / schema / table / view listing
- Column types, nullability, defaults, comments
- Primary keys
- Imported / exported / cross-reference foreign keys
- Index listing (per index; column-level expansion is a DuckDB limitation)
getTypeInfo,getFunctions
Register the driver in DBeaver → Database → Driver Manager:
| Field | Value |
|---|---|
| Driver Name | DuckDB (Quack) |
| Driver Type | Generic |
| Class Name | com.gizmodata.quack.jdbc.sql.QuackDriver |
| URL Template | jdbc:quack://{host}[:{port}]/[{database}] |
| Default Port | 9494 |
| Driver Files | quack-jdbc-<version>.jar |
Add tokenFile or tokenEnv as connection properties for safer shared
configuration. For quick local testing, token and password are also
supported.
mvn packageProduces target/quack-jdbc-<version>.jar with no runtime dependencies
(uses JDK 17 java.net.http.HttpClient). On release tags, CI also
publishes an un-versioned quack-jdbc.jar to the GitHub release so tools
that want "the latest jar" can fetch a stable URL.
mvn test # unit + integration tests
mvn -Dtest='!*Integration*' test # unit only (no duckdb required)Integration tests spawn a real DuckDB CLI as a Quack server and exercise
the driver end-to-end: connection handshake, CRUD, multi-chunk fetches,
scalar type round-trips, DatabaseMetaData listings, bad-token auth,
concurrent connections, and more. They auto-skip when duckdb is not on
PATH. Override the binary with QUACK_IT_DUCKDB=/path/to/duckdb.
codec/ BinaryReader/Writer — DuckDB BinarySerializer (LE uint16 field ids,
ULEB128/SLEB128 ints, length-prefixed strings/blobs/lists,
objects terminated by FIELD_END = 0xFFFF)
type/ Logical type model and codec (full DuckDB type system)
message/ Quack message records, MessageCodec, DataChunk vector decoder
(FLAT / CONSTANT / DICTIONARY / SEQUENCE encodings)
transport/ QuackUri parser, QuackHttpTransport (POST /quack via JDK HttpClient)
sql/ java.sql.* surface (Driver, Connection, Statement,
PreparedStatement, ResultSet, ResultSetMetaData, DatabaseMetaData,
with Skeletal* bases that throw SQLFeatureNotSupportedException
for the parts we don't yet implement)
The codec/type/message/transport layers are reusable for an ADBC driver
in Go (or any other language); a companion quack-adbc is on the
GizmoData roadmap.
- DataChunk vector encodings supported: FLAT, CONSTANT, DICTIONARY, SEQUENCE. FSST is not yet supported.
- Nested types (STRUCT / LIST / MAP / ARRAY) decode to Java collections
(
Map<String,Object>/List<Object>); fulljava.sql.Array/java.sql.Structwrapping is on the roadmap. - Prepared-statement parameters use client-side literal substitution. Native parameter binding will follow once the Quack protocol surfaces bind parameters.
- The
APPEND_REQUESTfast-path (sending DataChunks directly) is decoder-complete but not encoder-complete; surfaced as aRuntimeExceptioninVectorCodec.encodeDataChunkuntil done.
- Wire-format codec ported clean-room from
@quack-protocol/sdkby Tobi (MIT) DatabaseMetaDataqueries modeled onduckdb/duckdb-java'sDuckDBDatabaseMetaData(MIT)- Many thanks to the DuckDB team for shipping a refreshingly small remote protocol
MIT — see LICENSE for the full text plus attribution.
Issues and PRs welcome at https://github.com/gizmodata/quack-jdbc. See CLAUDE.md for contributor notes (layout, conventions, DBeaver-compat expectations).