Database Events (6)

久しぶりに Database Events。Database Events は、一連の記事(Database Events (1)Database Events (2)Database Events (3)Database Events (4)Database Events (5))を書いて以来、いつか使えるといいな...といった感じで暇なときに時々いじっていたりしました。どうにもパフォーマンスが悪いし...きっと使い方が悪いのかもしれないと思い。

ところで...。iTunes 関連のスクリプトで有名な Doug's AppleScripts for iTunes に Most Played Artists というスクリプトがあります。これ、試してみたことあるでしょうか?

iTunes でのアーティストごとの再生回数をランキング形式でファイルに書き出してくれるものですが、Database Events を使っています。このスクリプトを開いてみると最初にコメントが書かれていますが、ここに 5154 曲の再生回数を集計するのに 6 分 18 秒とあります。Dual 1.8GHz G5 で。

Database Events は、どうにも遅かったような...と思い、はたと気づく。スクリプトをいったんアプリケーション形式で保存して実行してみます。2232 曲で 4 分。スクリプトのままスクリプトメニューから実行してみる。2232 曲で 1 分 50 秒...。これ、使えるじゃん。Script Editor で実行していたから遅かったのか。迂闊だった...。

試してみると、他のアプリケーションでも同じでした。スクリプトメニューから実行すると処理が早くなる。Script Editor から試すと遅い。開発環境になんて不向きなんでしょう。まさか、これって既知のこと?うわっ、時代に取り残されていたよ...。

そんなわけで急遽 Database Events に取り組む。Database Events が使えるなら、なんだか可能性が増える感じがするなー、と思う。根拠もアイデアもないけど。

DBMS を使うとき、最初にデータベースの構造を設計すると思います。後で変更できないからですね。が、Database Events では、変更し放題です。record には、field いくつでも作ることができます。また、最初の record と 次の record が同じ field を持たないといけないということもありません。record と field の name 属性は、同じものが複数あってもかまいません。database を必ずファイルに保存しないといけない、ということもありません。処理のために一時的なデータ置き場として使うことが可能です。使い捨てとして。Doug's AppleScripts for iTunes の Most Played Artists は、まさにそういった使い方をしています。

Database Events であるデータベースファイルを開くと、他のスクリプトからでもそのデータベースを使うことができ(共有される)、また、閉じることも可能です。たとえ、処理中であっても。これを防ぐ方法はありません。with transaction が解決法として思い浮かびますが、Database Events は対応していません。Database Events は、終了するときにすべてのデータベースを閉じます。これも、他のスクリプトで何らかの処理を行っていても、否応無しにデータベースは閉じられます。System Events に追加された一連のトランザクション関連の命令(abort transaction、begin transaction、end transaction)が解決方法を提供してくれるのかもしれませんが、使い方は Web 上では発見できず。同じ疑問を書いていた人はいたけど。これは、探し方が悪いのかもしれませんが。

database は、write 命令のファイル書き出しのようにデータベースファイルを開き、閉じることができます。対応するのは、open 命令と close 命令。が、close 命令は不安定で時々エラーになります。確実に閉じることができるのは delete 命令。delete 命令は、record と field の削除も行えます。open 命令でファイルをデータベースファイルを開くことができますが、POSIX path を指定する必要があります。make 命令で database を作るとき、location 属性に指定するのは、POSIX file の方です。location 属性以外は、すべて POSIX path で指定します。

exists 命令では開かれているデータベースが存在するかどうかを確認することができるとともに、POSIX path を指定することでデータベースファイルが存在するかどうかも確認することができます。

ある一つのデータベースを複数のスクリプトから変更すると、後から来た方の命令でデータは上書きされます。このとき、トランザクションを行っていなくてもデータの不整合がおこることはありません。どうも、内部的に適切に処理を行っているような感じがします。ただ、処理中に他のスクリプトからデータベースを閉じられる可能性がある、ということに留意しておく必要はあります。このときはもちろんエラーになり、データは正しく保存されません(もしくは、データベースが壊れる)。

open 命令は、既存のデータベースを開くことができますが、open するたびに Database Events で開かれているデータベースの数が増えていきます。これは、make 命令も同じです。

と、まあいろいろと書きましたが、実際のスクリプトで見ていきましょう。まず、デスクトップに PREF.dbev というデータベースがあります。open 命令で開くには、以下のようにします。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    set db to open database (POSIX path of dbPath)
    databases
    --> {database "PREF" of application "Database Events", database "PREF" of application "Database Events"}
end tell

