mita2 database life

主にMySQLに関するメモです

MySQL 8.0 の LOAD DATA で The used command is not allowed with this MySQL version エラー

TL;DR

  • MySQL 8.0 で LOAD DATA INFILE LOCAL を利用するには、
  • サーバとクライアント両方で local-infile パラメータを ON にする必要がある
  • セキュリティ強化のため、8.0から LOAD DATA INFILE LOCAL はデフォルトで無効にされた

MySQL 8.0 で LOAD DATA INFILE LOCAL が通らない

Loading local data is disabled; this must be enabled on both the client and server sides エラー*1LOAD DATA INFILE LOCAL が通らなくなっていた 。

mysql> LOAD DATA LOCAL INFILE 'file.csv' INTO TABLE tbl SET name=@1, created_at=@2;
ERROR 3948 (42000): Loading local data is disabled; this must be enabled on both the client and server sides

local-infile パラメータ

MySQL 8.0 で LOAD DATA LOCAL INFILE は無効化されたようです。 有効にするには、local-infileON に設定する必要があるようです。

ON にして試します。

mysql> SET GLOBAL local_infile=on; 
Query OK, 0 rows affected (0.00 sec)

mysql> LOAD DATA LOCAL INFILE 'file.csv' INTO TABLE tbl SET name=@1, created_at=@2;
ERROR 3948 (42000): Loading local data is disabled; this must be enabled on both the client and server sides

ダメでした。。。クライアント側でも--local-infileを有効化しないと、通らないようです。

$ mysql -h my8.local --local-infile=1
mysql>  LOAD DATA LOCAL INFILE 'file.csv' INTO TABLE tbl SET name=@1, created_at=@2;
Query OK, 2 rows affected, 10 warnings (0.05 sec)

行けました 👍

なぜ、無効化されたのか?

LOAD DATA LOCAL INFILE がデフォルトで無効化された理由はマニュアルに記載されています。

The transfer of the file from the client host to the server host is initiated by the MySQL server. In theory, a patched server could be built that would tell the client program to transfer a file of the server's choosing rather than the file named by the client in the LOAD DATA statement. Such a server could access any file on the client host to which the client user has read access. (A patched server could in fact reply with a file-transfer request to any statement, not just LOAD DATA LOCAL, so a more fundamental issue is that clients should not connect to untrusted servers.)

https://dev.mysql.com/doc/refman/8.0/en/load-data-local.html

LOAD DATA LOCAL INFILE ではSQL中でロードするファイル名を指定しているものの、実際は、サーバからクライアントに対して、ロードするファイルを指示する仕組みになっています。 悪意のある(改ざんされた)サーバに対して実行した場合、クライアントが指定したファイルと異なるファイルをサーバが指示して、送信させることが可能になってしまいます。

In a Web environment where the clients are connecting from a Web server, a user could use LOAD DATA LOCAL to read any files that the Web server process has read access to (assuming that a user could run any statement against the SQL server). In this environment, the client with respect to the MySQL server actually is the Web server, not a remote program being run by users who connect to the Web server.

もう1つ理由が述べられてます。 任意のSQLを実行できるようなWebアプリケーションを実装していた場合、LOAD DATA INFILE LOCAL を使って、任意のWebサーバ上のファイルを読み取ることが可能になってしまう。

例えば、SQLインジェクション脆弱性があった場合、DBのデータを操作されてしまうだけでなく、Webサーバ上のファイルの読み取りも許してしまうことに繋がってしまうということですね。

LOAD DATA INFILE LOCAL は セキュリティ上のリスクになる可能性があるということで、デフォルトで無効化されたことがわかりました。 使い続ける場合は、リスクを理解した上で利用する必要がありそうです。

*1:8.0.18までは The used command is not allowed with this MySQL version というエラー文言でした

MySQL 8.0 Authentication requires secure connection エラーの件

MySQL 8.0 から デフォルトのパスワードの仕組みが caching_sha2_password に変更されました。 これに関連して、yoku0825 さんが以下のエントリーを掲載してます。

yoku0825.blogspot.com

