- Add page_size helper method to read from X-Per-Page header - Set min (1) and max (100) bounds for page sizes - Update all paginated endpoints to use dynamic page size - Maintain backward compatibility with default sizes
86 lines
3.1 KiB
Ruby
86 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class ConvertGameRawColumnsToJsonb < ActiveRecord::Migration[8.0]
|
|
def up
|
|
tables = %w[characters summons weapons]
|
|
|
|
tables.each do |table|
|
|
# Create a backup of game_raw_en to prevent data loss
|
|
add_column table, :game_raw_en_backup, :text
|
|
execute("UPDATE #{table} SET game_raw_en_backup = game_raw_en WHERE game_raw_en IS NOT NULL")
|
|
|
|
# Verify backup integrity
|
|
backup_validation = execute(<<~SQL).first
|
|
SELECT COUNT(*) AS missing_backups
|
|
FROM #{table}
|
|
WHERE game_raw_en IS NOT NULL#{' '}
|
|
AND game_raw_en_backup IS NULL
|
|
SQL
|
|
|
|
if backup_validation['missing_backups'].to_i.positive?
|
|
raise ActiveRecord::MigrationError, "Backup failed for #{table}. Aborting migration."
|
|
end
|
|
|
|
# Convert game_raw_en with data validation
|
|
begin
|
|
execute("ALTER TABLE #{table} ALTER COLUMN game_raw_en TYPE JSONB USING game_raw_en::JSONB")
|
|
rescue StandardError => e
|
|
# Find and report problematic rows
|
|
create_invalid_rows_table(table)
|
|
invalid_count = execute("SELECT COUNT(*) FROM invalid_#{table}_rows").first['count']
|
|
|
|
raise ActiveRecord::MigrationError, <<~ERROR
|
|
Failed to convert game_raw_en in #{table} to JSONB.
|
|
#{invalid_count} rows contain invalid JSON.
|
|
Original error: #{e.message}
|
|
See temporary table invalid_#{table}_rows for details.
|
|
ERROR
|
|
end
|
|
|
|
# Simply convert game_raw_jp (empty column)
|
|
execute("ALTER TABLE #{table} ALTER COLUMN game_raw_jp TYPE JSONB USING COALESCE(game_raw_jp::JSONB, 'null'::JSONB)")
|
|
|
|
# Add comment to indicate column purpose
|
|
execute("COMMENT ON COLUMN #{table}.game_raw_en IS 'JSON data from game (English)'")
|
|
execute("COMMENT ON COLUMN #{table}.game_raw_jp IS 'JSON data from game (Japanese)'")
|
|
end
|
|
|
|
# Leave a note about backup columns in migration output
|
|
say 'Migration successful. Backup columns (game_raw_en_backup) remain for verification.'
|
|
say 'Run a separate migration to remove backup columns after verification.'
|
|
end
|
|
|
|
def down
|
|
tables = %w[characters summons weapons]
|
|
|
|
tables.each do |table|
|
|
# Check if we can restore from backup
|
|
if column_exists?(table, :game_raw_en_backup)
|
|
say "Restoring #{table}.game_raw_en from backup..."
|
|
execute("UPDATE #{table} SET game_raw_en = game_raw_en_backup WHERE game_raw_en_backup IS NOT NULL")
|
|
remove_column table, :game_raw_en_backup
|
|
end
|
|
|
|
# Convert both columns back to TEXT
|
|
execute("ALTER TABLE #{table} ALTER COLUMN game_raw_en TYPE TEXT")
|
|
execute("ALTER TABLE #{table} ALTER COLUMN game_raw_jp TYPE TEXT")
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def create_invalid_rows_table(table)
|
|
execute(<<~SQL)
|
|
CREATE TEMPORARY TABLE invalid_#{table}_rows AS
|
|
SELECT id, game_raw_en#{' '}
|
|
FROM #{table}
|
|
WHERE game_raw_en IS NOT NULL
|
|
AND pg_typeof(game_raw_en) = 'text'::regtype
|
|
AND (
|
|
TRIM(game_raw_en) = ''#{' '}
|
|
OR#{' '}
|
|
(game_raw_en::JSONB) IS NULL
|
|
);
|
|
SQL
|
|
end
|
|
end
|