この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Introduction
Rustでsqliteを操作するためのcrateはたくさんありますが、
今回はRusqliteというcrateを使って
Sqliteにアクセスしてみました。
RustからPostgreSQLにアクセスするcrate、rust-postgresをベースに作成されており、
似たようなインターフェイスで使うことが可能になっています。
Environment
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 11.3.1
- rust : 1.61.0
Try
Sqliteのセットアップ
まずはsqlite3をHomebrewでインストールします。
% brew install sqlite3
インストールできたらexample_db.db3と名前指定して起動。
% sqlite3 example_db.db3
sqlite>
テーブル作成とテストデータを登録しておきましょう。
create table if not exists person (
id integer primary key,
department text not null,
name text not null unique,
salary integer not null
);
insert into person values(1,'development','Taro',120);
insert into person values(2,'development','Hanako',200);
insert into person values(3,'development','Syuta',260);
insert into person values(4,'sales','Mike',160);
insert into person values(5,'sales','Takeshi',240);
insert into person values(6,'sales','Kyousuke',100);
insert into person values(7,'sales','Teru',220);
insert into person values(8,'management','Hide',400);
insert into person values(9,'management','Marry',200);
insert into person values(10,'management','Kim',300);
Rust実装
Cargoでプロジェクト作成後、Cargo.tomlでライブラリの指定をします。
% cargo new rusqlite-example
% cd rusqlite-example
[dependencies]
rusqlite = "*"
main.rsにsqliteへアクセスするためのコードを書いていきます。
まずはuseと構造体の定義。
さきほどcreate tableしたpersonテーブルに対応する構造体を定義します。
avg_salaryは、部署ごとの平均salaryを示すフィールドです。
use rusqlite::{params, Connection, Result};
#[derive(Debug)]
struct Person{
id: u16,
department:String,
name: String,
salary: u32,
avg_salary:Option<f32>
}
次はコネクション取得関数を定義。
Connection::openでsqliteファイルを渡せばOK。
なお、Connection::open_in_memory関数を使えばインメモリで起動します。
fn open_my_db() -> Result<Connection,rusqlite::Error> {
let path = "./example_db.db3";
let con = Connection::open(&path)?;
println!("{}", con.is_autocommit());
Ok(con)
}
データinsert用関数はこんな感じです。
シンプル。
fn insert_person(con:&Connection,p:&Person) -> Result<usize,rusqlite::Error> {
return Ok(con.execute(
"insert into person (department,name, salary) values (?1, ?2,?3)",
params![p.department,p.name, p.salary]
)?);
}
personテーブル全件取得してprintlnする関数です。
query_mapでPerson配列をつくってます。
fn select_all(con:&Connection){
let mut stmt = con.prepare("select id,department,name,salary from person").unwrap();
let persons = stmt.query_map(params![], |row| {
Ok(Person {
id: row.get(0).unwrap(),
department: row.get(1).unwrap(),
name: row.get(2).unwrap(),
salary: row.get(3).unwrap(),
avg_salary:None
})
}).unwrap();
for p in persons {
println!("{:?}", p.unwrap());
}
}
sqliteをすっごく久しぶりにさわったので、window関数が実装されてたのとか知りませんでした。
(3.25から実装とのこと)
こんな感じでsql書いて、departmentごとの平均salaryを各レコードに計算してます。
fn select_window(con:&Connection) {
let mut stmt = con.prepare("select id,department,name,salary,avg(salary) over defw from person window defw as (partition by department)").unwrap();
let persons = stmt.query_map(params![], |row| {
Ok(Person {
id: row.get_unwrap(0),
department: row.get_unwrap(1),
name: row.get_unwrap(2),
salary: row.get_unwrap(3),
avg_salary:Some(row.get_unwrap(4))
})
}).unwrap();
for p in persons {
println!("{:?}", p.unwrap());
}
}
fn main() {
let con = open_my_db().unwrap();
select_window(&con);
}
Summary
とても簡単にsqliteにアクセスできました。
sqliteは今後お世話になる可能性があるので、使い方をおさらいしておきたいところです。