Rust SQLite FFI绑定库libsql-ffi的使用,实现高效安全的嵌入式数据库操作与SQLite交互
Rust SQLite FFI绑定库libsql-ffi的使用,实现高效安全的嵌入式数据库操作与SQLite交互
安装
在项目目录中运行以下Cargo命令:
cargo add libsql-ffi
或者在Cargo.toml中添加以下行:
libsql-ffi = "0.9.19"
完整示例代码
以下是一个使用libsql-ffi进行SQLite数据库操作的完整示例:
use libsql_ffi::{Database, Statement};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 打开或创建数据库
let db_path = Path::new("example.db");
let db = Database::open(db_path)?;
// 创建表
let create_table_sql = "CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)";
db.execute(create_table_sql, ())?;
// 插入数据
let insert_sql = "INSERT INTO users (name, email) VALUES (?, ?)";
let mut stmt = db.prepare(insert_sql)?;
stmt.bind(1, "Alice")?;
stmt.bind(2, "alice@example.com")?;
stmt.execute()?;
// 查询数据
let select_sql = "SELECT id, name, email FROM users";
let mut stmt = db.prepare(select_sql)?;
while stmt.step()? {
let id: i64 = stmt.column_int(0)?;
let name: String = stmt.column_text(1)?;
let email: String = stmt.column_text(2)?;
println!("User: id={}, name={}, email={}", id, name, email);
}
// 更新数据
let update_sql = "UPDATE users SET email = ? WHERE name = ?";
let mut stmt = db.prepare(update_sql)?;
stmt.bind(1, "new.alice@example.com")?;
stmt.bind(2, "Alice")?;
stmt.execute()?;
// 删除数据
let delete_sql = "DELETE FROM users WHERE name = ?";
let mut stmt = db.prepare(delete_sql)?;
stmt.bind(1, "Alice")?;
stmt.execute()?;
Ok(())
}
主要特性
- 安全绑定:通过FFI安全地与SQLite C API交互
- 线程安全:正确处理SQLite的线程安全模式
- 事务支持:提供BEGIN/COMMIT/ROLLBACK操作
- 预编译语句:支持参数化查询,防止SQL注入
- 错误处理:完善的错误处理机制
文档
更多详细用法请参考官方文档。该库遵循MIT许可证。
所有者
- tursodatabase/publish团队
- Glauber Costa
- Pekka Enberg
1 回复
Rust SQLite FFI绑定库libsql-ffi的使用指南
简介
libsql-ffi是一个Rust语言的SQLite FFI绑定库,它提供了高效、安全的嵌入式数据库操作能力,允许Rust程序与SQLite数据库进行交互。这个库特别适合需要在Rust项目中嵌入轻量级数据库的场景。
主要特性
- 完全兼容SQLite3的C API
- 提供内存安全的Rust接口
- 支持事务操作
- 线程安全设计
- 支持预编译语句
- 错误处理完善
安装方法
在Cargo.toml中添加依赖:
[dependencies]
libsql-ffi = "0.1" # 请使用最新版本
基本使用方法
1. 打开和关闭数据库
use libsql_ffi::{Database, OpenFlags};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 打开数据库(如果不存在则创建)
let db = Database::open(":memory:", OpenFlags::SQLITE_OPEN_READWRITE | OpenFlags::SQLITE_OPEN_CREATE)?;
// 执行SQL语句
db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)", ())?;
// 数据库会在离开作用域时自动关闭
Ok(())
}
2. 执行查询和获取结果
use libsql_ffi::{Database, Statement};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = Database::open("test.db", OpenFlags::SQLITE_OPEN_READWRITE | OpenFlags::SQLITE_OPEN_CREATE)?;
// 插入数据
db.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Alice", 30))?;
db.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Bob", 25))?;
// 准备查询语句
let mut stmt = db.pprepare("SELECT id, name, age FROM users WHERE age > ?")?;
// 绑定参数并执行查询
let rows = stmt.query(&[&26])?;
// 遍历结果
for row in rows {
let row = row?;
let id: i64 = row.get(0)?;
let name: String = row.get(1)?;
let age: i64 = row.get(2)?;
println!("User {}: {}, {} years old", id, name, age);
}
Ok(())
}
3. 使用事务
use libsql_ffi::{Database, TransactionBehavior};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = Database::open("test.db", OpenFlags::SQLITE_OPEN_READWRITE)?;
// 开始事务
let tx = db.transaction(TransactionBehavior::Immediate)?;
// 在事务中执行操作
tx.execute("UPDATE users SET age = age + 1 WHERE name = ?", ("Alice",))?;
tx.execute("UPDATE users SET age = age + 1 WHERE name = ?", ("Bob",))?;
// 提交事务
tx.commit()?;
Ok(())
}
高级用法
1. 自定义SQL函数
use libsql_ffi::{Database, FunctionFlags, Value};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = Database::open(":memory:", OpenFlags::SQLITE_OPEN_READWRITE)?;
// 注册自定义函数
db.create_scalar_function(
"square",
1,
FunctionFlags::SQLITE_UTF8,
|ctx, args| {
let value = args[0].as_integer()?;
ctx.result(value * value)
},
)?;
// 使用自定义函数
let result: i64 = db.query_row("SELECT square(5)", (), |row| row.get(0))?;
println!("5 squared is {}", result); // 输出: 5 squared is 25
Ok(())
}
2. 使用预编译语句提高性能
use libsql_ffi::{Database, Statement};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = Database::open("test.db", OpenFlags::SQLITE_OPEN_READWRITE)?;
// 准备预编译语句
let mut stmt = db.prepare("INSERT INTO users (name, age) VALUES (?, ?)")?;
// 批量插入数据
let users = [("Charlie", 28), ("David", 35), ("Eve", 22)];
for (name, age) in &users {
stmt.execute(&[name, age])?;
}
Ok(())
}
错误处理
libsql-ffi提供了详细的错误信息:
use libsql_ffi::{Database, Error};
fn main() {
match Database::open("/nonexistent/path.db", OpenFlags::SQLITE_OPEN_READONLY) {
Ok(_) => println!("Database opened successfully"),
Err(Error::CannotOpen(_)) => eprintln!("Failed to open database file"),
Err(e) => eprintln!("Database error: {}", e),
}
}
性能提示
- 对于频繁执行的查询,使用预编译语句(Statement)
- 批量操作时使用事务
- 考虑使用内存数据库(:memory:)进行临时数据处理
- 合理使用索引提高查询性能
完整示例代码
下面是一个完整的示例,展示了如何使用libsql-ffi进行数据库操作:
use libsql_ffi::{Database, OpenFlags, Statement, TransactionBehavior};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 打开数据库
let db = Database::open("demo.db", OpenFlags::SQLITE_OPEN_READWRITE | OpenFlags::SQLITE_OPEN_CREATE)?;
// 2. 创建表
db.execute(
"CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL,
stock INTEGER
)",
(),
)?;
// 3. 使用事务批量插入数据
let tx = db.transaction(TransactionBehavior::Immediate)?;
{
let mut stmt = tx.prepare("INSERT INTO products (name, price, stock) VALUES (?, ?, ?)")?;
let products = [
("Laptop", 999.99, 10),
("Phone", 699.99, 15),
("Tablet", 399.99, 20),
];
for (name, price, stock) in products {
stmt.execute(&[name, price, stock])?;
}
}
tx.commit()?;
// 4. 查询数据
let mut stmt = db.prepare("SELECT id, name, price, stock FROM products WHERE stock > ?")?;
let rows = stmt.query(&[&10])?;
println!("Products with stock > 10:");
for row in rows {
let row = row?;
let id: i64 = row.get(0)?;
let name: String = row.get(1)?;
let price: f64 = row.get(2)?;
let stock: i64 = row.get(3)?;
println!("ID: {}, Name: {}, Price: ${:.2}, Stock: {}", id, name, price, stock);
}
Ok(())
}
这个完整示例展示了:
- 数据库的创建和打开
- 表的创建
- 使用事务进行批量插入
- 使用预编译语句进行查询
- 结果集的遍历和处理
libsql-ffi为Rust开发者提供了与SQLite交互的高效安全方式,特别适合嵌入式应用和需要轻量级数据库解决方案的场景。