C言語でMySQLにinsertする書き方

sql_strの箇所をselectからinsert文に変えるだけです。update, deleteも一緒です。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>

int main(){
	MYSQL *conn = NULL;
	MYSQL_RES *resp = NULL;
     // MYSQL_ROW row;
	char sql_str[255];
	char *sql_serv = "localhost";
	char *user = "root";
	char *passwd = "hogehoge";
	char *db_name = "test";

	memset( &sql_str[0], 0x00, sizeof(sql_str));

	conn = mysql_init(NULL);
	if(!mysql_real_connect(conn, sql_serv, user, passwd, db_name, 0, NULL, 0)){ 
		 exit(-1);
	}

	snprintf(&sql_str[0], sizeof(sql_str)-1, "insert into test (id,name) values (4, 'ddd')"); 
	if(mysql_query(conn, &sql_str[0])){ 
		mysql_close(conn);
		exit(-1);
	}

	mysql_free_result(resp);
	mysql_close(conn);

	return 0;
}

### コンパイル&実行
$ gcc -Wall -o dev hello.c -lmysqlclient -L/usr/lib/x86_64-linux-gnu/
$ ./dev

### 動作確認
mysql> select * from test;
+——+——+
| id | name |
+——+——+
| 1 | aaa |
| 2 | bbb |
| 3 | ccc |
| 4 | ddd |
+——+——+
4 rows in set (0.00 sec)

なるほど、DBとの接続はわかった。次はGUIか。

C言語でMySQLのレコードからSelectする方法

### ソースコード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>

int main(){
	MYSQL *conn = NULL;
	MYSQL_RES *resp = NULL;
	MYSQL_ROW row;
	char sql_str[255];
	char *sql_serv = "localhost";
	char *user = "root";
	char *passwd = "hogehoge";
	char *db_name = "test";

	memset( &sql_str[0], 0x00, sizeof(sql_str)); // memsetはメモリに指定バイト数分の値をセット, 0x00はヌル文字

	conn = mysql_init(NULL);
	if(!mysql_real_connect(conn, sql_serv, user, passwd, db_name, 0, NULL, 0)){ // サーバ接続開始 mysql_real_connect(mysql, host, user,passwd,db, port,  unix_socket, client_flag)
		 exit(-1);
	}

	// クエリ実行
	snprintf(&sql_str[0], sizeof(sql_str)-1, "select * from test");  // snprintfは指定文字数分だけ文字配列に書き込む
	if(mysql_query(conn, &sql_str[0])){  // sql_str によって指示される SQL ステートメントを実行
		mysql_close(conn);
		exit(-1);
	}

	// レスポンス
	resp = mysql_use_result(conn);  // 結果セットの取得を開始
	while((row = mysql_fetch_row(resp)) != NULL){   // 結果を添字配列として取得する
		printf("%d: %s\n", atoi(row[0]), row[1]);
	}

	mysql_free_result(resp);  // 結果保持用メモリを開放
	mysql_close(conn); // MySQL 接続を閉じる

	return 0;
}

### ライブラリのlibmysqlclient.soの配置場所を確認
$ find / -name “libmysqlclient.so”
/usr/lib/x86_64-linux-gnu/libmysqlclient.so

### コンパイル、実行
// -Wは警告メッセージ抑制, -Lはオブジェクトライブラリ
$ gcc -Wall -o dev hello.c -lmysqlclient -L/usr/lib/x86_64-linux-gnu/
$ ./dev
1: aaa
2: bbb
3: ccc

おいおい、マジかよ。。。
感動した。

Ubuntu18.04 mysql5.7でAccess denied for user ‘root’@’localhost’

ubuntu18.04でtasksel install lamp-serverでmysqlを入れ、パスワードの設定をしたが、アクセスできない時

$ sudo mysql -u root
mysql> update user set authentication_string=password(“password”) where User=’root’;
$ mysql -u root -p
Enter password:
ERROR 1698 (28000): Access denied for user ‘root’@’localhost’

むむむむ?

MySQL 5.7では認証にauth_socket plugin(MySQL :: Security in MySQL :: 6.1.11 Socket Peer-Credential Pluggable Authentication) というものを使っていて、これを使っているとパスワードではアクセスできず、MySQLのユーザー名と、システムのユーザー名が一致しているときのみアクセスできる

mysql> SELECT User, Host, plugin FROM mysql.user;
+——————+———–+———————–+
| User | Host | plugin |
+——————+———–+———————–+
| root | localhost | auth_socket |
| mysql.session | localhost | mysql_native_password |
| mysql.sys | localhost | mysql_native_password |
| debian-sys-maint | localhost | mysql_native_password |
+——————+———–+———————–+
4 rows in set (0.00 sec)

