qusql-parse

qusql-parse is a fast, zero-dependency SQL parser for Rust. It turns a SQL string into an Abstract Syntax Tree (AST) and emits structured diagnostics (errors and warnings) with byte-accurate source spans so you can display them to users.

Supported dialects

Pass a SQLDialect to ParseOptions to select the right keyword set, identifier quoting rules, and type names for your database:

DialectSQLDialect value
MariaDB / MySQLSQLDialect::MariaDB
PostgreSQLSQLDialect::PostgreSQL
PostGISSQLDialect::PostGIS
SQLiteSQLDialect::Sqlite

Argument placeholder styles

The parser needs to know which placeholder syntax your driver uses so it can parse arguments correctly and assign them indices:

StyleSQLArguments valueExample
?SQLArguments::QuestionMarkMariaDB / MySQL
$1, $2, ...SQLArguments::DollarPostgreSQL
%sSQLArguments::PercentPython drivers

Basic usage

#![allow(unused)]
fn main() {
use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statement, Issues};

let options = ParseOptions::new()
    .dialect(SQLDialect::MariaDB)
    .arguments(SQLArguments::QuestionMark)
    .warn_unquoted_identifiers(true);

let sql = "SELECT `id`, `title` FROM `notes` WHERE `id` = ?";
let mut issues = Issues::new(sql);
let ast = parse_statement(sql, &mut issues, &options);

// Issues implements Display - prints a plain-text summary of all diagnostics.
println!("{}", issues);
println!("AST: {:#?}", ast);
}

Error recovery

The parser is deliberately error-tolerant: it keeps going after a syntax error and returns the best AST it can produce. All problems are collected in Issues rather than returned as a Result. This means you can highlight multiple errors in one pass, which is important for editor tooling and linters.

Source spans

Every AST node implements Spanned, which returns a Range<usize> of byte offsets into the original source string. If you need character offsets (e.g. for ariadne diagnostics), convert with ByteToChar:

#![allow(unused)]
fn main() {
use qusql_parse::ByteToChar;

let b2c = ByteToChar::new(sql.as_bytes());
let char_span = b2c.map_span(byte_span.start..byte_span.end);
}