GA

2024/03/11

SET_USER_ID権限がないアカウントでViewのDEFINERになっているアカウントをDROP USERしようとするとOperation DROP USER failed for .. as it is referenced as a definer account in a viewのエラーが出るようになった

TL;DR

  • タイトルがすべてを物語っている。 SET_USER_ID権限 があるとこのオペレーションは成功する

  • エラーコードは4006


MySQL error code MY-004006 (ER_CANNOT_USER_REFERENCED_AS_DEFINER): Operation %s failed for %.256s as it is referenced as a definer account in %s.

実験。
DROP USER用を試すダミーのアカウントを作ってVIEWのDEFINERに設定する。

mysql80 9> CREATE USER view_dummy;
Query OK, 0 rows affected (0.00 sec)

mysql80 9> CREATE DEFINER=view_dummy VIEW d1.v1 AS SELECT * FROM d1.t1;
Query OK, 0 rows affected (0.02 sec)

root@localhost ( SET_USER_ID 権限を持っている ) で DROP VIEW した場合はワーニングどまり (4005番なのでこれも新顔のはず, このワーニングは5.7では出ない)

mysql80 9> DROP USER view_dummy;
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql80 9> SHOW WARNINGS;
+---------+------+---------------------------------------------------------------------+
| Level   | Code | Message                                                             |
+---------+------+---------------------------------------------------------------------+
| Warning | 4005 | User 'view_dummy'@'%' is referenced as a definer account in a view. |
+---------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)

テストのためにもう一回同じ名前でCREATE USERする(この時点でd1.v1は存在していてDEFINERになっているのでもう一度ワーニングは出た)

mysql80 10> CREATE USER view_dummy;
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql80 10> SHOW WARNINGS;
+---------+------+---------------------------------------------------------------------+
| Level   | Code | Message                                                             |
+---------+------+---------------------------------------------------------------------+
| Warning | 4005 | User 'view_dummy'@'%' is referenced as a definer account in a view. |
+---------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)

今度は SET_USER_ID 権限を持っていないアカウントでログインする。よく見ないとわからないけどよく見ると SET_USER_ID privはない。

mysql80 12> SHOW GRANTS\G
*************************** 1. row ***************************
Grants for yoku0825@%: GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `yoku0825`@`%`
*************************** 2. row ***************************
Grants for yoku0825@%: GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ABORT_EXEMPT,AUDIT_ADMIN,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FIREWALL_EXEMPT,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,GROUP_REPLICATION_STREAM,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PASSWORDLESS_USER_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SENSITIVE_VARIABLES_OBSERVER,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,TELEMETRY_LOG_ADMIN,XA_RECOVER_ADMIN ON *.* TO `yoku0825`@`%`
2 rows in set (0.00 sec)

で、 DROP USER はエラーになる。

mysql80 12> DROP USER view_dummy;
ERROR 4006 (HY000): Operation DROP USER failed for 'view_dummy'@'%' as it is referenced as a definer account in a view.

2024/03/08

Rocky Linux 8とMySQLでcoreファイルが見つからない

TL;DR


—core-fileulimit -c unlimited も設定してるのにcoredumpが見つからない…と思ったら変わっていたらしい。


$ less /usr/mysql/8.0.36/data/error.log

..

2024-03-08T07:36:23Z UTC - mysqld got signal 11 ;

<secret>
/usr/mysql/8.0.36/bin/mysqld(do_command(THD*)+0x15b) [0xde00bb]
/usr/mysql/8.0.36/bin/mysqld() [0xf391b8]
/usr/mysql/8.0.36/bin/mysqld() [0x2576814]
/lib64/libpthread.so.0(+0x81da) [0x7fa868e941da]
/lib64/libc.so.6(clone+0x43) [0x7fa867236e73]

Trying to get some variables.
Some pointers may be invalid and cause the dump to abort.
Query (7fa80438de80): SHOW CREATE TABLE sys.metrics
Connection ID (thread ID): 14
Status: NOT_KILLED

The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains
information that should help you find out what is causing the crash.
Writing a core file

Writing a core file と言ってるくせに、datadirにない。

$ ll /usr/mysql/8.0.36/data/core*
ls: cannot access '/usr/mysql/8.0.36/data/core*': No such file or directory

