rom-sql 1.3.5 → 2.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -7
  3. data/Gemfile +7 -5
  4. data/lib/rom/plugins/relation/sql/auto_restrictions.rb +11 -17
  5. data/lib/rom/sql.rb +3 -2
  6. data/lib/rom/sql/associations.rb +5 -0
  7. data/lib/rom/sql/associations/core.rb +20 -0
  8. data/lib/rom/sql/associations/many_to_many.rb +83 -0
  9. data/lib/rom/sql/associations/many_to_one.rb +55 -0
  10. data/lib/rom/sql/associations/one_to_many.rb +31 -0
  11. data/lib/rom/sql/{association → associations}/one_to_one.rb +3 -2
  12. data/lib/rom/sql/{association → associations}/one_to_one_through.rb +3 -2
  13. data/lib/rom/sql/associations/self_ref.rb +39 -0
  14. data/lib/rom/sql/attribute.rb +44 -54
  15. data/lib/rom/sql/errors.rb +2 -0
  16. data/lib/rom/sql/extensions/mysql.rb +1 -1
  17. data/lib/rom/sql/extensions/mysql/attributes_inferrer.rb +10 -0
  18. data/lib/rom/sql/extensions/postgres.rb +1 -1
  19. data/lib/rom/sql/extensions/postgres/{inferrer.rb → attributes_inferrer.rb} +4 -4
  20. data/lib/rom/sql/extensions/postgres/types.rb +9 -19
  21. data/lib/rom/sql/extensions/sqlite.rb +1 -1
  22. data/lib/rom/sql/extensions/sqlite/{inferrer.rb → attributes_inferrer.rb} +2 -2
  23. data/lib/rom/sql/gateway.rb +29 -30
  24. data/lib/rom/sql/index.rb +13 -0
  25. data/lib/rom/sql/migration.rb +10 -0
  26. data/lib/rom/sql/migration/inline_runner.rb +86 -0
  27. data/lib/rom/sql/migration/migrator.rb +17 -0
  28. data/lib/rom/sql/migration/schema_diff.rb +177 -0
  29. data/lib/rom/sql/plugin/associates.rb +11 -45
  30. data/lib/rom/sql/plugin/pagination.rb +4 -4
  31. data/lib/rom/sql/relation.rb +22 -42
  32. data/lib/rom/sql/relation/reading.rb +3 -3
  33. data/lib/rom/sql/schema.rb +14 -21
  34. data/lib/rom/sql/schema/associations_dsl.rb +7 -6
  35. data/lib/rom/sql/schema/attributes_inferrer.rb +164 -0
  36. data/lib/rom/sql/schema/inferrer.rb +40 -141
  37. data/lib/rom/sql/type_extensions.rb +44 -0
  38. data/lib/rom/sql/version.rb +1 -1
  39. data/lib/rom/sql/wrap.rb +25 -0
  40. data/rom-sql.gemspec +2 -2
  41. data/spec/integration/{association → associations}/many_to_many/custom_fks_spec.rb +4 -2
  42. data/spec/integration/{association → associations}/many_to_many/from_view_spec.rb +2 -2
  43. data/spec/integration/{association → associations}/many_to_many_spec.rb +25 -30
  44. data/spec/integration/{association → associations}/many_to_one/custom_fks_spec.rb +5 -3
  45. data/spec/integration/{association → associations}/many_to_one/from_view_spec.rb +3 -3
  46. data/spec/integration/{association → associations}/many_to_one/self_ref_spec.rb +2 -2
  47. data/spec/integration/{association → associations}/many_to_one_spec.rb +20 -38
  48. data/spec/integration/{association → associations}/one_to_many/custom_fks_spec.rb +4 -2
  49. data/spec/integration/{association → associations}/one_to_many/from_view_spec.rb +2 -2
  50. data/spec/integration/{association → associations}/one_to_many/self_ref_spec.rb +2 -2
  51. data/spec/integration/{association → associations}/one_to_many_spec.rb +24 -11
  52. data/spec/integration/{association → associations}/one_to_one_spec.rb +13 -9
  53. data/spec/integration/{association → associations}/one_to_one_through_spec.rb +15 -11
  54. data/spec/integration/auto_migrations/errors_spec.rb +31 -0
  55. data/spec/integration/auto_migrations/indexes_spec.rb +109 -0
  56. data/spec/integration/auto_migrations/managing_columns_spec.rb +156 -0
  57. data/spec/integration/auto_migrations/postgres/column_types_spec.rb +63 -0
  58. data/spec/integration/commands/create_spec.rb +2 -4
  59. data/spec/integration/commands/delete_spec.rb +2 -2
  60. data/spec/integration/commands/update_spec.rb +2 -0
  61. data/spec/integration/graph_spec.rb +9 -3
  62. data/spec/integration/plugins/associates_spec.rb +16 -55
  63. data/spec/integration/plugins/auto_restrictions_spec.rb +0 -11
  64. data/spec/integration/relation_schema_spec.rb +49 -25
  65. data/spec/integration/schema/inferrer/postgres_spec.rb +1 -1
  66. data/spec/integration/schema/inferrer_spec.rb +7 -18
  67. data/spec/integration/setup_spec.rb +4 -0
  68. data/spec/integration/{plugins/auto_wrap_spec.rb → wrap_spec.rb} +13 -36
  69. data/spec/shared/accounts.rb +4 -0
  70. data/spec/shared/database_setup.rb +2 -1
  71. data/spec/shared/notes.rb +2 -0
  72. data/spec/shared/posts.rb +2 -0
  73. data/spec/shared/puppies.rb +2 -0
  74. data/spec/shared/relations.rb +2 -2
  75. data/spec/shared/users.rb +2 -0
  76. data/spec/shared/users_and_tasks.rb +4 -0
  77. data/spec/spec_helper.rb +3 -6
  78. data/spec/support/helpers.rb +11 -8
  79. data/spec/support/test_configuration.rb +16 -0
  80. data/spec/unit/plugin/associates_spec.rb +5 -10
  81. data/spec/unit/plugin/pagination_spec.rb +9 -9
  82. data/spec/unit/plugin/timestamp_spec.rb +9 -9
  83. data/spec/unit/relation/dataset_spec.rb +7 -5
  84. data/spec/unit/relation/inner_join_spec.rb +2 -15
  85. data/spec/unit/relation/primary_key_spec.rb +1 -1
  86. data/spec/unit/schema_spec.rb +6 -4
  87. metadata +65 -70
  88. data/lib/rom/plugins/relation/sql/auto_combine.rb +0 -71
  89. data/lib/rom/plugins/relation/sql/auto_wrap.rb +0 -62
  90. data/lib/rom/sql/association.rb +0 -103
  91. data/lib/rom/sql/association/many_to_many.rb +0 -119
  92. data/lib/rom/sql/association/many_to_one.rb +0 -73
  93. data/lib/rom/sql/association/name.rb +0 -78
  94. data/lib/rom/sql/association/one_to_many.rb +0 -60
  95. data/lib/rom/sql/extensions/mysql/inferrer.rb +0 -10
  96. data/lib/rom/sql/qualified_attribute.rb +0 -53
  97. data/lib/rom/sql/schema/dsl.rb +0 -75
  98. data/spec/unit/association/many_to_many_spec.rb +0 -89
  99. data/spec/unit/association/many_to_one_spec.rb +0 -81
  100. data/spec/unit/association/name_spec.rb +0 -68
  101. data/spec/unit/association/one_to_many_spec.rb +0 -82
  102. data/spec/unit/association/one_to_one_spec.rb +0 -83
  103. data/spec/unit/association/one_to_one_through_spec.rb +0 -69
@@ -1,3 +1,5 @@
1
+ require 'rom/sql/associations'
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  module Plugin
@@ -5,42 +7,18 @@ module ROM
5
7
  #
6
8
  # @api private
7
9
  module Associates
8
- class MissingJoinKeysError < StandardError
9
- ERROR_TEMPLATE = ':%{command} command for :%{relation} relation ' \
10
- 'is missing join keys configuration for :%{name} association'
11
-
12
- def initialize(command, assoc_name)
13
- super(ERROR_TEMPLATE % tokens(command, assoc_name))
14
- end
15
-
16
- def tokens(command, assoc_name)
17
- { command: command.register_as,
18
- relation: command.relation,
19
- name: assoc_name }
20
- end
21
- end
22
-
23
10
  class AssociateOptions
24
11
  attr_reader :name, :assoc, :opts
25
12
 
26
13
  def initialize(name, relation, opts)
27
14
  @name = name
28
- @opts = { assoc: name, keys: opts[:key] }
29
-
30
- relation.associations.try(name) do |assoc|
31
- @assoc = assoc
32
- @opts.update(assoc: assoc, keys: assoc.join_keys(relation.__registry__))
33
- end
34
-
15
+ @assoc = relation.associations[name]
16
+ @opts = { assoc: assoc, keys: assoc.join_keys }
35
17
  @opts.update(parent: opts[:parent]) if opts[:parent]
