JDBC driver
Build and use the experimental Java 17 NYXDB JDBC driver, including URLs, typed v2 results, metadata, limitations, retries, and version compatibility.
The experimental JDBC 4 driver lives in drivers/jdbc. It supports forward-only
one-shot results, append-shaped streaming row results, statements and prepared
statements, a DataSource, catalog metadata, and negotiated wire v2 typed
results for one-shot queries.
The driver is not published to a public Maven repository and does not provide
a GA compatibility contract. Build it from the same engine revision you
deploy. Append-shaped streaming and one-shot wire v2 are functional but
experimental; maintained snapshot/delta streams are not yet a JDBC surface.
This revision also has no JDBC property or handshake for operator OP_AUTH,
so queries receive authentication required from a server launched with
--require-auth. Do not disable that gate on an untrusted listener merely to
accommodate the driver.
Build and install locally
The driver requires Java 17 and Maven.
git clone https://github.com/NYXL-io/db.git
cd db/drivers/jdbc
mvn -q test
mvn -q installmvn install adds the current snapshot to your local Maven repository:
<dependency>
<groupId>io.nyxdb</groupId>
<artifactId>nyxdb-jdbc</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>For a direct classpath, use
target/nyxdb-jdbc-0.1.0-SNAPSHOT.jar. The JAR registers
io.nyxdb.jdbc.NyxDriver through Java's service-provider mechanism.
Connection URLs
Preferred URL:
jdbc:nyxdb://<host>:<port>/<database>?socketTimeoutMillis=<milliseconds>Examples:
jdbc:nyxdb://127.0.0.1:7510/default
jdbc:nyxdb://127.0.0.1:7777/default?socketTimeoutMillis=5000jdbc:nyx:// is accepted as a legacy prefix. Omitted values default to host
127.0.0.1, port 7510, and a 30-second socket timeout. The database path is
carried by the JDBC surface; verify namespace behavior against the current
server before depending on it.
Execute a query
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
try (Connection connection = DriverManager.getConnection(
"jdbc:nyxdb://127.0.0.1:7777/default?socketTimeoutMillis=5000");
PreparedStatement statement = connection.prepareStatement(
"SELECT market, amount FROM trades WHERE amount > ? ORDER BY market")) {
statement.setLong(1, 1000);
try (ResultSet rows = statement.executeQuery()) {
while (rows.next()) {
System.out.printf("%s %s%n", rows.getString("market"), rows.getObject("amount"));
}
}
}Prepared parameters are rendered through the driver's current SQL-literal layer. This is not a server-side multi-statement transaction boundary.
Typed results and metadata
The transport negotiates lazily on the first executeV2 path used by
Statement.execute/executeQuery, trying handshake schema 2, then schema 1,
then plain TSV on fresh sockets. After that setup it reuses one persistent query
connection. A schema-2 path uses OP_QUERY_META and receives one strict
typed-result envelope containing exact elapsed time, numeric engine query
identity, cache state, server identity, and one nested columnar frame. A v2
result preserves the full type descriptor:
- integer and floating-point widths;
- decimal precision, scale, and exact mantissa;
datetime64precision and timezone;- nullability and low-cardinality dictionaries; and
vector(N)element type and dimension.
ResultSetMetaData maps those descriptors to Java SQL types, getBigDecimal
preserves declared scale, temporal values retain their instant, and vectors are
returned as primitive arrays. If negotiation is explicitly unsupported, the
driver can continue in v1 TSV mode, where metadata and value fidelity are more
limited.
At the transport layer, NyxQueryResult.metadata() retains
elapsedUs, queryId, cache state, and server for both the schema-2 typed path
and its text fallback. Java stores the unsigned 64-bit fields in long; use an
unsigned rendering/conversion when a value is above Long.MAX_VALUE. The
standard Statement/ResultSet facade currently consumes this carrier but does
not expose an engine-query-ID or wire-cancel API, so applications must not claim
that Statement.cancel() targets OP_CANCEL_REQUEST.
For the schema-2 executeV2/Statement.execute path, unsupported or no-row
typed shapes—including a write acknowledgement reached through that path—fall
back to #meta plus TSV from the original captured execution. Encoding fallback
never re-executes the statement. executeUpdate/executeLargeUpdate use the
plain OP_QUERY transport and therefore receive a text acknowledgement without
the metadata carrier. Both behaviors are distinct from the driver's transport
retry below, which may deliberately start one new execution for a conservatively
classified read-only statement after connection loss.
On a schema-1 binary connection the driver chooses bare OP_QUERY to preserve
typed rows, so server timing and numeric query identity are unavailable on that
fallback. The protocol defines LZ4, but this JDBC revision requests binary
results plus typed metadata and does not enable LZ4.
The metadata surface can enumerate catalogs, schemas, tables, and columns for tools such as DataGrip. It is pre-GA and may grow append-only fields.
Stream append-shaped rows
The driver recognizes the canonical statement-prefix form and returns a
long-lived, forward-only ResultSet for append-shaped streams:
try (Connection connection = DriverManager.getConnection(
"jdbc:nyxdb://127.0.0.1:7777/default");
var statement = connection.createStatement();
ResultSet stream = statement.executeQuery(
"STREAM SELECT id, market, amount FROM trades")) {
while (stream.next()) {
System.out.printf("%d %s %d%n",
stream.getLong("id"),
stream.getString("market"),
stream.getLong("amount"));
}
}The stream socket uses v1 text row frames; ResultSet.next() blocks until the
next initial or live row arrives. The owning Connection tracks stream sockets
while they are opening and after they become active. Closing the result set, its
statement, or the connection closes the dedicated socket; Connection.abort()
uses the same teardown path. Connection shutdown also releases a thread blocked
in ResultSet.next() with a SQLException. Resource closure is idempotent, but
applications should still use try-with-resources so ownership remains explicit.
socketTimeoutMillis bounds connection and initial stream setup. After the
stream header arrives, the driver disables the socket read timeout: an idle
ResultSet.next() can wait indefinitely for a row or terminal condition. An
application that needs its own deadline must close the result set, statement,
or connection from another control path.
EOF, socket failure, server shutdown, source drop, and a terminal engine error end that result set. The driver does not claim cursor continuity on a replacement connection. If the application resubscribes, treat the initial row replay as a new replacement boundary before accepting live rows.
The current JDBC stream reader accepts append-row frames. Do not use it for a
keyed, aggregate, or UNION stream: those result shapes use snapshot and
positional-delta statuses that the driver does not yet maintain. Use nyxsql,
the web console, or a protocol client that implements those statuses.
Transaction and result limitations
DatabaseMetaData.supportsTransactions()is false and the reported isolation level isTRANSACTION_NONE.- The engine does not provide multi-statement
BEGIN/COMMIT/rollback or an MVCC snapshot-isolation contract. - Result sets are forward-only and read-only.
- Batch updates, stored procedures, generated keys, and many optional JDBC methods are not implemented.
- Streaming result sets are experimental, blocking, forward-only, v1-text only, and not resumable across connections. They currently support append-shaped row streams, not maintained snapshot/delta results.
Failure and retry behavior
Connection and protocol failures surface as SQLException. Use
socketTimeoutMillis plus an application-level overall deadline for bounded
operations; the live-row wait exception is described above. The transport
makes at most one reconnect/retry, and only for one conservatively classified
read-only statement: SELECT, SHOW, DESCRIBE/DESC, or EXPLAIN with a
recognized statement target. Classification is case-insensitive, skips leading
SQL trivia, requires complete keyword boundaries, and rejects multiple
statements.
- Retry a connection attempt with bounded backoff and jitter.
- A failed read may be repeated only when the caller accepts a fresh result.
- DDL, DML, topology commands, unknown statements, and malformed statements are never replayed by the driver.
- A transport failure after one of those requests may have reached the server.
The driver throws
SQLTransientConnectionExceptionwith SQLSTATE08007and the stable message: “NYXDB statement outcome is ambiguous; the driver did not retry it. Reconcile server state before retrying.” - A refusal while opening the socket remains a plain “Failed to connect to NYXDB …” connection failure; no request was sent, so it is not labeled as an ambiguous statement outcome.
- Do not retry deterministic
SQLExceptiondiagnostics as transport outages. - Close failed connections and result sets; do not reuse a connection after a framing or negotiation error.
Upgrade checklist
- Pin the engine and JDBC driver source SHAs.
- Run
mvn test, including golden wire-v2 frames. - Exercise v1 fallback and v2 typed results against the target server.
- Verify decimal, temporal, nullable, and vector columns used by the application.
- Test connection refusal, timeout, mid-response close, ambiguous DML, and append-stream cleanup before promotion.
See Wire protocol for the current experimental format. See Query identity, cancellation, and retries for the distinction between encoding fallback, transport retry, and cooperative cancellation.
nyxsql CLI reference
Command-line flags, query modes, key bindings, timing, disconnect behavior, and compatibility guidance for the experimental NYXDB terminal client.
Browser and platform endpoint clients
Implement the experimental WebSocket auth, endpoint execution, subscription, error, revocation, and reconnect contract without assuming an SDK.