journalctl を見たらなんかスタックトレース ( gdbthread apply all bt と同じように全スレッドぶん) が載ってる…こんなの前にはなかったはず…そして俺はスタックトレースじゃなくてcoreファイルが欲しいんだ…!

$ sudo journalctl --since=today | less
..
Mar 08 07:36:45 yoku0825-sandbox systemd-coredump[2500410]: Process 2453404 (mysqld) of user 1001 dumped core.

                                                            Stack trace of thread 2453453:
                                                            #0  0x00007fa868e9ba85 pthread_kill (libpthread.so.0)
                                                            #1  0x0000000000f4913c handle_fatal_signal (mysqld)
                                                            #2  0x00007fa868e9ed40 __restore_rt (libpthread.so.0)
..

と思ってググったらあっという間に冒頭のブログに出会った。

systemd-coredump環境で暮らす - 赤帽エンジニアブログ

$ sudo coredumpctl list
TIME                            PID   UID   GID SIG COREFILE  EXE
Thu 2024-03-07 02:41:14 GMT  2433147  1001  1001  11 present   /usr/mysql/8.0.36/bin/mysqld
Thu 2024-03-07 04:00:05 GMT  2441380  1001  1001   5 present   /usr/mysql/8.0.36/bin/mysqld
Fri 2024-03-08 07:36:45 GMT  2453404  1001  1001  11 present   /usr/mysql/8.0.36/bin/mysqld

coredumpctl dump .. でスタックトレースが出て「違う、俺はコアファイルが欲しいんだ」って気になったけど特に気にしなくて良くてちゃんとoutputに出力されてた。

$ sudo coredumpctl dump -1 --output ./core.2433147
           PID: 2453404 (mysqld)
           UID: 1001 (yoku0825)
           GID: 1001 (yoku0825)
        Signal: 11 (SEGV)
     Timestamp: Fri 2024-03-08 07:36:23 GMT (50min ago)
  Command Line: /usr/mysql/8.0.36/bin/mysqld --basedir=/usr/mysql/8.0.36 --datadir=/usr/mysql/8.0.36/data --plugin-dir=/usr/mysql/8.0.36/lib/plugin --server-id=1080 --default-authentication-plugin=mysql_native_password --mysqlx=off --early-plugin-load=keyring_file.so --default-time-zone=+00:00 --loose-rpl-semi-sync-source-enabled=ON --loose-innodb-ddl-threads=1 --log-error=error.log --pid-file=/usr/mysql/8.0.36/data/mysql.pid --socket=/usr/mysql/8.0.36/data/mysql.sock --port=64080
    Executable: /usr/mysql/8.0.36/bin/mysqld
 Control Group: /
         Slice: -.slice
       Boot ID: 64f2b36cc0ba43cea4f50ea42cae833f
    Machine ID: f0891c4bddbf4944abc3c0e87e12fe9c
      Hostname: yoku0825-sandbox
       Storage: /var/lib/systemd/coredump/core.mysqld.1001.64f2b36cc0ba43cea4f50ea42cae833f.2453404.1709883383000000.lz4
       Message: Process 2453404 (mysqld) of user 1001 dumped core.

..

dumpで吐き出させたあとはいつも通りgdbで見られる。 coredumpctl debug -1 でも良さそう(sudo噛ませると .gdbinit 読んでくれなくてちょっと見づらい)

$ gdb ../bin/mysqld core.2433147
or
$ sudo coredumpctl debug -1
           PID: 2453404 (mysqld)
           UID: 1001 (yoku0825)
           GID: 1001 (yoku0825)
        Signal: 11 (SEGV)
     Timestamp: Fri 2024-03-08 07:36:23 GMT (53min ago)
  Command Line: /usr/mysql/8.0.36/bin/mysqld --basedir=/usr/mysql/8.0.36 --datadir=/usr/mysql/8.0.36/data --plugin-dir=/usr/mysql/8.0.36/lib/plugin --server-id=1080 --default-authentication-plugin=mysql_native_password --mysqlx=off --early-plugin-load=keyring_file.so --default-time-zone=+00:00 --loose-rpl-semi-sync-source-enabled=ON --loose-innodb-ddl-threads=1 --log-error=error.log --pid-file=/usr/mysql/8.0.36/data/mysql.pid --socket=/usr/mysql/8.0.36/data/mysql.sock --port=64080
    Executable: /usr/mysql/8.0.36/bin/mysqld
 Control Group: /
         Slice: -.slice
       Boot ID: 64f2b36cc0ba43cea4f50ea42cae833f
    Machine ID: f0891c4bddbf4944abc3c0e87e12fe9c
      Hostname: yoku0825-sandbox
       Storage: /var/lib/systemd/coredump/core.mysqld.1001.64f2b36cc0ba43cea4f50ea42cae833f.2453404.1709883383000000.lz4
       Message: Process 2453404 (mysqld) of user 1001 dumped core.

