For Development HEAD DRAFTSearch (procedure/syntax/module):

12.28 dbm - 汎用DBMインタフェース

Module: dbm

DBM系のライブラリはキーでインデックスされた値をファイルに格納する簡単な方法を 提供します。一種の永続的な連想記憶と言えるでしょう。

このモジュールが定義する抽象クラス<dbm>は、DBM系ライブラリへの 統一されたインタフェースを提供します。dbmモジュールだけをインポートすれば、 既にオープンされたデータベースを操作することができます。

データベースをオープンしたり作成したりするには、dbmインタフェースを実装した モジュールが必要になります。デフォルトのビルド時コンフィグレーションでは、 以下の実装がGaucheに含まれます。他の様々なdbmライブラリへのバインディングが 拡張パッケージとして提供されています。 それぞれのモジュールは、dbmインタフェース共通の手続きの他に、 直接実装を操作できる低レベルの手続きも提供します。 システムによっては以下のインタフェースの全てが実装されているわけではないことに 注意してください。Gaucheではシステムが提供する実装のみを定義します。

dbm.fsdbm

ファイルシステムdbm (dbm.fsdbm - ファイルシステムdbm参照).

dbm.gdbm

GDBMライブラリ (dbm.gdbm - GDBMインタフェース参照).

dbm.ndbm

NDBMライブラリ (dbm.ndbm - NDBMインタフェース参照).

dbm.odbm

DBMライブラリ (dbm.odbm - オリジナルのDBMインタフェース参照).

以下にdbmデータベースの使用例を示します。

(use dbm)         ; dbm abstract interface
(use dbm.gdbm)    ; dbm concrete interface

; open the database
(define *db* (dbm-open <gdbm> :path "mydb" :rw-mode :write))

; put the value to the database
(dbm-put! *db* "key1" "value1")

; get the value from the database
(define val (dbm-get *db* "key1"))

; iterate over the database
(dbm-for-each *db* (lambda (key val) (foo key val)))

; close the database
(dbm-close *db*)

<dbm>抽象クラスは、コレクションフレームワークと ディクショナリフレームワークを実装しています。 (それぞれgauche.collection - コレクションフレームワークgauche.dictionary - ディクショナリフレームワーク参照。)


12.28.1 DBMデータベースのオープンとクローズ

Class: <dbm>

{dbm} DBM系のデータベースのための抽象クラスです。 <dictionary>クラスを継承します (gauche.dictionary - ディクショナリフレームワーク参照)。 データベースへの共通のオペレーションを 定義します。以下のインスタンススロットを持ちます。これらのスロットの値は dbm-openによってデータベースがオープンされる前にセットされて いなければなりません。

具体クラスは、データベースの操作をより細かく行うための追加のスロット(例えばロックを 行うかどうか)を持つかもしれません。

Instance Variable of <dbm>: path

データベースファイルのパス名。dbmの実装によっては、このパスにサフィックスが追加されます。

Instance Variable of <dbm>: rw-mode

読み書きのモードを指定します。以下の値のいずれかを取ります。

:read

データベースはdbm-openによって読みだし専用モードでオープンされます。 オープンされる時点でデータベースは存在していなければなりません。

:write

データベースはdbm-openによって読み書き可能なモードでオープンされます。 データベースが存在しなければ、dbm-openは新しいデータベースを作成します。

:create

dbm-openによって新しいデータベースが作成され、読み書き可能なモードでオープンされます。 既にデータベースが存在していた場合、その内容はクリアされます。

Instance Variable of <dbm>: file-mode

データベースが作成されるときのファイルパーミッションを指定します。 デフォルトは#o664です。

Instance Variable of <dbm>: key-convert
Instance Variable of <dbm>: value-convert

デフォルトでは、dbmデータベースはキーにも値にも文字列しか使うことはできません。 これらのスロットによって、それ以外のSchemeオブジェクトを取り扱う方法を指定することが できます。以下の値のいずれかが可能です。

#f

デフォルトの値です。キーあるいは値は変換されません。それらは文字列でなければなりません。

#t

キーあるいは値はwriteを使って文字列に変換されデータベースに格納されます。 そしてreadを使って文字列からSchemeオブジェクトへと変換されます。 後でreadで読みこめるようなキーあるいは値のみを扱うことができます。 (但し、dbmライブラリは書き込み時にそれが後で読み込めるかどうかのチェックは行いません)。 キーの比較は文字列に変換された後で行われるので、同じ値となるキーは同じ文字列表現を 持つ必要があります。