yokuさんのエントリーを抜粋すると:

  • caching_sha2_password プラグインを使っているアカウント
  • まだサーバー側にSHA2キャッシュが作られていないアカウント
  • サーバーの公開鍵を指定していない非SSLTCP接続

この条件にすべて合致しているとログインが転けて↑のエラーを食らう。

試します

挙動をおさらいします。

サーバ側で FLUSH PRIVILEGES を実行しSHA2キャッシュをクリアしたあと、SSL無効化して接続します。 確かに、Authentication requires secure connection. とエラーが出て、接続できません。

$ mysql -uuser -h my8.local --ssl-mode=DISABLED
ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.

次に、--get-server-public-key をつけます。このオプションはサーバの公開鍵を自動でクライアントに持ってくるオプションです。 サーバの公開鍵を使って、パスワードを暗号化し、サーバへ送信し、認証が行われます。

$ mysql -uuser -h my8.local --ssl-mode=DISABLED --get-server-public-key
mysql>

一度、接続すれば、SHA2キャッシュが作られるので、以降は--get-server-public-key を指定しなくても接続できるようになります。

$ mysql -uuser -h my8.local --ssl-mode=DISABLED
mysql>

運用への影響を考えてみる

(サーバを再起動したりして) SHA2キャッシュがクリアされたときに、SSLを利用していないクライアントが一斉に接続エラーになっては困ります。 そのようなことが起こり得るか検討してみます。

mysql コマンド

mysql コマンドはデフォルトでSSLを利用します。あえてSSLを無効化しているようなケースはあまりないでしょう。 コマンドラインMySQLを使っている場合は、心配しなくて良さそうです。

各種、言語ドライバーはどうでしょうか? mysql コマンドの --get-server-public-key 相当の挙動がドライバ側に実装されていれば、開発者側では特に意識する必要はないということになりますが。。。

ドライバの挙動

PHP 7.4

PHP 7.4 で試します。

<?php
try {
  $pdo = new PDO('mysql:host=my8.local;charset=utf8','user','Password%123');
} catch (PDOException $e) {
  exit($e->getMessage());
}

$stmt = $pdo->query("SHOW STATUS LIKE 'Ssl_cipher'");
print_r($stmt->fetch(PDO::FETCH_ASSOC));

おっ、特にエラーなく接続できた!Ssl_cipherが空なのでSSL無効でエラーなく接続できました。

$ php pdo.php
Array
(
    [Variable_name] => Ssl_cipher
    [Value] =>
)

PHP の コミットログをみると、きちんとサーバ側の公開鍵を自動で取ってくる実装が追加されていそうです👏

https://github.com/php/php-src/commit/d6e81f0bfd0cb90586dd83d4fd47a4302605261a

PHP7.3

PHP 7.3 でも試します。

$ php pdo.php
SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client

そもそも、caching_sha2_password がサポートされてないようですね 😢 MySQL 8 を使うには PHP7.4 以降を利用する必要があることがわかりました。

node.js

var mysql = require('mysql');

var con = mysql.createConnection({
  host: "my8.local",
  user: "user",
  password: "Password%123"
});

con.connect(function(err) {
  if (err) throw err;
  console.log("Connected!");
});

// simple query
con.query(
  "SHOW STATUS LIKE 'Ssl_cipher'",
  function(err, results, fields) {
    console.log(results);
  }
);

node.js の mysql もまだ caching_sha2_password をサポートしてないようですね。。。。

$ node mysql.js
/home/mita2/nodejs-conn/node_modules/mysql/lib/protocol/Parser.js:437
      throw err; // Rethrow non-MySQL errors
      ^
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client

PRは出ているのですが、まだ、取り込まれてないようです。 https://github.com/mysqljs/mysql/pull/2233

mysql2 のほうには取り込まれているようなので、mysql2 を試します。 mysqlmysql2 は互換性があります。ドライバを差し替えるにはvar mysql = require('mysql');var mysql = require('mysql2'); にするだけです。

$ node mysql.js
Connected!
[ TextRow { Variable_name: 'Ssl_cipher', Value: '' } ]

