rom-sql 2.0.0.beta2 → 2.0.0.beta3

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/lib/rom/plugins/relation/sql/postgres/explain.rb +54 -0
  4. data/lib/rom/sql.rb +1 -1
  5. data/lib/rom/sql/attribute.rb +17 -18
  6. data/lib/rom/sql/errors.rb +3 -0
  7. data/lib/rom/sql/extensions/mysql.rb +1 -1
  8. data/lib/rom/sql/extensions/mysql/type_builder.rb +28 -0
  9. data/lib/rom/sql/extensions/postgres.rb +3 -1
  10. data/lib/rom/sql/extensions/postgres/commands.rb +30 -13
  11. data/lib/rom/sql/extensions/postgres/{attributes_inferrer.rb → type_builder.rb} +24 -28
  12. data/lib/rom/sql/extensions/postgres/type_serializer.rb +39 -0
  13. data/lib/rom/sql/extensions/postgres/types.rb +24 -477
  14. data/lib/rom/sql/extensions/postgres/types/array.rb +163 -0
  15. data/lib/rom/sql/extensions/postgres/types/geometric.rb +135 -0
  16. data/lib/rom/sql/extensions/postgres/types/json.rb +235 -0
  17. data/lib/rom/sql/extensions/postgres/types/network.rb +15 -0
  18. data/lib/rom/sql/extensions/sqlite.rb +1 -1
  19. data/lib/rom/sql/extensions/sqlite/{attributes_inferrer.rb → type_builder.rb} +5 -5
  20. data/lib/rom/sql/extensions/sqlite/types.rb +8 -3
  21. data/lib/rom/sql/foreign_key.rb +17 -0
  22. data/lib/rom/sql/function.rb +86 -8
  23. data/lib/rom/sql/gateway.rb +26 -26
  24. data/lib/rom/sql/index.rb +4 -0
  25. data/lib/rom/sql/migration.rb +3 -3
  26. data/lib/rom/sql/migration/inline_runner.rb +9 -83
  27. data/lib/rom/sql/migration/migrator.rb +35 -12
  28. data/lib/rom/sql/migration/recorder.rb +21 -0
  29. data/lib/rom/sql/migration/runner.rb +115 -0
  30. data/lib/rom/sql/migration/schema_diff.rb +108 -53
  31. data/lib/rom/sql/migration/writer.rb +61 -0
  32. data/lib/rom/sql/relation.rb +2 -1
  33. data/lib/rom/sql/relation/reading.rb +63 -3
  34. data/lib/rom/sql/relation/writing.rb +38 -0
  35. data/lib/rom/sql/schema.rb +9 -3
  36. data/lib/rom/sql/schema/attributes_inferrer.rb +3 -119
  37. data/lib/rom/sql/schema/inferrer.rb +99 -18
  38. data/lib/rom/sql/schema/type_builder.rb +94 -0
  39. data/lib/rom/sql/type_dsl.rb +30 -0
  40. data/lib/rom/sql/type_extensions.rb +11 -6
  41. data/lib/rom/sql/type_serializer.rb +46 -0
  42. data/lib/rom/sql/types.rb +12 -0
  43. data/lib/rom/sql/version.rb +1 -1
  44. metadata +26 -244
  45. data/.codeclimate.yml +0 -15
  46. data/.gitignore +0 -17
  47. data/.rspec +0 -3
  48. data/.travis.yml +0 -39
  49. data/.yardopts +0 -2
  50. data/Gemfile +0 -33
  51. data/Guardfile +0 -24
  52. data/LICENSE.txt +0 -22
  53. data/Rakefile +0 -19
  54. data/circle.yml +0 -10
  55. data/lib/rom/sql/extensions/mysql/attributes_inferrer.rb +0 -10
  56. data/lib/rom/sql/relation/sequel_api.rb +0 -133
  57. data/log/.gitkeep +0 -0
  58. data/rom-sql.gemspec +0 -29
  59. data/spec/extensions/postgres/attribute_spec.rb +0 -217
  60. data/spec/extensions/postgres/integration_spec.rb +0 -59
  61. data/spec/extensions/postgres/types_spec.rb +0 -252
  62. data/spec/extensions/sqlite/types_spec.rb +0 -11
  63. data/spec/fixtures/migrations/20150403090603_create_carrots.rb +0 -8
  64. data/spec/integration/associations/many_to_many/custom_fks_spec.rb +0 -76
  65. data/spec/integration/associations/many_to_many/from_view_spec.rb +0 -88
  66. data/spec/integration/associations/many_to_many_spec.rb +0 -162
  67. data/spec/integration/associations/many_to_one/custom_fks_spec.rb +0 -64
  68. data/spec/integration/associations/many_to_one/from_view_spec.rb +0 -84
  69. data/spec/integration/associations/many_to_one/self_ref_spec.rb +0 -53
  70. data/spec/integration/associations/many_to_one_spec.rb +0 -117
  71. data/spec/integration/associations/one_to_many/custom_fks_spec.rb +0 -54
  72. data/spec/integration/associations/one_to_many/from_view_spec.rb +0 -57
  73. data/spec/integration/associations/one_to_many/self_ref_spec.rb +0 -54
  74. data/spec/integration/associations/one_to_many_spec.rb +0 -86
  75. data/spec/integration/associations/one_to_one_spec.rb +0 -69
  76. data/spec/integration/associations/one_to_one_through_spec.rb +0 -92
  77. data/spec/integration/auto_migrations/errors_spec.rb +0 -31
  78. data/spec/integration/auto_migrations/indexes_spec.rb +0 -253
  79. data/spec/integration/auto_migrations/managing_columns_spec.rb +0 -156
  80. data/spec/integration/auto_migrations/postgres/column_types_spec.rb +0 -63
  81. data/spec/integration/combine_with_spec.rb +0 -43
  82. data/spec/integration/commands/create_spec.rb +0 -304
  83. data/spec/integration/commands/delete_spec.rb +0 -84
  84. data/spec/integration/commands/update_spec.rb +0 -90
  85. data/spec/integration/commands/upsert_spec.rb +0 -83
  86. data/spec/integration/gateway_spec.rb +0 -107
  87. data/spec/integration/migration_spec.rb +0 -55
  88. data/spec/integration/plugins/associates/many_to_many_spec.rb +0 -69
  89. data/spec/integration/plugins/associates_spec.rb +0 -250
  90. data/spec/integration/plugins/auto_restrictions_spec.rb +0 -74
  91. data/spec/integration/relation_schema_spec.rb +0 -271
  92. data/spec/integration/schema/call_spec.rb +0 -24
  93. data/spec/integration/schema/inferrer/mysql_spec.rb +0 -45
  94. data/spec/integration/schema/inferrer/postgres_spec.rb +0 -203
  95. data/spec/integration/schema/inferrer/sqlite_spec.rb +0 -37
  96. data/spec/integration/schema/inferrer_spec.rb +0 -390
  97. data/spec/integration/schema/prefix_spec.rb +0 -16
  98. data/spec/integration/schema/qualified_spec.rb +0 -16
  99. data/spec/integration/schema/rename_spec.rb +0 -21
  100. data/spec/integration/schema/view_spec.rb +0 -29
  101. data/spec/integration/sequel_api_spec.rb +0 -36
  102. data/spec/integration/setup_spec.rb +0 -26
  103. data/spec/integration/support/active_support_notifications_spec.rb +0 -24
  104. data/spec/integration/support/rails_log_subscriber_spec.rb +0 -30
  105. data/spec/integration/wrap_spec.rb +0 -91
  106. data/spec/shared/accounts.rb +0 -48
  107. data/spec/shared/database_setup.rb +0 -70
  108. data/spec/shared/notes.rb +0 -23
  109. data/spec/shared/posts.rb +0 -34
  110. data/spec/shared/puppies.rb +0 -15
  111. data/spec/shared/relations.rb +0 -8
  112. data/spec/shared/users.rb +0 -32
  113. data/spec/shared/users_and_tasks.rb +0 -50
  114. data/spec/spec_helper.rb +0 -122
  115. data/spec/support/env_helper.rb +0 -25
  116. data/spec/support/helpers.rb +0 -24
  117. data/spec/support/oracle/create_users.sql +0 -7
  118. data/spec/support/oracle/set_sys_passwords.sql +0 -2
  119. data/spec/support/test_configuration.rb +0 -16
  120. data/spec/unit/attribute_spec.rb +0 -104
  121. data/spec/unit/function_spec.rb +0 -48
  122. data/spec/unit/gateway_spec.rb +0 -70
  123. data/spec/unit/logger_spec.rb +0 -14
  124. data/spec/unit/migration_tasks_spec.rb +0 -111
  125. data/spec/unit/migrator_spec.rb +0 -25
  126. data/spec/unit/order_dsl_spec.rb +0 -43
  127. data/spec/unit/plugin/associates_spec.rb +0 -94
  128. data/spec/unit/plugin/pagination_spec.rb +0 -91
  129. data/spec/unit/plugin/timestamp_spec.rb +0 -117
  130. data/spec/unit/projection_dsl_spec.rb +0 -110
  131. data/spec/unit/relation/assoc_spec.rb +0 -87
  132. data/spec/unit/relation/associations_spec.rb +0 -27
  133. data/spec/unit/relation/avg_spec.rb +0 -11
  134. data/spec/unit/relation/by_pk_spec.rb +0 -62
  135. data/spec/unit/relation/dataset_spec.rb +0 -50
  136. data/spec/unit/relation/distinct_spec.rb +0 -15
  137. data/spec/unit/relation/exclude_spec.rb +0 -11
  138. data/spec/unit/relation/exist_predicate_spec.rb +0 -25
  139. data/spec/unit/relation/exists_spec.rb +0 -18
  140. data/spec/unit/relation/fetch_spec.rb +0 -21
  141. data/spec/unit/relation/group_spec.rb +0 -61
  142. data/spec/unit/relation/having_spec.rb +0 -22
  143. data/spec/unit/relation/inner_join_spec.rb +0 -158
  144. data/spec/unit/relation/inspect_spec.rb +0 -11
  145. data/spec/unit/relation/instrument_spec.rb +0 -45
  146. data/spec/unit/relation/invert_spec.rb +0 -11
  147. data/spec/unit/relation/left_join_spec.rb +0 -55
  148. data/spec/unit/relation/lock_spec.rb +0 -93
  149. data/spec/unit/relation/map_spec.rb +0 -16
  150. data/spec/unit/relation/max_spec.rb +0 -11
  151. data/spec/unit/relation/min_spec.rb +0 -11
  152. data/spec/unit/relation/order_spec.rb +0 -51
  153. data/spec/unit/relation/pluck_spec.rb +0 -11
  154. data/spec/unit/relation/prefix_spec.rb +0 -29
  155. data/spec/unit/relation/primary_key_spec.rb +0 -27
  156. data/spec/unit/relation/project_spec.rb +0 -24
  157. data/spec/unit/relation/qualified_columns_spec.rb +0 -30
  158. data/spec/unit/relation/qualified_spec.rb +0 -25
  159. data/spec/unit/relation/read_spec.rb +0 -25
  160. data/spec/unit/relation/rename_spec.rb +0 -23
  161. data/spec/unit/relation/right_join_spec.rb +0 -57
  162. data/spec/unit/relation/select_append_spec.rb +0 -21
  163. data/spec/unit/relation/select_spec.rb +0 -40
  164. data/spec/unit/relation/sum_spec.rb +0 -11
  165. data/spec/unit/relation/union_spec.rb +0 -19
  166. data/spec/unit/relation/unique_predicate_spec.rb +0 -18
  167. data/spec/unit/relation/where_spec.rb +0 -133
  168. data/spec/unit/restriction_dsl_spec.rb +0 -34
  169. data/spec/unit/schema_spec.rb +0 -25
  170. data/spec/unit/types_spec.rb +0 -65
