DB_File-1.805 > DB_File
DB_File-1.805
Other versions:
DB_File-1.820

名前

DB_File - Perl5 access to Berkeley DB version 1.x

DB_File - Berkeley DB バージョン 1.xへの Perl5 アクセス

概要

 use DB_File;
 use DB_File;
 [$X =] tie %hash,  'DB_File', [$filename, $flags, $mode, $DB_HASH] ;
 [$X =] tie %hash,  'DB_File', $filename, $flags, $mode, $DB_BTREE ;
 [$X =] tie @array, 'DB_File', $filename, $flags, $mode, $DB_RECNO ;
 [$X =] tie %hash,  'DB_File', [$filename, $flags, $mode, $DB_HASH] ;
 [$X =] tie %hash,  'DB_File', $filename, $flags, $mode, $DB_BTREE ;
 [$X =] tie @array, 'DB_File', $filename, $flags, $mode, $DB_RECNO ;
 $status = $X->del($key [, $flags]) ;
 $status = $X->put($key, $value [, $flags]) ;
 $status = $X->get($key, $value [, $flags]) ;
 $status = $X->seq($key, $value, $flags) ;
 $status = $X->sync([$flags]) ;
 $status = $X->fd ;
 $status = $X->del($key [, $flags]) ;
 $status = $X->put($key, $value [, $flags]) ;
 $status = $X->get($key, $value [, $flags]) ;
 $status = $X->seq($key, $value, $flags) ;
 $status = $X->sync([$flags]) ;
 $status = $X->fd ;
 # BTREE only
 $count = $X->get_dup($key) ;
 @list  = $X->get_dup($key) ;
 %list  = $X->get_dup($key, 1) ;
 $status = $X->find_dup($key, $value) ;
 $status = $X->del_dup($key, $value) ;
 # BTREE のみ
 $count = $X->get_dup($key) ;
 @list  = $X->get_dup($key) ;
 %list  = $X->get_dup($key, 1) ;
 $status = $X->find_dup($key, $value) ;
 $status = $X->del_dup($key, $value) ;
 # RECNO only
 $a = $X->length;
 $a = $X->pop ;
 $X->push(list);
 $a = $X->shift;
 $X->unshift(list);
 @r = $X->splice(offset, length, elements);
 # RECNO のみ
 $a = $X->length;
 $a = $X->pop ;
 $X->push(list);
 $a = $X->shift;
 $X->unshift(list);
 @r = $X->splice(offset, length, elements);
 # DBM Filters
 $old_filter = $db->filter_store_key  ( sub { ... } ) ;
 $old_filter = $db->filter_store_value( sub { ... } ) ;
 $old_filter = $db->filter_fetch_key  ( sub { ... } ) ;
 $old_filter = $db->filter_fetch_value( sub { ... } ) ;
 # DBM フィルタ
 $old_filter = $db->filter_store_key  ( sub { ... } ) ;
 $old_filter = $db->filter_store_value( sub { ... } ) ;
 $old_filter = $db->filter_fetch_key  ( sub { ... } ) ;
 $old_filter = $db->filter_fetch_value( sub { ... } ) ;
 untie %hash ;
 untie @array ;
 untie %hash ;
 untie @array ;

説明

DB_File is a module which allows Perl programs to make use of the facilities provided by Berkeley DB version 1.x (if you have a newer version of DB, see "Using DB_File with Berkeley DB version 2 or greater"). It is assumed that you have a copy of the Berkeley DB manual pages at hand when reading this documentation. The interface defined here mirrors the Berkeley DB interface closely.

DB_File は Berkeley DB version 1.x によって提供されている機能を Perl プログラムが使えるようにするモジュールです (もしさらに新しいバージョンの DB を持っているのであれば、 "Using DB_File with Berkeley DB version 2 or greater" をご覧ください)。 このドキュメントを読むとき、あなたが Berkeley DBマ ニュアルページを 手元においていることを前提としています。 ここで定義されているインターフェースは Berkeley DB インターフェースを かなり反映しています。

Berkeley DB is a C library which provides a consistent interface to a number of database formats. DB_File provides an interface to all three of the database types currently supported by Berkeley DB.

Berkeley DB はいくつかのデータベースフォーマットに対する 一貫性のあるインターフェースを提供する C ライブラリです。 DB_File は現在 Berkeley DB によってサポートされている三つ全ての データベースタイプへのインタフェースを提供しています。

The file types are:

そのファイルタイプには以下ものがあります:

DB_HASH

This database type allows arbitrary key/value pairs to be stored in data files. This is equivalent to the functionality provided by other hashing packages like DBM, NDBM, ODBM, GDBM, and SDBM. Remember though, the files created using DB_HASH are not compatible with any of the other packages mentioned.

このデータベースタイプは、任意のキー/値の組をデータファイルに 格納することを可能にします。 これは機能の面で DBM, NDBM, ODBM, GDBM, SDBMといった他のハッシュを おこなうパッケージが提供する機能と同じです。 しかし DB_HASH を使って作られたファイルは、いま上げた他のパッケージと 互換性がないということを忘れないでください。

A default hashing algorithm, which will be adequate for most applications, is built into Berkeley DB. If you do need to use your own hashing algorithm it is possible to write your own in Perl and have DB_File use it instead.

デフォルトのハッシュアルゴリズムが Berkeley DB に組み込まれており、 ほとんどのアプリケーションに適合します。 もし独自のハッシュアルゴリズムを 使う必要があれば、Perl で独自に書込み、DB_File が代わりにそれを 使うようにすることも出来ます。

DB_BTREE

The btree format allows arbitrary key/value pairs to be stored in a sorted, balanced binary tree.

btree フォーマットは任意のキー/値の組を、バランスがとれた 二分木に格納できます。

As with the DB_HASH format, it is possible to provide a user defined Perl routine to perform the comparison of keys. By default, though, the keys are stored in lexical order.

DB_HASH フォーマットの場合と同じように、キーの比較を実行するユーザ定義の Perl のルーチンを提供することが出来ます。 しかしデフォルトでは、キーは文字(lexical)の順に格納されます。

DB_RECNO

DB_RECNO allows both fixed-length and variable-length flat text files to be manipulated using the same key/value pair interface as in DB_HASH and DB_BTREE. In this case the key will consist of a record (line) number.

DB_RECNO は固定長と可変長の両方のフラットなテキストファイルを、 DB_HASH や DB_BTREE と同じキー/値の組のインターフェースを使って扱える ようにします。 この場合、キーはレコード(行)番号になります。

DB_File を Berkeley DB バージョン 2 以上で使う

Although DB_File is intended to be used with Berkeley DB version 1, it can also be used with version 2, 3 or 4. In this case the interface is limited to the functionality provided by Berkeley DB 1.x. Anywhere the version 2 or greater interface differs, DB_File arranges for it to work like version 1. This feature allows DB_File scripts that were built with version 1 to be migrated to version 2 or greater without any changes.

DB_File は Berkeley DB バージョン 1 を使うようになっていますが、 バージョン 2, 3, 4 で使うことも出来ます。 この場合、インターフェースは Berkeley DB 1.x によって 提供されている機能に限定されます。 バージョン 2 以上のインターフェースで異なる箇所は、 どこでもバージョン 1 のように機能するよう DB_File が変更しています。 この機能により、バージョン 1 で構築された DB_File スクリプトを変更することなく、バージョン 2 以上に 移行できます。

If you want to make use of the new features available in Berkeley DB 2.x or greater, use the Perl module BerkeleyDB instead.

もし Berkeley DB 2.x 以上で利用可能な新しい機能を使いたいのであれば、 Perl モジュール BerkeleyDB を代わりに使ってください。

Note: The database file format has changed multiple times in Berkeley DB version 2, 3 and 4. If you cannot recreate your databases, you must dump any existing databases with either the db_dump or the db_dump185 utility that comes with Berkeley DB. Once you have rebuilt DB_File to use Berkeley DB version 2 or greater, your databases can be recreated using db_load. Refer to the Berkeley DB documentation for further details.

注意: Berkeley DB version 2, 3, 4 でデータベースファイルの フォーマットは何回か変更されています。 もしデータベースを再作成できないのであれば、 Berkeley DB に付いてくる db_dump あるいは db_dump185 ユーティリティのどちらかで、既存の全てのデータベースを ダンプしなければなりません。 一度、Berkeley DB version 2 以上を使うために DB_File を再作成すれば、 db_load を使って再作成することができます。 さらなる詳細は Berkeley DB ドキュメントを参照してください。

Please read "COPYRIGHT" before using version 2.x or greater of Berkeley DB with DB_File.

DB_File で Berkeley DB のバージョン 2.x 以上を使う前に "COPYRIGHT" をご覧になってください。

Berkeley DBへのインターフェース

DB_File allows access to Berkeley DB files using the tie() mechanism in Perl 5 (for full details, see "tie()" in perlfunc). This facility allows DB_File to access Berkeley DB files using either an associative array (for DB_HASH & DB_BTREE file types) or an ordinary array (for the DB_RECNO file type).

DB_File は Berkeley DB ファイルに Perl 5 の tie() 機能を使ってアクセスする ことを可能にします。 (完全な詳細については "tie()" in perlfunc をご覧下さい)。 この機能は DB_File に連想配列(DB_HASH と DB_BTREE ファイルタイプ)を使ったり、 通常の配列(DB_RECNO ファイルタイプ)を使って Berkeley DB ファイルにアクセスする ことを可能にします。

In addition to the tie() interface, it is also possible to access most of the functions provided in the Berkeley DB API directly. See "THE API INTERFACE".

tie() インターフェースに加えて、Berkeley DB API によって提供される、ほとんど 全ての関数に直接アクセスすることもできます。 "THE API INTERFACE" をご覧下さい。

Berkeley DB データベースファイルのオープン

Berkeley DB uses the function dbopen() to open or create a database. Here is the C prototype for dbopen():

Berkeley DB はデータベースのオープンや作成に dbopen() 関数を使います。 dbopen() の C プロトタイプは以下の通りです:

      DB*
      dbopen (const char * file, int flags, int mode, 
              DBTYPE type, const void * openinfo)

The parameter type is an enumeration which specifies which of the 3 interface methods (DB_HASH, DB_BTREE or DB_RECNO) is to be used. Depending on which of these is actually chosen, the final parameter, openinfo points to a data structure which allows tailoring of the specific interface method.

パラメータ type は列挙子(enumeration)で、三つのインタフェースメソッド (DB_HASH, DB_BTREE, DB_RECNO)のどれを使うかを指定します。 実際にどれが選ばれたかによって、最後のパラメータ openinfo には指定された インタフェースメソッドに適合するデータ構造体を指定します。

This interface is handled slightly differently in DB_File. Here is an equivalent call using DB_File:

インターフェースは DB_File では若干異なります。 DB_File を使った同じ呼び出しは以下の通りです:

        tie %array, 'DB_File', $filename, $flags, $mode, $DB_HASH ;

The filename, flags and mode parameters are the direct equivalent of their dbopen() counterparts. The final parameter $DB_HASH performs the function of both the type and openinfo parameters in dbopen().

filename, flags, mode パラメータは dbopen() での対応する ものとそのまま同じです。 最後のパラメータ $DB_HASH は dbopen() での typeopeninfo パラメータの両方の機能を行います。

