MySQL 8.0.22 で `ORDER BY ?` のプリペアドステートメントがフリーズする原因


https://zenn.dev/tmtms/articles/5a761600fbfd24e6c91b の続き。

C API だと ORDER BY ? のプリペアドステートメントでクライアントがフリーズするというのを書いたんだけど、Go だと問題ない という話があったので、MySQL のプロトコルを追ってみた。

プリペアドステートメントを発行すると通常は次のようにパケットが流れる。

=> COM_STMT_PREPARE "SELECT ?,?,? FROM tbl"
<= ステートメントID, 結果セットのカラム数(3), プレースホルダの数(3)
<= 謎パケット
<= 謎パケット
<= 謎パケット
<= EOF パケット

「謎パケット」はプレースホルダの数だけ流れるんだけど読み捨てられるだけなので本当に謎。

ところが 8.0.22 で ORDER BY ? の場合は次のようになる。

=> COM_STMT_PREPARE "SELECT a,b,c FROM tbl ORDER BY ?"
<= ステートメントID, 結果カラムの数(3), プレースホルダの数(1)
<= EOF パケット

プレースホルダの数が 1 なのに謎パケットが流れない。

C API mysql_stmt_prepare() の実装は、プレースホルダの数だけ謎パケットを読み飛ばして EOF パケットを待つようになっているので、謎パケットのつもりで EOF パケットを読み込んで、その後 EOF パケットを待ってしまう。

Go の実装は、プレースホルダの数は関係なく、EOF パケットが来るまでパケットを読み飛ばすという風になっているので、謎パケットが1つもなくても EOF パケットがくれば OK とみなして次に進むので問題が発生しない。

まあ、C API も Go と同じように EOF パケットが来るまで読み飛ばすというようにすればいいんだろうけど、これはたぶんサーバー側の応答が間違ってると思うんで、サーバー側の問題なんだろうなぁ(サーバーの実装は読んでない)。

英語を書くのがめんどうだし、自分では使わないからバグレポートを書く気がおきない…。