36
18
  end
37
19
 
38
20
  def after?
39
- assoc.is_a?(Association::ManyToMany)
40
- end
41
-
42
- def ensure_valid(command)
43
- raise MissingJoinKeysError.new(command, name) unless opts[:keys]
21
+ assoc.is_a?(SQL::Associations::ManyToMany)
44
22
  end
45
23
 
46
24
  def to_hash
@@ -53,6 +31,7 @@ module ROM
53
31
  klass.class_eval do
54
32
  extend ClassMethods
55
33
  include InstanceMethods
34
+
56
35
  defines :associations
57
36
 
58
37
  associations Hash.new
@@ -77,8 +56,6 @@ module ROM
77
56
  AssociateOptions.new(name, relation, opts)
78
57
  }.compact
79
58
 
80
- associate_options.each { |opts| opts.ensure_valid(self) }
81
-
82
59
  before_hooks = associate_options.reject(&:after?).map(&:to_hash)
83
60
  after_hooks = associate_options.select(&:after?).map(&:to_hash)
84
61
 
@@ -133,18 +110,12 @@ module ROM
133
110
 
134
111
  output_tuples =
135
112
  case assoc
136
- when Symbol
137
- fk, pk = keys
138
-
139
- with_input_tuples(tuples).map { |tuple|
140
- tuple.merge(fk => parent.fetch(pk))
141
- }
142
- when Association::ManyToMany
113
+ when SQL::Associations::ManyToMany
143
114
  result_type = tuples.is_a?(Array) ? :many : :one
144
115
 
145
- assoc.persist(__registry__, tuples, parent)
116
+ assoc.persist(tuples, parent)
146
117
 
147
- pk, fk = assoc.parent_combine_keys(__registry__)
118
+ pk, fk = assoc.parent_combine_keys
148
119
 
149
120
  case parent
150
121
  when Array
@@ -154,9 +125,9 @@ module ROM
154
125
  else
155
126
  tuples.map { |tuple| Hash(tuple).update(fk => parent[pk]) }
156
127
  end
157
- when Association
128
+ else
158
129
  with_input_tuples(tuples).map { |tuple|
159
- assoc.associate(__registry__, tuple, parent)
130
+ assoc.associate(tuple, parent)
160
131
  }
161
132
  end
162
133
 
@@ -171,11 +142,6 @@ module ROM
171
142
  associations: associations.merge(name => opts)
172
143
  )
173
144
  end
174
-
175
- # @api private
176
- def __registry__
177
- relation.__registry__
178
- end
179
145
  end
180
146
  end
181
147
  end
@@ -59,9 +59,9 @@ module ROM
59
59
  # Paginate a relation
60
60
  #
61
61
  # @example
62
- # rom.relation(:users).class.per_page(10)
63
- # rom.relation(:users).page(1)
64
- # rom.relation(:users).pager # => info about pagination
62
+ # rom.relations[:users].class.per_page(10)
63
+ # rom.relations[:users].page(1)
64
+ # rom.relations[:users].pager # => info about pagination
65
65
  #
66
66
  # @return [Relation]
67
67
  #
@@ -74,7 +74,7 @@ module ROM
74
74
  # Set limit for pagination
75
75
  #
76
76
  # @example
77
- # rom.relation(:users).page(2).per_page(10)
77
+ # rom.relations[:users].page(2).per_page(10)
78
78
  #
79
79
  # @api public
80
80
  def per_page(num)
@@ -1,70 +1,50 @@
1
1
  require 'rom/sql/types'
2
2
  require 'rom/sql/schema'
3
+ require 'rom/sql/attribute'
4
+ require 'rom/sql/wrap'
3
5
 
4
6
  require 'rom/sql/relation/reading'
5
7
  require 'rom/sql/relation/writing'
6
8
  require 'rom/sql/relation/sequel_api'
7
9
 
8
- require 'rom/plugins/relation/key_inference'
9
- require 'rom/plugins/relation/sql/auto_combine'
10
- require 'rom/plugins/relation/sql/auto_wrap'
11
-
12
10
  module ROM
13
11
  module SQL
14
12
  # Sequel-specific relation extensions
15
13
  #
16
14
  # @api public
17
15
  class Relation < ROM::Relation
18
- include SQL
19
-
20
16
  adapter :sql
21
17
 
22
- use :key_inference
23
- use :auto_combine
24
- use :auto_wrap
25
-
18
+ include SQL
26
19
  include Writing
