NaN という文字列を <=> で比較すると -1, 0, 1 ではなくて undef が返るというお話。(v5.8.8 built for i386-linux-thread-multi)
ことの発端は、DBD::CSV で ORDER BY するとエラー、という現象。
id,foo 1,FOO 2,BAR 3,NaN
例えばこんなのを foo で ORDER BY できない。
@> SELECT * FROM test.csv ORDER BY foo; Argument "BAR" isn't numeric in numeric comparison (<=>) at /usr/lib/perl5/site_perl/5.8.8/SQL/Statement.pm line 972, <GEN1> line 4. DBD::CSV::st execute failed: Sort subroutine didn't return a numeric value at /usr/lib/perl5/site_perl/5.8.8/SQL/Statement.pm line 985, <GEN1> line 4. [for Statement "SELECT * FROM test.csv ORDER BY foo "].
どうやら NaN という文字列が <=> で特別扱いされるのが原因。
SQL::Statement の中で以下のような部分があって、
my $sortFunc = sub { # 略 } elsif ( is_number($c,$d) ) { $result = ($c <=> $d); # 略 $result; };
ここで $c または $d に "NaN" という文字列が入ってくると $result == undef になり、あとの sort で $sortFunc を使って
Sort subroutine didn't return a numeric value
とエラーになる。しかし 10年近く Perl さわってるのに、NaN が特別扱いされるのは知らなかった…… perlop と sort のリファレンス(perldoc -f sort)にはちゃんと書いてあります。
結局 SQL::Statementが、数値っぽかったら数値比較、文字列っぽかったら文字列比較、などと気を利かせてくれたのが裏目。
しかし sort 中に比較する 2値が数値っぽいか文字列っぽいか、で比較関数が切り替わるってのも微妙だ。文字列と数値(に見えるもの) が混じっているカラムで ORDER BY すると、数値としても文字列としてもまともに sort されていない、変な順序で返ってきてしまう可能性がある。
仕方ないので、ORDER BY せずに取得してから自前で cmp 使って sort し直して凌いだ。
ちなみに Mac OSX の "v5.8.6 built for darwin-thread-multi-2level" では NaN はサポートされていないらしく、<=> で比較しても undef ではなく 0 が返る。