環境
- 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
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
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
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
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
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 辺りが気になりますね。