QDBM付属Ruby用API仕様書

Copyright (C) 2000-2006 Mikio Hirabayashi
Last Update: Thu, 26 Oct 2006 15:00:20 +0900

目次

  1. 概要
  2. インストール
  3. サンプルコード
  4. バグ

概要

QDBMにはRuby言語用のAPIがある。QDBMの基本APIと拡張APIと上級APIの関数群をRubyのクラス機構を用いてカプセル化したものである。また、Rubyのマルチスレッド機能においてスレッドセーフである。

クラス `Depot' か `Curia' か `Villa' のコンストラクタ `new' を呼び出してデータベースを開き、その戻り値のオブジェクトをハンドルにする。データベースを閉じるにはメソッド `close' を呼ぶ。ファイナライザは利用されないが、`new' のイテレータを用いることでデータベースの閉じ忘れを防ぐことができる。メソッド `put' はレコードを追加するために用いる。メソッド `out' はレコードを削除するために用いる。メソッド `get' はレコードを検索するために用いる。その他にも、C言語のAPIとほぼ同じ操作を利用することができる。各クラスは例外クラス `EANY' およびそのサブクラスを定数として持つ。それらは各メソッドが失敗した際に投げられる。

これらのクラスは `Enumerable' モジュールをMix-inしているので、`find' や `sort' といったメソッドが利用できる。また、`Hash' クラスに似せて '[]=' や '[]' 等のメソッドを定義しているので、普通のハッシュのように利用することができる。

データベースに格納するレコードのキーと値は文字列として扱われるが、バイナリデータをそのまま格納することも可能である。ただし、`Villa' は直列化可能かつ比較可能な任意のオブジェクトを格納することができる。`Depot' はファイルを用いてハッシュデータベースを実現し、`Curia' はディレクトリと複数のファイルを用いてハッシュデータベースを実現し、`Villa' はファイルを用いてB+木データベースを実現する。`Depot' は最も高速である。`Curia' は最もスケーラブルである。`Villa' はカーソルによって順序に基づく参照を可能にする。

`put' で既存のレコードの上書きがキャンセルされた際や `get' で存在しないレコードが検索された際には例外によって操作の失敗が通知されるが、それが鬱陶しい場合は `silent' フラグを真にするとよい。その場合は失敗が戻り値によって通知される。

APIの詳細に関しては、サブディレクトリ `rbapidoc' の文書を参照すること。


インストール

Rubyの1.6.5以降のバージョンがインストールされ、QDBMが `/usr/local' 以下にインストールされていることが必要である。

インストール作業は、サブディレクトリ `ruby' をカレントディレクトリにして行う。

cd ruby

ビルド環境を設定する。

./configure

プログラムをビルドする。

make

プログラムの自己診断テストを行う。

make check

プログラムをインストールする。作業は `root' ユーザで行う。

make install

一連の作業が終ると、Rubyのインストールディレクトリに応じた適当な場所に `depot.rb'、`mod_depot.so' 、`curia.rb' 、`mod_curia.so' 、`villa.rb' 、`mod_villa.so' 等のライブラリがインストールされ、コマンド `rbdptest' と `rbcrtest' と `rbvltest' が `/usr/local/bin' にインストールされる。

アンインストールするには、`./configure' をした後の状態で以下のコマンドを実行する。作業は `root' ユーザで行う。

make uninstall

サンプルコード

名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。

require 'depot'

NAME = "mikio"
NUMBER = "000-1234-5678"
DBNAME = "book"

def main
  depot = nil
  begin

    # データベースを開く
    depot = Depot::new(DBNAME, Depot::OWRITER | Depot::OCREAT)

    # レコードを格納する
    depot.put(NAME, NUMBER)

    # レコードを取得する
    printf("Name: %s\n", NAME)
    printf("Number: %s\n", depot.get(NAME))

  rescue Depot::EANY
    printf("%s\n", $!)
    return 1
  ensure

    # データベースを閉じる
    if(depot)
      begin
        depot.close()
      rescue Depot::EANY
        printf("%s\n", $!)
      end
    end

  end
  return 0
end

exit(main());

上記の例を `Hash' クラスに似せたインタフェースとイテレータを用いて書き直した例を以下に示す。

require 'depot'

NAME = "mikio"
NUMBER = "000-1234-5678"
DBNAME = "book"

def main
  begin

    # データベースを開いて自動的に閉じる
    Depot::new(DBNAME, Depot::OWRITER | Depot::OCREAT) do |depot|

      # レコードを格納する
      depot[NAME] = NUMBER

      # レコードを取得する
      printf("Name: %s\n", NAME)
      printf("Number: %s\n", depot[NAME])

    end

  rescue Depot::EANY
    printf("%s\n", $!)
    return 1
  end
  return 0
end

exit(main());

`Villa' クラスを用いて文字列の前方一致検索を行う例を以下に示す。

require 'villa'

DBNAME = "words"
PREFIX = "apple"

def main
  begin

    # データベースを開いて自動的に閉じる
    Villa::new(DBNAME, Villa::OWRITER | Villa::OCREAT) do |villa|

      # レコードを格納する
      villa.put("applet", "little application", Villa::DDUP)
      villa.put("aurora", "polar wonderwork", Villa::DDUP)
      villa.put("apple", "delicious fruit", Villa::DDUP)
      villa.put("amigo", "good friend", Villa::DDUP)
      villa.put("apple", "big city", Villa::DDUP)

      begin

        # カーソルを候補の先頭に置く
        villa.curjump(PREFIX)

        # カーソルを走査する
        while(true)
          key = villa.curkey()
          (key.index(PREFIX) == 0) || break
          val = villa.curval()
          printf("%s: %s\n", key, val)
          villa.curnext()
        end

      rescue Villa::ENOITEM
      end

    end

  rescue Villa::EANY
    printf("%s\n", $!)
    return 1
  end
  return 0
end

exit(main());

バグ

標準添付ライブラリの `DBM' クラスのインタフェースと微妙に違うところがある。

もしもRubyハッカー達の手にかかれば、より効率的な実装がなされるだろう。

インタフェースを簡潔にするため、Ruby用のCuriaにはラージオブジェクトを扱う機能はない。