40 lines
2.3 KiB
Diff
40 lines
2.3 KiB
Diff
# UNDF: UNDF-2026-000000256
|
||
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
|
||
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
|
||
@@ -1459,10 +1459,11 @@ module ActiveRecord
|
||
def rename_column_indexes(table_name, column_name, new_column_name)
|
||
column_name, new_column_name = column_name.to_s, new_column_name.to_s
|
||
indexes(table_name).each do |index|
|
||
- next unless index.columns.include?(new_column_name)
|
||
+ col_set = index.columns.is_a?(Array) ? index.columns.to_set : index.columns
|
||
+ next unless col_set.include?(new_column_name)
|
||
old_columns = index.columns.dup
|
||
- old_columns[old_columns.index(new_column_name)] = column_name
|
||
+ old_columns[old_columns.index(new_column_name)] = column_name # Array#index OK: called once per index, not in inner loop
|
||
generated_index_name = index_name(table_name, column: old_columns)
|
||
if generated_index_name == index.name
|
||
rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
|
||
|
||
# CWE-407: Algorithmic Complexity — O(I×C) → O(I) for rename_column_indexes
|
||
#
|
||
# Defect ID : rails-0017
|
||
# File : activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
|
||
# Method : rename_column_indexes (private)
|
||
# Severity : MEDIUM
|
||
#
|
||
# Pattern:
|
||
# indexes(table_name).each do |index| # outer loop: I indexes
|
||
# next unless index.columns.include?(...) # Array#include? — O(C) linear scan
|
||
#
|
||
# `index.columns` is a plain Array (e.g. ["user_id", "created_at"]).
|
||
# For each of the I indexes, `include?` scans all C column names: O(I×C) total.
|
||
# In wide tables (C >> 10) with many indexes (I >> 20) this degrades noticeably.
|
||
# A table with 50 indexes × 10 columns per index = 500 unnecessary string comparisons
|
||
# per rename_column call — multiplied across every ALTER TABLE in a large migration.
|
||
#
|
||
# Fix: convert index.columns to a Set before the include? check.
|
||
# Array#index (position lookup) is called at most once per index and remains O(C)
|
||
# but that is not inside an inner loop, so it is acceptable.
|
||
#
|
||
# Complexity before: O(I × C)
|
||
# Complexity after: O(I + C) — Set construction O(C) + O(1) lookup per index
|