H2は、もう1つの軽量・高速のJavaベースのデータベースエンジンであり、JDBCとODBCの両APIを使って利用できます。埋め込みモードでもサーバーモードでも利用でき、トリガ、結合、ビュー、ストアドプロシージャ、暗号化に対応しています。H2は同時実行ユーザをサポートし、さらに簡単なクラスタリングメカニズムをサポートしているので、厳しいアップタイム要件が求められる基幹アプリケーションにも適しています。H2はMozilla Public Licenseの下、オンラインで無料で入手できます。
Firebirdは、Windowsおよび*NIXプラットフォームで利用できる高機能のクライアント/サーバーRDBMSです。Borlandのオープンソース版InterBaseデータベースをベースにしています。Firebirdでは、シングルユーザーデータベースエンジンを、WindowsまたはLinuxアプリケーションに直接統合できるライブラリファイルとして実現することができます(Linux版では一部制約があります)。この埋め込みエンジンはSQL-92に完全準拠し、SQL-99にほぼ準拠しています。ACID対応のトランザクション、シーケンス、トリガ、サブセレクト、参照整合性制約をサポートし、データベース操作を行うための各種コマンドラインツールを含んでいます。埋め込みFirebirdエンジンは、Mozilla Public Licenseと同等のライセンスの下で利用できます。
データベースとしてSQLiteを選択したら、まずはインストールする必要があります。クライアント/サーバ方式のMySQLやPostgreSQLとは異なり、SQLiteでは、コマンドラインプログラムを使用してディスクベースのデータベースファイルを直接操作します。このプログラムの各プラットフォーム用のコンパイル済みバイナリは、こちらのサイトからダウンロードできます。現時点では、WindowsおよびLinuxのコンパイル済みバイナリと、Linux、Windows、Mac OS Xシステムに対応するクロスプラットフォームのスターキット(starkit)が用意されています。
root@thor:/tmp# tar -xzvf sqlite-3.3.17.tar.gz
root@thor:/tmp# mkdir build
root@thor:/tmp# cd build
root@thor:/tmp# ../sqlite-3.3.17/configure
root@thor:/tmp# make
root@thor:/tmp# make install
sqlite> .help
.bail ON|OFF Stop after hitting an error. Default is OFF
.databases List names and files of attached databases
.dump ?TABLE? ... Dump the database in an SQL text format
.echo ON|OFF Turn command echo on or off
.exit Exit this program
.explain ON|OFF Turn output mode for EXPLAIN on or off.
.header(s) ON|OFF Turn display of headers on or off
.help Show this message
.import FILE TABLE Import data from FILE into TABLE
.indices TABLE Show names of all indices on TABLE
...
root@thor:/usr/local/apache/data# sqlite3 todo.db3
SQLite version 3.3.17
Enter ".help" for instructions
sqlite>
次のCREATE TABLEコマンドを使用してテーブルを追加します。
sqlite> CREATE TABLE items (
...> id INTEGER NOT NULL PRIMARY KEY,
...> name TEXT NOT NULL,
...> due INTEGER NOT NULL,
...> complete INTEGER NULL,
...> priority INTEGER NOT NULL,
...> status INTEGER NOT NULL
...> );
<?php
require'include.php';
try {
// connect to database
$dbh = new PDO('sqlite:../../db/todo.db3');
// query and retrieve pending items
$sth = $dbh->query("SELECT * FROM items WHERE status = '1'
ORDER BY due DESC, priority DESC");
$pending = $sth->fetchAll();
$sth = null;
// query and retrieve completed items
$sth = $dbh->query("SELECT * FROM items WHERE status = '0'
ORDER BY due DESC, priority DESC");
$complete = $sth->fetchAll();
unset($dbh);
} catch (PDOException $e) {
die('Error: ' . $e->getMessage());
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>To-Do List</title>
<link rel="stylesheet" href="main.css" />
</head>
<body>
<h2>To-Do List</h2>
<div class="category">Pending Items</div>
<!-- generate listing of pending items -->
<?php if (sizeof($pending) > 0) { ?>
<table>
<tr>
<td class="head"></td>
<td class="head">Due date</td>
<td class="head">Priority</td>
</tr>
<?php foreach ($pending as $p) { ?>
<tr>
<td><?php echo $p['name']; ?></td>
<td><?php echo date('d M Y', $p['due']); ?></td>
<td><?php echo $priorities[$p['priority']]; ?></td>
</tr>
<?php } ?>
</table>
<?php } else { ?>
<p>None</p>
<?php } ?>
<!-- link to add a new to-do item -->
<p><a href="form.php">Add a new entry</a></p>
<!-- generate listing of completed items -->
<div class="category">Completed Items</div>
<?php if (sizeof($complete) > 0) { ?>
<table>
<tr>
<td class="head"></td>
<td class="head">Due date</td>
<td class="head">Completion date</td>
</tr>
<?php foreach ($complete as $c) { ?>
<tr>
<td><?php echo $c['name']; ?></td>
<td><?php echo date('d M Y', $c['due']); ?></td>
<td><?php echo date('d M Y', $c['complete']); ?></td>
</tr>
<?php } ?>
</table>
<?php } else { ?>
<p>None</p>
<?php } ?>
</body></html>
補足説明: 移植性
本稿のコードリストでは、PHP 5.1以降の新機能であるPHPのPDO(PHP Data Objects)拡張を使用しています。この拡張はデータベース抽象化レイヤのような働きをし、この拡張を利用すると、移植可能なデータベース非依存のコードを簡単に記述でき、新しいデータベースに切り換えるときにも、PDOコンストラクタに渡すDSN文字列を編集するだけで済みます。
たとえばMySQLからSQLiteに切り換える場合、必要な作業はDSN文字列を次のように変更することだけです。
<?php
$dbh = new PDO('mysql:host=localhost;port=3307;dbname=testdb',
'user, 'pass);
?>
この変更により、すべての保留項目に「Mark as Done」、「Change」、「Remove」というリンクが表示され、すべての完了項目に「Remove」リンクが表示されるようになります。さらに、修正版スクリプトではもう1つ機能を追加し、現在の日付(保留項目の場合)または完了日(完了項目の場合)を項目の実施期限と比較して、期限を過ぎている項目を赤で表示するようにしています。前掲の図1は、上記すべての機能を実装した状態のインデックスページです。
リスト7 修正版のindex.php
<?php
require'include.php';
try {
// connect to database
$dbh = new PDO('sqlite:../../db/todo.db3');
// query and retrieve pending items
$sth = $dbh->query("SELECT * FROM items WHERE status = '1'
ORDER BY due DESC, priority DESC");
$pending = $sth->fetchAll();
$sth = null;
// query and retrieve completed items
$sth = $dbh->query("SELECT * FROM items WHERE status = '0'
ORDER BY due DESC, priority DESC");
$complete = $sth->fetchAll();
unset($dbh);
} catch (PDOException $e) {
die('Error: ' . $e->getMessage());
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>To-Do List</title>
<link rel="stylesheet" href="main.css" />
</head>
<body>
<h2>To-Do List</h2>
<div class="category">Pending Items</div>
<!-- generate listing of pending items -->
<?php if (sizeof($pending) > 0) { ?>
<table>
<tr>
<td class="head"></td>
<td class="head">Due date</td>
<td class="head">Priority</td>
<td></td>
<td></td>
<td></td>
</tr>
<?php foreach ($pending as $p) {
$class = (mktime() > $p['due']) ? 'late' : 'ontime';
?>
<tr>
<td class="<?php echo $class; ?>">
<?php echo $p['name']; ?></td>
<td class="<?php echo $class; ?>">
<?php echo date('d M Y', $p['due']); ?></td>
<td class="<?php echo $class; ?>">
<?php echo $priorities[$p['priority']]; ?></td>
<td><a href="done.php?id=
<?php echo (int) $p['id']; ?>">Mark as done</a> | </td>
<td><a href="form.php?id=
<?php echo (int) $p['id']; ?>">Change</a> | </td>
<td><a href="delete.php?id=
<?php echo (int) $p['id']; ?>">Remove</a></td>
</tr>
<?php } ?>
</table>
<?php } else { ?>
<p>None</p>
<?php } ?>
<!-- link to add a new to-do item -->
<p><a href="form.php">Add a new entry</a></p>
<!-- generate listing of completed items -->
<div class="category">Completed Items</div>
<?php if (sizeof($complete) > 0) { ?>
<table>
<tr>
<td class="head"></td>
<td class="head">Due date</td>
<td class="head">Completion date</td>
<td></td>
</tr>
<?php foreach ($complete as $c) {
$class = ($c['complete'] > $c['due']) ? 'late' : 'ontime';
?>
<tr>
<td class="<?php echo $class; ?>">
<?php echo $c['name']; ?></td>
<td class="<?php echo $class; ?>">
<?php echo date('d M Y', $c['due']); ?></td>
<td class="<?php echo $class; ?>">
<?php echo date('d M Y', $c['complete']); ?></td>
<td><a href="delete.php?id=
<?php echo (int) $c['id']; ?>">Remove</a></td>
</tr>
<?php } ?>
</table>
<?php } else { ?>
<p>None</p>
<?php } ?>
</body>
</html>
sqlite> .dump
BEGIN TRANSACTION;
CREATE TABLE items (
id integer not null primary key,
name text not null,
due integer not null,
complete integer null,
priority integer not null,
status integer not null
);
INSERT INTO "items" VALUES(1,
'Buy anniversary present',1184437800,NULL,4,1);
INSERT INTO "items" VALUES(2,
'Send invoices',1180549800,NULL,5,1);
INSERT INTO "items" VALUES(3,
'Finish homework',1180549800,NULL,1,0);
INSERT INTO "items" VALUES(4,
'Visit Jane',1180290600,NULL,2,1);
INSERT INTO "items" VALUES(5,
'Finalize hotel reservations',1181737000,NULL,5,1);
INSERT INTO "items" VALUES(6,
'Angie''s birthday',1182537000,NULL,4,1);
INSERT INTO "items" VALUES(7,
'Weed the lawn',1182237000,NULL,1,1);
COMMIT;