kx-sql (0.1.3)
Published 2026-01-14 17:48:33 +08:00 by caisin
Installation
[registry]
default = "gitea"
[registries.gitea]
index = "sparse+ " # Sparse index
# index = " " # Git
[net]
git-fetch-with-cli = truecargo add kx-sql@0.1.3About this package
KX-SQL
A flexible and type-safe SQL query builder for Rust that supports multiple database backends.
Features
- Type-safe query building: Compile-time guarantees for SQL correctness
- Multiple database support: MySQL, Databend, and extensible for others
- Fluent API: Chain methods for readable query construction
- Macro support: Convenient macros for common operations
- Comprehensive SQL support: SELECT, INSERT, UPDATE, DELETE, CREATE TABLE
- Zero runtime overhead: All SQL generation happens at compile time
Quick Start
Add this to your Cargo.toml:
[dependencies]
kx-sql = "0.1"
Basic Usage
use kx_sql::*;
// Create a SELECT query
let mut query = SelectQuery::new("users");
query.add_column("id")
.add_column("name")
.where_clause(WhereClause::equals("active", true))
.limit(10);
// Build MySQL query
let mysql_sql = query.to_mysql()?;
println!("{}", mysql_sql);
// Output: SELECT `id`, `name` FROM `users` WHERE `active` = ? LIMIT 10
Using Macros
use kx_sql::*;
// Create fields using macros
let id_field = field!("id", "int", pk);
let name_field = field!("name", "varchar", size: 50);
let email_field = field!("email", "varchar", not_null);
// Create table using macro
let table = table!("users", "User table", {
fields: [id_field, name_field, email_field],
indexes: [
index!("user_email_idx", "email", unique)
],
drop: true,
});
let create_sql = table.to_mysql()?;
Where Clauses
use kx_sql::*;
// Using macro helpers
let condition = where_equals!("status", "active")
.and(where_like!("email", "%@company.com"))
.or(where_in!("role", vec!["admin", "user"]));
let mut query = SelectQuery::new("users");
query.where_clause(condition);
Supported Operations
SELECT Queries
let mut query = SelectQuery::new("users");
query.alias("u")
.columns(&["id", "name", "email"])
.left_join("profiles", "p", "u.id = p.user_id")
.where_clause(where_equals!("u.active", true))
.group_by_str("u.department")
.having(where_like!("COUNT(*)", "> 5"))
.order_by_columns("u.created_at desc")
.paginate(1, 20); // page 1, 20 items per page
let sql = query.to_mysql()?;
INSERT Queries
// Single insert
let mut insert = InsertQuery::into("users");
insert.fields(&["name", "email"])
.values(Values::Data(vec![
Record(vec!["John Doe".into(), "john@example.com".into()])
]));
// Batch insert with conflict resolution
let mut batch_insert = InsertQuery::into("users");
batch_insert.fields(&["name", "email"])
.values(Values::Data(vec![
Record(vec!["John".into(), "john@example.com".into()]),
Record(vec!["Jane".into(), "jane@example.com".into()]),
]))
.conflict(&Conflict::new().values("name,email"));
UPDATE Queries
let mut update = UpdateQuery::new("users");
update.set_field("last_login", "NOW()")
.set_field("login_count", "login_count + 1")
.where_clause(where_equals!("id", 123));
DELETE Queries
let delete = DeleteQuery::new("users")
.where_clause(
where_equals!("active", false)
.and(where_like!("created_at", "< DATE_SUB(NOW(), INTERVAL 1 YEAR)"))
);
CREATE TABLE
let mut table = CreateQuery::new("users");
table.comment("User management table")
.field(&Field::id_field("id")) // Auto-increment PK
.varchar_field("username", 50) // VARCHAR(50)
.varchar_field("email", 100) // VARCHAR(100)
.timestamp_field("created_at") // TIMESTAMP with CURRENT_TIMESTAMP
.idx(&index!("username_idx", "username", unique));
let create_sql = table.to_mysql()?;
Database Support
MySQL
use kx_sql::mysql::BuildMySql;
let query = SelectQuery::new("users");
let sql = query.to_mysql()?; // Returns String
let (sql, params) = query.to_mysql_v()?; // Returns (String, Vec<Value>)
Databend
use kx_sql::databend::BuildDatabend;
let query = SelectQuery::new("users");
let sql = query.to_databend()?;
Adding New Database Support
Implement the required traits:
use kx_sql::traits::*;
pub struct MyDatabaseBuilder;
impl CommonBuilder for MyDatabaseBuilder {
fn quote(&self) -> &'static str {
"\"" // Use double quotes instead of backticks
}
}
impl SqlBuilder for MyDatabaseBuilder {}
impl ColumnBuilder for MyDatabaseBuilder {}
impl TableBuilder for MyDatabaseBuilder {}
Advanced Features
Complex Queries
// Subqueries
let subquery = SelectQuery::new("orders")
.add_column("customer_id")
.where_clause(where_like!("total", "> 1000"));
let mut main_query = SelectQuery::new("customers");
main_query.where_clause(
where_in!("id", vec![/* subquery results */])
);
Aggregations
let mut query = SelectQuery::new("sales");
query.sum("amount", "total_sales")
.count()
.avg("order_value", "avg_order")
.group_by_str("region")
.having(where_like!("total_sales", "> 10000"));
Window Functions and Expressions
let mut query = SelectQuery::new("employees");
query.expr("ROW_NUMBER() OVER (ORDER BY salary DESC)", Some("rank"))
.expr("CASE WHEN salary > 50000 THEN 'High' ELSE 'Low' END", Some("pay_grade"));
Error Handling
The library provides comprehensive error handling:
use kx_sql::error::Error;
match query.to_mysql() {
Ok(sql) => println!("Generated SQL: {}", sql),
Err(Error::NoUpdateFields) => {
eprintln!("Update query needs at least one field");
}
Err(Error::NoDeleteConditions) => {
eprintln!("DELETE queries require WHERE conditions for safety");
}
Err(e) => eprintln!("Other error: {}", e),
}
Performance
- Zero-cost abstractions: All SQL generation happens at compile time
- Memory efficient: Minimal allocations during query building
- Type safety: Catch SQL errors at compile time, not runtime
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the same license as the parent workspace.
Dependencies
| ID | Version |
|---|---|
| anyhow | ^1 |
| chrono | ^0.4 |
| serde | ^1 |
| serde_json | ^1.0.140 |
| thiserror | ^2 |
| time | ^0.3 |
| tracing | ^0.1 |
Details
2026-01-14 17:48:33 +08:00
Assets (1)
Versions (1)
View all
Cargo
3
Caisin
Caisin
27 KiB
kx-sql-0.1.3.crate
27 KiB
0.1.3
2026-01-14