5

Perl と DBI、つまり DBD::Pg を使用して PostgreSQL に値を設定しようとしています。

奇妙なエラーが発生します。

2013-05-23 19:02:36.641139500 updating status to 0
2013-05-23 19:02:36.641410500 DBD::Pg::st execute failed: ERROR:  syntax error at or near "$1"
2013-05-23 19:02:36.641418500 LINE 1: UPDATE instances SET $1 = $2
2013-05-23 19:02:36.641423500                              ^ at /usr/lib/perl5/vendor_perl/Mitel/MslRest/mbg.pm line 161.
2013-05-23 19:02:36.642425500 [Thu May 23 19:02:36 2013] [error] DBD::Pg::st execute failed: ERROR:  syntax error at or near "$1"
2013-05-23 19:02:36.642438500 LINE 1: UPDATE instances SET $1 = $2
2013-05-23 19:02:36.642443500                              ^ at /usr/lib/perl5/vendor_perl/Mitel/MslRest/mbg.pm line 161.
2013-05-23 19:02:36.642447500

関連するコードは

my $sql = "UPDATE instances SET ? = ?";
my $dbh = Mitel::tug::getdbh();
$dbh->begin_work;
my $sth = $dbh->prepare($sql);
unless ($sth) {
    return $self->internal_error("prepare failed: " . $dbh->errstr);
}
foreach my $propname (sort keys %{ $raw_data }) {
    my $propval = $raw_data->{$propname};
    print STDERR "updating $propname to $propval\n";
    if (! $sth->execute($propname, $propval)) {
        $dbh->rollback;
        $sth->finish;
        return $self->internal_error("execute: " . $dbh->errstr);
    }
}
$dbh->commit;
$sth->finish;

そのため、execute メソッドを使用して SQL インジェクションを防止し、"status" を 0 に更新しようとしていますが、何らかの理由で構文エラーが発生します。

誰でもこれに遭遇しますか?

root@miketug2 ~]# perl -v

This is perl, v5.10.1 (*) built for i386-linux-thread-multi

perl-DBD-Pg-2.15.1-4.el6_3.i686
postgresql84-server-8.4.14-1PGDG.rhel6.i686

root@miketug2 ~]# uname -a
Linux miketug2 2.6.32-279.22.1.el6.i686 #1 SMP Wed Feb 6 00:31:03 UTC 2013 i686 i686 i386 GNU/Linux
4

1 に答える 1

9

PostgreSQL は、位置プレースホルダーよりも番号付きプレースホルダー ( $1$2、 ...)を使用することを好みます。?そのため、誰かが?プレースホルダーを番号プレースホルダーに変換します。それがあなたのSQLの理由です:

UPDATE instances SET ? = ?

最終的には次のようになります。

UPDATE instances SET $1 = $2

エラーメッセージで。

本当の問題は、識別子 (テーブル名、列名など) にプレースホルダーを使用できないことです。値にはプレースホルダーしか使用できません。SET ? = ?おそらく文字列補間を介して、列名を別の方法で指定する必要があるとは言えません。prepareこれは、ループ内を次のように移動する必要があることを意味します。

foreach my $propname (sort keys %{ $raw_data }) {
    my $prop    = $dbh->quote_identifier($propname);
    my $propval = $raw_data->{$propname};
    my $sth     = $dbh->prepare("UPDATE instances SET $prop = ?");
    $sth->execute($propval);
    $sth->finish();
}

もちろん、実際のコードにはエラー処理が含まれます。quote_identifierを使用して、列名を補間に対して安全にすることに注意してください。prepareこの単純な, execute,finishシーケンスで終わる場合は、do代わりに使用することをお勧めします。

于 2013-05-24T02:20:12.893 に答える