mysql> UPDATE user SET plugin=’mysql_native_password’ WHERE User=’root’;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

$ mysql -u root -p

EC2のmysql8.0.19でmmap(137363456 bytes) failed; errno 12

EC2でmysqlが頻繁に落ちる。
暫定的に以下を実行すると再起動できるが、1日経つとまた落ちる。

sudo touch /var/lib/mysql/mysql.sock
sudo chmod 777 /var/lib/mysql/mysql.sock
sudo service mysqld restart

cronの実行が影響しているのか? cronを止めて、PDOを手動実行するも問題なし。

ログを確認するとmmap(137363456 bytes) failed;となっている

$ sudo cat /var/log/mysqld.log
2020-03-21T20:45:21.161522Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.19) starting as process 12458
2020-03-21T20:45:21.299847Z 0 [ERROR] [MY-012681] [InnoDB] mmap(137363456 bytes) failed; errno 12
2020-03-21T20:45:21.299951Z 1 [ERROR] [MY-012956] [InnoDB] Cannot allocate memory for the buffer pool
2020-03-21T20:45:21.300002Z 1 [ERROR] [MY-012930] [InnoDB] Plugin initialization aborted with error Generic error.
2020-03-21T20:45:21.301125Z 1 [ERROR] [MY-010334] [Server] Failed to initialize DD Storage Engine
2020-03-21T20:45:21.301283Z 0 [ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
2020-03-21T20:45:21.301362Z 0 [ERROR] [MY-010119] [Server] Aborting
2020-03-21T20:45:21.306253Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.19)  MySQL Community Server - GPL.

innodb_buffer_pool_sizeを超えていることがわかる

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+
1 row in set (0.00 sec)

buffer_pool_sizeを256Mに変更
$ sudo vi /etc/my.cnf

[mysqld]
innodb_buffer_pool_size = 256M

$ sudo service mysqld restart
mysql> SHOW VARIABLES LIKE ‘innodb_buffer_pool_size’;
+————————-+———–+
| Variable_name | Value |
+————————-+———–+
| innodb_buffer_pool_size | 268435456 |
+————————-+———–+
1 row in set (0.02 sec)

これで2~3日様子を見よう。

mysqlからカラム名も取得してfputcsvによる帳票出力

### データ作成
create database universe;
use universe;
create table planets(
id int primary key auto_increment,
name varchar(255),
diameter float,
mass float
);
describe planets;
insert into planets(name, diameter, mass) values (‘Sun’,’110′,’330000′);
insert into planets(name, diameter, mass) values (‘Mercury’,’0.38′,’0.06′);
insert into planets(name, diameter, mass) values (‘Venus’,’0.95′,’0.82′);
insert into planets(name, diameter, mass) values (‘Earth’,’1′,’1′);
insert into planets(name, diameter, mass) values (‘Mars’,’0.53′,’0.11′);
insert into planets(name, diameter, mass) values (‘Jupiter’,’11’,’320′);
insert into planets(name, diameter, mass) values (‘Saturn’,’9.5′,’95’);
insert into planets(name, diameter, mass) values (‘Uranus’,’4′,’15’);
insert into planets(name, diameter, mass) values (‘Neptune’,’3.9′,’17’);
select * from planets;

### データ出力
var_dumpでテストしてから、出力します。

try {
$dbh = new PDO('mysql:host=localhost;dbname=universe;charset=utf8','root','hogehoge', array(PDO::ATTR_EMULATE_PREPARES => false));
} catch(PDOException $e){
    exit('データベース接続失敗。'.$e->getMessage());
}

$export_sql = "select * from planets";

// $stmt = $dbh->query($export_sql);
// $row = $stmt->fetchAll(PDO::FETCH_ASSOC);
// var_dump($row);

$file_path = "csv/planets.csv";
$export_csv_title = ["id","name","diameter","mass"];

foreach($export_csv_title as $key => $val){
	$export_header[] = mb_convert_encoding($val, 'SJIS-win', 'UTF-8');
	}

	if(touch($file_path)){
		$file = new SplFileObject($file_path, "w");

		$file->fputcsv($export_header);
		$stmt = $dbh->query($export_sql);

		while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
			$file->fputcsv($row);
		}
		$dbh = null;

	}

echo "done";

### カラム名も取得したい場合
show columnsとして配列を取得して、その中からFieldを取り出す

$export_column = "show columns from planets";
$stmt = $dbh->query($export_column);
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
	$field[] = $row["Field"];
}

つなげると

