Skip to content

gizmodata/quack-jdbc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

quack-jdbc

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.

Maven Central Latest Release Download latest jar GitHub Repo License: MIT

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"), quack ships as a core extension — no more core_nightly repository needed. This driver is tested against the quack extension bundled with DuckDB CLI v1.5.3.

Quickstart

1. Start a Quack server (DuckDB 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.)

2. Add the driver to your project

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

3. Connect and query

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"));
            }
        }
    }
}

Connection URL

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.

Basic timeout configuration

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
}

Safer token configuration

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.

Fully custom HTTP transport

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
}

DBeaver

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.

Building from source

mvn package

Produces 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.

Testing

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.

Design

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.

Compatibility notes

  • 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>); full java.sql.Array / java.sql.Struct wrapping 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_REQUEST fast-path (sending DataChunks directly) is decoder-complete but not encoder-complete; surfaced as a RuntimeException in VectorCodec.encodeDataChunk until done.

Credits

  • Wire-format codec ported clean-room from @quack-protocol/sdk by Tobi (MIT)
  • DatabaseMetaData queries modeled on duckdb/duckdb-java's DuckDBDatabaseMetaData (MIT)
  • Many thanks to the DuckDB team for shipping a refreshingly small remote protocol

License

MIT — see LICENSE for the full text plus attribution.

Contributing

Issues and PRs welcome at https://github.com/gizmodata/quack-jdbc. See CLAUDE.md for contributor notes (layout, conventions, DBeaver-compat expectations).

About

JDBC driver for DuckDB's Quack remote protocol (quack:// URI scheme). Lets any JVM tool query a remote DuckDB server over HTTP.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages