mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 10:54:22 +08:00
45ccadeeeb
Rails 6.1.3.1 deprecates a few API and has some internal changes that break our tests suite, so this commit fixes all the deprecations and errors and now Discourse should be fully compatible with Rails 6.1.3.1. We also have a new release of the rails_failover gem that's compatible with Rails 6.1.3.1.
137 lines
3.8 KiB
Ruby
137 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_dependency "migration/base_dropper"
|
|
|
|
class DbHelper
|
|
|
|
REMAP_SQL ||= <<~SQL
|
|
SELECT table_name::text, column_name::text, character_maximum_length
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'public'
|
|
AND is_updatable = 'YES'
|
|
AND (data_type LIKE 'char%' OR data_type LIKE 'text%')
|
|
ORDER BY table_name, column_name
|
|
SQL
|
|
|
|
TRIGGERS_SQL ||= <<~SQL
|
|
SELECT trigger_name::text
|
|
FROM information_schema.triggers
|
|
WHERE trigger_name LIKE '%_readonly'
|
|
SQL
|
|
|
|
TRUNCATABLE_COLUMNS ||= [
|
|
'topic_links.url'
|
|
]
|
|
|
|
def self.remap(from, to, anchor_left: false, anchor_right: false, excluded_tables: [], verbose: false)
|
|
like = "#{anchor_left ? '' : "%"}#{from}#{anchor_right ? '' : "%"}"
|
|
text_columns = find_text_columns(excluded_tables)
|
|
|
|
text_columns.each do |table, columns|
|
|
set = columns.map do |column|
|
|
replace = "REPLACE(\"#{column[:name]}\", :from, :to)"
|
|
replace = truncate(replace, table, column)
|
|
"\"#{column[:name]}\" = #{replace}"
|
|
end.join(", ")
|
|
|
|
where = columns.map do |column|
|
|
"\"#{column[:name]}\" IS NOT NULL AND \"#{column[:name]}\" LIKE :like"
|
|
end.join(" OR ")
|
|
|
|
rows = DB.exec(<<~SQL, from: from, to: to, like: like)
|
|
UPDATE \"#{table}\"
|
|
SET #{set}
|
|
WHERE #{where}
|
|
SQL
|
|
|
|
puts "#{table}=#{rows}" if verbose && rows > 0
|
|
end
|
|
|
|
finish!
|
|
end
|
|
|
|
def self.regexp_replace(pattern, replacement, flags: "gi", match: "~*", excluded_tables: [], verbose: false)
|
|
text_columns = find_text_columns(excluded_tables)
|
|
|
|
text_columns.each do |table, columns|
|
|
set = columns.map do |column|
|
|
replace = "REGEXP_REPLACE(\"#{column[:name]}\", :pattern, :replacement, :flags)"
|
|
replace = truncate(replace, table, column)
|
|
"\"#{column[:name]}\" = #{replace}"
|
|
end.join(", ")
|
|
|
|
where = columns.map do |column|
|
|
"\"#{column[:name]}\" IS NOT NULL AND \"#{column[:name]}\" #{match} :pattern"
|
|
end.join(" OR ")
|
|
|
|
rows = DB.exec(<<~SQL, pattern: pattern, replacement: replacement, flags: flags, match: match)
|
|
UPDATE \"#{table}\"
|
|
SET #{set}
|
|
WHERE #{where}
|
|
SQL
|
|
|
|
puts "#{table}=#{rows}" if verbose && rows > 0
|
|
end
|
|
|
|
finish!
|
|
end
|
|
|
|
def self.find(needle, anchor_left: false, anchor_right: false, excluded_tables: [])
|
|
found = {}
|
|
like = "#{anchor_left ? '' : "%"}#{needle}#{anchor_right ? '' : "%"}"
|
|
|
|
DB.query(REMAP_SQL).each do |r|
|
|
next if excluded_tables.include?(r.table_name)
|
|
|
|
rows = DB.query(<<~SQL, like: like)
|
|
SELECT \"#{r.column_name}\"
|
|
FROM \"#{r.table_name}\"
|
|
WHERE \""#{r.column_name}"\" LIKE :like
|
|
SQL
|
|
|
|
if rows.size > 0
|
|
found["#{r.table_name}.#{r.column_name}"] = rows.map do |row|
|
|
row.public_send(r.column_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
found
|
|
end
|
|
|
|
private
|
|
|
|
def self.finish!
|
|
SiteSetting.refresh!
|
|
Theme.expire_site_cache!
|
|
SiteIconManager.ensure_optimized!
|
|
ApplicationController.banner_json_cache.clear
|
|
end
|
|
|
|
def self.find_text_columns(excluded_tables)
|
|
triggers = DB.query(TRIGGERS_SQL).map(&:trigger_name).to_set
|
|
text_columns = Hash.new { |h, k| h[k] = [] }
|
|
|
|
DB.query(REMAP_SQL).each do |r|
|
|
next if excluded_tables.include?(r.table_name) ||
|
|
triggers.include?(Migration::BaseDropper.readonly_trigger_name(r.table_name, r.column_name)) ||
|
|
triggers.include?(Migration::BaseDropper.readonly_trigger_name(r.table_name))
|
|
|
|
text_columns[r.table_name] << {
|
|
name: r.column_name,
|
|
max_length: r.character_maximum_length
|
|
}
|
|
end
|
|
|
|
text_columns
|
|
end
|
|
|
|
def self.truncate(sql, table, column)
|
|
if column[:max_length] && TRUNCATABLE_COLUMNS.include?("#{table}.#{column[:name]}")
|
|
"LEFT(#{sql}, #{column[:max_length]})"
|
|
else
|
|
sql
|
|
end
|
|
end
|
|
end
|