try {
$dbh = new PDO('mysql:host=localhost;dbname=universe;charset=utf8','root','hogehoge', array(PDO::ATTR_EMULATE_PREPARES => false));
} catch(PDOException $e){
    exit('データベース接続失敗。'.$e->getMessage());
}

$export_column = "show columns from planets";
$stmt = $dbh->query($export_column);
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
	$field[] = $row["Field"];
}

$export_sql = "select * from planets";
$file_path = "csv/planets.csv";
$export_csv_title = $field;

foreach($export_csv_title as $key => $val){
	$export_header[] = mb_convert_encoding($val, 'SJIS-win', 'UTF-8');
	}

	if(touch($file_path)){
		$file = new SplFileObject($file_path, "w");

		$file->fputcsv($export_header);
		$stmt = $dbh->query($export_sql);

		while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
			$file->fputcsv($row);
		}
		$dbh = null;

	}

echo "done";

カラム名だけ取得のニーズはあるので、その命令文を作っても良いように思ったが、、、
MySQL8.0のGithubレポを見ると軽々しく言えんな。。
https://github.com/mysql/mysql-server/blob/8.0/sql/sql_select.cc

mysqlのパスワードが分からない時

ログに初期パスワードの記載有無を確認
$ sudo cat /var/log/mysqld.log

ないときは、再設定
$ service mysqld stop
$ mysqld_safe –skip-grant-tables &
$ mysql -u root
$ use mysql;
$ update user set password=PASSWORD(“${password}”) where User=’root’;
$ flush privileges;
$ quit
$ service mysqld stop
$ service mysqld start

パスワード失念した時は、冷静に^^

SQL(DDL, DML, DCL)

DDL, DML, DCLって何?

DDL(Data Definition Language)
CREATEやDROP、ALTERなどデータベースオブジェクトの生成や削除変更を行うコマンド

DML(Data Manipulation Language)
SELECT/INSERT/UPDATE/DELETEなどテーブルに対するデータの取得、追加、更新、削除を行うコマンド

DCL(Data Control Language)
BEGIN, COMMIT、ROLLBACKなどトランザクションを制御するためのコマンド

実務上はあまり使用しない名称だから、こんな言い方もあるんだなー程度か。

key

>KEY は通常、INDEX のシノニムです。キー属性 PRIMARY KEY もまた、カラム定義内で指定する場合は、単に KEY として指定できます。これは、ほかのデータベースシステムとの互換性のために実装されました。
Indexって理解でOK?
->確実に 識別する(identification) ための 主たる制約

MySQL unsigned

Integer types can handle positive and negative numbers, but if the data type is followed by UNSIGNED, only 0 and positive numbers can be stored. Such data types are called unsigned integer types. For example, to make INT type unsigned, write as follows.

mysql> create table numtest(
-> num1 smallint unsigned
-> );
Query OK, 0 rows affected (0.17 sec)

mysql> insert into numtest values(5000);
Query OK, 1 row affected (0.12 sec)

mysql> insert into numtest values(-10);
Query OK, 1 row affected, 1 warning (0.03 sec)

mysql> select * from numtest;
+——+
| num1 |
+——+
| 5000 |
| 0 |
+——+
2 rows in set (0.00 sec)

MySQLのcomments

CREATE TABLEの際にcommentを追加する
mysql> CREATE TABLE corporate(
-> name varchar(255) comment ‘商号’,
-> address varchar(255) comment ‘所在地’
-> )
-> default charset=utf8
-> comment=’会社テーブル’;
Query OK, 0 rows affected (0.36 sec)

mysql> describe corporate;
+———+————–+——+—–+———+——-+
| Field | Type | Null | Key | Default | Extra |
+———+————–+——+—–+———+——-+
| name | varchar(255) | YES | | NULL | |
| address | varchar(255) | YES | | NULL | |
+———+————–+——+—–+———+——-+
2 rows in set (0.06 sec)

show full columnsでカラムのcomment表示
mysql> show full columns from corporate;
+———+————–+—————–+——+—–+———+——-+—– —————————-+———–+
| Field | Type | Collation | Null | Key | Default | Extra | Priv ileges | Comment |
+———+————–+—————–+——+—–+———+——-+—– —————————-+———–+
| name | varchar(255) | utf8_general_ci | YES | | NULL | | sele ct,insert,update,references | 商号 |
| address | varchar(255) | utf8_general_ci | YES | | NULL | | sele ct,insert,update,references | 所在地 |
+———+————–+—————–+——+—–+———+——-+—– —————————-+———–+
2 rows in set (0.00 sec)

カラムの要件定義でカラムの意味は決めるので、sqlの中にわざわざcommentを追加する必要性は限られるようにも思うが、ケースバイケースということか?