sqlx-named-bind
A SQLx extension that provides named parameter binding with HRTB (Higher-Rank Trait Bounds) pattern, avoiding self-referential lifetime issues.
Features
- ✨ Named Placeholders: Use
:param_nameinstead of?in your SQL queries - 🔒 Type-Safe: Full compile-time type checking with SQLx
- 🚀 Zero Overhead: Placeholder conversion happens at construction time
- 🔄 Generic Executor: Works with
MySqlPool,Transaction, and any SQLxExecutor - 📦 Strongly-Typed Results:
PreparedQueryAsprovides type-safe query results viaFromRow - 🛡️ Lifetime Safe: HRTB pattern avoids self-referential lifetime issues
Installation
Add to your Cargo.toml:
[]
= { = "0.8", = ["mysql", "runtime-tokio"] }
= "0.1"
Quick Start
use MySqlPool;
use PreparedQuery;
async
Examples
Typed Query Results
use ;
use PreparedQueryAs;
async
Using with Transactions
use ;
use PreparedQuery;
async
Optional Results
use ;
use PreparedQueryAs;
async
How It Works
The library uses a three-step approach to avoid self-referential lifetime issues:
- Parse: Extract named placeholders (
:name) and convert SQL to use positional placeholders (?) - Store: Keep the converted SQL, placeholder order, and binder function separately
- Execute: Construct a fresh SQLx
Queryon each execution with the correct lifetime
This approach leverages HRTB (Higher-Rank Trait Bounds) to ensure the binder function works with any lifetime, making the API both safe and flexible.
Why HRTB?
Without HRTB, you'd encounter self-referential lifetime issues:
// ❌ This doesn't work - self-referential lifetime
With HRTB, we defer the lifetime decision to call-site:
// ✅ This works - lifetime chosen at each call
where F: for<'q> FnMut
API Documentation
PreparedQuery
For queries that execute but don't return rows (INSERT, UPDATE, DELETE).
Methods:
new(template, binder)- Create a new prepared queryexecute(executor)- Execute the query and returnMySqlQueryResult
PreparedQueryAs<R>
For queries that return typed rows (SELECT).
Methods:
new(template, binder)- Create a new prepared queryfetch_all(executor)- Fetch all matching rowsfetch_one(executor)- Fetch exactly one row (error if 0 or >1)fetch_optional(executor)- Fetch at most one row (returnsOption<R>)
Limitations
- Currently only supports MySQL (PostgreSQL and SQLite support planned)
- Placeholder names must match
[a-zA-Z0-9_]+ - All placeholders in the SQL must be handled by the binder function
Comparison with Alternatives
| Feature | sqlx-named-bind | SQLx native | Other libraries |
|---|---|---|---|
| Named parameters | ✅ :name |
❌ ? only |
✅ Varies |
| Type safety | ✅ Full | ✅ Full | ⚠️ Varies |
| Lifetime safety | ✅ HRTB | ✅ Native | ⚠️ Varies |
| Generic executor | ✅ Yes | ✅ Yes | ❌ Usually pool-only |
| Runtime overhead | ✅ Zero | ✅ Zero | ⚠️ Some have overhead |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Acknowledgments
This library was inspired by the need for named parameter binding in SQLx while maintaining the same level of type safety and performance. Special thanks to the SQLx team for creating an excellent async SQL toolkit.