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

[ocaml-ormはどのようにオブジェクトをRDBに格納するのか？（その１）](http://qiita.com/tmaeda/items/98305789c9a4e266c0e4) の続きです。


## foreign

RDB の一対多の最もシンプルな形ですね。

``` ocaml:foreign.mlから抜粋
type t = {
  foo: string;
  bar: int64;
  xyz: char;
} and x = {
  first: t;
  second: t;
  third: int;
} with orm (
 unique: t<xyz>, t<bar>;
 index: x<first,second>
)

let s1 = { foo="hello"; bar=100L; xyz='a' }
let s2 = { foo="world"; bar=200L; xyz='z' }
let x = { first=s1; second=s2; third=111 }
```

```
sqlite> .tables
__links__  __types__  t          x        
sqlite> .schema
CREATE TABLE __links__ (parent TEXT, field TEXT, child TEXT);
CREATE TABLE __types__ (n TEXT, t TEXT);
CREATE TABLE t (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, foo STRING,bar INTEGER,xyz INTEGER);
CREATE TABLE x (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, first INTEGER,second INTEGER,third INTEGER);
CREATE UNIQUE INDEX idx_t_bar ON t (bar);
CREATE UNIQUE INDEX idx_t_xyz ON t (xyz);
CREATE INDEX idx_x_first_second ON x (first,second);
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     
----------  ----------  ----------
x           first       t         
sqlite> select * from __types__;
n           t                               
----------  --------------------------------
t           E/t/{Rfoo:I:S*bar:I:I64*xyz:I:C}
x           E/x/{Rfirst:I:E/t/{Rfoo:I:S*bar:
sqlite> select * from t;
__id__      foo         bar         xyz       
----------  ----------  ----------  ----------
1           world       200         122       
2           hello       100         97        
sqlite> select * from x;
__id__      first       second      third     
----------  ----------  ----------  ----------
1           2           1           111    
```

- `unique: t<xyz>` で `t` の `xyz` にユニークインデックスが生成される。
- `index: x<first,second>` で複合インデックス。
- `x.first` も `x.second` も `t.__id__` を参照している。実にシンプル。なんだけど、`second` が `t` を参照してるってのはどうやってわかるんだろうなぁ？ `__links__` に情報がないわけで。すると残るは `__types__` しかないんだが、ソース軽く読んだ感じではそういう箇所見当たらないんだよなぁ。


## recursive

``` ocaml:recursive.mlから抜粋
type y = char with orm

type t = {
  t1: string;
  t2: x option
} and x = {
  x1: t option;
  x2: y
} with orm

type z = t with orm

type a = { a : y } with orm


let vy = 'a'
let rec vt = { t1= "hello"; t2=(Some vx) }
and vx = { x1=(Some vt); x2=vy }
let vz = vt
let va = { a = vy }
```

これはテストが全部終わった段階でレコードが全部消えてしまうように
なっているので、最後の delete のテストを削除してビルド＆実行し直した。

おそらく主旨としては、`x` が `t` を参照し、`t` も `x` を参照する
というところなんだろうけど、これ既に variant_nested のところで
やったので、だいたい想像つきますね。

それよりは、option 型が出てくるのが初めてなので、そこに注目したい。

```
sqlite> .tables
__links__  __types__  t          x          y        
sqlite> .schema
CREATE TABLE __links__ (parent TEXT, field TEXT, child TEXT);
CREATE TABLE __types__ (n TEXT, t TEXT);
CREATE TABLE t (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, t1 STRING,t2__0__isset INTEGER,t2__0 INTEGER);
CREATE TABLE x (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, x1__0__isset INTEGER,x1__0 INTEGER,x2 INTEGER);
CREATE TABLE y (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, val INTEGER);
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;
CREATE TRIGGER y_update_cache AFTER DELETE ON y FOR EACH ROW BEGIN SELECT SYNC_CACHE_y(OLD.__id__); END;
sqlite> select * from __links__;
parent      field       child     
----------  ----------  ----------
x           x2          y         
t           t2__0       x         
x           x1__0       t         
sqlite> select * from __types__;
n           t         
----------  ----------
y           E/y/C     
t           R@t@{Rt1:I
x           R@x@{Rx1:I
sqlite> select * from t;
__id__      t1          t2__0__isset  t2__0     
----------  ----------  ------------  ----------
1           hello       1             1         
sqlite> select * from x;
__id__      x1__0__isset  x1__0       x2        
----------  ------------  ----------  ----------
1           1             1           1         
sqlite> select * from y;
__id__      val       
----------  ----------
1           97   
```

- option 型は NULL を使うのかと思っていたが、どうやら `__isset` というカラムに値の有無を表すフラグを立てているっぽい。
- 参照関係は variant_nested と同じですね。`t.t2__0` が `1` で `x.__id__` が `1` のレコードを参照。`x.x1__0` が `1` で `t.__id__` が `1` のレコードを参照。という形で互いに参照しあっている。

## array_simple

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

let t1 = { foo=[|1|]; bar="t1" }
let t2 = { foo=[|1;2;3|]; bar="t2" }
let t3 = { foo=[||]; bar="t3" }
```

```
sqlite> .tables
__links__  __types__  s          s__foo__0
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, foo INTEGER,bar STRING);
CREATE TABLE s__foo__0 (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, __next__ INTEGER,__next_chunk__ INTEGER,__size__ INTEGER,val INTEGER);
CREATE UNIQUE INDEX idx_s__foo__0_enum ON s__foo__0 (__id__,__next__);
CREATE TRIGGER s_foo_cleanup AFTER UPDATE OF foo ON s FOR EACH ROW BEGIN SELECT CLEAN_UP_s__foo__0(OLD.foo,NEW.foo); END;
CREATE TRIGGER s_update_cache AFTER DELETE ON s FOR EACH ROW BEGIN SELECT SYNC_CACHE_s(OLD.__id__); END;
sqlite> select * from __links__;
parent      field       child     
----------  ----------  ----------
s           foo         s__foo__0 
sqlite> select * from __types__;
n           t         
----------  ----------
s__foo__0   !I63|     
s           E/s/{Rfoo:
sqlite> select * from s;
__id__      foo         bar       
----------  ----------  ----------
1           1           t1        
2           4           t2        
3                       t3        
sqlite> select * from s__foo__0;
__id__      __next__    __next_chunk__  __size__    val       
----------  ----------  --------------  ----------  ----------
1                                       1           1         
2                                       1           3         
3           2                           2           2         
4           3                           3           1    
```

- array 管理用に自動的に `s__foo__0` というテーブルが作成され、ここに array のデータが入るらしい。
- 空array に対しては NULL が入っているっぽい。
- `s__foo__0.val` が array 内の値。
- `s__foo__0.__next__` が array 内の次のレコードの `s__foo__0.__id__` を指す。つまり、`s.__id__` が `2` のレコードに着目すると、
  - `s.foo=4` なので、まずは `s__foo__0.__id__ = 4` のレコードに辿り着く(`val = 1`)。
  - すると、`__next__ = 3` なので、`s__foo__0.__id__ = 3` のレコードに辿り着く(`val = 2`)。
  - すると、`__next__ = 2` なので、`s__foo__0.__id__ = 2` のレコードに辿り着く(`val = 3`)。
  - すると、`__next__ = NULL` なので、ここで array はおしまい。
- `__size__` と `__next_chunk__` がなぜ必要なのか謎だけど、軽くソースを grep した感じだと、array を取ってくるために `s__foo__0` をひたすら自己参照で JOIN しまくっているらしいんだけど、長い array だと JOIN が大きくなりすぎるので、64 個ずつに区切って取得してくるのに使っているっぽい。うーん、あんまり RDBMS の機能を活かしてないマッピングな気がするが、良いんだろうか？


## list_simple

``` ocaml:list_simple.mlから抜粋
type x = {
  foo: string list;
  bar: (char * string) list;
  pla: bool;
} with orm(debug:none)

let x1 = { foo=["a1";"a2";"a3";"a4"]; bar=[ ('a',"AA"); ('b',"BB"); ('c',"CC") ] ; pla=true }
```

list と array で何か違いがあるのか。タプルの list はどうなるのか、
辺りに注目ですね。

```
sqlite> .tables
__links__  __types__  x          x__bar__0  x__foo__0
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 INTEGER,pla INTEGER);
CREATE TABLE x__bar__0 (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, __next__ INTEGER,__next_chunk__ INTEGER,__size__ INTEGER,__1 INTEGER,__2 STRI
NG);
CREATE TABLE x__foo__0 (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, __next__ INTEGER,__next_chunk__ INTEGER,__size__ INTEGER,val STRING);
CREATE UNIQUE INDEX idx_x__bar__0_enum ON x__bar__0 (__id__,__next__);
CREATE UNIQUE INDEX idx_x__foo__0_enum ON x__foo__0 (__id__,__next__);
CREATE TRIGGER x_bar_cleanup AFTER UPDATE OF bar ON x FOR EACH ROW BEGIN SELECT CLEAN_UP_x__bar__0(OLD.bar,NEW.bar); END;
CREATE TRIGGER x_foo_cleanup AFTER UPDATE OF foo ON x FOR EACH ROW BEGIN SELECT CLEAN_UP_x__foo__0(OLD.foo,NEW.foo); 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           bar         x__bar__0 
x           foo         x__foo__0 
sqlite> select * from __types__;
n           t         
----------  ----------
x__bar__0   [(C*S)]   
x__foo__0   [S]       
x           E/x/{Rfoo:
sqlite> select * from x;
__id__      foo         bar         pla       
----------  ----------  ----------  ----------
1           4           3           1         
sqlite> select * from x__bar__0;
__id__      __next__    __next_chunk__  __size__    __1         __2       
----------  ----------  --------------  ----------  ----------  ----------
1                                       1           99          CC        
2           1                           2           98          BB        
3           2                           3           97          AA        
sqlite> select * from x__foo__0;
__id__      __next__    __next_chunk__  __size__    val       
----------  ----------  --------------  ----------  ----------
1                                       1           a4        
2           1                           2           a3        
3           2                           3           a2        
4           3                           4           a1  
```

- array と同じっぽい。
- タプルの array だと `__1` とか `__2` というカラム名になる。

## alltypes

``` ocaml:alltypes.mlから抜粋
type x = {
  one: char;
  two: string;
  three: int;
  four: int32;
  five: bool;
  six: int64;
  seven: unit;
  eight: string option;
  nine: float;
  ten: (int * string);
  eleven: string list;
  twelve: (char * int32 * unit) option;
  thirteen: (char * (string * int64) option);
}
and y=int with orm

let x = { one='a'; two="foo"; three=1; four=2l; 
     five=true; six=3L; seven=(); eight=(Some "bar");
     nine=6.9; ten=(100,"hello"); eleven=["aa";"bb";"cc"];
     twelve=(Some ('t',9l,())); thirteen=('d', (Some ("abc",999L))) }
```

後は unit ぐらいがわかれば十分かなという感じがするので、
unit を使っている seven と twelve だけ見ますよ。

```
sqlite> .tables
__links__     __types__     x             x__eleven__0
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, one INTEGER,two STRING,three INTEGER,four INTEGER,five INTEGER,six INTEGER,seven INTE
GER,eight__0__isset INTEGER,eight__0 STRING,nine FLOAT,ten__1 INTEGER,ten__2 STRING,eleven INTEGER,twelve__0__isset INTEGER,twelve__0__1 INTEGE
R,twelve__0__2 INTEGER,twelve__0__3 INTEGER,thirteen__1 INTEGER,thirteen__2__0__isset INTEGER,thirteen__2__0__1 STRING,thirteen__2__0__2 INTEGE
R);
CREATE TABLE x__eleven__0 (__id__ INTEGER PRIMARY KEY AUTOINCREMENT, __next__ INTEGER,__next_chunk__ INTEGER,__size__ INTEGER,val STRING);
CREATE UNIQUE INDEX idx_x__eleven__0_enum ON x__eleven__0 (__id__,__next__);
CREATE TRIGGER x_eleven_cleanup AFTER UPDATE OF eleven ON x FOR EACH ROW BEGIN SELECT CLEAN_UP_x__eleven__0(OLD.eleven,NEW.eleven); 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           eleven      x__eleven__0
sqlite> select * from __types__;
n             t         
------------  ----------
x__eleven__0  [S]       
x             E/x/{Rone:

sqlite> select seven, twelve__0__isset, twelve__0__1, twelve__0__2, twelve__0__3 from x;
seven       twelve__0__isset  twelve__0__1  twelve__0__2  twelve__0__3
----------  ----------------  ------------  ------------  ------------
0           1                 116           9             0  
```

- unit は 0 を入れてるっぽい？

以上、おしまい。

foreign は RDBMS のフツーの使い方だと思うんだけど、array とか list は「オブジェクトのシリアライズ先がたまたま RDB でした」って感じで全然 RDB っぽくないと思った。