簡易データベース
 
小規模なデータベースなら古くからUNIXに実装されている、DBMを使うことによって簡単に、しかも高速なデータベースを作成できます。ただし、システムの実装方式にもよりますが、1レコードの長さが1Kバイトまでであったり、ファイルサイズが4Kバイトまでなどの制限があります。DBMの拡張版としてこのサイズの制限が多少拡張されたNDBM(New DBM)と呼ばれるものがあり、実際はこちらの制限に依存するはずです。他にもSDBM,GDBM(GNU DBM)、ODBM、BSD DBなどのいくつかのバリエーションがあります。

サンプルプログラムでは氏名、性別、E-Mailアドレス、電話番号のデータベースを作成します。レコード中の各フィールドの区切り文字は、カンマ , としています。E-Mailアドレスは重複することがないとして、キーはE-Mailアドレスとします。データベースをオープンする(dbmopen)と連想配列(ハッシュ)を経由してデータを得ることができます。処理が終了したらデータベースをクローズ(dbmclose)します。

ファイルを新規作成する際にはモード(パーミッション)を指定する必要があります。このモードはUNIXで用いられているもので、実際のオープンはこのモードにumaskでマスクされたモードで作成されます。umask は現時点ではWindows/Macには実装されていませんが、パーミッションのどのビットをオフ(無効)にするかを設定するものです。
ファイルをオープンする際に、ファイルが存在しない場合は作成したくない場合は、モードにundefを指定します。ファイルを新規作成するときだけモードを指定するとおぼえておくと良いでしょう。データベースファイルは、指定したファイル名.dirとファイル名.pagの2つが生成されます。

パーミッションモードは次のような規則です。数値は8進数(頭に0)です。rwx- はそれぞれRead、Write、eXecute、なし、の意味でUNIXの ls -l コマンドで表示されるものです。ただし、こちらの表現はdbmopenには用いることはできません。実は、UNIXではもう一つモードがあり先頭の-がそれなのですが、dbmopenでは関係ありませんから説明は省きます。
 
 
数値表現 文字表現 内容
0400 -r-------- ファイルのオーナーのリード権
0200 --w------- ファイルのオーナーのライト権
0100 ---x------ ファイルのオーナーと実行権
0040 ----r----- ファイルのオーナーと同一グループのユーザのリード権
0020 -----w---- ファイルのオーナーと同一グループのユーザのライト権
0010 ------x--- ファイルのオーナーと同一グループのユーザの実行権
0004 -------r-- その他のユーザのリード権
0002 --------w- その他のユーザのライト権
0001 ---------x その他のユーザの実行権

例えばオーナーにリード/ライト/実行権、オーナーと同一グループのユーザにリード権、その他のユーザはどの権限もなしの場合、

0400
0200
0100
0040
----
0740  -rwxr-----

のように、それぞれの数値を足しあわせた値となります。
 

入力画面

表示画面

[サンプルプログラムの実行]
 
 
simpledb.cgi
#!/usr/bin/perl
#
# simpledb.cgi
#
# (C)1999 Kaoru Fujita
#
use lib './lib';

require 'util.pl';

#
# 定数
#
$Title = '簡易データベースのサンプル';
$Location = './tmp';
$dbfile = $Location.'/mydb';
$delim = ',';
$Encoding = 'sjis';
$CharSet = 'Shift_JIS';

@Fields = ('LastName', 'FirstName', 'Sex', 'E-Mail', 'Telephone');

parseInput($Encoding);

if ($in{'Action'} eq 'Add') {
  addData();
}
else {
  viewData();
}

exit(0);


