rom-sql 2.5.0 → 3.3.0
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 +456 -278
- data/LICENSE +20 -0
- data/README.md +14 -24
- data/lib/rom-sql.rb +2 -0
- data/lib/rom/plugins/relation/sql/auto_restrictions.rb +2 -0
- data/lib/rom/plugins/relation/sql/instrumentation.rb +2 -0
- data/lib/rom/plugins/relation/sql/postgres/explain.rb +6 -7
- data/lib/rom/plugins/relation/sql/postgres/full_text_search.rb +53 -0
- data/lib/rom/plugins/relation/sql/postgres/streaming.rb +97 -0
- data/lib/rom/sql.rb +2 -0
- data/lib/rom/sql/associations.rb +2 -0
- data/lib/rom/sql/associations/core.rb +10 -0
- data/lib/rom/sql/associations/many_to_many.rb +10 -2
- data/lib/rom/sql/associations/many_to_one.rb +2 -0
- data/lib/rom/sql/associations/one_to_many.rb +2 -0
- data/lib/rom/sql/associations/one_to_one.rb +2 -0
- data/lib/rom/sql/associations/one_to_one_through.rb +2 -0
- data/lib/rom/sql/associations/self_ref.rb +2 -0
- data/lib/rom/sql/attribute.rb +87 -29
- data/lib/rom/sql/attribute_aliasing.rb +88 -0
- data/lib/rom/sql/attribute_wrapping.rb +30 -0
- data/lib/rom/sql/commands.rb +2 -0
- data/lib/rom/sql/commands/create.rb +2 -0
- data/lib/rom/sql/commands/delete.rb +2 -0
- data/lib/rom/sql/commands/error_wrapper.rb +2 -0
- data/lib/rom/sql/commands/update.rb +2 -0
- data/lib/rom/sql/dsl.rb +39 -1
- data/lib/rom/sql/error.rb +2 -0
- data/lib/rom/sql/errors.rb +2 -0
- data/lib/rom/sql/extensions.rb +2 -0
- data/lib/rom/sql/extensions/active_support_notifications.rb +2 -0
- data/lib/rom/sql/extensions/mysql.rb +2 -0
- data/lib/rom/sql/extensions/mysql/type_builder.rb +2 -0
- data/lib/rom/sql/extensions/postgres.rb +4 -0
- data/lib/rom/sql/extensions/postgres/commands.rb +3 -1
- data/lib/rom/sql/extensions/postgres/type_builder.rb +6 -4
- data/lib/rom/sql/extensions/postgres/type_serializer.rb +2 -0
- data/lib/rom/sql/extensions/postgres/types.rb +2 -0
- data/lib/rom/sql/extensions/postgres/types/array.rb +9 -8
- data/lib/rom/sql/extensions/postgres/types/array_types.rb +3 -1
- data/lib/rom/sql/extensions/postgres/types/geometric.rb +2 -0
- data/lib/rom/sql/extensions/postgres/types/json.rb +76 -19
- data/lib/rom/sql/extensions/postgres/types/ltree.rb +27 -25
- data/lib/rom/sql/extensions/postgres/types/network.rb +2 -0
- data/lib/rom/sql/extensions/postgres/types/range.rb +6 -4
- data/lib/rom/sql/extensions/rails_log_subscriber.rb +2 -0
- data/lib/rom/sql/extensions/sqlite.rb +2 -0
- data/lib/rom/sql/extensions/sqlite/type_builder.rb +2 -0
- data/lib/rom/sql/extensions/sqlite/types.rb +2 -0
- data/lib/rom/sql/foreign_key.rb +3 -1
- data/lib/rom/sql/function.rb +84 -6
- data/lib/rom/sql/gateway.rb +9 -1
- data/lib/rom/sql/group_dsl.rb +2 -0
- data/lib/rom/sql/index.rb +2 -0
- data/lib/rom/sql/join_dsl.rb +11 -0
- data/lib/rom/sql/mapper_compiler.rb +14 -3
- data/lib/rom/sql/migration.rb +20 -3
- data/lib/rom/sql/migration/inline_runner.rb +2 -0
- data/lib/rom/sql/migration/migrator.rb +5 -3
- data/lib/rom/sql/migration/recorder.rb +2 -0
- data/lib/rom/sql/migration/runner.rb +4 -2
- data/lib/rom/sql/migration/schema_diff.rb +4 -2
- data/lib/rom/sql/migration/template.rb +2 -0
- data/lib/rom/sql/migration/writer.rb +12 -4
- data/lib/rom/sql/order_dsl.rb +2 -0
- data/lib/rom/sql/plugin/associates.rb +4 -3
- data/lib/rom/sql/plugin/nullify.rb +37 -0
- data/lib/rom/sql/plugin/pagination.rb +22 -0
- data/lib/rom/sql/plugins.rb +4 -0
- data/lib/rom/sql/projection_dsl.rb +10 -4
- data/lib/rom/sql/rake_task.rb +2 -0
- data/lib/rom/sql/relation.rb +3 -1
- data/lib/rom/sql/relation/reading.rb +105 -16
- data/lib/rom/sql/relation/writing.rb +2 -0
- data/lib/rom/sql/restriction_dsl.rb +8 -8
- data/lib/rom/sql/schema.rb +16 -2
- data/lib/rom/sql/schema/attributes_inferrer.rb +7 -5
- data/lib/rom/sql/schema/dsl.rb +3 -1
- data/lib/rom/sql/schema/index_dsl.rb +8 -3
- data/lib/rom/sql/schema/inferrer.rb +12 -8
- data/lib/rom/sql/schema/type_builder.rb +4 -2
- data/lib/rom/sql/spec/support.rb +5 -3
- data/lib/rom/sql/tasks/migration_tasks.rake +16 -11
- data/lib/rom/sql/transaction.rb +2 -0
- data/lib/rom/sql/type_dsl.rb +3 -1
- data/lib/rom/sql/type_extensions.rb +4 -4
- data/lib/rom/sql/type_serializer.rb +3 -1
- data/lib/rom/sql/types.rb +6 -4
- data/lib/rom/sql/version.rb +3 -1
- data/lib/rom/sql/wrap.rb +2 -0
- data/lib/rom/types/values.rb +2 -0
- metadata +39 -37
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'pathname'
|
|
2
4
|
|
|
3
5
|
require 'rom/types'
|
|
@@ -20,7 +22,7 @@ module ROM
|
|
|
20
22
|
|
|
21
23
|
param :connection
|
|
22
24
|
|
|
23
|
-
option :path, type: ROM::Types.
|
|
25
|
+
option :path, type: ROM::Types.Nominal(Pathname), default: -> { DEFAULT_PATH }
|
|
24
26
|
|
|
25
27
|
option :inferrer, default: -> { DEFAULT_INFERRER }
|
|
26
28
|
|
|
@@ -65,12 +67,12 @@ module ROM
|
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
# @api private
|
|
68
|
-
def auto_migrate!(gateway, schemas, options = EMPTY_HASH
|
|
70
|
+
def auto_migrate!(gateway, schemas, options = EMPTY_HASH)
|
|
69
71
|
diff_finder = SchemaDiff.new(gateway.database_type)
|
|
70
72
|
|
|
71
73
|
changes = schemas.map { |target|
|
|
72
74
|
empty = SQL::Schema.define(target.name)
|
|
73
|
-
current = target.with(inferrer.(empty, gateway))
|
|
75
|
+
current = target.with(**inferrer.(empty, gateway))
|
|
74
76
|
|
|
75
77
|
diff_finder.(current, target)
|
|
76
78
|
}.reject(&:empty?)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ROM
|
|
2
4
|
module SQL
|
|
3
5
|
module Migration
|
|
@@ -67,8 +69,8 @@ module ROM
|
|
|
67
69
|
if attribute.type_changed?
|
|
68
70
|
from, to = attribute.current.unwrap, attribute.target.unwrap
|
|
69
71
|
raise UnsupportedConversion.new(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
"Don't know how to convert #{from.inspect} to #{to.inspect}"
|
|
73
|
+
)
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
if attribute.nullability_changed?
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/sql/type_serializer'
|
|
2
4
|
|
|
3
5
|
module ROM
|
|
@@ -75,8 +77,8 @@ module ROM
|
|
|
75
77
|
attr.primary_key?
|
|
76
78
|
end
|
|
77
79
|
|
|
78
|
-
def unwrap(
|
|
79
|
-
|
|
80
|
+
def unwrap(attr)
|
|
81
|
+
attr.optional? ? SQL::Attribute[attr.right, attr.options].meta(attr.meta) : attr
|
|
80
82
|
end
|
|
81
83
|
end
|
|
82
84
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/sql/migration/recorder'
|
|
2
4
|
|
|
3
5
|
module ROM
|
|
@@ -32,7 +34,7 @@ module ROM
|
|
|
32
34
|
operations.each do |operation|
|
|
33
35
|
op, args, nested = operation
|
|
34
36
|
buffer << indent << op.to_s << ' '
|
|
35
|
-
write_arguments(buffer,
|
|
37
|
+
write_arguments(buffer, args)
|
|
36
38
|
|
|
37
39
|
if !nested.empty?
|
|
38
40
|
buffer << ' do'
|
|
@@ -42,9 +44,15 @@ module ROM
|
|
|
42
44
|
end
|
|
43
45
|
end
|
|
44
46
|
|
|
45
|
-
def write_arguments(buffer,
|
|
47
|
+
def write_arguments(buffer, args)
|
|
48
|
+
if args.last.is_a?(::Hash)
|
|
49
|
+
args, options = args[0...-1], args.last
|
|
50
|
+
else
|
|
51
|
+
options = EMPTY_HASH
|
|
52
|
+
end
|
|
53
|
+
|
|
46
54
|
buffer << args.map(&:inspect).join(', ')
|
|
47
|
-
|
|
55
|
+
options.each do |key, value|
|
|
48
56
|
buffer << ', ' << key.to_s << ': ' << value.inspect
|
|
49
57
|
end
|
|
50
58
|
end
|
|
@@ -53,7 +61,7 @@ module ROM
|
|
|
53
61
|
create_or_alter, args = op
|
|
54
62
|
table_name = args[0]
|
|
55
63
|
|
|
56
|
-
"#{
|
|
64
|
+
"#{create_or_alter.to_s.sub('_table', '')}_#{table_name}"
|
|
57
65
|
end
|
|
58
66
|
end
|
|
59
67
|
end
|
data/lib/rom/sql/order_dsl.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/sql/associations'
|
|
2
4
|
|
|
3
5
|
module ROM
|
|
@@ -48,7 +50,7 @@ module ROM
|
|
|
48
50
|
# @see ROM::Command::ClassInterface.build
|
|
49
51
|
#
|
|
50
52
|
# @api public
|
|
51
|
-
def build(relation, options
|
|
53
|
+
def build(relation, **options)
|
|
52
54
|
command = super
|
|
53
55
|
|
|
54
56
|
configured_assocs = command.configured_associations
|
|
@@ -146,8 +148,7 @@ module ROM
|
|
|
146
148
|
def with_association(name, opts = EMPTY_HASH)
|
|
147
149
|
self.class.build(
|
|
148
150
|
relation,
|
|
149
|
-
**options,
|
|
150
|
-
associations: associations.merge(name => opts)
|
|
151
|
+
**options, associations: associations.merge(name => opts)
|
|
151
152
|
)
|
|
152
153
|
end
|
|
153
154
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ROM
|
|
4
|
+
module SQL
|
|
5
|
+
module Plugin
|
|
6
|
+
# Nullify relation by
|
|
7
|
+
#
|
|
8
|
+
# @api public
|
|
9
|
+
module Nullify
|
|
10
|
+
if defined? JRUBY_VERSION
|
|
11
|
+
# Returns a relation that will never issue a query to the database. It
|
|
12
|
+
# implements the null object pattern for relations.
|
|
13
|
+
# Dataset#nullify doesn't work on JRuby, hence we fall back to SQL
|
|
14
|
+
#
|
|
15
|
+
# @api public
|
|
16
|
+
def nullify
|
|
17
|
+
where { `1 = 0` }
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
# Returns a relation that will never issue a query to the database. It
|
|
21
|
+
# implements the null object pattern for relations.
|
|
22
|
+
#
|
|
23
|
+
# @see http://sequel.jeremyevans.net/rdoc-plugins/files/lib/sequel/extensions/null_dataset_rb.html
|
|
24
|
+
# @example result will always be empty, regardless if records exists
|
|
25
|
+
# users.where(name: 'Alice').nullify
|
|
26
|
+
#
|
|
27
|
+
# @return [SQL::Relation]
|
|
28
|
+
#
|
|
29
|
+
# @api public
|
|
30
|
+
def nullify
|
|
31
|
+
new(dataset.where { `1 = 0` }.__send__(__method__))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/initializer'
|
|
2
4
|
|
|
3
5
|
module ROM
|
|
@@ -72,6 +74,26 @@ module ROM
|
|
|
72
74
|
(total / per_page.to_f).ceil
|
|
73
75
|
end
|
|
74
76
|
|
|
77
|
+
# Return one-based index of first tuple in page
|
|
78
|
+
#
|
|
79
|
+
# @return [Integer]
|
|
80
|
+
#
|
|
81
|
+
# @api public
|
|
82
|
+
def first_in_page
|
|
83
|
+
((current_page - 1) * per_page) + 1
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Return one-based index of last tuple in page
|
|
87
|
+
#
|
|
88
|
+
# @return [Integer]
|
|
89
|
+
#
|
|
90
|
+
# @api public
|
|
91
|
+
def last_in_page
|
|
92
|
+
return total if current_page == total_pages
|
|
93
|
+
|
|
94
|
+
current_page * per_page
|
|
95
|
+
end
|
|
96
|
+
|
|
75
97
|
# @api private
|
|
76
98
|
def at(dataset, current_page, per_page = self.per_page)
|
|
77
99
|
current_page = current_page.to_i
|
data/lib/rom/sql/plugins.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/plugins/relation/sql/instrumentation'
|
|
2
4
|
require 'rom/plugins/relation/sql/auto_restrictions'
|
|
3
5
|
|
|
4
6
|
require 'rom/sql/plugin/associates'
|
|
7
|
+
require 'rom/sql/plugin/nullify'
|
|
5
8
|
require 'rom/sql/plugin/pagination'
|
|
6
9
|
|
|
7
10
|
ROM.plugins do
|
|
8
11
|
adapter :sql do
|
|
12
|
+
register :nullify, ROM::SQL::Plugin::Nullify, type: :relation
|
|
9
13
|
register :pagination, ROM::SQL::Plugin::Pagination, type: :relation
|
|
10
14
|
register :associates, ROM::SQL::Plugin::Associates, type: :command
|
|
11
15
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/sql/dsl'
|
|
2
4
|
require 'rom/sql/function'
|
|
3
5
|
|
|
@@ -29,13 +31,13 @@ module ROM
|
|
|
29
31
|
# users.select { function(:count, :id).as(:total) }
|
|
30
32
|
#
|
|
31
33
|
# @param [Symbol] name SQL function
|
|
32
|
-
# @param [Symbol]
|
|
34
|
+
# @param [Symbol] attrs
|
|
33
35
|
#
|
|
34
36
|
# @return [Rom::SQL::Function]
|
|
35
37
|
#
|
|
36
38
|
# @api public
|
|
37
|
-
def function(name,
|
|
38
|
-
::ROM::SQL::Function.new(::ROM::Types::Any, schema: schema).public_send(name,
|
|
39
|
+
def function(name, *attrs)
|
|
40
|
+
::ROM::SQL::Function.new(::ROM::Types::Any, schema: schema).public_send(name, *attrs)
|
|
39
41
|
end
|
|
40
42
|
alias_method :f, :function
|
|
41
43
|
|
|
@@ -54,7 +56,11 @@ module ROM
|
|
|
54
56
|
type = type(meth)
|
|
55
57
|
|
|
56
58
|
if type
|
|
57
|
-
|
|
59
|
+
if args.empty?
|
|
60
|
+
::ROM::SQL::Function.new(type, schema: schema)
|
|
61
|
+
else
|
|
62
|
+
::ROM::SQL::Attribute[type].value(args[0])
|
|
63
|
+
end
|
|
58
64
|
else
|
|
59
65
|
super
|
|
60
66
|
end
|
data/lib/rom/sql/rake_task.rb
CHANGED
data/lib/rom/sql/relation.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/sql/types'
|
|
2
4
|
require 'rom/sql/schema'
|
|
3
5
|
require 'rom/sql/attribute'
|
|
@@ -35,7 +37,7 @@ module ROM
|
|
|
35
37
|
table = opts[:from].first
|
|
36
38
|
|
|
37
39
|
if db.table_exists?(table)
|
|
38
|
-
select(*schema.
|
|
40
|
+
select(*schema.qualified_projection).order(*schema.project(*schema.primary_key_names).qualified)
|
|
39
41
|
else
|
|
40
42
|
self
|
|
41
43
|
end
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/support/inflector'
|
|
4
|
+
require 'rom/sql/join_dsl'
|
|
2
5
|
|
|
3
6
|
module ROM
|
|
4
7
|
module SQL
|
|
@@ -147,15 +150,19 @@ module ROM
|
|
|
147
150
|
|
|
148
151
|
# Pluck values from a specific column
|
|
149
152
|
#
|
|
150
|
-
# @example
|
|
153
|
+
# @example Single value
|
|
151
154
|
# users.pluck(:id)
|
|
152
|
-
# # [1, 2
|
|
155
|
+
# # [1, 2]
|
|
156
|
+
#
|
|
157
|
+
# @example Multiple values
|
|
158
|
+
# users.pluck(:id, :name)
|
|
159
|
+
# # [[1, "Jane"] [2, "Joe"]]
|
|
153
160
|
#
|
|
154
161
|
# @return [Array]
|
|
155
162
|
#
|
|
156
163
|
# @api public
|
|
157
|
-
def pluck(
|
|
158
|
-
select(
|
|
164
|
+
def pluck(*names)
|
|
165
|
+
select(*names).map(names.length == 1 ? names.first : names)
|
|
159
166
|
end
|
|
160
167
|
|
|
161
168
|
# Rename columns in a relation
|
|
@@ -217,7 +224,7 @@ module ROM
|
|
|
217
224
|
# Project relation using column names and projection DSL
|
|
218
225
|
#
|
|
219
226
|
# @example using attributes
|
|
220
|
-
# users.select(:id) {
|
|
227
|
+
# users.select(:id) { integer::count(id).as(:count) }.group(:id).first
|
|
221
228
|
# # {:id => 1, :count => 1}
|
|
222
229
|
#
|
|
223
230
|
# users.select { [id, name] }
|
|
@@ -387,7 +394,7 @@ module ROM
|
|
|
387
394
|
# users.
|
|
388
395
|
# qualified.
|
|
389
396
|
# left_join(tasks).
|
|
390
|
-
# select { [id, name,
|
|
397
|
+
# select { [id, name, integer::count(:tasks__id).as(:task_count)] }.
|
|
391
398
|
# group(users[:id].qualified).
|
|
392
399
|
# having(task_count: 2)
|
|
393
400
|
# first
|
|
@@ -402,7 +409,7 @@ module ROM
|
|
|
402
409
|
# users.
|
|
403
410
|
# qualified.
|
|
404
411
|
# left_join(tasks).
|
|
405
|
-
# select { [id, name,
|
|
412
|
+
# select { [id, name, integer::count(:tasks__id).as(:task_count)] }.
|
|
406
413
|
# group(users[:id].qualified).
|
|
407
414
|
# having { count(id.qualified) >= 1 }.
|
|
408
415
|
# first
|
|
@@ -558,6 +565,16 @@ module ROM
|
|
|
558
565
|
#
|
|
559
566
|
# @param [Relation] relation A relation for join
|
|
560
567
|
#
|
|
568
|
+
# @overload join(relation, &block)
|
|
569
|
+
# Join with another relation using DSL
|
|
570
|
+
#
|
|
571
|
+
# @example
|
|
572
|
+
# users.join(tasks) { |users:, tasks:|
|
|
573
|
+
# tasks[:user_id].is(users[:id]) & users[:name].is('John')
|
|
574
|
+
# }
|
|
575
|
+
#
|
|
576
|
+
# @param [Relation] relation A relation for join
|
|
577
|
+
#
|
|
561
578
|
# @return [Relation]
|
|
562
579
|
#
|
|
563
580
|
# @api public
|
|
@@ -598,6 +615,16 @@ module ROM
|
|
|
598
615
|
#
|
|
599
616
|
# @param [Relation] relation A relation for left_join
|
|
600
617
|
#
|
|
618
|
+
# @overload join(relation, &block)
|
|
619
|
+
# Join with another relation using DSL
|
|
620
|
+
#
|
|
621
|
+
# @example
|
|
622
|
+
# users.left_join(tasks) { |users:, tasks:|
|
|
623
|
+
# tasks[:user_id].is(users[:id]) & users[:name].is('John')
|
|
624
|
+
# }
|
|
625
|
+
#
|
|
626
|
+
# @param [Relation] relation A relation for left_join
|
|
627
|
+
#
|
|
601
628
|
# @return [Relation]
|
|
602
629
|
#
|
|
603
630
|
# @api public
|
|
@@ -637,6 +664,16 @@ module ROM
|
|
|
637
664
|
#
|
|
638
665
|
# @param [Relation] relation A relation for right_join
|
|
639
666
|
#
|
|
667
|
+
# @overload join(relation, &block)
|
|
668
|
+
# Join with another relation using DSL
|
|
669
|
+
#
|
|
670
|
+
# @example
|
|
671
|
+
# users.right_join(tasks) { |users:, tasks:|
|
|
672
|
+
# tasks[:user_id].is(users[:id]) & users[:name].is('John')
|
|
673
|
+
# }
|
|
674
|
+
#
|
|
675
|
+
# @param [Relation] relation A relation for right_join
|
|
676
|
+
#
|
|
640
677
|
# @return [Relation]
|
|
641
678
|
#
|
|
642
679
|
# @api public
|
|
@@ -772,7 +809,16 @@ module ROM
|
|
|
772
809
|
#
|
|
773
810
|
# @api public
|
|
774
811
|
def union(relation, options = EMPTY_HASH, &block)
|
|
775
|
-
|
|
812
|
+
# We use the original relation name here if both relations have the
|
|
813
|
+
# same name. This makes it so if the user at some point references
|
|
814
|
+
# the relation directly by name later on things won't break in
|
|
815
|
+
# confusing ways.
|
|
816
|
+
same_relation = name == relation.name
|
|
817
|
+
alias_name = same_relation ? name : "#{name.to_sym}__#{relation.name.to_sym}"
|
|
818
|
+
opts = { alias: alias_name.to_sym, **options }
|
|
819
|
+
|
|
820
|
+
new_schema = schema.qualified(opts[:alias])
|
|
821
|
+
new_schema.(new(dataset.__send__(__method__, relation.dataset, opts, &block)))
|
|
776
822
|
end
|
|
777
823
|
|
|
778
824
|
# Checks whether a relation has at least one tuple
|
|
@@ -847,8 +893,8 @@ module ROM
|
|
|
847
893
|
# @yieldparam relation [Array]
|
|
848
894
|
#
|
|
849
895
|
# @api public
|
|
850
|
-
def lock(options
|
|
851
|
-
clause = lock_clause(options)
|
|
896
|
+
def lock(**options, &block)
|
|
897
|
+
clause = lock_clause(**options)
|
|
852
898
|
|
|
853
899
|
if block
|
|
854
900
|
transaction do
|
|
@@ -944,17 +990,44 @@ module ROM
|
|
|
944
990
|
# @example adding number of user tasks
|
|
945
991
|
# tasks = relations[:tasks]
|
|
946
992
|
# users = relations[:users]
|
|
947
|
-
# user_tasks = tasks.where(tasks[:user_id].is(users[:id])
|
|
948
|
-
# tasks_count = user_tasks.select {
|
|
993
|
+
# user_tasks = tasks.where(tasks[:user_id].is(users[:id]))
|
|
994
|
+
# tasks_count = user_tasks.select { integer::count(id) }
|
|
949
995
|
# users.select_append(tasks_count.as(:tasks_count))
|
|
950
996
|
#
|
|
951
997
|
# @return [SQL::Attribute]
|
|
952
998
|
def query
|
|
953
999
|
attr = schema.to_a[0]
|
|
954
|
-
subquery = schema.project(attr).(self).dataset
|
|
1000
|
+
subquery = schema.project(attr).(self).dataset
|
|
955
1001
|
SQL::Attribute[attr.type].meta(sql_expr: subquery)
|
|
956
1002
|
end
|
|
957
1003
|
|
|
1004
|
+
# Discard restrictions in `WHERE` and `HAVING` clauses
|
|
1005
|
+
#
|
|
1006
|
+
# @example calling .by_pk has no effect
|
|
1007
|
+
# users.by_pk(1).unfiltered
|
|
1008
|
+
#
|
|
1009
|
+
# @return [SQL::Relation]
|
|
1010
|
+
#
|
|
1011
|
+
# @api public
|
|
1012
|
+
def unfiltered
|
|
1013
|
+
new(dataset.__send__(__method__))
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
# Wrap other relations using association names
|
|
1017
|
+
#
|
|
1018
|
+
# @example
|
|
1019
|
+
# tasks.wrap(:owner)
|
|
1020
|
+
#
|
|
1021
|
+
# @param [Array<Symbol>] names A list with association identifiers
|
|
1022
|
+
#
|
|
1023
|
+
# @return [Wrap]
|
|
1024
|
+
#
|
|
1025
|
+
# @api public
|
|
1026
|
+
def wrap(*names)
|
|
1027
|
+
others = names.map { |name| associations[name].wrapped }
|
|
1028
|
+
wrap_around(*others)
|
|
1029
|
+
end
|
|
1030
|
+
|
|
958
1031
|
private
|
|
959
1032
|
|
|
960
1033
|
# Build a locking clause
|
|
@@ -965,7 +1038,7 @@ module ROM
|
|
|
965
1038
|
stmt << ' OF ' << Array(of).join(', ') if of
|
|
966
1039
|
|
|
967
1040
|
if skip_locked
|
|
968
|
-
raise ArgumentError,
|
|
1041
|
+
raise ArgumentError, 'SKIP LOCKED cannot be used with (NO)WAIT clause' if !wait.nil?
|
|
969
1042
|
|
|
970
1043
|
stmt << ' SKIP LOCKED'
|
|
971
1044
|
else
|
|
@@ -988,6 +1061,8 @@ module ROM
|
|
|
988
1061
|
if k.is_a?(Symbol) && schema.canonical.key?(k)
|
|
989
1062
|
type = schema.canonical[k]
|
|
990
1063
|
h[k] = v.is_a?(Array) ? v.map { |e| type[e] } : type[v]
|
|
1064
|
+
elsif k.is_a?(ROM::SQL::Attribute)
|
|
1065
|
+
h[k.canonical] = v
|
|
991
1066
|
else
|
|
992
1067
|
h[k] = v
|
|
993
1068
|
end
|
|
@@ -999,16 +1074,30 @@ module ROM
|
|
|
999
1074
|
# @api private
|
|
1000
1075
|
def __join__(type, other, join_cond = EMPTY_HASH, opts = EMPTY_HASH, &block)
|
|
1001
1076
|
if other.is_a?(Symbol) || other.is_a?(ROM::Relation::Name)
|
|
1002
|
-
if join_cond.
|
|
1077
|
+
if join_cond.equal?(EMPTY_HASH) && !block
|
|
1003
1078
|
assoc = associations[other]
|
|
1004
1079
|
assoc.join(type, self)
|
|
1080
|
+
elsif block
|
|
1081
|
+
__join__(type, other, JoinDSL.new(schema).(&block), opts)
|
|
1005
1082
|
else
|
|
1006
1083
|
new(dataset.__send__(type, other.to_sym, join_cond, opts, &block))
|
|
1007
1084
|
end
|
|
1008
1085
|
elsif other.is_a?(Sequel::SQL::AliasedExpression)
|
|
1009
1086
|
new(dataset.__send__(type, other, join_cond, opts, &block))
|
|
1010
1087
|
elsif other.respond_to?(:name) && other.name.is_a?(Relation::Name)
|
|
1011
|
-
|
|
1088
|
+
if block
|
|
1089
|
+
join_cond = JoinDSL.new(schema).(&block)
|
|
1090
|
+
|
|
1091
|
+
if other.name.aliaz
|
|
1092
|
+
join_opts = { table_alias: other.name.aliaz }
|
|
1093
|
+
else
|
|
1094
|
+
join_opts = EMPTY_HASH
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
new(dataset.__send__(type, other.name.dataset.to_sym, join_cond, join_opts))
|
|
1098
|
+
else
|
|
1099
|
+
associations[other.name.key].join(type, self, other)
|
|
1100
|
+
end
|
|
1012
1101
|
else
|
|
1013
1102
|
raise ArgumentError, "+other+ must be either a symbol or a relation, #{other.class} given"
|
|
1014
1103
|
end
|