squeal
A type-safe SQL query builder for Rust targeting PostgreSQL.
squeal provides a simple, type-safe way to construct SQL queries using Rust structures. It offers both direct struct construction and fluent builder APIs with escape hatches built in for complex use cases.
Philosophy
- Keep it simple & stupid
- No attributes, macros, or other "magic"
- Escape hatches built in for custom SQL
- Any valid construction produces syntactically valid SQL
Features
- Type-safe query construction - Catch errors at compile time
- Fluent builder API - Chain methods for readable query construction
- Direct struct construction - Full control when needed
- PostgreSQL targeting - Designed for PostgreSQL dialect
- Zero ORM runtime overhead - Queries are built, not interpreted.
Supported Operations
SELECTqueries with WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSETJOINoperations (INNER, LEFT, RIGHT, FULL, CROSS) with table or subquery sourcesWITH/ Common Table Expressions (CTEs) for complex queriesINSERTstatements with single or multiple value sets, or from SELECT queriesINSERT ... ON CONFLICT(UPSERT) with DO NOTHING or DO UPDATEUPDATEstatements with SET and WHERE clausesDELETEstatements with WHERE conditionsRETURNINGclauses for INSERT, UPDATE, and DELETECREATE TABLEDDL statementsDROP TABLEDDL statements
Installation
Add to your Cargo.toml:
[]
= "0.1.0"
MSRV: Rust 1.90.0
Quick Start
use *;
// SELECT query with fluent builder
let query = Q
.select
.from
.where_
.order_by
.limit
.build;
assert_eq!;
// INSERT with fluent builder
let insert = I
.columns
.values
.build;
assert_eq!;
// UPDATE with fluent builder
let update = U
.columns
.values
.where_
.build;
assert_eq!;
// DELETE with fluent builder
let delete = D
.where_
.build;
Direct Struct Construction
For maximum control, you can construct query structs directly:
use *;
let query = Query ;
assert_eq!;
Escape Hatches
Use custom operators and raw SQL fragments when needed:
use *;
// Use Op::O for custom PostgreSQL operators
let custom_op = Condition;
let query = Q
.select
.from
.where_
.build;
Advanced Features
JOINs
Combine data from multiple tables with type-safe JOIN operations:
use *;
// Find all users with their order count
let query = Q
.select
.from
.inner_join
.group_by
.order_by
.build;
// LEFT JOIN to include users with no orders
let query_with_nulls = Q
.select
.from
.left_join
.build;
Common Table Expressions (WITH clause)
Build complex queries with CTEs for better readability:
use *;
// Calculate monthly revenue and compare to average
let monthly_revenue = Q
.select
.from
.group_by
.build;
let query = Q
.with
.select
.from
.order_by
.build;
// Result: WITH monthly_revenue AS (SELECT ...) SELECT month, revenue, ...
UPSERT (INSERT ... ON CONFLICT)
Handle unique constraint violations gracefully:
use *;
// Insert user, do nothing if email already exists
let insert = I
.columns
.values
.on_conflict_do_nothing
.build;
// Result: INSERT INTO users (email, name, created_at) VALUES (...)
// ON CONFLICT (email) DO NOTHING
// Insert or update: update the name if email exists
let upsert = I
.columns
.values
.on_conflict_do_update
.returning
.build;
// Result: INSERT INTO users (...) VALUES (...)
// ON CONFLICT (email) DO UPDATE SET name = '...', login_count = ...
// RETURNING id, email, updated_at
Multiple Row INSERT
Efficiently insert multiple rows in a single statement:
use *;
let insert = I
.columns
.rows
.returning
.build;
// Result: INSERT INTO products (name, price, category) VALUES
// ('Laptop', 999.99, 'electronics'),
// ('Mouse', 24.99, 'electronics'),
// ('Desk', 299.99, 'furniture')
// RETURNING id, name
INSERT ... SELECT
Copy data from one table to another:
use *;
// Archive old orders
let select_old_orders = Q
.select
.from
.where_
.build;
let archive = I
.columns
.select
.build;
// Result: INSERT INTO orders_archive (order_id, user_id, total, order_date)
// SELECT id, user_id, total, created_at FROM orders
// WHERE created_at < '2023-01-01'
RETURNING Clause
Get values from INSERT, UPDATE, or DELETE operations:
use *;
// Get the auto-generated ID after insert
let insert = I
.columns
.values
.returning
.build;
// Update and return the modified rows
let update = U
.columns
.values
.where_
.returning
.build;
// Delete and track what was removed
let delete = D
.where_
.returning
.build;
Development
Build and Test
# Build the project
# Run tests (excludes Docker tests)
# Run all tests including PostgreSQL integration tests (requires Docker)
# Run benchmarks
# Generate documentation
Code Quality
# Run clippy (required before committing)
# Auto-fix clippy warnings
Project Status
Version: 0.1.0 (pre-release)
This library is in active development. The API is stabilizing but may still change. It is suitable for experimentation and early adoption, but not recommended for production use until version 1.0.
Design Notes
A core problem of using SQL in programming is the impedence mismatch between the code of the database and the code of the client.
Efforts such as ActiveRecord exist; they are widely panned by SQL
experts as producing poor SQL and tend not to "fit" the database
itself. Writing SQL directly in code has also a poor history - it is
notorious for defects and difficulty in maintaining.
Query builders occupy a middle point, where the power of the programming language brings to bear some maintainability, but the intermediate artifact is still recognizable and controllable by the programmer, leading to a fit with the database.
This is my contribution in this effort; I developed a Scala query builder for a company quite a few years ago now and it worked well. I am pleased to offer a ground-up cleanroom Rust query builder.
The core problem this design does not touch is the object-relation mapping code. I would advise users to write a database model layer, perform the object-relation map there, then transform into the usual in-system state from that point out.
Related Projects
- PostgreSQL Rust driver: rust-postgres
- Similar library for Go: sqlf
Repository
License
LGPL 3.0 or later. See LICENSE.md for details.
Contributing
Contributions are welcome! Please ensure:
- All tests pass (
cargo test) - No clippy warnings (
cargo clippy) - New features include tests
- No decrease in code coverage (tarpaulin).
- Code follows the existing style
- Idealy no dependencies are taken outside of dev-dependencies.
- No unsafe code.
AI Note.
This library was hand written, and later AI was asked to extend it. A human contributing _takes responsibility_for their code, whether or not an AI was involved.