Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 (April 2026)

  • qail-zig is active again and no longer documented as deferred.
  • PG driver hardening now includes AST sanitization, stricter startup/auth sequencing, COPY fail-closed checks, and replication hardening suites.
  • The current public benchmark story is now the dedicated qail-zig versus pg.zig shared-surface matrix.
  • qail-zig now has its own versioned changelog and docs track.
  • Real PostgreSQL CLI validation is now explicitly tracked for exec, seed, pull, and migrate status|plan|up|down paths.
  • Migration receipt hardening now records full receipt fields and handles generated-version collisions for immediate back-to-back migration runs.
  • Current repository snapshot: 52,795 tracked text lines total, including 50,049 lines of Zig across 169 tracked .zig files.
  • The current qail.rs reference size for parity tracking is 209,728 total lines.

Repository Snapshot

  • Total LOC: 52,795 tracked text lines in qail-zig.
  • Zig LOC: 50,049 lines across 169 tracked .zig files.
  • qail.rs total LOC: 209,728 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

AreaStatus
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

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.pool.PgPool.init(...)
  • qail.driver.pipeline.Pipeline.init(...)
  • qail.validateAst(...)

Practical Direction

  • Use PgDriver for direct command execution and AST-native reads/writes.
  • Use Pipeline for high-throughput prepared batches.
  • Use PgPool for concurrent prepared singles and scoped workloads.
  • Validate untrusted AST input with qail.validateAst before 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.PgDriver
  • qail.driver.connection.Connection
  • qail.driver.pipeline.Pipeline
  • qail.driver.pool.PgPool

Driver Direction

The focus of qail-zig is not a SQL-string convenience wrapper. The serious path is:

  1. build AST or validated command input
  2. encode PostgreSQL protocol frames directly
  3. execute over a pure-Zig transport path
  4. 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.

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 path
  • strict runtime SQL-string allowlist checks for core/driver files
  • stricter startup/auth ordering checks
  • authentication method-switch rejection
  • SASL final / AuthenticationOk sequencing 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.
  • validateAst exists for untrusted or deserialized command ingress.
  • Public driver execution rejects raw SQL command payloads; remaining SQL strings are confined to audited internal renderers/helpers.
  • protocol handlers are moving toward explicit state validation and drain-to-ready behavior after errors.

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-zig
  • pg.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 connection
  • pool10 — prepared singles over ten connections

It covers five workloads:

  • point — tiny point lookup path
  • wide_rows — medium result sets with wide mixed rows
  • large_rows — larger result-set receive/decode path
  • many_params — parameter-heavy bind/encode path
  • aggregate — more server-heavy aggregate slice

The methodology is intentionally strict:

  • qail-zig workloads are authored as native QailCmd ASTs
  • qail-zig compiles them once to SQL for statement preparation
  • qail-zig then runs them through its prepared protocol path
  • pg.zig executes the same prepared SQL templates through its cached prepared-query path
  • pipeline is 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

WorkloadSingle (pg.zig / qail-zig)Pool10 (pg.zig / qail-zig)
Point19,535 / 44,862 q/s72,251 / 158,675 q/s
Wide rows4,544 / 5,474 q/s16,246 / 19,062 q/s
Large rows88.306 / 90.000 q/s279.950 / 306.865 q/s
Many params19,118 / 41,570 q/s71,257 / 153,386 q/s
Aggregate228.241 / 236.793 q/s1,438.975 / 1,474.389 q/s

Reading the Results

  • qail-zig leads all 10 / 10 shared throughput cells in the published matrix.
  • The biggest gains are on point and many_params, which points at lower execution-path and bind-handling cost.
  • wide_rows also stays positive, which means the win is not restricted to tiny dispatch-heavy lookups.
  • The smallest gaps are large_rows and aggregate, 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.zig comparison 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.

  • Read the public benchmark page at /zig/benchmarks for the published matrix and interpretation.
  • Read README.md in 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.zig comparison.

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/16 on live DB paths (exec, seed, pull, migrate status|plan|up|down).
  • Migration receipt-collision stress pass: 6/6 immediate dual-migrate up runs 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_dump wrappers
  • 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.rs removed raw runtime SQL APIs from the normal execution path entirely.
  • qail-zig now rejects .raw and nested procedural/raw escape hatches on the public driver path by default.
  • On TLS connections, qail-zig now treats connection-derived tls-server-end-point bytes as authoritative instead of allowing caller-supplied binding overrides.
  • qail-zig now also matches libpq-style gssencmode preface semantics and resolves hostnames across plain, TLS, async, and GSSENC-preface connect paths instead of assuming IPv4 literals.
  • qail-zig now ships Linux Kerberos environment preflight diagnostics (linuxKrb5Preflight) and a built-in Linux Kerberos provider (linuxKrb5TokenProvider) via runtime GSSAPI loading on Linux.
  • qail-zig now also exposes a session-aware GssTokenProviderEx callback shape, which removes the old API limitation that prevented Rust-style stateful GSS provider implementations.
  • On Linux, accepted GSSENCRequest now 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 by pg_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.PgDriver
  • qail.ast.QailCmd
  • qail.ast.Expr
  • qail.validateAst

Driver Module

  • qail.driver.connection.Connection
  • qail.driver.pipeline.Pipeline
  • qail.driver.pool.PgPool
  • qail.driver.tls.TlsConnection
  • qail.driver.connect_url.ConnectOptions
  • qail.driver.auth_options.AuthOptions
  • qail.driver.rls.RlsContext

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-run
  • qail seed --file on the PostgreSQL execution path
  • qail pull schema extraction from a live PostgreSQL database
  • qail migrate status|plan|up|down, including receipt recording on live DB
  • database URL resolution through QAIL_DATABASE_URL

Validation date: 2026-04-23, against local real PostgreSQL server runs.

  • 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.1, 2026-04-23)

  • Real PostgreSQL CLI matrix is validated on live DB paths for exec (inline/file/json/tx/dry-run), seed, pull, and migrate status|plan|up|down.
  • Migration receipt writes now include applied_at and sql_down on the AST-native path, matching the migration table contract used in runtime flows.
  • Auto migration receipts now use conflict-safe insertion and version-retry semantics, so immediate back-to-back migrate up calls no longer fail on _qail_migrations_version_key.
  • Migration-table creation via the public AST route remains raw-policy compatible (no trusted-only column default escape hatch in that path).
  • AST column-definition SQL rendering now preserves nullable-by-default columns correctly; NOT NULL is emitted only when explicitly requested.
  • The Linux PgDriver.connect and pipeline failure-metadata fixes from v0.8.1 remain in place.

For the repository changelog, see: