それが僕には楽しかったんです。

僕と MySQL と時々 MariaDB

MySQL 8.0.31 以降でビルドが通らないケースを git bisect で調べる

はじめに

mtr のオプションを拡張するパッチを出そうと最新の MySQL をビルドしようとしたら、またしてもすんなりと行かないケースが見つかったので何が原因でだめだったかをまとめる。今回は特に原因がわかりにくい部類だったのでその調べ方もついでに載せる。

問題のビルド

現在最新のバージョンである 8.0.33 をビルドすることを考える。いつもは配布されているソースコードを boost 共々ダウンロードしてきているが、今回は git clone で引っ張ってきたものを利用する。
ビルド自体は以下の通りで、基本的にいつもと同じ。

$ cd build && cd $_
$ mkdir boost
$ cmake ../ -DCMAKE_BUILD_TYPE=Debug -DWITH_BOOST=./boost -DDOWNLOAD_BOOST=1 -DWITH_DEBUG=1
$ cmake --build . --config debug -j7

すると、この状態ではわかりにくいが別途 make VERBOSE=1 で実行するとどうやらエラーを返していることがわかった。

[ 55%] Linking CXX executable ../runtime_output_directory/build_id_test
cd /home/lrf141/mysqlProject/mysql-server/build/mysys && /usr/bin/cmake -E cmake_link_script CMakeFiles/build_id_test.dir/link.txt --verbose=1
/usr/bin/c++ -std=c++17 -fno-omit-frame-pointer -ftls-model=initial-exec  -Wall -Wextra -Wformat-security -Wvla -Wundef -Wmissing-format-attribute -Woverloaded-virtual -Wcast-qual -Wimplicit-fallthrough=5 -Wstringop-truncation -Wsuggest-override -Wmissing-include-dirs -Wextra-semi -Wlogical-op -Werror -DSAFE_MUTEX -DENABLED_DEBUG_SYNC -g  -Wl,--build-id=sha1 CMakeFiles/build_id_test.dir/build_id_test.cc.o CMakeFiles/build_id_test.dir/build_id.cc.o -o ../runtime_output_directory/build_id_test 
Verifying build-id
cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && ./build_id_test | egrep -o [0-9a-f]{40} > build_id_test_1
cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && /usr/bin/readelf -n ./build_id_test | grep "Build ID:" | egrep -o [0-9a-f]{40} > build_id_test_2
make[2]: *** [mysys/CMakeFiles/build_id_test.dir/build.make:116: runtime_output_directory/build_id_test] エラー 1
make[2]: *** ファイル 'runtime_output_directory/build_id_test' を削除します
make[2]: ディレクトリ '/home/lrf141/mysqlProject/mysql-server/build' から出ます
make[1]: *** [CMakeFiles/Makefile2:7136: mysys/CMakeFiles/build_id_test.dir/all] エラー 2
make[1]: ディレクトリ '/home/lrf141/mysqlProject/mysql-server/build' から出ます

おそらく、最後の build_id_test 周りが悪さをしてビルドに失敗することまではわかるがそれ以上に情報がない状態である。

git bisect の準備

git bisect を使うと問題のある最初のコミットを二分探索で探すことができる。ただ、最後にビルドしたのは 8.0.30 で、そこから 3 バージョン分を対象にするとやや時間がかかりそうなのでどのバージョンまで素でビルドできるかを念の為確かめた。すると、8.0.31 から失敗することがわかったので問題の修正が入ったコミットは 8.0.30 の最新コミットから 8.0.31 の最新コミットのどこかにあることがわかる。
MySQLリポジトリでは 8.0 ブランチに各バージョンのタグが打たれ管理されている雰囲気を感じるので git bisect で探索するべき範囲は以下のようになる。

$ git bisect start mysql-8.0.31 mysql-8.0.30

テストスクリプト

git bisect で探すべき大体の範囲がわかったのでテストスクリプトを用意する。ビルドが通るかどうかという観点でコミットを探索したいので以下のようなスクリプトとなる。

#!/bin/bash

mkdir build && cd $_
mkdir boost

cmake ../ -DCMAKE_BUILD_TYPE=Debug -DWITH_BOOST=./boost -DDOWNLOAD_BOOST=1 -DWITH_DEBUG=1
cmake --build . --config debug -j7
BUILD_STATUS=$?
cd `git rev-parse --show-toplevel`
rm -rf build

exit $BUILD_STATUS

CMakeCache などが残っていると厄介なのでビルドしたときのステータスコードを保存しておき、build ディレクトリを最後に消している。ここでわざわざ exit を実行しているのは git bisect においてテストスクリプトの終了ステータスが 0 かどうかが重要らしいので rm -rf ./build の終了ステータスを参照しないようにするためである。
ここまで用意できれば元気に git bisect を走らせる。

$ git bisect run ../testMySQLBuild.sh

ビルドが通らなくなったであろうコミット

しばらくするとビルドがいつも通りの方法で通らなくなったコミットが見つかる。

321f106987a3fd62035057c8b544e7a874bc76d3 is the first bad commit
commit 321f106987a3fd62035057c8b544e7a874bc76d3
Author: Tor Didriksen <tor.didriksen@oracle.com>
Date:   Mon Apr 4 12:22:09 2022 +0200

    WL#15161 Add read-only build-id as system variable to mysqld
    
    For Linux builds: add --build-id=sha1 when linking executables.
    
    From 'man ld':
    
       --build-id=style
    
       Request the creation of a ".note.gnu.build-id" ELF note section or
       a ".buildid" COFF section.  The contents of the note are unique
       bits identifying this linked file.  style can be "uuid" to use 128
       random bits, "sha1" to use a 160-bit SHA1 hash on the normative
       parts of the output contents, ......
    
       The "md5" and "sha1" styles produces an identifier that is always
       the same in an identical output file, but will be unique among all
       nonidentical output files.  It is not intended to be compared as a
       checksum for the file's contents.  A linked file may be changed
       later by other tools, but the build ID bit string identifying the
       original linked file does not change.
    
    There is a new cmake option WITH_BUILD_ID to disable build-id on Linux.
    
    Any mysql executable linking with mysys/build_id.cc (in particular
    mysqld) can read its own BuildID.  The implementation is based on
    dl_iterate_phdr(3) which has been supported in glibc since version
    2.2.4.  The server will read the BuildID at startup, convert it to a
    hexidecimal string, and store it in a global character array.
    
    mysqld will print BuildID for 'mysqld --help'.
    We use this to extend the contents of the file INFO_BIN.
    
    mysqld will also log BuildID in the server log at startup.
    
    The signal handler, used for handling segfaults and the like, will
    also print the BuildID.
    
    There is a new @@GLOBAL.build_id read-only system variable,
    with a basic mtr test, verifying that it is read-only.
    
    Change-Id: I89ce4c63fed311422b7fbea1b12d29ebf7884a41

 CMakeLists.txt                                    |  24 +++--
 cmake/info_macros.cmake.in                        |  23 ++++
 cmake/mysql_add_executable.cmake                  |   4 +
 config.h.cmake                                    |   1 +
 mysql-test/include/have_build_id.inc              |   6 ++
 mysql-test/suite/sys_vars/r/build_id_basic.result |  34 ++++++
 mysql-test/suite/sys_vars/t/build_id_basic.test   |  34 ++++++
 mysys/CMakeLists.txt                              |  23 ++++
 mysys/build_id.cc                                 | 121 ++++++++++++++++++++++
 mysys/build_id.h                                  |  40 +++++++
 mysys/build_id_test.cc                            |  36 +++++++
 packaging/deb-in/control.in                       |   1 +
 packaging/rpm-docker/mysql.spec.in                |   1 +
 packaging/rpm-fedora/mysql.spec.in                |   1 +
 packaging/rpm-oel/mysql.spec.in                   |   1 +
 packaging/rpm-sles/mysql.spec.in                  |   1 +
 share/messages_to_error_log.txt                   |   3 +
 sql/mysqld.cc                                     |  19 ++++
 sql/mysqld.h                                      |   4 +
 sql/signal_handler.cc                             |   4 +
 sql/sys_vars.cc                                   |   8 ++
 21 files changed, 383 insertions(+), 6 deletions(-)
 create mode 100644 mysql-test/include/have_build_id.inc
 create mode 100644 mysql-test/suite/sys_vars/r/build_id_basic.result
 create mode 100644 mysql-test/suite/sys_vars/t/build_id_basic.test
 create mode 100644 mysys/build_id.cc
 create mode 100644 mysys/build_id.h
 create mode 100644 mysys/build_id_test.cc

どうやらこのコミットらしい。
github.com

対策(暫定)

cmake 実行時に -DWITH_BUILD_ID=0 をつけてやればとりあえず回避はできる。

おわりに

debuginfo とか gdbデバッグ情報をどうやって持ってくるか、みたいなところに関連してそうな雰囲気はあるがこの辺は詳しくないので、今回はとりあえず回避することだけ。
なんか build-id を取得して、それの verify でコケていそうな感じはするが単純に通すだけなら cmake のオプションでどうにかなったので良しとする。

追記 (2023/05/29 23:03)

このツイートによってすべてを理解してしまった…

何が起きたか

まず最初に LANG=C にした状態で -DWITH_BUILD_ID=1 、つまりデフォルトの状態でもう一度ビルドしてみる。
その後 make VERBOSE=1 でコケていた部分の特に readelf 関連に的を絞って実行すると、出力結果は次の通りになる。

$ cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && ./build_id_test
BuildID[sha1]=55fc665151e52fbbddb526fe78e4f23d5d7c6e3c

$ cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && /usr/bin/readelf -n ./build_id_test 
Displaying notes found in: .note.gnu.property
  Owner                Data size 	Description
  GNU                  0x00000020	NT_GNU_PROPERTY_TYPE_0
      Properties: x86 feature: IBT, SHSTK
	x86 ISA needed: x86-64-baseline

Displaying notes found in: .note.gnu.build-id
  Owner                Data size 	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 55fc665151e52fbbddb526fe78e4f23d5d7c6e3c

Displaying notes found in: .note.ABI-tag
  Owner                Data size 	Description
  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 3.2.0

それでは、ここで LANG=ja_JP.UTF-8 で同じことをやってみると結果はこうなる。

$ cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && ./build_id_test
BuildID[sha1]=55fc665151e52fbbddb526fe78e4f23d5d7c6e3c

$ cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && /usr/bin/readelf -n ./build_id_test
Displaying notes found in: .note.gnu.property
  所有者            データサイズ	Description
  GNU                  0x00000020	NT_GNU_PROPERTY_TYPE_0
      Properties: x86 feature: IBT, SHSTK
	x86 ISA needed: x86-64-baseline

Displaying notes found in: .note.gnu.build-id
  所有者            データサイズ	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (一意なビルドID ビット列)
    ビルドID: 55fc665151e52fbbddb526fe78e4f23d5d7c6e3c

Displaying notes found in: .note.ABI-tag
  所有者            データサイズ	Description
  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI バージョンタグ)
    OS: Linux, ABI: 3.2.0

そして make VERBOSE=1 でわかったコケているコマンドをもう一度見てみる。

cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && ./build_id_test | egrep -o [0-9a-f]{40} > build_id_test_1
cd /home/lrf141/mysqlProject/mysql-server/build/runtime_output_directory && /usr/bin/readelf -n ./build_id_test | grep "Build ID:" | egrep -o [0-9a-f]{40} > build_id_test_2

お分かり頂けただろうか。LANG=C でないと二行目で grep するべき "Build ID:" などというものは見つからないのである。
git bisect で見つけたコミットをよく見てみると build_id_test_1 と build_id_test_2 の diff がこれらのコマンドのあとに待っていて、それがコケたとわかる。

最終的な対策

cmake 実行時に -DWITH_BUILD_ID=0 を付与するか export LANG=C でビルドが通るようになる。

反省文

ちょくちょく開発用マシンの OS をクリーンインストールするのだが、特段こだわりがあるわけではないので面倒になって LANG=C を明示的に設定しないまま放置していたらそれが仇となった。

MySQL Connector/C++ を使ってセッションを気合で作る

はじめに

最近、公私共に MySQL Server と戯れることが多いが何らかの手順をクソ長いシェルスクリプトにして実行することに嫌気が指したので CLI ツールの自作を始めた。
MySQL そのものの実装を読むときも多いので、そろそろ慣れようと思って C++ で書いたが運の尽き。MySQL Connector/C++ が曲者中の曲者で、セッションを作るところすら辿り着くまで時間がかかったのでメモがてらまとめる。