大丈夫そうです😄

まとめ

  • PHPは 7.4 以上なら大丈夫!
  • Node.js のドライバは mysql2 なら大丈夫!

MySQL 8.0 の binlog_row_metadata オプションを試す

TL;DR

  • MySQL 8.0 で導入された binlog_row_metadata について調べた
  • 今のところデータ連携用途以外では使うところはなさそう

binlog_row_metadata オプションとは何か

マニュアルでは以下のように記載されています。

  • デフォルトはMINIMAL(最小限)
  • ROWベースのみ有効
  • MINIMALでは、SIGNEDフラグ、カラムの文字コードセット、Geometoryタイプのみをバイナリログに記録する。

Configures the amount of table metadata added to the binary log when using row-based logging. When set to MINIMAL, the default, only metadata related to SIGNED flags, column character set and geometry types are logged. When set to FULL complete metadata for tables is logged, such as column name, ENUM or SET string values, PRIMARY KEY information, and so on. https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_binlog_row_metadata

実際にMINIMALとFULLで比較してみます。 メタデータmysqlbinlog コマンドに --print-table-metadata オプションを指定し、確認します。

テスト用のテーブル:

mysql> DESC t.bin_meta_t;
+-------+---------------------+------+-----+---------+----------------+
| Field | Type                | Null | Key | Default | Extra          |
+-------+---------------------+------+-----+---------+----------------+
| pk    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| col1  | varchar(255)        | YES  |     | NULL    |                |
+-------+---------------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

MINIMAL

# mysqlbinlog -vv --print-table-metadata binlog.000009
<snip>
# at 306
#191231 16:52:57 server id 1  end_log_pos 367 CRC32 0x99f3c5db  Table_map: `t`.`bin_meta_t` mapped to number 93
# Columns(BIGINT UNSIGNED NOT NULL,
#         VARCHAR(255) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci)
# at 367
#191231 16:52:57 server id 1  end_log_pos 425 CRC32 0xd85ee9d6  Write_rows: table id 93 flags: STMT_END_F

BINLOG '
Wf4KXhMBAAAAPQAAAG8BAAAAAF0AAAAAAAEAAXQACmJpbl9tZXRhX3QAAggPAvwDAgEBgAID/P8A
28XzmQ==
Wf4KXh4BAAAAOgAAAKkBAAAAAF0AAAAAAAEAAgAC/wABAAAAAAAAAAwATUlOSU1BTCBNRVRB1ule
2A==
'/*!*/;
### INSERT INTO `t`.`bin_meta_t`
### SET
###   @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='MINIMAL META' /* VARSTRING(1020) meta=1020 nullable=1 is_null=0 */
# at 425
#191231 16:52:57 server id 1  end_log_pos 456 CRC32 0xaedc614c  Xid = 179
<snip>

FULL

# mysqlbinlog -vv  --print-table-metadata binlog.000009
<snip>
# at 306
#191231 16:53:49 server id 1  end_log_pos 380 CRC32 0xd2360199  Table_map: `t`.`bin_meta_t` mapped to number 93
# Columns(`pk` BIGINT UNSIGNED NOT NULL,
#         `col1` VARCHAR(255) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci)
# Primary Key(pk)
# at 380
#191231 16:53:49 server id 1  end_log_pos 435 CRC32 0x5826b7f8  Write_rows: table id 93 flags: STMT_END_F

BINLOG '
jf4KXhMBAAAASgAAAHwBAAAAAF0AAAAAAAEAAXQACmJpbl9tZXRhX3QAAggPAvwDAgEBgAID/P8A
BAgCcGsEY29sMQgBAJkBNtI=
jf4KXh4BAAAANwAAALMBAAAAAF0AAAAAAAEAAgAC/wACAAAAAAAAAAkARlVMTCBNRVRB+LcmWA==
'/*!*/;
### INSERT INTO `t`.`bin_meta_t`
### SET
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='FULL META' /* VARSTRING(1020) meta=1020 nullable=1 is_null=0 */
# at 435
<snip>

