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

SQL Primitives

Primitives are reusable building blocks for constructing SQL expressions. They handle quoting, escaping, vendor-specific syntax, and logical composition so you don’t have to.

Conditions built with typed columns and SqliteOperation cover simple comparisons, but real queries need more — OR groups, function calls, string concatenation, date formatting. That’s what primitives are for.

Import them with:

#![allow(unused)]
fn main() {
use vantage_sql::primitives::*;
}

Primitives are not part of the prelude — import them explicitly when needed.

Macros and structs

Some primitives have convenience macros that accept a variable number of arguments and call .expr() on each one automatically: fx!Fx, concat_!Concat. The macros are syntactic sugar — if you need to build arguments programmatically (e.g. from a Vec), use the underlying struct directly.

or_() / and_() — Logical Combinators

By default, multiple calls to .with_condition() combine with AND. When you need OR, use or_():

#![allow(unused)]
fn main() {
use vantage_sql::primitives::*;

// role = 'admin' OR role = 'superuser'
let cond = or_(ident("role").eq("admin"), ident("role").eq("superuser"));
}

For nested logic, combine with and_():

#![allow(unused)]
fn main() {
// (price > 100 AND in_stock = 1) OR (featured = 1)
let cond = or_(
    and_(ident("price").gt(100), ident("in_stock").eq(true)),
    ident("featured").eq(true),
);
}

Both return Expression<T>, so they plug directly into .with_condition().

ident() — Identifiers

Creates a quoted column or table name. Quoting adapts per backend (" for SQLite/Postgres, ` for MySQL).

#![allow(unused)]
fn main() {
let col = ident("price");                     // "price"
let qualified = ident("name").dot_of("u");    // "u"."name"
let aliased = ident("total").with_alias("t"); // "total" AS "t"
}

ident() is a shorthand for Identifier::new(). Reserved words and names with spaces are quoted automatically.

fx! — Function Calls

The fx! macro builds a SQL function call. Arguments are passed directly — .expr() is called on each one automatically:

#![allow(unused)]
fn main() {
fx!("count", sqlite_expr!("*"))
// => COUNT(*)

fx!("avg", ident("price"))
// => AVG("price")

// Multiple arguments
fx!("coalesce", ident("nickname"), "anonymous")
// => COALESCE("nickname", 'anonymous')

// Nested
fx!("round", fx!("avg", ident("price")), 2i64)
// => ROUND(AVG("price"), 2)
}

If you need to build arguments programmatically (e.g. from a Vec), use Fx::new() directly:

#![allow(unused)]
fn main() {
let args: Vec<Expression<AnySqliteType>> = columns.iter().map(|c| c.expr()).collect();
let f = Fx::new("coalesce", args);
}

ternary() — Conditional Expression

Three-valued conditional. Renders as IIF() on SQLite, IF() on MySQL, and CASE WHEN ... THEN ... ELSE ... END on PostgreSQL:

#![allow(unused)]
fn main() {
let expr = ternary(
    ident("stock").gt(0),
    "in stock",
    "sold out",
);
}

ternary() is a shorthand for Ternary::new().

Case — CASE Expressions

For more than two branches, use Case to build a full CASE WHEN ... END block:

#![allow(unused)]
fn main() {
let expr = Case::new()
    .when(ident("status").eq("active"), "yes")
    .when(ident("status").eq("banned"), "no")
    .else_("unknown");
}

concat_! — String Concatenation

Concatenates expressions. Renders as || on SQLite/Postgres, CONCAT() on MySQL. The concat_! macro calls .expr() on each argument automatically:

#![allow(unused)]
fn main() {
concat_!(ident("first_name"), " ", ident("last_name"))
}

Use .ws() for a separator — it accepts any Expressive<T>, including string literals:

#![allow(unused)]
fn main() {
concat_!(ident("first_name"), ident("last_name")).ws(", ")
// SQLite:   "first_name" || ', ' || "last_name"
// MySQL:    CONCAT_WS(', ', `first_name`, `last_name`)
}

Interval — Date Intervals

Portable date interval that adapts per backend:

#![allow(unused)]
fn main() {
let i = Interval::days(30);
// SQLite:   30  (used with date functions)
// MySQL:    INTERVAL 30 DAY
// Postgres: INTERVAL '30 days'
}

See Interval for available constructors (days, hours, months, etc.).

date_format() — Date Formatting

Portable strftime-style formatting. Translates format tokens per backend:

#![allow(unused)]
fn main() {
let formatted = date_format(ident("created_at"), "%Y-%m-%d");
// SQLite:   STRFTIME('%Y-%m-%d', "created_at")
// MySQL:    DATE_FORMAT("created_at", '%Y-%m-%d')
// Postgres: TO_CHAR("created_at", 'YYYY-MM-DD')
}

date_format() is a shorthand for DateFormat::new(). Use .raw_format() to skip token translation and pass a native format string.