二つの手続きのリスト

どちらの手続きも一つの引数を取ります。最初の手続きはSchemeオブジェクトを受け取り、 文字列を返します。キーあるいは値をデータベースに格納する時に呼ばれます。 二つ目の手続きは文字列を受け取りSchemeオブジェクトを返します。データベースから キーあるいは値を取り出す時に呼ばれます。 キーの比較は文字列に変換された後で行われるので、同じ値となるキーは同じ文字列に 変換される必要があります。

Metaclass: <dbm-meta>

{dbm} <dbm>クラス及びそのサブクラスのメタクラスです。

Method: dbm-open (dbm <dbm>)

{dbm} DBMデータベースをオープンします。dbmは、<dbm>クラスを継承した 具体クラスのインスタンスでなければなりません。また、そのスロットには適切な値が セットされている必要があります。オープンに成功したらdbm自身が返されます。 失敗した場合はエラーが報告されます。

Method: dbm-open (dbm-class <dbm-meta>) options …

{dbm} DBMインスタンスを作成してオープンするための便利なメソッドです。 次のように定義されます。

(define-method dbm-open ((class <class>) . initargs)
  (dbm-open (apply make class initargs)))

データベースファイルはガベージコレクトされる際にクローズされますが、 変更を正しくデータベースに反映するには、明示的にクローズした方が良いでしょう。

Method: dbm-close (dbm <dbm>)

{dbm} データベースdbmをクローズします。データベースがクローズされると、 それ以降のアクセスオペレーションはエラーとなります。

Method: dbm-closed? (dbm <dbm>)

{dbm} データベースdbmが既にクローズされていたら#tを返します。

Function: dbm-type->class dbmtype

{dbm} Sometimes you don’t know which type of dbm implementation you need to use in your application beforehand, but rather you need to determine the type according to the information given at run-time. This procedure fulfills the need.

The dbmtype argument is a symbol that names the type of dbm implementation; for example, gdbm for dbm.gdbm, and fsdbm for dbm.fsdbm. We assume that the dbm implementation of type foo is provided as a module dbm.foo, and its class is named as <foo>.

This procedure first checks if the required module has been loaded, and if not, it tries to load it. If the module loads successfully, it returns the class object of the named dbm implementation. If it can’t load the module, or can’t find the dbm class, this procedure returns #f.

(use dbm)