@@ -0,0 +1,15 @@
1
+ require 'ipaddr'
2
+
3
+ module ROM
4
+ module SQL
5
+ module Postgres
6
+ module Types
7
+ IPAddress = Type('inet') do
8
+ read = SQL::Types.Constructor(IPAddr) { |ip| IPAddr.new(ip.to_s) }
9
+
10
+ SQL::Types.Constructor(IPAddr, &:to_s).meta(read: read)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,2 +1,2 @@
1
1
  require 'rom/sql/extensions/sqlite/types'
2
- require 'rom/sql/extensions/sqlite/attributes_inferrer'
2
+ require 'rom/sql/extensions/sqlite/type_builder'
@@ -1,19 +1,19 @@
1
- require 'rom/sql/schema/attributes_inferrer'
2
-
3
1
  module ROM
4
2
  module SQL
5
- class Schema
6
- class SqliteInferrer < AttributesInferrer[:sqlite]
3
+ module SQLite
4
+ class TypeBuilder < Schema::TypeBuilder
7
5
  NO_TYPE = EMPTY_STRING
8
6
 
9
7
  def map_type(_, db_type, **_kw)
10
8
  if db_type.eql?(NO_TYPE)
11
- ROM::SQL::Types::SQLite::Object
9
+ ROM::SQL::Types::SQLite::Any
12
10
  else
