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
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-2020 rom-rb team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,38 +1,28 @@
|
|
|
1
1
|
[gem]: https://rubygems.org/gems/rom-sql
|
|
2
|
-
[
|
|
3
|
-
[
|
|
2
|
+
[actions]: https://github.com/rom-rb/rom-sql/actions
|
|
3
|
+
[codacy]: https://www.codacy.com/gh/rom-rb/rom-sql
|
|
4
|
+
[chat]: https://rom-rb.zulipchat.com
|
|
4
5
|
[inchpages]: http://inch-ci.org/github/rom-rb/rom-sql
|
|
5
6
|
|
|
6
|
-
# rom-sql
|
|
7
|
+
# rom-sql [][chat]
|
|
7
8
|
|
|
8
9
|
[][gem]
|
|
9
|
-
[][actions]
|
|
11
|
+
[][codacy]
|
|
12
|
+
[][codacy]
|
|
12
13
|
[][inchpages]
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
## Links
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
* [User documentation](http://rom-rb.org/learn/sql)
|
|
18
|
+
* [API documentation](http://rubydoc.info/gems/rom-sql)
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
- [API Documentation](http://rubydoc.info/gems/rom-sql)
|
|
20
|
+
## Supported Ruby versions
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
This library officially supports the following Ruby versions:
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```ruby
|
|
26
|
-
gem 'rom-sql'
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
And then execute:
|
|
30
|
-
|
|
31
|
-
$ bundle
|
|
32
|
-
|
|
33
|
-
Or install it yourself as:
|
|
34
|
-
|
|
35
|
-
$ gem install rom-sql
|
|
24
|
+
* MRI >= `2.5`
|
|
25
|
+
* jruby >= `9.2`
|
|
36
26
|
|
|
37
27
|
## License
|
|
38
28
|
|
data/lib/rom-sql.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ROM
|
|
2
4
|
module Plugins
|
|
3
5
|
module Relation
|
|
@@ -24,14 +26,11 @@ module ROM
|
|
|
24
26
|
#
|
|
25
27
|
# @api public
|
|
26
28
|
def explain(format: :text, **options)
|
|
27
|
-
bool_options = options.map { |opt, value| "#{
|
|
28
|
-
format_option = "FORMAT #{
|
|
29
|
+
bool_options = options.map { |opt, value| "#{opt.to_s.upcase} #{!!value}" }
|
|
30
|
+
format_option = "FORMAT #{format.to_s.upcase}"
|
|
31
|
+
explain_value = [format_option, *bool_options].join(', ')
|
|
29
32
|
|
|
30
|
-
query =
|
|
31
|
-
"EXPLAIN (" <<
|
|
32
|
-
[format_option, *bool_options].join(', ') <<
|
|
33
|
-
") " <<
|
|
34
|
-
dataset.sql
|
|
33
|
+
query = "EXPLAIN (#{explain_value}) #{dataset.sql}"
|
|
35
34
|
|
|
36
35
|
rows = dataset.with_sql(query).map(:'QUERY PLAN')
|
|
37
36
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ROM
|
|
4
|
+
module Plugins
|
|
5
|
+
module Relation
|
|
6
|
+
module SQL
|
|
7
|
+
module Postgres
|
|
8
|
+
# PG-specific extensions which adds `Relation#full_text_search` method
|
|
9
|
+
#
|
|
10
|
+
# @api public
|
|
11
|
+
module FullTextSearch
|
|
12
|
+
# Run a full text search on PostgreSQL.
|
|
13
|
+
# By default, searching for the inclusion of any of the terms in any of the cols.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# posts.full_text_search([:title, :content], 'apples', language: 'english') # => Relation which match the 'apples' phrase
|
|
17
|
+
#
|
|
18
|
+
# @option :headline [String] Append a expression to the selected columns aliased to headline that contains an extract of the matched text.
|
|
19
|
+
#
|
|
20
|
+
# @option :language [String] The language to use for the search (default: 'simple')
|
|
21
|
+
#
|
|
22
|
+
# @option :plain [Boolean] Whether a plain search should be used (default: false). In this case, terms should be a single string, and it will do a search where cols contains all of the words in terms. This ignores search operators in terms.
|
|
23
|
+
#
|
|
24
|
+
# @option :phrase [Boolean] Similar to :plain, but also adding an ILIKE filter to ensure that returned rows also include the exact phrase used.
|
|
25
|
+
#
|
|
26
|
+
# @option :rank [Boolean] Set to true to order by the rank, so that closer matches are returned first.
|
|
27
|
+
#
|
|
28
|
+
# @option :to_tsquery [Symbol] Can be set to :plain or :phrase to specify the function to use to convert the terms to a ts_query.
|
|
29
|
+
#
|
|
30
|
+
# @option :tsquery [Boolean] Specifies the terms argument is already a valid SQL expression returning a tsquery, and can be used directly in the query.
|
|
31
|
+
#
|
|
32
|
+
# @option :tsvector [Boolean] Specifies the cols argument is already a valid SQL expression returning a tsvector, and can be used directly in the query.
|
|
33
|
+
#
|
|
34
|
+
# @return [Relation]
|
|
35
|
+
#
|
|
36
|
+
# @see https://www.postgresql.org/docs/current/textsearch.html PostgreSQL docs
|
|
37
|
+
#
|
|
38
|
+
# @api public
|
|
39
|
+
def full_text_search(*args, &block)
|
|
40
|
+
new dataset.__send__(__method__, *args, &block)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
ROM.plugins do
|
|
50
|
+
adapter :sql do
|
|
51
|
+
register :pg_full_text_search, ROM::Plugins::Relation::SQL::Postgres::FullTextSearch, type: :relation
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ROM
|
|
4
|
+
module Plugins
|
|
5
|
+
module Relation
|
|
6
|
+
module SQL
|
|
7
|
+
module Postgres
|
|
8
|
+
# PG-specific extensions which adds `Relation#stream` method
|
|
9
|
+
#
|
|
10
|
+
# @api public
|
|
11
|
+
module Streaming
|
|
12
|
+
extend Notifications::Listener
|
|
13
|
+
|
|
14
|
+
class StreamingNotSupportedError < StandardError; end
|
|
15
|
+
|
|
16
|
+
subscribe("configuration.gateway.connected") do |opts|
|
|
17
|
+
conn = opts[:connection]
|
|
18
|
+
|
|
19
|
+
next unless conn.database_type.to_sym == :postgres
|
|
20
|
+
|
|
21
|
+
next if defined?(JRUBY_VERSION)
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
require "sequel_pg"
|
|
25
|
+
rescue LoadError
|
|
26
|
+
raise StreamingNotSupportedError, "add sequel_pg to Gemfile to use pg_streaming"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
unless Sequel::Postgres.supports_streaming?
|
|
30
|
+
raise StreamingNotSupportedError, "postgres version does not support streaming"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
conn.extension(:pg_streaming)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.included(klass)
|
|
37
|
+
super
|
|
38
|
+
ROM::Relation::Graph.include(Combined)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if defined?(JRUBY_VERSION)
|
|
42
|
+
# Allows you to stream returned rows one at a time, instead of
|
|
43
|
+
# collecting the entire result set in memory. Requires the `sequel_pg` gem
|
|
44
|
+
#
|
|
45
|
+
# @see https://github.com/jeremyevans/sequel_pg#streaming- sequel_pg docs
|
|
46
|
+
#
|
|
47
|
+
# @example
|
|
48
|
+
# posts.steam_each { |post| puts CSV.generate_line(post) }
|
|
49
|
+
#
|
|
50
|
+
# @return [Relation]
|
|
51
|
+
#
|
|
52
|
+
# @api publicY_VERSION
|
|
53
|
+
def stream_each
|
|
54
|
+
raise StreamingNotSupportedError, "not supported on jruby"
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
# Allows you to stream returned rows one at a time, instead of
|
|
58
|
+
# collecting the entire result set in memory. Requires the `sequel_pg` gem
|
|
59
|
+
#
|
|
60
|
+
# @see https://github.com/jeremyevans/sequel_pg#streaming- sequel_pg docs
|
|
61
|
+
#
|
|
62
|
+
# @example
|
|
63
|
+
# posts.steam_each { |post| puts CSV.generate_line(post) }
|
|
64
|
+
#
|
|
65
|
+
# @return [Relation]
|
|
66
|
+
#
|
|
67
|
+
# @api public
|
|
68
|
+
def stream_each
|
|
69
|
+
return to_enum unless block_given?
|
|
70
|
+
|
|
71
|
+
ds = dataset.stream
|
|
72
|
+
|
|
73
|
+
if auto_map?
|
|
74
|
+
ds.each { |tuple| yield(mapper.([output_schema[tuple]]).first) }
|
|
75
|
+
else
|
|
76
|
+
ds.each { |tuple| yield(output_schema[tuple]) }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
module Combined
|
|
81
|
+
def stream_each
|
|
82
|
+
raise StreamingNotSupportedError, "not supported on combined relations"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
ROM.plugins do
|
|
94
|
+
adapter :sql do
|
|
95
|
+
register :pg_streaming, ROM::Plugins::Relation::SQL::Postgres::Streaming, type: :relation
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/rom/sql.rb
CHANGED
data/lib/rom/sql/associations.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ROM
|
|
2
4
|
module SQL
|
|
3
5
|
module Associations
|
|
@@ -14,6 +16,14 @@ module ROM
|
|
|
14
16
|
|
|
15
17
|
target.where(target_key => target_pks)
|
|
16
18
|
end
|
|
19
|
+
|
|
20
|
+
# @api private
|
|
21
|
+
def wrapped
|
|
22
|
+
new_target = view ? target.send(view) : target
|
|
23
|
+
to_wrap = self.class.allocate
|
|
24
|
+
to_wrap.send(:initialize, definition, **options, target: new_target)
|
|
25
|
+
to_wrap.wrap
|
|
26
|
+
end
|
|
17
27
|
end
|
|
18
28
|
end
|
|
19
29
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'rom/associations/many_to_many'
|
|
2
4
|
require 'rom/sql/associations/core'
|
|
3
5
|
require 'rom/sql/associations/self_ref'
|
|
@@ -18,7 +20,7 @@ module ROM
|
|
|
18
20
|
if target != self.target
|
|
19
21
|
target.schema.merge(join_schema)
|
|
20
22
|
else
|
|
21
|
-
left.schema.project(*columns)
|
|
23
|
+
left.schema.uniq.project(*columns)
|
|
22
24
|
end
|
|
23
25
|
else
|
|
24
26
|
target_schema
|
|
@@ -36,8 +38,14 @@ module ROM
|
|
|
36
38
|
# @api public
|
|
37
39
|
def join(type, source = self.source, target = self.target)
|
|
38
40
|
through_assoc = source.associations[through]
|
|
41
|
+
|
|
42
|
+
# first we join source to intermediary
|
|
39
43
|
joined = through_assoc.join(type, source)
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
# then we join intermediary to target
|
|
46
|
+
target_ds = target.name.dataset
|
|
47
|
+
through_jk = through_assoc.target.associations[target_ds].join_keys
|
|
48
|
+
joined.__send__(type, target_ds, through_jk).qualified
|
|
41
49
|
end
|
|
42
50
|
|
|
43
51
|
# @api public
|
data/lib/rom/sql/attribute.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'sequel/core'
|
|
2
4
|
require 'dry/core/cache'
|
|
3
5
|
|
|
@@ -5,6 +7,8 @@ require 'rom/attribute'
|
|
|
5
7
|
|
|
6
8
|
require 'rom/sql/type_extensions'
|
|
7
9
|
require 'rom/sql/projection_dsl'
|
|
10
|
+
require 'rom/sql/attribute_wrapping'
|
|
11
|
+
require 'rom/sql/attribute_aliasing'
|
|
8
12
|
|
|
9
13
|
module ROM
|
|
10
14
|
module SQL
|
|
@@ -12,41 +16,31 @@ module ROM
|
|
|
12
16
|
#
|
|
13
17
|
# @api public
|
|
14
18
|
class Attribute < ROM::Attribute
|
|
19
|
+
include AttributeWrapping
|
|
20
|
+
include AttributeAliasing
|
|
21
|
+
|
|
15
22
|
OPERATORS = %i[>= <= > <].freeze
|
|
16
23
|
NONSTANDARD_EQUALITY_VALUES = [true, false, nil].freeze
|
|
17
|
-
META_KEYS = %i
|
|
24
|
+
META_KEYS = %i[index foreign_key target sql_expr qualified].freeze
|
|
18
25
|
|
|
19
26
|
# Error raised when an attribute cannot be qualified
|
|
20
27
|
QualifyError = Class.new(StandardError)
|
|
21
28
|
|
|
22
29
|
extend Dry::Core::Cache
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
option :extensions, type: Types::Hash, default: -> { TypeExtensions[type] }
|
|
30
|
-
|
|
31
|
-
# Return a new attribute with an alias
|
|
32
|
-
#
|
|
33
|
-
# @example
|
|
34
|
-
# users[:id].aliased(:user_id)
|
|
35
|
-
#
|
|
36
|
-
# @return [SQL::Attribute]
|
|
37
|
-
#
|
|
38
|
-
# @api public
|
|
39
|
-
def aliased(name)
|
|
40
|
-
super.meta(name: meta.fetch(:name, name), sql_expr: sql_expr.as(name))
|
|
31
|
+
class << self
|
|
32
|
+
# @api private
|
|
33
|
+
def [](type, options = EMPTY_HASH)
|
|
34
|
+
fetch_or_store([type, options]) { new(type, **options) }
|
|
35
|
+
end
|
|
41
36
|
end
|
|
42
|
-
alias_method :as, :aliased
|
|
43
37
|
|
|
44
38
|
# Return a new attribute in its canonical form
|
|
45
39
|
#
|
|
46
40
|
# @api public
|
|
47
41
|
def canonical
|
|
48
42
|
if aliased?
|
|
49
|
-
|
|
43
|
+
with(alias: nil).meta(sql_expr: nil)
|
|
50
44
|
else
|
|
51
45
|
self
|
|
52
46
|
end
|
|
@@ -62,16 +56,32 @@ module ROM
|
|
|
62
56
|
# @api public
|
|
63
57
|
def qualified(table_alias = nil)
|
|
64
58
|
return self if qualified? && table_alias.nil?
|
|
59
|
+
return meta(qualified: false) unless qualifiable?
|
|
65
60
|
|
|
66
61
|
case sql_expr
|
|
67
62
|
when Sequel::SQL::AliasedExpression, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
attr = meta(qualified: table_alias || true)
|
|
64
|
+
attr.meta(sql_expr: attr.to_sql_name)
|
|
70
65
|
else
|
|
71
66
|
raise QualifyError, "can't qualify #{name.inspect} (#{sql_expr.inspect})"
|
|
72
67
|
end
|
|
73
68
|
end
|
|
74
69
|
|
|
70
|
+
# Return a new attribute that is aliased and marked as qualified
|
|
71
|
+
#
|
|
72
|
+
# Intended to be used when passing attributes to `dataset#select`
|
|
73
|
+
#
|
|
74
|
+
# @return [SQL::Attribute]
|
|
75
|
+
#
|
|
76
|
+
# @api public
|
|
77
|
+
def qualified_projection(table_alias = nil)
|
|
78
|
+
if aliased?
|
|
79
|
+
qualified(table_alias).aliased(self.alias)
|
|
80
|
+
else
|
|
81
|
+
qualified(table_alias)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
75
85
|
# Return a new attribute marked as joined
|
|
76
86
|
#
|
|
77
87
|
# Whenever you join two schemas, the right schema's attribute
|
|
@@ -114,6 +124,15 @@ module ROM
|
|
|
114
124
|
meta[:qualified].equal?(true) || meta[:qualified].is_a?(Symbol)
|
|
115
125
|
end
|
|
116
126
|
|
|
127
|
+
# Return if an attribute is qualifiable
|
|
128
|
+
#
|
|
129
|
+
# @return [Boolean]
|
|
130
|
+
#
|
|
131
|
+
# @api public
|
|
132
|
+
def qualifiable?
|
|
133
|
+
!source.nil?
|
|
134
|
+
end
|
|
135
|
+
|
|
117
136
|
# Return a new attribute marked as a FK
|
|
118
137
|
#
|
|
119
138
|
# @return [SQL::Attribute]
|
|
@@ -239,7 +258,7 @@ module ROM
|
|
|
239
258
|
# Create a function DSL from the attribute
|
|
240
259
|
#
|
|
241
260
|
# @example
|
|
242
|
-
# users[:id].func {
|
|
261
|
+
# users[:id].func { integer::count(id).as(:count) }
|
|
243
262
|
#
|
|
244
263
|
# @return [SQL::Function]
|
|
245
264
|
#
|
|
@@ -281,12 +300,12 @@ module ROM
|
|
|
281
300
|
# @api private
|
|
282
301
|
def to_sql_name
|
|
283
302
|
@_to_sql_name ||=
|
|
284
|
-
if qualified? &&
|
|
285
|
-
Sequel.qualify(table_name, name).as(
|
|
303
|
+
if qualified? && aliased_projection?
|
|
304
|
+
Sequel.qualify(table_name, name).as(self.alias)
|
|
286
305
|
elsif qualified?
|
|
287
306
|
Sequel.qualify(table_name, name)
|
|
288
|
-
elsif
|
|
289
|
-
Sequel.as(name,
|
|
307
|
+
elsif aliased_projection?
|
|
308
|
+
Sequel.as(name, self.alias)
|
|
290
309
|
else
|
|
291
310
|
Sequel[name]
|
|
292
311
|
end
|
|
@@ -305,7 +324,7 @@ module ROM
|
|
|
305
324
|
end
|
|
306
325
|
|
|
307
326
|
# @api private
|
|
308
|
-
def
|
|
327
|
+
def meta_options_ast
|
|
309
328
|
meta = super
|
|
310
329
|
meta[:index] = true if indexed?
|
|
311
330
|
meta
|
|
@@ -318,7 +337,41 @@ module ROM
|
|
|
318
337
|
cleaned_meta = meta.reject { |k, _| META_KEYS.include?(k) }
|
|
319
338
|
type = optional? ? right : self.type
|
|
320
339
|
|
|
321
|
-
self.class.new(type.with(meta: cleaned_meta), options)
|
|
340
|
+
self.class.new(type.with(meta: cleaned_meta), **options)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Wrap a value with the type, it allows using attribute and type specific methods
|
|
344
|
+
# on literals and things like this
|
|
345
|
+
#
|
|
346
|
+
# @param [Object] value any SQL-serializable value
|
|
347
|
+
# @return [SQL::Attribute]
|
|
348
|
+
#
|
|
349
|
+
# @api public
|
|
350
|
+
def value(value)
|
|
351
|
+
meta(sql_expr: Sequel[value])
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Build a case expression based on attribute. See SQL::Function#case
|
|
355
|
+
# when you don't have a specific expression after the CASE keyword.
|
|
356
|
+
# Pass the :else keyword to provide the catch-all case, it's mandatory
|
|
357
|
+
# because of the Sequel's API used underneath.
|
|
358
|
+
#
|
|
359
|
+
# @example
|
|
360
|
+
# users.select_append { id.case(1 => `'first'`, else: `'other'`).as(:first_or_not) }
|
|
361
|
+
#
|
|
362
|
+
# @param [Hash] mapping mapping between SQL expressions
|
|
363
|
+
# @return [SQL::Attribute]
|
|
364
|
+
#
|
|
365
|
+
# @api public
|
|
366
|
+
def case(mapping)
|
|
367
|
+
mapping = mapping.dup
|
|
368
|
+
otherwise = mapping.delete(:else) do
|
|
369
|
+
raise ArgumentError, 'provide the default case using the :else keyword'
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
type = mapping.values[0].type
|
|
373
|
+
|
|
374
|
+
Attribute[type].meta(sql_expr: ::Sequel.case(mapping, otherwise, self))
|
|
322
375
|
end
|
|
323
376
|
|
|
324
377
|
private
|
|
@@ -376,6 +429,11 @@ module ROM
|
|
|
376
429
|
end
|
|
377
430
|
end
|
|
378
431
|
|
|
432
|
+
# @api private
|
|
433
|
+
def extensions
|
|
434
|
+
TypeExtensions[type]
|
|
435
|
+
end
|
|
436
|
+
|
|
379
437
|
memoize :joined, :to_sql_name, :table_name, :canonical
|
|
380
438
|
end
|
|
381
439
|
end
|