2008年3月22日土曜日

Safari 3.1の「Client-side Database Storage」を試してみた。

Safari 3.1がリリースされました。このバージョンでは、HTML5の仕様の一部が実装されています。その一つに「Client-side Database Storage」があります。仕様(Working Draft)はココで確認できます。

なかなか面白そうな機能だったので、ちょっと試してみました。情報源は、WebKitの開発ブログ「WebKit Dose HTML5 Client-side Database Storage」とサンプル「Notes Demo」です。


■データベースは「なに」を使っている?
SQLite」です。


■データベースは「どうやって」確認する?
今、Safariが管理しているデータベースに関しては、メニューバー「Safari」→「環境設定」→「セキュリティ」→「データベースの表示」から確認する事が出来ます。また、ここからデータベースの削除を行う事もできます。


↑「データベースの表示」をクリックすると、データベースの一覧が表示される。「取り除く」を実行するとデータベースファイルが削除されます。(物理的に消えます)


■データベースは「いつ」作成されるのか?
これは、javascriptで「openDatabase()」が実行された時です。(これはSQLiteっぽいですね!)


■データベースは「どこ」にあるのか?
「~/Library/Safari/Databases」以下です。もう少し詳しく紹介すると、管理用のデータベース「~/Library/Safari/Databases/Databases.db」とドメイン毎に実データを格納するデータベースが存在します。例えば「Notes Demo」にアクセスしていた場合、「~/Library/Safari/Databases/http_webkit.org_0/0000000000000001.db」がデータベースの実体になります。(ファイル名は異なるかもしれません。)

という訳で、クライアント側のデータベースをクロスドメイン対応する事は、出来ない or 何らかのテクニックが必要になると思われます。

※このディレクトリ、ファイルはSafariが自動的に管理(作成、削除)されるので、利用するだけなら意識する必要ありません。一応、念のため。


■「Databases.db」ってどんなデータベース?
クライアント側のデータベースを管理するためのデータベースです。Safariで操作した時やjavascriptの「openDatabase()」を実行した時に更新されているようです。また、このデータベースには、テーブル「Databases」と「Origins」が含まれています。「Notes Demo」にアクセス済みの状態で、「.schema 〜」と「select * from 〜」を実行した結果をまとめてみました。
  • 「Databases」のスキーマ
    CREATE TABLE Databases(
    guid INTEGER PRIMARY KEY AUTOINCREMENT,
    origin TEXT,
    name TEXT,
    displayName TEXT,
    estimatedSize INTEGER,
    path TEXT
    );
  • 「Databases」のデータ
    1
    http_webkit.org_0
    NoteTest
    HTML5 Database API example
    200000
    0000000000000001.db
  • メモ
    - 「guid」は、オートインクリメントされる数値で、データベースのファイル名と関連しているようです。
    - 「origin」は「(プロトコル)_(ドメイン名)_(ポート)」の書式です。また、ポートの指定が無い時は「0」となります。(80番の時に「0」になるのかも...)
    - 「name」はデータベースの名前で、大文字、小文字は区別されます。
    - 「displayName」はSafariの「データベースの表示」の「名前」として表示されます。
    - 「estimatedSize」はまだよく分かっていません...。
    - 「path」はデータベースのファイル名です。00...が付いて16桁になりますが、「guid」と同じ値です。

  • 「Origins」のスキーマ
    CREATE TABLE Origins (
    origin TEXT UNIQUE ON CONFLICT REPLACE,
    quota INTEGER NOT NULL ON CONFLICT FAIL
    );
  • 「Origins」のデータ
    http_webkit.org_0
    10485760
  • メモ
    - 「origin」は「Databases」表の「origin」と同じデータ。ただし、制約が付けられているところをみると、コチラがマスターデータと思われます。
    - 「quota」はSafariの「データベースを表示」→「最大サイズ」で設定した値が入る。データベースの容量制限に用いられる思われます。

■「0000000000000001.db」ってどんなデータベース?
クライアント側のデータベースです。ここに実データが格納されます。また、データを格納するテーブルとは別に「__WebKitDatabaseInfoTable__」が作成されます。これも管理用テーブルのようです。
  • 「__WebKitDatabaseInfoTable__」のスキーマ
    CREATE TABLE __WebKitDatabaseInfoTable__ (
    key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,
    value TEXT NOT NULL ON CONFLICT FAIL
    );
  • 「__WebKitDatabaseInfoTable__」のデータ
    WebKitDatabaseVersionKey
    1.0
