Module: OnlineMigrations::Utils

Defined in:
lib/online_migrations/utils.rb

Class Method Summary collapse

Class Method Details

.ar_versionObject



9
10
11
# File 'lib/online_migrations/utils.rb', line 9

def ar_version
  ActiveRecord.version.to_s.to_f
end

.define_model(table_name) ⇒ Object



55
56
57
58
59
60
# File 'lib/online_migrations/utils.rb', line 55

def define_model(table_name)
  Class.new(ActiveRecord::Base) do
    self.table_name = table_name
    self.inheritance_column = :_type_disabled
  end
end

.developer_env?Boolean



22
23
24
# File 'lib/online_migrations/utils.rb', line 22

def developer_env?
  env == "development" || env == "test"
end

.envObject



13
14
15
16
17
18
19
20
# File 'lib/online_migrations/utils.rb', line 13

def env
  if defined?(Rails.env)
    Rails.env
  else
    # default to production for safety
    ENV["RACK_ENV"] || "production"
  end
end

.estimated_count(connection, table_name) ⇒ Object

Returns estimated rows count for a table. www.citusdata.com/blog/2016/10/12/count-performance/



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/online_migrations/utils.rb', line 74

def estimated_count(connection, table_name)
  quoted_table = connection.quote(table_name)

  count = connection.select_value(<<~SQL)
    SELECT
      (reltuples / COALESCE(NULLIF(relpages, 0), 1)) *
      (pg_relation_size(#{quoted_table}) / (current_setting('block_size')::integer))
    FROM pg_catalog.pg_class
    WHERE relname = #{quoted_table}
      AND relnamespace = current_schema()::regnamespace
  SQL

  if count
    count = count.to_i
    # If the table has never yet been vacuumed or analyzed, reltuples contains -1
    # indicating that the row count is unknown.
    count = 0 if count < 0
    count
  end
end

.find_connection_class(model) ⇒ Object



117
118
119
120
121
122
# File 'lib/online_migrations/utils.rb', line 117

def find_connection_class(model)
  model.ancestors.find do |parent|
    parent == ActiveRecord::Base ||
      (parent.is_a?(Class) && parent.abstract_class?)
  end
end

.foreign_table_name(ref_name, options) ⇒ Object



66
67
68
69
70
# File 'lib/online_migrations/utils.rb', line 66

def foreign_table_name(ref_name, options)
  options.fetch(:to_table) do
    ActiveRecord::Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
  end
end

.multiple_databases?Boolean



134
135
136
137
# File 'lib/online_migrations/utils.rb', line 134

def multiple_databases?
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: env)
  db_config.reject(&:replica?).size > 1
end

.raise_in_prod_or_say_in_dev(message) ⇒ Object



43
44
45
46
47
48
49
# File 'lib/online_migrations/utils.rb', line 43

def raise_in_prod_or_say_in_dev(message)
  if developer_env?
    say(message)
  else
    raise message
  end
end

.raise_or_say(message) ⇒ Object



35
36
37
38
39
40
41
# File 'lib/online_migrations/utils.rb', line 35

def raise_or_say(message)
  if developer_env? && !multiple_databases?
    raise message
  else
    say(message)
  end
end

.run_background_migrations_inline?Boolean



139
140
141
142
# File 'lib/online_migrations/utils.rb', line 139

def run_background_migrations_inline?
  run_inline = OnlineMigrations.config.run_background_migrations_inline
  run_inline && run_inline.call
end

.say(message) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/online_migrations/utils.rb', line 26

def say(message)
  message = "[online_migrations] #{message}"
  if (migration = OnlineMigrations.current_migration)
    migration.say(message)
  elsif (logger = ActiveRecord::Base.logger)
    logger.info(message)
  end
end

.shard_names(model) ⇒ Object



124
125
126
127
128
129
130
131
132
# File 'lib/online_migrations/utils.rb', line 124

def shard_names(model)
  model.ancestors.each do |ancestor|
    # There is no official method to get shard names from the model.
    # This is the way that currently is used in ActiveRecord tests themselves.
    pool_manager = ActiveRecord::Base.connection_handler.send(:get_pool_manager, ancestor.name)

    return pool_manager.shard_names if pool_manager
  end
end

.to_bool(value) ⇒ Object



62
63
64
# File 'lib/online_migrations/utils.rb', line 62

def to_bool(value)
  value.to_s.match?(/^true|t|yes|y|1|on$/i)
end

.volatile_default?(connection, type, value) ⇒ Boolean



98
99
100
101
102
103
104
105
# File 'lib/online_migrations/utils.rb', line 98

def volatile_default?(connection, type, value)
  return false if !(value.is_a?(Proc) || (type.to_s == "uuid" && value.is_a?(String)))

  value = value.call if value.is_a?(Proc)
  return false if !value.is_a?(String)

  value.scan(FUNCTION_CALL_RE).any? { |(function_name)| volatile_function?(connection, function_name.downcase) }
end

.volatile_function?(connection, function_name) ⇒ Boolean



107
108
109
110
111
112
113
114
115
# File 'lib/online_migrations/utils.rb', line 107

def volatile_function?(connection, function_name)
  query = <<~SQL
    SELECT provolatile
    FROM pg_catalog.pg_proc
    WHERE proname = #{connection.quote(function_name)}
  SQL

  connection.select_value(query) == "v"
end

.warn(message) ⇒ Object



51
52
53
# File 'lib/online_migrations/utils.rb', line 51

def warn(message)
  Kernel.warn("[online_migrations] #{message}")
end