Class: SchemaFerry::Converter::TypeMapper

Inherits:
Object
  • Object
show all
Defined in:
lib/schema_ferry/converter/type_mapper.rb

Constant Summary collapse

LIMIT_STRIPPED_TYPES =

PG has no limit concept for text/binary/bigint/float; drop the MySQL-derived limits (float's limit: 53 is DOUBLE's internal bit width, not something AR ever reads back from a PG column).

%i[text binary bigint float].freeze
DEFAULT_PRECISION_TYPES =

PG's default timestamp precision is 6. Spelling it out makes ridgepole see a diff against the PG export (which omits it) on every run.

%i[datetime time].freeze
PG_DEFAULT_PRECISION =
6
NO_PRECISION_TYPES =

ActiveRecord's PostgreSQL adapter never honors a precision option on :timestamptz (only :datetime/:timestamp/:time do — see ActiveRecord::ConnectionAdapters::SchemaStatements#type_to_sql). Declaring one is always a lie, so it must never be emitted.

%i[timestamptz].freeze
DEFAULTS =
{
  json: :jsonb
}.freeze
KNOWN_TYPES =
%i[
  string text integer bigint float decimal
  datetime date time boolean binary json jsonb
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(global_overrides = {}) ⇒ TypeMapper

Returns a new instance of TypeMapper.



31
32
33
# File 'lib/schema_ferry/converter/type_mapper.rb', line 31

def initialize(global_overrides = {})
  @overrides = DEFAULTS.merge(global_overrides)
end

Instance Method Details

#call(ar_type, options = {}) ⇒ Object

Returns [pg_type_sym, adjusted_options_hash]



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/schema_ferry/converter/type_mapper.rb', line 36

def call(ar_type, options = {})
  unless KNOWN_TYPES.include?(ar_type)
    raise ConversionError, "Unknown MySQL AR type: #{ar_type.inspect}. " \
                           "Use map_type or map_column to specify a PostgreSQL type."
  end

  pg_type  = @overrides.fetch(ar_type, ar_type)
  adjusted = options.dup
  pg_type, adjusted = normalize_integer(adjusted) if pg_type == :integer
  adjusted.delete(:limit) if LIMIT_STRIPPED_TYPES.include?(pg_type)
  strip_default_precision(pg_type, adjusted)
  if pg_type == :decimal
    # PG numeric(20) equals numeric(20,0) and is exported without scale.
    adjusted[:scale] = nil if adjusted[:scale]&.zero?
    # AR's schema dumper renders decimal defaults as a string (e.g.
    # `default: "0"`), not a numeric literal. ridgepole compares against
    # that dumped form, so a BigDecimal/Integer default never matches
    # and gets re-applied on every run.
    adjusted[:default] = adjusted[:default]&.to_s
  end

  [pg_type, adjusted]
end