どうもデータベースの「バージョン」に関係しているようですが、現時点では詳細不明です...。


■JavaScriptからデータベースにアクセスする方法は?
Notes Demo」のソースコードが参考になると思います。データベースの初期化処理、CRUD全てを網羅していて、サンプルとして非常によく出来ていると思いました。ポイントとなりそうな箇所をピックアップしてみました。
  • データベースを開く処理(57行目〜64行目)
    try {
    if (window.openDatabase) {
    db = openDatabase("NoteTest", "1.0", "HTML5 Database API example", 200000);
    if (!db)
    alert("Failed to open the database on disk. This is probably because the version was bad or there is not enough space left in this domain's quota");
    } else
    alert("Couldn't open the database. Please try with a WebKit nightly with this feature enabled");
    } catch(err) { }

    window.openDatabaseでクライアント側のデータベースを利用できるか判定しています。

    openDatabae()の引数は左から、「データベース名」、「バージョン」、「ディスプレイ名」、「サイズ」です。データベースを開くには、「データベース名」だけでもOKです。その場合、ディスプレイ名などは「undefined」や「0」などが設定されます。(openDatabase()される時に管理情報が更新されています)例えば、Safariから管理する時にデータベースの名前が「undefined」と表示されてしまうので、実際には設定した方が良さそうです。

  • データベースの初期化処理(287行目〜298行目)
    function loaded()
    {
    db.transaction(function(tx) {
    tx.executeSql("SELECT COUNT(*) FROM WebkitStickyNotes", [], function(result) {
    loadNotes();
    }, function(tx, error) {
    tx.executeSql("CREATE TABLE WebKitStickyNotes (id REAL UNIQUE, note TEXT, timestamp REAL, left TEXT, top TEXT, zindex REAL)", [], function(result) {
    loadNotes();
    });
    });
    });
    }

    executeSql()の引数は、左から「SQL文」、「バインド変数の配列」、「コールバック関数」、「エラー発生時のコールバック関数」です。

    初回アクセス時、SQL文「SELECT COUNT(*) ...」を実行しても、まだテーブルも作られていない状態なのでエラーが発生します。その場合、エラー発生時のコールバック関数が実行され、「CREATE TABLE ...」でテーブルが作成されるようになっています。

  • データベースのCRUD
    - C(INSERT文):230〜233行目
    db.transaction(function(tx) {
    tx.executeSql("SELECT id, note, timestamp, left, top, zindex FROM WebKitStickyNotes", [], function(tx, result) {
    for (var i = 0; i < result.rows.length; ++i) {
    var row = result.rows.item(i);
    var note = new Note();
    note.id = row['id'];
    note.text = row['note'];
    note.timestamp = row['timestamp'];
    note.left = row['left'];
    note.top = row['top'];
    note.zIndex = row['zindex'];

    if (row['id'] > highestId)
    highestId = row['id'];
    if (row['zindex'] > highestZ)
    highestZ = row['zindex'];
    }

    if (!result.rows.length)
    newNote();
    }, function(tx, error) {
    alert('Failed to retrieve notes from database - ' + error.message);
    return;
    });
    });

    - U(UPDATE文):219〜222行目
    db.transaction(function (tx)
    {
    tx.executeSql("UPDATE WebKitStickyNotes SET note = ?, timestamp = ?, left = ?, top = ?, zindex = ? WHERE id = ?", [note.text, note.timestamp, note.left, note.top, note.zIndex, note.id]);
    });

    - D(DELETE文):178〜181行目
    db.transaction(function(tx)
    {
    tx.executeSql("DELETE FROM WebKitStickyNotes WHERE id = ?", [note.id]);
    });
    いずれも、executeSql()で実行されます。SQL文の「?」はバインド変数を意味します。また、「?」の出現位置と2番目の引数で渡される配列の位置がマッピングされます。


以上、Safari3.1の「Client-side Database Storage」に関して、調べた事をまとめてみました。まだ手探りな状態なので、推測ベースの情報が多く含まれています。その点はご了承下さい。

0 件のコメント: