ocaml-ormはオブジェクトをどのようにRDBに格納するのか？（その１）

## 環境

- MacOS X 10.9.3
- Xcode 5.1.1
- OCaml 4.01.0
- opam 1.1.1
- ocaml-orm 0.7.0
- sqlite3-ocaml 2.0.5
- ounit 2.0.0
- type_conv 109.60.01
- dyntype 0.9.0

## インストール

```
$ brew install opam
$ opam init
$ opam install orm
$ opam install ounit
```

## 方法

lib_test 以下のテストを実行して、コードとDBの対比から動きを確認してみる。
よくわからないところがあったら、自分で更にコードを書いて確かめてみる。

```
$ cd lib_test
$ make
ocamlfind ocamldep -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm suite.ml > ._d/suite.d
ocamlfind ocamldep -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm option_rec.ml > ._d/option_rec.d
ocamlfind ocamldep -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm stress_mutate.ml > ._d/stress_mutate.d
ocamlfind ocamldep -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm big_list.ml > ._d/big_list.d
ocamlfind ocamldep -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm delete.ml > ._d/delete.d
ocamlfind ocamldep -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm list_share.ml > ._d/list_share.d
ocamlfind ocamldep -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm get_set.ml > ._d/get_set.d
略
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g test_utils.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g simple.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g tuple.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g variant.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g alltypes.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g foreign.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g recursive.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g array_simple.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g foreign_and_variant.ml
ocamlfind ocamlopt -syntax camlp4o -package unix,sqlite3,oUnit,dyntype,orm.syntax,orm -c -annot -g foreign_tuple.ml
略
./run_test -verbose
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration suite_name = "anon"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration cache_filename = "/Users/tmaeda/ref/ocaml-orm-sqlite/lib_test/oUnit-
(suite_name).cache"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration display = "true"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration results_style_1_X = "true"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration verbose = "true"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration health_check_interval = "1."
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration testdata_dir = "none"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration chooser = "simple"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration output_html_dir = "none"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration processes_kill_period = "5."
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration runner = "sequential"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration shards = "2"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration processes_grace_period = "5."
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration log_encoding = "utf-8"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration output_junit_file = "none"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Configuration output_file = "none"
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Start test ORM:0:simple_order.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:0:simple_order is successful.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: End test ORM:0:simple_order.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Start test ORM:1:simple_init.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:1:simple_init is successful.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: End test ORM:1:simple_init.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Start test ORM:2:simple_id.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:2:simple_id is successful.
2014-06-15T00:32:24+00:00 tmaeda-no-MacBook-Air.local#00 I: End test ORM:2:simple_id.
略
==============
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Summary:
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:123:get is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:122:save is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:121:delete is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:120:bib_get is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:119:bib_save is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:118:bib_init is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:117:bibtex_save is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:116:bibtex_init is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:115:photo_save is successful.
2014-06-15T00:32:28+00:00 tmaeda-no-MacBook-Air.local#00 I: Test ORM:114:photo_init is successful.
```

## simple

``` ocaml:simple.mlから抜粋
type x = {
  foo: int;
  bar: string
} with
  orm
```

```
sqlite> .tables
__links__  __types__  x   
sqlite> .schema
CREATE TABLE __links__ (parent TEXT, field TEXT, child TEXT);
CREATE TABLE __types__ (n TEXT, t TEXT);
CREATE TABLE x (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, foo INTEGER,bar STRING);
CREATE TRIGGER x_update_cache AFTER DELETE ON x FOR EACH ROW BEGIN SELECT SYNC_CACHE_x(OLD.__id__); END;     
sqlite> select * from __links__;
sqlite> select * from __types__;
n           t                       
----------  ------------------------
x           E/x/{Rfoo:I:I63*bar:I:S}
sqlite> select * from x;
__id__      foo         bar        
----------  ----------  -----------
2           44          hello world
4           3           x3    
```

- `__links__` は謎。
- `__types__` は型の情報？軽くソース読んだ感じでは、create table したかどうかを判定するのに使ってそうな？
- 型名がテーブル名になる。
- PK には `__id__` が利用される。
- `foo` と `bar` については、まぁフツーですね。

## object_simple

``` ocaml:object_simple.mlから抜粋
type x = <
  foo: int;
  bar: string
> with
  orm()

let x b = object
  method foo = r
  method bar="hello "^b
end

let x1 = x "world"
let x2 = x "sky"
```


