MySQLでNOT NULL制約を設定してもNULLでエラーにならない問題(無効データの制約)

f:id:ryoutaku_jo:20190903233417p:plain

【結論】

MySQLは無効または不適切なデータ値を許容しており、これらを有効な値に強制的に変更して、データの整合性を取っている

・例えば、NOT NULL制約を設定している文字列タイプのカラムにNULLを保存しようとした場合、エラーにならず、空文字("")が挿入される。

・NULL値が保存されそうになったら保存を中断したいのであれば、アプリケーション側でバリデーションを設定するか、厳密 SQL モードを有効にする必要がある

【目次】

【本題】

MySQLでNOT NULL制約を設定してもNULL値の保存でエラーにならない

MySQLでは、NOT NULL制約を設定していても、NULL値を保存する際にエラーにならない場合が存在します。

これは無効データの制約による影響です。

無効データの制約について

MySQLは無効または不適切なデータ値を許容しており、これらを有効な値に強制的に変更する。

例えば、NOT NULL制約を設定している文字列タイプのカラム(char型)にNULLを保存しようとした場合、保存は中止されず空文字("")が挿入されます。

また、数値タイプ(int型の)のであれば、数値の0が挿入されます。

もし、NULL値が保存されそうになったら保存を中断したいという意図で設定していたのであれば、その通りの挙動にはなりません。

対策1:アプリケーション側でバリデーションを設定

アプリケーション側のモデルやフロントにNULL値を許容しないバリデーションを設定しておくことで、NULL値の保存が許容されることを防ぐことが出来ます。

対策2:厳密 SQL モードを有効にする

厳密 SQL モードとは、sql_mode 値がSTRICT_TRANS_TABLES または STRICT_ALL_TABLES のいずれかあるいは両方が有効な状態を表します。

STRICT_TRANS_TABLESは、値を指定したとおりにトランザクションテーブルに挿入できない場合、ステートメントを中止します。非トランザクションテーブルの場合、値が単一行ステートメントで発生するか、複数行ステートメントの先頭行で発生した場合、ステートメントを中止します。

STRICT_ALL_TABLESは、カラムに不正な値を挿入したときに、警告ではなくエラーを返します。

SQL モードを実行時に変更するには、SET ステートメントを使用して、グローバルまたはセッションの sql_mode システム変数を設定します。

SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES';
SET SESSION sql_mode = 'STRICT_ALL_TABLES';

GLOBAL 変数を設定するには SUPER 権限が必要で、この設定はその時点以降に接続するすべてのクライアントの動作に影響します。

SESSION 変数を設定すると、現在のクライアントにのみ影響します。

なお、現在のグローバルまたはセッションの sql_mode 値を確認するには、次のステートメントを使用します。

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

参考情報

MySQL :: MySQL 5.6 リファレンスマニュアル :: 1.7.3.3 無効データの制約

NOT NULLなどの制約の設定 - Ruby on Rails入門

《今日の学習進捗(3年以内に10000時間に向けて)》

自動テストの拡充を進める中で、テストは通っているが、意図通りの挙動になっていないシステムテストがいくつか見つかった。

例えば、メールアドレスの入力フォームに不正な値が入力された場合、正常にリダイレクトされるかチェックするテストでは、リダイレクト先の見出しが表示されているかでチェックを行っていた。
しかし、見出しだけでチェックを行うと、メールアドレス以外の箇所でエラーが発生したとしても、テストは通ってしまう。

テストコードが意図通りの挙動になっていなかったとしても、機能に悪影響が出るわけでは無いので、気を付けないと見逃してしまう危険性があると改めて感じた。
意図通りの挙動になっていなければ、せっかくテストコードを拡充しても無意味に終わってしまうので、対策を講じていきたい。
ひとまず、テストコードを実装する際は、実際の画面を触るなどしながら、意図した通りの挙動をテスト出来ているか十分に注意して進めたい。

また、現状モデルやリクエストでも十分テスト出来るが、システムに寄せてしまっている箇所もいくつかある。
当初、開発速度を優先して、システムのみ実装していたことで、役割ごとに分けられていなかった。
このあたりも含め、テストコード全体の設計を見直す必要性があると感じている。

学習開始からの期間 :271日
今日までの合計時間:2546h
一日あたりの平均学習時間:9.4h
今日までに到達すべき目標時間:2475h
目標との解離:71h
「10,000時間」まで、

残り・・・「7454時間!」MySQLのNOT NULL制約のフィールドにNULL値を入れてもエラーにならない