#
#
#
sub viewData
{
  dbmopen(%DB, $dbfile, undef) or
    exitError("データベース $dbfile がオープンできません。");

  print "Content-Type: text/html\n\n";
  print "<HTML>\n";
  print "<HEAD>\n";
  print "<META HTTP-EQUIV=Content-Type CONTENT=text/html; charset=$CharSet>\n";
  print "<TITLE>$Title</TITLE>\n";
  print "</HEAD>\n";
  print "<BODY>\n";
  print "<TABLE>\n";
  print "<TR>\n";
  for ($i=0; $i < scalar(@Fields); $i++) {
    print "<TH>$Fields[$i]</TH>\n";
  }
  print "</TR>\n";
  foreach (sort values %DB) {
    print "<TR>\n";
    @data = split(/$delim/);
    for ($i=0; $i < scalar(@Fields); $i++) {
      print '<TD BGCOLOR="#F0F0F0">',$data[$i],"</TD>\n";
    }
    print "</TR>\n";
  }
  print "</TABLE>\n";
  dbmclose(DB);
  print "</BODY>\n";
  print "</HTML>\n";
}

#
#
#
sub addData
{
  dbmopen(%DB, $dbfile, 0600) or
    exitError("データベース $dbfile がオープンできません。");

  $key = $in{'E-Mail'}; 
  if ($DB{$key}) {
    exitError("すでに同じE-Mailアドレス $key があります。");
  }

  foreach (@Fields) {
    $rec = $rec.$in{$_}.$delim;
  }
  chomp($rec);
  $DB{$key} = $rec;

  dbmclose(DB);

  print << "END_OF_HTML";
Content-Type: text/html

<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=$CharSet">
<TITLE>$Title</TITLE>
</HEAD>
<BODY>
登録しました。
</BODY>
</HTML>
END_OF_HTML
}

#--End of simpledb.cgi

 
simpledb.html
<HTML>
<HEAD>
<TITLE>簡易データベース サンプル</TITLE>
<SCRIPT language="JavaScript">
<!--
function doIt(act)
{
  document.Form.Action.value = act;
  document.Form.submit();
}
//-->
</SCRIPT>
</HEAD>
<BODY> 
<FORM ACTION="/cgi-bin/simpledb.cgi" NAME="Form">
<INPUT TYPE="hidden" NAME="Action">
氏名: <INPUT TYPE="text" NAME="LastName" SIZE="8">
<INPUT TYPE="text" NAME="FirstName" SIZE="8"><BR>
性別: <SELECT NAME="Sex">
<OPTION VALUE="Male" selected>男
<OPTION VALUE="Female">女
</SELECT><BR>
E-Mailアドレス: <INPUT TYPE="text" NAME="E-Mail" SIZE="32"><BR>
電話番号: <INPUT TYPE="test" NAME="Telephone" SIZE="32"><BR>
<INPUT TYPE="button" NAME="View" VALUE="表示" onClick=doIt('View')>
<INPUT TYPE="button" NAME="Add" VALUE="登録" onClick=doIt('Add')>
</FORM>
</BODY>
</HTML>

実は、dbmopenはPerl4の古い時代からのインターフェースで、Perl5ではtieでハッシュとDBMパケージクラスとを結び付けることができます。関連付けが必要なくなったときには、untieで解除します。dbmopenと違うところはFcntlモジュールで定義されているファイルオープンフラグを指定することです。
 
フラグ 内容
O_CREAT ファイルが存在しない場合は新規作成する。
O_RDWR リード/ライトモードでオープンする。
O_RDONLY リード専用でオープンする。
O_WRONLY ライト専用でオープンする。
O_EXCL O_CREATが指定されていて、ファイルが存在する場合にエラーを返す。
O_TRUNC ファイルを切り詰めてオープンする。

tileを用いて上記サンプルを書き換えると次のようになります。DBMモジュールには、NDBM_File、SDBM_File、GDBM_File、ODBM_File、DB_File、AnyDBM_Fileがありますが、ここでは標準でバンドルされているSDBM_Fileを利用します。
HTMLは呼び出すCGIを変更するだけです。

