Module: PgHaMigrations::UnsafeStatements
- Defined in:
- lib/pg_ha_migrations/unsafe_statements.rb
Class Method Summary collapse
- .delegate_raw_method_to_migration_base_class(method_name) ⇒ Object
- .delegate_unsafe_method_to_migration_base_class(method_name, with_lock: true) ⇒ Object
- .disable_or_delegate_default_method(method_name, error_message, allow_reentry_from_compatibility_module: false) ⇒ Object
Instance Method Summary collapse
- #unsafe_add_foreign_key(from_table, to_table, **options) ⇒ Object
- #unsafe_add_index(table, column_names, **options) ⇒ Object
-
#unsafe_change_table(*args, &block) ⇒ Object
Note that unsafe_* methods defined below do not run dependent object checks.
- #unsafe_create_table(table, **options, &block) ⇒ Object
-
#unsafe_make_column_not_nullable(table, column, **options) ⇒ Object
options arg is only present for backwards compatiblity.
- #unsafe_partman_standardize_partition_naming(table, statement_timeout: 1) ⇒ Object
- #unsafe_partman_update_config(table, **options) ⇒ Object
- #unsafe_remove_constraint(table, name:) ⇒ Object
- #unsafe_remove_foreign_key(from_table, to_table = nil, **options) ⇒ Object
- #unsafe_remove_index(table, column = nil, **options) ⇒ Object
- #unsafe_rename_enum_value(name, old_value, new_value) ⇒ Object
Class Method Details
.delegate_raw_method_to_migration_base_class(method_name) ⇒ Object
38 39 40 41 42 43 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 38 def self.delegate_raw_method_to_migration_base_class(method_name) define_method("raw_#{method_name}") do |*args, &block| execute_ancestor_statement(method_name, *args, &block) end ruby2_keywords "raw_#{method_name}" end |
.delegate_unsafe_method_to_migration_base_class(method_name, with_lock: true) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 21 def self.delegate_unsafe_method_to_migration_base_class(method_name, with_lock: true) define_method("unsafe_#{method_name}") do |*args, &block| if PgHaMigrations.config.check_for_dependent_objects disallow_migration_method_if_dependent_objects!(method_name, arguments: args) end if with_lock safely_acquire_lock_for_table(args.first) do execute_ancestor_statement(method_name, *args, &block) end else execute_ancestor_statement(method_name, *args, &block) end end ruby2_keywords "unsafe_#{method_name}" end |
.disable_or_delegate_default_method(method_name, error_message, allow_reentry_from_compatibility_module: false) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 2 def self.disable_or_delegate_default_method(method_name, , allow_reentry_from_compatibility_module: false) define_method(method_name) do |*args, &block| if PgHaMigrations.config.disable_default_migration_methods # Most migration methods are only ever called by a migration and # therefore aren't re-entrant or callable from another migration # method, but `execute` is called directly by at least one of the # implementations in `ActiveRecord::Migration::Compatibility` so # we have to explicitly handle that case by allowing execution of # the original implementation by its original name. unless allow_reentry_from_compatibility_module && caller[0] =~ /lib\/active_record\/migration\/compatibility.rb/ raise PgHaMigrations::UnsafeMigrationError, end end execute_ancestor_statement(method_name, *args, &block) end ruby2_keywords method_name end |
Instance Method Details
#unsafe_add_foreign_key(from_table, to_table, **options) ⇒ Object
145 146 147 148 149 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 145 def unsafe_add_foreign_key(from_table, to_table, **) safely_acquire_lock_for_table(from_table, to_table, mode: :share_row_exclusive) do execute_ancestor_statement(:add_foreign_key, from_table, to_table, **) end end |
#unsafe_add_index(table, column_names, **options) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 110 def unsafe_add_index(table, column_names, **) if ((ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 2) || ActiveRecord::VERSION::MAJOR > 5) && column_names.is_a?(String) && /\W/.match?(column_names) && .key?(:opclass) raise PgHaMigrations::InvalidMigrationError, "ActiveRecord drops the :opclass option when supplying a string containing an expression or list of columns; instead either supply an array of columns or include the opclass in the string for each column" end validated_table = PgHaMigrations::Table.from_table_name(table) validated_index = if [:name] PgHaMigrations::Index.new([:name], validated_table) else PgHaMigrations::Index.from_table_and_columns(validated_table, column_names) end [:name] = validated_index.name if [:algorithm] == :concurrently execute_ancestor_statement(:add_index, table, column_names, **) else safely_acquire_lock_for_table(table, mode: :share) do execute_ancestor_statement(:add_index, table, column_names, **) end end end |
#unsafe_change_table(*args, &block) ⇒ Object
Note that unsafe_* methods defined below do not run dependent object checks
98 99 100 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 98 def unsafe_change_table(*args, &block) raise PgHaMigrations::UnsafeMigrationError.new(":change_table is too generic to even allow an unsafe variant. Use a combination of safe and explicit unsafe migration methods instead") end |
#unsafe_create_table(table, **options, &block) ⇒ Object
102 103 104 105 106 107 108 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 102 def unsafe_create_table(table, **, &block) if [:force] && !PgHaMigrations.config.allow_force_create_table raise PgHaMigrations::UnsafeMigrationError.new(":force is NOT SAFE! Explicitly call unsafe_drop_table first if you want to recreate an existing table") end execute_ancestor_statement(:create_table, table, **, &block) end |
#unsafe_make_column_not_nullable(table, column, **options) ⇒ Object
options arg is only present for backwards compatiblity
171 172 173 174 175 176 177 178 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 171 def unsafe_make_column_not_nullable(table, column, **) # options arg is only present for backwards compatiblity quoted_table_name = connection.quote_table_name(table) quoted_column_name = connection.quote_column_name(column) safely_acquire_lock_for_table(table) do raw_execute("ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} SET NOT NULL") end end |
#unsafe_partman_standardize_partition_naming(table, statement_timeout: 1) ⇒ Object
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 214 def unsafe_partman_standardize_partition_naming(table, statement_timeout: 1) raise PgHaMigrations::MissingExtensionError, "The pg_partman extension is not installed" unless partman_extension.installed? raise PgHaMigrations::InvalidMigrationError, "This method is only available for pg_partman major version 4" unless partman_extension.major_version == 4 validated_table = PgHaMigrations::PartmanTable.from_table_name(table) part_config = validated_table.part_config(partman_extension: partman_extension) partition_rename_adapter = part_config.partition_rename_adapter before_automatic_maintenance = part_config.automatic_maintenance part_config.update!(automatic_maintenance: "off") if before_automatic_maintenance == "on" begin partitions = validated_table.partitions alter_table_sql = partition_rename_adapter.alter_table_sql(partitions) = "partman_standardize_partition_naming(" \ "#{table.inspect}, statement_timeout: #{statement_timeout}) - " \ "Renaming #{partitions.size - 1} partition(s)" # excluding default partition safely_acquire_lock_for_table(table) do adjust_statement_timeout(statement_timeout) do say_with_time() do part_config.update!(datetime_string: partition_rename_adapter.target_datetime_string) connection.execute(alter_table_sql) end end end ensure part_config.restore_attributes part_config.update!(automatic_maintenance: "on") if before_automatic_maintenance == "on" end end |
#unsafe_partman_update_config(table, **options) ⇒ Object
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 194 def unsafe_partman_update_config(table, **) = .keys - PgHaMigrations::PARTMAN_UPDATE_CONFIG_OPTIONS raise ArgumentError, "Unrecognized argument(s): #{}" unless .empty? part_config = PgHaMigrations::PartmanTable .from_table_name(table) .part_config(partman_extension: partman_extension) part_config.assign_attributes(**) inherit_privileges_changed = part_config.inherit_privileges_changed? say_with_time "partman_update_config(#{table.inspect}, #{.map { |k,v| "#{k}: #{v.inspect}" }.join(", ")})" do part_config.save! end safe_partman_reapply_privileges(table) if inherit_privileges_changed end |
#unsafe_remove_constraint(table, name:) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 180 def unsafe_remove_constraint(table, name:) raise ArgumentError, "Expected <name> to be present" unless name.present? quoted_table_name = connection.quote_table_name(table) quoted_constraint_name = connection.quote_table_name(name) sql = "ALTER TABLE #{quoted_table_name} DROP CONSTRAINT #{quoted_constraint_name}" safely_acquire_lock_for_table(table) do say_with_time "remove_constraint(#{table.inspect}, name: #{name.inspect})" do connection.execute(sql) end end end |
#unsafe_remove_foreign_key(from_table, to_table = nil, **options) ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 151 def unsafe_remove_foreign_key(from_table, to_table = nil, **) calculated_to_table = .fetch(:to_table, to_table) if calculated_to_table.nil? raise PgHaMigrations::InvalidMigrationError.new("The :to_table positional arg / kwarg is required for lock acquisition") end safely_acquire_lock_for_table(from_table, calculated_to_table) do execute_ancestor_statement(:remove_foreign_key, from_table, to_table, **) end end |
#unsafe_remove_index(table, column = nil, **options) ⇒ Object
135 136 137 138 139 140 141 142 143 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 135 def unsafe_remove_index(table, column = nil, **) if [:algorithm]== :concurrently execute_ancestor_statement(:remove_index, table, column, **) else safely_acquire_lock_for_table(table) do execute_ancestor_statement(:remove_index, table, column, **) end end end |
#unsafe_rename_enum_value(name, old_value, new_value) ⇒ Object
163 164 165 166 167 168 169 |
# File 'lib/pg_ha_migrations/unsafe_statements.rb', line 163 def unsafe_rename_enum_value(name, old_value, new_value) if ActiveRecord::Base.connection.postgresql_version < 10_00_00 raise PgHaMigrations::InvalidMigrationError, "Renaming an enum value is not supported on Postgres databases before version 10" end raw_execute("ALTER TYPE #{PG::Connection.quote_ident(name.to_s)} RENAME VALUE '#{PG::Connection.escape_string(old_value)}' TO '#{PG::Connection.escape_string(new_value)}'") end |