In the example above $DB_HASH is actually a pre-defined reference to a hash object. DB_File has three of these pre-defined references. Apart from $DB_HASH, there is also $DB_BTREE and $DB_RECNO.

上記の例では $DB_HASH は実際にあらかじめ定義されたハッシュオブジェクトへの リファレンスになります。 DB_File はこれらの既に定義された三つのリファレンスを 持っています。 $DB_HASH のほかに $DB_BTREE と $DB_RECNO があります。

The keys allowed in each of these pre-defined references is limited to the names used in the equivalent C structure. So, for example, the $DB_HASH reference will only allow keys called bsize, cachesize, ffactor, hash, lorder and nelem.

これら既に定義されているリファレンスで許されるキーは、 等価な C 構造体で使われている名前に限定されます。 そのため例えば $DB_HASH は bsize, cachesize, ffactor, hash, lorder, nelem だけをキーとして許します。

To change one of these elements, just assign to it like this:

これらの要素を変更するには、以下のように代入するだけです:

        $DB_HASH->{'cachesize'} = 10000 ;

The three predefined variables $DB_HASH, $DB_BTREE and $DB_RECNO are usually adequate for most applications. If you do need to create extra instances of these objects, constructors are available for each file type.

三つのあらかじめ定義された変数 $DB_HASH, $DB_BTREE そして $DB_RECNO は大抵の アプリケーションに適応します。 もしこられオブジェクトの特別なインスタンスを作る必要があれば、 それぞれのファイルタイプのためにコンストラクタを利用します。

Here are examples of the constructors and the valid options available for DB_HASH, DB_BTREE and DB_RECNO respectively.

DB_HASH, DB_BTREE そして DB_RECNOに対応するコンストラクタと適切なオプションの それぞれの例を以下に示します:

     $a = new DB_File::HASHINFO ;
     $a->{'bsize'} ;
     $a->{'cachesize'} ;
     $a->{'ffactor'};
     $a->{'hash'} ;
     $a->{'lorder'} ;
     $a->{'nelem'} ;

     $b = new DB_File::BTREEINFO ;
     $b->{'flags'} ;
     $b->{'cachesize'} ;
     $b->{'maxkeypage'} ;
     $b->{'minkeypage'} ;
     $b->{'psize'} ;
     $b->{'compare'} ;
     $b->{'prefix'} ;
     $b->{'lorder'} ;

     $c = new DB_File::RECNOINFO ;
     $c->{'bval'} ;
     $c->{'cachesize'} ;
     $c->{'psize'} ;
     $c->{'flags'} ;
     $c->{'lorder'} ;
     $c->{'reclen'} ;
     $c->{'bfname'} ;

The values stored in the hashes above are mostly the direct equivalent of their C counterpart. Like their C counterparts, all are set to a default values - that means you don't have to set all of the values when you only want to change one. Here is an example:

上記でハッシュに格納される値はCの対応する部分と全く同じです。 それらは C の対応部分のように、全てはデフォルトに設定されます - つまり一部を変更したいだけであれば全ての値を設定する 必要が無いということです。 以下に例を示します:

     $a = new DB_File::HASHINFO ;
     $a->{'cachesize'} =  12345 ;
     tie %y, 'DB_File', "filename", $flags, 0777, $a ;

A few of the options need extra discussion here. When used, the C equivalent of the keys hash, compare and prefix store pointers to C functions. In DB_File these keys are used to store references to Perl subs. Below are templates for each of the subs:

いくつかのオプションについては、さらに説明する必要があります。 使用するさい、Cでのhashcompareprefixという項目は C関数へのポインタを格納します。 DB_Fileでは、これらのキーは Perlサブルーチンへのリファレンスを格納するために使われます。 各サブルーチンのテンプレートは以下に示します:

    sub hash
    {
        my ($data) = @_ ;
        ...
        # return the hash value for $data
        return $hash ;
    }
    sub hash
    {
        my ($data) = @_ ;
        ...
        # $data のためのハッシュ値を返す
        return $hash ;
    }
    sub compare
    {
        my ($key, $key2) = @_ ;
        ...
        # return  0 if $key1 eq $key2
        #        -1 if $key1 lt $key2
        #         1 if $key1 gt $key2
        return (-1 , 0 or 1) ;
    }
    sub compare
    {
        my ($key, $key2) = @_ ;
        ...
        # 戻り値 $key1 eq $key2 ならば 0
        #        $key1 lt $key2 ならば -1
        #        $key1 gt $key2 ならば 1
        return (-1 , 0 or 1) ;
    }
    sub prefix
    {
        my ($key, $key2) = @_ ;
        ...
        # return number of bytes of $key2 which are 
        # necessary to determine that it is greater than $key1
        return $bytes ;
    }
    sub prefix
    {
        my ($key, $key2) = @_ ;
        ...
        # $key1 よりも大きいと判定するために必要な
        # $key2 の長さの長さを返します
        return $bytes ;
    }

See "Changing the BTREE sort order" for an example of using the compare template.

compare テンプレートを使った例については "Changing the BTREE sort order" を ご覧下さい。

If you are using the DB_RECNO interface and you intend making use of bval, you should check out "The 'bval' Option".

DB_RECNO インターフェースを使っていて、bval を使おうとするのであれば、 "The 'bval' Option" をチェックする必要があります。

デフォルトパラメータ

It is possible to omit some or all of the final 4 parameters in the call to tie and let them take default values. As DB_HASH is the most common file format used, the call:

tie の呼び出しでの最後の四つのパラメータのいくつか、 あるいは全てを省略し、デフォルト値をとるようにすることができます。 以下のように呼び出すと、最も一般的なファイルフォーマットである DB_HASH が使われます:

    tie %A, "DB_File", "filename" ;
    tie %A, "DB_File", "filename" ;

is equivalent to:

これは以下のものと同等です:

    tie %A, "DB_File", "filename", O_CREAT|O_RDWR, 0666, $DB_HASH ;

It is also possible to omit the filename parameter as well, so the call:

ファイル名(filename)パラメータも省略することが可能です。 以下のように 呼び出します:

    tie %A, "DB_File" ;

is equivalent to:

これは以下のものと同等です:

    tie %A, "DB_File", undef, O_CREAT|O_RDWR, 0666, $DB_HASH ;

See "In Memory Databases" for a discussion on the use of undef in place of a filename.

ファイル名の場所でundefを使うことについての説明は "In Memory Databases" をご覧下さい。

メモリ上のデータベース

Berkeley DB allows the creation of in-memory databases by using NULL (that is, a (char *)0 in C) in place of the filename. DB_File uses undef instead of NULL to provide this functionality.

Berkeley DB はファイル名の場所に NULL(つまり C での (char *)0)を 使うことにより、メモリ上のデータベース(in-memory database)を 作成することを可能にしています。 DB_File では、この機能を提供するために NULL の代わりに undef を使います。

DB_HASH

The DB_HASH file format is probably the most commonly used of the three file formats that DB_File supports. It is also very straightforward to use.

DB_HASH ファイルフォーマットは DB_File がサポートしている三つの ファイルフォーマットの中で、おそらく最も一般的に使われています。 これは非常に使い方がわかりやすいものです。

簡単な例

This example shows how to create a database, add key/value pairs to the database, delete keys/value pairs and finally how to enumerate the contents of the database.

この例ではデータベースの作り方、データベースへのキー/値の組の追加、 キー/値の組の削除、そして最後にデータベースを順に出力する方法を示します。

    use warnings ;
    use strict ;
    use DB_File ;
    our (%h, $k, $v) ;

    unlink "fruit" ;
    tie %h, "DB_File", "fruit", O_RDWR|O_CREAT, 0666, $DB_HASH 
        or die "Cannot open file 'fruit': $!\n";
    # Add a few key/value pairs to the file
    $h{"apple"} = "red" ;
    $h{"orange"} = "orange" ;
    $h{"banana"} = "yellow" ;
    $h{"tomato"} = "red" ;
    # いくつかのキー/値の組をファイルに追加
    $h{"apple"} = "red" ;
    $h{"orange"} = "orange" ;
    $h{"banana"} = "yellow" ;
    $h{"tomato"} = "red" ;
    # Check for existence of a key
    print "Banana Exists\n\n" if $h{"banana"} ;
    # キーが存在するかチェック
    print "Banana Exists\n\n" if $h{"banana"} ;
    # Delete a key/value pair.
    delete $h{"apple"} ;
    # キー/値の組を削除
    delete $h{"apple"} ;
    # print the contents of the file
    while (($k, $v) = each %h)
      { print "$k -> $v\n" }
    # ファイルの内容を出力する
    while (($k, $v) = each %h)
      { print "$k -> $v\n" }

    untie %h ;

here is the output:

以下のように出力されます:

    Banana Exists

    orange -> orange
    tomato -> red
    banana -> yellow

Note that the like ordinary associative arrays, the order of the keys retrieved is in an apparently random order.

通常の連想配列(ハッシュ)と同様、取り出されるキーの順番は見た目上、 でたらめになることに注意してください。

DB_BTREE

The DB_BTREE format is useful when you want to store data in a given order. By default the keys will be stored in lexical order, but as you will see from the example shown in the next section, it is very easy to define your own sorting function.

与えられた順序でデータを格納したいとき、DB_BTREE フォーマットは便利です。 デフォルトではキーは文字の(lexical)順で格納されます。 しかし次のセクションでの例からもわかるように、 ソートするための独自の関数を定義するのは、よても簡単です。

BTREEソート順の変更

This script shows how to override the default sorting algorithm that BTREE uses. Instead of using the normal lexical ordering, a case insensitive compare function will be used.

このスクリプトは BTREE が使うデフォルトのソート用アルゴリズムを 上書きする方法を示しています。 通常の文字による順序を使う代わりに、大文字/小文字の 違いを無視した比較関数を使います。

    use warnings ;
    use strict ;
    use DB_File ;

    my %h ;

    sub Compare
    {
        my ($key1, $key2) = @_ ;
        "\L$key1" cmp "\L$key2" ;
    }
    # specify the Perl sub that will do the comparison
    $DB_BTREE->{'compare'} = \&Compare ;
    # 比較を行う Perl sub を指定します
    $DB_BTREE->{'compare'} = \&Compare ;

    unlink "tree" ;
    tie %h, "DB_File", "tree", O_RDWR|O_CREAT, 0666, $DB_BTREE 
        or die "Cannot open file 'tree': $!\n" ;
    # Add a key/value pair to the file
    $h{'Wall'} = 'Larry' ;
    $h{'Smith'} = 'John' ;
    $h{'mouse'} = 'mickey' ;
    $h{'duck'}  = 'donald' ;
    # ファイルにキー/値の組を追加
    $h{'Wall'} = 'Larry' ;
    $h{'Smith'} = 'John' ;
    $h{'mouse'} = 'mickey' ;
    $h{'duck'}  = 'donald' ;
    # Delete
    delete $h{"duck"} ;
    # 削除
    delete $h{"duck"} ;
    # Cycle through the keys printing them in order.
    # Note it is not necessary to sort the keys as
    # the btree will have kept them in order automatically.
    foreach (keys %h)
      { print "$_\n" }
    # 順番にキーを繰り返し、出力します
    # btree が自動的に順番を保っているので
    # キーをソートする必要がないことに注意してください
    foreach (keys %h)
      { print "$_\n" }

    untie %h ;

