SQLite
SQLite データベースを作成する
rusqlite クレートを使用して SQLite データベースを開きます。Windows でのコンパイルについては、
クレート を参照してください。
Connection::open は、データベースがまだ存在しない場合、それを作成します。
use rusqlite::{Connection, Result};
fn main() -> Result<()> {
let conn = Connection::open("cats.db")?;
conn.execute(
"create table if not exists cat_colors (
id integer primary key,
name text not null unique
)",
(),
)?;
conn.execute(
"create table if not exists cats (
id integer primary key,
name text not null,
color_id integer not null references cat_colors(id)
)",
(),
)?;
Ok(())
}
データの挿入と選択
Connection::open は、前のレシピで作成したデータベース cats を開きます。
このレシピでは、Connection の execute メソッドを使用して、cat_colors テーブルと cats テーブルにデータを挿入します。まず、cat_colors テーブルにデータを挿入します。色のレコードが挿入された後、Connection の last_insert_rowid メソッドを使用して、最後に挿入された色の id を取得します。この id は、cats テーブルにデータを挿入する際に使用されます。次に、prepare メソッドを使用して select クエリを準備します。これにより statement 構造体が得られます。続いて、statement の query_map メソッドを使用してクエリを実行します。
use rusqlite::{params, Connection, Result};
use std::collections::HashMap;
#[derive(Debug)]
struct Cat {
name: String,
color: String,
}
fn main() -> Result<()> {
let conn = Connection::open("cats.db")?;
let mut cat_colors = HashMap::new();
cat_colors.insert(String::from("Blue"), vec!["Tigger", "Sammy"]);
cat_colors.insert(String::from("Black"), vec!["Oreo", "Biscuit"]);
for (color, catnames) in &cat_colors {
conn.execute(
"INSERT INTO cat_colors (name) VALUES (?1)",
[color],
)?;
let last_id = conn.last_insert_rowid();
for cat in catnames {
conn.execute(
"INSERT INTO cats (name, color_id) values (?1, ?2)",
params![cat, last_id],
)?;
}
}
let mut stmt = conn.prepare(
"SELECT c.name, cc.name FROM cats c
INNER JOIN cat_colors cc
ON cc.id = c.color_id;",
)?;
let cats = stmt.query_map([], |row| {
Ok(Cat {
name: row.get(0)?,
color: row.get(1)?,
})
})?;
for cat in cats {
if let Ok(found_cat) = cat {
println!(
"Found cat {:?} {} is {}",
found_cat,
found_cat.name,
found_cat.color,
);
}
}
Ok(())
}
トランザクションの使用
Connection::open は、冒頭のレシピにある cats.db データベースを開きます。
トランザクションは Connection::transaction で開始します。トランザクションは
Transaction::commit で明示的にコミットしない限りロールバックされます。
次の例では、色名に一意制約があるテーブルに 色を追加します。重複する色を挿入しようとすると、 トランザクションはロールバックされます。
use rusqlite::{Connection, Result};
fn main() -> Result<()> {
let mut conn = Connection::open("cats.db")?;
successful_tx(&mut conn)?;
let res = rolled_back_tx(&mut conn);
assert!(res.is_err());
Ok(())
}
fn successful_tx(conn: &mut Connection) -> Result<()> {
let tx = conn.transaction()?;
tx.execute("delete from cat_colors", [])?;
tx.execute("insert into cat_colors (name) values (?1)", ["lavender"])?;
tx.execute("insert into cat_colors (name) values (?1)", ["blue"])?;
tx.commit()
}
fn rolled_back_tx(conn: &mut Connection) -> Result<()> {
let tx = conn.transaction()?;
tx.execute("delete from cat_colors", [])?;
tx.execute("insert into cat_colors (name) values (?1)", ["lavender"])?;
tx.execute("insert into cat_colors (name) values (?1)", ["blue"])?;
tx.execute("insert into cat_colors (name) values (?1)", ["lavender"])?;
tx.commit()
}