9个版本
0.3.1 | 2024年3月11日 |
---|---|
0.3.0 | 2023年2月10日 |
0.2.5 | 2022年4月22日 |
0.2.3 | 2022年2月22日 |
0.1.0 | 2019年3月13日 |
#2630 in 数据库接口
在 3 crates 中使用
53KB
997 行
include-sql 是一个用于在Rust中 使用 SQL的宏。
include-sql 受到 Yesql 的启发。它允许程序员以SQL编写SQL查询,将它们与Rust代码分开,并通过此库提供的proc-macro轻松地将它们嵌入到Rust程序中。
仅凭自身,include-sql实际上做得很少 - 它读取并解析SQL文件,将其转换为对 impl_sql
宏的调用。预计 impl_sql
将由使用include-sql的项目或外部库提供。例如,有几个include-sql配套crates,如 include-postgres-sql、include-sqlite-sql 和 include-oracle-sql,它们实现了 impl_sql
。如果它们的SQL嵌入方法被认为适当且方便,则可以直接使用。或者,它们可以用作实现您自己的 impl_sql
的起点。
示例
由于include-sql不打算直接使用,我们将使用 include-sqlite-sql 来说明工作流程。
将 include-sqlite-sql
作为依赖项添加
[dependencies]
include-sqlite-sql = "0.2"
编写您的SQL并将其保存到文件中。例如,以下内容将保存为项目 sql
文件夹中的 library.sql
-- name: get_loaned_books?
-- Returns the list of books loaned to a patron
-- # Parameters
-- param: user_id: &str - user ID
SELECT book_title
FROM library
WHERE loaned_to = :user_id
ORDER BY 1
/
-- name: loan_books!
-- Updates the book record to reflect loan to a patron
-- # Parameters
-- param: book_titles: &str - book titles
-- param: user_id: &str - user ID
UPDATE library
SET loaned_to = :user_id
, loaned_on = current_timestamp
WHERE book_title IN (:book_titles)
/
注意 参数顺序由
param
声明定义。
然后在Rust中使用它
use include_sqlite_sql::{include_sql, impl_sql};
use rusqlite::{Result, Connection};
include_sql!("sql/library.sql");
fn main() -> Result<()> {
let db = Connection::open("library.db")?;
db.loan_books(&["Where the Sidewalk Ends", "A Wrinkle in Time", "Dune"], "Penny Teller")?;
db.get_loaned_books("Leonard Hofstadter", |row| {
let book_title : &str = row.get_ref("book_title")?.as_str()?;
println!("{book_title}");
Ok(())
})?;
Ok(())
}
注意 SQL文件的路径必须是相对于项目根目录的,即相对于
CARGO_MANIFEST_DIR
,即使您将SQL文件与包含它的rust模块放在同一目录下。因为include-sql针对稳定版Rust,此要求将持续到 SourceFile 稳定。
内部机制
在解析和验证SQL文件的 include-sql
内容后,将生成以下调用
impl_sql!{ LibrarySql =
{
? get_loaned_books (: user_id (&str))
" Returns the list of books loaned to a patron\n # Parameters\n * `user_id` - user ID"
$ "SELECT book_title\n FROM library\n WHERE loaned_to = " :user_id "\n ORDER BY 1"
},
{
! loan_books (# book_titles (&str) : user_id (&str))
" Updates the book records to reflect loan to a patron\n # Parameters\n * `user_id` - user ID\n * `book_titles` - book titles"
$ "UPDATE library\n SET loaned_to = " : user_id "\n, loaned_on = current_timestamp\n WHERE book_title IN (" # book_titles ")"
}
}
它将 include_sqlite_sql::impl_sql
转换为以下实现
trait LibrarySql {
/// Returns the list of books loaned to a patron
/// # Parameters
/// * `user_id` - user ID
fn get_loaned_books<F>(&self, user_id: &str, row_callback: F) -> rusqlite::Result<()>
where F: FnMut(&rusqlite::Row) -> rusqlite::Result<()>;
/// Updates the book records to reflect loan to a patron
/// # Parameters
/// * `book_titles` - book titles
/// * `user_id` - user ID
fn loan_books(&self, book_ids: &[&str], user_id: &str) -> rusqlite::Result<usize>;
}
当然,它还实现了这个特性
impl LibrarySql for rusqlite::Connection {
/// ...
}
文档
包含的文档描述了支持的SQL文件格式,并提供了编写自己的impl_sql
宏的说明。
💥 0.3版本中的重大更改
- 生成的方法的参数顺序由
param
描述符的顺序定义。这是一个潜在的破坏性更改,因为之前生成的方法的参数顺序遵循SQL语句中参数的顺序。当SQL语句头部不使用param
描述符时,则生成的泛型方法参数将根据它们在SQL语句中的出现顺序进行排序。 - 语句以斜杠
/
结尾,而不是分号;
。这是为了允许声明和使用SQLite的语句批次以及Oracle的PL/SQL块。请注意,当语句是文件中的最后一个语句或其后跟有另一个语句时,语句终止符是可选的,因为头部将自动终止前面的语句。
依赖项
~3.5–4.5MB
~88K SLoC