27
20
  include Reading
28
21
 
29
- schema_dsl SQL::Schema::DSL
30
-
31
- # Set default dataset for a relation sub-class
32
- #
33
- # @api private
34
- def self.inherited(klass)
35
- super
22
+ extend Notifications::Listener
36
23
 
37
- klass.class_eval do
38
- schema_inferrer -> (name, gateway) do
39
- inferrer_for_db = ROM::SQL::Schema::Inferrer.get(gateway.connection.database_type.to_sym)
40
- begin
41
- inferrer_for_db.new.call(name, gateway)
42
- rescue Sequel::Error => e
43
- inferrer_for_db.on_error(klass, e)
44
- ROM::Schema::DEFAULT_INFERRER.()
45
- end
46
- end
24
+ schema_class SQL::Schema
25
+ schema_attr_class SQL::Attribute
26
+ schema_inferrer ROM::SQL::Schema::Inferrer.new.freeze
27
+ wrap_class SQL::Wrap
47
28
 
48
- dataset do
49
- # TODO: feels strange to do it here - we need a new hook for this during finalization
50
- klass.define_default_views!
51
- schema = klass.schema
29
+ subscribe('configuration.relations.schema.set', adapter: :sql) do |event|
30
+ schema = event[:schema]
31
+ relation = event[:relation]
52
32
 
53
- table = opts[:from].first
33
+ relation.dataset do
34
+ table = opts[:from].first
54
35
 
55
- if db.table_exists?(table)
56
- if schema
57
- select(*schema.map(&:to_sql_name)).order(*schema.project(*schema.primary_key_names).qualified.map(&:to_sql_name))
58
- else
59
- select(*columns).order(*klass.primary_key_columns(db, table))
60
- end
61
- else
62
- self
63
- end
36
+ if db.table_exists?(table)
37
+ select(*schema.map(&:qualified)).order(*schema.project(*schema.primary_key_names).qualified)
38
+ else
39
+ self
64
40
  end
65
41
  end
66
42
  end
67
43
 
44
+ subscribe('configuration.relations.dataset.allocated', adapter: :sql) do |event|
45
+ event[:relation].define_default_views!
46
+ end
47
+
68
48
  # @api private
69
49
  def self.define_default_views!
70
50
  if schema.primary_key.size > 1
@@ -130,7 +110,7 @@ module ROM
130
110
  #
131
111
  # @api public
132
112
  def assoc(name)
133
- associations[name].(__registry__)
113
+ associations[name].()
134
114
  end
135
115
 
136
116
  # Return raw column names
@@ -902,17 +902,17 @@ module ROM
902
902
  #
903
903
  # @api private
904
904
  def __join__(type, other, join_cond = EMPTY_HASH, opts = EMPTY_HASH, &block)
905
- if other.is_a?(Symbol) || other.is_a?(Association::Name)
905
+ if other.is_a?(Symbol) || other.is_a?(ROM::Relation::Name)
906
906
  if join_cond.empty?
907
907
  assoc = associations[other]
908
- assoc.join(__registry__, type, self, __registry__[assoc.target.relation])
908
+ assoc.join(type, self)
909
909
  else
910
910
  new(dataset.__send__(type, other.to_sym, join_cond, opts, &block))
911
911
  end
912
912
  elsif other.is_a?(Sequel::SQL::AliasedExpression)
913
913
  new(dataset.__send__(type, other, join_cond, opts, &block))
914
914
  elsif other.respond_to?(:name) && other.name.is_a?(Relation::Name)
915
- associations[other.name.dataset].join(__registry__, type, self, other)
915
+ associations[other.name.key].join(type, self, other)
916
916
  else
917
917
  raise ArgumentError, "+other+ must be either a symbol or a relation, #{other.class} given"
918
918
  end
@@ -1,26 +1,18 @@
1
1
  require 'rom/schema'
2
+
2
3
  require 'rom/sql/order_dsl'
3
4
  require 'rom/sql/group_dsl'
4
5
  require 'rom/sql/projection_dsl'
5
6
  require 'rom/sql/restriction_dsl'
7
+ require 'rom/sql/index'
8
+ require 'rom/sql/schema/inferrer'
6
9
 
7
10
  module ROM
8
11
  module SQL
9
12
  class Schema < ROM::Schema