Here is the output from the code above.

上記のコードは以下のように出力します:

    mouse
    Smith
    Wall

There are a few point to bear in mind if you want to change the ordering in a BTREE database:

BTREE データベースで順序を変更したいのであれば、いくつか注意すべき ポイントがあります:

  1. The new compare function must be specified when you create the database.

    新しい比較関数はデータベースを作成するときに指定されなければなりません。

  2. You cannot change the ordering once the database has been created. Thus you must use the same compare function every time you access the database.

    一度データベースを作成してしまったら順序を変更することはできまんせん。 このためデータベースにアクセスするときには、いつも同じ比較関数を 使わなければなりません。

  3. Duplicate keys are entirely defined by the comparison function. In the case-insensitive example above, the keys: 'KEY' and 'key' would be considered duplicates, and assigning to the second one would overwrite the first. If duplicates are allowed for (with the R_DUPS flag discussed below), only a single copy of duplicate keys is stored in the database --- so (again with example above) assigning three values to the keys: 'KEY', 'Key', and 'key' would leave just the first key: 'KEY' in the database with three values. For some situations this results in information loss, so care should be taken to provide fully qualified comparison functions when necessary. For example, the above comparison routine could be modified to additionally compare case-sensitively if two keys are equal in the case insensitive comparison:

    キーの重複は完全に比較関数によって定義されます。 上記の大文字/小文字を区別しない例では、キー: 'KEY'と'key' は 重複するものと考えられ、2 番目のものを代入すると最初のものを上書き するでしょう。 (下記で説明する R_DUPS フラグで)重複が許されるれるので あれば、一つの重複したキーがデータベースに格納されます --- そのため (再び上記の例では)キー: 'KEY'、'Key' そして 'key' は、データベースに 三つの値を持った最初のキー: 'KEY' だけを残します。 状況によってはこれは情報のロスになります。 そこで必要なときには完全に修飾された 比較を提供するように注意しなければなりません。 例えば上記の比較ルーチンは、もし二つのキーが大文字/小文字を 区別しないときに同じであれば、追加として大文字/小文字を区別して 比較するように変更することができます:

        sub compare {
            my($key1, $key2) = @_;
            lc $key1 cmp lc $key2 ||
            $key1 cmp $key2;
        }
        sub compare {
            my($key1, $key2) = @_;
            lc $key1 cmp lc $key2 ||
            $key1 cmp $key2;
        }

    And now you will only have duplicates when the keys themselves are truly the same. (note: in versions of the db library prior to about November 1996, such duplicate keys were retained so it was possible to recover the original keys in sets of keys that compared as equal).

    こうすれば、キーそのものが本当に同じときにだけ、 重複を持つことになります。 (注意:1996 年 11 月頃までの db ライブラリのバージョンでは、 そのような重複したキーが残され、そのため同じであると比較されるキーの 集合で元のキーを戻すことができました)。

重複するキーの取り扱い

The BTREE file type optionally allows a single key to be associated with an arbitrary number of values. This option is enabled by setting the flags element of $DB_BTREE to R_DUP when creating the database.

BTREE ファイルタイプはオプションで、一つのキーを任意の個数の値に 関連付けることを可能にしています。 このオプションはデータベースを作成するとき、 $DB_BTREE のフラグ要素を R_DUP に設定することにより可能になります。

There are some difficulties in using the tied hash interface if you want to manipulate a BTREE database with duplicate keys. Consider this code:

BTREE データベースを重複したキーを持って扱いたいならば、tie されている ハッシュインターフェースを使う場合、いくつかの問題があります。 以下のコードについて考えてみてください:

    use warnings ;
    use strict ;
    use DB_File ;

    my ($filename, %h) ;

    $filename = "tree" ;
    unlink $filename ;
    # Enable duplicate records
    $DB_BTREE->{'flags'} = R_DUP ;
    # レコードの重複を可能とします
    $DB_BTREE->{'flags'} = R_DUP ;

    tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE 
        or die "Cannot open $filename: $!\n";
    # Add some key/value pairs to the file
    $h{'Wall'} = 'Larry' ;
    $h{'Wall'} = 'Brick' ; # Note the duplicate key
    $h{'Wall'} = 'Brick' ; # Note the duplicate key and value
    $h{'Smith'} = 'John' ;
    $h{'mouse'} = 'mickey' ;
    # いくつかのキー/値の組をファイルに追加します
    $h{'Wall'} = 'Larry' ;
    $h{'Wall'} = 'Brick' ; # キーの重複に注意
    $h{'Wall'} = 'Brick' ; # キーと値の重複に注意
    $h{'Smith'} = 'John' ;
    $h{'mouse'} = 'mickey' ;
    # iterate through the associative array
    # and print each key/value pair.
    foreach (sort keys %h)
      { print "$_  -> $h{$_}\n" }
    # 連想配列の分繰り返し、
    # 各キー/値の組を出力します
    foreach (sort keys %h)
      { print "$_  -> $h{$_}\n" }

    untie %h ;

Here is the output:

これは以下のように出力します:

    Smith   -> John
    Wall    -> Larry
    Wall    -> Larry
    Wall    -> Larry
    mouse   -> mickey

As you can see 3 records have been successfully created with key Wall - the only thing is, when they are retrieved from the database they seem to have the same value, namely Larry. The problem is caused by the way that the associative array interface works. Basically, when the associative array interface is used to fetch the value associated with a given key, it will only ever retrieve the first value.

キーWallで3つのレコードが正常に作成できたように見えます - これが唯一本当のことです。 データベースから取り出すと、まるで Larry という名前で 同じ値が入っているかのように見えます。 問題は連想配列インターフェースが機能する方法によって発生します。 基本的に連想配列インターフェースは与えられたキーに関連付けられた値を 取り出すために使われます。 これは最初の値だけを取り出します。

Although it may not be immediately obvious from the code above, the associative array interface can be used to write values with duplicate keys, but it cannot be used to read them back from the database.

これは上記のコードでは、あまり明確にはなっていませんが、 連想配列インターフェースは重複するキーで値を書きこむことはできます。 しかし、データベースから値を読みこむためには使えません。

The way to get around this problem is to use the Berkeley DB API method called seq. This method allows sequential access to key/value pairs. See "THE API INTERFACE" for details of both the seq method and the API in general.

この問題を回避するためには seq という Berkeley DB API メソッドを 使います。 このメソッドは順番にキー/値の組にアクセスすることを可能にします。 seq メソッドの詳細とAPI全般については "THE API INTERFACE" を ご覧下さい。

Here is the script above rewritten using the seq API method.

seq API メソッドを使って上記のスクリプトを書き直したものは 以下の通りです:

    use warnings ;
    use strict ;
    use DB_File ;

    my ($filename, $x, %h, $status, $key, $value) ;

    $filename = "tree" ;
    unlink $filename ;
    # Enable duplicate records
    $DB_BTREE->{'flags'} = R_DUP ;
    # レコードの重複を可能とします
    $DB_BTREE->{'flags'} = R_DUP ;

    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE 
        or die "Cannot open $filename: $!\n";
    # Add some key/value pairs to the file
    $h{'Wall'} = 'Larry' ;
    $h{'Wall'} = 'Brick' ; # Note the duplicate key
    $h{'Wall'} = 'Brick' ; # Note the duplicate key and value
    $h{'Smith'} = 'John' ;
    $h{'mouse'} = 'mickey' ;
    # いくつかのキー/値の組をファイルに追加します
    $h{'Wall'} = 'Larry' ;
    $h{'Wall'} = 'Brick' ; # キーの重複に注意
    $h{'Wall'} = 'Brick' ; # キーと値の重複に注意
    $h{'Smith'} = 'John' ;
    $h{'mouse'} = 'mickey' ;
    # iterate through the btree using seq
    # and print each key/value pair.
    $key = $value = 0 ;
    for ($status = $x->seq($key, $value, R_FIRST) ;
         $status == 0 ;
         $status = $x->seq($key, $value, R_NEXT) )
      {  print "$key -> $value\n" }
    # 連想配列の分繰り返し、
    # 各キー/値の組を出力します。
    $key = $value = 0 ;
    for ($status = $x->seq($key, $value, R_FIRST) ;
         $status == 0 ;
         $status = $x->seq($key, $value, R_NEXT) )
      {  print "$key -> $value\n" }

    undef $x ;
    untie %h ;

that prints:

これは以下のように出力します:

    Smith   -> John
    Wall    -> Brick
    Wall    -> Brick
    Wall    -> Larry
    mouse   -> mickey

This time we have got all the key/value pairs, including the multiple values associated with the key Wall.

今度はキー Wall に関連付けられた複数の値も含めて、全てのキー/値を 取得しました。

To make life easier when dealing with duplicate keys, DB_File comes with a few utility methods.

重複するキーの扱いをより簡単にするため、DB_File はいくつかの ユーティリティメソッドを備えています。

get_dup() メソッド

The get_dup method assists in reading duplicate values from BTREE databases. The method can take the following forms:

get_dup メソッドは BTREE データベースから重複した値を読み込むことを 助けます。 このメソッドは以下のような形式とることができます:

    $count = $x->get_dup($key) ;
    @list  = $x->get_dup($key) ;
    %list  = $x->get_dup($key, 1) ;
    $count = $x->get_dup($key) ;
    @list  = $x->get_dup($key) ;
    %list  = $x->get_dup($key, 1) ;

In a scalar context the method returns the number of values associated with the key, $key.

スカラコンテキストでは $key というキーに関連付けられた 値の数を返します。

In list context, it returns all the values which match $key. Note that the values will be returned in an apparently random order.

リストコンテキストでは $key に対応する値を全て返します。 値は見かけ上、でたらめな順番で返って来るということに注意してください。

In list context, if the second parameter is present and evaluates TRUE, the method returns an associative array. The keys of the associative array correspond to the values that matched in the BTREE and the values of the array are a count of the number of times that particular value occurred in the BTREE.

リストコンテキストでは、2 番目の引数があり真に評価されれば、 メソッドは連想配列を返します。 その連想配列のキーは BTREE でのマッチした 値に対応し、その配列の値はその値が BTREE に現れる回数です。

So assuming the database created above, we can use get_dup like this:

これをもとに上記で作成したデータベースについて考えると、以下のように get_dup を使うことができます:

    use warnings ;
    use strict ;
    use DB_File ;

    my ($filename, $x, %h) ;

    $filename = "tree" ;
    # Enable duplicate records
    $DB_BTREE->{'flags'} = R_DUP ;
    # レコードの重複を可能とします
    $DB_BTREE->{'flags'} = R_DUP ;

    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE 
        or die "Cannot open $filename: $!\n";

    my $cnt  = $x->get_dup("Wall") ;
    print "Wall occurred $cnt times\n" ;

    my %hash = $x->get_dup("Wall", 1) ;
    print "Larry is there\n" if $hash{'Larry'} ;
    print "There are $hash{'Brick'} Brick Walls\n" ;

    my @list = sort $x->get_dup("Wall") ;
    print "Wall =>      [@list]\n" ;

    @list = $x->get_dup("Smith") ;
    print "Smith =>     [@list]\n" ;

    @list = $x->get_dup("Dog") ;
    print "Dog =>       [@list]\n" ;

