昨日は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!!