..
### これもしばらくスタックトレース吐いたあと止まるけどそのうちgdbが始まる

coredumpctl infoだと圧縮されたコアファイルのパスも吐いてくれるので、lz4catとかでもなんとかなった

$ sudo coredumpctl info -1
           PID: 2453404 (mysqld)
           UID: 1001 (yoku0825)
           GID: 1001 (yoku0825)
        Signal: 11 (SEGV)
     Timestamp: Fri 2024-03-08 07:36:23 GMT (54min ago)
  Command Line: /usr/mysql/8.0.36/bin/mysqld --basedir=/usr/mysql/8.0.36 --datadir=/usr/mysql/8.0.36/data --plugin-dir=/usr/mysql/8.0.36/lib/plugin --server-id=1080 --default-authentication-plugin=mysql_na>
    Executable: /usr/mysql/8.0.36/bin/mysqld
 Control Group: /
         Slice: -.slice
       Boot ID: 64f2b36cc0ba43cea4f50ea42cae833f
    Machine ID: f0891c4bddbf4944abc3c0e87e12fe9c
      Hostname: yoku0825-sandbox
       Storage: /var/lib/systemd/coredump/core.mysqld.1001.64f2b36cc0ba43cea4f50ea42cae833f.2453404.1709883383000000.lz4         <----- ここ
       Message: Process 2453404 (mysqld) of user 1001 dumped core.

..

abrtd にコアファイルを消し去られる事案があってそれかなあと思っていたら RHEL8では非推奨 になっていたんですね。

OSが変わったことを感じる…。

2024/03/07

replicate_wild_ignore_table="mysql.%" で CREATE USERやGRANTがignoreされる理由

TL;DR

  • 体感としては知ってたけどレプリケーションフィルタには対応するコードがなくてもんにょりしていた
  • sql/rpl_filter.cc じゃなくて sql/auth/sql_user_table.cc に埋まっていやがった

ドキュメントには、

ただし、通常はこの情報を間接的に更新するステートメント (GRANT、REVOKE、およびトリガー、ストアドルーチン、およびビューを操作するステートメント) は、ステートメントベースレプリケーションを使用してレプリカにレプリケートされます。

MySQL :: MySQL 8.0 リファレンスマニュアル :: 17.5.1.22 mysql システムスキーマのレプリケーション

Table-level replication filters are only applied to tables that are explicitly mentioned and operated on in the query. They do not apply to tables that are implicitly updated by the query. For example, a GRANT statement, which updates the mysql.user system table but does not mention that table, is not affected by a filter that specifies mysql.% as the wildcard pattern.

https://dev.mysql.com/doc/refman/8.0/en/replication-options-replica.html#option_mysqld_replicate-wild-ignore-table

(日本語版にはこの表記がなかった…)

しかもご丁寧に回避方法まで

If you need to filter out GRANT statements or other administrative statements, a possible workaround is to use the —replicate-ignore-db filter. This filter operates on the default database that is currently in effect, as determined by the USE statement.

https://dev.mysql.com/doc/refman/8.0/en/replication-options-replica.html#option_mysqld_replicate-wild-ignore-table

と、あたかもCREATE USERもGRANTもフィルターに引っかからずにステートメントベースのルールでだけ評価されそうなことを言っているのに、現実には replicate_wild_ignore_table=mysql.% でフィルタされてしまう。

実験。

### Sourceになる
mysqlsh -- dba deploySandboxInstance 3306 --password=testpass