データベースファイルの POSIX path を指定し、かつ、database とクラスを指定します。これで開くことができるのですが、結果はなぜか同じものが二つ返ってきます。実のところ...というほどでもないのですが、データベースファイルを開くには以下のようにするだけでいいのでした。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    set db to database (POSIX path of dbPath)
    databases
    --> {database "PREF" of application "Database Events"}
end tell

database (POSIX path) とするだけでデータベースが開かれます。その結果に open 命令を行っているので同じものが複数返ってくるのです。以下のような感じですね。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    set db to database (POSIX path of dbPath)
    set dbCopy to open db
    databases
    --> {database "PREF" of application "Database Events", database "PREF" of application "Database Events"}
end tell

open 命令は、既に開いているデータベースを再度開き、同じものを結果として返します。複製を作っているような感じなのですが...何度も繰り返して open を行うと、使わないにもかかわらず開かれているデータベースの数は増えていきます。上記のスクリプトを何回か繰り返して実行してみると分かると思います。これは、make 命令も同じです。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    make new database with properties {name:theFile, location:dbPath}
    databases
end tell

このスクリプトを何回か繰り返してもエラーにはなりません。ただ、開かれているデータベースの数が増えるだけです。不必要にも関わらず、同じデータベースが複数開かれているということもありえるので注意が必要です(open 命令ではなく、データベースファイルのパスを指定する方では、数は増えません)。

データベースを閉じるのが close 命令です。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    set db to database (POSIX path of dbPath)
    set dbCopy to open db
    close db
    databases
    --> {database "PREF" of application "Database Events"}
end tell

また、delete 命令でも閉じることができます。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    set db to database (POSIX path of dbPath)
    set dbCopy to open db
    close db
    delete dbCopy
    databases
    --> {}
end tell

ただ、close 命令は(何が原因か分かりませんが)エラーになることがあります。

exists で database(もしくは、record や field)の存在を確認することができます。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    set db to database (POSIX path of dbPath)
    exists database "PREF"
    --> true
end tell

exists にパスを指定することでデータベースファイルが存在するかどうかの確認もできます。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    exists database (POSIX path of dbPath)
    --> true
    databases
    --> {database "PREF" of application "Database Events"}
end tell

存在すれば真を返し、データベースは開かれます。存在しないと偽を返し、エラーにはなりません。データベースファイルがなければ作成し、あればそれを開くという処理は open 命令を使うよりも exists 命令を使う方がいいでしょう。

ただ、Database Events はどんなファイルであってもデータベースとして開いてしまうようで、例えば、テキストファイルを指定してもデータベースとして開いてしまいます。以下のスクリプトは、スクリプトファイルを指定した例です。

Script Editor で開く

set theFile to "Database.scpt"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    exists database (POSIX path of dbPath)
    --> true
    databases
    --> {database "Database" of application "Database Events"}
end tell

開けるからといって使えるわけではありません。この辺りの注意はスクリプトを作る人の責任になる...のでしょうね。

Database Events は、起動しているあいだに開かれたデータベースをすべて知っています。

Script Editor で開く

set theFile to "PREF.dbev"
set dir to path to desktop as Unicode text
set dbPath to dir & theFile

tell application "Database Events"
    exists database (POSIX path of dbPath)
    databases
    --> {database "PREF" of application "Database Events"}
end tell

このスクリプトを実行します。その後、新規ドキュメントを作って以下のスクリプトを実行します。

Script Editor で開く

tell application "Database Events"
    delete databases
    databases
    --> {}
end tell

このように他のスクリプトから閉じることができます。もちろん、操作もできます。が、処理中のデータベースがあるかどうかを調べることはできません。Database Events は、終了時にすべてのデータベースを自動的に閉じます。そのために quit delay という属性で Database Events が起動してから何秒後に終了するかを指定することができます。ただ、データベースを修正したかどうか、セーブする必要があるかどうかといったことを調べる方法は Database Events には用意されていません。しかし、修正されていた場合、データベースを閉じるときに Database Events が保存を行います(データベースがファイルとして既に保存されているなら、です)。Database Events が終了するときではありませんので注意を。

他のスクリプトからでも開かれているデータベースにアクセスできますが、操作の競合やデータの不整合が起こることはないようです。処理は順番待ちで実行されます。小難しい処理は Database Events が責任を持って行っている、という感じでしょうか。データベースの操作にそれほど神経質にならなくてもいいのかもしれません(あくまで、主観。もしかしたら、全然違うかもしれません)。

最後に一つだけ書きたいことがあるのですが...さすがに、これ以上書くと長くなるので次回に続く...ということで。

0 件のコメント :

コメントを投稿