📅

Aurora2から3にバージョンアップする時にハマったこと

2024/12/01に公開

はじめに

今年もアドベントカレンダーはじまりました!
NEアドベントカレンダーの1日目の記事です!
NE株式会社が自分たちの興味あるテーマについて自由に書いていきます。
言い出しっぺの特権で1日目をいただいていますw
毎年一緒に参加して盛り上げてくれる皆さんに感謝です👏

概要

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.MySQL57.EOL.html
Aurora MySQL バージョン2は2024 年 10 月 31 日に標準サポートを終了しました。
Aurora2から3(MySQL5系から8系)
にバージョンアップする時にハマったことをまとめます。

起きた事象

日付の項目に空文字検索をしてエラーになる

元々空文字検索が可能だった部分でエラーが発生するようになりました。
こちらは「登録されていないもの」を検索しようとする機能でした。

例えば以下のようなtest_tableがあった場合に

以下のクエリを打つとバージョンアップ前はエラーにならないが、バージョンアップ後はエラーとなります。

select * from test_table
where
datetime='';


バージョンアップ前


バージョンアップ後

こちらはそもそもdatetime型のカラムに空文字比較をすること自体を辞めてIS NULL比較や「0000-00-00 00:00:00」にて「値が登録されていない状態」を表現するようにしました。

並び順が異なる

これはAurora2→3固有の問題ではなく実行計画が更新されたことによるものです。
MySQLはorder byを指定しない時に返す並び順は特に定義されていないため、実行計画によって返す順序が異なります。
ハマった部分ではorder byの指定のないクエリだったため
「元々MySQLが良い感じに返してくれていた順番に依存していた」
ので
「バージョンアップと共に異なる順序で返すようになり不都合が生じた」
という感じでした。
こちらは意図している部分でorder byをかけることで解決しました。

空文字検索の解説

以下のエラーが出ます。

Query 1 ERROR at Line 1: : Incorrect DATETIME value: ''

これは日付型のカラムに対して文字列を渡した場合の挙動のバグ修正によるもののようでした。

公式のリリースノート

When comparing DATE values with constant strings, MySQL first tries to convert the string to a DATE and then to perform the comparison. When the conversion failed, MySQL executed the comparison treating the DATE as a string, which could lead to unpredictable behavior. Now in such cases, if the conversion of the string to a DATE fails, the comparison fails with ER_WRONG_VALUE. (Bug #29025656)

なので空文字がどうとかというよりは「日付型として認識できない文字列」を渡した場合の挙動の変更となります。

バージョンアップ前
日付として正しくない文字列が渡された場合に、逆に比較対象の日付カラムの方をstringに変換して比較してくれていたようでした。
バージョンアップ後
日付として正しくない文字列が渡された場合にエラーとなるようになりました。

つまりバージョンアップ後に元の挙動を再現したい場合は以下のようなクエリにすれば問題なく通るようになります。

select * from test_table
where cast(datetime AS CHAR CHARACTER SET utf8) = "";

参考

https://rabbitfoot141.hatenablog.com/entry/2020/07/01/215728
https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-16.html#mysqld-8-0-16-sql-syntax

空文字以外のケース

今回のケースは空文字に限った話ではなかったので別ケースも紹介

2020年より最近のデータを取得するようなクエリ。
バージョンアップ前は問題なく通っている。

select * from test_table
where datetime > "2020";

バージョンアップ後はエラーとなる。

元の挙動を再現するには
以下のようなクエリにすると同じ意味になる。

まとめ

ハマった時には、そもそも日付カラムに空文字を入れるのが変なのでそれをコードベースで直す、という方向に舵を切り問題解決しました。
今回記事を書くにあたり改めて問題を調査したのですが、はじめはsql_modeの違いだと思っていたのですが手元の環境のsql_modeを両方一致させても事象が再現するため、問題調査でもハマりました。
結果バージョンアップによるバグ修正による挙動の差異だということが分かったので、また1つ賢くなりました。
「なんとなくこうかな?」を記事にまとめることで「こうである」と明らかにできたので大変有意義でした!
皆様もバージョンアップはご安全に!

おわりに

今年もアドカレ頑張っていきましょー!
明日の記事もお楽しみに〜!

NE株式会社の開発ブログ

Discussion