Columns の部分が異なっています。 FULL の場合は以下のようにカラム名メタデータに含まれていることが確認できました。また、プライマリキーになっているカラム名も追加されています。MINIMAL ではカラム名は含まれません。「テーブルの何番目のカラムが変更されたか」のみが記載されています。

# Columns(`pk` BIGINT UNSIGNED NOT NULL,
#         `col1` VARCHAR(255) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci)
# Primary Key(pk)

どういう時に役に立つのか

マニュアルには以下の2つが記載されてます。

1つ目:データ連携がやりやすくなる。データが変更されたカラムの名前がバイナリログを見れば、すぐ判断できるため、データを連携する仕組みがこれまでより用意に実装できるということでしょう。

  • External software can use the metadata to decode row events and store the data into external databases, such as a data warehouse.

2つ目:マスターとスレーブで構成が異なるテーブルであってもレプリケーションが可能になるマスターとスレーブでカラムの順番が異なるとかそういうケースを指していると思われる。

  • Slaves use the metadata to transfer data when its table structure is different from the master's.

これは実際試してみたのですが、うまく動きませんでした(レプリケーションが普通に止まります)。 Worklogを見ると、「スコープ外」と記載されているので、まだ実装されてないようです。

There is a online DDL solution, which first changes slave's data structure and then change master's data structure. So the table on slave may have more or less columns, different column types and different column orders. If required metadata is logged, then slave can support the online DDL solution better. However, making the applier aware of the additional metadata when applying ROW events is out of this worklog scope.

MySQL の クッキーを焼いた

クッキーを焼く

MySQL クッキーを焼いた話です。技術的な話は出てきませんw

年末年始は、3D プリンタで遊んでました。 3Dプリンタ扱うのは初めてです。試しに、MySQLのイルカのロゴ(Sakila)のクッキー型を作りました。

https://www.mysql.com/common/logos/logo-mysql-170x115.png

f:id:mita2db:20200105215101j:plain

Sakila クッキー型の作り方

  1. ネットで適当に拾った画像データをベクトルデータに変換する。 Image2Vector を使いました。

    Image2Vector - Converts Images to Vector Graphics

    Image2Vector - Converts Images to Vector Graphics

    • Wenzhi Liao
    • グラフィック&デザイン
    • 無料
    apps.apple.com

  2. ベクトルデータからゴミを取り除く 変換したベクトルデータ(STL)にはゴミが含まれている場合があります。 ベクトルデータを扱えるドロー系のイラストを扱えるソフトウェアでゴミを削除します。自分は Affinity Designer を使いました。

affinity.serif.com 3. モデリングソフトで3Dに変換して、クッキー型に仕上げる f:id:mita2db:20200105213937p:plain

できあがり

f:id:mita2db:20200105214844j:plain f:id:mita2db:20200105214847j:plain

MySQL テストコードを書いてみる

先日、MySQLをソースからビルドする方法を書きました。今日はテストコードについてのエントリーです。 mita2db.hateblo.jp

MySQL のテストコード

mysql-test/suite ディレクトリ以下にテストコードが記述されています。 5800以上のテストが記述されています。

$ find ~/mysql-8.0.18/mysql-test/suite/ -type f -name '*.test' | wc -l
5854

MySQL 8.0 でも 500以上のテストが追加されたとアピールされてます(スライド11ページ)。MySQLの品質はこのテストコードで担保されているんですね。

www.slideshare.net

テストを実行してみる

ビルドしたディレクトリの mysql-test ディレクトリ以下に、テストを実行するためのスクリプトが配置されます。 mtrmysql-test-run がそのスクリプトです。どちらも実態は mysql-test-run.pl で、同じものです。

