discourse/migrations/lib/importer/shared_data.rb
Selase Krakani d304e708be
FIX: Stop silently dropping first two rows during load_mapping (#33076)
Currently, the first two rows returned by `DiscourseDB#query_array` are
silently dropped during the column size check in
`DiscourseDB#load_mapping`. This happens because the rows object, while
an enumerator, isn't fully compliant, it doesn't rewind during
introspection. As a result, calls like `#first`, `#peek`, or `#any?`
advance the iterator.

Ideally, we’d fix this by updating the `query_array` enumeration
implementation. However, customizing the enumerator to be fully
compliant would likely introduce unnecessary perf overhead for all use
cases. So, this fix works around that limitation by building the map a
little differently.
2025-06-09 23:26:59 +02:00

63 lines
1.5 KiB
Ruby

# frozen_string_literal: true
module Migrations::Importer
class SharedData
def initialize(discourse_db)
@discourse_db = discourse_db
end
def load_set(sql)
@discourse_db.query_array(sql).map(&:first).to_set
end
def load_mapping(sql)
rows = @discourse_db.query_array(sql)
# While rows is an enumerator, it's not fully compliant, it does not
# rewind on #first, #peek, #any?, etc.
# So we need to hold on to first_row for use later
first_row = rows.first
return {} if first_row.nil?
has_multiple_values = first_row.size > 2
result =
if has_multiple_values
rows.to_h { |key, *values| [key, values] }
else
rows.to_h
end
result[first_row[0]] = has_multiple_values ? first_row[1..] : first_row[1]
result
end
def load(type)
case type
when :usernames
@existing_usernames_lower ||= load_set <<~SQL
SELECT username_lower
FROM users
SQL
when :group_names
@existing_group_names_lower ||= load_set <<~SQL
SELECT LOWER(name)
FROM groups
SQL
else
raise "Unknown type: #{type}"
end
end
def unload_shared_data(type)
case type
when :usernames
@existing_usernames_lower = nil
when :group_names
@existing_group_names_lower = nil
else
raise "Unknown type: #{type}"
end
end
end
end