mirror of
https://github.com/discourse/discourse.git
synced 2024-12-16 08:33:45 +08:00
6a3c8fe69c
Often we need to amend our schema, it is tempting to use drop_table, rename_column and drop_column to amned schema trouble though is that existing code that is running in production can depend on the existance of previous schema leading to application breaking until new code base is deployed. The commit enforces new rules to ensure we can never drop tables or columns in migrations and instead use Migration::ColumnDropper and Migration::TableDropper to defer drop the db objects
65 lines
2.0 KiB
Ruby
65 lines
2.0 KiB
Ruby
require_dependency 'migration/base_dropper'
|
|
|
|
module Migration
|
|
class ColumnDropper < BaseDropper
|
|
def self.drop(table:, after_migration:, columns:, delay: nil, on_drop: nil)
|
|
validate_table_name(table)
|
|
columns.each { |column| validate_column_name(column) }
|
|
|
|
ColumnDropper.new(table, columns, after_migration, delay, on_drop).delayed_drop
|
|
end
|
|
|
|
def self.mark_readonly(table_name, column_name)
|
|
create_readonly_function(table_name, column_name)
|
|
|
|
ActiveRecord::Base.exec_sql <<~SQL
|
|
CREATE TRIGGER #{readonly_trigger_name(table_name, column_name)}
|
|
BEFORE INSERT OR UPDATE OF #{column_name}
|
|
ON #{table_name}
|
|
FOR EACH ROW
|
|
WHEN (NEW.#{column_name} IS NOT NULL)
|
|
EXECUTE PROCEDURE #{readonly_function_name(table_name, column_name)};
|
|
SQL
|
|
end
|
|
|
|
private
|
|
|
|
def initialize(table, columns, after_migration, delay, on_drop)
|
|
super(after_migration, delay, on_drop)
|
|
|
|
@table = table
|
|
@columns = columns
|
|
end
|
|
|
|
def droppable?
|
|
builder = SqlBuilder.new(<<~SQL)
|
|
SELECT 1
|
|
FROM INFORMATION_SCHEMA.COLUMNS
|
|
/*where*/
|
|
LIMIT 1
|
|
SQL
|
|
|
|
builder.where("table_schema = 'public'")
|
|
.where("table_name = :table")
|
|
.where("column_name IN (:columns)")
|
|
.where(previous_migration_done)
|
|
.exec(table: @table,
|
|
columns: @columns,
|
|
delay: "#{@delay} seconds",
|
|
after_migration: @after_migration).to_a.length > 0
|
|
end
|
|
|
|
def execute_drop!
|
|
@columns.each do |column|
|
|
ActiveRecord::Base.exec_sql <<~SQL
|
|
DROP TRIGGER IF EXISTS #{BaseDropper.readonly_trigger_name(@table, column)} ON #{@table};
|
|
DROP FUNCTION IF EXISTS #{BaseDropper.readonly_function_name(@table, column)} CASCADE;
|
|
SQL
|
|
|
|
# safe cause it is protected on method entry, can not be passed in params
|
|
ActiveRecord::Base.exec_sql("ALTER TABLE #{@table} DROP COLUMN IF EXISTS #{column}")
|
|
end
|
|
end
|
|
end
|
|
end
|