rom-sql 2.0.0.beta2 → 2.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -0
- data/lib/rom/plugins/relation/sql/postgres/explain.rb +54 -0
- data/lib/rom/sql.rb +1 -1
- data/lib/rom/sql/attribute.rb +17 -18
- data/lib/rom/sql/errors.rb +3 -0
- data/lib/rom/sql/extensions/mysql.rb +1 -1
- data/lib/rom/sql/extensions/mysql/type_builder.rb +28 -0
- data/lib/rom/sql/extensions/postgres.rb +3 -1
- data/lib/rom/sql/extensions/postgres/commands.rb +30 -13
- data/lib/rom/sql/extensions/postgres/{attributes_inferrer.rb → type_builder.rb} +24 -28
- data/lib/rom/sql/extensions/postgres/type_serializer.rb +39 -0
- data/lib/rom/sql/extensions/postgres/types.rb +24 -477
- data/lib/rom/sql/extensions/postgres/types/array.rb +163 -0
- data/lib/rom/sql/extensions/postgres/types/geometric.rb +135 -0
- data/lib/rom/sql/extensions/postgres/types/json.rb +235 -0
- data/lib/rom/sql/extensions/postgres/types/network.rb +15 -0
- data/lib/rom/sql/extensions/sqlite.rb +1 -1
- data/lib/rom/sql/extensions/sqlite/{attributes_inferrer.rb → type_builder.rb} +5 -5
- data/lib/rom/sql/extensions/sqlite/types.rb +8 -3
- data/lib/rom/sql/foreign_key.rb +17 -0
- data/lib/rom/sql/function.rb +86 -8
- data/lib/rom/sql/gateway.rb +26 -26
- data/lib/rom/sql/index.rb +4 -0
- data/lib/rom/sql/migration.rb +3 -3
- data/lib/rom/sql/migration/inline_runner.rb +9 -83
- data/lib/rom/sql/migration/migrator.rb +35 -12
- data/lib/rom/sql/migration/recorder.rb +21 -0
- data/lib/rom/sql/migration/runner.rb +115 -0
- data/lib/rom/sql/migration/schema_diff.rb +108 -53
- data/lib/rom/sql/migration/writer.rb +61 -0
- data/lib/rom/sql/relation.rb +2 -1
- data/lib/rom/sql/relation/reading.rb +63 -3
- data/lib/rom/sql/relation/writing.rb +38 -0
- data/lib/rom/sql/schema.rb +9 -3
- data/lib/rom/sql/schema/attributes_inferrer.rb +3 -119
- data/lib/rom/sql/schema/inferrer.rb +99 -18
- data/lib/rom/sql/schema/type_builder.rb +94 -0
- data/lib/rom/sql/type_dsl.rb +30 -0
- data/lib/rom/sql/type_extensions.rb +11 -6
- data/lib/rom/sql/type_serializer.rb +46 -0
- data/lib/rom/sql/types.rb +12 -0
- data/lib/rom/sql/version.rb +1 -1
- metadata +26 -244
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -17
- data/.rspec +0 -3
- data/.travis.yml +0 -39
- data/.yardopts +0 -2
- data/Gemfile +0 -33
- data/Guardfile +0 -24
- data/LICENSE.txt +0 -22
- data/Rakefile +0 -19
- data/circle.yml +0 -10
- data/lib/rom/sql/extensions/mysql/attributes_inferrer.rb +0 -10
- data/lib/rom/sql/relation/sequel_api.rb +0 -133
- data/log/.gitkeep +0 -0
- data/rom-sql.gemspec +0 -29
- data/spec/extensions/postgres/attribute_spec.rb +0 -217
- data/spec/extensions/postgres/integration_spec.rb +0 -59
- data/spec/extensions/postgres/types_spec.rb +0 -252
- data/spec/extensions/sqlite/types_spec.rb +0 -11
- data/spec/fixtures/migrations/20150403090603_create_carrots.rb +0 -8
- data/spec/integration/associations/many_to_many/custom_fks_spec.rb +0 -76
- data/spec/integration/associations/many_to_many/from_view_spec.rb +0 -88
- data/spec/integration/associations/many_to_many_spec.rb +0 -162
- data/spec/integration/associations/many_to_one/custom_fks_spec.rb +0 -64
- data/spec/integration/associations/many_to_one/from_view_spec.rb +0 -84
- data/spec/integration/associations/many_to_one/self_ref_spec.rb +0 -53
- data/spec/integration/associations/many_to_one_spec.rb +0 -117
- data/spec/integration/associations/one_to_many/custom_fks_spec.rb +0 -54
- data/spec/integration/associations/one_to_many/from_view_spec.rb +0 -57
- data/spec/integration/associations/one_to_many/self_ref_spec.rb +0 -54
- data/spec/integration/associations/one_to_many_spec.rb +0 -86
- data/spec/integration/associations/one_to_one_spec.rb +0 -69
- data/spec/integration/associations/one_to_one_through_spec.rb +0 -92
- data/spec/integration/auto_migrations/errors_spec.rb +0 -31
- data/spec/integration/auto_migrations/indexes_spec.rb +0 -253
- data/spec/integration/auto_migrations/managing_columns_spec.rb +0 -156
- data/spec/integration/auto_migrations/postgres/column_types_spec.rb +0 -63
- data/spec/integration/combine_with_spec.rb +0 -43
- data/spec/integration/commands/create_spec.rb +0 -304
- data/spec/integration/commands/delete_spec.rb +0 -84
- data/spec/integration/commands/update_spec.rb +0 -90
- data/spec/integration/commands/upsert_spec.rb +0 -83
- data/spec/integration/gateway_spec.rb +0 -107
- data/spec/integration/migration_spec.rb +0 -55
- data/spec/integration/plugins/associates/many_to_many_spec.rb +0 -69
- data/spec/integration/plugins/associates_spec.rb +0 -250
- data/spec/integration/plugins/auto_restrictions_spec.rb +0 -74
- data/spec/integration/relation_schema_spec.rb +0 -271
- data/spec/integration/schema/call_spec.rb +0 -24
- data/spec/integration/schema/inferrer/mysql_spec.rb +0 -45
- data/spec/integration/schema/inferrer/postgres_spec.rb +0 -203
- data/spec/integration/schema/inferrer/sqlite_spec.rb +0 -37
- data/spec/integration/schema/inferrer_spec.rb +0 -390
- data/spec/integration/schema/prefix_spec.rb +0 -16
- data/spec/integration/schema/qualified_spec.rb +0 -16
- data/spec/integration/schema/rename_spec.rb +0 -21
- data/spec/integration/schema/view_spec.rb +0 -29
- data/spec/integration/sequel_api_spec.rb +0 -36
- data/spec/integration/setup_spec.rb +0 -26
- data/spec/integration/support/active_support_notifications_spec.rb +0 -24
- data/spec/integration/support/rails_log_subscriber_spec.rb +0 -30
- data/spec/integration/wrap_spec.rb +0 -91
- data/spec/shared/accounts.rb +0 -48
- data/spec/shared/database_setup.rb +0 -70
- data/spec/shared/notes.rb +0 -23
- data/spec/shared/posts.rb +0 -34
- data/spec/shared/puppies.rb +0 -15
- data/spec/shared/relations.rb +0 -8
- data/spec/shared/users.rb +0 -32
- data/spec/shared/users_and_tasks.rb +0 -50
- data/spec/spec_helper.rb +0 -122
- data/spec/support/env_helper.rb +0 -25
- data/spec/support/helpers.rb +0 -24
- data/spec/support/oracle/create_users.sql +0 -7
- data/spec/support/oracle/set_sys_passwords.sql +0 -2
- data/spec/support/test_configuration.rb +0 -16
- data/spec/unit/attribute_spec.rb +0 -104
- data/spec/unit/function_spec.rb +0 -48
- data/spec/unit/gateway_spec.rb +0 -70
- data/spec/unit/logger_spec.rb +0 -14
- data/spec/unit/migration_tasks_spec.rb +0 -111
- data/spec/unit/migrator_spec.rb +0 -25
- data/spec/unit/order_dsl_spec.rb +0 -43
- data/spec/unit/plugin/associates_spec.rb +0 -94
- data/spec/unit/plugin/pagination_spec.rb +0 -91
- data/spec/unit/plugin/timestamp_spec.rb +0 -117
- data/spec/unit/projection_dsl_spec.rb +0 -110
- data/spec/unit/relation/assoc_spec.rb +0 -87
- data/spec/unit/relation/associations_spec.rb +0 -27
- data/spec/unit/relation/avg_spec.rb +0 -11
- data/spec/unit/relation/by_pk_spec.rb +0 -62
- data/spec/unit/relation/dataset_spec.rb +0 -50
- data/spec/unit/relation/distinct_spec.rb +0 -15
- data/spec/unit/relation/exclude_spec.rb +0 -11
- data/spec/unit/relation/exist_predicate_spec.rb +0 -25
- data/spec/unit/relation/exists_spec.rb +0 -18
- data/spec/unit/relation/fetch_spec.rb +0 -21
- data/spec/unit/relation/group_spec.rb +0 -61
- data/spec/unit/relation/having_spec.rb +0 -22
- data/spec/unit/relation/inner_join_spec.rb +0 -158
- data/spec/unit/relation/inspect_spec.rb +0 -11
- data/spec/unit/relation/instrument_spec.rb +0 -45
- data/spec/unit/relation/invert_spec.rb +0 -11
- data/spec/unit/relation/left_join_spec.rb +0 -55
- data/spec/unit/relation/lock_spec.rb +0 -93
- data/spec/unit/relation/map_spec.rb +0 -16
- data/spec/unit/relation/max_spec.rb +0 -11
- data/spec/unit/relation/min_spec.rb +0 -11
- data/spec/unit/relation/order_spec.rb +0 -51
- data/spec/unit/relation/pluck_spec.rb +0 -11
- data/spec/unit/relation/prefix_spec.rb +0 -29
- data/spec/unit/relation/primary_key_spec.rb +0 -27
- data/spec/unit/relation/project_spec.rb +0 -24
- data/spec/unit/relation/qualified_columns_spec.rb +0 -30
- data/spec/unit/relation/qualified_spec.rb +0 -25
- data/spec/unit/relation/read_spec.rb +0 -25
- data/spec/unit/relation/rename_spec.rb +0 -23
- data/spec/unit/relation/right_join_spec.rb +0 -57
- data/spec/unit/relation/select_append_spec.rb +0 -21
- data/spec/unit/relation/select_spec.rb +0 -40
- data/spec/unit/relation/sum_spec.rb +0 -11
- data/spec/unit/relation/union_spec.rb +0 -19
- data/spec/unit/relation/unique_predicate_spec.rb +0 -18
- data/spec/unit/relation/where_spec.rb +0 -133
- data/spec/unit/restriction_dsl_spec.rb +0 -34
- data/spec/unit/schema_spec.rb +0 -25
- 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/
|
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
|
-
|
6
|
-
class
|
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::
|
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
|
6
|
-
module
|
7
|
-
|
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
|
data/lib/rom/sql/function.rb
CHANGED
@@ -1,9 +1,41 @@
|
|
1
|
-
require 'rom/
|
1
|
+
require 'rom/attribute'
|
2
2
|
|
3
3
|
module ROM
|
4
4
|
module SQL
|
5
|
-
# @api
|
6
|
-
class Function < ROM::
|
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
|
-
|
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
|
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)
|
data/lib/rom/sql/gateway.rb
CHANGED
@@ -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
|
-
#
|
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
data/lib/rom/sql/migration.rb
CHANGED
@@ -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.
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
attr_reader :gateway
|
4
|
+
# @api private
|
5
|
+
class InlineRunner < BasicObject
|
6
|
+
extend Initializer
|
8
7
|
|
9
|
-
|
10
|
-
@gateway = gateway
|
11
|
-
end
|
8
|
+
param :connection
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
77
|
-
|
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
|
-
|
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,
|
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
|
61
|
-
|
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
|
-
|
65
|
-
|
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
|
-
|
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
|