12月 12, 2014

libpqxxを使ってみた。

PostgreSQL Advent Calendar 2014 の 12日目です。枠が空いていたので、2回目の飛び込みをしてみました。
昨日はnuko_yokohamaさんのjsqueryを使ってみたでした。

 普段わたしはウェブアプリをPHPで作っていて、そこからPostgreSQLへアクセスするのですが、バッチ処理やループ処理などはCやC++で書くことがあります。
 少し前までは、C言語でバッチ処理を書いていて、ecpgを使った埋め込みSQLを使っていました。
 AIXでDB2を使っていた経験があり、その延長で埋め込みSQLは便利だなと思っていたのです。

 しかし、複雑なことをするに従って、また他のライブラリの都合などもあり、徐々にC++に移行してきました。

 7日目の nuko_yokohama さんのスライドにもあったように、C++でecpgを使うのは、ちょっと微妙です。完全対応では無いので、何か問題が発生したときに切り分けで嵌まりそうです。
 そこで、libpqxxという、C++ライブラリを使ってみました。

http://pqxx.org/development/libpqxx/
 現在、最新バージョンとしては4.0.1が公開されています。
 が、開発版のスナップショットも公開されています。
 4.0.1はリリースが古く、それからちょこっと修正も入っているので、こちらの方がいいかなと思います。
http://pqxx.org/~jtv/tmp/pqxx/snapshot/

 configure して make して make install 簡単ですね。

 コードは、とてもシンプルです。
 コネクションやトランザクション、結果などそれぞれがオブジェクトになるので、簡単にアクセスできます。
// パスワードは .pgpass にでも書いておきましょう
unique_ptr<pqxx::connection> db_conn(new pqxx::connection("host=pgsql.example.jp port=5432 dbname=testdb user=testuser application_name=testprog"));
try {
     pqxx::read_transaction tran(*db_conn);
     ostringstream query;
     query << "SELECT val" << endl;
     query << "FROM tbl" << endl;
     query << "WHERE cond='cond'" << endl;
     pqxx::result res(tran.exec( query.str() ));
     if (res.begin() == res.end()) cerr << "nodata" << endl;
     for (pqxx::result::const_iterator row = res.begin(); row != res.end(); row++) {
          cout << row["val"] << endl;
     }
     tran.commit();
}
catch (const pqxx::sql_error& e) {
     cerr << e.what() << " : SQL->" << e.query() << endl;
}
catch (const pqxx::usage_error& e) {
     cerr << e.what() << endl;
}

 更新でも同じ流れです。
pqxx::work tran(*db_conn);
std::ostringstream query;
query << "INSERT INTO tbl (" << endl;
query << "val, cond" << endl;
query << ") VALUES (" << endl;
query << "  " << std::dec << val << endl;
query << ", " << tran.quote( cond ) << endl;
query << ")" << endl;
pqxx::result res(tran.exec( query.str() ));
tran.commit();

 quoteだけではなく、ちゃんとプリペアドステートメントも使えます。
pqxx::connection_base &con_base(*db_conn);
con_base.prepare("sel", "SELECT name FROM tbl WHERE code=$1");
pqxx::read_transaction tran(*db_conn);
pqxx::result res( tran.prepared("sel")("hoge").exec() );
for (pqxx::result::const_iterator row = res.begin(); row != res.end(); row++) {
    cout << row["name"] << endl;
}
tran.commit();

 プリペアドステートメントはデータベースコネクションへ登録、実際のステートメントはトランザクションから
tran.prepared(ステートメント名)($1)($2)・・・.exec()
という形式で呼び出します。

 嵌まりどころがなくて、拍子抜けするぐらいです。
 以前のバージョンでは、PostgreSQLのbigint型を扱うときにCのlong long int型を使おうとするには、includeファイルの定義を変更したりする必要があったのですが、今のバージョンでは不要です。

これでC++からも簡単PostgreSQL!!

0 件のコメント:

コメントを投稿