PostgreSQLのスキーマ修飾テーブルに対するインデックスコメントのバグ修正
スキーマ修飾テーブル(例: "other_schema.widgets")に comment: オプション付きで add_index を呼び出すと、COMMENT ON INDEX 文のインデックス名がスキーマ修飾されず PG::UndefinedTable エラーが発生する問題が修正されました。
背景
PostgreSQLでは、インデックスコメントのSQL文においてインデックス名をスキーマ修飾名で参照する必要があります。other_schema 上に作成されたインデックスは "other_schema"."index_widgets_on_id" と指定しなければならず、"index_widgets_on_id" だけでは対象のリレーションが見つかりません。
#56581 で報告されたとおり、以下の手順で問題を再現できます。
ActiveRecord::Base.connection.execute("CREATE SCHEMA other_schema")
ActiveRecord::Base.connection.create_table("other_schema.widgets")
ActiveRecord::Base.connection.add_index(
"other_schema.widgets",
:id,
comment: "test comment"
)
Active Record はインデックス自体は正しく other_schema 上に作成するものの、続く COMMENT ON INDEX 文では非修飾名を使用していたため、以下のエラーが発生していました。
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "index_widgets_on_id" does not exist
この不一致は、change_index_comment_sql がインデックス名だけを受け取り、テーブル名(=スキーマ情報)を持たない設計に起因していました。
技術的な変更
change_index_comment_sql メソッドのシグネチャに table_name 引数が追加され、PostgreSQL実装側でスキーマ修飾名を生成するロジックが加わりました。
抽象基底クラス schema_statements.rb の2か所で table_name を渡すよう変更されています。1つは create_table 内でインデックスコメントを発行する箇所、もう1つは add_index 後にコメントを発行する箇所です。
変更前:
statements << change_index_comment_sql(index_definition.index) if index_definition.index.comment.present?
# ...
def change_index_comment_sql(index)
raise NotImplementedError
end
変更後:
statements << change_index_comment_sql(index_definition.index, table_name) if index_definition.index.comment.present?
# ...
def change_index_comment_sql(index, table_name)
raise NotImplementedError
end
PostgreSQL固有の実装では、Utils.extract_schema_qualified_name でテーブル名からスキーマ部分を抽出し、スキーマが存在する場合は PostgreSQL::Name.new(name.schema, index.name) でスキーマ修飾インデックス名を組み立てます。
変更前:
def change_index_comment_sql(index)
"COMMENT ON INDEX #{quote_column_name(index.name)} IS #{quote(index.comment)}"
end
変更後:
def change_index_comment_sql(index, table_name)
name = Utils.extract_schema_qualified_name(table_name.to_s)
index_name = name.schema ? PostgreSQL::Name.new(name.schema, index.name).to_s : index.name
"COMMENT ON INDEX #{quote_table_name(index_name)} IS #{quote(index.comment)}"
end
name.schema が nil の場合(スキーマ修飾なしのテーブル名)は従来どおり非修飾の index.name を使うため、既存の動作との後方互換性は維持されます。また、引用処理が quote_column_name から quote_table_name に変更されており、"schema"."index_name" のようなドット区切り名を正しくクォートできます。
テストとして test_index_with_comment_on_table_with_schema が追加され、second_schema.posts 上のインデックスにコメントが正しく設定されることを検証しています。
設計判断
スキーマ情報の取得に既存の Utils.extract_schema_qualified_name ユーティリティが再利用されています。add_index や create_table でテーブル名のスキーマ解析に使われているのと同じ仕組みであり、新たなパース処理を追加せずに一貫性を保てます。
change_index_comment_sql を抽象メソッドとして table_name を受け取る設計に変更したことで、PostgreSQL以外のアダプターが同メソッドをオーバーライドする際も同じインターフェースに従うことになります。これにより、将来的に他のアダプターがスキーマ修飾の考慮を必要とする場合にも対応しやすくなっています。
まとめ
本PRは、change_index_comment_sql にテーブル名を渡すという最小限の変更で、インデックス作成とコメント付与のSQL文に一貫したスキーマ修飾名が使われるよう修正しています。スキーマ修飾テーブルを利用するPostgreSQLアプリケーションでは、add_index の comment: オプションが期待どおり動作するようになります。