H2Databaseを追っかけていたりしたブログ

H2 database のリリースノートを読んだりとか。

Version 1.3.174 (2013-10-19)

約3ヶ月ぶりのリリースでした。最近は、MVS周りの実装に力が注がれていて、その他はあんまり。あと、Geometory関連は粛々とパッチが送られてきているのできっと充実してきているのかな。

QUERY_STATISTICSまわりは実装大丈夫なのかなと言う気はしますが、パフォーマンスチューニングに便利でしょう。ちょっと突っ込んでみてみたい。

久しぶりにConsoleツールの不具合を見つけたので、久しぶりにパッチ書いてみようかな。

  • LIRSキャッシュ: 空のエントリのみを含むキャッシュについてのバグ修正
    • よくわかりません。
  • インメモリデータベースで、ハッシュインデックスのカラムでgroup byを行った場合RuntimeExceptionがスローされていた
  • 一部のシンタックスエラーについてエラーメッセージの改善
  • ファイルシステム抽象化: 直接使用された場合、いくつかのファイルシステムで正しくバイトが出力されなかった(データベースエンジンはこれらを使用していない)
  • MVStoreストレージエンジン(現在開発中)を使用するため、データベースURLに";mv_store=true"を設定可能に。テーブルを作成する際にMVTableEngineを使うのは推奨されなくなった。
  • ユーザ定義関数をコンパイルするために、使用できるようであれば、javax.tools.JavaCompilerが使われるようになり、テンポラリファイルも作成されなくなった。これにより並列に同一のユーザ定義関数を作成する際の問題が解決される。無効にするためには、システムプロパティ"h2.javaSystemCompiler"をfalseに設定する。
  • 異なるデータベースで並列にfunction aliasを作成すると例外"javac: file not found"が発生する結果となっていた。
  • 関数 "regexp_replace"が不正な置換文字列が渡された場合に適切ではない種類の例外をスローしていた。
  • チェックポイントがMAX_LOG_SIZEの二分の一ごとに実施されるようになった(変更前はMAX_LOG_SIZE毎)。そのためトランザクションログはそれほど大きくならなくなった。
  • MVStore table engine: 古いデータを保持する時間を設定するための新しい設定"retention_time"。デフォルトは45秒。
  • TableEngine.createTable()メソッドがTableオブジェクトを返すようになった。
  • 読み込み専用のデータベースでトレースレベルが"debug"のとき、トレース情報がテンポラリのディレクトリに出力されるようになった
    • 元々は出力されていなかったようです。
  • ファイルロックのクローズがバックグラウンドのスレッドが停止されるのを待つようなった。
  • version 1.3.172にて、Issue 389(複合主キーがある場合に必ずしも正しいインデックスを選択できない)の修正においてパフォーマンスのリグレッションが発生していた。これは"order by"のカラムリストにマッチしたインデックスを選択されやすくする機能に関連している(適切ではないインデックスが使用されるケースがあった)
  • 位置情報索引とデータタイプの改善。
  • Issue 467: OSGiクラスローダー(他のクラスローダー内のクラスへの参照を作成できるようにする。例えば他のOSGi bundleなど)
  • 集約関数を使ったサブクエリを含む"group by"クエリでいくつかのケースで不正な結果を返していた。
  • 検索のキーとテーブルのハッシュインデックスの型が異なる場合、不正確な結果になっていた。例えば、片方がINTで片方がLONGという場合。
    • 原文は"Fix bug in unique and non-unique hash indexes which manifested as incorrect results when the search key was a different cardinal type from the table index key. e.g. where the one was INT and the other was LONG"
  • Bug: データベースの定義が変わった際に、クエリーキャッシュの無効化が行われていなかった
  • 新機能: データベース毎にカスタムのJavaオブジェクトのシリアライズエンジンを使えるようになった
  • とても大きなデータベース(6G超)においてリカバリーツールが走っている時、いくつかの統計がマイナスの値を返していた
  • 大きなトランザクションを保持しているセッションを検知するために、SESSIONSテーブルにCONTAINS_UNCOMMITEDカラムが追加された
    • 機能追加当初は、UNDO_LOG_SIZEというカラムだったのが途中で変更された
  • いくつかのGEOMETRY関連の修正
  • BNFツールとオートコンプリートの機能がOSGi向けにエクスポートされた。これによりエディタでオートコンプリートが可能になるかもしれない。
  • DROP ALL OBJECTSとDROP SCHEMAにおいて導出項目による依存関係チェックの修正
    • 原文は、"Fix DROP ALL OBJECTS and DROP SCHEMA in the presence of tables with computed column dependencies."。正確に訳すとなんでしょうね。
    • CREATE TABLE B (B INT AS SELECT A FROM A)みたいに作られたテーブルの依存関係のチェック。
    • あまり細かくは見てないのですが、元々動作上は問題なさそうな感じでしたが、単体のテーブルをドロップする場合(この例だと、drop table a した場合)に、エラーメッセージが二度表示されていたのが一回になっています。これは副次的な効果なのかもしれませんが。
    • とか見てるときに、しょうもないバグ(org.h2.tools.Shell周り)見つけた...。
  • セッションテンポラリのLOGが時々累積されてしまっていて、シャットダウンまでDBファイルのサイズが増加してしまっていた。コミット毎にクリアされるようになった。
  • 1カラムより多いカラムを持つハッシュインデックスが暗黙に通常のインデックスに変換されてしまうバグがあった。このような場合は、例外をスローするようになった。
    • CREATE HASH INDEX ON HOGE(ID, ID2)みたいな場合。
  • クエリの統計: 直近に実行された100のSQLとそのパフォーマンスデータが蓄積されるようになった。パフォーマンスが悪いクエリを見つけ出すのに有用。
    • ヘルプにもかいてありますが、SET QUERY_STATISTICS=trueで有効化、select * from infromation_schema.query_statisticsで参照可能です。
    • が、なんだかちょっと動きが微妙な気が...
  • LOBカラムの読み込みと更新による、LOBデッドロックの修正
  • JDBC DatabaseMetaData#getClientInfoProperties()メソッドのサポート。空の結果を返却する。これはWebSphereの為に実装された。
  • Server#openBrowserが環境変数$BROWSERをチェックするようになった。
  • org.h2.util.ScriptReaderの閉じられていないブロックコメントのハンドリングの修正
  • 巨大なステートメントを生成する壊れたスクリプトをハンドリングする際により適切な例外をスローするようにした
    • "Make org.h2.util.ScriptReader throw a better exception when handling broken scripts which generate extremely large statements."
    • この修正により、1ステートメントの長さが約1GB(というかInteger.MAX_VALUE/2)に制限された
  • ALLOW_LITERALS=NONEの場合、insertの際に定期的に実行されるテーブルのアナライズで、例外が発生しており、そのバグの修正。コンソールツールの同様の問題も修正された。
    • INFORMATION_SCHEMA.SETTINGSテーブルからMODEを取得する際に内部的に使っていたSQL文がNAME='MODE'とリテラルでかかれていたのでそれでエラーになっていた模様。
  • Issue 510: 利用者のために org.h2.bnfをpublicにした
    • 原文は"Issue 510: Make org.h2.bnf public for consumption by external projects, patch by Nicolas Fortin"
    • あと、追加でプロシージャ(エイリアス)もBnfオブジェクトに入るようになったとか。
  • Issue 509: ジオメトリデータ型の重要な修正。ValueGeometry#getDimensionCount の信頼性の向上。不正なジオメトリ値をチェックするユニットテストの追加。ジオメトリ型からオブジェクトへの変換に関してのユニットテストの追加。ジオメトリ値がWKBで表現できるかチェック。
  • Issue 506: デッドロックが発生した場合に備えて、Thread.getName()もデッドロックの際のエラー表示に含めてほしい。
  • "GRANT ALTER ANY SCHEMA TO "のサポート

