開発業者が納品したWebアプリケーションがエラーを吐いていた。
致命的な問題で無かったことから発覚が遅れ、瑕疵担保責任を追及できる期間はとうに過ぎていたので、自分で直したという話。
調査ついでにエラーログの見方を簡単に説明しよう。
出力されたエラーメッセージ
_mysql_exceptions.DataError: (1264, "Out of range value for column 'カラム名' at row 1")
MySQLdbというPythonのモジュールが投げた例外のようだ。
最初の 1264 は4桁なので、MySQL独自のエラーコードだろう。
2つのエラー番号
MySQLのエラー番号には、
- MySQL独自のエラーコード(4桁)
- ODBCやANSI SQLで定められたSQLSTATE(5桁)
がある。
SQLSTATEは汎化されているので、MySQLに特化した独自コードの方が詳細に特定できる。
公式サイトですべてのエラーメッセージを確認できるが、特に良く見かけるものを以下に抜粋する。
Row 行番号 doesn't contain data for all columns
- エラーコード:1261
- SQLSTATE:01000
- 意味:すべてのカラムへのデータを含んでいない
Row 行番号 was truncated; it contained more data than there were input columns
- エラーコード:1262
- SQLSTATE:01000
- 意味:カラムよりも多いデータを含んでいたので、切り捨てたデータがある
Column set to default value; NULL supplied to NOT NULL column 'カラム名' at row 行番号
- エラーコード:1263
- SQLSTATE:22004
- 意味:NOT NULL のカラムにデフォルト値の NULL を登録した
Out of range value for column 'カラム名' at row 行番号
- エラーコード:1264
- SQLSTATE:22003
- 意味:範囲外の値だったので、取り敢えず限界の値で登録した
▶ 今回出力されたエラーメッセージである。
Data truncated for column 'カラム名' at row 行番号
- エラーコード:1265
- SQLSTATE:01000
- 意味:先頭からみて入れられるところまで登録し、残りは切り捨てた
つまり…
エラーコード 1264 は、データ型の範囲を超えた値をINSERTしたときに発生するWarningである。直前に実行したSQLのWarningは SHOW WARNINGS
で確認できる。
データ型の範囲を超えると、そのデータ型の最大値または最小値に調整されて登録されるはずだが、PythonがDataError例外を投げたのでrollbackされたと推測。実際にも登録されていなかった。
insert into zzzzz_table values(5,1611728121,0,162,58.2,63.9,1,27.5)
Traceback (most recent call last):
File "/foo/bar/example.py", line 547, in <module>
fooList()
File "/foo/bar/example.py", line 164, in barList
cursor.execute(qstr)
File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 226, in execute
self.errorhandler(self, exc, value)
File "/usr/lib/python2.7/dist-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorvalue
_mysql_exceptions.DataError: (1264, "Out of range value for column 'xxxxx' at row 1")
データ型
対象カラムのデータ型をDESC
で確認するとTINYINT
であった。
TINYINT
の取りうる範囲は次の通り。
- バイト数: 1バイト
- デフォルト桁数: tinyint(4)
- 符号付き(SIGNED)の範囲:-128 ~ 127
データ型の後にUNSIGNED
を付けると符号なしとなり、0~255の範囲になる。
ちなみに tinyint(1) はBoolean型と同じだ。
直前のSQLで127を超える値をINSERTしていたが、運用上ありえない値なのでソースコードを修正した。