### 比較のためにreplicate_ignore_db=mysqlを設定したMySQL
mysqlsh -- dba deploySandboxInstance 3307 --password=testpass --mysqldOptions="replicate-ignore-db=mysql"

### 本丸のreplicate_wild_ignore_table=mysql.%を設定したMySQL
mysqlsh -- dba deploySandboxInstance 3308 --password=testpass --mysqldOptions="replicate-wild-ignore-table=mysql.%"

ソースにCREATE USER/GRANTでレプリケーション用アカウントを作る。ここまではフツー。

mysqlsh root:testpass@localhost:3306 --sql

CREATE USER replicate IDENTIFIED BY 'replication';
GRANT REPLICATION SLAVE ON *.* TO replicate;

3307のインスタンスと3308のインスタンスでそれぞれレプリケーションを組む。 source_ssl=1 にしているのはcaching_sha2_passwordのエラー避け ( 日々の覚書: MySQL 8.0のcaching_sha2_password + 非SSL接続が転ける )
別に GET_SOURCE_PUBLIC_KEY=1 でもいい。

mysqlsh root:testpass@localhost:3307 --sql

CHANGE REPLICATION SOURCE TO source_host = '127.0.0.1', source_port = 3306, source_user = 'replicate', source_password = 'replication', source_ssl = 1;
START REPLICA;

---

mysqlsh root:testpass@localhost:3308 --sql

CHANGE REPLICATION SOURCE TO source_host = '127.0.0.1', source_port = 3306, source_user = 'replicate', source_password = 'replication', source_ssl = 1;
START REPLICA;

source_log_pos を指定していないのでソースに残っている先頭のバイナリログのpos=1からレプリケーションするので、本来 CREATE USERとGRANTはレプリカでも実行されてアカウントが作成されるはず。

それぞれのインスタンスでSHOW GRANTSしてみると、

for port in 3306 3307 3308 ; do
  mysqlsh root:testpass@localhost:$port --sql -e "SHOW GRANTS FOR replicate"
done

Grants for replicate@%
GRANT REPLICATION SLAVE ON *.* TO `replicate`@`%`    ### 3306, replication source

Grants for replicate@%
GRANT REPLICATION SLAVE ON *.* TO `replicate`@`%`    ### 3307, replicate_ignore_db=mysql だけどCREATE USERもGRANTも `USE` しない状態でやっているのでフィルターされない

ERROR: 1141 (42000) at line 1: There is no such grant defined for user 'replicate' on host '%'   ### 3308のreplicate_wild_ignore_tablesだとフィルターされてCREATEもGRANTも実行されない

コードを見る限りたぶんMySQLのバグじゃなくてドキュメントが間違ってるんだと思う。

https://github.com/mysql/mysql-server/blob/mysql-8.0.36/sql/auth/sql_user_table.cc#L1975-L1990

2024/03/05

MySQLのCHECKSUM TABLEは特別なことをせずに愚直に行を全部読んでチェックサムを取っている

TL;DR

  • タイトルが全て
  • インデックスの情報は考慮しないのでインデックスが違ってもチェックサムは一致する
  • 最終的に行ごとのチェックサムを合計しているので行の順番が違ってもチェックサムは一致する

CHECKSUM TABLE は不思議なちからでテーブルのチェックサムを計算しているかのように思えるかも知れないがさにあらず。
実に地味に1行1フィールドずつ読んでチェックサムを取っている。

https://github.com/mysql/mysql-server/blob/mysql-8.0.36/sql/sql_table.cc#L18745-L18904

ということは、データ型が同じ扱いになるやつや行の順番には関係がないので CHECKSUM TABLE が通ることがある…?

  • PKあり vs PKなし、データ型いっしょ、行の順番違う

    • CHECKSUMいっしょ
mysql80 40> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `num` bigint unsigned NOT NULL AUTO_INCREMENT,
  `val` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`num`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql80 40> SHOW CREATE TABLE t2\G
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `num` bigint unsigned NOT NULL,
  `val` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql80 40> SELECT * FROM t1;
+-----+------+
| num | val  |
+-----+------+
|   1 | one  |
|   2 | two  |
+-----+------+
2 rows in set (0.00 sec)

