1 个不稳定版本

0.1.0 2020 年 5 月 10 日

#87#query-builder

MIT 许可证

10KB
169

compose-sql

(真实名称待定...)

这是一个实验性的 SQL 查询构建器,它更倾向于使 SQL 查询可重用和可组合,而不是保证在编译时进行类型安全的查询。我发现,为了在编译时捕捉无效查询所需的高级类型系统技术通常会损害可用性,使得组合和重用查询变得困难。在所有地方使用原始 SQL 对于可用性来说很好,但它使得无法组合查询,结果是你不得不经常重复自己。

它被视为你已使用的 ORM/查询构建器的补充。实际运行 SQL 查询不在此项目的范围之内。

不言而喻,这非常是一个概念验证,不应该用于任何严肃的事情。

示例用法

// Declare our tables. The macro syntax and what is expands to is similar to Diesel.
//
// It defines empty structs for our tables and columns and implements a couple of
// traits to make the DSL methods work.
table! {
    users {
        id -> Integer,
        name -> Text,
        country_id -> Integer,
    }
}

table! {
    countries {
        id -> Integer,
        name -> Text,
    }
}

// Just a place to group user related queries
struct UserQueries;

impl UserQueries {
    // Notice this should just returns a `Query`
    // No complex generics getting in the way
    fn named_bob() -> Query {
        users::table.filter(users::name.eq("Bob"))
    }

    fn in_country_named(name: &str) -> Query {
        users::table.inner_join(
            countries::table.on(countries::id
                .eq(users::country_id)
                .and(countries::name.eq(name))),
        )
    }
}

let (query, mut binds) = UserQueries::named_bob()
    // Columns to select are applied last and is what triggers SQL generation
    // The returns the raw SQL and an iterator over the bind params
    .select(users::star);
assert_eq!(
    query,
    r#"SELECT "users".* FROM "users" WHERE "users"."name" = $1"#
);
assert_eq!(
    binds.collect::<Vec<_>>(),
    vec![Bind::String("Bob".to_string())]
);

let (query, mut binds) = UserQueries::in_country_named("Denmark")
    // we can also narrow our selects
    .select((users::id, countries::id));
assert_eq!(
    query,
    r#"SELECT "users"."id", "countries"."id" FROM "users" INNER JOIN "countries" ON "countries"."id" = "users"."country_id" AND "countries"."name" = $1"#
);
assert_eq!(
    binds.collect::<Vec<_>>(),
    vec![Bind::String("Denmark".to_string())]
);

// Having defined the queries separately we're now able to "merge" them together
//
// `Query::merge` makes a new query with all the "joins" and all the "wheres" of both
// queries
let (query, mut binds) = UserQueries::named_bob()
    .merge(UserQueries::in_country_named("Denmark"))
    .select((users::star, countries::star));
assert_eq!(
    query,
    r#"SELECT "users".*, "countries".* FROM "users" INNER JOIN "countries" ON "countries"."id" = "users"."country_id" AND "countries"."name" = $1 WHERE "users"."name" = $2"#
);
assert_eq!(
    binds.collect::<Vec<_>>(),
    vec![
        Bind::String("Denmark".to_string()),
        Bind::String("Bob".to_string()),
    ]
);

// I'm trying to optimize for easy of use, rather than type safety SQL generation. So
// this compiles just fine but wouldn't work at runtime. I recommend you test your queries
// :)
users::table.select(countries::star);

依赖项

~465KB