突発的にC言語でプログラミングが書きたくなる病


こんにちは。好きなオプションは-Wall -Werrorです。masuyamaです。

SmartEducationでは、サーバ側の言語として正式にはRubyを採用しています。
そのため、日ごろはほとんどRuby、たまにPython、まれにPHP、Perlを使っていたりします。

LL言語はすごく簡単にやりたいことができていいのですが、なぜか突然C言語(C++ではなく)でプログラムを書きたくなることがよくあります。
今回は、一時的なデータ集計プログラムを書く機会があったのであえてC言語で書いてみました。

C言語でMySQLにアクセスする

今回のプログラムでは、MySQL内にあるログ的なデータをかき集めて属性ごとにカウントする、というだけのプログラムでした。
C言語でこの手のプログラムを書くことはあまりないと思いますが、この時に必須になるMySQLへのアクセス手法について紹介したいと思います。

MySQLに接続する

とりあえずの初期化とデータベースへの接続を行います。

#include <mysql/mysql.h>

MYSQL *mysql = mysql_init(NULL);
MYSQL *socket = mysql_real_connect(mysql, "hostname", "username", "password", "sampledb", 3306, NULL, CLIENT_COMPRESS);

エラー処理等は省略していますが、このあたりはどの言語でもほとんど同じ処理だと思います。

MySQLにSELECTクエリを発行する

接続してしまえば、SQLを発行するだけです。

char *query = "SELECT id,key,data FROM data WHERE date>='2014-01-01'";
mysql_real_query(socket, query, strlen(query));

このあたりもほかの言語と同じです。
ただ、最近ですとこういった直接クエリを発行するような機会はほとんどなく、より安全な方法でアクセスできるようになっていると思います。
パラメータのbindが使える場合は安全のためにも積極的に使っていきたいところです。
C言語の場合でも最低限は mysql_real_escape_string() をしてあげないといけません。

SELECTの結果をとる

このあたりがC言語で書く場合に一番面倒なところとなります。

MYSQL_RES *result = mysql_store_result(socket);

この関数で直前のリクエストの結果を取得することができます。
mysql_store_result() と mysql_use_result() の二種類の結果を取得する関数があり、前者は結果を全て、後者は一件ごとにクライアント側に取得します。

MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
    unsigned long *lengths = mysql_fetch_lengths(result);
    int id = atoi(row[0]);

    char key[256];
    strncpy(key, row[1], sizeof(key));

    char data[65536];
    int data_len;
    memcpy(data, row[2], lengths[2]);
    data_len = lengths[2];
}

このあたりがC言語でアクセスすると面倒なところ(多言語では隠蔽してくれているところ)です。

取得できたレコードを mysql_fetch_row() を使って1レコードだけ取り出します。
mysql.h での定義は以下のようになっています。

typedef char **MYSQL_ROW;		/* return data as array of strings */

このrowは、各カラムのデータ文字列へのポインタの配列となっていますので、添え字を間違えるとずれますし、NULLアクセスで簡単に落ちてくれます。

この各rowの要素の長さは mysql_fetch_lengths() で取得します。
バイナリ列としてデータを取得したい場合はこちらを利用してデータを扱います。

取得する要素がなくなったら mysql_fetch_row() は NULL を返すのでループを終了します。

開放

アクセスが終了したら、resultを開放します。

mysql_free_result(result);

これを忘れて次のアクセスを行うとメモリリークしてしまうので注意が必要です。
ここまで終わったら次のクエリを発行することができます。

参考

今回登場したmysql関連の関数については公式マニュアルに記載されていますので参照してください。

まとめ

C言語でのmysqlへのアクセス方法を紹介するだけで終わってしまいましたが、たったこれだけのことをするのにコンパイラに怒られまくることでだいぶストレスが発散できました。
次の機会があったらC言語でがんばってArrayやHashを実装してみたいと思います。