2010年9月10日金曜日

SafariのopenDatabase

数年前にSafariにSQLiteのデータベースがついて一時期話題になりましたが、その後も色々と改善が続いているようです。その解説が以下のURLにあります。

Safari Client-Side Storage and Offline Applications Programming Guide: Introduction
http://developer.apple.com/safari/library/documentation/iPhone/Conceptual/SafariJSDatabaseGuide/Introduction/Introduction.html

ちなみに、この資料の日付は2010/01/20となっていますが、PDF版の作成日にはいくつかのバージョンがあるようです。

この資料では"HTML 5 Offline Application Cache"や"Key-Value Storage"の解説が追加されています。

データベースに関する解説も手が加えられています。"Relational Database Basics"はSQLの初心者を対象にデータベースの解説があり、"Using the JavaScript Database"でJavaScriptからデータベースを利用する方法が解説されています。"APPENDIX A: Database Example: A Simple Text Editor"にはサンプルコードを元に解説があります。

解説中のサンプル コードはわかりやすい書き方になっていますが、まだ完成には時間が必要ではないかという気がします。例えば"Listing 4-1 Creating and opening a database"にあるデータベースを開く時の処理です。

  1. try {  
  2.  if (!window.openDatabase) {  
  3.   alert('not supported');  
  4.  } else {  
  5.   var shortName = 'mydatabase';  
  6.   var version = '1.0';  
  7.   var displayName = 'My Important Database';  
  8.   var maxSize = 65536; // in bytes  
  9.   var db = openDatabase(shortName, version, displayName, maxSize);  
  10.   // You should have a database instance in db.  
  11.  }  
  12. catch(e) {  
  13.  // Error handling code goes here.  
  14.  if (e == 2) {  
  15.   // Version number mismatch.  
  16.   alert("Invalid database version.");  
  17.  } else {  
  18.   alert("Unknown error "+e+".");  
  19.  }  
  20.  return;  
  21. }  
  22. alert("Database is: "+db);  

この書き方であれば、データベースの名前やバージョンなど後から調べる時に便利そうです。

ただし、このコードは関数内に入っていません。「最初に実行されるコード」の項目の1stの方法と同じようにすればこのままでも動作します。しかし、最初に一度しか実行しないコードをdeleteせずにメモリ上に置いておくのは無駄です。関数の中に入れて最初に実行させ最後にdeleteするのが妥当だと思います。ただし、この時には変数宣言のvar dbは式の外に置いてグローバル変数としないと他の関数でトランザクション処理ができなくなります。

また、サンプルにある// You should have a database instance in db.というコメントは具体的に何をしようとしているのかが分かりません。データベースと同時にtaransaction()メソッドで'CREATE TABLE'を使ったテーブルの処理も考えているのかもしれませんが、この解説ではテーブルの処理は別の関数で処理しています。

古いサンプルコードと比べるとopenDatabaseでデータベースが開けない時の処理がなくなり、try-catchcatchでエラー処理ができるようになったようです。エラー処理は"Handling Errors"と言う項目で解説されていますが、この通りには動いていないように思います。例えば上記のデータベースを開く時のcatchでの処理です。試しにversion1.12.0にすると、アラートに"Invalid database version."が表示されずに、以下のようなエラーとなりました。

"Unknown error Error: INVALID_STATE_ERR: DOM Exception 11."

さらに、"APPENDIX A: Database Example: A Simple Text Editor"にあるエラー処理では2の代わりに変数"INVALID_STATE_ERR"を置いていますが未定義のエラーになります。

エラーのメッセージやエラーコードの処理が紹介されています。ただ、後半部分が何を意味するのか分かりません。
  1. function errorHandler(transaction, error)  
  2. {  
  3.  // error.message is a human-readable string.  
  4.  // error.code is a numeric error code  
  5.  alert('Oops.  Error was '+error.message+' (Code '+error.code+')');  
  6.   
  7.  // Handle errors here  
  8.  var we_think_this_error_is_fatal = true;  
  9.  if (we_think_this_error_is_fatal) return true;  
  10.  return false;  
  11. }  

先にも書いた'CREATE TABLE'を使ったテーブルの処理は"Listing 4-2 Creating a SQL table"に書かれています。しかし、'CREATE TABLE'のSQL文は長文になりやすく、読みにくいものです。以下のような書き方の方が後から調べやすいと思います。また、複数のテーブルを扱う場合には、この方法を発展させると便利になります。

  1. db.transaction(  
  2.  function (transaction) {  
  3.   var mySQL = 'CREATE TABLE people(' +  
  4.    'id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' +  
  5.    'name TEXT NOT NULL DEFAULT "John Doe", ' +  
  6.    'shirt TEXT NOT NULL DEFAULT "Purple");';  
  7.   transaction.executeSql(mySQL, [], nullDataHandler, errorHandler);  
  8.  }  
  9. );  

以上の内容をまとめると、今のところデータベースを開く関数は以下のような書き方が便利そうです。

  1. function initDB() {  
  2.  try {  
  3.   if (!window.openDatabase) {  
  4.    alert('not supported');  
  5.   } else {  
  6.    var shortName = 'mydatabase';  
  7.    var version = '1.0';  
  8.    var displayName = 'My Important Database';  
  9.    var maxSize = 65536; // in bytes  
  10.    db = openDatabase(shortName, version, displayName, maxSize);  
  11.    // You should have a database instance in db.  
  12.    db.transaction(  
  13.     function (transaction) {  
  14.      var mySQL = 'CREATE TABLE people(' +  
  15.       'id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' +  
  16.       'name TEXT NOT NULL DEFAULT "John Doe", ' +  
  17.       'shirt TEXT NOT NULL DEFAULT "Purple");';  
  18.      transaction.executeSql(mySQL, [], nullDataHandler, errorHandler);  
  19.     }  
  20.    );  
  21.   }  
  22.  } catch(e) {  
  23.   // Error handling code goes here.  
  24.   if (e == 2) {  
  25.    // Version number mismatch.  
  26.    alert("Invalid database version.");  
  27.   } else {  
  28.    alert("Unknown error "+e+".");  
  29.   }  
  30.   return;  
  31.  }  
  32.  // alert("Database is: "+db);  
  33.  delete initDB;  
  34. }  

0 件のコメント: