Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SQLite

SQLite データベースを作成する

rusqlite-badge cat-database-badge

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(())
}

データの挿入と選択

rusqlite-badge cat-database-badge

Connection::open は、前のレシピで作成したデータベース cats を開きます。 このレシピでは、Connectionexecute メソッドを使用して、cat_colors テーブルと cats テーブルにデータを挿入します。まず、cat_colors テーブルにデータを挿入します。色のレコードが挿入された後、Connectionlast_insert_rowid メソッドを使用して、最後に挿入された色の id を取得します。この id は、cats テーブルにデータを挿入する際に使用されます。次に、prepare メソッドを使用して select クエリを準備します。これにより statement 構造体が得られます。続いて、statementquery_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(())
}

トランザクションの使用

rusqlite-badge cat-database-badge

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()
}