and it will print:

これは以下のように出力します:

    Wall occurred 3 times
    Larry is there
    There are 2 Brick Walls
    Wall =>     [Brick Brick Larry]
    Smith =>    [John]
    Dog =>      []
    Wall occurred 3 times
    Larry is there
    There are 2 Brick Walls
    Wall => [Brick Brick Larry]
    Smith =>    [John]
    Dog =>  []

find_dup() メソッド

    $status = $X->find_dup($key, $value) ;

This method checks for the existence of a specific key/value pair. If the pair exists, the cursor is left pointing to the pair and the method returns 0. Otherwise the method returns a non-zero value.

このメソッドは、あるキー/値の組が存在するかどうかをチェックします。 もしその組があればカーソルはその組を示し、メソッドは 0 を返します。 そうでなければメソッドは 0 以外の値を返します。

Assuming the database from the previous example:

前の例からのデータベースについて考えれば:

    use warnings ;
    use strict ;
    use DB_File ;

    my ($filename, $x, %h, $found) ;

    $filename = "tree" ;
    # Enable duplicate records
    $DB_BTREE->{'flags'} = R_DUP ;
    # レコードの重複を可能とします
    $DB_BTREE->{'flags'} = R_DUP ;

    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE 
        or die "Cannot open $filename: $!\n";

    $found = ( $x->find_dup("Wall", "Larry") == 0 ? "" : "not") ; 
    print "Larry Wall is $found there\n" ;

    $found = ( $x->find_dup("Wall", "Harry") == 0 ? "" : "not") ; 
    print "Harry Wall is $found there\n" ;

    undef $x ;
    untie %h ;

prints this

これは以下のように出力します:

    Larry Wall is  there
    Harry Wall is not there

del_dup() メソッド

    $status = $X->del_dup($key, $value) ;

This method deletes a specific key/value pair. It returns 0 if they exist and have been deleted successfully. Otherwise the method returns a non-zero value.

このメソッドは指定されたキー/値の組を削除します。 もし存在し、正常に削除できたら 0 を返します。 そうでなければ 0 以外の値を返します。

Again assuming the existence of the tree database

再び tree データベースがあるとします。

    use warnings ;
    use strict ;
    use DB_File ;

    my ($filename, $x, %h, $found) ;

    $filename = "tree" ;
    # Enable duplicate records
    $DB_BTREE->{'flags'} = R_DUP ;
    # レコードの重複を可能とします
    $DB_BTREE->{'flags'} = R_DUP ;

    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE 
        or die "Cannot open $filename: $!\n";

    $x->del_dup("Wall", "Larry") ;

    $found = ( $x->find_dup("Wall", "Larry") == 0 ? "" : "not") ; 
    print "Larry Wall is $found there\n" ;

    undef $x ;
    untie %h ;

prints this

これは以下のように出力します:

    Larry Wall is not there

部分キーのマッチング

The BTREE interface has a feature which allows partial keys to be matched. This functionality is only available when the seq method is used along with the R_CURSOR flag.

BTREE インターフェースはマッチさせるための部分キーを許すという 機能を持っています。 この機能は R_CURSOR フラグと一緒に seq メソッドを 使ったときに のみ 有効です。

    $x->seq($key, $value, R_CURSOR) ;

Here is the relevant quote from the dbopen man page where it defines the use of the R_CURSOR flag with seq:

dbopen マニュアルページから関連する seq での R_CURSOR フラグの使い方に ついて定義している部分の引用を以下に示します:

    Note, for the DB_BTREE access method, the returned key is not
    necessarily an exact match for the specified key. The returned key
    is the smallest key greater than or equal to the specified key,
    permitting partial key matches and range searches.
    DB_BTREE アクセスメソッドについては返されるキーは必ずしも指定された
    キーに完全にマッチするわけではないことに注意。
    返されるキーは指定されたキーより大きいか同じである
    最小のキーで、部分キーマッチや範囲検索を可能としています。

In the example script below, the match sub uses this feature to find and print the first matching key/value pair given a partial key.

以下の例のスクリプトでは、match サブルーチンは、この機能を使って 指定された部分キーにマッチする最初のキー/値の組を見つけ出力します。

    use warnings ;
    use strict ;
    use DB_File ;
    use Fcntl ;

    my ($filename, $x, %h, $st, $key, $value) ;

    sub match
    {
        my $key = shift ;
        my $value = 0;
        my $orig_key = $key ;
        $x->seq($key, $value, R_CURSOR) ;
        print "$orig_key\t-> $key\t-> $value\n" ;
    }

    $filename = "tree" ;
    unlink $filename ;

    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE
        or die "Cannot open $filename: $!\n";
    # Add some key/value pairs to the file
    $h{'mouse'} = 'mickey' ;
    $h{'Wall'} = 'Larry' ;
    $h{'Walls'} = 'Brick' ; 
    $h{'Smith'} = 'John' ;
    # ファイルにいくつかのキー/値の組を追加します
    $h{'mouse'} = 'mickey' ;
    $h{'Wall'} = 'Larry' ;
    $h{'Walls'} = 'Brick' ; 
    $h{'Smith'} = 'John' ;

    $key = $value = 0 ;
    print "IN ORDER\n" ;
    for ($st = $x->seq($key, $value, R_FIRST) ;
         $st == 0 ;
         $st = $x->seq($key, $value, R_NEXT) )

      {  print "$key    -> $value\n" }

    print "\nPARTIAL MATCH\n" ;

    match "Wa" ;
    match "A" ;
    match "a" ;

    undef $x ;
    untie %h ;

Here is the output:

これは以下のように出力します:

    IN ORDER
    Smith -> John
    Wall  -> Larry
    Walls -> Brick
    mouse -> mickey

    PARTIAL MATCH
    Wa -> Wall  -> Larry
    A  -> Smith -> John
    a  -> mouse -> mickey

DB_RECNO

DB_RECNO provides an interface to flat text files. Both variable and fixed length records are supported.

DB_RECNO はフラットなテキストファイルへのインターフェースを提供します。 可変長レコードと固定長レコードの両方をサポートします。

In order to make RECNO more compatible with Perl, the array offset for all RECNO arrays begins at 0 rather than 1 as in Berkeley DB.

RECNO を Perl との互換性を持たせるため、RECNO 配列のための配列オフセットは Berkeley DB での 1 ではなく 0 から始まります。

As with normal Perl arrays, a RECNO array can be accessed using negative indexes. The index -1 refers to the last element of the array, -2 the second last, and so on. Attempting to access an element before the start of the array will raise a fatal run-time error.

通常の Perl 配列と同じように、RECNO 配列には負のインデックスを 使ってアクセスすることができます。 インデックス -1 は配列の最後の要素を、 -2 は後ろから 2 番目をといったように参照します。 配列の最初よりも前の要素にアクセスしようとすると 致命的なランタイムエラーを起こします。

'bval' オプション

The operation of the bval option warrants some discussion. Here is the definition of bval from the Berkeley DB 1.85 recno manual page:

bval オプションの操作はいくつかの点を保証します。 以下は Berkeley DB 1.85 recno マニュアルページからの bval の定義です:

    The delimiting byte to be used to mark  the  end  of  a
    record for variable-length records, and the pad charac-
    ter for fixed-length records.  If no  value  is  speci-
    fied,  newlines  (``\n'')  are  used to mark the end of
    variable-length records and  fixed-length  records  are
    padded with spaces.
    可変長レコードため、レコードの終わりをマークするために使われる
    区切りバイト、そして固定長レコードのために埋められる文字。
    指定されなければ、改行("\n") が可変長レコードの終わりを示すために
    使われ、固定長レコードには空白が埋められます。

The second sentence is wrong. In actual fact bval will only default to "\n" when the openinfo parameter in dbopen is NULL. If a non-NULL openinfo parameter is used at all, the value that happens to be in bval will be used. That means you always have to specify bval when making use of any of the options in the openinfo parameter. This documentation error will be fixed in the next release of Berkeley DB.

2 番目の文は間違っています。 実際には、dbopen での openinfo パラメータが NULL であるときだけ、bval はデフォルトで "\n" となります。 NULL 以外の openinfo パラメータが使われると、bval が使われるとき、 たまたまその値になってしまいます。 つまり openinfo パラメータでオプションを使用するとき には、bval を指定しなければなりません。 このドキュメントのエラーは Berkeley DB の 次のリリースで修正されるでしょう。

That clarifies the situation with regards Berkeley DB itself. What about DB_File? Well, the behavior defined in the quote above is quite useful, so DB_File conforms to it.

Berkeley DB 自身についての状況は明らかになりました。 それでは DB_File はどうでしょう? 上記の引用で定義されている動きはとても便利です; そこで DB_File はこれに従っています。

That means that you can specify other options (e.g. cachesize) and still have bval default to "\n" for variable length records, and space for fixed length records.

つまり bval を可変長レコードには "\n" を、固定長レコードには 空白をデフォルトにしたまま、他のオプション(例えば cachesize)を 指定することができます。

Also note that the bval option only allows you to specify a single byte as a delimeter.

また bval オプションは区切りとして 1 バイトだけが指定できることにも 注意してください。

簡単な例

