c++からmysql_query

libmysqlclient-devを使ってみるテスト。 http://dev.mysql.com/doc/refman/5.0/en/c-api-functions.html に書いてあるサンプルをつぎはぎしただけでそれなりに動きました。

php でいう PEAR_DB みたいな DB の抽象化クラスって c++ にはないのかな。 php みたく「きっと親切なんだろう」という安心感がなく,思わず逐一エラーの確認をしてしまうので, SQL の syntax エラーみたいな単純なエラーを捉まえやすいことが分かった:-p

MYSQL構造体というのがあって,これがphpのresource(mysql link)に相当するようです。あとはphpとほとんど同じ。mysql_fetch_assoc()みたいな便利なものはさすがにないみたい。

ということで簡単なサンプル。c++のやつを全部つなげればいちおう動くものになるはずです。

超メモ: 0=false, 0以外=true ですね。

はじめに

#include <iostream>
#include <mysql/mysql.h>

int main()
{
    MYSQL mysql;
    mysql_init(&mysql);
    if (!mysql_real_connect(&mysql, "host", "user", "pass", "db_name", 0, NULL, 0)) {
        std::cout << "error in connect: " << mysql_error(&mysql) << std::endl;
        return 1;
    }

mysql/mysql.h を include します。phpだと $conn = mysql_connect('host', 'user', 'pass'); して mysql_select_db('db_name', $conn)); することに相当。

selectする

    if (mysql_query(&mysql, "select * from table")) {
        std::cout << "error in query: " << mysql_error(&mysql) << std::endl;
        return 1;
    }

mysql_queryは0が帰ってくると成功らしい。なんか統一感がない気がするんですが,気のせいですか...?

fetch_row

phpだと while($row = mysql_fetch_assoc($result)) {} で簡単にできちゃうものも,Cだとちょっと苦労します。

    MYSQL_RES *result;
    if (!(result = mysql_store_result(&mysql))) {
        std::cout << "error in store_result: " << mysql_error(&mysql) << std::endl;
        return 1;
    }

    int num_fields;
    MYSQL_FIELD *fields;
    MYSQL_ROW row;

    num_fields = mysql_num_fields(result);
    fields = mysql_fetch_fields(result);
    while (row = mysql_fetch_row(result)) {
        for (int i = 0; i < num_fields; ++i) {
            std::cout << fields[i].name << " : " << row[i] << std::endl;
        }
    }

php でいうところの $row = mysql_fetch_assoc($result) だとすると...

  1. mysql_num_fields() で count($row) を取得
  2. mysql_fetch_fields() で array_keys($row)
  3. mysql_fetch_row() は php のそのまま(ふつうの配列でfetch)

というかんじ。

assocはなぜ遅くない?

http://jp2.php.net/manual/ja/function.mysql-fetch-assoc.php には,

特筆すべき点として、mysql_fetch_assoc() が著しい付加価値があるにもかかわらず、
mysql_fetch_row()よりそれほど遅くはないということが言えます。

なんてことが書いてあります。なんで遅くないんでしょう? と気になりますよね。

ext/mysql/php_mysql.c によると,だいたい 1955 行目付近の

if (result_type & MYSQL_NUM) {
    add_index_stringl(return_value, i, data, data_len, should_copy);
    should_copy = 1;
}

if (result_type & MYSQL_ASSOC) {
    add_assoc_stringl(return_value, mysql_field->name, data, data_len, should_copy);
}

というかんじで,返り値となる配列を作るときに,どういう呼び出しをされたかに応じてふつうの配列にするか連想配列にするか,あるいは両方やるか,を選んでいるようです。

ようするに,最初から連想配列のキーを取得しちゃってるから,使わなくてもコストはほとんど同じだよーん,ってことですね。

おまけ

ちなみに,add_index_stringlとかはzend_hash_index_updateとかを経由してzend_hash_add_or_updateとかを呼び出すことになるわけですが(zend_hash.hの85行目付近),

ZEND_API int zend_hash_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag);
#define zend_hash_update(ht, arKey, nKeyLength, pData, nDataSize, pDest) \
        zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_UPDATE)
#define zend_hash_add(ht, arKey, nKeyLength, pData, nDataSize, pDest) \
        zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_ADD)

みたいにプリプロセッサでいろんなインタフェースをもった関数(?)を定義するのがCの流儀なんですね。Javaだとpublicをたくさん作ってprotectedなメソッドを中で呼び出したくなるところ。

とまあ,強引にphpの話でまとめてみました。おわり。