1 个不稳定版本
0.1.0 | 2019年3月13日 |
---|
#2811 在 数据库接口
3KB
include-sql 是一个Rust中使用SQL的宏。
include-sql 受到 Yesql 的启发。它允许程序员用SQL编写SQL查询,将它们与Rust代码分离,并通过该库提供的proc-macro轻松地将它们嵌入到Rust程序中。
仅凭自身,include-sql 实际上做得很少——它读取并解析SQL文件,将其转换为对 impl_sql
宏的调用。预计 impl_sql
由使用include-sql的项目或外部库提供。例如,有几个include-sql配套crate,如 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块声明和使用语句批处理。注意,当语句是文件中的最后一个语句,或者其后跟有另一个语句,该语句的头部将自动结束前一个语句时,语句终止符是可选的。