`reset_column_sequences!` が quoted スキーマのテーブルでクラッシュする不具合を修正
reset_column_sequences! がスキーマ名を引用符で囲んだテーブルに対して呼び出された際、NoMethodError が発生していました。本稿では原因と修正内容、設計上の判断を解説します。
背景
reset_column_sequences! は PostgreSQL アダプタでシリアル/UUID 列のシーケンスをテーブル内容に合わせてリセットする内部ロジックです。
Quoted schema(例: "App".widgets)のテーブルを対象にすると、内部で生成された @tables マップのキーと実際に取得したテーブル名が一致せず、nil.column = … が実行されて NoMethodError がスローされました。
根本原因は SequenceReset が regclass::text から得たスキーマ修飾名を delete_prefix('"').delete_suffix('"') で単純にアンコートしていた点です。この手法は "App".widgets を App".widgets に変換し、マップキー App.widgets と食い違っていました。既存テストは小文字スキーマしか扱わなかったため、問題は顕在化していませんでした。
技術的な変更
SequenceReset の初期化 でテーブル名を格納する際、Utils.extract_schema_qualified_name を用いて正規化した文字列をキーにしました。
@tables = tables.to_h { |table, data| [Utils.extract_schema_qualified_name(table.to_s).to_s, Data.new(*data)] }
シーケンスのバックフィル 部分でも同様に正規化を適用し、delete_prefix 系の処理を排除しました。
primary_key_column_and_sequences.rows.each do |table, column, namespace, sequence|
table = Utils.extract_schema_qualified_name(table).to_s
@tables[table].column = column
@tables[table].sequence = PostgreSQL::Name.new(namespace, sequence) if sequence
end
UUID ループでも同様の変更を行い、両方のパスで一貫した名前処理を実現しています。これにより quoted スキーマでも @tables のキーが正しくマッチし、reset_column_sequences! が期待通りにシーケンスをリセットします。
テスト追加 により修正を検証しました。新しいテスト test_reset_column_sequences_with_quoted_schema は、"Test_CamelSchema".widgets に対してシーケンスが 100 以上に進むことを確認します。
def test_reset_column_sequences_with_quoted_schema
@connection.execute('CREATE SCHEMA "Test_CamelSchema"')
@connection.execute('CREATE TABLE "Test_CamelSchema".widgets (id serial primary key)')
@connection.execute('INSERT INTO "Test_CamelSchema".widgets (id) VALUES (100)')
@connection.reset_column_sequences!([['"Test_CamelSchema".widgets']])
next_id = @connection.select_value(%Q{SELECT nextval(pg_get_serial_sequence('"Test_CamelSchema".widgets', 'id'))})
assert_operator next_id.to_i, :>, 100
ensure
@connection.execute('DROP SCHEMA IF EXISTS "Test_CamelSchema" CASCADE')
end
設計判断
修正では 既存の Utils.unquote_identifier を置き換えるのではなく、汎用的に利用可能な Utils.extract_schema_qualified_name を統一的に適用する方針を採りました。これにより、現在のコードベースで他の箇所が期待通りに動作しているかを追加調査する必要がなく、最小限のリスクで問題を解消できます。
同じ根本原因が別の箇所(foreign_keys の to_table 取得)でも顕在化しており、そちらは別 PR #57561 で対処されています。今回の変更はシーケンスリセットロジックに限定し、他の機能への副作用を回避する点が設計上の重要な判断です。
まとめ
reset_column_sequences! が quoted スキーマのテーブルでクラッシュしていた原因は不適切な名前アンコートにあり、Utils.extract_schema_qualified_name を用いた正規化で根本的に解決しました。テストの追加により安全性が検証され、既存の非 quoted ケースへの影響はありません。