13
11
  super
14
12
  end
15
13
  end
16
14
  end
17
15
  end
16
+
17
+ Schema::TypeBuilder.register(:sqlite, SQLite::TypeBuilder.new.freeze)
18
18
  end
19
19
  end
@@ -2,10 +2,15 @@ require 'dry-types'
2
2
 
3
3
  module ROM
4
4
  module SQL
5
- module Types
6
- module SQLite
7
- Object = ::ROM::SQL::Types::Object
5
+ module SQLite
6
+ module Types
7
+ Any = ::ROM::SQL::Types::Any
8
+ Object = Any
8
9
  end
9
10
  end
11
+
12
+ module Types
13
+ SQLite = ::ROM::SQL::SQLite::Types
14
+ end
10
15
  end
11
16
  end
@@ -0,0 +1,17 @@
1
+ module ROM
2
+ module SQL
3
+ # @api private
4
+ class ForeignKey
5
+ extend Initializer
6
+ include Dry::Equalizer(:attributes, :parent_table, :options)
7
+
8
+ DEFAULT_PARENT_KEYS = %i(id).freeze
9
+
10
+ param :attributes
11
+
12
+ param :parent_table, type: Dry::Types['strict.symbol']
13
+
14
+ option :parent_keys, default: -> { DEFAULT_PARENT_KEYS }
15
+ end
16
+ end
17
+ end
@@ -1,9 +1,41 @@
1
- require 'rom/schema/attribute'
1
+ require 'rom/attribute'
2
2
 