```
sqlite> .tables
__links__  __types__  x        
sqlite> .schema
CREATE TABLE __links__ (parent TEXT, field TEXT, child TEXT);
CREATE TABLE __types__ (n TEXT, t TEXT);
CREATE TABLE x (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, foo INTEGER,bar STRING);
CREATE TRIGGER x_update_cache AFTER DELETE ON x FOR EACH ROW BEGIN SELECT SYNC_CACHE_x(OLD.__id__); END;
sqlite> select * from __links__;
sqlite> select * from __types__;
n           t                       
----------  ------------------------
x           E/x/{Ofoo:I:I63*bar:I:S}
sqlite> select * from x;
__id__      foo         bar        
----------  ----------  -----------
1           82          hello world
```

- `__types__.t` の値が simple とは若干違うけど、テーブル `x` に関しては simple と同じっぽいですね。

## tuple

``` ocaml:tuple.mlから抜粋
type x = {
  foo: int32;
  bar: string * char
} with orm

let x = { foo = 1000l ; bar = ("hello",'w') }
```

```
sqlite> .tables
__links__  __types__  x        
sqlite> .schema
CREATE TABLE __links__ (parent TEXT, field TEXT, child TEXT);
CREATE TABLE __types__ (n TEXT, t TEXT);
CREATE TABLE x (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, foo INTEGER,bar__1 STRING,bar__2 INTEGER);
CREATE TRIGGER x_update_cache AFTER DELETE ON x FOR EACH ROW BEGIN SELECT SYNC_CACHE_x(OLD.__id__); END;
sqlite> select * from __links__;
sqlite> select * from __types__;
n           t                           
----------  ----------------------------
x           E/x/{Rfoo:I:I32*bar:I:(S*C)}
sqlite> select * from x;
__id__      foo         bar__1      bar__2    
----------  ----------  ----------  ----------
1           1000        hello       119       
```

へー、タプルもそもまま入れられるんですねー！

- タプルはカラム名の後ろに連番がついて入る(`bar__1`, `bar__2`)。


## variant

``` ocaml:variant.mlから抜粋
type s = 
  |Foo
  |Bar of int
  |Xyz of string
  |Blah of (int * char)
and x = {
  foo: s;
  bar: s;
} with orm

let x1 = { foo=Foo; bar=(Bar 1) }
let x2 = { foo=(Xyz "hello"); bar=Foo }
```

これは面白そう。

```
sqlite> .tables
__links__  __types__  s          x        
sqlite> .schema
CREATE TABLE __links__ (parent TEXT, field TEXT, child TEXT);
CREATE TABLE __types__ (n TEXT, t TEXT);
CREATE TABLE s (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, __row__ TEXT,Bar__1 INTEGER,Xyz__1 STRING,Blah__1__1 INTEGER,Blah__1__2 INTEGER);
CREATE TABLE x (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, foo INTEGER,bar INTEGER);
CREATE TRIGGER s_update_cache AFTER DELETE ON s FOR EACH ROW BEGIN SELECT SYNC_CACHE_s(OLD.__id__); END;
CREATE TRIGGER x_update_cache AFTER DELETE ON x FOR EACH ROW BEGIN SELECT SYNC_CACHE_x(OLD.__id__); END;
sqlite> select * from __links__;
parent      field       child     
----------  ----------  ----------
x           foo         s         
sqlite> select * from __types__;
n           t                                             
----------  ----------------------------------------------
s           E/s/<NFoo:()*Bar:(I63)*Xyz:(S)*Blah:((I63*C))>
x           E/x/{Rfoo:I:E/s/<NFoo:()*Bar:(I63)*Xyz:(S)*Bla
sqlite> select * from s;
__id__      __row__     Bar__1      Xyz__1      Blah__1__1  Blah__1__2
----------  ----------  ----------  ----------  ----------  ----------
1           Bar         1                                             
2           Foo                                                       
sqlite> select * from x;
__id__      foo         bar       
----------  ----------  ----------
1           2           1         
```

ついに `__links__` に値が入りましたね。

- `__links__` はどうやら他のテーブルへの参照関係を保持しているらしい？
- `__links__.parent` が参照元テーブル、`__links__.field` が参照元のフィールド、`__links__.child` が参照先テーブルと思われるが... `field` が `bar` のレコードは存在しなくて良いんだろうか？
- variant も型名に対応するテーブルができる。
- variant は `__row__` カラムにコンストラクタ名が入り、それぞれのコンストラクタの引数が連番付きのカラムに入る(`Bar__1`, `Xyz__1`など)。コンストラクタの引数がタプルのときは更にその後ろに連番が付く(`Blah__1__1`, `Blah__1__2` など)。この格納の仕方は Single Table Inheritance と似ていますね。
- `x.foo` や `x.bar` には `s.__id__` が入っているっぽい。

## variant_nested

