2个版本

0.2.9 2022年10月18日
0.2.8 2022年7月11日

#2332数据库接口

GPL-3.0-or-later

160KB
4K SLoC

qians_xql

用于sqlx的SQL查询构建器。工作正在进行中

目录

  1. 基本查询构建
    1. 插入语句
    2. 选择语句
    3. 更新语句
    4. 删除语句
  2. 通配符
    1. 在表达式上使用通配符
    2. 在表表达式上使用通配符
    3. SELECTVALUES语句上使用通配符
  3. 推导
  4. 执行
  5. 关于strString的说明

基本查询构建

假设您有一个这样的表

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 = qians_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 = qians_xql::select(["id", "title"])
    .from("book")
    .filter(qians_xql::or(qians_xql::eq("id", 1), qians_xql::eq("id", 2)))
    .order_by(qians_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 = qians_xql::update("book")
    .set("author", author)
    .filter(qians_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 = qians_xql::delete("book")
    .filter(qians_xql::eq("id", 1))
    .returning(["id", "title"]);

assert_eq!(
    delete.to_string(),
    "DELETE FROM book WHERE id = 1 RETURNING id, title",
);

通配符

qians_xql::blanket中定义的特质中存在一些通配实现,以协助查询构建。

在表达式上使用通配符

qians_xql::ops中定义的大多数expr函数都有qians_xql::blanket::ExprExt的通配实现方法。

use qians_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 = qians_xql::select(["id"]).from("book").filter(cond);
assert_eq!(query.to_string(), "SELECT id FROM book WHERE year > 1900 AND year <= 2000");

看起来很冗长。这是不可避免的,因为使用gtle将与PartialOrd冲突(即使使用no_implicit_prelude也无法禁用)。下面的代码将无法编译。

use qians_xql::blanket::ExprExt;

let cond = "year".gt(1900).and("year".le(2000));

一种解决方案是将左侧转换为Expr或使用表限定列引用。

use qians_xql::expr::Expr;
use qians_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 qians_xql::blanket::ExprExt;
use qians_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");

SELECTVALUES语句上使用通配符

只有SELECTVALUES语句可以使用UNION函数族。

use qians_xql::blanket::ResultExt;

let query = qians_xql::select([1, 2]).union(qians_xql::values([(3, 4)]));

assert_eq!(query.to_string(), "SELECT 1, 2 UNION VALUES (3, 4)");

如果您好奇,ResultExt的名字来源于qians_xql::stmt::result::Result,这是一个仅包含SelectValues枚举的类型。为什么是Result?因为命名很难,而且它在Stmt枚举定义中看起来很好。

enum Stmt {
    Insert,
    Select,
    Update,
    Delete,
    Values,
    Binary,
    Result, // See!? Exactly 6 characters! Perfectly balanced as all things should be!
}

执行

要执行这些查询,启用sqlx功能和以下之一:postgresmysqlsqlite功能。

#[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 = qians_xql::select(["id", "title"]).from("book");
    let rows = qians_xql::exec::fetch_all(query, &pool).await?;

    // sqlx::query_as(..).fetch_all
    let query = qians_xql::select(["id", "title"]).from("book");
    let rows: Vec<Output> = qians_xql::exec::fetch_all_as(query, &pool).await?;

    // sqlx::query_scalar(..).fetch_all
    let query = qians_xql::select(["id"]).from("book");
    let rows: Vec<i32> = qians_xql::exec::fetch_all_scalar(query, &pool).await?;

    // or in blanket form
    use qians_xql::blanket::StmtExt;

    let rows = qians_xql::select(["id", "title"])
        .from("book")
        .fetch_all(&pool).await?;

    let rows: Vec<Output> = qians_xql::select(["id", "title"])
        .from("book")
        .fetch_all_as(&pool)
        .await?;


    let rows: Vec<i32> = qians_xql::select(["id"])
        .from("book")
        .fetch_all_scalar(&pool).await?;

    Ok(())
}

可用变体包括:fetch_onefetch_allfetch_optional,分别带有_as_scalar或无后缀。

关于strString的说明

您可能在上面的示例中注意到多次使用&"text".to_string()。这是因为&str将转换为标识符,而&String将转换为文本字面量。

依赖关系

~0–14MB
~184K SLoC