3
3
  module ROM
4
4
  module SQL
5
- # @api private
6
- class Function < ROM::Schema::Attribute
5
+ # @api public
6
+ class Function < ROM::Attribute
7
+ class << self
8
+ # @api private
9
+ def frame_limit(value)
10
+ case value
11
+ when :current then 'CURRENT ROW'
12
+ when :start then 'UNBOUNDED PRECEDING'
13
+ when :end then 'UNBOUNDED FOLLOWING'
14
+ else
15
+ if value > 0
16
+ "#{ value } FOLLOWING"
17
+ else
18
+ "#{ value.abs } PRECEDING"
19
+ end
20
+ end
21
+ end
22
+
23
+ private :frame_limit
24
+ end
25
+
26
+ # @api private
27
+ WINDOW_FRAMES = Hash.new do |cache, frame|
28
+ type = frame.key?(:rows) ? 'ROWS' : 'RANGE'
29
+ bounds = frame[:rows] || frame[:range]
30
+ cache[frame] = "#{ type } BETWEEN #{ frame_limit(bounds[0]) } AND #{ frame_limit(bounds[1]) }"
31
+ end
32
+
33
+ WINDOW_FRAMES[nil] = nil
34
+ WINDOW_FRAMES[:all] = WINDOW_FRAMES[rows: [:start, :end]]
35
+ WINDOW_FRAMES[:rows] = WINDOW_FRAMES[rows: [:start, :current]]
36
+ WINDOW_FRAMES[range: :current] = WINDOW_FRAMES[range: [:current, :current]]
37
+
38
+ # @api private
7
39
  def sql_literal(ds)
8
40
  if name
9
41
  ds.literal(func.as(name))
@@ -12,41 +44,87 @@ module ROM
12
44
  end
13
45
  end
14
46
 
47
+ # @api private
15
48
  def name
16
49
  meta[:alias] || super
17
50
  end
18
51
 
19
- def qualified
52
+ # @api private
53
+ def qualified(table_alias = nil)
20
54
  meta(
21
- func: ::Sequel::SQL::Function.new(func.name, *func.args.map { |arg| arg.respond_to?(:qualified) ? arg.qualified : arg })
55
+ func: ::Sequel::SQL::Function.new(func.name, *func.args.map { |arg| arg.respond_to?(:qualified) ? arg.qualified(table_alias) : arg })
22
56
  )
23
57
  end
24
58
 
59
+ # @api private
25
60
  def is(other)
26
61
  ::Sequel::SQL::BooleanExpression.new(:'=', func, other)
27
62
  end
28
63
 
