4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ocaml-ormはオブジェクトをどのようにRDBに格納するのか?(その1)

Last updated at Posted at 2014-06-15

環境

  • 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__ が利用される。
  • foobar については、まぁフツーですね。

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 が参照先テーブルと思われるが... fieldbar のレコードは存在しなくて良いんだろうか?
  • variant も型名に対応するテーブルができる。
  • variant は __row__ カラムにコンストラクタ名が入り、それぞれのコンストラクタの引数が連番付きのカラムに入る(Bar__1, Xyz__1など)。コンストラクタの引数がタプルのときは更にその後ろに連番が付く(Blah__1__1, Blah__1__2 など)。この格納の仕方は Single Table Inheritance と似ていますね。
  • x.foox.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 がわかれば、なんとなく想像がつく。
tfoo の型が x で、xXtwo の型が n を引数にとり n も更に
バリアントってところと、nNfour の型が x を引数に取るので、nx
互いに参照しあっているところが注目ですね。

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__6Xthree 32 に戻ってくるので、{foo = Xtwo (Nfour (Xthree 32)) ; bar = Nfour XONE ; xyz = 'X' } が表現できていることになります。

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

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

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?