QAIL Zig Documentation
Zig-first PostgreSQL wire-protocol driver with AST-native query building, PostgreSQL-path parity tracking against qail.rs, and optional Linux Kerberos/GSSENC integration via the platform GSSAPI stack.
QAIL Zig is the active Zig implementation of the QAIL PostgreSQL stack. It shares the same AST direction as qail.rs, but keeps the runtime, protocol path, and tooling in Zig. The core driver path is Zig-native; Linux Kerberos/GSSENC support is an optional runtime integration with system libc/GSSAPI rather than a self-contained Zig Kerberos stack. Full qail.rs ecosystem parity is still incomplete outside the PostgreSQL-focused track.
Latest Updates (May 2026)
- qail-zig remains on the
v0.8.3release line; the currentmainbranch carries additional post-v0.8.3API hardening. - The current public API is AST-native:
QailCmd, typedExprvalues,PgDriver,PgPool,Pipeline, COPY helpers, RLS helpers, and explicit URL/options connection APIs. - Public raw SQL inputs and nested raw escape hatches are not part of the current driver API; public runtime and transpiler paths fail closed.
- Cursor SQL helpers, trusted/raw compatibility helpers, and data-safety raw helper modules are internal implementation details.
- CLI parity work now includes branch management, codegen tools, migration receipt hardening, and live PostgreSQL validation for
exec,seed,pull, andmigrate status|plan|up|down. - Current repository snapshot: 56,749 tracked text lines total, including 53,426 lines of Zig across 172 tracked
.zigfiles. - The current qail.rs reference size for parity tracking is 210,593 total tracked lines.
Repository Snapshot
- Total LOC: 56,749 tracked text lines in qail-zig.
- Zig LOC: 53,426 lines across 172 tracked
.zigfiles. - qail.rs total LOC: 210,593 tracked lines.
.
├── src/ # Driver, AST, parser, protocol, runtime, CLI, tests, benches
├── scripts/ # Codegen, parity, and policy guards
├── docs/ # mdBook pages and theme overrides
├── .github/workflows/ # CI workflows
├── build.zig # Build graph and targets
└── PARITY_AST_PG_DRIVER.md
What QAIL Zig Covers
| Area | Status |
|---|---|
| PostgreSQL driver | ✅ Active |
| Connection pooling | ✅ Active |
| Prepared pipelines | ✅ Active |
| COPY in/out helpers | ✅ Active |
| TLS | ✅ Active |
| Logical replication core | ✅ Active |
| CLI | ✅ Active |
| Editor LSP (via qail.rs extension) | ✅ External |
| Security hardening suites | ✅ Active |
| qail.rs parity tracking | ✅ Active |
Implementation Positioning
- qail.rs is still the production reference and widest implementation.
- qail-zig is the serious Zig track, with active parity work and dedicated benchmarks.
- Security boundary: on the AST flow, the goal remains no application SQL string interpolation surface.
Docs Map
- Start with Installation and Quick Start.
- Use PostgreSQL Driver for transport and feature coverage.
- Use Security Hardening for the recent fail-closed work.
- Use Throughput Benchmarks and qail.rs Parity Status to track the implementation line.
Installation
Requirements
- Zig
0.16+ - PostgreSQL
14+ - macOS, Linux, or another platform supported by the Zig toolchain
Clone and Build
git clone https://github.com/qail-io/qail-zig.git
cd qail-zig
zig build -Doptimize=ReleaseFast
Optional Wrapper
./scripts/zigw is a thin convenience wrapper for common repo tasks.
Examples:
./scripts/zigw doctor
./scripts/zigw test
./scripts/zigw pgzig-bench qail single --workload point
./scripts/zigw delegates to normal zig build commands.
Docs Build
The Zig docs book is configured to publish into the existing dev.qail.io tree at public/zig/docs.
cd docs
mdbook build
Quick Start
const std = @import("std");
const qail = @import("qail");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var driver = try qail.driver.driver.PgDriver.connect(allocator, "127.0.0.1", 5432, "postgres", "mydb");
defer driver.deinit();
const cmd = qail.ast.QailCmd.get("users")
.select(&.{ qail.ast.Expr.col("id"), qail.ast.Expr.col("email") })
.where(&.{.{ .condition = .{ .column = "active", .op = .eq, .value = .{ .bool = true } } }})
.limit(10);
const rows = try driver.fetchAll(&cmd);
defer {
for (rows) |*row| row.deinit();
allocator.free(rows);
}
}
High-Level Entry Points
qail.driver.driver.PgDriver.connect(...)qail.driver.driver.PgDriver.connectUrl(...)qail.driver.pool.PgPool.init(...)qail.driver.pipeline.Pipeline.init(...)qail.validateAst(...)
Practical Direction
- Use
PgDriverfor direct command execution and AST-native reads/writes. - Use
Pipelinefor high-throughput prepared batches. - Use
PgPoolfor concurrent prepared singles and scoped workloads. - Validate untrusted AST input with
qail.validateAstbefore execution.
PostgreSQL Driver
QAIL Zig implements PostgreSQL directly over the wire protocol. The active surface includes:
- plain TCP connections
- connection timeouts
- TLS transport
- startup/auth handling for cleartext, MD5, SCRAM, and enterprise auth hooks
- prepared statement execution
- pipeline execution
- connection pooling
- COPY helpers
- LISTEN / NOTIFY
- logical replication core
- RLS helper APIs
Primary Types
qail.driver.driver.PgDriverqail.driver.connection.Connectionqail.driver.pipeline.Pipelineqail.driver.pool.PgPool
Driver Direction
The focus of qail-zig is not a SQL-string convenience wrapper. The serious path is:
- build AST or validated command input
- encode PostgreSQL protocol frames directly
- execute over a pure-Zig transport path
- fail closed on protocol and state-machine violations
Current Emphasis
Recent work concentrated on hardening the PG driver instead of broadening surface area first. That includes startup/auth state validation, protocol framing checks, COPY sequencing checks, replication stream fail-closed handling, and AST sanitization.
The current public driver surface keeps raw SQL and trusted compatibility helpers internal. Application code should enter through QailCmd, PgDriver, PgPool, Pipeline, COPY helpers, and documented connection options.
Pool, Pipeline, and COPY
Pool
PgPool provides fixed-size connection pooling with scoped helpers and reset-on-release behavior.
Use it when:
- you want concurrent prepared single-query workloads
- you need pooled RLS or tenant-scoped acquisition
- you want explicit
min_connections/max_connections
Pipeline
Pipeline batches Bind + Execute messages and sends one Sync per batch. This is the highest-throughput path for prepared count-only or row-collecting workloads on a single connection.
Use it when:
- you already have a prepared statement
- batch throughput matters more than per-query latency
- you want one connection and many completions per round trip
COPY
The COPY helpers are active and hardened.
Supported areas:
- COPY FROM STDIN helpers
- COPY TO STDOUT raw export helpers
- stricter protocol sequencing checks
- fail-closed handling on malformed COPY states and oversized data
COPY is now treated as a protocol feature that must reject unexpected backend frames rather than attempting to continue.
Security Hardening
The recent qail-zig work focused on PG-driver hardening parity against qail.rs.
Added or Tightened
- AST sanitization for untrusted command input
- raw SQL escape-hatch rejection on the sanitization, runtime, and public transpiler paths
- strict runtime SQL-string allowlist checks for core/driver files
- public API guards that keep raw, trusted-compatibility, cursor SQL helper, and data-safety raw helper modules internal
- stricter startup/auth ordering checks
- authentication method-switch rejection
- SASL final /
AuthenticationOksequencing checks - Bind / Parse parameter-count guards
- COPY fail-closed state validation
- replication stream fail-closed handling on malformed
CopyData - startup, protocol, and replication hardening suites
Why This Matters
Protocol bugs are often state-machine bugs, not just parsing bugs. The hardening work in qail-zig now rejects malformed or unexpected backend sequences earlier instead of silently progressing through them.
Current Safety Model
- AST-native execution is the preferred path.
validateAstexists for untrusted or deserialized command ingress.- Public driver execution rejects raw SQL command payloads; remaining SQL strings are confined to audited internal renderers/helpers.
- Public transpilation fails closed for raw command payloads.
- Protocol handlers use explicit state validation and drain-to-ready behavior after errors where recovery is possible.
Remaining Direction
The active parity target is not “support every surface first”. It is “close driver and hardening gaps without weakening the transport guarantees.”
QAIL Zig Benchmarks
The current public benchmark page is published at /zig/benchmarks on dev.qail.io.
The current public Zig driver comparison is:
qail-zigpg.zig
Harness
The active harness is src/benchmarks/qail_pgzig_bench.zig and reports the shared prepared-statement surface:
single— prepared single-query path on one connectionpool10— prepared singles over ten connections
It covers five workloads:
point— tiny point lookup pathwide_rows— medium result sets with wide mixed rowslarge_rows— larger result-set receive/decode pathmany_params— parameter-heavy bind/encode pathaggregate— more server-heavy aggregate slice
The methodology is intentionally strict:
- qail-zig workloads are authored as native
QailCmdASTs - qail-zig compiles them once to SQL for statement preparation
- qail-zig then runs them through its prepared protocol path
pg.zigexecutes the same prepared SQL templates through its cached prepared-query pathpipelineis excluded so the published page stays on the clearest shared modes between the two drivers
Runner Examples
# Canonical benchmark runner
zig build pgzig-bench -- qail single --workload point
zig build pgzig-bench -- pgzig single --workload point
# Optional wrapper (equivalent commands)
./scripts/zigw pgzig-bench qail single --workload point
./scripts/zigw pgzig-bench pgzig single --workload point
# Full published matrix surface
zig build pgzig-bench -- qail pool10 --workload wide_rows
zig build pgzig-bench -- pgzig pool10 --workload wide_rows
zig build pgzig-bench -- qail single --workload many_params
zig build pgzig-bench -- pgzig pool10 --workload aggregate
Latest Published 3-Round Medians
| Workload | Single (pg.zig / qail-zig) | Pool10 (pg.zig / qail-zig) |
|---|---|---|
| Point | 19,535 / 44,862 q/s | 72,251 / 158,675 q/s |
| Wide rows | 4,544 / 5,474 q/s | 16,246 / 19,062 q/s |
| Large rows | 88.306 / 90.000 q/s | 279.950 / 306.865 q/s |
| Many params | 19,118 / 41,570 q/s | 71,257 / 153,386 q/s |
| Aggregate | 228.241 / 236.793 q/s | 1,438.975 / 1,474.389 q/s |
Reading the Results
- qail-zig leads all
10 / 10shared throughput cells in the published matrix. - The biggest gains are on
pointandmany_params, which points at lower execution-path and bind-handling cost. wide_rowsalso stays positive, which means the win is not restricted to tiny dispatch-heavy lookups.- The smallest gaps are
large_rowsandaggregate, where PostgreSQL itself dominates more of the total work. - The page is intentionally narrow on feature parity. Pipeline is a qail-zig capability, but it is not part of the
pg.zigcomparison page so the benchmark stays on the clearest shared surface.
Benchmark Discipline
The benchmark numbers are only meaningful when the compared paths are equivalent. The current work explicitly separated:
- shared SQL template after qail-zig AST compilation
- matched prepared execution on both sides
- interleaved rounds with median reporting
That keeps the comparison at the driver/runtime boundary instead of turning it into a fake API-surface mismatch.
Recommended Next Reads
- Read the public benchmark page at
/zig/benchmarksfor the published matrix and interpretation. - Read
README.mdin the repo root for the short current benchmark summary. - If you want pipeline numbers, treat them as qail-zig-only capability measurements rather than as a direct
pg.zigcomparison.
qail.rs Parity Status
qail-zig tracks qail.rs as the reference implementation for PostgreSQL driver behavior and hardening.
Current Snapshot
As of 2026-04-23, the narrow AST/codegen parity checks against a local qail.rs checkout are green:
./scripts/check_codegen_sync.sh ../qail.rs->codegen sync check passed./scripts/check_parity.sh ../qail.rs->AST actions: rust=75 zig=76,Encoder actions: rust=57 zig=76,parity check passed
That means the Rust-driven AST porting/codegen path is working for its current scope, and the PostgreSQL AST encoder still covers the Rust action surface completely.
Real PostgreSQL CLI validation on the Zig side is also green on this date:
- Broad CLI matrix pass:
16/16on live DB paths (exec,seed,pull,migrate status|plan|up|down). - Migration receipt-collision stress pass:
6/6immediate dual-migrate upruns across fresh databases.
Active Areas with Strong Coverage
- AST core exports
- Rust-driven AST codegen sync
- PostgreSQL wire protocol
- prepared execution and pipelines
- pooling
- TLS transport
- COPY in/out helpers
- LISTEN / NOTIFY
- logical replication core
- RLS helper APIs
- CLI PostgreSQL execution path (
exec,seed,pull,migrate status|plan|up|down) - startup/auth policy controls
- TLS SCRAM channel-binding derivation and fail-closed precedence on TLS startup
- protocol hardening suites
- typed policy parsing and diff normalization for common
pg_dumpwrappers - typed recursive CTE AST support and typed source-query constructors for views/materialized views
Current Reality
Parity is not complete across the entire qail.rs ecosystem. The largest gaps remain outside the core PG driver track:
- gateway / auto-REST / WebSocket / OpenAPI stack
- qdrant vector driver and hybrid execution path
- workflow engine
- typed schema codegen (
qail types) and build-time SQL / N+1 guard rails - CLI breadth outside the core PG path (
qail init,types, vector/hybrid flows) - editor tooling breadth remains on the qail.rs OpenVSX LSP track (not bundled in qail-zig)
- direct SDKs and broader non-driver surfaces
Important Policy Delta
The main remaining policy difference is narrower now:
qail.rsremoved raw runtime SQL APIs from the normal execution path entirely.qail-zignow rejects.rawand nested procedural/raw escape hatches on the public driver path by default.- On TLS connections,
qail-zignow treats connection-derivedtls-server-end-pointbytes as authoritative instead of allowing caller-supplied binding overrides. qail-zignow also matches libpq-stylegssencmodepreface semantics and resolves hostnames across plain, TLS, async, and GSSENC-preface connect paths instead of assuming IPv4 literals.qail-zignow ships Linux Kerberos environment preflight diagnostics (linuxKrb5Preflight) and a built-in Linux Kerberos provider (linuxKrb5TokenProvider) via runtime GSSAPI loading on Linux.qail-zignow also exposes a session-awareGssTokenProviderExcallback shape, which removes the old API limitation that prevented Rust-style stateful GSS provider implementations.- On Linux, accepted
GSSENCRequestnow proceeds into an encrypted GSS transport instead of failing closed after the preface. - The repository now also carries a dedicated Linux Kerberos/GSSENC smoke workflow that provisions a local realm + PostgreSQL service principal and proves one AST-native roundtrip over
gssencmode=require. - Typed RLS helpers and typed policy parsing are now present on the Zig side, including normalization of common wrapped
current_setting(...)forms emitted bypg_dump. - The old raw nested-query and raw policy-SQL string fields have been removed from the Zig AST shape entirely; trusted compatibility now flows through internal helper modules and raw AST variants that the public runtime gate already rejects.
- Migration receipt recording now writes the full tracked shape (
version,name,applied_at,checksum,sql_up,sql_down) and handles generated-version collisions without aborting roll-forward migrations.
The main remaining enterprise-auth gap is narrower now:
- runtime coverage depth and maintenance burden are now the main gap, especially expanding beyond the new smoke path and keeping the local TLS/GSS compatibility layers stable across Zig upgrades
PG Driver Focus
The PG driver is the serious parity target right now. That is why recent work landed in:
- sanitization
- startup/auth sequencing
- protocol hardening
- replication hardening
- benchmark comparability
For detailed driver parity notes, see the repository parity file:
PARITY_AST_PG_DRIVER.md
API Surface
The high-signal public surface for qail-zig currently centers on the PostgreSQL driver and related tooling.
Core Exports
qail.driver.driver.PgDriverqail.ast.QailCmdqail.ast.Exprqail.validateAst
Driver Module
qail.driver.connection.Connectionqail.driver.pipeline.Pipelineqail.driver.pool.PgPoolqail.driver.pool.PoolConfigqail.driver.tls.TlsConnectionqail.driver.connect_url.ConnectOptionsqail.driver.auth_options.AuthOptionsqail.driver.rls.RlsContext
Current API Notes
- Use
qail.ast.QailCmdand typedqail.ast.Exprvalues for application queries. - Use
qail.driver.driver.PgDriver.connect(...),connectUrl(...),connectEnv(...), orconnectWithOptions(...)for direct driver connections. - Use
qail.driver.pool.PgPool.init(allocator, config)orPgPool.initUri(allocator, uri)for pooled workloads. - Use
qail.driver.pipeline.Pipelinefor prepared batch execution. - Validate untrusted/deserialized commands with
qail.validateAstbefore execution. - Do not depend on raw SQL, cursor SQL helper, trusted compatibility, or data-safety raw helper modules; those are internal implementation details on the current line.
Tooling
- CLI entry via
zig build cli - editor LSP via the published qail.rs extension (OpenVSX/VS Code)
- benchmark runners under
src/*bench*.zig
Verified Real-DB CLI Surface
qail exec: inline query,--file,--json,--tx, and--dry-runqail seed --fileon the PostgreSQL execution pathqail pullschema extraction from a live PostgreSQL databaseqail migrate status|plan|up|down, including receipt recording on live DB- database URL resolution through
QAIL_DATABASE_URL
Validation date: 2026-05-21, against the current docs/API surface.
Recommended Reading Order
- Start with the driver docs.
- Then read the hardening page.
- Then use the parity page to understand what is intentionally in-scope versus still missing.
Changelog
QAIL Zig now tracks its own release notes separately from qail.rs.
Current Highlights (post-v0.8.3, 2026-05-21)
- qail-zig’s latest release tag is
v0.8.3; the current docs also cover post-v0.8.3main-branch API hardening. - Public raw SQL inputs, cursor SQL helpers, trusted/raw compatibility helpers, and data-safety raw helpers are hidden from the current public API.
- Public driver execution and public transpilation fail closed for raw commands and nested raw escape hatches.
- Real PostgreSQL CLI matrix remains validated on live DB paths for
exec(inline/file/json/tx/dry-run),seed,pull, andmigrate status|plan|up|down. - Migration receipt writes include
applied_at,sql_down, conflict-safe insertion, and generated-version retry semantics. - AST column-definition SQL rendering preserves nullable-by-default columns correctly;
NOT NULLis emitted only when explicitly requested. - The Linux
PgDriver.connectstack-size fix and pipeline failure metadata fromv0.8.1remain in place.
For the repository changelog, see: