DBD::SQLite 1.39

気がつけば前回の安定版を出してから丸一年が過ぎてしまいましたが、ようやくDBD::SQLiteの1.39をリリースしました。

それぞれ以前にも書いたことですが、今回の1.39ではimmediate transactionをデフォルトにしたのと、クエリオプティマイザ最適化によってORDER BYのないSELECT文の返り値の並び順がこれまでと異なったものになる可能性があるのが大きな変更点です。

また、やや地味な変更ですが、カラム名などにutf8文字列が含まれている場合の処理が改善されたほか(jamadam++)、bind_paramに渡す変数を再利用しようとしたときに起こる問題も修正されました。

最近のDBD::SQLiteOpenBSDを除くBSD系のシステムでスレッド対応版のPerlを使っているときにうまくコンパイルできていなかった件も暫定的に対応してあります。

その他、細かい変更点についてはChangesファイルをご覧ください。

なお、従来DBD::SQLiteのメンテナンスはAdam Kennedy氏がメール対応やブログエントリの執筆を含めたリリース管理を行い、コードまわりの修正やチケットの対応はおおむね私が行うという分業体制になっていましたが、このところ氏が多忙でなかなか時間がとれなくなっていたため、先日からリリース作業の方も私が行っています。リポジトリも氏のSubversionリポジトリからgithubの方へ移しましたので、バグやらタイポやら見つけましたら下記へpull requestを送っていただけると助かります。

https://github.com/DBD-SQLite/DBD-SQLite

ExtUtils::MakeMakerでPUREPERL_ONLYに対応する方法

先日のランカスター・コンセンサスで、pure perlなモジュールのみインストールするよう強制したい場合はPUREPERL_ONLY=1という値をPERL_MM_OPTに追加するか、コマンドラインオプションとして渡すべし、という話になったわけですが、ふつうのモジュールを書いている人がいちいち環境変数コマンドラインオプションをチェックするのはいかにも面倒ですよね。

いささか行儀の悪い方法ですが、以下のようにEUMMを(最小限の設定で)newしたあと、内部的に利用されているARGSというハッシュキーを利用すると、(いまのところ未知のオプションということで警告は出るものの) PERL_MM_OPTやコマンドラインオプションの処理が終わった状態のPUREPERL_ONLYの情報がとれるようになります。

use strict;
use warnings;
use ExtUtils::MakeMaker;

my %mm_args = (
    NAME => 'Foo::Bar',
    ...
);

my $eumm = ExtUtils::MakeMaker->new(\%mm_args);
my $pureperl_only = $eumm->{ARGS}{PUREPERL_ONLY};

if ($pureperl_only) {
    # 消したり変更するオプションの内容は適宜変えてください
    delete $mm_args{$_} for (qw/XS XSOPT LIBS INC/);
}

WriteMakefile(%mm_args);

WorePAN 0.02

Parse::PMFileのテストに多用したWorePANの方も、内部の索引生成をCPAN::ParseDistributionからParse::PMFile(+PAUSE由来のもろもろのコード)に切り替えて0.02としてリリースしました。

また、0.02ではCPANミラーの中身の調査を楽にできるよう、walkというメソッドを追加しました。使い方は以下の通りです。

use strict;
use warnings;
use WorePAN;
use Parse::PMFile;
use YAML::Tiny qw/LoadFile/;

my $worepan = WorePAN->new(root => '/path/to/minicpan');

$worepan->walk(callback => sub {
  my ($dir, $path, $dist) = @_;

  my $mtime = $dist->mtime;
  my $metadata = LoadFile($dir->file('META.yml'));
  my $parser = Parse::PMFile->new($metadata);

  $dir->recurse(callback => sub {
    my $file = shift;
    return unless $file =~ /\.pm$/;

    my $packages_info = $parser->parse($file);
    ...
  });
});

walkメソッドのコールバックには、WorePANミラー内の各ディストリビューションを展開した一時ディレクトリ(を格納しているPath::Extended::Dirオブジェクト)、識別用のパス(F/FO/FOO/Foo-Bar-1.24.tar.gzなど)、展開前のディストリビューションを格納しているPath::Extended::Fileオブジェクトが渡ります。

この例ではさらにその一時ディレクトリの中身をなめて個々の.pmファイルに対して処理を行っていますが、もちろん特定のファイルのテストさえ行えればよいのであれば、META.ymlの情報をロードしている箇所のように、$dirから直接ファイルを指定すればよいでしょう。以下の例は指定した各ディストリビューションがModule::Installを使っているか確認するテストです。

use strict;
use warnings;
use WorePAN;
use Test::More;

my @files = qw(
  F/FO/FOO/Foo-Bar-1.23.tar.gz
);

my $worepan = WorePAN->new(
  files => \@files,
  local_mirror => '/path/to/darkpan'
  no_network => 0,
  use_backpan => 1,
  cleanup => 1,
);

$worepan->walk(callback => sub {
  my ($dir, $path) = @_;
  ok -f $dir->file('inc/Module/Install.pm'), "$path: Module::Install exists";
});

done_testing;

以上、author test用のツールとしてお役に立てばさいわいです。

Parse::PMFile 0.01

遅ればせながらQA Hackathonの成果のひとつとして、Parse::PMFileというモジュールをCPANにアップしました。これは2013年4月時点のPAUSEの中に埋もれているパッケージ/バージョン情報を抽出するコードをほとんどそのまま独立したモジュールとして抜き出したものです。

主目的はCPANTSのバージョン情報抽出精度の向上なので、現状ではPAUSEがやっているようなパッケージの解凍や索引の作成、またパーミッション情報の確認(PAUSEでは不適切なパーミッションを持つディストリビューションは索引に含めないようになっています)などはできないようになっています(パーミッションについてはTODOとして今後なんらかの方法で対応できないか検討する予定です)。また、そもそもがCPANの索引づくりを行うためのものなので、索引に載せないように工夫されたパッケージの情報も取得できません。

使い方は以下の通りです。単に.pmファイルのパッケージ/バージョン情報を取りたいだけならMETA.ymlの情報は渡さなくてもかまいませんが、META.ymlにprovidesフィールドが存在している場合(より厳密にはそのMETA.ymlが過去の壊れていたModule::Buildが生成したものではない、という条件もつきます)、その実際に.pmファイルの中身を精査せず、providesフィールドの情報を返すようになっています。

use strict;
use warnings;
use Parse::PMFile;
use Data::Dumper qw/Dumper/;
use YAML::Tiny qw/LoadFile/;

# サンプルなので横着していますが、本当はCPAN::Meta::YAMLを使うところです
my $metadata = LoadFile('META.yml');
my $parser = Parse::PMFile->new($metadata);
my $packages_info = $parser->parse('lib/Foo.pm');

print Dumper $packages_info;

# $VAR1 = {
#   'Foo' => {
#     'parsed' => 1,
#     'filemtime' => 1234567890,
#     'version' => '0.01',
#     'infile' => 'lib/Foo.pm',
#     'simile' => 'lib/Foo.pm',
#   }
# };

(DBD::)SQLiteにおけるORDER BYの最適化と検索結果の並び順について

最初に結論を書いておきますが、これは正しい使い方をできている方ならまったく気にする必要がない記事です。要するに「ORDER BYを指定していないのにSELECT文の返値に一定の並び順を期待する方が間違い」というだけの話なんですが、先日のQA Hackathon潜在的な問題が存在していることが浮き彫りになったので、念のため共有しておきます。

さて、SQLiteは2012年12月12日にリリースされたバージョン3.7.15でクエリオプティマイザを強化し、その結果、ORDER BYがより積極的に最適化されるようになりました。

ただ、DBD::SQLiteはしばらくリリースから遠ざかっていたため、この変更がPerl界隈の開発者の目に触れるようになったのはつい最近、2013年4月4日に1.38_02がリリースされたときのことでした。

このときどのモジュールのどのテストがこけるようになったのかはまだ把握していません。私のところに届いたのは最小限の検証スクリプトだけなのですが、たしかに以下のスクリプトをDBD::SQLite 1.38_02と、最適化以前のバージョンである1.38_01で実行すると、最後の行の出力がかわります(1.38_01ではSELECT * FROM cdの場合と同じくFoo, Barの順に出てくるのですが、1.38_02ではSELECT title FROM cdの方だけBar, Fooの順に出てくるようになります)。そのため、「不適切な方法でテストをしていると」、1.38_02からは並び順がそろわないことになり、テストがこけるようになります。

use 5.010;
use strict;
use warnings;
use DBI;
use Data::Dumper;

my $dbh = DBI->connect('dbi:SQLite::memory:', undef, undef, {
  AutoCommit => 1,
  sqlite_allow_multiple_statements => 1,
});

$dbh->do(<<'SQL');
CREATE TABLE cd (id integer primary key, title unique, year);
INSERT INTO cd (title, year) VALUES ('foo', 2000);
INSERT INTO cd (title, year) VALUES ('bar', 2001);
SQL

say $DBD::SQLite::VERSION;
say $DBD::SQLite::sqlite_version;
say Dumper $dbh->selectall_arrayref('SELECT * FROM cd');
say Dumper $dbh->selectall_arrayref('SELECT title FROM cd');

この件については問題の大きさを検証するため、QA Hackathonの最中に特別にスモーカーを回してもらっていたようで、その後何も言ってこないところをみると結果的にそれほど大きな問題にはならなかったものと思われますが、気になる方は、SQLiteにはreverse_unordered_selectsというプラグマが用意されているので、それぞれのテストの先頭でこのプラグマを発行するか、ソースに手を加えることをいとわないなら、dbdimp.cのsqlite_db_login6に以下の一行を追加して様子を見てみるとよいでしょう。

    sqlite_exec(dbh, "PRAGMA reverse_unordered_selects = ON");

これでテストがこけるようなら、暗黙のうちにSELECT文の結果が一定の順序で返ってくることを期待していた可能性が高い、ということになります。

実際、DBD::SQLite自身にもこの問題に引っかかっているテストが2つありました(リポジトリの方では直しておきました)。DBD::SQLiteをアップグレードしたときに急にテストがこけるようになった場合は、このような問題が関係している可能性があることも覚えておいていただけるとさいわいです。

OSDC.TW 2013にも参加してきました

Perl QA Hackathon 2013に続いて、4月19日、20日台北で開催されたOSDC.TW 2013にも参加してきました。自分の発表はさておくとして、個人的には前から気になっていた零時政府プロジェクトの話を聞けただけでも十分に行った甲斐があったと思えたことです。以下、gugod、audreyt両氏がそれぞれ同プロジェクトで作成している台湾の国語辞典サイト/アプリで利用している技術について発表した資料と、実際に利用できる「萌典」へのリンク、それとg0vハッカソンについて発表されていたtkirby氏の資料へのリンクを貼っておきます(*)。

(*)「萌典」といってもいわゆる「萌え〜」についての辞典ではなく、日本の文科省に相当する台湾の教育部 (Ministry of Education)でつくられている辞典、の意味ですね。

また、滞在中はhcchien氏をはじめとするおなじみの方々に加えて、今年はLiveScriptの話をされていたMindos氏にも大変お世話になりました。多謝!

で、問題の発表資料ですが、今年もまた土壇場で方針変更してしまったため、当初の予定とはかなり違ったものになってしまいました。これについてはまた何かの機会に整理し直したいとおもっています。

Perl QA Hackathon 2013に参加してきました

以前こちらでも予告しておいた通り、4月12日から14日までイギリスのランカスターで開催されたPerl QA Hackathonに参加してきました。今回のイベントも多くのスポンサーさまに支えていただきました。運営チームのみなさまおよび企業団体スポンサー(cPanel, Dijkmat, Dyn, Eligo, Evozon, $foo, Shadowcat Systems Limited, Enlightened Perl Organisation, Mongueurs de Perl)のみなさま、そして昨年に引き続き個人スポンサーとして寄付をいただいたSyohei Yoshida (@syohex)さま、ありがとうございました。おかげさまで大変有意義な時間を過ごすことができました。

また、今回のQA Hackathonでは、ランカスターでのHackathonにあわせて東京でも一日Hackathonが行われたので、こちらにもリモートで参加しました。会場をご提供くださったFreakOut Inc.さまにもあわせて感謝いたします。

さて、今回のHackathonではいろいろと興味深い出来事がありました。私が何をしたかはあとから書くとして、まずはHackathon全体としてどのようなことが行われたかを簡単に紹介しておきます。

Lancaster Consensus