10
- # @!attribute [r] primary_key_name
11
- # @return [Symbol] The name of the primary key. This is set because in
12
- # most of the cases relations don't have composite pks
13
- attr_reader :primary_key_name
14
-
15
- # @!attribute [r] primary_key_names
16
- # @return [Array<Symbol>] A list of all pk names
17
- attr_reader :primary_key_names
18
-
19
- # @api private
20
- def initialize(*)
21
- super
22
- initialize_primary_key_names
23
- end
13
+ # @!attribute [r] attributes
14
+ # @return [Array<Index>] Array with schema indexes
15
+ option :indexes, default: -> { EMPTY_ARRAY }
24
16
 
25
17
  # @api public
26
18
  def restriction(&block)
@@ -105,21 +97,22 @@ module ROM
105
97
  end
106
98
 
107
99
  # @api private
108
- def finalize!(*args)
100
+ def finalize_attributes!(options = EMPTY_HASH)
109
101
  super do
110
102
  initialize_primary_key_names
111
103
  end
112
104
  end
113
105
 
114
106
  # @api private
115
- def initialize_primary_key_names
116
- if primary_key.size > 0
117
- @primary_key_name = primary_key[0].meta[:name]
118
- @primary_key_names = primary_key.map { |type| type.meta[:name] }
107
+ def finalize_associations!(relations:)
108
+ super do
109
+ associations.map do |definition|
110
+ SQL::Associations.const_get(definition.type).new(definition, relations)
111
+ end
119
112
  end
120
113
  end
114
+
115
+ memoize :qualified, :canonical, :joined, :project_pk
121
116
  end
122
117
  end
123
118
  end
124
-
125
- require 'rom/sql/schema/dsl'
@@ -1,5 +1,6 @@
1
1
  require 'dry/core/inflector'
2
- require 'rom/sql/association'
2
+
3
+ require 'rom/associations/definitions'
3
4
 
4
5
  module ROM
5
6
  module SQL
@@ -52,7 +53,7 @@ module ROM
52
53
  if options[:through]
53
54
  many_to_many(target, options)
54
55
  else
55
- add(Association::OneToMany.new(source, target, options))
56
+ add(::ROM::Associations::Definitions::OneToMany.new(source, target, options))
56
57
  end
57
58
  end
58
59
  alias_method :has_many, :one_to_many
@@ -77,7 +78,7 @@ module ROM
77
78
  if options[:through]
78
79
  one_to_one_through(target, options)
79
80
  else
80
- add(Association::OneToOne.new(source, target, options))
81
+ add(::ROM::Associations::Definitions::OneToOne.new(source, target, options))
81
82
  end
82
83
  end
83
84
 
@@ -90,7 +91,7 @@ module ROM
90
91
  #
91
92
  # @api public
92
93
  def one_to_one_through(target, options = {})
93
- add(Association::OneToOneThrough.new(source, target, options))
94
+ add(::ROM::Associations::Definitions::OneToOneThrough.new(source, target, options))
94
95
  end
95
96
 
96
97
  # Establish a many-to-many association
@@ -107,7 +108,7 @@ module ROM
107
108
  #
108
109
  # @api public
109
110
  def many_to_many(target, options = {})
110
- add(Association::ManyToMany.new(source, target, options))
111
+ add(::ROM::Associations::Definitions::ManyToMany.new(source, target, options))
111
112
  end
112
113
 
113
114
  # Establish a many-to-one association
@@ -124,7 +125,7 @@ module ROM
124
125
  #
125
126
  # @api public
126
127
  def many_to_one(target, options = {})
127
- add(Association::ManyToOne.new(source, target, options))
128
+ add(::ROM::Associations::Definitions::ManyToOne.new(source, target, options))
128
129
  end
129
130
 
130
131
  # Shortcut for many_to_one which sets alias automatically
