誤解された CASCADE 1 つで、本番データが数秒で消し飛ぶことがあります。このキーワードは無害に見えますが、スキーマ全体にまたがる削除を引き起こせます。
ルールの話に入る前に、何から守ろうとしているのかをはっきりさせておきましょう。Bytebase の SQL レビュー には、たった 1 つの目的のために用意されたルールが 2 つあります。CASCADE が、頼んだ以上のものを静かに巻き込んで道連れにするのを止めることです。
-
ON DELETE 句に CASCADE オプションを使うことを禁止する:
ON DELETEのCASCADEオプションは、依存オブジェクトの大量削除や変更を引き起こし、想定外の結果につながる。 -
テーブル削除時に CASCADE を使うことを禁止する: テーブル削除時の
CASCADEオプションは、依存オブジェクトの大量削除や変更を引き起こし、想定外の結果につながる。
CASCADE とは
SQL における CASCADE は 2 つの文脈で現れます。そして人がやけどを負うのは 2 つ目のほうです。
外部キー制約での CASCADE
CREATE TABLE posts (
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE -- Deleting a user deletes all their posts
);TRUNCATE 操作での CASCADE
TRUNCATE TABLE users CASCADE; -- Truncates users AND all tables referencing it補足: TRUNCATE ... CASCADE は PostgreSQL 固有です。外部キーで依存しているテーブルを自動的に truncate するこの挙動は、MySQL、SQL Server などの他データベースには存在しません。Oracle にも TRUNCATE ... CASCADE はありますが、目的は別 (マテリアライズドビュー) です。
これが危険なのは、まさに多くの開発者が「自分はこれが何をするか分かっている」と思い込んでいるからです。
よくある誤解
あるチームがデータベースを移行していて、いくつかの新テーブルからテストデータをクリーンアップしたい、という場面を想像してください。開発者は次を実行します。
TRUNCATE TABLE new_feature_table CASCADE;意図: 新テーブルからテストデータを削除する。
仮定: CASCADE は他テーブルの関連テストデータだけを消すはず。
実際に起きたこと: TRUNCATE ... CASCADE は new_feature_table へ外部キーを向けているすべてのテーブルの全データを削除しました。数年分の本番データも含めてです。
ここが肝心です。TRUNCATE TABLE A CASCADE は、B テーブルの関連行を消すだけではありません。A を指す外部キーを持つテーブルまるごとを truncate します。テーブル同士が相互参照で複雑に絡み合ったスキーマでは、たった 1 つの CASCADE がデータベース全体を空にしかねません。糸を 1 本引いたら、セーター全体がほどけていくのを眺めるようなものです。ただし、そのセーターはあなたの本番データなのですが。
データベースを守る方法
1. 明示的な DELETE 文を使う
-- ❌ DANGEROUS: CASCADE affects unexpected tables
TRUNCATE TABLE users CASCADE;
-- ✅ SAFE: Full control over what's deleted
DELETE FROM user_sessions WHERE user_id IN (SELECT id FROM users WHERE ...);
DELETE FROM users WHERE ...;2. 自動 SQL レビューを導入する
Bytebase を使って、実行前にすべての SQL 文をスキャンし、本番での危険な CASCADE 操作をブロックしましょう。
3. 危険な権限を取り上げる
REVOKE TRUNCATE ON ALL TABLES IN SCHEMA public FROM app_user;4. マイグレーションを徹底的にテストする
本番ライクなデータを入れたステージングでフルマイグレーションを実行し、影響範囲を確認し、本番に触れる前にロールバックをテストしましょう。
5. より安全な外部キーオプションを使う
ON DELETE CASCADE の代わりに、ON DELETE RESTRICT や ON DELETE SET NULL を検討しましょう。
まとめ
CASCADE はデータベース全体にまたがる自動削除を引き起こせます。覚えておくべき事実はただ 1 つ。TRUNCATE TABLE A CASCADE は、A を指す外部キーを持つすべてのテーブルを truncate するのであって、関連行だけではない、ということです。
データベースを守る:
- 明示的な DELETE 文を使い、データ削除を完全に制御する
- Bytebase の SQL レビュールールで本番の CASCADE をブロックする
- アプリケーションユーザーから TRUNCATE 権限を取り上げる
- スキーマ変更は必ずステージング環境でテストする
CASCADE は便利です。便利でなくなるその日までは。被害範囲が本番データに及ぶなら、便利さに見合う価値などありません。