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: The AST-Native Approach

QAIL takes a unique approach to building SQL queries: instead of strings or macros, queries are constructed as a typed Abstract Syntax Tree.

The AST-Native Difference

ApproachHow Queries Work
String-basedSQL written as text, parameterized at runtime
Macro-basedDSL macros expand to SQL at compile time
AST-NativeTyped AST compiles directly to wire protocol

What QAIL Enables

Native PostgreSQL Features

#![allow(unused)]
fn main() {
use qail_core::{Qail, builders::*};

// Native JSON operators (->, ->>)
json_path("metadata", ["vessel_bookings", "0", "key"])

// COALESCE with type safety
coalesce([col("booking_number"), text("N/A")])

// String concatenation
concat([col("first_name"), text(" "), col("last_name")])

// Type casting  
cast(col("total_fare"), "float")

// CASE WHEN expressions
case_when(gt("score", 80), text("pass"))
    .otherwise(text("fail"))
}

Full Query Example

A production WhatsApp integration query with JSON access, string concat, and type casts:

#![allow(unused)]
fn main() {
use qail_core::{Qail, Operator, builders::*};

let route = coalesce([
    concat([
        json_path("o.metadata", ["vessel_bookings", "0", "depart_departure_loc"]),
        text(" → "),
        json_path("o.metadata", ["vessel_bookings", "0", "depart_arrival_loc"]),
    ]),
    text("Route"),
]).alias("route");

let cmd = Qail::get("orders")
    .table_alias("o")
    .column_expr(col("o.id"))
    .column_expr(coalesce([col("o.booking_number"), text("N/A")]).alias("booking_number"))
    .column_expr(cast(col("o.status"), "text").alias("status"))
    .column_expr(route)
    .column_expr(coalesce([
        json_path("o.metadata", ["vessel_bookings", "0", "depart_travel_date"]),
        text("TBD")
    ]).alias("travel_date"))
    .filter_cond(cond(json("o.contact_info", "phone"), Operator::Eq, param(1)))
    .or_filter_cond(cond(
        replace(json("o.contact_info", "phone"), text("+"), text("")),
        Operator::Eq, 
        param(1)
    ))
    .order_desc("o.created_at")
    .limit(10);

let orders = pool.fetch_all::<OrderRow>(&cmd).await?;
}

QAIL Highlights

FeatureQAIL Approach
SafetyStructural - no SQL strings to inject
JSONNative json(), json_path() operators
Expressionscoalesce(), concat(), cast() builders
CTEswith_cte() for complex queries
AsyncFull async/await support
Type ValidationColumnType enum with compile-time checks

ColumnType Validation

QAIL validates types at build time:

#![allow(unused)]
fn main() {
pub enum ColumnType {
    Uuid, Text, Varchar(Option<u16>), Int, BigInt, 
    Serial, BigSerial, Bool, Float, Decimal(Option<(u8,u8)>),
    Jsonb, Timestamp, Timestamptz, Date, Time, Bytea,
}

// Compile-time validation
ColumnType::Uuid.can_be_primary_key()     // true
ColumnType::Jsonb.can_be_primary_key()    // false - caught at build time
ColumnType::Jsonb.supports_indexing()     // false - warned before migration
}

When to Use QAIL

QAIL shines for:

  • Complex PostgreSQL queries with JSON, CTEs, aggregates
  • Type-safe query building with IDE support
  • Production systems where safety is critical
  • Projects that need advanced SQL features without string literals