| Crates.io | chain-builder |
| lib.rs | chain-builder |
| version | 1.0.1 |
| created_at | 2024-01-07 12:28:01.589821+00 |
| updated_at | 2025-08-11 12:31:11.088138+00 |
| description | A query builder for MySQL for Rust is designed to be flexible and easy to use. |
| homepage | |
| repository | https://github.com/AssetsArt/chain-builder.git |
| max_upload_size | |
| id | 1091734 |
| size | 191,862 |
A flexible and easy-to-use query builder for MySQL and SQLite in Rust. This library provides a fluent interface for building SQL queries with support for complex operations like JOINs, CTEs, and subqueries.
Add this to your Cargo.toml:
[dependencies]
chain-builder = "1.0.0"
serde_json = "1.0"
For MySQL with sqlx integration:
[dependencies]
chain-builder = { version = "1.0.0", features = ["sqlx_mysql"] }
sqlx = { version = "0.8", features = ["mysql", "runtime-tokio-rustls"] }
For SQLite with sqlx integration:
[dependencies]
chain-builder = { version = "1.0.0", features = ["sqlx_sqlite"] }
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio-rustls"] }
For both MySQL and SQLite with sqlx integration:
[dependencies]
chain-builder = { version = "1.0.0", features = ["sqlx_mysql", "sqlx_sqlite"] }
sqlx = { version = "0.8", features = ["mysql", "sqlite", "runtime-tokio-rustls"] }
use chain_builder::{ChainBuilder, Client, Select};
use serde_json::Value;
// Create a new query builder for MySQL
let mut builder = ChainBuilder::new(Client::Mysql);
// Build a simple SELECT query
builder
.db("mydb")
.select(Select::Columns(vec!["*".into()]))
.table("users")
.query(|qb| {
qb.where_eq("name", Value::String("John".to_string()));
qb.where_eq("status", Value::String("active".to_string()));
});
// Generate SQL
let (sql, binds) = builder.to_sql();
println!("SQL: {}", sql);
println!("Binds: {:?}", binds);
use chain_builder::{ChainBuilder, Client, Select};
use serde_json::Value;
// Create a new query builder for SQLite
let mut builder = ChainBuilder::new(Client::Sqlite);
// Build a simple SELECT query
builder
.select(Select::Columns(vec!["*".into()]))
.table("users")
.query(|qb| {
qb.where_eq("name", Value::String("John".to_string()));
qb.where_eq("status", Value::String("active".to_string()));
});
// Generate SQL
let (sql, binds) = builder.to_sql();
println!("SQL: {}", sql);
println!("Binds: {:?}", binds);
use chain_builder::{ChainBuilder, Client, Select};
use serde_json::Value;
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.db("mydb")
.select(Select::Columns(vec!["id".into(), "name".into(), "email".into()]))
.table("users")
.query(|qb| {
qb.where_eq("status", Value::String("active".to_string()));
qb.where_gt("age", Value::Number(18.into()));
qb.limit(10);
qb.offset(5);
qb.order_by("name", "ASC");
});
let (sql, binds) = builder.to_sql();
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.db("mydb")
.select(Select::Columns(vec!["users.name".into(), "profiles.bio".into()]))
.table("users")
.query(|qb| {
qb.join("profiles", |join| {
join.on("users.id", "=", "profiles.user_id");
});
qb.where_eq("users.status", Value::String("active".to_string()));
});
builder.query(|qb| {
qb.where_eq("status", Value::String("active".to_string()));
qb.where_in("department", vec![
Value::String("IT".to_string()),
Value::String("HR".to_string()),
]);
// Case-insensitive LIKE
qb.where_ilike("name", Value::String("john".to_string()));
// Column-to-column comparison
qb.where_column("users.age", ">", "profiles.min_age");
// EXISTS subquery
qb.where_exists(|sub| {
sub.db("mydb")
.table("orders")
.select(Select::Columns(vec!["id".into()]))
.query(|sub_qb| {
sub_qb.where_column("orders.user_id", "=", "users.id");
sub_qb.where_eq("status", Value::String("completed".to_string()));
});
});
// JSON contains (MySQL only)
qb.where_json_contains("metadata", Value::String("premium".to_string()));
// Raw SQL
qb.where_raw(
"(latitude BETWEEN ? AND ?) AND (longitude BETWEEN ? AND ?)",
Some(vec![
Value::Number(40.0.into()),
Value::Number(41.0.into()),
Value::Number(70.0.into()),
Value::Number(71.0.into()),
]),
);
});
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.db("mydb")
.table("users")
.insert(serde_json::json!({
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"status": "active"
}));
let (sql, binds) = builder.to_sql();
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.db("mydb")
.table("users")
.update(serde_json::json!({
"status": "inactive",
"updated_at": "2024-01-15"
}))
.query(|qb| {
qb.where_eq("id", Value::Number(1.into()));
});
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.db("mydb")
.table("users")
.delete()
.query(|qb| {
qb.where_eq("status", Value::String("inactive".to_string()));
});
// Create a CTE for active users
let mut active_users = ChainBuilder::new(Client::Mysql);
active_users
.db("mydb")
.table("users")
.select(Select::Columns(vec!["*".into()]))
.query(|qb| {
qb.where_eq("status", Value::String("active".to_string()));
});
// Use the CTE in main query
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.with("active_users", active_users)
.select(Select::Columns(vec!["*".into()]))
.table("active_users")
.query(|qb| {
qb.where_gt("age", Value::Number(25.into()));
});
let mut pending_users = ChainBuilder::new(Client::Mysql);
pending_users
.db("mydb")
.table("users")
.select(Select::Columns(vec!["*".into()]))
.query(|qb| {
qb.where_eq("status", Value::String("pending".to_string()));
});
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.union(pending_users)
.db("mydb")
.select(Select::Columns(vec!["*".into()]))
.table("users")
.query(|qb| {
qb.where_eq("status", Value::String("active".to_string()));
});
builder.query(|qb| {
qb.left_join("profiles", |join| {
join.on("users.id", "=", "profiles.user_id");
});
qb.inner_join("departments", |join| {
join.on("users.department_id", "=", "departments.id");
join.or()
.on("users.role", "=", "departments.manager_role");
});
qb.full_outer_join("orders", |join| {
join.on("users.id", "=", "orders.user_id");
});
qb.cross_join("roles", |join| {
join.on("users.role_id", "=", "roles.id");
});
qb.join_using("permissions", vec!["user_id".to_string()]);
});
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.db("mydb")
.table("orders")
.query(|qb| {
qb.group_by(vec!["user_id".to_string()]);
qb.having("COUNT(*)", ">", Value::Number(5.into()));
qb.having_between("SUM(amount)", [
Value::Number(100.into()),
Value::Number(1000.into())
]);
});
// Add aggregate functions
builder
.select_count("id")
.select_sum("amount")
.select_avg("amount")
.select_max("created_at")
.select_min("created_at")
.select_alias("user_id", "uid")
.select_raw("CONCAT(first_name, ' ', last_name) AS full_name", None);
use chain_builder::{ChainBuilder, Client, Select};
use sqlx::mysql::MySqlPool;
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPool::connect("mysql://user:pass@localhost/db").await?;
let mut builder = ChainBuilder::new(Client::Mysql);
builder
.db("mydb")
.select(Select::Columns(vec!["*".into()]))
.table("users")
.query(|qb| {
qb.where_eq("status", Value::String("active".to_string()));
});
// Convert to sqlx query
let query = builder.to_sqlx_query();
// Execute
let rows = query.fetch_all(&pool).await?;
Ok(())
}
use chain_builder::{ChainBuilder, Client, Select};
use sqlx::sqlite::SqlitePool;
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = SqlitePool::connect("sqlite://path/to/database.db").await?;
let mut builder = ChainBuilder::new(Client::Sqlite);
builder
.select(Select::Columns(vec!["*".into()]))
.table("users")
.query(|qb| {
qb.where_eq("status", Value::String("active".to_string()));
});
// Convert to sqlx query (available with sqlx_sqlite feature)
let query = builder.to_sqlx_query();
// Execute
let rows = query.fetch_all(&pool).await?;
Ok(())
}
The main query builder class.
new(client: Client) - Create a new buildernew_mysql() - Create a new MySQL buildernew_sqlite() - Create a new SQLite builderdb(name: &str) - Set database nametable(name: &str) - Set table nameselect(select: Select) - Add SELECT clauseinsert(data: Value) - Set INSERT dataupdate(data: Value) - Set UPDATE datadelete() - Set DELETE operationquery(closure) - Configure WHERE, JOIN, etc.to_sql() - Generate SQL string and bind parametersselect(select: Select) - Basic SELECTselect_raw(sql, binds) - Raw SELECT expressionselect_distinct(columns) - DISTINCT SELECTselect_count(column) - COUNT aggregateselect_sum(column) - SUM aggregateselect_avg(column) - AVG aggregateselect_max(column) - MAX aggregateselect_min(column) - MIN aggregateselect_alias(column, alias) - SELECT with aliasto_sqlx_query() - Convert to sqlx query (requires sqlx_mysql or sqlx_sqlite feature)to_sqlx_query_as<T>() - Convert to typed sqlx query (requires sqlx_mysql or sqlx_sqlite feature)count(column, pool) - Count rows (MySQL only, requires sqlx_mysql feature)Used for WHERE clauses and other query parts.
where_eq(column, value) - Equal conditionwhere_ne(column, value) - Not equal conditionwhere_in(column, values) - IN conditionwhere_not_in(column, values) - NOT IN conditionwhere_gt(column, value) - Greater thanwhere_gte(column, value) - Greater than or equalwhere_lt(column, value) - Less thanwhere_lte(column, value) - Less than or equalwhere_between(column, [min, max]) - BETWEEN conditionwhere_not_between(column, [min, max]) - NOT BETWEEN conditionwhere_like(column, pattern) - LIKE conditionwhere_not_like(column, pattern) - NOT LIKE conditionwhere_ilike(column, pattern) - Case-insensitive LIKEwhere_null(column) - IS NULLwhere_not_null(column) - IS NOT NULLwhere_exists(closure) - EXISTS subquerywhere_not_exists(closure) - NOT EXISTS subquerywhere_column(lhs, op, rhs) - Column-to-column comparisonwhere_json_contains(column, value) - JSON contains (MySQL)where_subquery(closure) - Subquery conditionor() - Start OR chainwhere_raw(sql, binds) - Raw SQL conditionhaving(column, operator, value) - HAVING conditionhaving_between(column, [min, max]) - HAVING BETWEENhaving_in(column, values) - HAVING INhaving_not_in(column, values) - HAVING NOT INhaving_raw(sql, binds) - Raw HAVING SQLjoin(table, closure) - INNER JOINleft_join(table, closure) - LEFT JOINright_join(table, closure) - RIGHT JOINleft_outer_join(table, closure) - LEFT OUTER JOINright_outer_join(table, closure) - RIGHT OUTER JOINfull_outer_join(table, closure) - FULL OUTER JOINcross_join(table, closure) - CROSS JOINjoin_using(table, columns) - JOIN USINGlimit(n) - LIMIT clauseoffset(n) - OFFSET clauseorder_by(column, direction) - ORDER BYgroup_by(columns) - GROUP BYwith(alias, builder) - WITH clauseunion(builder) - UNION clauseThe library is organized into several modules:
src/types.rs - Core types and enumssrc/builder.rs - Main ChainBuilder implementationsrc/query/ - Query building functionality
src/query/common.rs - Common query operations (WHERE, HAVING, etc.)src/query/join/ - JOIN functionalitysrc/common/ - Shared compilation logicsrc/mysql/ - MySQL-specific compilationsrc/sqlite/ - SQLite-specific compilationsrc/sqlx_mysql.rs - MySQL sqlx integration (conditional compilation)src/sqlx_sqlite.rs - SQLite sqlx integration (conditional compilation)The library uses feature flags to control functionality:
mysql (default) - Enable MySQL supportsqlite - Enable SQLite supportsqlx_mysql (default) - Enable MySQL sqlx integrationsqlx_sqlite - Enable SQLite sqlx integrationpostgres - Enable PostgreSQL support (future)MIT License - see LICENSE file for details.