刹那(せつな)の瞬き

Willkömmen! Ich heiße Setsuna. Haben Sie etwas Zeit für mich?

Ubuntuでodbc-iterクレートを試したら便利だった

前回までで、Rust の odbc クレートから SQL Server 2019 on Linux に接続して、日本語を含むクエリが処理できるようになりました。

次は日付や数値等の評価をしてみようと、データベース型の扱いについて調べてたら、odbc-iter クレートの存在を知りました。

odbc-iter クレートは、内部で odbc クレートを使用しています。
現状では、日付時刻は chrono、数値は rust_decimal の各クレートも併用してるとの事なので、それならば私が自力で寄せ集めなくても済むし、試してみようかと。

サンプルやソースコードを読むと、odbc クレートよりも簡略化して書けそうです。
以下に、比較用に書いたコードと実行結果を貼っておきます。

・Cargo.toml への追記
[dependencies]
libc = "*"
odbc-iter = { version = "0.2.5", features = ["rust_decimal"] }
odbc = "0.13.0"
ソースコード : main.rs

とりあえず、単一の値を返却するクエリで試してみます。

extern crate libc;
extern crate odbc_iter;
extern crate odbc;

use odbc_iter::{Odbc, Value};
use odbc::*;

fn main() {
    unsafe { libc::setlocale(libc::LC_ALL, std::ffi::CString::new("").unwrap().as_ptr()); }
    let conn_str = "Driver={ODBC Driver 17 for SQL Server};Server=localhost;UID=sa;PWD=abcd1234$;Database=my_test_db";
    //let conn_str = "Driver={FreeTDS};ServerName=mssql-server;UID=sa;PWD=abcd1234$;Database=my_test_db";

    // for odbc crate
    let env = create_environment_v3().unwrap();
    let conn = env.connect_with_connection_string(conn_str).unwrap();

    // for odbc-iter crate
    let mut connection = Odbc::connect(conn_str).expect("failed to connect to database");
    let mut db = connection.handle();
    
    let sql_cmds = vec![ 
        "SELECT '絵文字の笑顔は😀 ビールは\u{1F37A}です。'",    // varchar
        "SELECT N'絵文字の笑顔は😀 ビールは\u{1F37A}です。'",   // nvarchar
        "SELECT NULL",      // NULL
        "SELECT 1 + 2",     // 整数演算
        "SELECT 1.0 / 3",   // 小数演算
        "SELECT CAST('922337203685477.5807' as decimal(19,4))",
        "SELECT CAST('-922337203685477.5808' as decimal(19,4))",
]; println!("\n---- odbc crate"); { for qry in &sql_cmds { let stmt = Statement::with_parent(&conn).unwrap(); let mut stmt = match stmt.exec_direct(qry).unwrap() { Data(stmt) => stmt, NoData(_) => panic!("結果セットは空"), }; if let Some(mut cursor) = stmt.fetch().unwrap() { match cursor.get_data::<&str>(1).unwrap() { Some(val) => println!("スカラ値: {}", val), None => println!("NULL値を取得"), } } } } println!("\n---- odbc-iter crate"); { for qry in &sql_cmds { match db.query::<Option<Value>>(qry).expect("failed to run query") .single().expect("failed to fetch row") { Some(val) => println!("スカラ値: {}", val), None => println!("NULL値を取得"), } } } }

このコードでは NULL 値を扱いたいので、Option<Value>で受けてます。

NULL 値が不要なら、odbc-iter クレートのサイトに掲載されているサンプルのように、より簡潔に書けます。サンプルでは型指定になってますが、型を指定したくなければ、Valueで受けるのも可能です。

・実行結果

f:id:infinity_volts:20200506234035p:plain

小数の出力結果が少し異なりますが、ほぼ同じです。

補足

今回の件とは別に、ODBC の評価ではいつも同じようなコードを書いてます。

実は、既に odbc クレート用に評価コード書いて検証を済ませてました。
それを odbc-iter クレート用に書き換えたら、思ってた以上にデータベース操作が楽になりました。

しばらく odbc-iter クレートで過すのもアリかな、と思うくらい気に入ったので、もっと利用者が増える事を願ってます。