Releases: gizmodata/quack-jdbc
v0.2.0-alpha.4
Contributed in part by Jose Davila-Ciullo (@jdctinuiti) — thanks!
Added
- Token provider properties for safer shared JDBC configurations:
tokenEnvandtokenFile. Both are accepted via connection
Propertiesonly — they are rejected on the JDBC URL so that a pasted
or shared URL cannot read a local secret and send it to an
attacker-chosen host.
Fixed
- Manual-commit transactions now work with DataGrip, DBeaver, and other
tools (#4). WithautoCommitoff, the driver lazily issues
BEGIN TRANSACTIONbefore the first statement, so a later
Connection.commit()no longer fails with
"cannot commit - no transaction is active".commit()/rollback()
with no pending transaction are harmless no-ops, and re-enabling
auto-commit mid-transaction commits it per the JDBC spec. - HTTPS transport now keeps the original hostname in request URIs instead
of replacing it with a resolved IP address. This preserves TLS SNI and
certificate hostname verification for gateways and load balancers that
route by hostname. Plain HTTP endpoints still expand to resolved address
candidates for the existing localhost IPv4/IPv6 fallback behavior.
v0.2.0-alpha.3
Supersedes the never-published v0.2.0-alpha.2 tag (Maven Central
rejected the re-publish after the original tag's deploy was cancelled
mid-flight). Content is identical to what alpha.2 would have shipped,
plus contributor credit in the [0.2.0-alpha.1] notes below.
Changed
- Integration tests now
INSTALL quack;from the core repository,
matching DuckDB v1.5.3 ("Variegata") where Quack ships as a signed
core extension. The-unsignedflag andcore_nightlyrepository are
no longer required. The full 83-test suite passes against the
quackextension bundled with DuckDB CLI v1.5.3 — no driver code
changes needed, the wire format is unchanged. - README and CLAUDE.md updated to reflect the v1.5.3 install path; the
pre-1.5.3INSTALL quack FROM core_nightlyrecipe is kept as a note
for users on older builds.
v0.2.0-alpha.1
Thanks to @mwieczorkiewicz
(Mikołaj Wieczorkiewicz) for contributing the pluggable transport SPI
and configurable HTTP timeouts in #1
— their first contribution to the project.
Added — Pluggable transport SPI (#1)
QuackTransportinterface andQuackTransportFactory
(functional, takes a parsedQuackUri) let applications supply their
own transport implementation — a customHttpClientwith proxies /
mTLS / interceptors, or an in-process fake for testing.QuackDriver.connect(url, properties, transportFactory)overload
threads the factory throughQuackConnectionandQuackSession. The
no-factoryconnect(url, properties)path is unchanged and still uses
the built-in HTTP transport.QuackHttpTransportnow implementsQuackTransport; the existing
QuackSession(QuackUri, QuackHttpTransport)constructor is retained
for binary compatibility.
Added — Configurable HTTP timeouts
connectTimeoutandrequestTimeoutJDBC URL /Properties
options, accepted as either a plain integer (seconds) or an ISO-8601
duration likePT30S. Defaults remain 10s connect / 60s request.
Surfaced viaDriver.getPropertyInfoso DBeaver / DataGrip show them
in their connection dialogs.- Invalid / non-positive values raise a
QuackExceptionat parse time.
Tests
- New
QuackDriverCustomTransportTestexercising factory injection,
null-factory error handling,getPropertyInfoshape, and binary
compatibility of the legacyQuackSessionconstructor. - New
QuackUriTestcases for timeout parsing (URL, properties, invalid
values) and aQuackHttpTransportTestcase forfrom(QuackUri).
Total suite: 83 tests, all green.
v0.1.0-alpha.1
Published to Maven Central as
com.gizmodata:quack-jdbc:0.1.0-alpha.1 and as a GitHub Release with
both versioned and un-versioned jars at
https://github.com/gizmodata/quack-jdbc/releases/tag/v0.1.0-alpha.1.
First public alpha release. The Quack protocol itself is in beta until
DuckDB v2.0 ships in September 2026 — this driver is pinned to
duckdb/duckdb-quack@daae4826
(2026-05-10). Don't use this in production; do use it to evaluate
DBeaver / DataGrip / Spark / direct JDBC against a remote DuckDB.
All [Unreleased] entries below shipped in this release.
Added — Release distribution
- GitHub Release jars are now attached to every tagged release by
CI, in addition to the versioned artifact going to Maven Central.
Each release gets:quack-jdbc-<version>.jar— versioned filenamequack-jdbc.jar— un-versioned filename (always the latest), so
DBeaver / DataGrip / curl users can fetch a stable URL:
https://github.com/gizmodata/quack-jdbc/releases/latest/download/quack-jdbc.jar- Matching
*-sources.jarand*-javadoc.jarin both forms SHA256SUMSfor every uploaded asset
- README badges added at the top: Maven Central version, latest
GitHub release, direct download link to the latest un-versioned jar,
link to the GitHub repo, MIT license.
Added — APPEND_REQUEST encoder + appendChunk bulk-load API
VectorCodec.encodeDataChunkWrapper/encodeDataChunk/
encodeVector/encodeFlatVectorBodyare now implemented for
the common scalar physical types: BOOLEAN, all integer family
(signed and unsigned, including HUGEINT / UHUGEINT), FLOAT, DOUBLE,
DECIMAL (width 1-38), VARCHAR / CHAR / BLOB / BIT / GEOMETRY,
DATE, TIME / TIME_NS, TIMESTAMP variants
(sec/ms/us/ns/TZ), INTERVAL, UUID. STRUCT / LIST / MAP / ARRAY
encoding still throws — opens an issue if you need them.QuackSession.appendChunk(schema, table, chunk)sends an
APPEND_REQUESTto the server with a fully-encoded DataChunk.
This is the bulk-load fast-path: it sends column-oriented binary
data directly, bypassing per-row INSERT parsing. Typical workloads
see an order-of-magnitude speed-up over
PreparedStatement.executeBatch().- 5 round-trip unit tests in
VectorCodecRoundTripTestexercise
encode → decode against an in-memory buffer (no server needed) for
primitive vectors, nullable columns, and a mixed scalar payload. - 5 integration tests in
AppendIntegrationTestexercise the
full APPEND_REQUEST flow against a live DuckDB+Quack server —
CREATE TABLE → build chunk in code →appendChunk(...)→ SELECT
to verify — covering INTEGER+VARCHAR, nullable BIGINT, all-scalar
mix (BOOLEAN/INT/BIGINT/DOUBLE/DECIMAL/DATE/TIMESTAMP/VARCHAR),
5000-row bulk, and BLOB round-trip.
Total suite: 75 tests, all green.
Changed — Bitset validity + typed CONSTANT/DICTIONARY/SEQUENCE paths
- Bitset-packed validity.
DecodedVector's {@code boolean[] validity}
is replaced with a {@code long[]} bitmap that holds one bit per row.
Wire format hasn't changed (still a byte-aligned bitmap), but the
driver-side memory footprint is now 8× smaller for the validity mask
(1 bit/row instead of 1 byte/row from a {@code boolean[]}). The
validity reader produces {@code long[]} directly via a packed
little-endian read. Validityhelper with bit-test, all-valid initializer, byte
round-trip, and set-valid/set-null helpers — covered by 7 new unit
tests pinning bit ordering and round-trips.- CONSTANT vectors now broadcast a primitive value into the right
typed primitive vector (e.g.,SELECT 42::INTEGER FROM range(1000)→
IntVec(int[])) instead of falling back toObjectVec. - DICTIONARY vectors preserve the dictionary's storage type when
projecting through the selection vector — typed in → typed out. - SEQUENCE vectors for INTEGER and BIGINT logical types
materialize directly intoIntVec/LongVec(the common
SELECT i FROM range(...)case). - 2 new integration tests in
StreamingIntegrationTestpin the typed
SEQUENCE and CONSTANT paths (total: 65 tests).
Changed — Streaming cursor + typed primitive vectors (memory rewrite)
- Streaming cursor.
QuackSession.prepare(sql)is replaced by
QuackSession.cursor(sql), which returns aQuackSession.Cursor
that holds at most one server batch in memory at a time. The
initial PREPARE_RESPONSE is parsed eagerly; subsequent
FETCH_REQUESTs fire only when the local buffer drains. Peak driver
memory for a million-row SELECT now grows with the server batch
size (default ~12 chunks ≈ a few hundred KB), not with the total
result-set row count. - Typed primitive vectors.
DecodedVectorbecomes a sealed
interface with primitive-array records (BoolVec,ByteVec,
ShortVec,IntVec,LongVec,FloatVec,DoubleVec) plus an
ObjectVecfallback. Fixed-width primitive logical types
(BOOLEAN, TINYINT..BIGINT and unsigned siblings, FLOAT, DOUBLE)
now decode directly intoint[]/long[]/double[]/etc. instead
ofList<Object>of boxed wrappers — roughly 4-8× smaller in
memory for those columns, matching the bytes-on-the-wire footprint.
Logical types whose materialized Java form is not a primitive
(DECIMAL, DATE, TIME / TIME_NS / TIME_TZ, TIMESTAMP variants, UUID,
INTERVAL, HUGEINT/UHUGEINT, ENUM, VARCHAR, BLOB, STRUCT, LIST,
ARRAY, MAP) stay inObjectVec. ResultSetgetters route through the new typed accessors
(DecodedVector.getInt,getLong,getDouble, etc.) so reading
a primitive column no longer triggers anInteger.valueOf
per row.QuackConnection.session()is nowpublicso advanced callers
can open aCursordirectly and drain chunks without going through
the JDBCResultSetsurface.- 4 new integration tests in
StreamingIntegrationTestpin the
lazy-fetch behavior and the typed-vector layout (total: 56 tests
passing).
Added — JDBC coverage parity with DuckDB's own driver
Closed the eight method-coverage gaps surfaced by a side-by-side audit
against org.duckdb.DuckDB* (full audit at /tmp/jdbc-coverage-audit.md
during development). All eight are required by at least one of
DBeaver / IntelliJ DataGrip / dbt / Spark JDBC / HikariCP.
PreparedStatement.getMetaData()— returns
{@link java.sql.ResultSetMetaData} for the prepared query by running
SELECT * FROM (<sql>) LIMIT 0with NULL-filled placeholders.
Returns {@code null} for non-SELECT statements per JDBC contract.
Required byspark.read.jdbc(...)for schema inference.PreparedStatement.getParameterMetaData()— returns a
QuackParameterMetaDatareporting the?-marker count (counted
outside single-quoted strings and double-quoted identifiers). Used
by Hibernate / Spring JDBC / DataGrip parameter inspectors.Statement.addBatch(String)/clearBatch()/executeBatch()
andPreparedStatement.addBatch()/executeBatch()— executed
as a sequential loop (no native batch protocol exists in Quack today).
ThrowsBatchUpdateExceptionon individual failures with the partial
counts array. Required bydbt seedand Sparkdf.write.jdbc(...).ResultSet.getArray(...)— wraps the decoded {@code List}
in a {@code QuackArray} (java.sql.Array) carrying the element's
logical type forgetBaseType/getBaseTypeName. Required by
DBeaver's value editor for LIST / ARRAY columns.ResultSet.getBlob(...)— wraps the decoded {@code byte[]} in a
{@code QuackBlob} (java.sql.Blob). Required by DBeaver's BLOB value
editor.Connection.createArrayOf/createStruct— return opaque
QuackArray/QuackStructwrappers usable insetObject(_, Array)
/setObject(_, Struct). Used by dbt IN-list macros and adapter
frameworks.Connection.setCatalog/setSchema— now emit
USE "catalog"."schema"instead of silently storing a field. DBeaver
catalog-navigator switching actually changes context server-side.Connection.isValid(int)— now actually runsSELECT 1to detect
dead server-side connections instead of only checking the local
closedflag. Required by HikariCP / pgbouncer-style pool
health-check semantics.Connection.setTypeMap(Map)— silently acceptsnulland empty
maps (the call HikariCP makes during eviction), throws only for
non-empty mappings.Statement.cancel()— degraded fromSQLFeatureNotSupportedException
to a best-effort no-op so DBeaver / DataGrip query-timeout buttons
don't crash the UI. Real protocol cancel will follow when Quack
surfaces it.QuackHttpTransportnow iterates every address returned by
InetAddress.getAllByName(host)instead of relying on JDK
HttpClient's first-address behavior. Hosts likelocalhostthat
resolve to both127.0.0.1and::1now succeed against a server
bound to either family — previously aConnectExceptionon the first
address (IPv4 by default on macOS) aborted the whole request even
though an IPv6 listener was reachable.- Error messages no longer say
Quack HTTP request failed: nullwhen
the cause has no message; the exception class name is used as a
fallback. The exhausted-addresses error names every address that was
tried, including the underlying failure detail. - First cut of the JDBC driver for DuckDB's Quack remote protocol.
BinaryReader/BinaryWriterfor DuckDB's BinarySerializer wire format
(little-endian uint16 field ids, ULEB128/SLEB128, fixed-width primitives,
length-prefixed strings/blobs/lists, nested objects terminated by
FIELD_END = 0xFFFF).- Logical type model and codec covering BOOLEAN, integer family
(TINYINT…HUGEINT includ...
Plus 15 new integration tests exercising every fix end-to-end against
a live DuckDB+Quack server (52 tests total, all green).