[サンプルプログラムの実行]
 
 
simpledb2.cgi
#!/usr/bin/perl
#
# simpledb2.cgi
#
# (C)1999 Kaoru Fujita
#
use lib './lib';

require 'util.pl';

use Fcntl;
use SDBM_File;

#
# 定数
#
$Title = '簡易データベースのサンプル(2)';
$Location = './tmp';
$dbfile = $Location.'/mydb';
$delim = ',';
$Encoding = 'sjis';
$CharSet = 'Shift_JIS';

@Fields = ('LastName', 'FirstName', 'Sex', 'E-Mail', 'Telephone');

parseInput($Encoding);

if ($in{'Action'} eq 'Add') {
  addData();
}
else {
  viewData();
}

exit(0);


#
#
#
sub viewData
{
  tie(%DB, SDBM_File, $dbfile, O_RDONLY, undef) or
    exitError("データベース $dbfile がオープンできません。");

  print "Content-Type: text/html\n\n";
  print "<HTML>\n";
  print "<HEAD>\n";
  print "<META HTTP-EQUIV=Content-Type CONTENT=text/html; charset=$CharSet>\n";
  print "<TITLE>$Title</TITLE>\n";
  print "</HEAD>\n";
  print "<BODY>\n";
  print "<TABLE>\n";
  print "<TR>\n";
  for ($i=0; $i < scalar(@Fields); $i++) {
    print "<TH>$Fields[$i]</TH>\n";
  }
  print "</TR>\n";
  foreach (sort values %DB) {
    print "<TR>\n";
    @data = split(/$delim/);
    for ($i=0; $i < scalar(@Fields); $i++) {
      print '<TD BGCOLOR="#F0F0F0">',$data[$i],"</TD>\n";
    }
    print "</TR>\n";
  }
  print "</TABLE>\n";
  print "</BODY>\n";
  print "</HTML>\n";

  untie(%DB);
}

#
#
#
sub addData
{
  tie(%DB, SDBM_File, $dbfile, O_CREAT|O_RDWR, 0600) or
    exitError("データベース $dbfile がオープンできません。");

  $key = $in{'E-Mail'}; 
  if ($DB{$key}) {
    exitError("すでに同じE-Mailアドレス $key があります。");
  }

  foreach (@Fields) {
    $rec = $rec.$in{$_}.$delim;
  }
  chomp($rec);
  $DB{$key} = $rec;

  untie(%DB);

  print << "END_OF_HTML";
Content-Type: text/html

<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=$CharSet">
<TITLE>$Title</TITLE>
</HEAD>
<BODY>
登録しました。
</BODY>
</HTML>
END_OF_HTML
}

#--End of simpledb2.cgi

 
simpledb2.html
<HTML>
<HEAD>
<TITLE>簡易データベース サンプル(2)</TITLE>
<SCRIPT language="JavaScript">
<!--
function doIt(act)
{
  document.Form.Action.value = act;
  document.Form.submit();
}
//-->
</SCRIPT>
</HEAD>
<BODY> 
<FORM ACTION="/cgi-bin/simpledb2.cgi" NAME="Form">
<INPUT TYPE="hidden" NAME="Action">
氏名: <INPUT TYPE="text" NAME="LastName" SIZE="8">
<INPUT TYPE="text" NAME="FirstName" SIZE="8"><BR>
性別: <SELECT NAME="Sex">
<OPTION VALUE="Male" selected>男
<OPTION VALUE="Female">女
</SELECT><BR>
E-Mailアドレス: <INPUT TYPE="text" NAME="E-Mail" SIZE="32"><BR>
電話番号: <INPUT TYPE="test" NAME="Telephone" SIZE="32"><BR>
<INPUT TYPE="button" NAME="View" VALUE="表示" onClick=doIt('View')>
<INPUT TYPE="button" NAME="Add" VALUE="登録" onClick=doIt('Add')>
</FORM>
</BODY>
</HTML>