How to use of changeVersion

HTML5のclient-side databaseはバージョン管理されています。Asynchronous database APIでは、このバージョンが変わった際に実行するハンドラを登録する仕組みが提供されています。

The changeVersion() method allows scripts to atomically verify the version number and change it at the same time as doing a schema update. When the method is invoked, it must immediately return, and then asynchronously run the transaction steps with the transaction callback being the third argument, the error callback being the fourth argument, the success callback being the fifth argument, the preflight operation being the following:

1. Check that the value of the first argument to the changeVersion() method exactly matches the database's actual version. If it does not, then the preflight operation fails.

...the postflight operation being the following:

1. Change the database's actual version to the value of the second argument to the changeVersion() method.
2. Change the Database object's expected version to the value of the second argument to the changeVersion() method.

...and the mode being read/write.

If any of the optional arguments are omitted, then they must be treated as if they were null.

http://dev.w3.org/html5/webdatabase/#asynchronous-database-api

ということで試してみました。

function initDb(callback_success){
	getDbConnection().changeVersion("1.0", "1.1",
		function(t){
			t.executeSql('CREATE TABLE cal_events_new(id INTEGER PRIMARY KEY, serverKey TEXT, year INTEGER, month INTEGER, day INTEGER, title TEXT)');
			t.executeSql('INSERT INTO cal_events_new(id, year, month, day, title) SELECT id, year, month, day, title FROM cal_events');
			t.executeSql('DROP TABLE cal_events');
			t.executeSql('ALTER TABLE cal_events_new RENAME TO cal_events');
		},
		function(error){
			window.console.log("(initDb)db error:" + error.message);
		},
		function(){
			callback_success();
		}
	);
}
function getDbConnection(){
	return openDatabase(dbName, '1.0', dbName, 5*1024*1024);
}

実行環境はChrome(6.0.472.62)です。バージョンアップ前のバージョンをpreVer、バージョンアップ後のバージョンをpostVerとします。
まず、changeVersionを呼び出す際のdatabaseオブジェクトはperVer、postVerのどちらを指定してopenDatabaseしたものを使用するか、という疑問がありましたが、preVerを指定するのが正しいようです。postVerでは取得できませんでした。postVerのDBが存在していないからでしょう。
バージョンアップとその際のハンドラの実行はうまくいったのですが、その後次の問題が出ました。

  • openDatabaseする際に、preVerを指定するとエラーとなる
    • INVALID_STATE_ERR: DOM Exception 11
  • openDatabaseにpostVerを指定し、changeVersion(preVer, postVer, callback)を呼ぶと、currentVersionと引数が違う、というエラーが出る
    • current version of the database and `oldVersion` argument do not match

後者は、changeVersionの第4引数を以下のようにしてスルーすれば回避できるでしょう。

		function(error){
			window.console.log("(initDb)db error:" + error.message);
			callback_success();
		},

前者の対応をどうするべきか考え中です。openDatabaseの呼び出すをtry-cacheで囲んで、複数のバージョンでトライしてみて、エラーにならなかったものを返す、という方式ならばできなくもないですが。

function getDbConnection(){
	try{
		return openDatabase(dbVer, '1.0', dbVer, 5*1024*1024);
	}catch(ignore){}
	
	try{
		return openDatabase(dbVer, '1.1', dbVer, 5*1024*1024);
	}catch(ignore){}
}

追記:

後者は、changeVersionの第4引数を以下のようにしてスルーすれば回避できるでしょう。

上記はNGでした。理由は次のエントリで記述します。