Tips

C言語(Windows環境)からMySQL #5 ソースコード解説編

C言語(Windows環境)からMySQL #5 ソースコード解説編

C言語(Windows環境)からMySQL #5 ソースコード解説編

C言語(Windows環境)からMySQL #5 ソースコード解説編

C言語(Windows環境)からMySQL #5 ソースコード解説編

 

前回までのあらすじ~

Windowsのコマンドプロンプト(文字コード:cp932)からC言語でMySQLにアクセスしたい!!と思い立った勇者 筆者はついに実行ファイルを完成させ、プログラムを実行したのだった。

しかしこのままではSample.cの中身は何が起こるかわからない魔法の呪文状態で何が書いてあるかわからない。

そこで勇者 筆者はSample.cの解説をしようと決意するのだった…。

 

前回から1ヶ月以上経ってるYO(^お^

 

 

なお、今回は安全性や動作速度などは一切考えずにあくまでも「Windowsのコマンドプロンプトから直接MySQLを使えるプログラムをC言語で作る」ことに注力しています。

セキュリティ面や動作速度、バグの有無などは考慮に入れていませんので参考にする場合はそのあたりにはお気を付けの上、自己責任で利用してください。

って、急にまじめに戻ってるYO!!

 

開発環境

 

C言語

C言語の環境はこちらー>環境導入前編 環境導入後編 おまけその1 おまけその2

 

MySQLの環境は以下のものを用意しました。

MySQL 5.5.56(zip形式のものをダウンロード、解凍して使用)

 

Connector/Cは以下のものを用意しました。

Connector/C 6.1.10(zip形式のものをダウンロード、解凍して使用)

 

 

本記事は以上の環境を前提に説明を行います。

 

 

 

C言語からMySQLを利用する手順

C言語からMySQLを利用する手順

C言語からMySQLを利用するための手順は大雑把に書くと以下の通りです。

1.MySQLの初期設定をする

2.Connector/Cを準備する

3.ソースを書く
   ソース解説編はこちら ← 今回はココ

4.コンパイルする

5.実行する

おまけ:実行時のエラーと対処法

 

 

他の環境(Javaとか)から簡単に叩けるんだし、楽勝!!・・・と思ったら、コンパイルで思いっきり詰まって悔しかったのでメモに残したのは内緒。

前回までで5の実行までが無事終りました。めでたしめでたし…と言いたいところですが、サンプルソースの解説を一切していないので説明を加えます。

 

・・・えっ、実行したら

 

Can't connect to MySQL server on 'localhost' (10061)

とか

Access denied for user 'user_name'@'localhost' (using password: YES)

とか

SELECT command denied to user 'user_name'@'localhost' for table 'table_name'

とか

Table 'db_name.table_name' doesn't exist

とか

って言われたって?それはデータベース側に原因が(モゴモゴ

 

 

 

そんなあなたにはこちら→#6 トラブルシュート編

 

 

 

ソースコードの解説

ソースコードの解説

 

ソースコードの解説をしていきます。

ソースコード全体の流れは以下の通りです。

・MySQLを使う準備
・MySQLとの接続
・queryの実行
・終了処理

では、ソースコードを確認してみましょう。

 

※ディレクトリ構成はこちらをご覧ください。

※コンパイルの方法はこちらをご覧ください。

 

#include <stdio.h>
#include <stdlib.h>
#include "mysql.h" /* MySQLを利用するための構造体の定義などが書かれたヘッダファイル */
int main(void){

    /* MySQLを利用するために必要な構造体 */
    MYSQL *conn; /* MySQLとの接続を表す構造体 */
    MYSQL_RES *res; /* SELECT文(など)の結果を表す構造体 */
    MYSQL_ROW row; /* MYSQL_RESの中の1レコードを示す構造体 */

    /* 実行するquery */
    const char query[256] = "SELECT `table_name`.`id`, `table_name`.`sample_column`, `table_name`.`date_time` FROM `db_name`.`table_name`";

    /* 接続に必要な情報 */
    /* mysql_real_connect()関数の引数として使用。ベタ打ちでもよいが、定数宣言しておくと管理が楽。 */
    const char *SERV = "localhost"; /* MySQLが設置してあるサーバのIPアドレスorホスト名 */
    const char *USER = "user_name"; /* MySQLにアクセスする際のユーザ名 */
    const char *PASSWORD = "password"; /* MySQLにアクセスする際にパスワード */
    const char *DB_NAME = "db_name"; /* 接続先のMySQLで使用するデータベース名 */
    const unsigned int PORT = 3306; /* 接続先のサーバでMySQLが稼働しているポート番号 */

    /* 接続処理 */

    /* 初期化及び接続前の設定 */
    conn = mysql_init(NULL); /* 接続の初期化 */
    mysql_options(conn, MYSQL_SET_CHARSET_NAME, "cp932"); /* オプション設定。今回は文字コードの設定をcp932に設定している。 */

    /* 接続 */
    /* mysql_real_connect()関数:MySQLと接続を開始 */
    if( !mysql_real_connect(conn,SERV,USER,PASSWORD,DB_NAME,PORT,NULL,0) ){
        fprintf(stderr, "%s\r\n", mysql_error(conn)); /* エラーが発生した場合、標準エラー出力に内容を表示 */
        exit(-1);
    }

    /* query実行 */
    /* mysql_query()関数:queryを実行 */
    if( mysql_query( conn, query) ){
        fprintf(stderr, "%s\r\n", mysql_error(conn));
        exit(-1);
    }
    /* 結果を取得 */
    /* mysql_use_result()関数:queryの実行結果全体を取得 */
    res = mysql_use_result(conn);

    /* 結果の処理 */
    /* mysql_fetch_row()関数:実行結果の中から1レコードを取得 */
    while( NULL != (row = mysql_fetch_row(res)) ){
        unsigned int col;
        /* mysql_num_fields()関数:現在のレコードのカラム数を取得 */
        for(col = 0; col < mysql_num_fields(res); col++){
            printf("%s ", row[col]);
        }
        printf("\r\n");
    }

    /* 構造体の解放処理 */
    /* mysql_free_result()関数:検索結果を保持している構造体のメモリを解放 */
    if(NULL != res){
        mysql_free_result(res);
    }

    /* 接続の解放処理 */
    /* mysql_close()関数:mysqlとの接続を切断 */
    if(NULL != conn){
        mysql_close(conn);
    }

    return 0;
}

 

解説コメントが書いてある Σ(゜O゜

 

では、プログラムの流れをみてみましょう。

プログラムの流れ

今回のソースコードは以下の流れで書かれています。

 

1.MySQLを使う準備

・ヘッダファイルを取り込む
・必要な構造体を準備する

 

2.MySQLとの接続

・接続の初期化を行う
・接続のオプション設定を行う
・接続する

 

3.queryの実行

・queryを実行する
・queryの結果を取得する
・queryの結果から1レコード取得する
・処理する
(次のレコードがあれば取得→処理を繰り返す)

 

4.終了処理

・結果の解放
・接続の終了
・プログラム終了

 

以下それぞれの詳細です。長いので流れだけで良い方は飛ばしてください。

 

MySQLを使う準備

Connector/Cを利用してMySQLに接続するための準備です。

 

・ヘッダファイルを取り込む

MySQLを使うためにはmysql.hというヘッダファイルが必要です。そこで、mysql.hをincludeします。(サンプルソース3行目)

#include "mysql.h"

 

・必要な構造体を準備する

MySQLを使うために必要な構造体を準備します。SELECT文を使っているので以下の3つを準備します。

MYSQL* conn(7行目):MySQLとの接続を表す構造体へのポインタ
MYSQL_RES* res(8行目):SELECT文の結果を表す構造体へのポインタ
MYSQL_ROW row(9行目):MYSQL_RESの中の1レコードを表す構造体

MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;

 

また、接続に必要な情報はここで定数宣言しておくと後で修正する際に楽になります。

サンプルでは以下のものを定数宣言しています。

実行するSELECT文(12行目)
MySQLが設置してあるサーバ名(16行目)
MySQLにアクセスするユーザ名(17行目)
MySQLにアクセスするユーザのパスワード(18行目)
使用したいデータベース名(19行目)
接続先のサーバのポート番号(20行目)

const char query[256] = "SELECT (長いので省略) ";
const char *SERV = "localhost";
const char *USER = "user_name";
const char *PASSWORD = "password";
const char *DB_NAME = "db_name";
const unsigned int PORT = 3306;

 

MySQLとの接続

MySQLの接続設定および接続を行います。

 

・接続の初期化

接続の初期化を行います。(サンプルソース25行目)

接続の初期化はmysql_init()関数で行います。戻り値をconnに代入して接続の初期化を行っています。代わりに以下のように、引数に初期化したいMYSQL*型の変数を渡すこともできます。その場合はmallocとmemsetを忘れないように注意してください。(memset関数を使うためにはstring.hをincludeする必要があります。)

/* 接続の初期化その1 */
//conn = mysql_init(NULL);

/* 接続の初期化その2 */
conn = (MYSQL*)malloc(sizeof(MYSQL));
memset(conn,0,sizeof(MYSQL));
mysql_init(conn);

 

・接続のオプション設定

接続のオプション設定を行います。(26行目)

mysql_options()関数で行います。引数には接続先のMYSQL*、設定項目、値の3つを指定します。

//mysql_options(接続先, 設定項目, 値);
mysql_options(conn, MYSQL_SET_CHARSET_NAME, "cp932");

今回は文字コード(MYSQL_SET_CHARSET_NAME)をコマンドプロンプトと同じcp932に設定しています。

 

・接続する

MySQLとの接続を開始します。(30行目)

接続はmysql_real_connect()関数で行います。第1引数で指定したMYSQL型のポインタに、接続を示すポインタが格納されます。

そのほかの引数には接続先のホスト名やパスワードなど、サンプルプログラム冒頭で宣言していた接続に必要な定数を指定します。

//mysql_real_connect(接続先,接続先のホスト名,ユーザ名,パスワード,データベース名,ポート番号,unix_socket,client_flag)
mysql_real_connect(conn,SERV,USER,PASSWORD,DB_NAME,PORT,NULL,0)

unix_socketとclient_flagについてはそれぞれNULLと0を指定しています。client_flagについては、値を変えることで一部の設定を変更することができます(設定を間違えると脆弱性につながる恐れがあるものもあります)。

この関数は正常に接続ができた場合0が返ってきます。そのため、0以外が帰ってきた場合接続に失敗していることを意味します。

 

※接続失敗時の処理

サンプルプログラムでは接続失敗の際にmysql_error()関数でエラー内容を取得し、エラー出力するようにしています。(31行目)

//mysql_error(接続先)
mysql_error(conn)

38行目でもmysql_error()関数を使っています。31行目同様、エラー内容の取得を行っています。

 

queryの実行

queryを実行します。

 

・queryを実行する

queryを実行します(37行目)

queryの実行にはmysql_query()関数を利用します。(何度も繰り返しqueryを実行する場合や、外部からの入力値を利用したqueryを実行する場合は、実行速度やセキュリティの都合でプリペアードステートメントを利用した方が良い場合もありますが説明がさらに長くなるので今回は省略。)

//mysql_query( 接続先, 実行するquery)
mysql_query( conn, query)

mysql_query()関数は成功すると0が返ってくるので、それ以外の場合はmysql_real_connect()関数を使用した時と同じく、エラー出力をしています。(38行目)

 

実行後は、結果の処理を行います。以下の3つです。

・queryから結果を取得する(43行目)
・queryの結果から1レコード取得する(47行目)
・処理する(50~52行目)

SELECT文の場合、実行結果を読み込んで処理する必要があります。mysql_use_result()関数で結果全体を取得後、mysql_fetch_row()関数で1レコードずつ取り出して処理していきます。

まずはmysql_use_result()関数で結果全体を取得します。mysql_use_result()関数は引数にqueryを実行したMYSQL∗を指定します。実行結果を保持しているアドレスが帰ってくるのでそれをMYSQL_RES∗に代入します。

//mysql_use_result(接続先)
res = mysql_use_result(conn)

次に、取り出した結果全体から1レコードずつ結果を取り出していきます。
レコードの取り出しにはmysql_fetch_row()関数を使います。この関数は引数に結果(res)をとります。戻り値は、取り出すレコードが残っていれば次のレコードを、なければNULLを返します。
従って、この結果がNULLの場合、結果のレコードを全件取り出し終わったことになります。

//mysql_fetch_row(結果全体のアドレス)
row = mysql_fetch_row(res)

 

これを利用して以下のように書くと、レコードがNULLになるまでレコードの取り出し→処理を繰り返すプログラムになります。

つまり、レコード全件に対して処理を行うプログラムになります。

while(NULL != (row = mysql_fetch_row(res))){
    //処理
}

取り出した1件分のレコードからは各カラムを取り出して処理を行います。今回はコマンドプロンプトの画面に結果を出力します。
rowに取り出したカラムはそのままrow[取り出したいカラムの番号]で取り出せます。カラムの番号はSELECT文で指定したカラムの順番になるはずですが、実験出来ていないので使う際は気を付けて

また、サンプルプログラムでは、mysql_num_field()関数を利用して、取り出したレコードのカラム数を取得しています。これをfor文と組み合わせて、レコードの全項目をコマンドプロンプトの画面に出力しています。
今回のサンプルではSELECT文で以下の3つのカラムを取得しているので、カラム数は3になります。

`table_name`.`id`
`table_name`.`sample_column`
`table_name`.`date_time`

そのため、サンプルプログラムを実行するとこれら3項目が表示されます。
※↓実行例。あらかじめデータベースにデータが入っていないとダメなので要注意。

終了処理

データベースとのやりとりがすべて終わったら、終了処理を行います。MYSQL∗の解放を行います。また、今回はSELECT文を利用したので、その結果の保存で利用しているMYSQL_RES∗も開放する必要があります。

解放の順番は

MYSQL_RES* の解放(59行目)

MYSQL* の解放(65行目)

です。

 

MYSQL_RES*の解放にはmysql_free_result()関数を利用します。

//mysql_free_result(解放するMYSQL_RES*)
mysql_free_result(res)

なお、解放前にすでにresがNULLの場合、プログラムがNULLで落ちてしまうのでNULLチェックは忘れずにしましょう。

MYSQL*の解放にはmysql_close()関数を利用します。

//mysql_close(解放する接続)
mysql_close(conn)

こちらもNULLチェックは忘れずに行いましょう。

以上で長すぎて読む気がしない解説は終了です。

・・・いつかもっと小分けにして書き直そう。

 

(次回、トラブルシュート解説編へ続く)

 


筆者のおすすめ記事
MinGW-w64とmsys2で作るC言語環境構築
C言語練習問題一覧
C言語でBINGOを作ろうシリーズ記事一覧

実践力が身につくC言語講座 連載リンク

競技プログラミングをイメージしたライブラリ活用講座
競技プログラミング風-標準Cライブラリ入門 連載

アルゴリズムをマスターして技術力アップ!
実践アルゴリズム講座 連載

パズルゲームの解析をテーマにしたC++講座
ゲーム解析プログラミング 連載

Recent News

Recent Tips

Tag Search