64
+ # Add an OVER clause making a window function call
65
+ # @see https://www.postgresql.org/docs/9.6/static/tutorial-window.html
66
+ #
67
+ # @example
68
+ # users.select { [id, int::row_number().over(partition: name, order: id).as(:row_no)] }
69
+ # users.select { [id, int::row_number().over(partition: [first_name, last_name], order: id).as(:row_no)] }
70
+ #
71
+ # @example frame variants
72
+ # # ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
73
+ # row_number.over(frame: { rows: [-3, :current] })
74
+ #
75
+ # # ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING
76
+ # row_number.over(frame: { rows: [-3, 3] })
77
+ #
78
+ # # ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
79
+ # row_number.over(frame: { rows: [:start, :current] })
80
+ #
81
+ # # ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
82
+ # row_number.over(frame: { rows: [:current, :end] })
83
+ #
84
+ # @example frame shortcuts
85
+ # # ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
86
+ # row_number.over(frame: :all)
87
+ #
88
+ # # ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
89
+ # row_number.over(frame: :rows)
90
+ #
91
+ # # RANGE BETWEEN CURRENT ROW AND CURRENT ROW
92
+ # row_number.over(frame: { range: :current} )
93
+ #
94
+ # @option :partition [Array<SQL::Attribute>,SQL::Attribute] A PARTITION BY part
95
+ # @option :order [Array<SQL::Attribute>,SQL::Attribute] An ORDER BY part
96
+ # @option :frame [Hash,Symbol] A frame part (RANGE or ROWS, see examples)
97
+ # @return [SQL::Function]
98
+ #
99
+ # @api public
100
+ def over(partition: nil, order: nil, frame: nil)
101
+ super(partition: partition, order: order, frame: WINDOW_FRAMES[frame])
102
+ end
103
+
29
104
  # Convert an expression result to another data type
30
105
  #
31
106
  # @example
32
107
  # users.select { bool::cast(json_data.get_text('activated'), :boolean).as(:activated) }
108
+ # users.select { bool::cast(json_data.get_text('activated')).as(:activated) }
33
109
  #
34
110
  # @param [ROM::SQL::Attribute] expr Expression to be cast
35
- # @param [String] db_type Target database type
111
+ # @param [String] db_type Target database type (usually can be inferred from the target data type)
36
112
  #
37
113
  # @return [ROM::SQL::Attribute]
38
114
  #
39
- # @api private
40
- def cast(expr, db_type)
115
+ # @api public
116
+ def cast(expr, db_type = TypeSerializer[:default].call(type))
41
117
  Attribute[type].meta(sql_expr: ::Sequel.cast(expr, db_type))
42
118
  end
43
119
 
44
120
  private
45
121
 
122
+ # @api private
46
123
  def func
47
124
  meta[:func]
48
125
  end
49
126
 
127
+ # @api private
50
128
  def method_missing(meth, *args)
51
129
  if func
52
130
  if func.respond_to?(meth)
@@ -1,4 +1,5 @@
1
1
  require 'logger'
2
+ require 'sequel/core'
2
3
 
3
4
  require 'dry/core/constants'
4
5
 
@@ -31,23 +32,6 @@ module ROM
31
32
  # @return [Hash] Options used for connection
32
33
  attr_reader :options
33
34
 
34
- subscribe('configuration.commands.class.before_build') do |event|
35
- klass = event[:command]
36
- dataset = event[:dataset]
37
- type = dataset.db.database_type
38
-
39
- if type == :postgres
40
- ext =
41
- if klass < Commands::Create
42
- Commands::Postgres::Create
43
- elsif klass < Commands::Update
44
- Commands::Postgres::Update
45
- end
46
-
47
- klass.send(:include, ext) if ext
48
- end
49
- end
50
-
51
35
  # Initialize an SQL gateway
52
36
  #
53
37
  # Gateways are typically initialized via ROM::Configuration object, gateway constructor
@@ -71,14 +55,6 @@ module ROM
71
55
  #
72
56
  # @param [Hash] options connection options
73
57
  #
74
- # @option options [Array<Symbol>] :inferrable_relations
75
- # A list of dataset names that should be inferred. If
76
- # this is set explicitly to an empty array relations
77
- # won't be inferred at all
78
- #
79
- # @option options [Array<Symbol>] :not_inferrable_relations
80
- # A list of dataset names that should NOT be inferred
81
- #
82
58
  # @option options [Array<Symbol>] :extensions
83
59
  # A list of connection extensions supported by Sequel
84
60
  #
@@ -118,7 +94,7 @@ module ROM
118
94
 
119
95
  # Return dataset with the given name
120
96
  #
121
- # Thsi returns a raw Sequel database
97
+ # This returns a raw Sequel database
122
98
  #
123
99
  # @param [String, Symbol] name The dataset name
124
100
  #
@@ -202,6 +178,30 @@ module ROM
202
178
  @database_type ||= connection.database_type.to_sym
203
179
  end
204
180
 
181
+ # Call a SQL function
182
+ #
183
+ # @example
184
+ # gateway.(:upper, 'John Doe') # => "JOHN DOE"
185
+ #
186
+ # @param [Symbol] function Function name
187
+ # @param [Array<Object>] args Function arguments
188
+ #
189
+ # @return [Object]
190
+ #
191
+ # @api public
192
+ def call(function, *args)
193
+ connection[Sequel.function(function, *args)].first.values.first
194
+ end
195
+
196
+ # Execute a statement
197
+ #
198
+ # @param [String] statement
199
+ #
200
+ # @api public
201
+ def run(statement)
202
+ connection.run(statement)
203
+ end
204
+
205
205
  private
206
206
 
207
207
  # Connect to database or reuse established connection instance
data/lib/rom/sql/index.rb CHANGED
@@ -24,6 +24,10 @@ module ROM
24
24
  def partial?
25
25
  !predicate.nil?
26
26
  end
27
+
28
+ def can_access?(attribute)
29
+ !partial? && attributes[0].name == attribute.name
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -138,12 +138,12 @@ module ROM
138
138
  end
139
139
 
140
140
  # @api public
141
- def auto_migrate!(conf)
141
+ def auto_migrate!(conf, options = EMPTY_HASH, &block)
142
142
  schemas = conf.relation_classes(self).map do |klass|
143
- klass.schema || klass.schema_proc.call.finalize_attributes!(gateway: self)
143
+ klass.schema_proc.call.finalize_attributes!(gateway: self)
144
144
  end
145
145
 
146
- migrator.auto_migrate!(self, schemas)
146
+ migrator.auto_migrate!(self, schemas, options)
147
147
  end
148
148
  end
149
149
  end
@@ -1,92 +1,18 @@
1
1
  module ROM
2
2
  module SQL
3
3
  module Migration
4
- class Migrator
5
- # @api private
6
- class InlineRunner
7
- attr_reader :gateway
4
+ # @api private
5
+ class InlineRunner < BasicObject
6
+ extend Initializer
8
7
 
9
- def initialize(gateway)
10
- @gateway = gateway
11
- end
8
+ param :connection
12
9
 
13
- def call(changes)
14
- changes.each do |diff|
15
- apply(diff)
16
- end
17
- end
18
-
19
- def apply(diff)
20
- case diff
21
- when SchemaDiff::TableCreated
22
- create_table(diff)
23
- when SchemaDiff::TableAltered
24
- alter_table(diff)
25
- else
26
- raise NotImplementedError
27
- end
28
- end
29
-
30
- def create_table(diff)
31
- gateway.create_table(diff.table_name) do
32
- diff.attributes.each do |attribute|
33
- if attribute.primary_key?
34
- primary_key attribute.name
35
- else
36
- column attribute.name, attribute.type, null: attribute.null?
37
- end
38
- end
39
-
40
- diff.indexes.each do |index|
41
- index index.attributes,
42
- name: index.name,
43
- unique: index.unique?,
44
- type: index.type,
45
- where: index.predicate
46
- end
47
- end
48
- end
49
-
50
- def alter_table(diff)
51
- gateway.connection.alter_table(diff.table_name) do
52
- diff.attribute_changes.each do |attribute|
53
- case attribute
54
- when SchemaDiff::AttributeAdded
55
- add_column attribute.name, attribute.type, null: attribute.null?
56
- when SchemaDiff::AttributeRemoved
57
- drop_column attribute.name
58
- when SchemaDiff::AttributeChanged
59
- if attribute.type_changed?
60
- from, to = attribute.to_a.map(&attribute.method(:unwrap))
61
- raise UnsupportedConversion.new(
62
- "Don't know how to convert #{ from.inspect } to #{ to.inspect }"
63
- )
64
- end
65
-
66
- if attribute.nullability_changed?
67
- if attribute.null?
68
- set_column_allow_null attribute.name
69
- else
70
- set_column_not_null attribute.name
71
- end
72
- end
73
- end
74
- end
10
+ def migration
11
+ yield(connection)
12
+ end
75
13
 
76
- diff.index_changes.each do |index|
77
- case index
78
- when SchemaDiff::IndexAdded
79
- add_index index.attributes,
80
- name: index.name,
81
- unique: index.unique?,
82
- type: index.type,
83
- where: index.predicate
84
- when SchemaDiff::IndexRemoved
85
- drop_index index.attributes, name: index.name
86
- end
87
- end
88
- end
89
- end
14
+ def method_missing(m, *args, &block)
15
+ connection.public_send(m, *args, &block)
90
16
  end
91
17
  end
92
18
  end
@@ -3,7 +3,9 @@ require 'pathname'
3
3
  require 'rom/types'
4
4
  require 'rom/initializer'
5
5
  require 'rom/sql/migration'
6
+ require 'rom/sql/migration/runner'
6
7
  require 'rom/sql/migration/inline_runner'
8
+ require 'rom/sql/migration/writer'
7
9
 
8
10
  module ROM
9
11
  module SQL
@@ -14,11 +16,14 @@ module ROM
14
16
 
15
17
  DEFAULT_PATH = 'db/migrate'.freeze
16
18
  VERSION_FORMAT = '%Y%m%d%H%M%S'.freeze
19
+ DEFAULT_INFERRER = Schema::Inferrer.new.suppress_errors.freeze
17
20
 
18
21
  param :connection
19
22
 
20
23
  option :path, type: ROM::Types.Definition(Pathname), default: -> { DEFAULT_PATH }
21
24
 
25
+ option :inferrer, default: -> { DEFAULT_INFERRER }
26
+
22
27
  # @api private
23
28
  def run(options = {})
24
29
  Sequel::Migrator.run(connection, path.to_s, options)
@@ -35,13 +40,16 @@ module ROM
35
40
  end
36
41
 
37
42
  # @api private
38
- def create_file(name, version = generate_version)
39
- filename = "#{version}_#{name}.rb"
43
+ def create_file(name, version = generate_version, **options)
44
+ sequence = options[:sequence] ? '%03d' % options[:sequence] : nil
45
+ filename = "#{ version }#{ sequence }_#{ name }.rb"
46
+ content = options[:content] || migration_file_content
47
+ path = options[:path] || self.path
40
48
  dirname = Pathname(path)
41
49
  fullpath = dirname.join(filename)
42
50
 
43
51
  FileUtils.mkdir_p(dirname)
44
- File.write(fullpath, migration_file_content)
52
+ File.write(fullpath, content)
45
53
 
46
54
  fullpath
47
55
  end
@@ -57,19 +65,34 @@ module ROM
57
65
  end
58
66
 
59
67
  # @api private
60
- def diff(gateway, inferrer, target)
61
- empty = SQL::Schema.define(target.name)
62
- current = target.with(inferrer.(empty, gateway))
68
+ def auto_migrate!(gateway, schemas, options = EMPTY_HASH, &block)
69
+ diff_finder = SchemaDiff.new(gateway.database_type)
63
70
 
64
- SchemaDiff.new.(current, target)
65
- end
71
+ changes = schemas.map { |target|
72
+ empty = SQL::Schema.define(target.name)
73
+ current = target.with(inferrer.(empty, gateway))
74
+
75
+ diff_finder.(current, target)
76
+ }.reject(&:empty?)
66
77
 
67
- def auto_migrate!(gateway, schemas)
68
- runner = InlineRunner.new(gateway)
69
- inferrer = ROM::SQL::Schema::Inferrer.new.suppress_errors
70
- changes = schemas.map { |schema| diff(gateway, inferrer, schema) }.reject(&:empty?)
78
+ runner = migration_runner(options)
71
79
  runner.(changes)
72
80
  end
81
+
82
+ # @api private
83
+ def migration_runner(options)
84
+ if options[:inline]
85
+ Runner.new(InlineRunner.new(connection))
86
+ else
87
+ counter = 0
88
+ writer = Writer.new do |name, content|
89
+ create_file(name, **options, content: content, sequence: counter)
90
+ counter += 1
91
+ end
92
+
93
+ Runner.new(writer)
94
+ end
95
+ end
73
96
  end
74
97
  end
75
98
  end