まず、今回のHackathonのひとつのハイライトとして、CPANモジュールのインストールに必要な各種モジュール、サービス (toolchain) にかかわっている人たちが今後開発やサービスを継続していくにあたって確認しておきたいさまざまな項目を1日約2時間ずつ議論しました。その成果はランカスター・コンセンサス (Lancaster Consensus)という形でまとめられています。詳細については議論をリードしたDavid Golden氏の記事に注釈付きで詳しく説明されていますが、いささか長いので、ここでは多くの方に影響がありそうなところだけ要約しておきます (特殊なことをしているモジュール作者やtoolchain開発者向けには別途全訳を用意するのでしばしお待ちください)。

追記: ランカスター・コンセンサスおよびオスロ・コンセンサスの翻訳をperldoc.jpにアップしておきました。

  • 今回の合意で、今後toolchainモジュールがサポートするPerlのバージョンは2003年9月にリリースされた5.8.1以上とすることが決まりました (念のため、Perl 5 PortersがサポートするPerlのバージョンは直近の安定版2つのみです。2013年4月時点ではPerl 5.16.xとPerl 5.14.xの2つですが、近々リリース予定のPerl 5.18が出た時点でPerl 5.14.x系列のサポートは終了します)。お仕事の関係でどうしても5.6.x系列を使い続ける必要がある方は CPXXXAN (読み方についてはこちらの記事をどうぞ) と呼ばれる過去のバージョン専用のCPANを使うことで対応するモジュールをインストールすることはできますが、ExtUtils::MakeMakerやTest::Moreをはじめとするtoolchainの根幹をなす各種ツールが、今後メンテナンスの手間を軽減するため、Perl 5.8で導入された機能や、Perl 5.8でコア入りしたモジュールを積極的に利用していくようになることも予想されますので、可能であればこの機会により新しいPerlへの移行をご検討ください。
  • 「特定の状況以外では実行してほしくないテスト」を指定する環境変数がより細かく定義されました。特に現在AUTOMATED_TESTINGという環境変数を利用しているCPANモジュール作者の方は、(1) CPAN Testers以外には実行してほしくないテストなのか、(2) ユーザからの入力を求める(実行中に処理が止まりうる)コードが含まれるテストなのか、(3) 長い時間がかかったり、不確実な外部リソースを利用する必要があるのでインストール時にはスキップしてもいいテストなのか、(4) リリース時のみ実行すればいいテストなのか、(5) 開発時のみ実行すればいいテストなのか、を判断したうえで、それぞれ AUTOMATED_TESTING、NONINTERACTIVE_TESTING、EXTENDED_TESTING、RELEASE_TESTING、AUTHOR_TESTING という環境変数を適切に設定ないし処理することが求められます。
  • 今後CPANにアップロードするモジュールは、メインのモジュールとディストリビューション (tarball)の名前を適切に対応させることが求められるようになります。また、大文字小文字の区別がない環境で問題を起こさないよう、ディストリビューション登録時のチェックも大文字小文字の区別なしに行われます。
  • PAUSE IDの登録は現在手作業で行われていますが、今後しかるべき対策をとったのちに自動化される予定です。また、モジュールのアップロードやRTへのバグ報告などを含めて、まったく利用実績のないIDについては、しかるべき手続きをとったのちに削除されるようになる見込みです(なお、過去にモジュールをアップロードしたことのあるIDについてはこの自動化で削除の対象になることはありませんのでご安心ください)。
  • CPANにアップロードされているディストリビューションのうち、一定の条件を満たす古いバージョン (それより新しい安定版が3つ以上ある、等)については、しかるべき手続きをとったのち自動的に削除されるようになる予定です。何らかの事情で旧版が必要な方は前述のCPXXXANやBackPANからダウンロードしてもらうようになる見込みです。
  • 昔はCPANモジュールの登録時にはあらかじめ名前空間やモジュールの種類を登録しておくことが推奨されていました (この登録も手動で行われていたため、不適切な名前で登録しようとすると管理者側からより適切な名前が提案されるなどの教育効果がありました)。今後この名前空間などの登録(およびそれにともなう人的作業)は廃止される見込みです。新しくCPANモジュールを登録する前にPerlコミュニティからの意見を聞いてみたい方は、PrePANなどをご利用ください。

その他の成果