環境

  • Ubuntu 22.04 (実際には PopOS)
  • g++ 11.3.0-1ubuntu1~22.04
  • cmake 3.22.1
  • MySQL 8.0.30 (source code build)

8.0.16 あたりで conncpp も少し変わったみたいだが面倒なので一切考慮しない。

ここでやりたいこと

MySQL :: MySQL Connector/C++ 8.0 Developer Guide :: 5.1 Building Connector/C++ Applications: General Considerations
公式ドキュメントによると

#include <mysqlx/xdevapi.h>

を引っ張ることができれば大体のことを実装できそうな雰囲気があるので、そこにあるものを使ってセッションを作る。

Connector/C++ をインストールする

この時点ですでに曲者である。
MySQL :: Download MySQL Connector/C++ (Archived Versions)
公式で配布されている deb パッケージから使いたいバージョンや OS などを指定して deb パッケージを探しインストールするが
自分の場合 libmysqlcppconn8-2_8.0.30-1ubuntu22.04_amd64.deb をインストールする時に mysql-community-client-plugins が入っていないと怒られる。

そんなものどこにも書いてない気がするが、これが入らないことには始まらないので
MySQL :: Download MySQL Community Server
公式から mysql-community-client-plugins_8.0.30-1ubuntu22.04_amd64.deb を探し出してインストールした。その後 libmysqlcppconn* を全部インストールする。

諸々のパスを確認する

libmysqlcppconn8* の場所を確認する。後でリンクするので必要になる。

$ dpkg -L libmysqlcppconn8-2
/.
/usr
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/libmysqlcppconn8.so.2.8.0.30
/usr/share
/usr/share/doc
/usr/share/doc/libmysqlcppconn8-2
/usr/share/doc/libmysqlcppconn8-2/INFO_BIN
/usr/share/doc/libmysqlcppconn8-2/INFO_SRC
/usr/share/doc/libmysqlcppconn8-2/LICENSE.txt.gz
/usr/share/doc/libmysqlcppconn8-2/README.txt
/usr/share/doc/libmysqlcppconn8-2/changelog.Debian.gz
/usr/share/doc/libmysqlcppconn8-2/copyright
/usr/share/lintian
/usr/share/lintian/overrides
/usr/share/lintian/overrides/libmysqlcppconn8-2
/usr/lib/x86_64-linux-gnu/libmysqlcppconn8.so.2

/usr/lib/x86_64-linux-gnu 以下に入っているらしい。

次に libmysqlcppconn-dev でインストールした諸々のパスを確認する。include dir を指定するのに後で使う。

$  dpkg -L libmysqlcppconn-dev
/usr/include/mysql-cppconn-8
/usr/include/mysql-cppconn-8/jdbc
/usr/include/mysql-cppconn-8/jdbc/cppconn
/usr/include/mysql-cppconn-8/jdbc/cppconn/build_config.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/callback.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/config.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/connection.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/datatype.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/driver.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/exception.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/metadata.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/parameter_metadata.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/prepared_statement.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/resultset.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/resultset_metadata.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/sqlstring.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/statement.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/variant.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/version_info.h
/usr/include/mysql-cppconn-8/jdbc/cppconn/warning.h
/usr/include/mysql-cppconn-8/jdbc/mysql_connection.h
/usr/include/mysql-cppconn-8/jdbc/mysql_driver.h
/usr/include/mysql-cppconn-8/jdbc/mysql_error.h
/usr/include/mysql-cppconn-8/mysql
/usr/include/mysql-cppconn-8/mysql/jdbc.h
/usr/include/mysql-cppconn-8/mysqlx
/usr/include/mysql-cppconn-8/mysqlx/common
/usr/include/mysql-cppconn-8/mysqlx/common/api.h
/usr/include/mysql-cppconn-8/mysqlx/common/error.h
/usr/include/mysql-cppconn-8/mysqlx/common/op_if.h
/usr/include/mysql-cppconn-8/mysqlx/common/settings.h
/usr/include/mysql-cppconn-8/mysqlx/common/util.h
/usr/include/mysql-cppconn-8/mysqlx/common/value.h
/usr/include/mysql-cppconn-8/mysqlx/common.h
/usr/include/mysql-cppconn-8/mysqlx/common_constants.h
/usr/include/mysql-cppconn-8/mysqlx/devapi
/usr/include/mysql-cppconn-8/mysqlx/devapi/collations.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/collection_crud.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/common.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/crud.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/detail
/usr/include/mysql-cppconn-8/mysqlx/devapi/detail/crud.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/detail/error.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/detail/result.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/detail/row.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/detail/session.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/detail/settings.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/document.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/error.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/executable.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/mysql_charsets.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/mysql_collations.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/result.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/row.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/settings.h
/usr/include/mysql-cppconn-8/mysqlx/devapi/table_crud.h
/usr/include/mysql-cppconn-8/mysqlx/version_info.h
/usr/include/mysql-cppconn-8/mysqlx/xapi.h
/usr/include/mysql-cppconn-8/mysqlx/xdevapi.h

どうやら /usr/include/mysql-cppconn-8 以下に入っているらしい。

簡単なサンプル

最も簡単なパターンでセッションの確立を試みる。

#include <mysqlx/xdevapi.h>

int main() {
        mysqlx::SessionSettings session_settings = mysqlx::SessionSettings("localhost", 33060, "root", "root", "test");
        mysqlx::Session session(session_settings);
        return 0;
}


コンパイルして実行する。

$ g++ -std=c++11 -I/usr/include/mysql-cppconn-8/ -L/usr/lib/x86_64-linux-gnu main.cpp -lmysqlcppconn8 -o main
$ ./main
$ 

何もエラーがでなければ成功である。

CDK Error: unexpected message

これが曲者ナンバーワンである。
コンパイルして、実行するとこのメッセージが出力される場合がある。

$ ./main 
terminate called after throwing an instance of 'mysqlx::abi2::r0::Error'
  what():  CDK Error: unexpected message

これだけでは何が原因か全くわからないが、mysqlx plugin がないか動いていない場合または x protocol を利用しているのでポートが 3306 ではなく 33060 にしていない場合に起こることがわかった。
これから推測するにおそらく認証等々以前に接続に必要な前提が欠けている場合に起こるっぽい。

参考:
使用C++连接MySql8.0提示错误:CDK Error: unexpected message_cdk error unexpected message_icky_1024的博客-CSDN博客
MySQL :: CDK Error: unexpected message
c++ - X DevAPI mysqlx::Session() over linux socket fails with “CDK Error: unexpected message” - Stack Overflow

