DEV: Correct historical schema discrepancies (#9955)

Rails schema defaults have changed over the years. This migration will correct discrepancies, and make all databases match the modern schema.
This commit is contained in:
David Taylor 2020-06-08 12:20:27 +01:00 committed by GitHub
parent 833b5d89e0
commit f7d676dce1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 249 additions and 0 deletions

View File

@ -0,0 +1,249 @@
# frozen_string_literal: true
class CorrectSchemaDiscrepancies < ActiveRecord::Migration[6.0]
disable_ddl_transaction!
def up
timestamp_columns = %w{
category_tag_groups.created_at
category_tag_groups.updated_at
category_tags.created_at
category_tags.updated_at
child_themes.created_at
child_themes.updated_at
embeddable_hosts.created_at
embeddable_hosts.updated_at
group_archived_messages.created_at
group_archived_messages.updated_at
group_mentions.created_at
group_mentions.updated_at
muted_users.created_at
muted_users.updated_at
permalinks.created_at
permalinks.updated_at
post_stats.created_at
post_stats.updated_at
remote_themes.created_at
remote_themes.updated_at
stylesheet_cache.created_at
stylesheet_cache.updated_at
tag_group_memberships.created_at
tag_group_memberships.updated_at
tag_groups.created_at
tag_groups.updated_at
tag_users.created_at
tag_users.updated_at
tags.created_at
tags.updated_at
theme_fields.created_at
theme_fields.updated_at
topic_tags.created_at
topic_tags.updated_at
topic_timers.created_at
topic_timers.updated_at
unsubscribe_keys.created_at
unsubscribe_keys.updated_at
user_api_keys.created_at
user_api_keys.updated_at
user_archived_messages.created_at
user_archived_messages.updated_at
user_auth_tokens.created_at
user_auth_tokens.updated_at
user_emails.created_at
user_emails.updated_at
user_exports.created_at
user_exports.updated_at
user_field_options.created_at
user_field_options.updated_at
user_fields.created_at
user_fields.updated_at
user_warnings.created_at
user_warnings.updated_at
watched_words.created_at
watched_words.updated_at
web_hook_events.created_at
web_hook_events.updated_at
web_hooks.created_at
web_hooks.updated_at
}
char_limit_columns = %w{
badge_groupings.name
badge_posts.action_code
badge_posts.edit_reason
badge_types.name
badges.icon
badges.name
categories.email_in
categories.slug
color_scheme_colors.hex
color_scheme_colors.name
color_schemes.name
draft_sequences.draft_key
drafts.draft_key
email_logs.email_type
email_logs.to_address
email_tokens.email
email_tokens.token
embeddable_hosts.host
github_user_infos.screen_name
groups.name
groups.title
invites.email
message_bus.context
message_bus.name
oauth2_user_infos.email
oauth2_user_infos.name
oauth2_user_infos.provider
oauth2_user_infos.uid
optimized_images.url
plugin_store_rows.key
plugin_store_rows.plugin_name
plugin_store_rows.type_name
post_details.key
post_details.value
post_search_data.locale
posts.action_code
posts.edit_reason
schema_migrations.version
screened_emails.email
screened_urls.domain
screened_urls.url
single_sign_on_records.external_email
single_sign_on_records.external_id
single_sign_on_records.external_name
single_sign_on_records.external_username
site_settings.name
stylesheet_cache.digest
stylesheet_cache.target
themes.name
topic_links.title
topic_search_data.locale
topics.archetype
topics.slug
topics.subtype
topics.title
uploads.original_filename
uploads.url
user_exports.file_name
user_field_options.value
user_fields.description
user_fields.field_type
user_fields.name
user_histories.context
user_histories.custom_type
user_histories.email
user_histories.ip_address
user_open_ids.email
user_open_ids.url
user_profiles.location
user_profiles.website
users.name
users.title
}
float_default_columns = %w{
top_topics.all_score
top_topics.daily_score
top_topics.monthly_score
top_topics.weekly_score
top_topics.yearly_score
}
other_default_columns = %w{
categories.color
topic_search_data.topic_id
}
lookup_sql = (
timestamp_columns +
char_limit_columns +
float_default_columns +
other_default_columns
).map do |ref|
table, column = ref.split(".")
"(table_name='#{table}' AND column_name='#{column}')"
end.join(" OR ")
raw_info = DB.query_hash <<~SQL
SELECT table_name, column_name, is_nullable, character_maximum_length, column_default
FROM information_schema.columns
WHERE table_schema='public'
AND (
#{lookup_sql}
)
SQL
schema_hash = {}
raw_info.each do |row|
schema_hash["#{row["table_name"]}.#{row["column_name"]}"] = row
end
# In the past, rails changed the default behavior for timestamp columns
# This only affects older discourse installations
# This migration will make old database schemas match modern behavior
timestamp_columns.each do |ref|
current_value = schema_hash[ref]["is_nullable"]
next if current_value == "NO"
table, column = ref.split(".")
# There shouldn't be any null values - rails inserts timestamps automatically
# But just in case, set them to now() if there are any nulls
DB.exec <<~SQL
UPDATE #{table} SET #{column} = now() WHERE #{column} IS NULL
SQL
DB.exec <<~SQL
ALTER TABLE #{table} ALTER COLUMN #{column} SET NOT NULL
SQL
end
# In the past, rails changed the default behavior for varchar columns
# This only affects older discourse installations
# This migration removes the character limits from columns, so that they match modern behavior
char_limit_columns.each do |ref|
current_value = schema_hash[ref]["character_maximum_length"]
next if current_value == nil
table, column = ref.split(".")
DB.exec <<~SQL
ALTER TABLE #{table} ALTER COLUMN #{column} TYPE varchar
SQL
end
# In the past, rails changed the default behavior for float columns
# This only affects older discourse installations
# This migration updates the default values, so that they match modern behavior
float_default_columns.each do |ref|
current_value = schema_hash[ref]["column_default"]
next if current_value == "0.0"
table, column = ref.split(".")
DB.exec <<~SQL
ALTER TABLE #{table} ALTER COLUMN #{column} SET DEFAULT 0.0
SQL
end
# Category color default was changed in https://github.com/discourse/discourse/commit/faf09bb8c80fcb28b132a5a644ac689cc9abffc2
# But should have been added in a new migration
if schema_hash["categories.color"]["column_default"] != "'0088CC'::character varying"
DB.exec <<~SQL
ALTER TABLE categories ALTER COLUMN color SET DEFAULT '0088CC'
SQL
end
# Older sites have a default value like nextval('topic_search_data_topic_id_seq'::regclass)
# Modern sites do not. This is likely caused by another historical change in rails
if schema_hash["topic_search_data.topic_id"]["column_default"] != nil
DB.exec <<~SQL
ALTER TABLE topic_search_data ALTER COLUMN topic_id SET DEFAULT NULL
SQL
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end