h2databaseを読む

f:id:ysobj:20131005032941p:plain

H2のデータファイルの内部構造(PageStore)。データファイルは最低で5ページ。デフォルトのページサイズは8192バイト。指定可能なページサイズは64バイトから32768バイトで、2の累乗である必要がある。

0ページ目はヘッダ。ページサイズと、書き込みバージョン、読み込みバージョンが記載されている。

1,2ページ目は、書き込み回数と、現行のトランザクションログのキーやページが書き込まれている。あと、それらのデータのCRC32。1ページ目と2ページ目の内容は完全に同一で、通常は1ページ目だけが使用されている。1ページ目を読み込んで、CRC32でチェックし破損が認められた場合、2ページ目の内容が使われるらしい。

3ページ目は、フリーリストページの1ページ目。

4ページ目は、メタテーブルのルートページ。

loader周りの動作確認

下記のようにrequireの第一引数としてオブジェクトを渡してあげるとloaderのフェーズ毎にログ出力されるようになる。

      require({$
            trace:{$
                  "loader-inject":1, // turn the loader-inject group on
                      "loader-define":0 // turn the loader-define group off
                        }
                      },["dojo/dom","dojo/dom-construct","dijit/form/TextBox","ysobj/Hoge",
        //--
        "dojo/domReady!"], function(dom,domConstruct,TextBox,Hoge){
       }

こんな感じ。

trace:loader-inject:script, dojo/dom, dojo/dom.js dojo.js:1672
trace:loader-inject:script dojo/dom-construct, dojo/dom-construct.js dojo.js:1672
trace:loader-inject:script, dijit/form/TextBox, dijit/form/TextBox.js dojo.js:1672
trace:loader-inject:script, ysobj/Hoge, ysobj/Hoge.js dojo.js:1672
trace:loader-inject:script, dojo/domReady, dojo/domReady.js dojo.js:1672

設定可能な項目は、下記の通り。

			"loader-inject":0,
			"loader-define":0,
			"loader-exec-module":0,
			"loader-run-factory":0,
			"loader-finish-exec":0,
			"loader-define-module":0,
			"loader-circular-dependency":0,
			"loader-define-nonmodule":0

vimでdojoの開発をしやすくなる1行。

dojoというかAMDでモジュール化されているjavascript全般ですが。

.vimrcに以下の行を追加。

autocmd FileType javascript setlocal includeexpr=substitute(v:fname,'$','.js','') | setlocal path+=;/

下記のdefineにかいてあるような、モジュールID(パス)の上でgfで、該当のファイルが開くように。

define([
	"require",
	"dojo/_base/declare", // declare
	"dojo/dom-class", // domClass.toggle
	"dojo/has",			// has("dijit-legacy-requires")
	"dojo/_base/kernel", // kernel.deprecated
	"dojo/_base/lang", // lang.trim
	"dojo/ready",
	"./_FormWidget",
	"./_ButtonMixin",
	"dojo/text!./templates/Button.html"
], function(require, declare, domClass, has, kernel, lang, ready, _FormWidget, _ButtonMixin, template){

//

}

テンプレートのhtmlも開けるし、かなり快適です。おススメ。

サジェスト機能を作ってみた

サジェスト機能作ってみようかなと思って調べてみたら、下記のエントリを見つけて、ああそうかとおもったので作ってみた。

http://tech.naver.jp/blog/?p=367

ソースコードは下記。Eclipseのプロジェクトになっている。gitのリポジトリを作ったときにフォルダの位置を間違えてしまっているのが非常にかっこわるい感じであるのだけれど。

https://github.com/ysobj/zouka

とりあえず、Appを実行すれば、Tomcatが4126番ポートで起動するので、下記URLにアクセスしたら、なんとなく動く感じになっている。

http://localhost:4126/test/sample.html

コミットしてあるdata.txtには十数件しか入っていないが、60000件位でも十分実用的な時間で動作している。

インデックスはパトリシア木。構文解析にはkuromojiを使っているので、辞書も内包されているので、一層お手軽に試せる感じになっている。

クライアントは、CDNにあるdojotoolkitを使っているので、インターネットアクセス必須。

Version 1.3.173 (2013-07-28)

今回は、PgServer周りの修正が多かったです。PgServer経由で使っているところから、多数のパッチが寄せられたようです。あと、GEOMETRYデータタイプとspatial indexが追加になったのがさらっと書いてありますが、大きいのでは。これも、パッチの提供があったみたいです。(地味に、僕がレポートした不具合も修正されました)

  • コメントしか含んでいないような空のステートメントのサポート
  • Server mode: LOBの読み取り中にエラーがあった場合、いくつかのケースでセッションがクローズされていた
  • Issue 463: OsgiDataSourceFactory と JdbcDatabaseMetaDataでDriverの名前とバージョンが同じになった
  • JaQu: データタイプTEXTではなくVARCHARがStringの為に使われるようになった(明示的に指定された場合を除く)
  • インメモリデータベースでCLOBやBLOBに対するインデックスの作成がサポートされなくなった。おれはMVTableEngineの簡易化のため。
  • INFORMATION_SCHEMAに対する新しいカラムの追加: "information_schema.tables.row_count_estimate"
  • Issue 468: trunc(timestamp)が間違った値(12時間プラスされる)を返す事があった。また、trunc(number)でNullPointerExceptionがスローされる場合があった。
  • trunc(number)という表現でNullPointerExceptionをスローしていた
  • LOBを同時に更新する場合のデッドロックを修正。TestLob.testDeadlock2()を参照。
  • とても大きな結果セットに関連するデッドロックを修正。
  • "-list"オプションがシェルツールに追加。これは、ファイルから読み込むときに、結果セットをリストモードで表示するため。
  • Issue 474: MySQLの互換モードで、CREATE TABLE文中のCOMMENTを無視するのに失敗していた。
  • Issue 476: jaqu.htmlページ内でリンク切れ
  • org.h2.store.FileStore内でのUTF8エンコーディング関連の潜在的な問題の解消
  • 整合性がとれていなかった場合のエラーメッセージの改善
  • ユニーク制約に違反した場合のエラーメッセージの改善。問題となったキーをエラーメッセージ中に表示。
  • Issue 478: "SHOW TRANSACTION ISOLATION LEVEL"のサポート
  • Issue 475: pgServer: CancelRequestのサポート
  • Issue 473: pgServer で -key オプションが実装されていなかった
  • Issue 471: CREATE VIEWでユーザの権限をチェックしていなかった
  • Issue 477: PgServerのクエリパラメータのバイナリ転送が実装されていない
  • Issue 479: FROM句なしでのSUBSTRINGのサポート。
  • Issue 472: PgServerが最近のいくつかのPostgres JDBC driverで動作しない
  • 追加パラメータを自作のTableEngine実装に渡すための文法の追加。
  • Issue 480: Issue 475,477についてのバグフィックス
  • Issue 481: よりよいPG JDBCのサポートのための更なる拡張
  • 空間的データタイプGEOMETRYのサポート
  • インメモリの空間データタイプインデックスのサポート
  • 更新頻度が非常に高いデータベースのためにPageStore#changeCountフィールドをintからlongに変更
  • ビューに対する外部参照を追加しようとした場合のNullPointerExceptionを修正
  • WebSphere向けに、javax.sql.Connectionの実装でClientInfo取得をより効率的にした
  • Issue 482: class LobStorageBackend$LobInputStreamがInputStream.available()メソッドをオーバライドしていない
  • オプション"WRITE_DELAY=0"の時に、"SELECT DISTINCT"を使用して、メモリからあふれた場合に結果が壊れる点を修正
  • LOBを含むテーブルへの更新と、LOBの読み取りが同時に行われた場合の問題を修正。以前は、例外をスローしていたが、今は動作するようになった
  • Issue 484: H2 Console toolで、INFOで始まるすべてのスキーマが非表示になっていた。データベースがH2でない場合のみ、非表示にするようになった
    • どうなのかなこれ。
  • MySQL compatibility: CREATE TABLE文にて、"AUTO_INCREMENT=3"部のサポート
  • Issue 486: MySQL compatibility: CREATE TABLE文にて、"DEFAULT CHARSET"部のサポート
  • Issue 487: MySQLの"SET foreign_key_checks = 0"コマンドのサポート
  • Issue 490: MySQLの"USING BTREE"インデックス定義のサポート
  • Issue 485: create tableステートメントの中で定義された整合性制約のカラム名がリネームされるとデータベースが破損する
  • Issue 499: MySQLの"UNIQUE KEY (ID) USING BTREE"制約シンタックスのサポート
  • Issue 501: "CREATE TABLE .. WITH"がシリアライズされていなかった
  • JAVA_TOOL_OPTIONS環境変数をセットしていて、ALIASメソッドを実行時にコンパイルする場合の問題の回避

Version 1.3.172 (2013-05-25)

MVStore周りの修正が多いですが、オプティマイザでインデックスを選択する際にorder byの項目もコスト計算に含めるようになったりとちょっと面白い。あと、CREATE ALIASでGroovyが使えるようになったというのは、結構面白いのではないかと。

  • 参照整合性: 参照整合性制約追加に失敗した時、自動的に作成されたインデックスが削除されていなかった
  • 自動アナライズ機能で、今後はテーブル毎に10000行の代わりに1000行しか読まなくなった
  • IN(...)クエリとORを組み合わせた場合、最適化により"列 x はリストによりグループ化されなければなりません"という奇妙な例外が発生していた
    • 簡単には再現しなかった。しかし、x = 1 or x = 2 はx in (1,2)に最適化されたが、x = 1 or x = 2 or x = 3はx in (1,2) or x = 3にしか最適化されない?っぽい。
    • と思ったら、1.3.172で最終的には最適化された。
  • Issue 454:型安全なキャラセットの使用
  • LIMIT と OFFSETを同時に使ったクエリでIllegalArgumentExceptionをスローする可能性があった
  • MVStore: 複数のIssueのクローズ。460,461,462,464,466。
  • MVStore: 大きなストア(数GB)が少々速くなった
  • ローカルのテンポラリテーブルを使っていて、セッションを閉じる前にドロップせず、プロセスがキルされた場合にデータベースがオープンできなくなった(リカバーツールの使用なしでは)
  • Oracleとの互換性向上のための、TRUNC(timestamp)のサポート。
    • とはいえ、引数を省略した場合のみ。TRUNC(timestamp,fmt)はサポートされない これ書いている間にバグ発見してレポート。次のリリースでなおる模様。
  • PostgreSQL互換性向上のための、CREATE TABLE TEST(ID BIGSERIAL)のサポート
  • 照合順序についての新しいコマンド、SET BINARY_COLLATION UNSIGNEDの追加。これは、MySQLモード時のBINARYカラムのテストに便利。
  • Issue 453: TABLE LINKのコネクションの共有において、ABBA型の競合状態が発生していた。
  • Issue 449: PostgreSQLのSERIALデータ型は自動的にプライマリーキーと認識されるべきではない
    • PostgreSQLモードのときだけ、SERIAL/BIGSERIALが自動的にプライマリーキーにならなくなったんですが、それでいいのか。
  • Issue 406: "select h2version()"のサポート。
    • 一見たいした事ない機能追加でしたが、意外と紆余曲折あり。最初はversion()関数として実装されたものの、PostgreSQLODBCドライバーが返り値に"PostgreSQL"で始まる何かを返すVERSION()関数があることを前提としていて云々。
  • Issue 389: 複合主キーが存在する場合に、必ずしも正しいインデックスを引けるとは限らない。
    • タイトルはそんな感じのものの、実際は、indexA X(a,b)とindexB X(a,c)があったときに、select * from X where a = 1 order by a,cというSQLがきた場合に、order by もコスト計算にいれた上でindexを選択するように対応されている。
  • Issue 305: SELECT … FOR FETCH ONLYのサポート
    • まぁ、エラーにせずに読み飛ばすようになっただけなんですが。
  • Issue 274: Sybase/MSSQLServer 互換性: GETDATE,CHARINDEXのシステム関数の追加
  • Issue 274: Sybase/MSSQLServer 互換性: CONVERT関数の引数の入れ替え
  • Issue 274: Sybase/MSSQLServer 互換性: INDEX節のサポート 例: "select * from test (index table1_index)"
    • これも読み飛ばす。
  • SELECT * FROM A WHERE X=1 OR X=2 OR X=3 を SELECT * FROM A WHERE X IN (1,2,3) に最適化する際の不具合修正
  • Issue 442: SourceCompilerへのGroovyパッチ。
    • classpathにGroovyのjarがあれば、CREATE ALIASでGroovyを使って関数がかけるようになった模様。サンプルはちょっと地味ですが。
  • Issue 459: LOBのドキュメンテーションの改善
    • LOB周りの実装とドキュメントが食い違っていたのでその修正。LOBの実態がどこに置かれるか、システムプロパティのMAX_LENGTH_INPLACE_LOGがどこに影響するか、など。