コンパイルできない

曲者ナンバーツーである。
CMake とかでイケイケビルドをしようとすると、以下のようなエラーに遭遇する場合がある。

$ make
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o: warning: relocation against `_ZTVN6mysqlx4abi22r05DbDocE' in read-only section `.text.startup'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o: in function `mysqlx::abi2::r0::Value::~Value()':
main.cpp:(.text._ZN6mysqlx4abi22r05ValueD1Ev[_ZN6mysqlx4abi22r05ValueD1Ev]+0x49): undefined reference to `vtable for mysqlx::abi2::r0::DbDoc'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o: in function `mysqlx::abi2::r0::internal::Warning_detail::print(std::ostream&) const':
main.cpp:(.text._ZNK6mysqlx4abi22r08internal14Warning_detail5printERSo[_ZNK6mysqlx4abi22r08internal14Warning_detail5printERSo]+0x75): undefined reference to `mysqlx::abi2::r0::string::Impl::to_utf8[abi:cxx11](mysqlx::abi2::r0::string const&)'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o: in function `virtual thunk to mysqlx::abi2::r0::internal::Warning_detail::print(std::ostream&) const':
main.cpp:(.text._ZNK6mysqlx4abi22r08internal14Warning_detail5printERSo[_ZNK6mysqlx4abi22r08internal14Warning_detail5printERSo]+0x38a): undefined reference to `mysqlx::abi2::r0::string::Impl::to_utf8[abi:cxx11](mysqlx::abi2::r0::string const&)'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o: in function `mysqlx::abi2::r0::Value::print(std::ostream&) const':
main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0xd8): undefined reference to `mysqlx::abi2::r0::DbDoc::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x17f): undefined reference to `mysqlx::abi2::r0::common::Value::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x217): undefined reference to `mysqlx::abi2::r0::DbDoc::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x2b7): undefined reference to `mysqlx::abi2::r0::common::Value::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x34d): undefined reference to `mysqlx::abi2::r0::DbDoc::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x3df): undefined reference to `mysqlx::abi2::r0::common::Value::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x463): undefined reference to `mysqlx::abi2::r0::DbDoc::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x4e7): undefined reference to `mysqlx::abi2::r0::common::Value::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x44): undefined reference to `mysqlx::abi2::r0::DbDoc::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x6a7): undefined reference to `mysqlx::abi2::r0::common::Value::print(std::ostream&) const'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o: in function `virtual thunk to mysqlx::abi2::r0::Value::print(std::ostream&) const':
main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x6e7): undefined reference to `mysqlx::abi2::r0::DbDoc::print(std::ostream&) const'
/usr/bin/ld: main.cpp:(.text._ZNK6mysqlx4abi22r05Value5printERSo[_ZNK6mysqlx4abi22r05Value5printERSo]+0x7d4): undefined reference to `mysqlx::abi2::r0::common::Value::print(std::ostream&) const'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o: in function `_GLOBAL__sub_I_main':
main.cpp:(.text.startup+0x91): undefined reference to `vtable for mysqlx::abi2::r0::DbDoc'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o:(.data.rel.ro._ZTIN6mysqlx4abi22r05ValueE[_ZTIN6mysqlx4abi22r05ValueE]+0x28): undefined reference to `typeinfo for mysqlx::abi2::r0::common::Value'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o:(.data.rel.ro._ZTCN6mysqlx4abi22r05ValueE0_NS1_6common5ValueE[_ZTVN6mysqlx4abi22r05ValueE]+0x18): undefined reference to `typeinfo for mysqlx::abi2::r0::common::Value'
/usr/bin/ld: CMakeFiles/kt-innodb-buffer-page-list.dir/main.cpp.o:(.data.rel.ro._ZTCN6mysqlx4abi22r05ValueE0_NS1_6common5ValueE[_ZTVN6mysqlx4abi22r05ValueE]+0x20): undefined reference to `mysqlx::abi2::r0::common::Value::print(std::ostream&) const'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make[2]: *** [innodb-buffer-page-list/CMakeFiles/kt-innodb-buffer-page-list.dir/build.make:99: innodb-buffer-page-list/kt-innodb-buffer-page-list] エラー 1
make[1]: *** [CMakeFiles/Makefile2:142: innodb-buffer-page-list/CMakeFiles/kt-innodb-buffer-page-list.dir/all] エラー 2
make: *** [Makefile:91: all] エラー 2

何を言っているのか全くわからない。色々と原因はありそうだが、基本的にリンクをミスってる。
この場合は黙って g++ を使って最小構成の実装をビルドしてみると良い。大体、何が原因かわかる。

参考:
Undefined reference using the mysql c++ connector - Stack Overflow

deb でなく tar.gz から入れた場合に mysqlcppconn8 が見つからないと怒られる

コンパイルする時に怒られる場合がある。LD_LIBRARY_PATH を設定すれば行けた。cmake なら find_library で行ける。

おまけ

cmake でいい感じにする。

流石に Makefile を自分で書くのは面倒だったので cmake に頼りたいが find_package でどうにかなる雰囲気がなかったので多少どうにかする必要があった。そして何故か CMake と MySQL Connector/C++ を併用している人がいないので参考になる実装も見当たらないという中々な状況。
未来の自分のために残しておく。これでさっきのサンプルがビルドできる。

cmake_minimum_required(VERSION 3.8)

project("sample cmake build" VERSION 0.0.1)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_COMPILER "/usr/bin/g++" CACHE STRING "gnu c++ compiler" FORCE)
set(CMAKE_C_COMPILER "/usr/bin/gcc" CACHE STRING "gnu cc compiler" FORCE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3 -std=c++11")

set(MYSQL_CONNECTOR_CPP_LIBS_DIR "/usr/lib/x86_64-linux-gnu")
set(MYSQL_CONNECTOR_INCLUDE_DIR "/usr/include/mysql-cppconn-8")
find_library(
        MYSQL_CONN_CPP_LIBS
        NAMES mysqlcppconn8 libmysqlconncpp8
        HINTS "${MYSQL_CONNECTOR_CPP_LIBS_DIR}"
        NO_DEFAULT_PATH
)
message("-- mysql connector libs - ${MYSQL_CONN_CPP_LIBS}")

file(GLOB main_src "*.cpp")

add_executable(main ${main_src})

target_include_directories(main PRIVATE ${MYSQL_CONNECTOR_INCLUDE_DIR})
target_link_libraries(main ${MYSQL_CONN_CPP_LIBS})

余談

セッションをどうやら作成できればあとはクエリなどでよろしくできるらしいということは数少ないサンプル実装からわかっているが、それに相当するドキュメントが見当たらないので実質 MySQL Connector/C++ の実装がドキュメントである。
mysqlx::Session から実装を読み解いていくと何となくわかるので、コメントと共に困ったときは読むべきかもしれない。

おわりに

自分の C++ 力、CMake 力、リンクなどの事柄に対する理解不足という甘さが招いたことも多かったという状態に加えて
あまり参考になるドキュメントがないということも相まってだいぶきつかった。これだけで先週の金曜から一週間も溶かしてしまった。

割と罠いところでコケている人が見受けられるので、誰かの助けになることを願って。直近では明日あたりの自分がまた見そう。

VLDB 2022 にある論文のアブストラクトを流し読みする回

はじめに

今度から続・データベース論文輪読会に参加するのでそのアップとして VLDB 2022 のアブストラクトをひたすら眺めて、雑にメモっていく。流し読みなので色々間違っている可能性はだいぶある。
対象は以下のサイトに掲載されているもので、タイトルから気になったやつ。

vldb.org

Design Trade-offs for a Robust Dynamic Hybrid Hash Join

ハイブリッドハッシュジョイン(以下、HHJ)の性能評価に関する話。
HHJ を用いる場合にパーティション数が性能にどう影響するのか、最小の CPU コストで最大のメモリ使用率を得るためのパーティション追加やパーティションの動的選択について既存の研究にあるアルゴリズムと比較している。
ストレージは HDD, SSD, EBS を使っていて、 AsterixDB のコンテキストとしてアルゴリズムは実装されている。
面白そう。

Plush: A Write-Optimized Persistent Log-Structured Hash-Table

不揮発性メモリの利点である低い書き込みレイテンシと小さい書き込み読み込みの帯域幅を使って性能の向上を図るらしい。
これはパッとみてよくわからなかった。

Cardinality Estimation in DBMS: A Comprehensive Benchmark Evaluation

カーディナリティ推定において、その目的である実環境でどの程度有効かというものを計測するベンチマークについての話。
PostgreSQL に各種アルゴリズムを組み込み、クエリプランの精度向上にどの程度有効であるかなどを総合的に評価するというのが主題っぽい。
既存の推定指標への問題点とそれを改善した新たな指標も提示しているらしい。
面白そう。

Your Read is Our Priority in Flash Storage

フラッシュストレージにおいて read-after-write (以下、RAW) は同期的な読み込みを書き込みによって遅くさせる場合がありそれがトランザクションスループットとレイテンシを悪化させる問題を提起した上で、それらを解決する新しいストレージインターフェースとバッファープールを提案している。

Cosine: A Cloud-Cost Optimized Self-Designing Key-Value Storage Engine

入力のワークロード、クラウドサービスの予算、目標パフォーマンスと SLA を与えることで完璧に近いストレージエンジンの設計を見つけ出し、実際のコードとして具現化するストレージエンジンについての紹介。
単純にこれが何か気になる。

Lotus: Scalable Multi-Partition Transactions on Single-Threaded Partitioned Databases

H-Store/VoltDB の同時実行制御の再検討。マルチパーティション(?)ワークロードでパフォーマンスが低下する問題の改善に念頭をおいている。
面白そう。

Memory-Optimized Multi-Version Concurrency Control for Disk-Based Database Systems

メモリに最適化した MVCC の提案。バージョニング情報をフラッシュすることなくメモリ上に保持することが可能であること、それによってオーバーヘッドを低下されることができるらしい。
なかなか面白そう。

A Scalable and Generic Approach to Range Joins

eq join ではない range join などでは、入れ子を伴うループが発生する場合があり、これが許容できない処理時間をもたらす場合がある。
それを解決するために kd-tree ベースの多次元 range join の提案と性能評価を行う。
面白そう。

Designing an Open Framework for Query Optimization and Compilation

クエリコンパイルと最適化は開発者の利便性や柔軟性、拡張性を犠牲にしてパフォーマンスの向上をもたらす事への問題提起とそれを解決するために MLIR をベースにしたクエリコンパイルスタックの提案。
それらを LingoDB に組み込み、性能評価を行う。
面白そう。

おわりに

10 本読むはずだったけど思いの外面白そうなやつが見つかってきたのでここまで。
ここから気になるやつを一本選んで読んでみる。

MySQL 8.0.29 以下をビルドできない場合がある

はじめに

ちょいと調子が戻ってきたので MySQL で検証したいことを検証しようとビルドを元気に進めていたら、仕事用の macOS マシンでビルドできないならまだしも Ubuntu 系統の OS でビルドできない場合があるというまさかの事態にぶち当たったのでメモがてら残す。

ビルド

で、ビルドを試みた。

$ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-boost-8.0.28.tar.gz
$ tar xzvf mysql-boost-8.0.28.tar.gz
$ cd mysql-boost-8.0.28
$ mkdir build && cd $_
$ cmake ../ -DCMAKE_BUILD_TYPE=Debug -DWITH_BOOST=../boost

問題の躓きポイント

するとこれである

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:61 (MESSAGE):
  Please install the appropriate openssl developer package.

libssl-dev も入っていて実態もある中でこれが出るともうだめだ、ということでインターネットの海をさまよっていると同じ状態に陥っている以下の記事を見つけた

日々の覚書: ConoHaの上でひたすらMySQLをビルドする 2021

どうやら OpenSSL (か、それ相当) のバージョンをうまく検出できてないっぽい。
実際エラーメッセージの上に出ているログを見ていると

-- OPENSSL_MAJOR_VERSION = 
-- OPENSSL_MINOR_VERSION = 
-- OPENSSL_FIX_VERSION = 

となっているので、本当にバージョンが見つけられていない様子。

OpenSSL 系を探っている cmake は cmake/ssl.cmake にあるのでひとまずそれを見る。
mysql-8.0.28 のタグでその cmake ファイルを探して見てみるとこのあたりが怪しそうな雰囲気がある。

https://github.com/mysql/mysql-server/blob/mysql-8.0.28/cmake/ssl.cmake#L216-L227

OPENSSL_VERSION_NUMBER なるものから、それぞれ Major, Minor Fix バージョンを抜き出している。

と、ここまでわかったので /usr/include/openssl/opensslv.h を見てみると、yoku さんのブログ記事にあるようにバージョンの実装が変更されたことがわかる。

/* Synthesize OPENSSL_VERSION_NUMBER with the layout 0xMNN00PPSL */
# ifdef OPENSSL_VERSION_PRE_RELEASE
#  define _OPENSSL_VERSION_PRE_RELEASE 0x0L
# else
#  define _OPENSSL_VERSION_PRE_RELEASE 0xfL
# endif
# define OPENSSL_VERSION_NUMBER          \
    ( (OPENSSL_VERSION_MAJOR<<28)        \
      |(OPENSSL_VERSION_MINOR<<20)       \
      |(OPENSSL_VERSION_PATCH<<4)        \
      |_OPENSSL_VERSION_PRE_RELEASE )

どこからならビルドできるのか

8.0.29 もだめそう。
https://github.com/mysql/mysql-server/blob/mysql-8.0.29/cmake/ssl.cmake#L228-L239

8.0.30 はいけそう。
https://github.com/mysql/mysql-server/blob/mysql-8.0.30/cmake/ssl.cmake#L113-L135

確かに手元の opensslv.h を見ているとそれぞれ OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH が存在するのでそれを見るように修正が入っていた。
ので、 8.0.30 をビルドしてみると成功したのでどうやらそういうことらしい。

コメントをみるに、OpenSSL version3 未満 (ただし、1.0.0 以上でないと最低要件は満たさない)では 8.0.28 もビルドできる様子はあるが 3 以上は上述の理由でビルドできないっぽい。

まとめ

8.0.29 以下のビルドでは OpenSSL のバージョンが 3 以上だとコケる

MariaDB の Application-Time Periods について

はじめに

気がついたら 3 ヶ月もブログを更新していなかったので、色々あって見ていた MariaDB の Application-Time Periods について雑に書く。ほとんど日本語訳に近いがまぁそれもいいでしょう。
環境は以下のもので debug build したやつを make install で手元に突っ込んでやっている。

  • 10.8.3-MariaDB-debug Source distribution commit: cbf9d8a

Application-Time Periods

まずそれが一体何なのかについて。公式ドキュメントは下記の通り。
mariadb.com

Application-Time Periods Table は 10.4.3 からサポートされている機能で、Time Periods と呼ばれる2つの temporal column によって範囲が決定される。
このテーブルにおいて Time Periods に相当するカラムは Date and Time Data Types*1 にあるデータ型のうち TIME と YEAR を除いたものであり、データ型が同じでないといけない。

ここまでざっと書いたが、何らかの時間的な範囲を何かするテーブルということしかわからないので実例について。まずは公式ドキュメントにあるやつをそのまま見ていく。
Application Time Periods は次の様に宣言することができる。*2

CREATE TABLE t1(
   name VARCHAR(50), 
   date_1 DATE,
   date_2 DATE,
   PERIOD FOR date_period(date_1, date_2));

name カラムは特に今関係無い。重要なのは PERIOD FOR *(date_1, date_2) の部分。これがまさに前述の Time Periods というやつ。

これによって date_1, date_2 を元にした範囲が定義されて、その範囲に対して INSERT, DELETE, UPDATE をすると何かが起こる。
ちなみにこの段階では特殊な何かの値が入っているというわけでもない。

MariaDB [test]> CREATE TABLE t1(
    ->    name VARCHAR(50), 
    ->    date_1 DATE,
    ->    date_2 DATE,
    ->    PERIOD FOR date_period(date_1, date_2));
Query OK, 0 rows affected (0.013 sec)

MariaDB [test]> select * from t1;
Empty set (0.002 sec)

これだけでもまだわからないので実際にテーブルのデータを追加したり、更新したりする。

INSERT

まずは INSERT から。

INSERT INTO t1 (name, date_1, date_2) VALUES
    ('a', '1999-01-01', '2000-01-01'),
    ('b', '1999-01-01', '2018-12-12'),
    ('c', '1999-01-01', '2017-01-01'),
    ('d', '2017-01-01', '2019-01-01');

余談だが date_period(date_1, date_2) という風に Time Periods を宣言した場合、それぞれのカラムに入る値は date_1 < date_2 となる必要がある。

MariaDB [test]> insert into t1(name, date_1, date_2) values ('e', '2022-01-01', '2021-01-01');
ERROR 4025 (23000): CONSTRAINT `date_period` failed for `test`.`t1`
MariaDB [test]> insert into t1(name, date_1, date_2) values ('e', '2022-01-01', '2022-01-01');
ERROR 4025 (23000): CONSTRAINT `date_period` failed for `test`.`t1`

DELETE

次に Time Periods に基づいて DELETE を飛ばしてみる。

MariaDB [test]> DELETE FROM t1
    -> FOR PORTION OF date_period
    ->     FROM '2001-01-01' TO '2018-01-01';
Query OK, 3 rows affected (0.008 sec)

MariaDB [test]> SELECT * FROM t1 ORDER BY name;
+------+------------+------------+
| name | date_1     | date_2     |
+------+------------+------------+
| a    | 1999-01-01 | 2000-01-01 |
| b    | 1999-01-01 | 2001-01-01 |
| b    | 2018-01-01 | 2018-12-12 |
| c    | 1999-01-01 | 2001-01-01 |
| d    | 2018-01-01 | 2019-01-01 |
+------+------------+------------+
5 rows in set (0.001 sec)

すると name = b の行が何故か2つになり、date_1, date_2 の値も変わっている。 name = c, name = d の列は増えてはないが date_1, date_2 の値が変わっている。
大体予想はつくが何が起こったかを図にするとこうなる。

application time periods before after
DELETE FOR PORTION OF

DELETE FOR PORTION OF は指定された Time Periods に基づいてその範囲を含むレコードの Time Periods を縮小・分割する。公式ドキュメントの例にはなかったが、DELETE で指定された Time Periods に収まりきる Time Periods を持つレコードは削除される。

MariaDB [test]> INSERT INTO t1(name, date_1, date_2) values('e', '2002-01-01', '2007-01-01');
Query OK, 1 row affected (0.007 sec)

MariaDB [test]> DELETE FROM t1 FOR PORTION OF date_period     FROM '2001-01-01' TO '2018-01-01';
Query OK, 1 row affected (0.007 sec)

MariaDB [test]> SELECT * FROM t1 ORDER BY name;
+------+------------+------------+
| name | date_1     | date_2     |
+------+------------+------------+
| a    | 1999-01-01 | 2000-01-01 |
| b    | 1999-01-01 | 2001-01-01 |
| b    | 2018-01-01 | 2018-12-12 |
| c    | 1999-01-01 | 2001-01-01 |
| d    | 2018-01-01 | 2019-01-01 |
+------+------------+------------+
5 rows in set (0.001 sec)

UPDATE

次に UPDATE について。 まずはさっきと同じテーブルを作る。

MariaDB [test]> TRUNCATE t1;
MariaDB [test]> INSERT INTO t1 (name, date_1, date_2) VALUES
    ->     ('a', '1999-01-01', '2000-01-01'),
    ->     ('b', '1999-01-01', '2018-12-12'),
    ->     ('c', '1999-01-01', '2017-01-01'),
    ->     ('d', '2017-01-01', '2019-01-01');
Query OK, 4 rows affected (0.001 sec)
Records: 4  Duplicates: 0  Warnings: 0

MariaDB [test]> SELECT * FROM t1;
+------+------------+------------+
| name | date_1     | date_2     |
+------+------------+------------+
| a    | 1999-01-01 | 2000-01-01 |
| b    | 1999-01-01 | 2018-12-12 |
| c    | 1999-01-01 | 2017-01-01 |
| d    | 2017-01-01 | 2019-01-01 |
+------+------------+------------+
4 rows in set (0.001 sec)

その上で次のように UPDATE する。

MariaDB [test]> UPDATE t1 FOR PORTION OF date_period
    ->   FROM '2000-01-01' TO '2018-01-01' 
    -> SET name = CONCAT(name,'_original');
Query OK, 3 rows affected (0.009 sec)
Rows matched: 3  Changed: 3  Inserted: 4  Warnings: 0

MariaDB [test]> SELECT * FROM t1 ORDER BY name;
+------------+------------+------------+
| name       | date_1     | date_2     |
+------------+------------+------------+
| a          | 1999-01-01 | 2000-01-01 |
| b          | 1999-01-01 | 2000-01-01 |
| b          | 2018-01-01 | 2018-12-12 |
| b_original | 2000-01-01 | 2018-01-01 |
| c          | 1999-01-01 | 2000-01-01 |
| c_original | 2000-01-01 | 2017-01-01 |
| d          | 2018-01-01 | 2019-01-01 |
| d_original | 2017-01-01 | 2018-01-01 |
+------------+------------+------------+
8 rows in set (0.001 sec)

これもまた図にするとこうなる。

application time periods before after
UPDATE FOR PORTION OF

name = a は例によって範囲外なので影響を受けない。name = b, name = c, name = d は範囲内が更新され範囲外の Time Periods は分割・縮小される。
ここで気になるのが、やはり範囲内に収まりきる Time Periods の挙動について。UPDATE により前述の *_original も影響を受け、とても見づらいが範囲内に収まる Time Periods については DELETE 時と同様に UPDATE の影響を受け、Time Periods が分割・縮小されない。

MariaDB [test]> INSERT INTO t1(name, date_1, date_2) values('e', '2002-01-01', '2007-01-01');
Query OK, 1 row affected (0.007 sec)

MariaDB [test]> UPDATE t1 FOR PORTION OF date_period   FROM '2000-01-01' TO '2018-01-01'  SET name = CONCAT(name,'_original');
Query OK, 4 rows affected (0.009 sec)
Rows matched: 4  Changed: 4  Inserted: 0  Warnings: 0

MariaDB [test]> SELECT * FROM t1 ORDER BY name;
+---------------------+------------+------------+
| name                | date_1     | date_2     |
+---------------------+------------+------------+
| a                   | 1999-01-01 | 2000-01-01 |
| b                   | 1999-01-01 | 2000-01-01 |
| b                   | 2018-01-01 | 2018-12-12 |
| b_original_original | 2000-01-01 | 2018-01-01 |
| c                   | 1999-01-01 | 2000-01-01 |
| c_original_original | 2000-01-01 | 2017-01-01 |
| d                   | 2018-01-01 | 2019-01-01 |
| d_original_original | 2017-01-01 | 2018-01-01 |
| e_original          | 2002-01-01 | 2007-01-01 |
+---------------------+------------+------------+
9 rows in set (0.001 sec)

WITHOUT OVERLAPS

ここまで書いた上で WITHOUT OVERLAPS なんて名前のものが出てきたら察しがつくが MariaDB 10.5.3 から重複不可にも利用できる。

MariaDB [test]> CREATE OR REPLACE TABLE rooms (
    ->  room_number INT,
    ->  guest_name VARCHAR(255),
    ->  checkin DATE,
    ->  checkout DATE,
    ->  PERIOD FOR p(checkin,checkout),
    ->  UNIQUE (room_number, p WITHOUT OVERLAPS)
    ->  );
Query OK, 0 rows affected (0.012 sec)

MariaDB [test]> INSERT INTO rooms VALUES 
    ->  (1, 'Regina', '2020-10-01', '2020-10-03'),
    ->  (2, 'Cochise', '2020-10-02', '2020-10-05'),
    ->  (1, 'Nowell', '2020-10-03', '2020-10-07'),
    ->  (2, 'Eusebius', '2020-10-04', '2020-10-06');
ERROR 1062 (23000): Duplicate entry '2-2020-10-06-2020-10-04' for key 'room_number'

UNIQUE と組み合わせる必要があるが、こういった利用方法もありらしい。

おわりに

これと partitioning が絡んだらバグるパターンが存在していてそれを直そうとするなかで面白い機能だったのでまとめたが、イマイチこれの使いどころが想像つかない。
普段は MySQL ばかりなので、この手の勝手に何か行が増えたり書き換わったりするやつは見たことがなくデバッグしていても中々に面白かった。唐突に write_row が出てきたり。
で、書いていて思ったがその辺りや諸々気になったことがあるのでまた余裕があるときにまとめる。


訂正: 3ヶ月じゃなくて約 7 ヶ月だった…

*1:Date and Time Data Types - MariaDB Knowledge Base

*2: ALTER TABLE でも Time Periods の追加、削除ができるが面倒なので今回はそこについて言及しない。

MariaDB のデバッグをいい感じにやる

はじめに

久々に MariaDBmacOS 上でデバッグしていたらデバッグしたまま寝てしまい、それを機に色々と壊してしまい戻すまでに時間がかかったので雑にまとめる。

デバッグの構成

mtrgdb をいい感じに利用できないことが多いので CLion に組み込まれている lldb を愛用している。この時、手元で実行すると data ディレクトリなどなどをいい感じに初期化して配置しておく必要がある。
basedir は /usr/local/mysql, datadir は /usr/local/mysql/data としている。

やることリスト

普通にビルドする。
rabbitfoot141.hatenablog.com
ただし、オプションに -DMYSQL_MAINTAINER_MODE=OFF を追加する。
これで groonga, mroonga 周りのビルド大ゴケ問題をだいたい解決できる。

ビルドしたら次にやることは make install。
これをやると basedir 以下にバイナリなどが配置される。

バイナリを配置したらやることは

$ /usr/local/mysql/scripts/mysql_install_db --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data

で datadir を初期化する。

あとはいつも通り mariadbd を起動してデバッグする。

ビルド役立ちメモ

特定ストレージエンジンのビルドをスキップしたい時は -DWITHOUT_MROONGA=1 みたいにオプションを渡してやると幸せになれる

MariaDB 10.6 を macOS BigSur 11.4 でビルドする

はじめに

どうも、最近クラウドアトラスを見てからあまり映画を漁りに行くことをしなくなり、代わりに異世界スローライフ系の作品を脳死で見ているけんつです。
気軽に Fork していた mariadb/sever リポジトリに対して fetch upstream を実行したらビルドが全く通らなくなったのでこれを機にビルド方法を残しておく。
なにやら macOS 上でのビルドは気軽に死ぬので mariadb/server 10.6 のこのコミット時点でビルドすることを前提にする。
bump the VERSION · MariaDB/server@a841069 · GitHub

ビルドする前に

ソースコードを持ってくる

いつもの。

> git@github.com:MariaDB/server.git

必要なパッケージ群をインストールする

Build Environment Setup for Mac - MariaDB Knowledge Base
ここに書いてあるように必要なパッケージをインストールする。今回は homebrew を使って突っ込んだ。

bison を変更する

XCode に頼っていると次のようなエラーがビルド時に出力される。

[ 22%] [BISON][gen_mariadb_cc_hh] Building parser with bison 2.3
/Users/lrf141/mariaProject/server/build/sql/yy_mariadb.yy:351.9-16: syntax error, unexpected identifier, expecting string
make[2]: *** [sql/yy_mariadb.cc] Error 1
make[1]: *** [sql/CMakeFiles/gen_lex_token.dir/all] Error 2
make: *** [all] Error 2

なにやら bison のバージョンが古いことが原因らしいので、homebrew を使っていい感じにする。

$ brew install bison
$ brew link bison --force

brew link 時にパスを通せと言われるので、お手持ちの .bashrc なり .zshrc なりに追加する。

どうしても /usr/bin 以下に突っ込みたい人

System Integrity Protection が有効になっていると sudo でも操作できないので

$ csrutil status 

の結果が enable だった場合、リカバリーモードで立ち上げて

$ csrutil disable

をやれば突っ込めるようになる。

gcc, g++ を突っ込む(宗教に応じて選択してください、おすすめはしません)

やりたい人はお勧めしないが Free Software Foundation の方の gcc, g++ を突っ込む。

$ brew install gcc
// path の確認
$ ls /usr/local/bin | grep gcc
$ ls /usr/local/bin | grep g++

$ ln -s /usr/local/bin/gcc-8 /usr/local/bin/gcc
$ ln -s /usr/local/bin/g++-8 /usr/local/bin/g++

ここまでやったらさっきと同様にパスを通す。

ビルド

CMake でよろしくやる

zenn.dev
Nayuta Yanagisawa さんの記事によると

cmake -DCMAKE_BUILD_TYPE=Debug ..

とすると良いようですが macOS 版でこのまま進むと mroonga のビルド時に vendor に入っている groonga をコンパイルするあたりで -Wformat を吐くのでこいつだけ抑制しておきます。
なので基本的には以下のような感じにするといい感じにやってくれます。
どうしても mroonga, groonga を触りたいんだという人は頑張ってください。

$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAG="-Wno-format" ..
$ cmake --build . --config Debug -j 8
homebrew で突っ込んだ gcc, g++ を突っ込むと何が起こるか

結論から書くとビルドでこけます。
何が起こるかというと

In file included from server/sql/structs.h:26,
                 from server/sql/handler.h:34,
                 from server/storage/innobase/include/trx0xa.h:27,
                 from server/storage/innobase/include/trx0trx.h:34,
                 from server/storage/innobase/btr/btr0pcur.cc:30:
server/include/my_time.h: In function 'void my_timeval_trunc(timeval*, uint)':
server/include/my_time.h:249:65: error: conversion from 'long int' to '__darwin_suseconds_t' {aka 'int'} may change value [-Werror=conversion]
  249 |   tv->tv_usec-= my_time_fraction_remainder(tv->tv_usec, decimals);

my_time.h で int と long int の変換やってええんか?って感じでエラーになります。
server/my_time.h at 76f4a78ba2639b5abd01a88b24a3c509c11530ce · MariaDB/server · GitHub


この辺をよく見てみると _timeval.h なるヘッダーで

/*
 * Copyright (c) 2003-2012 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
#ifndef _STRUCT_TIMEVAL
#define _STRUCT_TIMEVAL         struct timeval

#include <machine/types.h> /* __darwin_time_t */
#include <sys/_types.h> /* __darwin_suseconds_t */

_STRUCT_TIMEVAL
{
	__darwin_time_t         tv_sec;         /* seconds */
	__darwin_suseconds_t    tv_usec;        /* and microseconds */
};
#endif /* _STRUCT_TIMEVAL */

https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/sys/_types/_timeval.h.auto.html

__darwin_suseconds_t なる輩が int なのでこいつが問題になってくるわけです。

おわりに

10.6 がリリースされる前に触ってたけどその時はさらに色々あって大変だったけどいい感じにビルドできることがわかって大変満足。
特にどのストレージエンジンか忘れたけど鬼のように出力されていた -Wsign-compare が消え去った上に、デバッグしようものなら gdb で table とか見れなかったのが見れるようになっていてこの上なく満足。