``` ocaml:variant_nested.mlから抜粋
type n =
  | Non of int64
  | NTwo
  | Nthree of string
  | Nfour of x
and x =
  | XONE
  | Xtwo of n
  | Xthree of int
and t = {
  foo: x;
  bar: n;
  xyz: char;
} with orm

let t1 = {foo = XONE; bar = Nfour (Xthree 34); xyz = 'a' }
let t2 = {foo = Xtwo (Nfour XONE) ; bar = Nfour (Xthree 12) ;xyz = 'b' }
let t3 = {foo = Xtwo (Nfour (Xthree 32)) ; bar = Nfour XONE ; xyz = 'X' }
```

variant がわかれば、なんとなく想像がつく。
`t` の `foo` の型が `x` で、`x` の `Xtwo` の型が `n` を引数にとり `n` も更に
バリアントってところと、`n` の `Nfour` の型が `x` を引数に取るので、`n` と `x` は
互いに参照しあっているところが注目ですね。


```
sqlite> .tables
__links__  __types__  n          t          x        
sqlite> .schema
CREATE TABLE __links__ (parent TEXT, field TEXT, child TEXT);
CREATE TABLE __types__ (n TEXT, t TEXT);
CREATE TABLE n (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, __row__ TEXT,Non__1 INTEGER,Nthree__1 STRING,Nfour__1 INTEGER);
CREATE TABLE t (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, foo INTEGER,bar INTEGER,xyz INTEGER);
CREATE TABLE x (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, __row__ TEXT,Xtwo__1 INTEGER,Xthree__1 INTEGER);
CREATE TRIGGER n_update_cache AFTER DELETE ON n FOR EACH ROW BEGIN SELECT SYNC_CACHE_n(OLD.__id__); END;
CREATE TRIGGER t_update_cache AFTER DELETE ON t FOR EACH ROW BEGIN SELECT SYNC_CACHE_t(OLD.__id__); END;
CREATE TRIGGER x_update_cache AFTER DELETE ON x FOR EACH ROW BEGIN SELECT SYNC_CACHE_x(OLD.__id__); END;
sqlite> select * from __links__;
parent      field       child     
----------  ----------  ----------
n           Nfour__1    x         
x           Xtwo__1     n         
t           foo         x         
sqlite> select * from __types__;
n           t                                                                                
----------  ---------------------------------------------------------------------------------
n           R@n@<NNon:(I64)*NTwo:()*Nthree:(S)*Nfour:(E/x/<NXONE:()*Xtwo:(@n)*Xthree:(I63)>)>
x           R@x@<NXONE:()*Xtwo:(E/n/<NNon:(I64)*NTwo:()*Nthree:(S)*Nfour:(@x)>)*Xthree:(I63)>
t           E/t/{Rfoo:I:R@x@<NXONE:()*Xtwo:(E/n/<NNon:(I64)*NTwo:()*Nthree:(S)*Nfour:(@x)>)*X
sqlite> select * from n;
__id__      __row__     Non__1      Nthree__1   Nfour__1  
----------  ----------  ----------  ----------  ----------
1           Nfour                               1         
2           Nfour                               3         
3           Nfour                               2         
4           Nfour                               2         
5           Nfour                               6         
sqlite> select * from t;
__id__      foo         bar         xyz       
----------  ----------  ----------  ----------
1           2           1           97        
2           4           2           98        
3           5           4           88        
sqlite> select * from x;
__id__      __row__     Xtwo__1     Xthree__1 
----------  ----------  ----------  ----------
1           Xthree                  34        
2           XONE                              
3           Xthree                  12        
4           Xtwo        3                     
5           Xtwo        5                     
6           Xthree                  32    
```

- `__links__` は上に書いた通りの参照関係が入ってますね。
- `t.foo` には `x.__id__` が入る。`t.bar` には `n.__id__` が入る。
- `x.Xtwo_1` が注目で、ここが更に `n.__id__` を参照している。
- 例えば、`t.__id__` が `3` のレコードに注目すると、`t.foo=5` なので、`x.__id__` が `5` のレコードに辿り着く。すると、`__row__=Xtwo, Xtwo__1=5` だとわかるので、`n.__id__` が `5` のレコードに辿り着く。すると、`Nfour 6` だとわかり、再び `x.__id__` が `6` の `Xthree 32` に戻ってくるので、`{foo = Xtwo (Nfour (Xthree 32)) ; bar = Nfour XONE ; xyz = 'X' }` が表現できていることになります。

長くなってきたので、続きは別記事で。後は Array とか List とか Option とか Unit 辺りが気になりますね。

追記。続き → http://qiita.com/tmaeda/items/b5e2fcc6f3a0bf7faa3c