mysql80 40> SELECT * FROM t2;
+-----+------+
| num | val  |
+-----+------+
|   2 | two  |
|   1 | one  |
+-----+------+
2 rows in set (0.00 sec)

mysql80 40> CHECKSUM TABLE t1, t2;
+-------+------------+
| Table | Checksum   |
+-------+------------+
| d1.t1 | 4082666774 |
| d1.t2 | 4082666774 |
+-------+------------+
2 rows in set (0.00 sec)
  • varchar vs char, 行の順番一緒

    • 違うのになる
mysql80 40> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `val` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql80 40> SHOW CREATE TABLE t2\G
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `val` char(32) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql80 40> CHECKSUM TABLE t1, t2;
+-------+------------+
| Table | Checksum   |
+-------+------------+
| d1.t1 | 1816645479 |
| d1.t2 | 2736998372 |
+-------+------------+
2 rows in set (0.00 sec)
  • varchar(32) vs varchar(255), 行の順番一緒

    • 同じのになる
mysql80 40> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `val` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql80 40> SHOW CREATE TABLE t2\G
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `val` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql80 40> CHECKSUM TABLE t1, t2;
+-------+------------+
| Table | Checksum   |
+-------+------------+
| d1.t1 | 1816645479 |
| d1.t2 | 1816645479 |
+-------+------------+
2 rows in set (0.00 sec)

あなたの知らない CHECKSUM TABLE の世界

2024/02/09

GRANTでデータベース名にワイルドカードを指定することとpartial revokesと

TL;DR

  • partial_revokes が有効な状態だと GRANT ALL ON 'd%'.* TO .. みたいなデータベースワイルドカードの構文が効かない
In privilege assignments, enabling partial_revokes causes MySQL to interpret occurrences of unescaped _ and % SQL wildcard characters in schema names as literal characters, just as if they had been escaped as \_ and \%. Because this changes how MySQL interprets privileges, it may be advisable to avoid unescaped wildcard characters in privilege assignments for installations where partial_revokes may be enabled.

In addition, use of _ and % as wildcard characters in grants is deprecated as of MySQL 8.0.35, and you should expect support for them to be removed in a future version of MySQL.

https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_partial_revokes


ドキュメントに書いてある以外に地味な罠があったし、そもそもこのpartial_revokesの制限を知らずに知らずに時間を溶かしたのでメモ。。

CREATE USER yoku0825;
GRANT ALL ON `d%`.* TO yoku0825;  -- シングルクォートでなくてバッククォート

mysql80 9> SHOW GRANTS;
+--------------------------------------------------+
| Grants for yoku0825@%                            |
+--------------------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`%`             |
| GRANT ALL PRIVILEGES ON `d%`.* TO `yoku0825`@`%` |
+--------------------------------------------------+
2 rows in set (0.00 sec)

mysql80 9> CREATE DATABASE d11;   -- d%` にマッチするから
Query OK, 1 row affected (0.00 sec)

mysql80 9> CREATE DATABASE d15;   -- d% にマッチするから
Query OK, 1 row affected (0.01 sec)

mysql80 9> CREATE DATABASE newdb;  -- d% にマッチしないから
ERROR 1044 (42000): Access denied for user 'yoku0825'@'%' to database 'newdb'

SET GLOBAL partial_revokes = ON;

mysql80 11> CREATE DATABASE d17;   -- d% が文字列リテラルとして解釈されるから
ERROR 1044 (42000): Access denied for user 'yoku0825'@'%' to database 'd17'

mysql80 11> DROP DATABASE d11;  -- !?
Query OK, 0 rows affected (0.00 sec)

mysql80 11> CREATE TABLE d15.t1 (num int);  -- !?
Query OK, 0 rows affected (0.01 sec)

このデータベースのワイルドカード指定が8.0.35から非推奨だから、この一貫性のない挙動が直ってくれる気はしない…。


【2024/02/09 15:21】

他の環境(8.0.32 + partial_revokes)だと既存のスキーマに対する操作もちゃんと拒否された(一貫性がある)

違いがわからぬ。。。


【2024/02/09 17:41】

my.cnfにpartial_revokes足してmysqldを再起動したらちゃんと一貫性のある動作になった。安心。