From 696bd0bf0522eb15a21e6ea3dd63823e2813eb5f Mon Sep 17 00:00:00 2001 From: Andrei Prigorshnev Date: Fri, 9 Jul 2021 20:04:28 +0400 Subject: [PATCH] DEV: add a script for moving timestamps in database (#13682) We're going to use this script for updating timestamps on Try, but it can be used with a local database during development as well. Usage: Commands: ruby db_timestamp_updater.rb yesterday move all timestamps by x days so that will be moved to yesterday ruby db_timestamp_updater.rb 100 move all timestamps forward by 100 days ruby db_timestamp_updater.rb -100 move all timestamps backward by 100 days The script moves all timestamps in the database by the same amount of days forward or backward. No need to change the script if we add a new column in the future. The more simple solution would be just to move timestamps in several tables (topics, posts, and so on). I didn't want to go that way because it could generate additional work in the future. For example, if we add a new column with a timestamp and users can see that timestamp we'd need to add that column to the script. Or, for example, if we move a post's timestamp to the future but forget to move a timestamp of topic timer or user action it can cause weird bugs. --- script/db_timestamps_mover.rb | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 script/db_timestamps_mover.rb diff --git a/script/db_timestamps_mover.rb b/script/db_timestamps_mover.rb new file mode 100644 index 00000000000..b1afef25c43 --- /dev/null +++ b/script/db_timestamps_mover.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true +require "pg" + +usage = <<-END +Commands: + ruby db_timestamp_updater.rb yesterday move all timestamps by x days so that will be moved to yesterday + ruby db_timestamp_updater.rb 100 move all timestamps forward by 100 days + ruby db_timestamp_updater.rb -100 move all timestamps backward by 100 days +END + +class TimestampsUpdater + TABLE_SCHEMA = 'public' + + def initialize + @raw_connection = PG.connect( + host: ENV['DISCOURSE_DB_HOST'] || 'localhost', + port: ENV['DISCOURSE_DB_PORT'] || 5432, + dbname: ENV['DISCOURSE_DB_NAME'] || 'discourse_development', + user: ENV['DISCOURSE_DB_USERNAME'] || 'postgres', + password: ENV['DISCOURSE_DB_PASSWORD'] || '') + end + + def move_by(days) + postgresql_date_types = [ + "timestamp without time zone", + "timestamp with time zone", + "date" + ] + + postgresql_date_types.each do |data_type| + columns = all_columns_of_type(data_type) + columns.each do |c| + table = c["table_name"] + column = c["column_name"] + move_timestamps table, column, days + end + end + end + + def move_to_yesterday(date) + days = (Date.today.prev_day - date).to_i + move_by days + end + + private + + def all_columns_of_type(data_type) + sql = <<~SQL + SELECT c.column_name, c.table_name + FROM information_schema.columns AS c + JOIN information_schema.tables AS t + ON c.table_name = t.table_name + WHERE c.table_schema = '#{TABLE_SCHEMA}' + AND c.data_type = '#{data_type}' + AND t.table_type = 'BASE TABLE' + SQL + @raw_connection.exec(sql) + end + + def move_timestamps(table_name, column_name, days) + operator = days < 0 ? "-" : "+" + sql = <<~SQL + UPDATE #{table_name} + SET #{column_name} = #{column_name} #{operator} INTERVAL '#{days.abs} day' + SQL + @raw_connection.exec(sql) + end +end + +def is_i?(string) + true if Integer(string) rescue false +end + +def is_date?(string) + true if Date.parse(string) rescue false +end + +if ARGV.length == 2 && ARGV[0] == "yesterday" && is_date?(ARGV[1]) + date = Date.parse(ARGV[1]) + TimestampsUpdater.new.move_to_yesterday date +elsif ARGV.length == 1 && is_i?(ARGV[0]) + days = ARGV[0].to_i + TimestampsUpdater.new.move_by days +else + puts usage + exit 1 +end