rom-sql 3.1.0 → 3.3.3
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 +214 -101
- data/LICENSE +1 -1
- data/README.md +12 -54
- 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 +108 -0
- data/lib/rom/sql.rb +2 -0
- data/lib/rom/sql/associations.rb +2 -0
- data/lib/rom/sql/associations/core.rb +3 -1
- data/lib/rom/sql/associations/many_to_many.rb +3 -1
- 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 +26 -7
- data/lib/rom/sql/attribute_aliasing.rb +28 -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 +11 -3
- 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 +3 -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 +6 -4
- 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 +13 -11
- data/lib/rom/sql/extensions/postgres/types/ltree.rb +25 -23
- data/lib/rom/sql/extensions/postgres/types/network.rb +2 -0
- data/lib/rom/sql/extensions/postgres/types/range.rb +2 -0
- 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 +14 -3
- data/lib/rom/sql/gateway.rb +6 -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 +2 -0
- data/lib/rom/sql/mapper_compiler.rb +2 -0
- data/lib/rom/sql/migration.rb +5 -3
- data/lib/rom/sql/migration/inline_runner.rb +2 -0
- data/lib/rom/sql/migration/migrator.rb +4 -2
- 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 +2 -0
- 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 -2
- data/lib/rom/sql/plugin/nullify.rb +2 -0
- data/lib/rom/sql/plugin/pagination.rb +2 -0
- data/lib/rom/sql/plugins.rb +2 -0
- data/lib/rom/sql/projection_dsl.rb +3 -1
- data/lib/rom/sql/rake_task.rb +2 -0
- data/lib/rom/sql/relation.rb +3 -1
- data/lib/rom/sql/relation/reading.rb +6 -4
- data/lib/rom/sql/relation/writing.rb +2 -0
- data/lib/rom/sql/restriction_dsl.rb +9 -1
- data/lib/rom/sql/schema.rb +16 -2
- data/lib/rom/sql/schema/attributes_inferrer.rb +5 -3
- data/lib/rom/sql/schema/dsl.rb +3 -1
- data/lib/rom/sql/schema/index_dsl.rb +5 -2
- 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 +2 -0
- data/lib/rom/sql/type_extensions.rb +3 -1
- data/lib/rom/sql/type_serializer.rb +2 -0
- data/lib/rom/sql/types.rb +2 -0
- 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 +25 -45
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -1,70 +1,28 @@
|
|
|
1
1
|
[gem]: https://rubygems.org/gems/rom-sql
|
|
2
2
|
[actions]: https://github.com/rom-rb/rom-sql/actions
|
|
3
|
-
[
|
|
4
|
-
[inchpages]: http://inch-ci.org/github/rom-rb/rom-sql
|
|
3
|
+
[codacy]: https://www.codacy.com/gh/rom-rb/rom-sql
|
|
5
4
|
[chat]: https://rom-rb.zulipchat.com
|
|
5
|
+
[inchpages]: http://inch-ci.org/github/rom-rb/rom-sql
|
|
6
6
|
|
|
7
|
-
# rom-sql [][chat]
|
|
8
8
|
|
|
9
9
|
[][gem]
|
|
10
10
|
[][actions]
|
|
11
|
-
[][codacy]
|
|
12
|
+
[][codacy]
|
|
13
13
|
[][inchpages]
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Resources:
|
|
18
|
-
|
|
19
|
-
- [User Documentation](http://rom-rb.org/learn/sql/)
|
|
20
|
-
- [API Documentation](http://rubydoc.info/gems/rom-sql)
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
Add this line to your application's Gemfile:
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
gem 'rom-sql'
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
And then execute:
|
|
31
|
-
|
|
32
|
-
$ bundle
|
|
33
|
-
|
|
34
|
-
Or install it yourself as:
|
|
35
|
-
|
|
36
|
-
$ gem install rom-sql
|
|
37
|
-
|
|
38
|
-
## Docker
|
|
39
|
-
|
|
40
|
-
### Development
|
|
41
|
-
|
|
42
|
-
In order to have reproducible environment for development, Docker can be used. Provided it's installed, in order to start developing, one can simply execute:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
docker-compose run --rm gem "bash"
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
If this is the first time this command is executed, it will take some time to set up the dependencies and build the rom-sql container. This should happen only on first execution and in case dependency images are removed.
|
|
49
|
-
|
|
50
|
-
After dependencies are set container will be started in a bash shell.
|
|
51
|
-
|
|
52
|
-
### Testing
|
|
53
|
-
|
|
54
|
-
In order to test the changes, execute:
|
|
15
|
+
## Links
|
|
55
16
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
bin/run-specs
|
|
59
|
-
```
|
|
17
|
+
* [User documentation](http://rom-rb.org/learn/sql)
|
|
18
|
+
* [API documentation](http://rubydoc.info/gems/rom-sql)
|
|
60
19
|
|
|
61
|
-
|
|
20
|
+
## Supported Ruby versions
|
|
62
21
|
|
|
63
|
-
|
|
22
|
+
This library officially supports the following Ruby versions:
|
|
64
23
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
```
|
|
24
|
+
* MRI >= `2.5`
|
|
25
|
+
* jruby >= `9.2`
|
|
68
26
|
|
|
69
27
|
## License
|
|
70
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,108 @@
|
|
|
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
|
+
ROM::Relation::Composite.include(Composite)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if defined?(JRUBY_VERSION)
|
|
43
|
+
# Allows you to stream returned rows one at a time, instead of
|
|
44
|
+
# collecting the entire result set in memory. Requires the `sequel_pg` gem
|
|
45
|
+
#
|
|
46
|
+
# @see https://github.com/jeremyevans/sequel_pg#streaming- sequel_pg docs
|
|
47
|
+
#
|
|
48
|
+
# @example
|
|
49
|
+
# posts.steam_each { |post| puts CSV.generate_line(post) }
|
|
50
|
+
#
|
|
51
|
+
# @return [Relation]
|
|
52
|
+
#
|
|
53
|
+
# @api publicY_VERSION
|
|
54
|
+
def stream_each
|
|
55
|
+
raise StreamingNotSupportedError, "not supported on jruby"
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
# Allows you to stream returned rows one at a time, instead of
|
|
59
|
+
# collecting the entire result set in memory. Requires the `sequel_pg` gem
|
|
60
|
+
#
|
|
61
|
+
# @see https://github.com/jeremyevans/sequel_pg#streaming- sequel_pg docs
|
|
62
|
+
#
|
|
63
|
+
# @example
|
|
64
|
+
# posts.steam_each { |post| puts CSV.generate_line(post) }
|
|
65
|
+
#
|
|
66
|
+
# @return [Relation]
|
|
67
|
+
#
|
|
68
|
+
# @api public
|
|
69
|
+
def stream_each
|
|
70
|
+
return to_enum unless block_given?
|
|
71
|
+
|
|
72
|
+
ds = dataset.stream
|
|
73
|
+
|
|
74
|
+
if auto_map?
|
|
75
|
+
ds.each { |tuple| yield(mapper.([output_schema[tuple]]).first) }
|
|
76
|
+
else
|
|
77
|
+
ds.each { |tuple| yield(output_schema[tuple]) }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module Combined
|
|
82
|
+
def stream_each
|
|
83
|
+
raise StreamingNotSupportedError, "not supported on combined relations"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
module Composite
|
|
88
|
+
def stream_each
|
|
89
|
+
return to_enum unless block_given?
|
|
90
|
+
|
|
91
|
+
left.stream_each do |tuple|
|
|
92
|
+
yield right.call([tuple]).first
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
ROM.plugins do
|
|
105
|
+
adapter :sql do
|
|
106
|
+
register :pg_streaming, ROM::Plugins::Relation::SQL::Postgres::Streaming, type: :relation
|
|
107
|
+
end
|
|
108
|
+
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
|
|
@@ -19,7 +21,7 @@ module ROM
|
|
|
19
21
|
def wrapped
|
|
20
22
|
new_target = view ? target.send(view) : target
|
|
21
23
|
to_wrap = self.class.allocate
|
|
22
|
-
to_wrap.send(:initialize, definition, options
|
|
24
|
+
to_wrap.send(:initialize, definition, **options, target: new_target)
|
|
23
25
|
to_wrap.wrap
|
|
24
26
|
end
|
|
25
27
|
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
|
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
|
|
|
@@ -19,16 +21,18 @@ module ROM
|
|
|
19
21
|
|
|
20
22
|
OPERATORS = %i[>= <= > <].freeze
|
|
21
23
|
NONSTANDARD_EQUALITY_VALUES = [true, false, nil].freeze
|
|
22
|
-
META_KEYS = %i
|
|
24
|
+
META_KEYS = %i[index foreign_key target sql_expr qualified].freeze
|
|
23
25
|
|
|
24
26
|
# Error raised when an attribute cannot be qualified
|
|
25
27
|
QualifyError = Class.new(StandardError)
|
|
26
28
|
|
|
27
29
|
extend Dry::Core::Cache
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
class << self
|
|
32
|
+
# @api private
|
|
33
|
+
def [](type, options = EMPTY_HASH)
|
|
34
|
+
fetch_or_store([type, options]) { new(type, **options) }
|
|
35
|
+
end
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
# Return a new attribute in its canonical form
|
|
@@ -63,6 +67,21 @@ module ROM
|
|
|
63
67
|
end
|
|
64
68
|
end
|
|
65
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
|
+
|
|
66
85
|
# Return a new attribute marked as joined
|
|
67
86
|
#
|
|
68
87
|
# Whenever you join two schemas, the right schema's attribute
|
|
@@ -281,11 +300,11 @@ module ROM
|
|
|
281
300
|
# @api private
|
|
282
301
|
def to_sql_name
|
|
283
302
|
@_to_sql_name ||=
|
|
284
|
-
if qualified? &&
|
|
303
|
+
if qualified? && aliased_projection?
|
|
285
304
|
Sequel.qualify(table_name, name).as(self.alias)
|
|
286
305
|
elsif qualified?
|
|
287
306
|
Sequel.qualify(table_name, name)
|
|
288
|
-
elsif
|
|
307
|
+
elsif aliased_projection?
|
|
289
308
|
Sequel.as(name, self.alias)
|
|
290
309
|
else
|
|
291
310
|
Sequel[name]
|
|
@@ -318,7 +337,7 @@ 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)
|
|
322
341
|
end
|
|
323
342
|
|
|
324
343
|
# Wrap a value with the type, it allows using attribute and type specific methods
|