canvas-lms/db/migrate/20230222192524_guard_excess...

85 lines
3.1 KiB
Ruby

# frozen_string_literal: true
#
# Copyright (C) 2023 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
class GuardExcessiveUpdates < ActiveRecord::Migration[7.0]
tag :postdeploy
disable_ddl_transaction!
def up
execute(<<~SQL.squish)
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("setting_as_int")}( IN p_setting TEXT ) RETURNS INT4 as $$
DECLARE
v_text text;
v_int8 int8;
BEGIN
v_text := current_setting( p_setting, true );
IF v_text IS NULL THEN
RETURN NULL;
END IF;
IF NOT v_text ~ '^-?[0-9]{1,10}$' THEN
RETURN NULL;
END IF;
v_int8 := v_text::INT8;
IF v_int8 > 2147483647 OR v_int8 < -2147483648 THEN
RETURN NULL;
END IF;
RETURN v_int8::int4;
END;
$$ language plpgsql;
SQL
execute(<<~SQL.squish)
CREATE OR REPLACE FUNCTION #{connection.quote_table_name("guard_excessive_updates")}() RETURNS TRIGGER AS $BODY$
DECLARE
record_count integer;
max_record_count integer;
BEGIN
SELECT count(*) FROM oldtbl INTO record_count;
max_record_count := COALESCE(setting_as_int('inst.max_update_limit.' || TG_TABLE_NAME), setting_as_int('inst.max_update_limit'), '1000');
IF record_count > max_record_count THEN
IF current_setting('inst.max_update_fail', true) IS NOT DISTINCT FROM 'true' THEN
RAISE EXCEPTION 'guard_excessive_updates: % to %.% failed. Would update % records but max is %', TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME, record_count, max_record_count;
ELSE
RAISE WARNING 'guard_excessive_updates: % to %.% was dangerous. Updated % records but threshold is %', TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME, record_count, max_record_count;
END IF;
END IF;
RETURN NULL;
END
$BODY$ LANGUAGE plpgsql;
SQL
set_search_path("guard_excessive_updates")
::ActiveRecord::InternalMetadata[:guard_dangerous_changes_installed] = "true"
ActiveRecord::Base.connection.tables.grep_v(/^_/).each do |table|
add_guard_excessive_updates(table)
end
end
def down
execute("DROP FUNCTION IF EXISTS #{connection.quote_table_name("guard_excessive_updates")}() CASCADE")
execute("DROP FUNCTION IF EXISTS #{connection.quote_table_name("setting_as_int")}( IN p_setting TEXT ) CASCADE")
::ActiveRecord::InternalMetadata[:guard_dangerous_changes_installed] = "false"
end
end