こんにちは。そのまま続きをやっていきます。
前回は下記のようなコマンドで何か全権限を与えた時点でPCを落としてました。
GRANT ALL PRIVILEGES ON *.* TO 'RWW'@'localhost' WITH GRANT OPTION;
ここで実際にユーザが追加されているのか?そして、権限がどうなっているのか?これを確認する必要がでてきます。これに関しては「わくわくBank」さんに詳しく書いてあったので、そのままやっていこうと思います。
まずは、現在登録されているユーザを出していきます。
SELECT user, host FROM mysql.user;
パッと見た感じ、UserにRWWが登録されていること、hostはlocalhostになっていることがわかります。ちなみにSELECT * FROM mysql.user;
と入力することでuserやhost以外の列も出力できます。全然意味がわからないものがほとんどなので、そのうち理解して行きたいですね。
また、権限周りを確認するために、下記コマンドを入力してみました。
どうやらここでは%の部分がエラーになっているようで。%は正規表現か何かだと思って元から変更しなかったのですが、元記事ではlocalhostではなく%にしていたので、こういう表記になったようだ。そこで、localhostを指定することによって、うまく確認することができた。
ちなみに、前回最新IT技術情報_arkgame.comさんの記事では、GRANT ALL PRIVILEGESした後にFLUSH PRIVILEGESを実行して権限付与を確定させていただのだが、筆者の場合はFLUSH PRIVILEGESせずにPCの電源を落とすだけで権限付与が成功していた。これについてはMy Opinion is my ownに詳細な記載があったため、知りたい方はぜひ見に行ってくださいね。
Contents
3. データベース内でユーザが切り替えられない
せっかくユーザを作成して権限を付与したので、これからそのユーザになってテーブルを作ってやるぞー!となった矢先、mysql内でcurrent userを切り替える方法が探しても見つからない。これは何故だ?と思って同じような質問を見に行くと「クライアントに何を使用しているのか不明ですが、利用ユーザは MySQL への接続の際に指定しているので、一般的には一度接続を解除して、その後、再度別ユーザを指定して接続しなおします。」との回答が。なるほど、そういう思想なんですね。
ということで、一旦EXITを入力してmysqlから脱出した後、今度はRWWとしてログインすることにします。
mysql -u RWW -p
これによって、確かにRWWとしてログインできたようです。
4. データベースの作成
未だにデータベースという概念が分かっていませんが、テーブルデータの塊のことを指すのでしょうか?一応ChatGPTくんもDBはTableを束ねる上位存在というようなことを言っているので、その認識のまま進めていきます。
ちなみに今回扱えるようにしたかったデータはテーブルデータだったため、正直な話データベースを構築して、そこからテーブルに参照させることの意義が理解できていないです。そんなこんなでテーブルから作ろうとしたら怒られたので、愚直にDBから作って行きます。
上記コマンド(SQLではクエリと呼ぶのかしら?)を実行することでTESTDBが作られたのち、GRANTクエリによってRWWにはこのデータベースのすべての権限が与えられたのかな?それではここで、テーブルを作ってみます。
mysql> USE TESTDB
Database changed
mysql> SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| TESTDB |
+------------+
1 row in set (0.00 sec)
まず、テーブルを作りたいDBに移動します。もちろんこのDBの中身は空っぽです。ここにTableを追加していくことにしました。最初は雑に年齢と性別だけ書いてある非常にシンプルなやつにしています。
5. 他言語からの呼び出し
ここまでやってみて気づきました。SQLは対話ベースの仕組みですので、人為的ミスが起きやすいということに。SQLの文法そのものはかなり直感的で、英語さえ理解できれば多少はルールも応用が効くのですが、いかんせんタイポや操作ミスに弱そうですよね。そこで、ミスの起こりやすい入力系、操作系は別言語で自動化してしまった方が良いという結論になりました。そこで、SQLをC++言語で発行できるライブラリについて調べてみました。すると、OracleよりOSSで公開されているものを見つけました。Ubuntuを選択すればdpkg -i hogehogeで入れられましたので早速採用してみます。
まずは公式ドキュメントのチュートリアルとQiitaにある手順書(tomov3さん)に沿って組んでいきます。tomov3さんの設定はヘッダに任せてしまうという思想が良いと思ったのでそのように記述しましたが、後から見るとjsonとかにしておくのが良かったですね。これはあとから直しておきたい。
ということでヘッダファイル(mysql_config.hpp)はこちら。
#define HOST "tcp://127.0.0.1:3306"
#define USER "RWW"
#define PASSWORD "20230218"
#define DATABASE "TESTDB"
続いて、メインプログラムであるsqlpractice.cppを書いていきます。まず、ヘッダファイルに規定しているHOST等の文字列はchar型配列で定義されているため”+”演算ができず不便です。これを解決するため、string型に直す関数を準備します。なお返り値は「string型のデータを要素として持つ構造体を作る」のと「string型文字列を複数格納した配列」のどちらかを選択すれば良いと思いますが、今回は後者を選択しました。また、後者の場合、Pythonで言うList型かdict型のどちらかを選択しますが、List型にしています。これは個人実装では早くて簡単だから選択しますが、後々振り返った際にわかりにくいのでオススメはしません。素直に構造体を使うか、dict等で引数が明示的にされる方を選択しましょう。
5.1 sqlpractice.cppのsetup_userinfo()関数
vector<string> setup_userinfo()
{
string url(HOST);
const string user(USER);
const string pass(PASSWORD);
const string database(DATABASE);
vector<string> ret_arrangement=
{
url,user,pass,database
};
cout << "Connector/C++ tutorial framework..." << endl;
cout << " |URL:"<<url<<\
" |USER:" <<user<<\
" |PASS:"<<pass<<\
" |DB:"<<database<<"|"<<endl;
cout << endl;
return (ret_arrangement);
}
5.2 sqlpractice.cppのmain関数付近
#include <vector>
#include <mysql_driver.h>
#include <mysql_connection.h>
#include <mysql_error.h>
#include <cppconn/statement.h>
#include <cppconn/resultset.h>
#include "mysql_config.hpp"
using namespace std;
vector<string> setup_userinfo();
int main()
{
vector<string> userinfo = setup_userinfo();
}
6. includeの関係でうまく動作しない?
続いて、実際にこのチュートリアルを参考にMySQLを触ろうとした際に何故か下記のようなエラーに遭遇。どうやらdriverクラスに登録された関数が定義されていない?みたいな文章である。
調べてみると、英語ではあるがStackOverflowにて回答がありました。どうやら関連するincludeファイルがあるようで、それを明示的に指定してビルドするだけで治るよう。まずは、まえもって下記を実行してincludeファイルを入れておきます。
sudo apt-get install libmysqlcppconn-dev
その後、ビルドコマンドに-Iでインクルードを指定、-Lや-lも指定すればコンパイルが通るようになりました。
6.1 この時点でのメイン関数
int main()
{
vector<string> userinfo = setup_userinfo();
sql::mysql::MySQL_Driver *driver;
sql::Connection *con;
driver = sql::mysql::get_mysql_driver_instance();
6.2 ビルドスクリプト
g++ -Wall -I/usr/include/cppconn -o testapp sqlpractice.cpp -L/usr/lib -lmysqlcppconn
7. 実際にクエリを送り込む
実際にクエリを送り込むチュートリアルはここにありました。これに沿って同じように実装していきます。
7.1 この時点でのメイン関数付近
int main()
{
vector<string> userinfo = setup_userinfo();
sql::mysql::MySQL_Driver *driver;
sql::Connection *con;
sql::Statement *stmt;
driver = sql::mysql::get_mysql_driver_instance();
con = driver->connect(userinfo[0], userinfo[1], userinfo[2]);
stmt = con->createStatement();
stmt->execute("USE "+ userinfo[3]);
stmt->execute("DROP TABLE IF EXISTS test");
stmt->execute("CREATE TABLE test(id INT, label CHAR(1))");
stmt->execute("INSERT INTO test(id, label) VALUES (1, 'a')");
delete stmt;
delete con;
}
クエリに関してはmysql_driverから生成される、connectionオブジェクトよって生成されるStatementによって実行できるよう(かなり複雑・・)。
動きを見ている限りはconnectionオブジェクトが生成された時点でmysqlへの接続が行われているような遅延が発生している。そして、stmt->execute関数でクエリが実行される形となっている。
これを実行した後に、MySQLに入り直して確認してみます。
mysql -u RWW -p
mysql> USE TESTDB;
mysql> show columns from test;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| label | char(1) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.01 sec)
どうやらうまくできているようです。嬉しい。
ただ、まだまだチュートリアルは続くので、機会があれば完全版まで仕上げたものを紹介したいですね。
8.まとめ
今週末は2日間に渡って、MySQLのインストールから操作までを勉強しつつ、CppからMySQLを操作する簡単なプロトタイピングを行いました。なかなか難易度が高かった印象ですが、ドキュメントが充実していたおかげでなんとか達成できて満足です。うまく動くようになってきたら、OSSで公開したいですね。
最後に余談ですが、ChatGPTくんに自分で作ったソースコードに他人の作ったライブラリが含まれている場合の公開方法について相談した際の会話が非常に良かったので載せておきます。彼の回答中では3番目の選択肢が自分の理想かなと思います。自分の理想のOSを自作する際に参考にしたいです。