C++ から MySQL に接続する

#はじめに

本記事は Hatena Blog に投稿した以下の記事について再びまとめたものになります．  

[C++ から MySQL のデータベースに接続する](http://tomov3.hatenablog.com/entry/2016/08/01/175308)

思った以上にアクセスが伸びていたため，より分かりやすい形式で Qiita に投稿しようと思いました．  
内容はあまり変更せず，（1）MySQL への接続方法と（2）データベースに対する操作についてまとめます．

詳しくは以下の Document を参照していただければと思います．

https://dev.mysql.com/doc/connector-cpp/en/connector-cpp-getting-started-examples.html

#MySQL
MySQL はオープンソースで公開されている RDBMS の1つです．  

C++ のプログラムから MySQL に接続するには Connector/C++ を用います．
以下のリンクからダウンロード可能なので，OS に応じて適切なものを選択してください．

https://dev.mysql.com/downloads/connector/cpp/

また，本記事では MySQL と Connector/C++ のインストール方法や，SQL 構文については省略します．

#Connector/C++
それでは C++ のプログラムから MySQL に接続する方法を解説していきます．

処理の流れの概要は次の通りになります．

1. ドライバの生成  

2. ドライバを用いてデータベースに接続

3. データベースに対して処理を行う

1 と 2 は API を用いて各1行で記述することができるため，3 の処理について詳しく解説したいと思います．

まず，実際に Connector/C++ で MySQL に対して処理を行うプログラムを添付します．

https://gist.github.com/tomov3/4509b0098673a474b2722ddb3dd337ff

```c++:connect_mysql.cpp
#include <iostream>
#include <sstream>
#include <memory>
#include <mysql_driver.h>
#include <mysql_connection.h>
#include <mysql_error.h>
#include <cppconn/Statement.h>
#include <cppconn/ResultSet.h>

//define your own configurations in mysql_config.hpp
//for example: HOST = "tcp://127.0.0.1:3306", USER = "root", PASSWORD = "password"
//             DATABASE = "database_name"
#include "mysql_config.hpp"
using namespace std;

int main()
{
	try {
		sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance();
		unique_ptr<sql::Connection> con(driver->connect(HOST, USER, PASSWORD));
		unique_ptr<sql::Statement> stmt(con->createStatement());


		stmt->execute("USE " + DATABASE);
		stmt->execute("DROP TABLE IF EXISTS conference");
		stmt->execute("CREATE TABLE conference(cid int, name varchar(10))");
		cout << "\"conference\" table has been created." << endl;

		stmt->execute("INSERT INTO conference VALUES(1, 'SIGMOD')");
		stmt->execute("INSERT INTO conference VALUES(2, 'VLDB')");
		stmt->execute("INSERT INTO conference VALUES(3, 'ICDE')");
		stmt->execute("INSERT INTO conference VALUES(4, 'KDD')");

		unique_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT * FROM conference"));

		size_t row = 0;
		while (res->next()) {
			cout << row << "\t";
			/* You can use either numeric offsets... */
			cout << "cid = " << res->getInt(1);
			/* ... or column names for accessing results. The latter is recommended. */
			cout << ", name = '" << res->getString("name") << "' " << endl;
			row++;
		}

	} catch (sql::SQLException &e) {
		cout << "# ERR: SQLException in " << __FILE__ << " on line " << __LINE__ << endl;
		cout << "# ERR: " << e.what() << endl;
		cout << " (MySQL error code: " << e.getErrorCode();
		cout << ", SQLState: " << e.getSQLState() << " )" << endl;
		return EXIT_FAILURE;
	} catch (runtime_error &e) {
		cout << "# ERR: runtime_error in " << __FILE__ << " on line " << __LINE__ << endl;
		cout << "# ERR: " << e.what() << endl;
		return EXIT_FAILURE;
	}
}
```

このプログラムはデータベースに以下に示すテーブルを作成し，全ての要素を取得するものになっています．

| cid | name | 
|:----------:|:-----------:| 
|1|SIGMOD| 
|2|VLDB|
|3|ICDE|
|4|KDD|

具体的には，conference TABLE を CREATE し，4つの VALUES を INSERT します．

最後にすべての要素を SELECT し，データを列挙します．

##ドライバの生成とデータベースへの接続
ドライバの生成はプログラムの19行目，データベースへの接続は20行目で行っています．

```
19: sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance();
20: unique_ptr<sql::Connection> con(driver->connect(HOST, USER, PASSWORD));
```

get_mysql_driver_instance() によってドライバを生成し，取得したドライバを用いて connect() でデータベースへと接続しています．

（connect() の戻り値である sql::Connection は最終的に解放する必要があるため，ここでは unique_ptr を使用しています．）

##データベースに対して処理を行う

データベースに対する処理は大きく分けて，（1）結果が返ってこない処理，（2）結果が返ってくる処理の2つに分類することができます．

（1）は INSERT，（2）は SELECT といった処理が該当します．


Connector/C++ では（1）と（2）について，それぞれ execute() 関数と executeQuery() 関数を用いて実行します．

まず前準備として，sql::Statement インスタンスを生成します．

```
unique_ptr<sql::Statement> stmt(con->createStatement());
```

execute() と executeQuery() は sql::Statement のメンバ関数なので，以降の処理は stmt から実行します．

###execute()
execute() 関数を用いてテーブルの作成，データの挿入を行う例について説明します．

上記のプログラムでは 24~32 行目に該当します．

```
stmt->execute("USE " + DATABASE);
stmt->execute("DROP TABLE IF EXISTS conference");
stmt->execute("CREATE TABLE conference(cid int, name varchar(10))");
cout << "\"conference\" table has been created." << endl;

stmt->execute("INSERT INTO conference VALUES(1, 'SIGMOD')");
stmt->execute("INSERT INTO conference VALUES(2, 'VLDB')");
stmt->execute("INSERT INTO conference VALUES(3, 'ICDE')");
stmt->execute("INSERT INTO conference VALUES(4, 'KDD')");
```

前半4行がテーブルの作成，後半4行がデータの挿入です．

execute() 関数の引数には SQL クエリをそのまま記述することができます．

###executeQuery()
続いて，executeQuery() 関数を用いてデータの取得をする例について説明します．

上記プログラムでは 34~44 行目に該当します．

```
unique_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT * FROM conference"));

size_t row = 0;
while (res->next()) {
	cout << row << "\t";
	/* You can use either numeric offsets... */
	cout << "cid = " << res->getInt(1);
	/* ... or column names for accessing results. The latter is recommended. */
	cout << ", name = '" << res->getString("name") << "' " << endl;
	row++;
}
```

executeQuery() を実行すると，sql::ResultSet インスタンスが返されます．

このとき，execute() と同様に executeQuery() の引数には SQL クエリをそのまま記述することができます．

取得したデータへのアクセスは， getXX() 関数を用います．

このプログラムの例ですと，Int 型の cid と String 型の name の2つの要素があるため，それぞれ getInt() と getString() を用いて結果を取得します．

また，getInt()　の引数は列番号であり，getString() の引数は attribute 名を指定します．
※列番号は 1 から始まります．

#まとめ
Connector/C++ を用いて MySQL に接続する方法を，実際にプログラムを用いて紹介しました．

処理の流れの概要は，

1. ドライバの生成
2. データベースに接続
3. データベースに処理を行う

でした．

1 と 2 に関しては API を用いて簡単に記述でき，3 の処理に関しても execute() や executeQuery() 関数に SQL クエリを渡すことによって簡単に実行することができました．