Skip to content

feat(plugin-duckdb): add remote Quack protocol connections#1720

Merged
datlechin merged 1 commit into
mainfrom
feat/duckdb-quack-remote
Jun 18, 2026
Merged

feat(plugin-duckdb): add remote Quack protocol connections#1720
datlechin merged 1 commit into
mainfrom
feat/duckdb-quack-remote

Conversation

@datlechin

@datlechin datlechin commented Jun 18, 2026

Copy link
Copy Markdown
Member

Addresses #1716.

Adds an experimental remote connection mode to the DuckDB driver using DuckDB's Quack client-server protocol, alongside the existing local-file mode. macOS and iOS.

Scope (and an honest limitation)

You can connect to a remote DuckDB server over Quack and run SQL against it (e.g. SELECT * FROM alias.main.table). The sidebar does not list remote tables: the current Quack beta cannot enumerate a remote catalog. Every listing path is rejected server-side (duckdb_databases() returns "not implemented", information_schema is empty, SHOW TABLES fails with "multiple streaming scans"). DuckLake over Quack hits the same wall (attaching it runs a snapshot query that needs multiple streaming scans), so it is not included.

This is upstream in DuckDB 1.5.4, not fixable client-side. Browsing and DuckLake are expected once Quack stabilizes in DuckDB 2.0. The mode is labeled "Remote (Quack, experimental)" and the limitation is documented.

What changed

DuckDB switches to the libSQL/Turso pattern: connectionMode: .apiOnly with a duckdbMode picker (Local File / Remote). No ConnectionMode enum change and no PluginKit ABI bump; new fields ride the existing additionalFields channel.

  • Connection form gains host, port (9494), token (secure, Keychain-backed), and database alias for remote. Local-file connections are unchanged, with a migration that moves the old database path into the new file-path field.
  • The driver opens an in-memory DuckDB, runs INSTALL quack / LOAD quack, CREATE SECRET (TYPE quack, ...), then ATTACH 'quack:host:port' and USE the alias. SQL is built in QuackConnectBuilder with literal escaping and identifier quoting; the port is range-validated.
  • For remote connections the database list resolves to the attached alias instead of duckdb_databases() (which the remote rejects), so the connection no longer errors.
  • quack://host:port/alias parses and formats as a remote DuckDB URL; duckdb:///path stays file-based.

Tests

  • DuckDBConnectionFieldsTests: field set, visibility rules, password row stays hidden, token is secure.
  • DuckDBQuackConnectTests: QuackConnectBuilder SQL and escaping against injection-hostile tokens/hosts/aliases, plus host/port validation.
  • Quack URL parse/format round-trips added to the existing URL tests.

Live remote needs a server, so it is not in CI; the tests cover the SQL the driver sends.

Library prerequisites

  • macOS: bump scripts/build-duckdb.sh to DuckDB 1.5.3+ (Quack ships as a core extension from 1.5.3), fill the real libduckdb-src.zip checksum, rebuild, publish via scripts/publish-libs.sh, commit the checksums.
  • iOS: scripts/build-duckdb-ios.sh + scripts/duckdb-ios-extensions.cmake statically link quack + httpfs (iOS cannot autoload), against the OpenSSL already in Libs/ios. No x86_64 simulator OpenSSL slice ships, so that slice is skipped unless BUILD_X86_SIMULATOR=1.

Notes

  • No PluginKit version bump (additive, ABI-safe).
  • swiftlint --strict is clean on the changed files.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 340e4c203e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +279 to +280
case "quack":
return .duckdb

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Populate DuckDB remote fields for quack URLs

When a quack:// URL is opened and does not match a saved connection, TabRouter.openDatabaseURL builds a transient connection via TransientConnectionFactory, which copies only host/port/database and never sets duckdbMode, duckdbHost, or duckdbAlias. The DuckDB driver only enters the remote path when additionalFields["duckdbMode"] == "remote", so these transient Quack links fall back to connectLocal() and open or create a local DuckDB file named after the alias instead of connecting to the remote server. Please carry the parsed Quack mode into the transient connection's additional fields.

Useful? React with 👍 / 👎.

Comment thread scripts/build-duckdb.sh
# After bumping the version, set DUCKDB_SHA256 to the checksum of the new
# libduckdb-src.zip: shasum -a 256 /tmp/duckdb-build/libduckdb-src.zip
DUCKDB_VERSION="v1.5.3"
DUCKDB_SHA256="REPLACE_WITH_libduckdb-src.zip_SHA256_FOR_v1.5.3"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replace the DuckDB checksum placeholder

With a clean /tmp/duckdb-build, this placeholder is piped directly into shasum -a 256 -c - before the source archive is unzipped, so scripts/build-duckdb.sh now fails immediately for the v1.5.3 rebuild needed by Quack. Please commit the real libduckdb-src.zip SHA-256 or change the script so it does not ship an unusable checksum.

Useful? React with 👍 / 👎.

@mintlify

mintlify Bot commented Jun 18, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jun 18, 2026, 4:10 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@datlechin datlechin force-pushed the feat/duckdb-quack-remote branch from 340e4c2 to b215225 Compare June 18, 2026 16:58
@datlechin datlechin merged commit adcebcd into main Jun 18, 2026
2 of 3 checks passed
@datlechin datlechin deleted the feat/duckdb-quack-remote branch June 18, 2026 16:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2152257a4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

static func attachSQL(host: String, port: Int, alias: String) -> String {
"ATTACH '\(quackTarget(host: host, port: port))' AS \(quoteIdentifier(alias))"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Support plain-HTTP Quack remotes

When the host is non-local but the server is exposed as plain HTTP (for example CALL quack_serve('quack:0.0.0.0:9494', allow_other_hostname => true) on a LAN/dev box), DuckDB's Quack client assumes HTTPS unless the ATTACH includes DISABLE_SSL true (DuckDB docs). This builder never emits that option and the new fields do not expose any way to request it, so those remote connections fail at attach time even with the correct host, port, and token.

Useful? React with 👍 / 👎.

}

private static func quackTarget(host: String, port: Int) -> String {
"quack:\(escapeLiteral(host)):\(port)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve IPv6 brackets in Quack targets

For an IPv6 host parsed from a URL such as quack://[::1]:9494/remotedb or entered as ::1, the stored host value is ::1, but this constructs quack:::1:9494 instead of the bracketed Quack URI form quack:[::1]:9494 that DuckDB documents for IPv6 endpoints. Those connections cannot be parsed by Quack; wrap colon-containing hosts in brackets before building the target.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant