rom-sql 1.3.5 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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