トップ «前の日記(2007-04-24) 最新 次の日記(2007-04-26)» 編集

3 日坊主日記


2007-04-25 [長年日記]

_ [Rel] Relational Ruby があるとすると

Rel は VM を持っていて

? P s WHERE status=10;

この文だと次のような opcode が積まれるのだけれど

Pending definitions:
[
  #<Session::Definition: @name=nil @operator=#<OperatorRel: @code=[
    [op: Global Var Constant Get],
    [rv: 's'],
    [op: Select],
    #<OperatorRel: @code=[
      [op: Parm Get],
      1, # depth
      0, # offset
      [op: GetTriple],
      3, # triple index (=status)
      [op: LITERAL],
      10,
      [op: EQ],
      [op: RETURN VALUE]
    ] @vc=0>,
    [op: WRITELN]
  ] @vc=0>>
]

これって結局

s.select { |tuple| tuple[:status] == 10 }

なんだよね (LINQ てゆか Python でいうところの Dee)。

じゃあ最初からこう書けばいいじゃん。

で index scan に optimize すると

s.find(:status => 10)

みたいになると。

s は関係変数であるという触れ込みなので

s = Relation.new( 型情報 )
s.insert(:code => 'S1', :status => 20, ...)

こう書けるはず (これ何て Table Data Gateway)。

型情報は

 {:code => String, :status => Fixnum}

みたいな感じで。

ようは Ruby/BDB を扱いやすくラップするだけで RDBMS 足り得るのではないか。

1周回って元に戻ってきたような。

range scan

s.find(:status => 10..20)

ここで :status => :id な index (secondary database) があるとして c: cursor とすると

c.set(10)
while assoc = c.get
  key, data = assoc
  break if key > 20
  yield assoc
end

たぶんこのように実装できる。

update

s.update(:status => 10) { |t| t[:code] == S1 }

ブロックが条件節。 SQL でもそうだけど update する前に select する必要はない。 O/R マッパ経由だと普通は select しちゃうよね。

_ [BDB] BDB handle の依存関係

これは結局 db -> txn の依存関係を管理できていないために起こる。

docs/ref/program/scope.html の依存関係を図にまとめるとこうなる。

bdb-1.gif

双方向リストで管理するなら、次のようになるはず。

bdb-2.gif

依存関係による error の例 (注: rel-ruby にある ruby-bdb):

#!/usr/bin/ruby
require '../lib/bdb'
env = BDB::Env.new('ex_txn')
env.open('./')
db = env.db_create
txn = env.txn_begin
db.open('mydb.db', nil, txn)
db.put("1", "hello", txn)
txn.commit
db.close
env.close
__END__

  7: db.open('mydb.db', nil)

ex_txn: Transaction specified for a non-transactional database
ex_txn.rb:8:in `put': bdb_put failed (BDB::Fatal)
        from ex_txn.rb:8

  8: db.put("1", "hello")

ex_txn: Transaction that opened the DB handle is still active
ex_txn.rb:8:in `put': bdb_put failed (BDB::Fatal)
        from ex_txn.rb:8

  9: db.close
 10: txn.commit

ex_txn: Locker does not exist
ex_txn.rb:10:in `commit': txn commit failed (BDB::Fatal)
        from ex_txn.rb:10

  9: #db.close

ex_txn: Locker does not exist
ex_txn: PANIC: Invalid argument
ex_txn: unable to abort transaction 0x80000001: DB_RUNRECOVERY: Fatal error, run database recovery
ex_txn: PANIC: DB_RUNRECOVERY: Fatal error, run database recovery
ex_txn: Error: closing the transaction region with active transactions
ex_txn: PANIC: fatal region error detected; run recovery
ex_txn.rb:11:in `close': DB_RUNRECOVERY: Fatal error, run database recovery (BDB::Fatal)
        from ex_txn.rb:11
ex_txn.rb:11: [BUG] Segmentation fault
ruby 1.8.6 (2007-03-13) [i386-mingw32]

最後の例は SafeEnv を使えばましになるけど db -> txn を管理できていないことに違いはない。

  3: env = BDB::SafeEnv.new('ex_txn')

ex_txn: Locker does not exist
ex_txn: PANIC: Invalid argument
../lib/bdb.rb:13:in `abort': txn abort failed (BDB::Fatal)
        from ../lib/bdb.rb:13:in `close'
        from ../lib/bdb.rb:13:in `each'
        from ../lib/bdb.rb:13:in `close'
        from ex_txn.rb:11
ex_txn: PANIC: fatal region error detected; run recovery

db -> txn

ふむ。 db.open で txn を指定すると、以降の db.get db.put db.cursor などは必ず txn 付きで呼ばないと失敗する。 そして db の txn を入れ替えることはできない。 ということは db は txn をひとつだけ保持する、として良さそうだ。

db -> txn -> cursor

デフォルトの txn を用意すれば db -> txn -> cursor だけになって話は簡単になるなあ。

[]