#sql-query #postgresql #macro #compile-time #syntax #compact

compact_sql

用于将使用Rust语法的Postgres SQL查询压缩(并可选项地在编译时进行检查)的宏。

5个版本

新版本 0.0.5 2024年8月13日
0.0.4 2024年5月3日
0.0.3 2023年5月4日
0.0.2 2022年1月28日
0.0.1 2021年12月20日

#851 in 数据库接口

MIT/Apache

63KB
1.5K SLoC

Compact SQL

一个简单的宏,允许编写Postgres的SQL查询,这些查询将在编译时进行压缩(并可选项地检查)。

依赖项

[dependencies]
compact_sql = "0.0.5"

漂亮的错误

如果可以使用指向具体错误范围的指针获得更好的错误,请使用 pretty-errors 功能(需要不稳定Rust版本)。

[dependencies]
compact_sql = { version = "0.0.5", features = ["pretty-errors"] }

使用Compact SQL

仅生成SQL字符串

use compact_sql::pg_sql;

fn main() {
    let sql = pg_sql! {
        SELECT
            relname,
            relnamespace,
            format("%I", oid),
        FROM
            pg_catalog.pg_class
        WHERE
            oid = {var_oid}::oid
    };
    assert_eq!(
        sql,
        "SELECT relname,relnamespace,format('%I',oid)FROM pg_catalog.pg_class WHERE oid=$1::oid"
    );
}

注意!关键字之间允许有空格(对于VCS更方便),花括号中的命名参数将被替换为Postgres编号参数(相同名称具有相同编号)。

如"SELECT"、"AS"等常见关键字(作为警告)需要大写。

生成带SQL参数的SQL字符串和结构体

宏将Postgres的"命名参数"替换为编号参数,合理地创建具有与参数名称相同的属性名称的结构体。此外,使用Rust的类型轻松约束这些参数。

use compact_sql::pg_sql;

pg_sql! {
    impl OidFromPgClass {
        SELECT
            oid,
            relkind,
        FROM
            pg_catalog.pg_class
        WHERE
            relname = {name: String}
            AND
            relnamespace = {nsp: u32}
    }
}


fn main() {
    let name: String = "pg_attribute".to_string();
    let params = OidFromPgClass {
        nsp:  &11,
        name: &name,
    };
    assert_eq!(OidFromPgClass::PARAM_NAMES, &["name", "nsp"]);
    assert_eq!(
        OidFromPgClass::SQL_TEXT,
        concat!(
            "-- OidFromPgClass\n",
            "SELECT oid,relkind FROM pg_catalog.pg_class WHERE relname=$1 AND relnamespace=$2",
        ),
    );
    assert_eq!(params.params().len(), 2);
}

生成带SQL参数的结构体和特性实现

此库可以为想要构建与生成的结构体一起工作的库(或内部函数)的人生成特性实现体。并非所有用户结构体都设计为公开,因此生成的特性实现不会在公共crate中引用。相反,从本地作用域使用特性名称。但是,您仍然可以使用来自公共 compact_sql_traits crate的特性。

您可以指定预期的行数,并根据需要使用不同的特性。此外,您必须声明返回行类型。您可以使用 PgResultRow derive宏来生成必要的实现。

use compact_sql::{pg_sql, PgResultRow};
use compact_sql_traits::*;
use futures_util::{pin_mut, StreamExt, TryStreamExt};
use tokio_postgres::Client;

type PgResult<T> = Result<T, tokio_postgres::Error>;

#[derive(Debug, PgResultRow)]
struct PgClassItem {
    relname:      String,
    relnamespace: u32,
}

#[derive(Debug)]
enum QueriesId {
    GetPgClassItem,
}

pg_sql! {
        // You can add container attributes like derive to the generated struct:
        #[derive(Debug)]
        impl QueriesId::GetPgClassItem for ?PgClassItem {
            SELECT
                relname,
                relnamespace,
            FROM
                pg_catalog.pg_class
            WHERE
                oid = {class_oid}
        }
}

async fn query_opt<Q: QueryMaybeOne>(db: &Client, params: Q) -> PgResult<Option<Q::ResultRow>>
where <Q as QueryMaybeOne>::SqlIdType: std::fmt::Debug
{
    dbg!(Q::SQL_ID);
    let query_result = db
        .query_raw(Q::SQL_TEXT, params.params())
        .await?;
    let future = query_result.map_ok(Q::ResultRow::from_row);
    pin_mut!(future);

    Ok(match future.next().await {
        None => None,
        Some(v) => Some(v??),
    })
}

async fn func(db: &Client) -> PgResult<()> {
    let name: String = "pg_attribute".to_string();
    let params = GetPgClassItem {
        class_oid: &11,
    };
    let res: Option<PgClassItem> = query_opt(db, params).await?;
    dbg!(res);

    Ok(())
}

您可以在 test_trait_impl.rs 中查看更多示例。

测试SQL查询

如果您想检查查询(通过"准备"),您可以定义DBMS的ENV

  • TEST_PG_HOST
  • TEST_PG_PORT(可选;默认:5432)
  • TEST_PG_USER(可选;默认:"postgres")
  • TEST_PG_PASS(可选;默认:"")
  • TEST_PG_DATB

许可证

许可以下任一项

任选其一。

贡献

除非您明确声明,否则根据 Apache-2.0 许可证定义的,您有意提交以包含在作品中的任何贡献,将按照上述方式双重许可,不附加任何额外条款或条件。

依赖

~7–16MB
~226K SLoC