2004年1月27日火曜日

pg_atoi: zero-length string


僕はDBMSというとPostgreSQLを主に使っているのですが、このPostgreSQL、バージョン間で互換性のない変更点というのが結構あって、これを知らないと、データベースの移行作業とか、テストサーバで開発したものを本番サーバにアップしようとか言うときに、この違いのせいでトラブるなんてことになりかねません。


「pg_atoi: zero-length string」なんていうエラーもほとんどが7.2から7.3への互換性のない変更点に書かれている、

整数型の読み込みで、空文字列が禁止されました。 この結果、整数型フィールドに対し、COPY 文での入力フィールドの空指定や VALUES 句での空文字列 '' 指定などで、0 入力とはならず、エラーとなります。

が原因でしょう。


例として、名前と年齢をフォームから入力させて裏でDBに保存するようなWebアプリケーションを考えてみましょう。
名前と年齢を保存しておくテーブルを次のように定義します。

CREATE TABLE user_master(
name text,
age int
);


そしてこのDBにデータを保存するためのPerlスクリプトを次のように書いたとしましょう。
#! /usr/local/bin/perl
use strict;
use warnings;

use CGI;
use DBI;

my $query = CGI->new;
my $dbh = DBI->connect('dbi:Pg:dbname=testdb', 'username','passwd');

my $name = $query->param('name');
my $age = $query->param('age')

$dbh->do("INSERT INTO user_master (name,age) values(?,?)",$name,$age);

このスクリプトは7.2では問題なく動きますが、7.3だとエラーになる可能性があります。
エラーになるのはageに値が入力されなかった場合です。いつからだったか忘れてしまいましたが、(2.7xくらいからだったような。。)CGI.pmではparam('key')の値が空だった場合、空文字列を返すので、もしageの値が空だと$ageには空文字列が代入されます。それをそのままPostgreSQLのint型のカラムに入れようとしているので、pg_atoi: zero-length string」というエラーがでてしまうわけです。このエラーを回避するために修正したスクリプトは以下のようになります。
#! /usr/local/bin/perl
use strict;
use warnings;

use CGI;
use DBI;

my $query = CGI->new;
my $dbh = DBI->connect('dbi:Pg:dbname=testdb', 'username','passwd');

my $name = $query->param('name') || undef;
my $age = $query->param('age') || undef;

$dbh->do("INSERT INTO user_master (name,age) values(?,?)",$name,$age);

param('key')の値が偽だった場合(空文字も偽になります)、undefを代入するようにしました。入力データが多い場合は、mapを使って一括処理してやるのが効率的でしょう。

my @keys = qw(name age kana birth_year birth_month birth_day);
my %params = map {$_ => $query->param($_) ? $query->param($_) : undef} @keys;


2 件のコメント:

  1. my $age = $query->param('age') || undef;
    だと、0歳の人も null になってしまいます。

    返信削除
  2. そうですね(汗
    このアプリケーションは0歳の人の登録を受け付けないと言うことで許してください(苦笑)
    0歳がnullにならないようにするにはこうしないといけませんね。
    my $age = $query->param('age');
    $age = undef if $age eq '';

    返信削除