$ ls -alh
total 24K
drwxr-xr-x  5 mita2san users  174 Dec 27 11:03 .
drwxr-xr-x 32 mita2san users 4.0K Dec 26 17:02 ..
drwxr-xr-x  3 mita2san users   87 Dec 26 13:41 CMakeFiles
-rw-r--r--  1 mita2san users 2.5K Dec 26 13:41 cmake_install.cmake
-rw-r--r--  1 mita2san users  295 Dec 21 14:07 CTestTestfile.cmake
drwxr-xr-x  3 mita2san users   16 Dec 21 14:07 lib
-rw-r--r--  1 mita2san users 7.8K Dec 26 13:41 Makefile
lrwxrwxrwx  1 mita2san users   19 Dec 26 13:40 mtr -> ./mysql-test-run.pl
lrwxrwxrwx  1 mita2san users   19 Dec 26 13:40 mysql-test-run -> ./mysql-test-run.pl
-rwxr-xr-x  1 mita2san users  252 Dec 21 14:07 mysql-test-run.pl
drwxr-xr-x  8 mita2san users   97 Dec 27 11:03 var

このコマンドの引数にテストのファイル名を指定することで、特定のテストだけを実行することができます。 何も引数を指定しなければ全てのテストが実行されます。

$ ls  mysql-test/suite/sys_vars/t/ | grep binlog_format
binlog_format_basic.test

$ ./mtr binlog_format_basic
<snip>
==============================================================================
                  TEST NAME                       RESULT  TIME (ms) COMMENT
------------------------------------------------------------------------------
[100%] sys_vars.binlog_format_basic              [ pass ]     13
------------------------------------------------------------------------------
The servers were restarted 0 times
The servers were reinitialized 0 times
Spent 0.013 of 17 seconds executing testcases

Completed: All 1 tests were successful.

binlog_format_basic はこのようなテストコードです。 performance_schema.global_variablesbinlog_format の列が存在することを確かめていまいした。

--echo '#---------------------BS_STVARS_002_03----------------------#'
#################################################################
# Check if the value in GLOBAL Table matches value in variable  #
#################################################################

--disable_warnings
SELECT @@GLOBAL.binlog_format = VARIABLE_VALUE
FROM performance_schema.global_variables
WHERE VARIABLE_NAME='binlog_format';
--enable_warnings
--echo 1 Expected

テストを記述してみる

オリジナルのテストを記述してみたいと思います。

mysql-test/suite/ 以下に任意のデュレクトリを作成し、その下に t というディレクトリにテストスクリプトを記述します。 r にはそのスクリプトの期待される出力を記述します。 SELECTを1回だけ実行するシンプルなテストを記述してみました。

$ cd ../mysql-test/suite/
$ mkdir -p mita2/{t,r}
$ touch mita2/r/sample_t.result

$ vim mita2/t/sample_t.test
SELECT "123";
--echo 123 Expected

実行します。

$ ./mtr sample_t
Logging: /home/mita2san/mysql-8.0.18/mysql-test/mysql-test-run.pl sample_t
MySQL Version 8.0.18
Checking supported features
 - Binaries are debug compiled
Using 'all' suites
Collecting tests
Checking leftover processes
Removing old var directory
Creating var directory '/home/mita2san/mysql-8.0.18/build/mysql-test/var'
Installing system database
Using parallel: 1

==============================================================================
                  TEST NAME                       RESULT  TIME (ms) COMMENT
------------------------------------------------------------------------------
[100%] mita2.sample_t                            [ fail ]
        Test ended at 2019-12-27 12:20:10

CURRENT_TEST: mita2.sample_t
--- /home/mita2san/mysql-8.0.18/mysql-test/suite/mita2/r/sample_t.result        2019-12-27 05:30:37.966983335 +0300
+++ /home/mita2san/mysql-8.0.18/build/mysql-test/var/log/sample_t.reject        2019-12-27 06:20:09.905602126 +0300
@@ -0,0 +1,4 @@
+SELECT "123";
+123
+123
+123 Expected

mysqltest: Result length mismatch

safe_process[21816]: Child process: 21817, exit: 1

 - the logfile can be found in '/home/mita2san/mysql-8.0.18/build/mysql-test/var/log/mita2.sample_t/sample_t.log'

[ fail ] と、失敗しました。 まだ、r ディレクトリに 期待する出力結果を記述していないため、失敗と判定されました。 ログディレクトリに実行結果が記録されています。これを r ディレクトリにコピーします。

