概要
Laravel + PostgreSQLの環境でとあるテーブルのidをつかってexistsのバリデーションをかけた。バリデーションルールにちゃんと引っかかることを確認したくて999999
という値をidのキーでPOSTした。なぜかバリデーションチェックエラーではなくSQL系のエラーが返された。何が起きたのか簡単にまとめておく。
原因
当該テーブルのidカラムのデータ型の値範囲を超えてexistsルールでチェックしていた。
返されたSQLエラー
バリデーションエラーで400が帰ってほしいのに下記が返された。
SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block
このとき自分はexistsのバリデーションルールがSQLを投げてチェックしていることを完全に失念しており、「なんでありえないIDを投げているのにバリデーションルール通過して、DB書き込み処理まで到達してしまったんだ!!!」というようにプチパニック担っていた。
簡単な説明
先に原因で書いたように当該のidカラムのデータ型を超えた値を渡してしまったのでエラーになった。
ようはexistsでルールはもちろん裏でSQLを実行しており、その時に「引数で渡された値がカラムの値格納範囲越えてるからエラーね。」と訴えかけてきていた。
よく見たら当該のidはマイグレーションでtinyIncrementsが指定されていた。
このとき自分は「tinyIncrementsならインクリメントだしUNSIGNEDだから最大値255か〜、ってことはidの値を255渡したときはバリデーションエラーで、256渡したときはこのSQLのエラーが返るんだろうな。それで裏付け取れるわ。」って思ってた。甘かった。知らなかったんです。PostgreSQLがTINYINTとUNSIGNEDが無いことを。当然、256をPOSTしてもSQLのエラーではなくバリデーションエラーが返る。またもパニック。
ちょっと調べたらPostgreSQLはTINYINTとUNSIGNEDが無いこと発覚、laravelは頭が良いのでマイグレーションでtinyIncrementsって指定されててもDBのクライアントを判断してSMALLINTでカラムを作ってくれるらしい。
SMALLINTの最大値である32767をIDとしてPOSTしたらバリデーションエラーになり、32768をIDとしてPOSTしたらSQLのエラーが返ってきたので裏付けが取れた。
Q.E.D
これで今夜はスッキリして眠れそう。