Home Expressions
Docs
Drivers Gateway SDKs Benchmarks
Changelog
GitHub
Blog Status Roadmap

May 27, 2026 8 min read

QAIL.rs v1.2.1: Fail-Closed PostgreSQL, Pulled Schema Compatibility, and Phased Migrations

A release deep-dive on QAIL.rs v1.2.1 and the current operational model: parser compatibility for pulled PostgreSQL schemas, fail-closed protocol handling, and expand/backfill/contract migrations.

RustReleasePostgreSQLMigrationsQAIL

QAIL.rs v1.2.1 is a small version number with a very practical meaning: the Rust line now handles richer real PostgreSQL schema pulls, keeps corrupted protocol state out of normal connection reuse, and documents migrations around expand, backfill, and contract instead of treating up/down as the primary operating model.

What Changed Across 1.1.1, 1.2.0, and 1.2.1

The release is not about adding another convenience API. It is about making the current API survive real database shape, real protocol failures, and real deployment sequencing.

The Schema Parser Now Accepts What Pull Produces

The parser changes are intentionally narrow. Pulled schemas can now include table-level RLS state without those directives being misread as columns. Multi-word PostgreSQL types are parsed as one type prefix. Comment text can include quoted examples without being rejected as trailing content.

                            table car_fullday_reseller_pricing {
  id UUID
  tenant_id UUID
  percentage_markup DOUBLE PRECISION
  starts_at TIMESTAMP WITH TIME ZONE
  enable_rls
  force_rls
}

comment on pickup_zones.ribbon_color "Hex color (e.g., \"#f97316\" for orange)"
                        

In the implementation, this is handled by parsing the longest valid column-type prefix before processing column options. That matters because DOUBLE PRECISION and TIMESTAMP WITH TIME ZONE must be treated as types, not as a type plus stray modifiers.

Fail-Closed Means Do Not Reuse a Suspect Connection

The PostgreSQL driver hardening in v1.2.0 follows a simple rule: if protocol ordering, stream framing, unexpected EOF, invalid UTF-8, NUL bytes, or COPY/replication state makes a connection suspect, mark it desynchronized instead of quietly putting it back into normal reuse.

How We Handle Migrations Now

The current operational model is not old-style up/down as the normal story. The normal story is phased apply from deltas/: expand first, backfill second, contract last. Rollback still exists, but it is explicit history repair or explicit down-file execution, not the default framing for safe forward deployment.

                            deltas/
  20260527090000_add_user_name/
    expand.qail
    backfill.qail
    contract.qail
                        
                            # 1. Add compatible schema. Do not break old code yet.
qail migrate apply --phase expand

# 2. Move data in resumable chunks.
qail migrate apply --phase backfill --backfill-chunk-size 10000

# 3. Remove the old shape only after code references are gone.
qail migrate apply --phase contract --codebase ./src
                        

What Each Phase Is For

How Backfill Directives Work

Backfill files are intentionally directive-driven. The file tells QAIL which table and primary key to walk, which column to set, which source column to read from, and how to transform values. The runner checkpoints progress so a long migration can resume instead of starting over.

                            -- @backfill.table: users
-- @backfill.pk: id
-- @backfill.set_column: name_ci
-- @backfill.set_source: name
-- @backfill.set_transform: lower|trim
-- @backfill.where_null: name_ci
-- @backfill.chunk_size: 10000
                        

How Contract Safety Fails

The contract phase is allowed to be destructive, but it is not allowed to be casual. If a contract migration drops a table or column, QAIL parses the migration SQL, scans code references, and blocks when QAIL or raw SQL references still point at the dropped shape.

                            qail migrate apply --phase contract --codebase ./src

# If old references still exist:
# Contract migration '20260527090000_add_user_name/contract.qail' blocked:
# detected live references to dropped fields/tables.
                        

Operational Guidance for This Release

The Bottom Line

QAIL.rs v1.2.1 makes the stable Rust line more honest about production reality. Real PostgreSQL schemas are messy, driver protocols can desynchronize, and safe migrations are staged, not magical. The current model handles that by accepting the schema details pull actually emits, failing closed when wire state is suspect, and requiring expand/backfill/contract discipline for forward migration.

← Back to Blog