PostgreSQL アダプターのUUID正規表現で末尾ダッシュを拒否するよう修正

rails/rails

PostgreSQL アダプターの ACCEPTABLE_UUID 正規表現に存在したバグを修正し、末尾にダッシュを持つ不正なUUID文字列を正しく拒否するようになりました。これにより、不正なUUIDで find を呼び出した際に ActiveRecord::RecordNotFound が適切に発生します。

背景

#49934 は、PostgreSQL が受け入れる多様なUUIDフォーマット(波括弧付き・大文字小文字・ダッシュの有無)を #changed? が正しく扱えるよう、ACCEPTABLE_UUID 正規表現を拡張した変更です。しかし、この変更が意図せず末尾ダッシュを持つUUIDを受け入れてしまう挙動を引き起こしていました。

その結果、次のようなコードが ActiveRecord::RecordNotFound を発生させずに動作していました。

User.find('a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11-') # 末尾に余分なダッシュ

Active Record は内部でUUIDを正規化する際に末尾ダッシュをサイレントに除去しており、不正なUUID文字列が有効なUUIDとして扱われていました。PostgreSQL の公式ドキュメントが定義するUUID形式とは異なる入力が透過的に受け入れられていたことが問題の核心です。

技術的な変更

ACCEPTABLE_UUID 正規表現を1行修正し、UUIDの各4文字グループがダッシュを「前置できる」構造に変えることで末尾ダッシュを排除しました。

変更前:

ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}

変更後:

ACCEPTABLE_UUID = %r{\A(\{)?[a-fA-F0-9]{4}(-?[a-fA-F0-9]{4}){7}(?(1)\}|)\z}

変更前の正規表現は ([a-fA-F0-9]{4}-?){8} というパターンで「4桁英数字、その後にオプションのダッシュ」を8回繰り返す構造でした。この構造では最後のグループもダッシュを後置できるため、32桁の英数字の後にダッシュが来るケース(...0a11-)が許容されていました。変更後は最初の4桁グループを固定し、残り7グループを (-?[a-fA-F0-9]{4}) で「オプションのダッシュを前置した4桁英数字」として繰り返すことで、ダッシュが必ず英数字の前にしか置けない構造になっています。

テストには末尾ダッシュのケースが追加されました。

"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11-"].each do |invalid_uuid|
  uuid = UUIDType.new guid: invalid_uuid
  assert_nil uuid.guid
end

設計判断

既存の正規表現パターンを再構築する方式が採用されました。末尾ダッシュを禁止する方法としては末尾に [^-] の先読み否定を加える手もありますが、本PRでは繰り返しグループの構造そのものを見直し、「ダッシュは英数字グループの前置のみ許容」という意味的に正確なモデルで表現しています。これにより、パターンが受け入れるUUID形式の構造が正規表現から直接読み取れるようになっています。

CANONICAL_UUID(標準的なハイフン区切り形式のみを受け入れる正規表現)は変更されていません。ACCEPTABLE_UUID はPostgreSQLが許容する柔軟な入力形式に対応するためのものであり、その柔軟さを維持しつつ明示的に不正な形式だけを排除する最小限の修正となっています。

まとめ

本PRは、#49934 で導入されたUUID形式の柔軟な受け入れ処理が引き起こした末尾ダッシュの見逃しを、正規表現の構造的な修正で解消しました。不正な入力のサイレントな正規化を防ぐことで、find の引数バリデーションが仕様通りに機能するようになります。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
f3b21a1e

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術・設計(各論)→まとめ(結論)の3部構成が明確で、理想的な構成です。特に任意項目である「設計判断」セクションが含まれており、記事の価値を高めています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト、PR番号のリンク記法など、すべてのカスタムMarkdown構文が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Active Recordの内部実装や正規表現に関する内容であり、専門知識を持つエンジニアという対象読者に完全に適合しています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論で構成され、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、非常に読みやすいです。

Diff内容との照合 ⚠ WARNING

コードブロックとDiff内容の一致

テストコードの引用が構文的に不完全です。追加されたテストケースの主旨は伝わりますが、配列の開始ブラケットが欠けており、そのままでは実行できないコードになっています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

ACCEPTABLE_UUID, ActiveRecord::RecordNotFoundなど、RailsとPostgreSQLに関連する技術用語が正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

正規表現の変更がなぜ末尾ダッシュを拒否するのかについての解説は、技術的に正確かつ論理的で、非常に分かりやすいです。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

「設計判断」セクションはPRに明記されていないものの、コードの変更内容から論理的に導出できる技術的な解説であり、創作や憶測(ハルシネーション)には該当しません。その他の主張もすべてPR情報で裏付けられています。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#57136, #49934)やその他の固有名詞はすべて正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルは、PRのタイトル「Reject trailing dash in PostgreSQL adapter」の内容を日本語で正確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PRで言及されていないバージョンのサポート状況やリリース日程などの外部知識は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「挙動を引き起こしていました」など、過去の事象と今回の修正という時間的な前後関係が正しく表現されています。