Here is a simple example that uses RECNO (if you are using a version of Perl earlier than 5.004_57 this example won't work -- see "Extra RECNO Methods" for a workaround).

以下に RECNO を使った簡単な例を示します(もし 5.004_57 よりも前の バージョンを Perl を使っていると、このサンプルは動きません -- 解決するためには "Extra RECNO Methods" をご覧下さい)。

    use warnings ;
    use strict ;
    use DB_File ;

    my $filename = "text" ;
    unlink $filename ;

    my @h ;
    tie @h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_RECNO 
        or die "Cannot open file 'text': $!\n" ;
    # Add a few key/value pairs to the file
    $h[0] = "orange" ;
    $h[1] = "blue" ;
    $h[2] = "yellow" ;
    # いくつかのキー/値の組をファイルに加えます
    $h[0] = "orange" ;
    $h[1] = "blue" ;
    $h[2] = "yellow" ;

    push @h, "green", "black" ;

    my $elements = scalar @h ;
    print "The array contains $elements entries\n" ;

    my $last = pop @h ;
    print "popped $last\n" ;

    unshift @h, "white" ;
    my $first = shift @h ;
    print "shifted $first\n" ;
    # Check for existence of a key
    print "Element 1 Exists with value $h[1]\n" if $h[1] ;
    # キーの存在をチェックします
    print "Element 1 Exists with value $h[1]\n" if $h[1] ;
    # use a negative index
    print "The last element is $h[-1]\n" ;
    print "The 2nd last element is $h[-2]\n" ;
    # マイナスのインデックスを使います
    print "The last element is $h[-1]\n" ;
    print "The 2nd last element is $h[-2]\n" ;

    untie @h ;

Here is the output from the script:

スクリプトの出力は以下のようになります:

    The array contains 5 entries
    popped black
    shifted white
    Element 1 Exists with value blue
    The last element is green
    The 2nd last element is yellow

特別な RECNO メソッド

If you are using a version of Perl earlier than 5.004_57, the tied array interface is quite limited. In the example script above push, pop, shift, unshift or determining the array length will not work with a tied array.

5.004_57 より前のバージョンの Perl を使っているのであれば、tie された 配列のインターフェースは非常に限定されます。 上記のスクリプト例での push, pop, shift, unshift や 長さを判定することは tie された配列ではうまく機能しません。

To make the interface more useful for older versions of Perl, a number of methods are supplied with DB_File to simulate the missing array operations. All these methods are accessed via the object returned from the tie call.

古い Perl のバージョンのための、インターフェースをより使いやすくするため、 DB_File では、失われた配列操作をシミュレートする、いくつかのメソッドが 提供されています。 これらすべてのメソッドはtie呼び出しで返される オブジェクトを介してアクセスされます。

Here are the methods:

以下のメソッドがあります:

$X->push(list) ;

Pushes the elements of list to the end of the array.

配列の最後に list の要素を追加します。

$value = $X->pop ;

Removes and returns the last element of the array.

配列の最後の要素を削除し返します。

$X->shift

Removes and returns the first element of the array.

配列の最初の要素を削除し返します。

$X->unshift(list) ;

Pushes the elements of list to the start of the array.

配列の先頭に list の要素を追加します。

$X->length

Returns the number of elements in the array.

配列の要素数を返します。

$X->splice(offset, length, elements);

Returns a splice of the the array.

配列の splice を返します。

もう1つの例

Here is a more complete example that makes use of some of the methods described above. It also makes use of the API interface directly (see "THE API INTERFACE").

以下に、上記で説明したメソッドのいくつかを使ったもう一つ完全な例を 示します。 これは API インターフェースも直接使っています。 ("THE API INTERFACE" もご覧下さい)。

    use warnings ;
    use strict ;
    my (@h, $H, $file, $i) ;
    use DB_File ;
    use Fcntl ;

    $file = "text" ;

    unlink $file ;

    $H = tie @h, "DB_File", $file, O_RDWR|O_CREAT, 0666, $DB_RECNO 
        or die "Cannot open file $file: $!\n" ;
    # first create a text file to play with
    $h[0] = "zero" ;
    $h[1] = "one" ;
    $h[2] = "two" ;
    $h[3] = "three" ;
    $h[4] = "four" ;
    # 最初に、扱うテキストファイルを作成します
    $h[0] = "zero" ;
    $h[1] = "one" ;
    $h[2] = "two" ;
    $h[3] = "three" ;
    $h[4] = "four" ;
    # Print the records in order.
    #
    # The length method is needed here because evaluating a tied
    # array in a scalar context does not return the number of
    # elements in the array.  
    # レコードを順番に出力します。
    #
    # スカラコンテキストでtieされた配列を評価しても、
    # 配列の要素数を返さないので、lengthメソッドが必要です。

    print "\nORIGINAL\n" ;
    foreach $i (0 .. $H->length - 1) {
        print "$i: $h[$i]\n" ;
    }
    # use the push & pop methods
    $a = $H->pop ;
    $H->push("last") ;
    print "\nThe last record was [$a]\n" ;
    # push と pop メソッドを使います
    $a = $H->pop ;
    $H->push("last") ;
    print "\nThe last record was [$a]\n" ;
    # and the shift & unshift methods
    $a = $H->shift ;
    $H->unshift("first") ;
    print "The first record was [$a]\n" ;
    # そして shift と unshift メソッドを使います
    $a = $H->shift ;
    $H->unshift("first") ;
    print "The first record was [$a]\n" ;
    # Use the API to add a new record after record 2.
    $i = 2 ;
    $H->put($i, "Newbie", R_IAFTER) ;
    # レコード 2 の後に新しいレコードを追加するため API を使います。
    $i = 2 ;
    $H->put($i, "Newbie", R_IAFTER) ;
    # and a new record before record 1.
    $i = 1 ;
    $H->put($i, "New One", R_IBEFORE) ;
    # そして新しいレコードをレコード 1 の前に
    $i = 1 ;
    $H->put($i, "New One", R_IBEFORE) ;
    # delete record 3
    $H->del(3) ;
    # レコード 3 を削除
    $H->del(3) ;
    # now print the records in reverse order
    print "\nREVERSE\n" ;
    for ($i = $H->length - 1 ; $i >= 0 ; -- $i)
      { print "$i: $h[$i]\n" }
    # 今度は逆の順番でレコードを出力します
    print "\nREVERSE\n" ;
    for ($i = $H->length - 1 ; $i >= 0 ; -- $i)
      { print "$i: $h[$i]\n" }
    # same again, but use the API functions instead
    print "\nREVERSE again\n" ;
    my ($s, $k, $v)  = (0, 0, 0) ;
    for ($s = $H->seq($k, $v, R_LAST) ; 
             $s == 0 ; 
             $s = $H->seq($k, $v, R_PREV))
      { print "$k: $v\n" }
    # 同じことをもう一度。ただし代わりに API 関数を使います。
    print "\nREVERSE again\n" ;
    my ($s, $k, $v)  = (0, 0, 0) ;
    for ($s = $H->seq($k, $v, R_LAST) ; 
             $s == 0 ; 
             $s = $H->seq($k, $v, R_PREV))
      { print "$k: $v\n" }

    undef $H ;
    untie @h ;

and this is what it outputs:

これは以下のように出力します:

    ORIGINAL
    0: zero
    1: one
    2: two
    3: three
    4: four

    The last record was [four]
    The first record was [zero]

    REVERSE
    5: last
    4: three
    3: Newbie
    2: one
    1: New One
    0: first

    REVERSE again
    5: last
    4: three
    3: Newbie
    2: one
    1: New One
    0: first

Notes:

注意:

  1. Rather than iterating through the array, @h like this:

    以下のように配列 @h 全体を繰り返すのではなく:

        foreach $i (@h)
        foreach $i (@h)

    it is necessary to use either this:

    以下のようにする必要があります:

        foreach $i (0 .. $H->length - 1) 
        foreach $i (0 .. $H->length - 1) 

    or this:

    あるいは以下のようにします:

        for ($a = $H->get($k, $v, R_FIRST) ;
             $a == 0 ;
             $a = $H->get($k, $v, R_NEXT) )
        for ($a = $H->get($k, $v, R_FIRST) ;
             $a == 0 ;
             $a = $H->get($k, $v, R_NEXT) )
  2. Notice that both times the put method was used the record index was specified using a variable, $i, rather than the literal value itself. This is because put will return the record number of the inserted line via that parameter.

    put メソッドが使われたときは両方ともレコードインデックス にリテラル値ではなく、変数 $i が使われていることに 注意してください。 これは put はパラメータによって追加された 行のレコード番号を返すためです。

API インターフェース

As well as accessing Berkeley DB using a tied hash or array, it is also possible to make direct use of most of the API functions defined in the Berkeley DB documentation.

tie されたハッシュや配列を使って Berkeley DB にアクセスするだけでなく、 Berkeley DB ドキュメントで定義されてる API 関数のほとんどを 直接使うこともできます。

To do this you need to store a copy of the object returned from the tie.

これをするためには tie から返されるオブジェクトのコピーを 取っておく必要があります。

        $db = tie %hash, "DB_File", "filename" ;

Once you have done that, you can access the Berkeley DB API functions as DB_File methods directly like this:

一度そうすれば、以下のように DB_File メソッドのように Berkeley DB API 関数に直接アクセスすることができます:

        $db->put($key, $value, R_NOOVERWRITE) ;

Important: If you have saved a copy of the object returned from tie, the underlying database file will not be closed until both the tied variable is untied and all copies of the saved object are destroyed.

重要: tie から返されるオブジェクトのコピーを保存した場合は、 tie された変数の結び付けがはずされ(untie)、保存されたオブジェクトが 破壊されるまで、元になっているデータベースファイルはクローズ されません

    use DB_File ;
    $db = tie %hash, "DB_File", "filename" 
        or die "Cannot tie filename: $!" ;
    ...
    undef $db ;
    untie %hash ;

See "The untie() Gotcha" for more details.

詳細については "The untie() Gotcha" をご覧ください。

All the functions defined in dbopen are available except for close() and dbopen() itself. The DB_File method interface to the supported functions have been implemented to mirror the way Berkeley DB works whenever possible. In particular note that:

close() と dbopen() 自身を除く dbopen で定義された全ての関数を 利用することができます。 サポートされる関数への DB_File メソッドインターフェースは、 可能な限り Berkeley DB の方法と同じように実装されています。 特に以下の点に注意してください:

  • The methods return a status value. All return 0 on success. All return -1 to signify an error and set $! to the exact error code. The return code 1 generally (but not always) means that the key specified did not exist in the database.

    メソッドはステータス値を返します。 成功すれば 0 を返します。 エラーを表すためには -1 を返し、厳密なエラーコードは $! に設定します。 リターンコード1は(常にではありませんが)一般的には指定されたキーが データベースに存在しないことを意味します。

    Other return codes are defined. See below and in the Berkeley DB documentation for details. The Berkeley DB documentation should be used as the definitive source.

    他にもリターンコードが定義されています。 詳細については以下の説明および Berkeley DB ドキュメントをご覧下さい。 Berkeley DB ドキュメントが決定的な情報源です。

  • Whenever a Berkeley DB function returns data via one of its parameters, the equivalent DB_File method does exactly the same.

    Berkeley DB 関数がデータをパラメータの一つを介して返すときにはいつでも、 同様の DB_File メソッドは全く同じ名前で存在します。

  • If you are careful, it is possible to mix API calls with the tied hash/array interface in the same piece of code. Although only a few of the methods used to implement the tied interface currently make use of the cursor, you should always assume that the cursor has been changed any time the tied hash/array interface is used. As an example, this code will probably not do what you expect:

    あなたが注意すれば、tie されたハッシュ/配列インターフェースと API を同じプログラムの中に混ぜることもできます。 tie されたインターフェースを実装するために使われるメソッドのうち ほんの少ししかカーソルを利用しませんが、tie された ハッシュ/配列インターフェースが使われたときは、 いつもカーソルが変更されていると考えなければなりません。 例えば、このコードはたぶん期待していたようには動きません:

        $X = tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0777, $DB_BTREE
            or die "Cannot tie $filename: $!" ;
        # Get the first key/value pair and set  the cursor
        $X->seq($key, $value, R_FIRST) ;
        # 最初のキー/値の組を取得し、カーソルを設定します
        $X->seq($key, $value, R_FIRST) ;
        # this line will modify the cursor
        $count = scalar keys %x ; 
        # この行はカーソルを変更してしまいます
        $count = scalar keys %x ; 
        # Get the second key/value pair.
        # oops, it didn't, it got the last key/value pair!
        $X->seq($key, $value, R_NEXT) ;
        # 2 番目のキー/値の組を取り出します。
        # あら、そうではありません。最後のキー/値の組を取り出しました!
        $X->seq($key, $value, R_NEXT) ;

    The code above can be rearranged to get around the problem, like this:

    この問題を回避するために上記のコードを以下のように書きかえることができます:

        $X = tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0777, $DB_BTREE
            or die "Cannot tie $filename: $!" ;
        # this line will modify the cursor
        $count = scalar keys %x ; 
        # この行はカーソルを変更してしまいます。
        $count = scalar keys %x ; 
        # Get the first key/value pair and set  the cursor
        $X->seq($key, $value, R_FIRST) ;
        # 最初のキー/値の組を取得し、カーソルを設定します
        $X->seq($key, $value, R_FIRST) ;
        # Get the second key/value pair.
        # worked this time.
        $X->seq($key, $value, R_NEXT) ;
        # 2 番目のキー/値の組を取り出します。
        # 今度はうまくいきました
        $X->seq($key, $value, R_NEXT) ;

All the constants defined in dbopen for use in the flags parameters in the methods defined below are also available. Refer to the Berkeley DB documentation for the precise meaning of the flags values.

flag パラメータで利用できるように dbopen で定義されている全ての定数を 以下のメソッドでも使うことができます。 flags の値の正確な意味については Berkeley DB ドキュメントをご覧下さい。

Below is a list of the methods available.

利用できるメソッドの一覧を以下に示します。

$status = $X->get($key, $value [, $flags]) ;

Given a key ($key) this method reads the value associated with it from the database. The value read from the database is returned in the $value parameter.

キー ($key) が与えられると、このメソッドはデータベースから 関連する値を読みこみます。 データベースから読みこんだ値は $valueパラメータに返されます。

If the key does not exist the method returns 1.

キーが存在しなければ、メソッドは1を返します。

No flags are currently defined for this method.

このメソッドについては、現在なんの flag も拒否されません。

$status = $X->put($key, $value [, $flags]) ;

Stores the key/value pair in the database.

データベースにキー/値の組みを格納します。

If you use either the R_IAFTER or R_IBEFORE flags, the $key parameter will have the record number of the inserted key/value pair set.

R_IAFTER または R_IBEFORE フラグを使うと、$key パラメータは 挿入するキー/値の組のレコード番号を持ちます。

Valid flags are R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE and R_SETCURSOR.

適切な flag は R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE, R_SETCURSOR です。

$status = $X->del($key [, $flags]) ;

Removes all key/value pairs with key $key from the database.

データベースからキー $key をもつ全てのキー/値の組を削除します。

A return code of 1 means that the requested key was not in the database.

リターンコード1は要求されたキーがデータベースに存在しなかったことを 意味します。

R_CURSOR is the only valid flag at present.

R_CURSOR だけがいまのことろ唯一適切な flag です。

$status = $X->fd ;

Returns the file descriptor for the underlying database.

元になっているデータベースのためのファイルディスクリプタを 返します。

See "Locking: The Trouble with fd" for an explanation for why you should not use fd to lock your database.

データベースをロックするために fd を使うべきではない理由の 説明については "Locking: The Trouble with fd" をご覧下さい。

$status = $X->seq($key, $value, $flags) ;

This interface allows sequential retrieval from the database. See dbopen for full details.

このインターフェースはデータベースから順番に取り出すことを 可能にします。 完全な詳細については dbopen をご覧下さい。

Both the $key and $value parameters will be set to the key/value pair read from the database.

$key$value パラメータの両方はデータベースから 読みこまれるキー/値の組に設定されます。

The flags parameter is mandatory. The valid flag values are R_CURSOR, R_FIRST, R_LAST, R_NEXT and R_PREV.

flags パラメータは必須です。 適切なフラグの値は R_CURSOR, R_FIRST, R_LAST, R_NEX, R_PREVです。

$status = $X->sync([$flags]) ;

Flushes any cached buffers to disk.

キャッシュされたバッファをディスクにフラッシュします。

R_RECNOSYNC is the only valid flag at present.

今は R_RECNOSYNC だけが適切なフラグです。

DBMフィルタ

A DBM Filter is a piece of code that is be used when you always want to make the same transformation to all keys and/or values in a DBM database.

DBM フィルタはコードの集まりでであり、DBM データベースの中の 全てのキーと/または値に同じ変換を行いたいと 常に 思っている ときに使われます。

There are four methods associated with DBM Filters. All work identically, and each is used to install (or uninstall) a single DBM Filter. Each expects a single parameter, namely a reference to a sub. The only difference between them is the place that the filter is installed.

DBM フィルタに関連しては四つのメソッドがあります。 全て同様に機能します。 それぞれは一つの DBM フィルタをインストール (またはアンインストール)するために使われます。 各メソッドは一つのパラメータ、つまり sub へのリファレンスを期待します。 それぞれの唯一の違いはフィルタがインストールされる場所です。

To summarise:

まとめると以下のようになります:

filter_store_key

If a filter has been installed with this method, it will be invoked every time you write a key to a DBM database.

このメソッドでフィルタがインストールされると、 DBM データベースにキーを書きこむたびに、それが呼ばれます。

filter_store_value

If a filter has been installed with this method, it will be invoked every time you write a value to a DBM database.

このメソッドでフィルタがインストールされると、DBM データベースに 値を書きこむたびに、それが呼ばれます。

filter_fetch_key

If a filter has been installed with this method, it will be invoked every time you read a key from a DBM database.

このメソッドでフィルタがインストールされると、DBM データベースから キーを読みこむたびに、それが呼ばれます。

filter_fetch_value

If a filter has been installed with this method, it will be invoked every time you read a value from a DBM database.

このメソッドでフィルタがインストールされると、DBM データベースから 値を読みこむたびに、それが呼ばれます。

You can use any combination of the methods, from none, to all four.

全く無しから四つ全てまで、これらを自由に組み合わせて使うことができます。

All filter methods return the existing filter, if present, or undef in not.

全てのフィルタメソッドは、もしあれば既存のフィルタを、 無ければ undef を返します。

To delete a filter pass undef to it.

フィルタを削除するには undef を渡してください。

フィルタ

When each filter is called by Perl, a local copy of $_ will contain the key or value to be filtered. Filtering is achieved by modifying the contents of $_. The return code from the filter is ignored.

各フィルタがPerlから呼び出されるとき、$_ のローカルコピーには フィルタされるキーまたは値が入ります。 フィルタリングは $_ の内容を 変更することにより実現されます。 フィルタからの戻り値は無視されます。

例 -- NULL終わりの問題

Consider the following scenario. You have a DBM database that you need to share with a third-party C application. The C application assumes that all keys and values are NULL terminated. Unfortunately when Perl writes to DBM databases it doesn't use NULL termination, so your Perl application will have to manage NULL termination itself. When you write to the database you will have to use something like this:

以下のシナリオについて考えてみてください。 サードパーティの C アプリケーションと共有する必要がある DBM データベースを持っているとします。 その C アプリケーションは 全ての キーと 値は NULL で終わるものと仮定しています。 不幸にも Perl が DBM データベースに書きこむとき、NULL 終わりを使いません。 そのため、あなたの Perl アプリケーションは自分自身で NULL 終わりを 管理しなければなりません。 データベースに書きこむとき、以下のような 方法をとる必要があります:

    $hash{"$key\0"} = "$value\0" ;

Similarly the NULL needs to be taken into account when you are considering the length of existing keys/values.

同様に存在するキー/値の長さを考えるとき、NULL を考慮に入れる 必要があります。

It would be much better if you could ignore the NULL terminations issue in the main application code and have a mechanism that automatically added the terminating NULL to all keys and values whenever you write to the database and have them removed when you read from the database. As I'm sure you have already guessed, this is a problem that DBM Filters can fix very easily.

メインのアプリケーションプログラムでの NULL 終わり問題を無視する ことができ、データベースに書きこむときにはいつでも自動的に全ての キーと値に終わりの NULL を付与し、データベースから読みこむときには、 それらを削除するような機構を持つならば、素晴らしいことです。 既におわかりかと思いますが、この問題は DBM フィルタによって とても簡単に修正することができます。

    use warnings ;
    use strict ;
    use DB_File ;

    my %hash ;
    my $filename = "/tmp/filt" ;
    unlink $filename ;

    my $db = tie %hash, 'DB_File', $filename, O_CREAT|O_RDWR, 0666, $DB_HASH 
      or die "Cannot open $filename: $!\n" ;
    # Install DBM Filters
    $db->filter_fetch_key  ( sub { s/\0$//    } ) ;
    $db->filter_store_key  ( sub { $_ .= "\0" } ) ;
    $db->filter_fetch_value( sub { s/\0$//    } ) ;
    $db->filter_store_value( sub { $_ .= "\0" } ) ;
    # DBMフィルタのインストール
    $db->filter_fetch_key  ( sub { s/\0$//    } ) ;
    $db->filter_store_key  ( sub { $_ .= "\0" } ) ;
    $db->filter_fetch_value( sub { s/\0$//    } ) ;
    $db->filter_store_value( sub { $_ .= "\0" } ) ;

    $hash{"abc"} = "def" ;
    my $a = $hash{"ABC"} ;
    # ...
    undef $db ;
    untie %hash ;

Hopefully the contents of each of the filters should be self-explanatory. Both "fetch" filters remove the terminating NULL, and both "store" filters add a terminating NULL.

できるならば各フィルタの内容は自己説明的であるべきです。 両方の "fetch" フィルタが NULL 終わりを取り除き、 両方の "store" フィルタが NULL 終わりを付与します。

もう一つの例 -- キーが C int

Here is another real-life example. By default, whenever Perl writes to a DBM database it always writes the key and value as strings. So when you use this:

実際の場面での例をもう一つ。 デフォルトでは Perl は DBM データベースに 書きこむときはいつでも、キーと値を文字列として書きこみます。 そのため以下のようにすると:

    $hash{12345} = "soemthing" ;

the key 12345 will get stored in the DBM database as the 5 byte string "12345". If you actually want the key to be stored in the DBM database as a C int, you will have to use pack when writing, and unpack when reading.

キー12345 は 5 バイトの文字列 "12345" として DBM データベースに 格納されます。 もし本当に DBM データベースに C の int でキーを 格納したいのであれば、書きこむときに pack し、読みこむときに unpack する必要があります。

Here is a DBM Filter that does it:

以下はそれをおこなう DBM フィルタです:

    use warnings ;
    use strict ;
    use DB_File ;
    my %hash ;
    my $filename = "/tmp/filt" ;
    unlink $filename ;

    my $db = tie %hash, 'DB_File', $filename, O_CREAT|O_RDWR, 0666, $DB_HASH 
      or die "Cannot open $filename: $!\n" ;

    $db->filter_fetch_key  ( sub { $_ = unpack("i", $_) } ) ;
    $db->filter_store_key  ( sub { $_ = pack ("i", $_) } ) ;
    $hash{123} = "def" ;
    # ...
    undef $db ;
    untie %hash ;

This time only two filters have been used -- we only need to manipulate the contents of the key, so it wasn't necessary to install any value filters.

今回は二つのフィルタを使いました -- キーの内容を扱うことだけが必要 だったので、値のフィルタをインストールする必要がありません。

ヒントと小技

ロック: fd でのトラブル

Until version 1.72 of this module, the recommended technique for locking DB_File databases was to flock the filehandle returned from the "fd" function. Unfortunately this technique has been shown to be fundamentally flawed (Kudos to David Harris for tracking this down). Use it at your own peril!

このモジュールのバージョン 1.72 まで、DB_File データベースをロックする ための推奨されるテクニックは、"fd" 関数から返されるファイルハンドルを flock することでした。 残念ながら、このテクニックは根本的に無効である ことが明らかになりました(これを突き止めた栄誉は David Harris にあります)。 あなた自身の責任でそれを使ってください!

The locking technique went like this.

ロックのテクニックは以下のようなものでした。

    $db = tie(%db, 'DB_File', '/tmp/foo.db', O_CREAT|O_RDWR, 0666)
        || die "dbcreat /tmp/foo.db $!";
    $fd = $db->fd;
    open(DB_FH, "+<&=$fd") || die "dup $!";
    flock (DB_FH, LOCK_EX) || die "flock: $!";
    ...
    $db{"Tom"} = "Jerry" ;
    ...
    flock(DB_FH, LOCK_UN);
    undef $db;
    untie %db;
    close(DB_FH);

In simple terms, this is what happens:

簡単にいえば、このようなことが起こります:

  1. Use "tie" to open the database.

    データベースをオープンするため "tie" を使ってください。

  2. Lock the database with fd & flock.

    fd & flock でデータベースをロックしてください。

  3. Read & Write to the database.

    データベースの読み込み & 書き込み。

  4. Unlock and close the database.

    データベースのロックを解除してクローズ。

Here is the crux of the problem. A side-effect of opening the DB_File database in step 2 is that an initial block from the database will get read from disk and cached in memory.

ここに問題の急所があります。 ステップ 2 で DB_File をオープンする副作用 としてデータベースからの先頭ブロックはディスクから読み込まれ、 メモリにキャッシュされます。

To see why this is a problem, consider what can happen when two processes, say "A" and "B", both want to update the same DB_File database using the locking steps outlined above. Assume process "A" has already opened the database and has a write lock, but it hasn't actually updated the database yet (it has finished step 2, but not started step 3 yet). Now process "B" tries to open the same database - step 1 will succeed, but it will block on step 2 until process "A" releases the lock. The important thing to notice here is that at this point in time both processes will have cached identical initial blocks from the database.

この問題の理由をわかるため、二つのプロセス "A" と "B" の両方が同じ DB_File データベースを上記のロック手順を使って更新したいときに 何が起きるのかを考えてみましょう。 プロセス "A" が既にデータベースをオープンし、 書込ロックを持っているけれども、まだデータベースを実際には 更新していないとします(ステップ2まで終わっていても、まだ ステップ 3 を開始していません)。 そこでプロセス "B" が同じデータベースを オープンしようとします - ステップ 1 は成功します。 しかしプロセス "A" が ロックを解除するまでステップ 2 でブロックされます。 ここで気をつけなければならない重要なことは、この時点で両方のプロセスが データベースからのキャッシュされた同じ先頭ブロックを持っていることです。

Now process "A" updates the database and happens to change some of the data held in the initial buffer. Process "A" terminates, flushing all cached data to disk and releasing the database lock. At this point the database on disk will correctly reflect the changes made by process "A".

そしてプロセス "A" がデータベースを更新し、たまたま先頭バッファの中の データをいくらか変更したとします。 プロセス "A" が終了し、全てのキャッシュされたデータをディスクに フラッシュし、データベースロックを解除します。 この時点で、ディスク上のデータベースは プロセス "A" によって行われた変更を正しく反映しています。

With the lock released, process "B" can now continue. It also updates the database and unfortunately it too modifies the data that was in its initial buffer. Once that data gets flushed to disk it will overwrite some/all of the changes process "A" made to the database.

ロックが解除されると、プロセス "B" が処理を続けることができます。 それもデータベースを更新し、それも残念なことに先頭バッファにある データを変更したとします。 データがディスクにフラッシュされると、プロセス "A" がデータベースに 対して行った、一部/全ての変更を上書きしてしまいます。

The result of this scenario is at best a database that doesn't contain what you expect. At worst the database will corrupt.

このシナリオの結果は、最善でもデータベースには、あなたが予期している ものが入っていません。 悪ければデータベースがおかしくなっていまいます。

The above won't happen every time competing process update the same DB_File database, but it does illustrate why the technique should not be used.

上記のことは競合するプロセスが同じ DB_File データベースを更新する 度に発生するわけではありません。 しかしなぜそのテクニックを使うべきで ないかは明らかにしています。

データベースをロックする安全な方法

Starting with version 2.x, Berkeley DB has internal support for locking. The companion module to this one, BerkeleyDB, provides an interface to this locking functionality. If you are serious about locking Berkeley DB databases, I strongly recommend using BerkeleyDB.

version 2.x から、Berkeley DB は内部でロックをサポートしています。 これに親密な関係がある、BerkeleyDB は、このロック機能への インターフェースを提供しています。 Berkeley DB データベースをロックすることに真剣であれば、 BerkeleyDB を使うことを強く推奨します。

If using BerkeleyDB isn't an option, there are a number of modules available on CPAN that can be used to implement locking. Each one implements locking differently and has different goals in mind. It is therefore worth knowing the difference, so that you can pick the right one for your application. Here are the three locking wrappers:

もし BerkeleyDB を使うことが選択になければ、ロックを実装するために 使うことが出来る、いくつかのモジュールを CPAN から取得することができます。 それぞれは異なる方法でロックを実装し、異なるゴールを想定しています。 このため、あなたのアプリケーションにあったものを選べるように、 その違いを知ることには価値があります。 以下に三つのロックを行うラッパーを示します:

Tie::DB_Lock

A DB_File wrapper which creates copies of the database file for read access, so that you have a kind of a multiversioning concurrent read system. However, updates are still serial. Use for databases where reads may be lengthy and consistency problems may occur.

マルチバージョン化された並行読み込みアクセスの一種を実現するため、 読み込みアクセスのためのデータベースファイルのコピーを作成する DB_File ラッパー。 しかし更新はシリアルです。 読み込みが長く、一貫性の問題が起きるかもしれないデータベースに 使ってください。

Tie::DB_LockFile

A DB_File wrapper that has the ability to lock and unlock the database while it is being used. Avoids the tie-before-flock problem by simply re-tie-ing the database when you get or drop a lock. Because of the flexibility in dropping and re-acquiring the lock in the middle of a session, this can be massaged into a system that will work with long updates and/or reads if the application follows the hints in the POD documentation.

それが使われている間、データベースをロックしアンロックする能力を 持つ DB_File ラッパー。 ロックを取得したり、はずすとき、データベースを 再びtieすることにより、flock の前の tie 問題を避けます。 セッションの途中でのロックを解除し、再び取得するときの柔軟性のため、 アプリケーションが POD ドキュメントにあるヒントに従えば、 長い更新そして/あるいは読み込みを行うシステムに当てはめることができます。

DB_File::Lock

An extremely lightweight DB_File wrapper that simply flocks a lockfile before tie-ing the database and drops the lock after the untie. Allows one to use the same lockfile for multiple databases to avoid deadlock problems, if desired. Use for databases where updates are reads are quick and simple flock locking semantics are enough.

データベースをtieする前にロックファイルを単純に flock し、 untieの後にロックをはずす、非常に軽い DB_File ラッパー。 デッドロック問題を回避するため、そうしたければ複数のデータベースに 同じロックファイルを使うことを可能にしています。 更新と読み込みが速く、単純な flock ロックのセマンティクで十分で あるデータベースで利用してください。

Cアプリケーションとのデータベースの共有

There is no technical reason why a Berkeley DB database cannot be shared by both a Perl and a C application.

Berkeley DB データベースを Perl と C アプリケーションとで共有できないという 技術的な理由は何もありません。

The vast majority of problems that are reported in this area boil down to the fact that C strings are NULL terminated, whilst Perl strings are not. See "DBM FILTERS" for a generic way to work around this problem.

ここで報告されている非常に大きな問題は Perl 文字列はそうでないのに、 C 文字列が NULL 終わりであるということです。 この問題を回避する一般的な方法については "DBM FILTERS" をご覧下さい。

Here is a real example. Netscape 2.0 keeps a record of the locations you visit along with the time you last visited them in a DB_HASH database. This is usually stored in the file ~/.netscape/history.db. The key field in the database is the location string and the value field is the time the location was last visited stored as a 4 byte binary value.

実際の例を以下に示します。 Netscape 2.0 は DB_HASH データベースに、あなたが訪れた場所(location)を、 あなたが最後にそこを訪れた時間とを一緒にレコードにして保存しています。 これは通常、ファイル~/.netscape/history.db に格納されます。 そのデータベースのキーフィールドは場所(location)の文字列であり、 値のフィールドは最後に訪れた時刻が4バイトのバイナリ値として 格納されます。

If you haven't already guessed, the location string is stored with a terminating NULL. This means you need to be careful when accessing the database.

つまり場所の文字列は NULL 付きで格納されます。 データベースにアクセスするときにはこれに気をつけなければなりません。

Here is a snippet of code that is loosely based on Tom Christiansen's ggh script (available from your nearest CPAN archive in authors/id/TOMC/scripts/nshist.gz).

以下に部分的なプログラムを示します。 これはTom Christiansenの ggh スクリプトを不正確ながら ベースにしています (これはあなたに最も近いCPANアーカイブの authors/id/TOMC/scripts/nshist.gz で取得することができます)。

    use warnings ;
    use strict ;
    use DB_File ;
    use Fcntl ;

    my ($dotdir, $HISTORY, %hist_db, $href, $binary_time, $date) ;
    $dotdir = $ENV{HOME} || $ENV{LOGNAME};

    $HISTORY = "$dotdir/.netscape/history.db";

    tie %hist_db, 'DB_File', $HISTORY
        or die "Cannot open $HISTORY: $!\n" ;;
    # Dump the complete database
    while ( ($href, $binary_time) = each %hist_db ) {
    # データベース全体をダンプ
    while ( ($href, $binary_time) = each %hist_db ) {
        # remove the terminating NULL
        $href =~ s/\x00$// ;
        # 末尾の NULL を削除
        $href =~ s/\x00$// ;
        # convert the binary time into a user friendly string
        $date = localtime unpack("V", $binary_time);
        print "$date $href\n" ;
    }
        # バイナリの time をユーザフレントリな文字列に変換する
        $date = localtime unpack("V", $binary_time);
        print "$date $href\n" ;
    }
    # check for the existence of a specific key
    # remember to add the NULL
    if ( $binary_time = $hist_db{"http://mox.perl.com/\x00"} ) {
        $date = localtime unpack("V", $binary_time) ;
        print "Last visited mox.perl.com on $date\n" ;
    }
    else {
        print "Never visited mox.perl.com\n"
    }
    # 特定のキーが存在するかをチェック
    # NULL を付けるのを忘れないで下さい
    if ( $binary_time = $hist_db{"http://mox.perl.com/\x00"} ) {
        $date = localtime unpack("V", $binary_time) ;
        print "Last visited mox.perl.com on $date\n" ;
    }
    else {
        print "Never visited mox.perl.com\n"
    }

    untie %hist_db ;

untie() がしでかすこと

If you make use of the Berkeley DB API, it is very strongly recommended that you read "The untie Gotcha" in perltie.

Berkeley DB API を使うのであれば、"The untie Gotcha" in perltie を読むことを 非常に 強くお勧めします。

Even if you don't currently make use of the API interface, it is still worth reading it.

もし今のところは API インターフェースを使わないのであっても、 読む価値はあります。

Here is an example which illustrates the problem from a DB_File perspective:

DB_File の見地から問題を示す例を以下に示します:

    use DB_File ;
    use Fcntl ;

    my %x ;
    my $X ;

    $X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_TRUNC
        or die "Cannot tie first time: $!" ;

    $x{123} = 456 ;

    untie %x ;

    tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT
        or die "Cannot tie second time: $!" ;

    untie %x ;

When run, the script will produce this error message:

実行すると、このスクリプトは以下のエラーメッセージを出します:

    Cannot tie second time: Invalid argument at bad.file line 14.

Although the error message above refers to the second tie() statement in the script, the source of the problem is really with the untie() statement that precedes it.

上記のエラーメッセージではスプリプトでの2 番目の tie() ステートメントを 指しているにもかかわらず、この問題の原因は本当はその前にある untie() ステートメントにあります。

Having read perltie you will probably have already guessed that the error is caused by the extra copy of the tied object stored in $X. If you haven't, then the problem boils down to the fact that the DB_File destructor, DESTROY, will not be called until all references to the tied object are destroyed. Both the tied variable, %x, and $X above hold a reference to the object. The call to untie() will destroy the first, but $X still holds a valid reference, so the destructor will not get called and the database file tst.fil will remain open. The fact that Berkeley DB then reports the attempt to open a database that is already open via the catch-all "Invalid argument" doesn't help.

perltie を読んであれば既にお分かりのように、エラーは $X に 格納された tie されたオブジェクトの余分な複製によって起こされています。 読んでいなければ、要するに問題は tie されているオブジェクトへの 全ての リファレンスが破壊されなければ、DB_File デストラクタ DESTROY が呼ばれないということにあります。 上記のtieされた変数 %X$X は両方 ともオブジェクトへのリファレンスを持っています。 untie() を呼び出すと最初のものは破壊されますが、しかし $X はまだ適切なリファレンスを持っており、 そのためデストラクタは呼ばれず、tst.fil はオープンされたままです。 事実 Berkeley DB は、すでにオープンしているデータベースを 開こうとする とcatch-all な"Invalid argument"(不適切な引数)を 報告しますが、何の助けにもなりません。

If you run the script with the -w flag the error message becomes:

このスクリプトを -w フラグを付けて実行すると、以下のような エラーメッセージになります:

    untie attempted while 1 inner references still exist at bad.file line 12.
    Cannot tie second time: Invalid argument at bad.file line 14.

which pinpoints the real problem. Finally the script can now be modified to fix the original problem by destroying the API object before the untie:

これは本当の問題を指しています。 最終的には API オブジェクトを untie の前に 破壊することにより元の問題を修正するように変更することができます:

    ...
    $x{123} = 456 ;

    undef $X ;
    untie %x ;

    $X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT
    ...

よくある質問

私のデータベースに Perl ソースが入っているのはなぜですか?

If you look at the contents of a database file created by DB_File, there can sometimes be part of a Perl script included in it.

DB_File によって作成されたデータベースの中身を見ると、その中に Perl スクリプトの一部が入っていることがあります。

This happens because Berkeley DB uses dynamic memory to allocate buffers which will subsequently be written to the database file. Being dynamic, the memory could have been used for anything before DB malloced it. As Berkeley DB doesn't clear the memory once it has been allocated, the unused portions will contain random junk. In the case where a Perl script gets written to the database, the random junk will correspond to an area of dynamic memory that happened to be used during the compilation of the script.

これは Berkeley DB が、後でデータベースファイルに書きこまれるバッファを 取るために動的メモリを使っているためです。 動的であるために、メモリは DB が malloc する前に 使われていたままになります。 Berkeley DB はアロケートされたメモリをクリアしないので、 使われない部分にはデタラメなゴミが入っています。 Perl スクリプトがデータベースに書き込まれている場合、 そのデタラメなゴミが、スクリプトをコンパイルするとき、たまたま使われた 動的メモリの領域に対応していたのでしょう。

Unless you don't like the possibility of there being part of your Perl scripts embedded in a database file, this is nothing to worry about.

Perl スクリプトの一部だけでもデータベースファイル埋め込まれるかもしれないと いうことが嫌でなければ、何も心配することはありません。

DB_File に複雑なデータ構造を格納するにはどうすればいいですか?

Although DB_File cannot do this directly, there is a module which can layer transparently over DB_File to accomplish this feat.

DB_File は直接これをできませんが、この機能を実現するため DB_File の 上に透過的に重ねることのできるモジュールがあります。

Check out the MLDBM module, available on CPAN in the directory modules/by-module/MLDBM.

CPAN の modules/by-module/MLDBM ディレクトリから取得できる MLDBM モジュールを試してみてください。

"Invalid Argument" ってどういう意味ですか?

You will get this error message when one of the parameters in the tie call is wrong. Unfortunately there are quite a few parameters to get wrong, so it can be difficult to figure out which one it is.

tie の呼び出しでパラメータの一つが間違っていると、このエラーメッセージを 取得します。 残念ながらパラメータが間違うことはあまりないので、 それが難なのかを発見することは難しいかもしれません。

Here are a couple of possibilities:

以下にいくつかの可能性をしめします:

  1. Attempting to reopen a database without closing it.

    データベースをクローズなしに再オープンしようとした。

  2. Using the O_WRONLY flag.

    O_WRONLY フラグを使っている。

"Bareword 'DB_File' not allowed"ってどういう意味ですか?

You will encounter this particular error message when you have the strict 'subs' pragma (or the full strict pragma) in your script. Consider this script:

この特定のエラーメッセージは、スクリプトの中に strict "subs" プラグマ (あるいはすべての strict プラグマ)があるときに発生します。 以下のスクリプトについて考えてみてください:

    use warnings ;
    use strict ;
    use DB_File ;
    my %x ;
    tie %x, DB_File, "filename" ;

Running it produces the error in question:

これを実行すると質問のエラーを起こします:

    Bareword "DB_File" not allowed while "strict subs" in use 

To get around the error, place the word DB_File in either single or double quotes, like this:

このエラーを回避するには、以下のように DB_File という単語をシングル またはダブルクォートの中に入れてください:

    tie %x, "DB_File", "filename" ;

Although it might seem like a real pain, it is really worth the effort of having a use strict in all your scripts.

これはかなりの苦痛に見えますが、use strict をあなたの全てのスクリプトに 入れようとすることは本当に価値のあることです。

参考文献

Articles that are either about DB_File or make use of it.

DB_File やその使い方についての記事は以下の通り。

  1. Full-Text Searching in Perl, Tim Kientzle (tkientzle@ddj.com), Dr. Dobb's Journal, Issue 295, January 1999, pp 34-41

HISTORY

Moved to the Changes file.

Changes ファイルに移動しました。

バグ

Some older versions of Berkeley DB had problems with fixed length records using the RECNO file format. This problem has been fixed since version 1.85 of Berkeley DB.

以前のいくつかの Berkeley DB は、RECNO ファイルフォーマットを使った 固定長レコードで問題がありました。 この問題は Berkeley DB の バージョン 1.85 から修正されています。

I am sure there are bugs in the code. If you do find any, or can suggest any enhancements, I would welcome your comments.

このプログラムにバグがあると思っています。 何か見つけたら、あるいは拡張についてなにか提案できるのであれば、 コメントをお待ちしています。

可用性

DB_File comes with the standard Perl source distribution. Look in the directory ext/DB_File. Given the amount of time between releases of Perl the version that ships with Perl is quite likely to be out of date, so the most recent version can always be found on CPAN (see "CPAN" in perlmod for details), in the directory modules/by-module/DB_File.

DB_File は標準の Perl source ディストリビューションと一緒に入ります。 ext/DB_File をご覧下さい。 Perl といっしょに出されたバージョンの リリースからの時間のために古くなっているかもしれません。 そこで最新のバージョンは常に CPAN(詳細については "CPAN" in perlmod をご覧下さい)のディレクトリ modules/by-module/DB_File で 取得することができます。

This version of DB_File will work with either version 1.x, 2.x or 3.x of Berkeley DB, but is limited to the functionality provided by version 1.

このバージョンの DB_File は Berkeley DB の 1.x, 2.x, 3.x のいずれでも 動きます。 しかしバージョン 1 によって提供されている機能に限定されます。

The official web site for Berkeley DB is http://www.sleepycat.com. All versions of Berkeley DB are available there.

Berkeley DB の公式ウェブサイトは http://www.sleepycat.com です。 そこではすべてのバージョンの Berkeley DB を取得することができます。

Alternatively, Berkeley DB version 1 is available at your nearest CPAN archive in src/misc/db.1.85.tar.gz.

あるいは、Berkeley DB バージョン 1 は最寄りの CPAN アーカイブでの src/misc/db.1.85.tar.gz で取得できます。

If you are running IRIX, then get Berkeley DB version 1 from http://reality.sgi.com/ariel. It has the patches necessary to compile properly on IRIX 5.3.

IRIX を使っているのであれば、Berkeley DB バージョン 1 を http://reality.sgi.com/ariel で取得することができます。 IRIX 5.3 で適切にコンパイルするために必要なパッチが入っています。

コピーライト

Copyright (c) 1995-2002 Paul Marquess. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Although DB_File is covered by the Perl license, the library it makes use of, namely Berkeley DB, is not. Berkeley DB has its own copyright and its own license. Please take the time to read it.

DB_File は Perl ライセンスによってカバーされますが、これが使用する ライブラリ、つまり Berkeley DB はそうではありません。 Berkeley DB はそれ自身の著作権と独自のライセンスを持っています。 それを読んでください。

Here are are few words taken from the Berkeley DB FAQ (at http://www.sleepycat.com) regarding the license:

Berkeley DB FAQ (http://www.sleepycat.com) からライセンスについての 一部を示します:

    Do I have to license DB to use it in Perl scripts? 
    Perl スクリプトで使うために DB をライセンスする必要がありますか?
    No. The Berkeley DB license requires that software that uses
    Berkeley DB be freely redistributable. In the case of Perl, that
    software is Perl, and not your scripts. Any Perl scripts that you
    write are your property, including scripts that make use of
    Berkeley DB. Neither the Perl license nor the Berkeley DB license
    place any restriction on what you may do with them.
    いいえ。
    Berkeley DB ライセンスは Berkeley DB を利用するソフトウェアは、
    自由に再配布可能であることを必要とします。
    Perl の場合、つまりソフトウェアは Perl であり、あなたの
    スクリプトではありません。
    あなたが書いた全ての Perl スクリプトは Berkeley DB を使ったものも
    含めて、あなたの資産です。
    Perl ライセンスも Berkeley DB ライセンスも
    あなたがそれらを使ってできることを何ら制限しません。

If you are in any doubt about the license situation, contact either the Berkeley DB authors or the author of DB_File. See "AUTHOR" for details.

もしライセンスの状況に疑問があれば、Berkeley DB の作者あるいは DB_File の 作者にコンタクトしてください。 下記の "AUTHOR" をご覧ください。

SEE ALSO

perl(1), dbopen(3), hash(3), recno(3), btree(3), dbmfilter

作者

The DB_File interface was written by Paul Marquess <Paul.Marquess@btinternet.com>. Questions about the DB system itself may be addressed to <db@sleepycat.com<gt>.

DB_File インターフェースは Paul Marquess <Paul.Marquess@btinternet.com> によって作成されました。 DB システムそのものについての疑問は <db@sleepycat.com<gt> にお願いします。