🪝 QAIL — The AST-Native Query Language
Write queries as data. Send them as bytes. No SQL strings.
The Vision
QAIL is not a query transpiler. QAIL is a data language.
Instead of passing SQL strings through your stack, you work directly with a typed AST (Abstract Syntax Tree). This AST compiles to database wire protocol bytes — no string interpolation, no SQL injection, no parsing at runtime.
┌─────────────────────────────────────────────────────────────────┐
│ Layer 1: Intent │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ let cmd = QailCmd::get("users").filter("id", Eq, 42); │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Layer 2: Brain (Pure Logic - NO ASYNC) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ let bytes = PgEncoder::encode(&cmd); // → BytesMut │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Layer 3: Muscle (Async I/O - Tokio) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ stream.write_all(&bytes).await?; │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Layer 4: Database │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ PostgreSQL / MySQL / etc. │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Why This Matters
| Old Way (SQL Strings) | QAIL Way (AST-Native) |
|---|---|
"SELECT * FROM users WHERE id = $1" |
QailCmd::get("users").filter("id", Eq, id) |
| String concatenation risk | Typed at compile time |
| Parse SQL at runtime | Compile to bytes directly |
| Locked to one driver (sqlx, pg) | Runtime-agnostic |
Architecture
qail.rs/
├── qail-core/ # Layer 1: AST + Parser
│ ├── ast/ # QailCmd, Expr, Value
│ ├── parser/ # Text → AST (for CLI, LSP)
│ └── transpiler/ # AST → SQL text (legacy path)
│
├── qail-pg/ # PostgreSQL Driver
│ ├── protocol/ # Layer 2: AST → BytesMut (pure, sync)
│ └── driver/ # Layer 3: Async I/O (tokio)
│
├── qail-cli/ # Command-line tool
├── qail-lsp/ # Language server
├── qail-wasm/ # Browser playground
└── qail-ffi/ # C-API for other languages
Quick Start
Rust
use QailCmd;
use ;
// Layer 1: Express intent as AST
let cmd = get
.columns
.filter;
// Layer 2: Compile to wire protocol (pure, sync)
let bytes = encode_simple_query;
// Layer 3: Send over network (async)
let mut driver = connect.await?;
let rows = driver.fetch_all.await?;
CLI (for migration / debugging)
# Install
# Transpile QAIL text to SQL (legacy mode)
# → SELECT id, email FROM users WHERE active = true
The Three Layers
Layer 2: The Brain (Pure Logic)
This is the key innovation. The encoder:
- Takes a
QailCmd(AST) - Returns
BytesMut(wire protocol bytes) - Has zero async, zero I/O, zero tokio
// This is PURE computation - can compile to WASM
let bytes = encode_simple_query;
Layer 3: The Muscle (Async Runtime)
The only place where tokio lives. If a better runtime emerges, only this layer changes:
// Currently uses tokio - swappable in the future
let mut driver = connect.await?;
driver.send.await?;
Supported Databases
| Database | Status | Crate |
|---|---|---|
| PostgreSQL | 🔄 In Progress | qail-pg |
| MySQL | 📋 Planned | qail-mysql |
| SQLite | 📋 Planned | qail-sqlite |
Each database has its own wire protocol, so each gets its own encoder.
Contributing
License
MIT © 2025 QAIL Contributors