$ cat build/mysql-test/var/log/sample_t.reject
SELECT "123";
123
123
123 Expected
$ mv build/mysql-test/var/log/sample_t.reject mysql-test/suite/mita2/r/sample_t.result

今度はテストをパスしました。めでたしめでたし。

$ ./mtr sample_t
Logging: /home/mita2san/mysql-8.0.18/mysql-test/mysql-test-run.pl sample_t
MySQL Version 8.0.18
Checking supported features
 - Binaries are debug compiled
Using 'all' suites
Collecting tests
Checking leftover processes
Removing old var directory
Creating var directory '/home/mita2san/mysql-8.0.18/build/mysql-test/var'
Installing system database
Using parallel: 1

==============================================================================
                  TEST NAME                       RESULT  TIME (ms) COMMENT
------------------------------------------------------------------------------
[100%] mita2.sample_t                            [ pass ]      1
------------------------------------------------------------------------------
The servers were restarted 0 times
The servers were reinitialized 0 times
Spent 0.001 of 17 seconds executing testcases

Completed: All 1 tests were successful.

CentOS7 で MySQL 8.0 をソースからビルドする

yoku0825さんが、CentOS8 で MySQL 8.0 をビルドする記事を書いていました。 CentOS8でもビルドするために、あれこれインストールしないといけないようです。

yoku0825.blogspot.com

CentOS7インストール直後の状態では色々足りない

最近のMySQLはビルドに最新の開発ツール(cmakeやgcc)を要求してきます。 EPEL等の追加のリポジトリから、色々インストールしなければビルドできません。

手順を忘れないようにメモしておきたいと思います。

CentOS7 だと cmake時に、以下のようなエラーに出くわします。

まず、cmake が古いと、

$ cmake -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/tmp/boost ..
<snip>
-- Running cmake version 2.8.12.2
CMake Warning at CMakeLists.txt:43 (MESSAGE):
  Please use cmake3 rather than cmake on this platform

gccもCentOS7のデフォルトのものだと古いと、

$ cmake3 -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/tmp/boost ..
<snip>
CMake Error at cmake/os/Linux.cmake:59 (MESSAGE):
  GCC 5.3 or newer is required (-dumpversion says 4.8.5)
Call Stack (most recent call first):
  CMakeLists.txt:442 (INCLUDE)

openssl-devel, ncurses-devel が必要

$ cmake3 ..
<snip>
-- Looking for SHA512_DIGEST_LENGTH
-- Looking for SHA512_DIGEST_LENGTH - not found
--
Cannot find appropriate system libraries for WITH_SSL=system.
Make sure you have specified a supported SSL version.
Valid options are :
system (use the OS openssl library),
yes (synonym for system),
</path/to/custom/openssl/installation>

CMake Error at cmake/ssl.cmake:65 (MESSAGE):
  Please install the appropriate openssl developer package.

Call Stack (most recent call first):
  cmake/ssl.cmake:293 (FATAL_SSL_NOT_FOUND_ERROR)
  CMakeLists.txt:1181 (MYSQL_CHECK_SSL)
-- Could NOT find Curses (missing: CURSES_LIBRARY CURSES_INCLUDE_PATH)
CMake Error at cmake/readline.cmake:71 (MESSAGE):
  Curses library not found.  Please install appropriate package,

      remove CMakeCache.txt and rerun cmake.On Debian/Ubuntu, package name is libncurses5-dev, on Redhat and derivates it is ncurses-devel.
Call Stack (most recent call first):
  cmake/readline.cmake:100 (FIND_CURSES)
  cmake/readline.cmake:194 (MYSQL_USE_BUNDLED_EDITLINE)
  CMakeLists.txt:1185 (MYSQL_CHECK_EDITLINE)

開発ツールをインストールする

openssl と ncurses は特に追加のリポジトリを必要としません。CentOS7標準のRPMパッケージをインストールするだけです。

$ sudo yum install openssl-devel ncurses-devel 

cmakeの バージョン3以上 は CentOS7 のデフォルトのリポジトリにはないため、epel を利用します。

$ sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
$ sudo yum install --enablerepo='epel' cmake3 

同じくgcc 5.3以上もデフォルトのリポジトリに存在しないため、Software Collection から新しめのバージョンのものをインストールします。

$ sudo yum install -y http://mirror.centos.org/centos/7/extras/x86_64/Packages/centos-release-scl-2-3.el7.centos.noarch.rpm 
http://mirror.centos.org/centos/7/extras/x86_64/Packages/centos-release-scl-rh-2-3.el7.centos.noarch.rpm

$ sudo yum install devtoolset-7-gcc*
<snip>
Dependencies Resolved

================================================================================================================================================================================
 Package                                                 Arch                            Version                                  Repository                               Size
================================================================================================================================================================================
Installing:
 devtoolset-7-gcc                                        x86_64                          7.3.1-5.16.el7                           centos-sclo-rh                           29 M
 devtoolset-7-gcc-c++                                    x86_64                          7.3.1-5.16.el7                           centos-sclo-rh                           11 M
 devtoolset-7-gcc-gdb-plugin                             x86_64                          7.3.1-5.16.el7                           centos-sclo-rh                          124 k
 devtoolset-7-gcc-gfortran                               x86_64                          7.3.1-5.16.el7                           centos-sclo-rh                           11 M
 devtoolset-7-gcc-plugin-devel                           x86_64                          7.3.1-5.16.el7                           centos-sclo-rh                          1.3 M
Installing for dependencies:
 devtoolset-7-binutils                                   x86_64                          2.28-11.el7                              centos-sclo-rh                          5.3 M
 devtoolset-7-libquadmath-devel                          x86_64                          7.3.1-5.16.el7                           centos-sclo-rh                          154 k
 devtoolset-7-libstdc++-devel                            x86_64                          7.3.1-5.16.el7                           centos-sclo-rh                          2.5 M
 devtoolset-7-runtime                                    x86_64                          7.1-4.el7                                centos-sclo-rh                           20 k
 gmp-devel                                               x86_64                          1:6.0.0-15.el7                           centos-base                             181 k
 libgfortran4                                            x86_64                          8.3.1-2.1.1.el7                          centos-base                             686 k
 libmpc-devel                                            x86_64                          1.0.1-3.el7                              centos-base                              32 k
 libquadmath                                             x86_64                          4.8.5-39.el7                             centos-base                             190 k
 mpfr-devel                                              x86_64                          3.1.1-4.el7                              centos-base                              68 k
 scl-utils                                               x86_64                          20130529-19.el7                          centos-base                              24 k

Transaction Summary
================================================================================================================================================================================
Install  5 Packages (+10 Dependent packages)

/opt/rh/devtoolset-7 以下にインストールされました。

$ /opt/rh/devtoolset-7/root/bin/gcc -v
Using built-in specs.
COLLECT_GCC=/opt/rh/devtoolset-7/root/bin/gcc
COLLECT_LTO_WRAPPER=/opt/rh/devtoolset-7/root/usr/libexec/gcc/x86_64-redhat-linux/7/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/opt/rh/devtoolset-7/root/usr --mandir=/opt/rh/devtoolset-7/root/usr/share/man --infodir=/opt/rh/devtoolset-7/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-plugin --with-linker-hash-style=gnu --enable-initfini-array --with-default-libstdcxx-abi=gcc4-compatible --with-isl=/builddir/build/BUILD/gcc-7.3.1-20180303/obj-x86_64-redhat-linux/isl-install --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

パスを通すために scl enable を実行します。

$ scl enable devtoolset-7 bash
$ which gcc
/opt/rh/devtoolset-7/root/usr/bin/gcc

ビルドする

-j オプションを付けると並列でコンパイルできて、高速です。しかし、並列だとビルドが途中でコケてしまうんですよね(たぶん、メモリ不足が原因)。 自分は以下のように、コケたら、並列度を落として再実行するようにしてます。

$ cd <mysqlのソースディレクトリ>

$ mkdir build; cd build
$ cmake3 -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/tmp/boost ..

