Using syntaqlite from Rust

This tutorial walks you through adding syntaqlite to a Rust project. By the end you'll have a small program that formats SQL, validates it against a schema, and prints diagnostics.

1. Create a project

cargo new sql-check
cd sql-check

Add syntaqlite with the features you need:

cargo add syntaqlite --features fmt,validation,sqlite

2. Format a query

Replace src/main.rs with:

use syntaqlite::Formatter;

fn main() {
    let mut fmt = Formatter::new();
    let output = fmt
        .format("select id,name,email from users where active=1 order by name")
        .expect("parse error");
    println!("{output}");
}

Run it:

cargo run
SELECT id, name, email FROM users WHERE active = 1 ORDER BY name;

The formatter handle is reusable; internal allocations are recycled across calls.

3. Validate against a schema

Now let's add schema validation. Update src/main.rs:

use syntaqlite::{Catalog, Formatter, SemanticAnalyzer, ValidationConfig};

fn main() {
    // Format
    let mut fmt = Formatter::new();
    let output = fmt
        .format("select id,nme from users where active=1")
        .expect("parse error");
    println!("Formatted:\n{output}");

    // Validate
    let mut analyzer = SemanticAnalyzer::new();

    // Register schema from CREATE TABLE statements
    let schema = "CREATE TABLE users (id INTEGER, name TEXT, email TEXT, active INTEGER);";
    let (catalog, errors) = Catalog::from_ddl(
        syntaqlite::sqlite_dialect(),
        &[(schema, None)],
    );
    assert!(errors.is_empty(), "Schema errors: {errors:?}");

    // Validate a query against the schema
    let config = ValidationConfig::default().with_strict_schema();
    let query = "SELECT id, nme FROM users WHERE active = 1";
    let model = analyzer.analyze(query, &catalog, &config);

    if model.diagnostics().is_empty() {
        println!("No errors found.");
    } else {
        for d in model.diagnostics() {
            println!("{:?}: {}", d.severity(), d.message());
        }
    }
}

Run it:

cargo run
Formatted:
SELECT id, nme FROM users WHERE active = 1;

Error: unknown column 'nme'

The validator caught the typo: nme should be name.

4. Parse and inspect the AST

To work with the syntax tree directly, use syntaqlite-syntax:

cargo add syntaqlite-syntax --features sqlite
use syntaqlite_syntax::{Parser, ParseOutcome};

fn main() {
    let parser = Parser::new();
    let mut session = parser.parse("SELECT 1 + 2; SELECT 'hello';");

    let mut i = 0;
    loop {
        match session.next() {
            ParseOutcome::Ok(stmt) => {
                i += 1;
                let dump = stmt.dump();
                println!("--- statement {i} ---\n{dump}");
            }
            ParseOutcome::Err(err) => {
                eprintln!("error: {}", err.message());
                break;
            }
            ParseOutcome::Done => break,
        }
    }
}

Next steps