(dbm-type->class 'gdbm)
  ⇒ #<class <gdbm>>

(dbm-type->class 'nosuchdbm)
  ⇒ #f

12.28.2 DBMデータベースのアクセス

データベースがオープンされたら、以下のアクセスメソッドが使えます。

Method: dbm-put! (dbm <dbm>) key value

{dbm} 値valueをキーkeyと関連付けて保存します。

Method: dbm-get (dbm <dbm>) key :optional default

{dbm} キーkeyに関連付けられた値を返します。もし値が存在しなければ、defaultが 与えられていればそれを返し、そうでなければエラーを報告します。

Method: dbm-exists? (dbm <dbm>) key

{dbm} キーkeyに関連付けられた値が存在すれば#tを返します。

Method: dbm-delete! (dbm <dbm>) key

{dbm} キーkeyに関連付けられた値を消去します。値が存在しない場合は何もしません。


12.28.3 DBMデータベース上の繰り返し処理

全データベースを渡り歩く処理のために、以下のメソッドが用意されています。

Method: dbm-fold (dbm <dbm>) procedure knil

{dbm} 基本的な繰り返し処理です。データベース内の各キー/値のペアに関して、手続き procedure(procedure key value r), のように呼ばれます。ここでrは、最初のprocedureの呼び出しの時にはknil が、以降の呼び出しの時にはその直前のprocedureが返した値が渡されます。 最後のprocedureの戻り値がdbm-foldの戻り値となります。 データベース中にデータがひとつもなければknilがそのまま返されます。

次の例は、データベース中の整数の値を全て加算します。

(dbm-fold dbm (lambda (k v r) (if (integer? v) (+ v r) r)) 0)
Method: dbm-for-each (dbm <dbm>) procedure

{dbm} データベース内の各キー/値のペアに関して、手続きprocedureを呼び出します。 procedureにはキーと値が渡されます。procedureの戻り値は捨てられます。

Method: dbm-map (dbm <dbm>) procedure

{dbm} データベース内の各キー/値のペアに関して、手続きprocedureを呼び出します。 procedureにはキーと値が渡されます。procedureの戻り値はリストに 集められてdbm-mapの戻り値となります。


12.28.4 DBMデータベースインスタンスの管理

各DBM実装は、データベースを格納するのに独自の方法を使います。 レガシーなDBMは、pathスロットの値にそれぞれ.dir.pagを付けた名前の2つのファイルを使います。 fsdbmpathの下にディレクトリを作ります。 DBMデータベースが他のデータベースサーバによってバック アップされる場合は、pathはそのサーバで単なるキーと して使われるでしょう。

以下のメソッドは、そのようなバリエーションを隠し、 データベースそれ自体を管理する簡易な方法を提供します。 最初の引数に、具体的なDBMデータベースを実装している クラスを渡す必要があります。

Generic Function: dbm-db-exists? class name

{dbm} nameで指定されたclassクラスのデータベースが 存在する場合は#tを返します。

;; Returns #t if testdb.dir and testdb.pag exist
(dbm-db-exists? <odbm> "testdb")
Generic Function: dbm-db-remove class name

{dbm} nameで指定されるclassクラスのデータベース 全体を削除します。

Generic Function: dbm-db-copy class from to

{dbm} fromで指定されたclassクラスのデータベースを toへコピーします。 classのdbm実装がロックをサポートしている限り、fromの一貫性は 保たれます (つまり、コピー中に他のプロセスがfromを 変更しようとした場合であっても、toが壊れたデータベースになることは ありません)。 もしコピー先のtoが既に存在するデータベースで あった場合、toの元の内容は失われます。 コピーが中断された場合にtoが不完全な状態のままになるかどうかは dbm実装に依存します。dbm実装の多くはトランザクショナルな振る舞い、 すなわち、コピーが失敗した場合に元のtoを復元することを試みます。 しかし確実な操作のためには、コピーが失敗した場合には呼び出し側でtoの 状態を確認することが必要です。

(dbm-db-copy <gdbm> "testdb.dbm" "backup.dbm")
Generic Function: dbm-db-move class from to

{dbm} fromで指定されたclassクラスのデータベースを toへ移動、あるいはリネームします。dbm-db-copyと同じく、 classのdbm実装がロックをサポートしていれば データベースの一貫性は保証されます。移動先のtoが既に 存在していた場合、その元の内容は失われます。


12.28.5 DBMデータベースのダンプとリストア

Most dbm implementations use some kind of binary format, and some of them are architecture dependent. That makes it difficult to pass around dbm databases between different machines. A safe way is to write out the content of a dbm database into some portable format on the source machine, and rebuild another dbm database from it on the destination machine.

The operation is so common that Gauche provides convenience scripts that does the job. They are installed into the standard Gauche library directory, so it can be invoked by gosh <scriptname>.

To write out the content of a dbm database named by dbm-name, you can use dbm/dump script:

$ gosh dbm/dump [-o outfile][-t type] dbm-name

The outfile argument names the output file. If omitted, the output is written out to stdout. The type argument specifies the implementation type of the dbm database; e.g. gdbm or fsdbm. The program calls dbm-type->class (see DBMデータベースのオープンとクローズ) on the type argument to load the necessary dbm implementation.

The dumped format is simply a series of S-expressions, each of which is a dotted pair of string key and string value. Character encodings are assumed to be the same as gosh’s native character encoding.

The dumped output may contain S-expressions other than dotted pair of strings to include meta information. For now, programs that deals with dumped output should just ignore S-expressions other than dotted pairs.

To read back the dumped dbm format, you can use dbm/restore script:

$ gosh dbm/restore [-i infile][-t type] dbm-name

The infile argument names the dumped file to be read. If omitted, it reads from stdin. The type argument specifies the dbm type, as in dbm/dump script. The dbm-name argument names the dbm database; if the database already exists, its content is cleared, so be careful.


12.28.6 DBM実装を書く

When you write an extension module that behaves like a persistent hashtable, it is a good idea to adapt it to the dbm interface, so that the application can use the module in a generic way.

The minimum procedures to conform the dbm interface are as follow:

Besides above, you may define the following methods.

It is generally recommended to name the implementation module as dbm.foo, and the class of the implementation as <foo>. With this convention it is easier to write an application that dynamically loads and uses dbm implementation specified at runtime.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT