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.
- 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
|