$ make -j 8 || make -j 6 || make -j 4 || make

MySQL ヒストリーに記録されない条件

TL; DR;

  • *IDENTIFIED*:*PASSWORD* にマッチしたクエリ、つまりパスワードを含むクエリはヒストリーに記録されない
  • 環境変数 MYSQL_HISTIGNORE で記録しないクエリを追加指定できる

ヒストリーに記録されない時がある

mysql コマンド には履歴機能があります。 Linuxのシェルのように、過去に実行したSQLを上の矢印キーで遡ったり、Ctrl-R でキーワード検索することが出来ます。

時々、ヒストリーに記録されないSQLがあることが気になっていました。 今回はその条件を調べてみました。

ソースから辿ってみる

client/mysql.cc を読んでいきます。 add_filtered_history() という関数でヒストリーファイルに追記しています。 ignore_matcher がヒストリーに残さないものを判定していそうです。

2929 /* Add the given line to mysql history and syslog. */
2930 static void add_filtered_history(const char *string) {
2931   // line shouldn't be on history ignore list
2932   if (ignore_matcher.is_matching(string, charset_info)) return;
2933
2934 #ifdef HAVE_READLINE
2935   if (!quick && not_in_history(string)) add_history(string);
2936 #endif
2937
2938   if (opt_syslog) add_syslog(string);
2939 }

ignore_matcher は 以下の箇所で定義されており、HI_DEFAULTS がデフォルト値です。 そして、 MYSQL_HISTIGNORE という環境変数でヒストリーに記録しないパターンを追加できることもわかりました。

1349   if (!status.batch) {
1350     // history ignore patterns are initialized to default values
1351     ignore_matcher.add_patterns(HI_DEFAULTS);
1352
1353     /*
1354       Additional patterns may be supplied using either --histignore option or
1355       MYSQL_HISTIGNORE environment variable. If supplied, they'll get appended
1356       to the default patterns. In case both are specified, pattern(s) supplied
1357       using --histignore option will be used.
1358     */
1359     if (opt_histignore)
1360       ignore_matcher.add_patterns(opt_histignore);
1361     else if (getenv("MYSQL_HISTIGNORE"))
1362       ignore_matcher.add_patterns(getenv("MYSQL_HISTIGNORE"));

HI_DEFAULTS は以下のように定義されていました。 パスワードが入っているSQLをヒストリーに記録してしまうと、ヒストリーファイルからパスワードが読み取られてしまうリスクがあるため、記録しないということでしょう。

132 /** default set of patterns used for history exclusion filter */
133 const static std::string HI_DEFAULTS("*IDENTIFIED*:*PASSWORD*");

MYSQL_HISTIGNORE を試してみる

環境変数 MYSQL_HISTIGNORE で無視する条件を追加してみます。 HIMITSU を含んだSQLをヒストリーに記録しないようにします。

$ export MYSQL_HISTIGNORE='*HIMITSU*'

$ mysql
mysql> SELECT 'HOGEHOGE';
+----------+
| HOGEHOGE |
+----------+
| HOGEHOGE |
+----------+
1 row in set (0.00 sec)

mysql> SELECT 'HIMITSU NO HOGEHOGE';
+---------------------+
| HIMITSU NO HOGEHOGE |
+---------------------+
| HIMITSU NO HOGEHOGE |
+---------------------+
1 row in set (0.00 sec)

ちゃんと、HIMITSU NO HOGEHOGE が無視されて、HOGEHOGEだけがヒストリーファイルに記録されてました。

$ cat ~/.mysql_history
_HiStOrY_V2_
SELECT\040'HOGEHOGE';
exit

マニュアルにも書いてある

ソースから辿りましたが、マニュアルにもちゃんと記載がありました。

mysql ignores for logging purposes statements that match any pattern in the “ignore” list. By default, the pattern list is "IDENTIFIED:PASSWORD", to ignore statements that refer to passwords. Pattern matching is not case-sensitive. Within patterns, two characters are special: https://dev.mysql.com/doc/refman/8.0/en/mysql-logging.html