その他、各参加者の成果については下記のページにまとめられています。まだすべての成果が網羅されているわけではありませんが (ごめんなさい、私もまだ書いていません)、Devel::Coverチーム、Test::Builderチーム、DBI/DBDチームなど、それぞれが数人のグループに分かれて、いかにもHackathonらしい議論と実装に取り組んでいました。

http://2013.qa-hackathon.org/qa2013/wiki?node=Achievements

また、個人的なプロジェクトとしては、CPAN->grepの作者でもあるDavid Leadbeater (dg)氏のperl in JavaScriptが興味深い成果をあげています。

私の作業報告

私は予告通りもっぱらCPANTSの整備をしていました。これについてはまだ作業中の部分が多く、本番サーバには変更を反映できていませんが、期間中30件強のコミットを行い、メトリクスを2増8減して、都合14件のチケットを閉じられる状態にしておきました。また、CPANTSの改善の一環として、PAUSE作者のAndreas Koenig氏に(PAUSE内で行っている).pmファイルのpackage名やバージョン情報を取得するコードを独立させられないかを相談したり(氏にはCPANのインスタントミラーの最新事情についても教えていただきました)、Module::ExtractUseの作者であるThomas Klausner氏に連絡をとってYasutaka ATARASHI氏のパッチを取り込んでくださるようお願いしたり、Test::KwaliteeやModule::CPANTS::Analyseのメンテナ氏と相談してModule::CPANTS::Kwalitee以下のメトリックスのうち、ローカルにインストールする必要のないものや、インストールに問題があるものなどを別ディストリビューションにわける相談などをしていました。これらの作業は現在も継続中で、それぞれ以下のリポジトリで開発やテストを行っています。

また、サテライト会場でのHackathonでは、h2xsの置き換えを念頭において開発を進めていたモジュールの整備をしてgithubに公開しました。

これらのモジュールについては、もう少し開発を進めてCPANにリリースするなりなんなりした時点であらためて記事を用意しようとおもいます。

そのほか、DBICのメンテナである ribasushi 氏から SQLite 3.7.15 以降 ORDER BY の機能強化が行われたことによる潜在的な問題点の指摘を受けたので調査と対応を行いました。これについては項をあらためて説明します。

その他

例によって日中のハック以外にもさまざまな出来事がありました。特にお世話になった方を中心に紹介しておきます。

  • 昨年もいろいろ気を遣ってくださったWendyさんとLizさんから、昨年話題になったドイツにあるPerl村のワインをおみやげとしていただきました。リュックひとつの旅だったため中身は帰りの飛行機に乗る前の日に飲んできてしまったのですが (コルク抜きを探してさんざん歩き回ったのですが、いざ開けようとしたらスクリューキャップだったことに気がついたという)、ラベルははがして持ち帰り、次のOSDC.TWのスライドにネタとして埋め込みました。
  • 最終日の夜にはmst氏も現れ、9月に来日するという話や、Shadowcat Systemsをつくった理由などをうかがいました。OSDC.TWのスライドには同じくQA Hackathonのおみやげとしていただいた氏のコースター?(名刺?)の写真も載せてあります。
  • 2日目の夕食後にはCPANTS仲間でもあるdolmen氏がランカスターに持ち込んでいたChartreuse VerteをOslo.pmのSJN氏、Joakim氏といっしょにいただきました。
  • Git::*モジュール仲間ということで、Git::Repositoryの作者であるBooK氏に誘われて、同じくGit::Subというモジュールを書いているdolmen氏といっしょに記念撮影。どこかで見かけることがあったら苦笑してください。

ほかにも、NY.pmのJames Keenan氏や、DuckDuckGoのGetty氏など、いろいろな方にいろいろなお話をうかがいました。もちろん全員とお話できたわけではありませんでしたが、今回は毎朝各自持ち時間1分以内で進捗報告をする時間が用意されていたおかげもあって、前回よりもかなり多くの方の顔と名前を一致させられたようにおもいます。

来年はフランスのLyonが会場とのこと。今回のHackathonの反省をもとに、今回よかったこと、足りなかったことの整理がもう始まっています。参加申し込みが始まるのはずっと先の話ではありますが、興味を持たれた方はぜひperl-qaのメーリングリストや、irc.perl.orgの#perl-qaに参加してみてください。