3个版本

0.2.7 2022年3月5日
0.2.6 2022年3月5日
0.1.3 2022年3月2日
0.1.2 2022年2月27日

#2588数据库接口

每月29次下载

GPL-3.0-or-later

155KB
4K SLoC

xql

sqlx提供的SQL查询构建器。 正在进行中

目录

  1. 基本查询构建
    1. 插入语句
    2. 选择语句
    3. 更新语句
    4. 删除语句
  2. 覆盖
    1. 表达式覆盖
    2. 表表达式覆盖
    3. SELECT和VALUES语句覆盖
  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 = 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");

看起来很冗长。这是不可避免的,因为使用gtle将与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语句覆盖

SELECTVALUES是唯一可以使用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,它只包含SelectValues枚举。为什么是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 的列或 UPDATESET 中,合格的列将变为不合格。

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 功能以及以下之一: 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 = 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_onefetch_allfetch_optional,分别带有 _as_scalar 或无后缀。

关于strString的说明

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

依赖关系

~0–14MB
~186K SLoC