D言語ネイティブのMySQLドライバーmysql-nativeの基本的な使い方

# はじめに

[D-man bot](https://twitter.com/d_man_bot)で使われている[mysql-d](https://code.dlang.org/packages/mysql-d)はlibmysqlに依存しています。
今回、読んで字のごとくD言語ネイティブな[mysql-native](https://code.dlang.org/packages/mysql-native)に変更するため、基本的な使い方をまとめます。

# 基本的な使い方

READMEのBasic exampleに書かれています。

```d
import std.variant;
import mysql;

void main(string[] args)
{
	// Connect
	auto connectionStr = args[1];
	Connection conn = new Connection(connectionStr);
	scope(exit) conn.close();

	// Insert
	auto rowsAffected = exec(conn,
		"INSERT INTO `tablename` (`id`, `name`) VALUES (1, `Ann`), (2, `Bob`)");

	// Query
	ResultRange range = query(conn, "SELECT * FROM `tablename`");
	Row row = range.front;
	Variant id = row[0];
	Variant name = row[1];
	assert(id == 1);
	assert(name == "Ann");

	range.popFront();
	assert(range.front[0] == 2);
	assert(range.front[1] == "Bob");

	// Prepared statements
	Prepared prepared = prepare(conn, "SELECT * FROM `tablename` WHERE `name`=? OR `name`=?");
	prepared.setArgs("Bob", "Bobby");
	ResultRange bobs = prepared.query();
	bobs.close(); // Skip them
	
	prepared.setArgs("Bob", "Ann");
	ResultSet rs = prepared.querySet();
	assert(rs.length == 1);
	assert(rs[0][0] == 1);
	assert(rs[0][1] == "Ann");
	assert(rs[1][0] == 2);
	assert(rs[1][1] == "Bob");

	// Nulls
	Prepared insert = prepare(conn, "INSERT INTO `tablename` (`id`, `name`) VALUES (?,?)");
	insert.setArgs(null, "Cam"); // Also takes Nullable!T
	insert.exec();
	range = query(conn, "SELECT * FROM `tablename` WHERE `name`='Cam'");
	assert( range.front[0][0].type == typeid(typeof(null)) );
}
```

## // Connect
```d
auto connectionStr = args[1];
Connection conn = new Connection(connectionStr);
scope(exit) conn.close();
```

例では`connectionStr`として文字列でMySQLの接続情報を取得しています。`"host=localhost;port=3306;user=joe;pwd=pass123;db=myappsdb"`のように`;`で値を連結したものを用います。
それ以外にも[いくつかの](http://semitwist.com/mysql-native-docs/v1.1.4/mysql/connection/Connection.this.html)コンストラクタが利用できます。

## // Insert
```
auto rowsAffected = exec(conn,
	"INSERT INTO `tablename` (`id`, `name`) VALUES (1, `Ann`), (2, `Bob`)");
```

[`exec`](http://semitwist.com/mysql-native-docs/v1.1.4/mysql/commands/exec.html)で直接SQLを実行します。返り値は変更された行数が`ulong`で返ります。

## // Query
```d
ResultRange range = query(conn, "SELECT * FROM `tablename`");
Row row = range.front;
Variant id = row[0];
Variant name = row[1];
assert(id == 1);
assert(name == "Ann");

range.popFront();
assert(range.front[0] == 2);
assert(range.front[1] == "Bob");
```

[`query`](http://semitwist.com/mysql-native-docs/v1.1.4/mysql/commands/query.html)はSQLを実行し、結果を[`ResultRange`](http://semitwist.com/mysql-native-docs/v1.1.4/mysql/result/ResultRange.html)で返します。
[`Row`](http://semitwist.com/mysql-native-docs/v1.1.4/mysql/result/Row.html)の`range`なので、`front`などで`Row`として取り出すことができます。
`Row`は`Variant`で各カラムの値を持ちます。また、[`toStruct`](http://semitwist.com/mysql-native-docs/v1.1.4/mysql/result/Row.toStruct.html)で構造体に変換できます。

## // Prepared statements
```d
Prepared prepared = prepare(conn, "SELECT * FROM `tablename` WHERE `name`=? OR `name`=?");
prepared.setArgs("Bob", "Bobby");
ResultRange bobs = prepared.query();
bobs.close(); // Skip them
	
prepared.setArgs("Bob", "Ann");
ResultSet rs = prepared.querySet();
assert(rs.length == 1);
assert(rs[0][0] == 1);
assert(rs[0][1] == "Ann");
assert(rs[1][0] == 2);
assert(rs[1][1] == "Bob");
```

[`Prepared`](http://semitwist.com/mysql-native-docs/v1.1.4/mysql/prepared/PreparedImpl.html)でプリペアドステートメントを利用できます。
`setArgs`で値をセット、`query`は`ResultRange`、`querySet`は`ResultSet`を返します。


## // Nulls

```d
Prepared insert = prepare(conn, "INSERT INTO `tablename` (`id`, `name`) VALUES (?,?)");
insert.setArgs(null, "Cam"); // Also takes Nullable!T
insert.exec();
range = query(conn, "SELECT * FROM `tablename` WHERE `name`='Cam'");
assert( range.front[0][0].type == typeid(typeof(null)) );
```

`null`値の扱いです。

# さいごに

[コミット](https://github.com/simdnyan/dmanbot/commit/d29edc522d5b9e838057177e5b271467931edbb2)しました。

## 注意

> This package supports both Phobos sockets and Vibe.d sockets. Vibe.d support is disabled by default, to avoid unnecessary depencency on Vibe.d. To enable Vibe.d support, use:
> (このパッケージはPhobosのソケットとVibe.dのソケット両方をサポートしています。デフォルトでは不必要な依存を避けるためにVibe.dは無効になっていますが、下記で有効にすることができます。)
>
> `-version=Have_vibe_d_core`

とありますが、DUBの[バグ](https://github.com/dlang/dub/issues/1089)のためVibe.dは常に有効になります。