読者です 読者をやめる 読者になる 読者になる

一歩前進

プログラミングに関する雑多なメモ

OCamlでPostgreSQLにアクセスする

OCaml

シンプルなPostgreSQLOCamlライブラリがないかと探したところ、Mottl氏らによるpostgresql-ocamlというライブラリがありました。

というわけで、postgresql-ocamlの導入と簡単な使い方のご紹介です。

公式ページ

Postgresql-ocaml

インストール

OPAM経由でインストールすると楽です。

opam update
opam upgrade
opam install postgresql-ocaml

インストールしたところ、ライブラリのディレクトリ(~/.opam/4.02.0/lib)にpostgresql-ocamlpostgresqlディレクトリが作成されていました。postgresql-ocaml側が空で、本体はpostgresqlディレクトリに格納されています。

ocamlfindに登録されているか確認します。

$ ocamlfind list
bigarray            (version: [distributed with Ocaml])
postgresql          (version: 2.1.0)
threads             (version: [distributed with Ocaml])
unix                (version: [distributed with Ocaml])

なお、postgresql-ocamlはbigarray, threads(とunix)を必要とします。

サンプルデータを仕込む

適当なSQLファイルを作成します。

example.sql:

-- テーブルを作成する
CREATE TABLE customers
( id INTEGER PRIMARY KEY,
  name VARCHAR(20) NOT NULL,
  address VARCHAR(60)
);

-- サンプルデータを投入する
INSERT INTO customers (id, name, address) VALUES
    (100, '山田太郎', '東京都港区'),
    (200, '佐藤一郎', '東京都千代田区');

SQLファイルをロードします。

$ psql -U user1 example -f example.sql

ここではデータベース、テーブル、所有者を以下のようにしています。

  • データベース名: example, 所有者: user1
  • テーブル名: customers, 所有者: user1

PostgreSQLのセットアップやコマンドの使い方はこちらを参照: MacでPostgreSQL - 一歩前進

サンプルコード

前半は通常のクエリ発行で、後半はprepared statementを使用しています。

#use "topfind";;
#thread;;
#require "postgresql";;

open Printf
open Postgresql

(* DB名、ユーザ名、パスワードを指定してconnectionクラスのインスタンスを作成する *)
let conn = new connection ~dbname:"example" ~host:"localhost" ~user:"user1" ~password:"user1" ()
(* SQL文;$1, $2, ... でプレースホルダを指定できる *)
let query = "SELECT id, name, address FROM customers WHERE id = $1"

(* 取得した結果を表示する関数 *)
let show res =
  for tuple = 0 to res#ntuples - 1 do
    for field = 0 to res#nfields - 1 do
      printf "%s, " (res#getvalue tuple field)
    done;
    print_newline ()
  done

(* 受け取った文字列sをパラメータとしてクエリを発行し、結果を出力する *)
let run s = show @@ conn#exec ~expect:[Tuples_ok] ~params:[| s |] query;;
(* @@ はHaskellの$と同じく、( ) を省略するためのもの
 * ~ はラベル付き引数
 * ~expectは、結果がTuples_ok(データが返ってきた場合)以外であれば例外を出すという指定
 * ~paramsでプレースホルダにバインドするパラメータを、配列で指定
 *)

run "100";;
(* => 100, 山田太郎, 東京都港区, *)
run "200";;
(* => 200, 佐藤一郎, 東京都千代田区, *)
run "300";;
(* => (表示されない) *)


(* test1 という識別名でprepared statementを作る *)
assert ((conn#prepare "test1" query)#status = Command_ok);;

let prepared_run s name =
  show @@ conn#exec_prepared ~expect:[Tuples_ok] ~params:[|s|] name
;;

prepared_run "100" "test1";;
(* => 100, 山田太郎, 東京都港区, *)

prepared_run "200" "test1";;
(* 200, 佐藤一郎, 東京都千代田区, *)

prepared_run "300" "test1";;
(* => (表示されない) *)