dbm
- 汎用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
- ディクショナリフレームワーク参照。)
• DBMデータベースのオープンとクローズ: | ||
• DBMデータベースのアクセス: | ||
• DBMデータベース上の繰り返し処理: | ||
• DBMデータベースインスタンスの管理: | ||
• DBMデータベースのダンプとリストア: | ||
• DBM実装を書く: |
{dbm
}
DBM系のデータベースのための抽象クラスです。
<dictionary>
クラスを継承します (gauche.dictionary
- ディクショナリフレームワーク参照)。
データベースへの共通のオペレーションを
定義します。以下のインスタンススロットを持ちます。これらのスロットの値は
dbm-open
によってデータベースがオープンされる前にセットされて
いなければなりません。
具体クラスは、データベースの操作をより細かく行うための追加のスロット(例えばロックを 行うかどうか)を持つかもしれません。
<dbm>
: path ¶データベースファイルのパス名。dbmの実装によっては、このパスにサフィックスが追加されます。
<dbm>
: rw-mode ¶読み書きのモードを指定します。以下の値のいずれかを取ります。
:read
データベースはdbm-open
によって読みだし専用モードでオープンされます。
オープンされる時点でデータベースは存在していなければなりません。
:write
データベースはdbm-open
によって読み書き可能なモードでオープンされます。
データベースが存在しなければ、dbm-open
は新しいデータベースを作成します。
:create
dbm-open
によって新しいデータベースが作成され、読み書き可能なモードでオープンされます。
既にデータベースが存在していた場合、その内容はクリアされます。
<dbm>
: file-mode ¶データベースが作成されるときのファイルパーミッションを指定します。
デフォルトは#o664
です。
<dbm>
: key-convert ¶<dbm>
: value-convert ¶デフォルトでは、dbmデータベースはキーにも値にも文字列しか使うことはできません。 これらのスロットによって、それ以外のSchemeオブジェクトを取り扱う方法を指定することが できます。以下の値のいずれかが可能です。
#f
デフォルトの値です。キーあるいは値は変換されません。それらは文字列でなければなりません。
#t
キーあるいは値はwrite
を使って文字列に変換されデータベースに格納されます。
そしてread
を使って文字列からSchemeオブジェクトへと変換されます。
後でread
で読みこめるようなキーあるいは値のみを扱うことができます。
(但し、dbmライブラリは書き込み時にそれが後で読み込めるかどうかのチェックは行いません)。
キーの比較は文字列に変換された後で行われるので、同じ値となるキーは同じ文字列表現を
持つ必要があります。
どちらの手続きも一つの引数を取ります。最初の手続きはSchemeオブジェクトを受け取り、 文字列を返します。キーあるいは値をデータベースに格納する時に呼ばれます。 二つ目の手続きは文字列を受け取りSchemeオブジェクトを返します。データベースから キーあるいは値を取り出す時に呼ばれます。 キーの比較は文字列に変換された後で行われるので、同じ値となるキーは同じ文字列に 変換される必要があります。
{dbm
}
<dbm>クラス及びそのサブクラスのメタクラスです。
{dbm
}
DBMデータベースをオープンします。dbmは、<dbm>
クラスを継承した
具体クラスのインスタンスでなければなりません。また、そのスロットには適切な値が
セットされている必要があります。オープンに成功したらdbm自身が返されます。
失敗した場合はエラーが報告されます。
{dbm
}
DBMインスタンスを作成してオープンするための便利なメソッドです。
次のように定義されます。
(define-method dbm-open ((class <class>) . initargs) (dbm-open (apply make class initargs)))
データベースファイルはガベージコレクトされる際にクローズされますが、 変更を正しくデータベースに反映するには、明示的にクローズした方が良いでしょう。
<dbm>
) ¶{dbm
}
データベースdbmをクローズします。データベースがクローズされると、
それ以降のアクセスオペレーションはエラーとなります。
<dbm>
) ¶{dbm
}
データベースdbmが既にクローズされていたら#t
を返します。
{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
データベースがオープンされたら、以下のアクセスメソッドが使えます。
<dbm>
) key value ¶{dbm
}
値valueをキーkeyと関連付けて保存します。
<dbm>
) key :optional default ¶{dbm
}
キーkeyに関連付けられた値を返します。もし値が存在しなければ、defaultが
与えられていればそれを返し、そうでなければエラーを報告します。
<dbm>
) key ¶{dbm
}
キーkeyに関連付けられた値が存在すれば#t
を返します。
<dbm>
) key ¶{dbm
}
キーkeyに関連付けられた値を消去します。値が存在しない場合は何もしません。
全データベースを渡り歩く処理のために、以下のメソッドが用意されています。
<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)
<dbm>
) procedure ¶{dbm
}
データベース内の各キー/値のペアに関して、手続きprocedureを呼び出します。
procedureにはキーと値が渡されます。procedureの戻り値は捨てられます。
<dbm>
) procedure ¶{dbm
}
データベース内の各キー/値のペアに関して、手続きprocedureを呼び出します。
procedureにはキーと値が渡されます。procedureの戻り値はリストに
集められてdbm-map
の戻り値となります。
各DBM実装は、データベースを格納するのに独自の方法を使います。
レガシーなDBMは、pathスロットの値にそれぞれ.dirと
.pagを付けた名前の2つのファイルを使います。
fsdbm
はpathの下にディレクトリを作ります。
DBMデータベースが他のデータベースサーバによってバック
アップされる場合は、pathはそのサーバで単なるキーと
して使われるでしょう。
以下のメソッドは、そのようなバリエーションを隠し、 データベースそれ自体を管理する簡易な方法を提供します。 最初の引数に、具体的なDBMデータベースを実装している クラスを渡す必要があります。
{dbm
}
nameで指定されたclassクラスのデータベースが
存在する場合は#t
を返します。
;; Returns #t if testdb.dir and testdb.pag exist (dbm-db-exists? <odbm> "testdb")
{dbm
}
nameで指定されるclassクラスのデータベース
全体を削除します。
{dbm
}
fromで指定されたclassクラスのデータベースを
toへコピーします。
classのdbm実装がロックをサポートしている限り、fromの一貫性は
保たれます (つまり、コピー中に他のプロセスがfromを
変更しようとした場合であっても、toが壊れたデータベースになることは
ありません)。 もしコピー先のtoが既に存在するデータベースで
あった場合、toの元の内容は失われます。
コピーが中断された場合にtoが不完全な状態のままになるかどうかは
dbm実装に依存します。dbm実装の多くはトランザクショナルな振る舞い、
すなわち、コピーが失敗した場合に元のtoを復元することを試みます。
しかし確実な操作のためには、コピーが失敗した場合には呼び出し側でtoの
状態を確認することが必要です。
(dbm-db-copy <gdbm> "testdb.dbm" "backup.dbm")
{dbm
}
fromで指定されたclassクラスのデータベースを
toへ移動、あるいはリネームします。dbm-db-copy
と同じく、
classのdbm実装がロックをサポートしていれば
データベースの一貫性は保証されます。移動先のtoが既に
存在していた場合、その元の内容は失われます。
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.
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:
<foo-meta>
. It doesn’t
need to inherit anything except <class>
.
<foo>
that inherits <dbm>
and whose metaclass is <foo-meta>
.
dbm-open
, dbm-close
,
dbm-put!
, dbm-get
, dbm-exists
,
dbm-delete!
, dbm-fold
, dbm-closed?
,
specialized for <foo>
.
(The case of dbm-open
for <foo-meta>
is
handled automatically, so you don’t need to define it unless
you want something special).
Also note that the specialized dbm-open
must call
next-method
in it to set up dbm base class internals.
dbm-db-exists?
and
dbm-db-remove
on <foo-meta>
.
Besides above, you may define the following methods.
dbm-for-each
and dbm-map
.
If you don’t define them, a generic implementation
by dbm-fold
is used. There may be an implementation
specific way which is more efficient.
dbm-db-copy
and dbm-db-move
.
If you don’t define them, a fallback method
opens the specified databases and copies elements one by
one, and removes the original if the method is dbm-db-move
.
Note that the fallback method is not only inefficient,
but also it may not copy any implementation-specific
meta information. It is highly recommended for the
dbm implementation to provide these methods as well.
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.