@@ -0,0 +1,164 @@
1
+ require 'dry/core/class_attributes'
2
+
3
+ module ROM
4
+ module SQL
5
+ class Schema < ROM::Schema
6
+ # @api private
7
+ class AttributesInferrer
8
+ extend Dry::Core::ClassAttributes
9
+ extend Initializer
10
+
11
+ defines :ruby_type_mapping, :numeric_pk_type, :db_type, :registry
12
+
13
+ class << self
14
+ def inherited(klass)
15
+ super
16
+
17
+ registry[klass.db_type] = klass.new.freeze unless klass.name.nil?
18
+ end
19
+
20
+ def [](type)
21
+ Class.new(self) { db_type(type) }
22
+ end
23
+
24
+ def get(db_type)
25
+ registry[db_type]
26
+ end
27
+ end
28
+
29
+ CONSTRAINT_DB_TYPE = 'add_constraint'.freeze
30
+ DECIMAL_REGEX = /(?:decimal|numeric)\((\d+)(?:,\s*(\d+))?\)/.freeze
31
+
32
+ registry Hash.new(new.freeze)
33
+
34
+ ruby_type_mapping(
35
+ integer: Types::Int,
36
+ string: Types::String,
37
+ time: Types::Time,
38
+ date: Types::Date,
39
+ datetime: Types::Time,
40
+ boolean: Types::Bool,
41
+ decimal: Types::Decimal,
42
+ float: Types::Float,
43
+ blob: Types::Blob
44
+ ).freeze
45
+
46
+ numeric_pk_type Types::Serial
47
+
48
+ option :attr_class, optional: true
49
+
50
+ # @api private
51
+ def call(schema, gateway)
52
+ dataset = schema.name.dataset
53
+
54
+ columns = filter_columns(gateway.connection.schema(dataset))
55
+ fks = fks_for(gateway, dataset)
56
+
57
+ inferred = columns.map do |(name, definition)|
58
+ type = build_type(**definition, foreign_key: fks[name])
59
+
60
+ attr_class.new(type.meta(name: name, source: schema.name)) if type
61
+ end.compact
62
+
63
+ missing = columns.map(&:first) - inferred.map { |attr| attr.meta[:name] }
64
+
65
+ [inferred, missing]
66
+ end
67
+
68
+ # @api private
69
+ def with(new_options)
70
+ self.class.new(options.merge(new_options))
71
+ end
72
+
73
+
74
+ # @api private
75
+ def filter_columns(schema)
76
+ schema.reject { |(_, definition)| definition[:db_type] == CONSTRAINT_DB_TYPE }
77
+ end
78
+
79
+ # @api private
80
+ def build_type(primary_key:, db_type:, type:, allow_null:, foreign_key:, **rest)
81
+ if primary_key
82
+ map_pk_type(type, db_type)
83
+ else
84
+ mapped_type = map_type(type, db_type, rest)
85
+
86
+ if mapped_type
87
+ read_type = mapped_type.meta[:read]
88
+ mapped_type = mapped_type.optional if allow_null
89
+ mapped_type = mapped_type.meta(foreign_key: true, target: foreign_key) if foreign_key
90
+
91
+ if read_type && allow_null
92
+ mapped_type.meta(read: read_type.optional)
93
+ elsif read_type
94
+ mapped_type.meta(read: read_type)
95
+ else
96
+ mapped_type
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ # @api private
103
+ def map_pk_type(_ruby_type, _db_type)
104
+ self.class.numeric_pk_type.meta(primary_key: true)
105
+ end
106
+
107
+ # @api private
108
+ def map_type(ruby_type, db_type, **kw)
109
+ type = self.class.ruby_type_mapping[ruby_type]
110
+
111
+ if db_type.is_a?(String) && db_type.include?('numeric') || db_type.include?('decimal')
112
+ map_decimal_type(db_type)
113
+ elsif db_type.is_a?(String) && db_type.include?('char') && kw[:max_length]
114
+ type.meta(limit: kw[:max_length])
115
+ else
116
+ type
117
+ end
118
+ end
119
+
120
+ # @api private
121
+ def fks_for(gateway, dataset)
122
+ gateway.connection.foreign_key_list(dataset).each_with_object({}) do |definition, fks|
123
+ column, fk = build_fk(definition)
124
+
125
+ fks[column] = fk if fk
126
+ end
127
+ end
128
+
129
+ # @api private
130
+ def column_indexes(indexes, column)
131
+ indexes.each_with_object(Set.new) do |(name, idx), set|
132
+ set << name if idx[:columns][0] == column
133
+ end
134
+ end
135
+
136
+ # @api private
137
+ def build_fk(columns: , table: , **rest)
138
+ if columns.size == 1
139
+ [columns[0], table]
140
+ else
141
+ # We don't have support for multicolumn foreign keys
142
+ columns[0]
143
+ end
144
+ end
145
+
146
+ # @api private
147
+ def map_decimal_type(type)
148
+ precision = DECIMAL_REGEX.match(type)
149
+
150
+ if precision
151
+ prcsn, scale = precision[1..2].map(&:to_i)
152
+
153
+ self.class.ruby_type_mapping[:decimal].meta(
154
+ precision: prcsn,
155
+ scale: scale
156
+ )
157
+ else
158
+ self.class.ruby_type_mapping[:decimal]
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end