3个版本
0.2.7 | 2022年3月5日 |
---|---|
0.2.6 | 2022年3月5日 |
0.1.3 |
|
0.1.2 |
|
#2588 在 数据库接口
每月29次下载
155KB
4K SLoC
xql
为sqlx提供的SQL查询构建器。 正在进行中
目录
基本查询构建
假设你有一个这样的表
CREATE TABLE book(
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
author TEXT,
lang TEXT,
year SMALLINT
);
CRUD(或ISUD与sql缩写)将看起来像这样
INSERT
语句。
let book1 = "The Fellowship of the Rings".to_string();
let auth1 = "J. R. R. Tolkien".to_string();
let book2 = "Dune".to_string();
let auth2 = "Frank Herbret".to_string();
let english = "English".to_string();
let values = [
(1_i32, &book1, &auth1, &english, 1954_i16),
(2_i32, &book2, &auth2, &english, 1965_i16),
];
let insert = xql::insert("book", ["id", "title", "author", "lang", "year"])
.values(values)
.returning(["id"]);
assert_eq!(
insert.to_string(),
"INSERT INTO book(id, title, author, lang, year) VALUES \
(1, 'The Fellowship of the Rings', 'J. R. R. Tolkien', 'English', 1954), \
(2, 'Dune', 'Frank Herbret', 'English', 1965) \
RETURNING id",
);
SELECT
语句。
let select = xql::select(["id", "title"])
.from("book")
.filter(xql::or(xql::eq("id", 1), xql::eq("id", 2)))
.order_by(xql::desc("year"));
assert_eq!(
select.to_string(),
"SELECT id, title FROM book WHERE id = 1 OR id = 2 ORDER BY year DESC"
);
UPDATE
语句。
let author = &"Frank Herbert".to_string();
let update = xql::update("book")
.set("author", author)
.filter(xql::eq("id", 2))
.returning(["id"]);
assert_eq!(
update.to_string(),
"UPDATE book SET author = 'Frank Herbert' WHERE id = 2 RETURNING id",
);
DELETE
语句。
let delete = xql::delete("book")
.filter(xql::eq("id", 1))
.returning(["id", "title"]);
assert_eq!(
delete.to_string(),
"DELETE FROM book WHERE id = 1 RETURNING id, title",
);
覆盖
在xql::blanket
中定义的一些特性有覆盖实现,用于辅助查询构建。
表达式覆盖
大多数在xql::ops
中定义的expr函数都有xql::blanket::ExprExt
的覆盖实现方法。
use xql::blanket::ExprExt;
let cond = "year".greater_than(1900).and("year".less_equal(2000));
assert_eq!(cond.to_string(), "year > 1900 AND year <= 2000");
let query = xql::select(["id"]).from("book").filter(cond);
assert_eq!(query.to_string(), "SELECT id FROM book WHERE year > 1900 AND year <= 2000");
看起来很冗长。这是不可避免的,因为使用gt
或le
将与PartialOrd
(即使使用no_implicit_prelude
也无法禁用)冲突。下面这个将无法编译。
use xql::blanket::ExprExt;
let cond = "year".gt(1900).and("year".le(2000));
一种解决方案是将左侧转换为Expr
或使用表限定列引用。
use xql::expr::Expr;
use xql::blanket::ExprExt;
let year = Expr::from("year");
let qualified = ("book", "year");
let cond = year.gt(1900).and(qualified.le(2000));
assert_eq!(cond.to_string(), "year > 1900 AND book.year <= 2000");
表表达式覆盖
join
系列函数有一些覆盖实现。
use xql::blanket::ExprExt;
use xql::blanket::TableExprExt;
let table = "book".join("category", ("book", "category_id").eq(("category", "id")));
assert_eq!(table.to_string(), "book JOIN category ON book.category_id = category.id");
SELECT和VALUES语句覆盖
SELECT
和VALUES
是唯一可以使用UNION
系列函数的语句。
use xql::blanket::ResultExt;
let query = xql::select([1, 2]).union(xql::values([(3, 4)]));
assert_eq!(query.to_string(), "SELECT 1, 2 UNION VALUES (3, 4)");
关于ResultExt
的命名,它来自xql::stmt::result::Result
,它只包含Select
和Values
枚举。为什么是Result
?好吧,因为命名很难,它在Stmt
枚举定义中看起来不错。
enum Stmt {
Insert,
Select,
Update,
Delete,
Values,
Binary,
Result, // See!? Exactly 6 characters! Perfectly balanced as all things should be!
}
推导
您可以通过启用 derive
功能来使查询构建看起来更短或更美观。
use xql::Schema;
#[derive(Schema)]
struct Book {
id: i32,
title: String,
author: Option<String>,
lang: Option<String>,
year: Option<i32>,
}
let shorter = xql::select(Book::columns()).from(Book::table());
assert_eq!(shorter.to_string(), "SELECT book.id, book.title, book.author, book.lang, book.year FROM book");
let nicer = xql::select([Book::id, Book::title, Book::author, Book::lang, Book::year]).from(Book);
assert_eq!(nicer.to_string(), "SELECT book.id, book.title, book.author, book.lang, book.year FROM book");
assert_eq!(shorter, nicer);
在 INSERT
的列或 UPDATE
的 SET
中,合格的列将变为不合格。
use xql::Schema;
use xql::blanket::ExprExt;
#[derive(Schema)]
struct Book {
id: i32,
title: String,
author: Option<String>,
lang: Option<String>,
year: Option<i32>,
}
let values = [(&"Dune".to_string(),)];
let insert = xql::insert(Book, [Book::title]).values(values);
assert_eq!(insert.to_string(), "INSERT INTO book(title) VALUES ('Dune')");
let author = "Frank Herbert".to_string();
let update = xql::update(Book).set(Book::author, &author).filter(Book::id.eq(2));
assert_eq!(update.to_string(), "UPDATE book SET author = 'Frank Herbert' WHERE book.id = 2");
执行
要执行这些查询,请启用 sqlx
功能以及以下之一: postgres
、mysql
或 sqlite
功能。
#[derive(sqlx::FromRow)]
struct Output {
id: i32,
title: String,
}
#[cfg(feature = "postgres")]
async fn execute(pool: sqlx::Pool::<sqlx::Postgres>) -> Result<(), sqlx::Error> {
// sqlx::query(..).fetch_all
let query = xql::select(["id", "title"]).from("book");
let rows = xql::exec::fetch_all(query, &pool).await?;
// sqlx::query_as(..).fetch_all
let query = xql::select(["id", "title"]).from("book");
let rows: Vec<Output> = xql::exec::fetch_all_as(query, &pool).await?;
// sqlx::query_scalar(..).fetch_all
let query = xql::select(["id"]).from("book");
let rows: Vec<i32> = xql::exec::fetch_all_scalar(query, &pool).await?;
// or in blanket form
use xql::blanket::StmtExt;
let rows = xql::select(["id", "title"])
.from("book")
.fetch_all(&pool).await?;
let rows: Vec<Output> = xql::select(["id", "title"])
.from("book")
.fetch_all_as(&pool)
.await?;
let rows: Vec<i32> = xql::select(["id"])
.from("book")
.fetch_all_scalar(&pool).await?;
Ok(())
}
可用变体有: fetch_one
、fetch_all
、fetch_optional
,分别带有 _as
、_scalar
或无后缀。
关于str
和String
的说明
您可能在上面的示例中注意到多个使用 &""text.to_string()
的地方。这是因为 &str
将变成一个标识符,而 &String
将变成一个文本字面量。
依赖关系
~0–14MB
~186K SLoC