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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +456 -278
  3. data/LICENSE +20 -0
  4. data/README.md +14 -24
  5. data/lib/rom-sql.rb +2 -0
  6. data/lib/rom/plugins/relation/sql/auto_restrictions.rb +2 -0
  7. data/lib/rom/plugins/relation/sql/instrumentation.rb +2 -0
  8. data/lib/rom/plugins/relation/sql/postgres/explain.rb +6 -7
  9. data/lib/rom/plugins/relation/sql/postgres/full_text_search.rb +53 -0
  10. data/lib/rom/plugins/relation/sql/postgres/streaming.rb +97 -0
  11. data/lib/rom/sql.rb +2 -0
  12. data/lib/rom/sql/associations.rb +2 -0
  13. data/lib/rom/sql/associations/core.rb +10 -0
  14. data/lib/rom/sql/associations/many_to_many.rb +10 -2
  15. data/lib/rom/sql/associations/many_to_one.rb +2 -0
  16. data/lib/rom/sql/associations/one_to_many.rb +2 -0
  17. data/lib/rom/sql/associations/one_to_one.rb +2 -0
  18. data/lib/rom/sql/associations/one_to_one_through.rb +2 -0
  19. data/lib/rom/sql/associations/self_ref.rb +2 -0
  20. data/lib/rom/sql/attribute.rb +87 -29
  21. data/lib/rom/sql/attribute_aliasing.rb +88 -0
  22. data/lib/rom/sql/attribute_wrapping.rb +30 -0
  23. data/lib/rom/sql/commands.rb +2 -0
  24. data/lib/rom/sql/commands/create.rb +2 -0
  25. data/lib/rom/sql/commands/delete.rb +2 -0
  26. data/lib/rom/sql/commands/error_wrapper.rb +2 -0
  27. data/lib/rom/sql/commands/update.rb +2 -0
  28. data/lib/rom/sql/dsl.rb +39 -1
  29. data/lib/rom/sql/error.rb +2 -0
  30. data/lib/rom/sql/errors.rb +2 -0
  31. data/lib/rom/sql/extensions.rb +2 -0
  32. data/lib/rom/sql/extensions/active_support_notifications.rb +2 -0
  33. data/lib/rom/sql/extensions/mysql.rb +2 -0
  34. data/lib/rom/sql/extensions/mysql/type_builder.rb +2 -0
  35. data/lib/rom/sql/extensions/postgres.rb +4 -0
  36. data/lib/rom/sql/extensions/postgres/commands.rb +3 -1
  37. data/lib/rom/sql/extensions/postgres/type_builder.rb +6 -4
  38. data/lib/rom/sql/extensions/postgres/type_serializer.rb +2 -0
  39. data/lib/rom/sql/extensions/postgres/types.rb +2 -0
  40. data/lib/rom/sql/extensions/postgres/types/array.rb +9 -8
  41. data/lib/rom/sql/extensions/postgres/types/array_types.rb +3 -1
  42. data/lib/rom/sql/extensions/postgres/types/geometric.rb +2 -0
  43. data/lib/rom/sql/extensions/postgres/types/json.rb +76 -19
  44. data/lib/rom/sql/extensions/postgres/types/ltree.rb +27 -25
  45. data/lib/rom/sql/extensions/postgres/types/network.rb +2 -0
  46. data/lib/rom/sql/extensions/postgres/types/range.rb +6 -4
  47. data/lib/rom/sql/extensions/rails_log_subscriber.rb +2 -0
  48. data/lib/rom/sql/extensions/sqlite.rb +2 -0
  49. data/lib/rom/sql/extensions/sqlite/type_builder.rb +2 -0
  50. data/lib/rom/sql/extensions/sqlite/types.rb +2 -0
  51. data/lib/rom/sql/foreign_key.rb +3 -1
  52. data/lib/rom/sql/function.rb +84 -6
  53. data/lib/rom/sql/gateway.rb +9 -1
  54. data/lib/rom/sql/group_dsl.rb +2 -0
  55. data/lib/rom/sql/index.rb +2 -0
  56. data/lib/rom/sql/join_dsl.rb +11 -0
  57. data/lib/rom/sql/mapper_compiler.rb +14 -3
  58. data/lib/rom/sql/migration.rb +20 -3
  59. data/lib/rom/sql/migration/inline_runner.rb +2 -0
  60. data/lib/rom/sql/migration/migrator.rb +5 -3
  61. data/lib/rom/sql/migration/recorder.rb +2 -0
  62. data/lib/rom/sql/migration/runner.rb +4 -2
  63. data/lib/rom/sql/migration/schema_diff.rb +4 -2
  64. data/lib/rom/sql/migration/template.rb +2 -0
  65. data/lib/rom/sql/migration/writer.rb +12 -4
  66. data/lib/rom/sql/order_dsl.rb +2 -0
  67. data/lib/rom/sql/plugin/associates.rb +4 -3
  68. data/lib/rom/sql/plugin/nullify.rb +37 -0
  69. data/lib/rom/sql/plugin/pagination.rb +22 -0
  70. data/lib/rom/sql/plugins.rb +4 -0
  71. data/lib/rom/sql/projection_dsl.rb +10 -4
  72. data/lib/rom/sql/rake_task.rb +2 -0
  73. data/lib/rom/sql/relation.rb +3 -1
  74. data/lib/rom/sql/relation/reading.rb +105 -16
  75. data/lib/rom/sql/relation/writing.rb +2 -0
  76. data/lib/rom/sql/restriction_dsl.rb +8 -8
  77. data/lib/rom/sql/schema.rb +16 -2
  78. data/lib/rom/sql/schema/attributes_inferrer.rb +7 -5
  79. data/lib/rom/sql/schema/dsl.rb +3 -1
  80. data/lib/rom/sql/schema/index_dsl.rb +8 -3
  81. data/lib/rom/sql/schema/inferrer.rb +12 -8
  82. data/lib/rom/sql/schema/type_builder.rb +4 -2
  83. data/lib/rom/sql/spec/support.rb +5 -3
  84. data/lib/rom/sql/tasks/migration_tasks.rake +16 -11
  85. data/lib/rom/sql/transaction.rb +2 -0
  86. data/lib/rom/sql/type_dsl.rb +3 -1
  87. data/lib/rom/sql/type_extensions.rb +4 -4
  88. data/lib/rom/sql/type_serializer.rb +3 -1
  89. data/lib/rom/sql/types.rb +6 -4
  90. data/lib/rom/sql/version.rb +3 -1
  91. data/lib/rom/sql/wrap.rb +2 -0
  92. data/lib/rom/types/values.rb +2 -0
  93. metadata +39 -37
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  class Relation < ROM::Relation
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/sql/dsl'
2
4
 
3
5
  module ROM
@@ -6,15 +8,13 @@ module ROM
6
8
  class RestrictionDSL < DSL
7
9
  # @api private
8
10
  def call(&block)
9
- instance_exec(relations, &block)
10
- end
11
+ arg, kwargs = select_relations(block.parameters)
11
12
 
12
- # Returns a result of SQL EXISTS clause.
13
- #
14
- # @example
15
- # users.where { exists(users.where(name: 'John')) }
16
- def exists(relation)
17
- ::ROM::SQL::Attribute[Types::Bool].meta(sql_expr: relation.dataset.exists)
13
+ if kwargs.nil?
14
+ instance_exec(arg, &block)
15
+ else
16
+ instance_exec(**kwargs, &block)
17
+ end
18
18
  end
19
19
 
20
20
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/schema'
2
4
 
3
5
  require 'rom/sql/schema/dsl'
@@ -65,6 +67,18 @@ module ROM
65
67
  new(map { |attr| attr.qualified(table_alias) })
66
68
  end
67
69
 
70
+ # Return a new schema with attributes that are aliased
71
+ # and marked as qualified
72
+ #
73
+ # Intended to be used when passing attributes to `dataset#select`
74
+ #
75
+ # @return [Schema]
76
+ #
77
+ # @api public
78
+ def qualified_projection(table_alias = nil)
79
+ new(map { |attr| attr.qualified_projection(table_alias) })
80
+ end
81
+
68
82
  # Project a schema
69
83
  #
70
84
  # @see ROM::Schema#project
@@ -127,7 +141,7 @@ module ROM
127
141
  #
128
142
  # @api public
129
143
  def call(relation)
130
- relation.new(relation.dataset.select(*self), schema: self)
144
+ relation.new(relation.dataset.select(*self.qualified_projection), schema: self)
131
145
  end
132
146
 
133
147
  # Return an empty schema
@@ -142,7 +156,7 @@ module ROM
142
156
  # Finalize all attributes by qualifying them and initializing primary key names
143
157
  #
144
158
  # @api private
145
- def finalize_attributes!(options = EMPTY_HASH)
159
+ def finalize_attributes!(**options)
146
160
  super do
147
161
  @attributes = map(&:qualified)
148
162
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/core/class_attributes'
2
4
 
3
5
  module ROM
@@ -22,13 +24,13 @@ module ROM
22
24
 
23
25
  columns = filter_columns(gateway.connection.schema(dataset))
24
26
 
25
- inferred = columns.map do |(name, definition)|
26
- type = type_builder.(definition)
27
+ inferred = columns.map do |name, definition|
28
+ type = type_builder.(**definition)
27
29
 
28
- attr_class.new(type.meta(name: name, source: schema.name)) if type
30
+ attr_class.new(type.meta(source: schema.name), name: name) if type
29
31
  end.compact
30
32
 
31
- missing = columns.map(&:first) - inferred.map { |attr| attr.meta[:name] }
33
+ missing = columns.map(&:first) - inferred.map { |attr| attr.name }
32
34
 
33
35
  [inferred, missing]
34
36
  end
@@ -42,7 +44,7 @@ module ROM
42
44
 
43
45
  # @api private
44
46
  def filter_columns(schema)
45
- schema.reject { |(_, definition)| definition[:db_type] == CONSTRAINT_DB_TYPE }
47
+ schema.reject { |_, definition| definition[:db_type] == CONSTRAINT_DB_TYPE }
46
48
  end
47
49
  end
48
50
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/sql/schema/index_dsl'
2
4
 
3
5
  module ROM
@@ -15,7 +17,7 @@ module ROM
15
17
  #
16
18
  # @api public
17
19
  def indexes(&block)
18
- @index_dsl = IndexDSL.new(options, &block)
20
+ @index_dsl = IndexDSL.new(**options, &block)
19
21
  end
20
22
 
21
23
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  module ROM
@@ -19,6 +21,7 @@ module ROM
19
21
 
20
22
  instance_exec(&block)
21
23
  end
24
+ ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
22
25
 
23
26
  # @api public
24
27
  def index(*attributes, **options)
@@ -26,8 +29,10 @@ module ROM
26
29
  end
27
30
 
28
31
  # @api private
29
- def call(schema_name, types)
30
- attributes = types.map { |type| attr_class.new(type).meta(source: schema_name) }
32
+ def call(schema_name, attrs)
33
+ attributes = attrs.map do |attr|
34
+ attr_class.new(attr[:type], **(attr[:options] || {})).meta(source: schema_name)
35
+ end
31
36
 
32
37
  registry.map { |attr_names, options|
33
38
  build_index(attributes, attr_names, options)
@@ -42,7 +47,7 @@ module ROM
42
47
  attributes.find { |a| a.name == name }.unwrap
43
48
  end
44
49
 
45
- Index.new(index_attributes, options)
50
+ Index.new(index_attributes, **options)
46
51
  end
47
52
  end
48
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  require 'rom/sql/schema/type_builder'
@@ -32,9 +34,9 @@ module ROM
32
34
  # @api private
33
35
  def call(schema, gateway)
34
36
  if enabled?
35
- infer_from_database(gateway, schema, super)
37
+ infer_from_database(gateway, schema, **super)
36
38
  else
37
- infer_from_attributes(gateway, schema, super)
39
+ infer_from_attributes(gateway, schema, **super)
38
40
  end
39
41
  rescue Sequel::Error => error
40
42
  on_error(schema.name, error)
@@ -54,7 +56,7 @@ module ROM
54
56
  end
55
57
 
56
58
  # @api private
57
- def infer_from_attributes(gateway, schema, attributes:, **rest)
59
+ def infer_from_attributes(_gateway, schema, attributes:, **rest)
58
60
  indexes = schema.indexes | indexes_from_attributes(attributes)
59
61
  foreign_keys = foreign_keys_from_attributes(attributes)
60
62
 
@@ -69,7 +71,8 @@ module ROM
69
71
  if gateway.connection.respond_to?(:indexes)
70
72
  dataset = schema.name.dataset
71
73
 
72
- gateway.connection.indexes(dataset).map { |index_name, columns:, unique:, **rest|
74
+ gateway.connection.indexes(dataset).map { |index_name, definition|
75
+ columns, unique = definition.values_at(:columns, :unique)
73
76
  attrs = columns.map { |name| attributes[name] }
74
77
 
75
78
  SQL::Index.new(attrs, name: index_name, unique: unique)
@@ -83,7 +86,8 @@ module ROM
83
86
  def foreign_keys_from_database(gateway, schema, attributes)
84
87
  dataset = schema.name.dataset
85
88
 
86
- gateway.connection.foreign_key_list(dataset).map { |columns:, table:, key:, **rest|
89
+ gateway.connection.foreign_key_list(dataset).map { |definition|
90
+ columns, table, key = definition.values_at(:columns, :table, :key)
87
91
  attrs = columns.map { |name| attributes[name] }
88
92
 
89
93
  SQL::ForeignKey.new(attrs, table, parent_keys: key)
@@ -147,9 +151,9 @@ module ROM
147
151
  raise e
148
152
  elsif !silent
149
153
  warn "[#{dataset}] failed to infer schema. " \
150
- "Make sure tables exist before ROM container is set up. " \
151
- "This may also happen when your migration tasks load ROM container, " \
152
- "which is not needed for migrations as only the connection is required " \
154
+ 'Make sure tables exist before ROM container is set up. ' \
155
+ 'This may also happen when your migration tasks load ROM container, ' \
156
+ 'which is not needed for migrations as only the connection is required ' \
153
157
  "(#{e.message})"
154
158
  end
155
159
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  class Schema
@@ -22,7 +24,7 @@ module ROM
22
24
  DECIMAL_REGEX = /(?:decimal|numeric)\((\d+)(?:,\s*(\d+))?\)/.freeze
23
25
 
24
26
  ruby_type_mapping(
25
- integer: Types::Int,
27
+ integer: Types::Integer,
26
28
  string: Types::String,
27
29
  time: Types::Time,
28
30
  date: Types::Date,
@@ -39,7 +41,7 @@ module ROM
39
41
  if primary_key
40
42
  map_pk_type(type, db_type, **rest)
41
43
  else
42
- mapped_type = map_type(type, db_type, rest)
44
+ mapped_type = map_type(type, db_type, **rest)
43
45
 
44
46
  if mapped_type
45
47
  read_type = mapped_type.meta[:read]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  if defined? JRUBY_VERSION
2
4
  USING_JRUBY = true
3
5
  else
@@ -5,15 +7,15 @@ else
5
7
  end
6
8
 
7
9
  if USING_JRUBY
8
- SEQUEL_TEST_DB_URI = "jdbc:sqlite::memory:"
10
+ SEQUEL_TEST_DB_URI = 'jdbc:sqlite::memory:'
9
11
  else
10
- SEQUEL_TEST_DB_URI = "sqlite::memory"
12
+ SEQUEL_TEST_DB_URI = 'sqlite::memory'
11
13
  end
12
14
 
13
15
  DB = Sequel.connect(SEQUEL_TEST_DB_URI)
14
16
 
15
17
  def seed(db = DB)
16
- db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name STRING)")
18
+ db.run('CREATE TABLE users (id INTEGER PRIMARY KEY, name STRING)')
17
19
 
18
20
  db[:users].insert(id: 1, name: 'Jane')
19
21
  db[:users].insert(id: 2, name: 'Joe')
@@ -1,5 +1,7 @@
1
- require "pathname"
2
- require "fileutils"
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'fileutils'
3
5
 
4
6
  module ROM
5
7
  module SQL
@@ -27,7 +29,10 @@ module ROM
27
29
  def gateway
28
30
  if env.nil?
29
31
  Gateway.instance ||
30
- raise(MissingEnv, "Set up a configuration with ROM::SQL::RakeSupport.env= in the db:setup task")
32
+ raise(
33
+ MissingEnv,
34
+ 'Set up a configuration with ROM::SQL::RakeSupport.env= in the db:setup task'
35
+ )
31
36
  else
32
37
  env.gateways[:default]
33
38
  end
@@ -41,36 +46,36 @@ end
41
46
 
42
47
  namespace :db do
43
48
  task :rom_configuration do
44
- Rake::Task["db:setup"].invoke
49
+ Rake::Task['db:setup'].invoke
45
50
  end
46
51
 
47
- desc "Perform migration reset (full erase and migration up)"
52
+ desc 'Perform migration reset (full erase and migration up)'
48
53
  task reset: :rom_configuration do
49
54
  ROM::SQL::RakeSupport.run_migrations(target: 0)
50
55
  ROM::SQL::RakeSupport.run_migrations
51
- puts "<= db:reset executed"
56
+ puts '<= db:reset executed'
52
57
  end
53
58
 
54
- desc "Migrate the database (options [version_number])]"
59
+ desc 'Migrate the database (options [version_number])]'
55
60
  task :migrate, [:version] => :rom_configuration do |_, args|
56
61
  version = args[:version]
57
62
 
58
63
  if version.nil?
59
64
  ROM::SQL::RakeSupport.run_migrations
60
- puts "<= db:migrate executed"
65
+ puts '<= db:migrate executed'
61
66
  else
62
67
  ROM::SQL::RakeSupport.run_migrations(target: version.to_i)
63
68
  puts "<= db:migrate version=[#{version}] executed"
64
69
  end
65
70
  end
66
71
 
67
- desc "Perform migration down (erase all data)"
72
+ desc 'Perform migration down (removes all tables)'
68
73
  task clean: :rom_configuration do
69
74
  ROM::SQL::RakeSupport.run_migrations(target: 0)
70
- puts "<= db:clean executed"
75
+ puts '<= db:clean executed'
71
76
  end
72
77
 
73
- desc "Create a migration (parameters: NAME, VERSION)"
78
+ desc 'Create a migration (parameters: NAME, VERSION)'
74
79
  task :create_migration, [:name, :version] => :rom_configuration do |_, args|
75
80
  name, version = args.values_at(:name, :version)
76
81
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  # @api private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  # Type DSL used by Types.define method
@@ -11,7 +13,7 @@ module ROM
11
13
  if value_type.class < ::Dry::Types::Type
12
14
  @definition = value_type
13
15
  else
14
- @definition = ::ROM::SQL::Types.Definition(value_type)
16
+ @definition = ::ROM::SQL::Types.Nominal(value_type)
15
17
  end
16
18
  end
17
19
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  # Type-specific methods
@@ -7,14 +9,12 @@ module ROM
7
9
  class << self
8
10
  # Gets extensions for a type
9
11
  #
10
- # @param [Dry::Types::Type] wrapped
12
+ # @param type [Dry::Types::Type] wrapped
11
13
  #
12
14
  # @return [Hash]
13
15
  #
14
16
  # @api public
15
- def [](wrapped)
16
- type = wrapped.default? ? wrapped.type : wrapped
17
- type = type.optional? ? type.right : type
17
+ def [](type)
18
18
  @types[type.meta[:database]][type.meta[:db_type]] || EMPTY_HASH
19
19
  end
20
20
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/core/class_attributes'
2
4
 
3
5
  module ROM
@@ -21,7 +23,7 @@ module ROM
21
23
  defines :mapping
22
24
 
23
25
  mapping(
24
- Types::Int => 'integer',
26
+ Types::Integer => 'integer',
25
27
  Types::String => 'varchar',
26
28
  Types::Time => 'timestamp',
27
29
  Types::Date => 'date',
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sequel/core'
2
4
  require 'sequel/sql'
3
5
 
@@ -17,10 +19,10 @@ module ROM
17
19
  # @example with a custom type
18
20
  # attribute :user_id, Types.ForeignKey(:users, Types::UUID)
19
21
  #
20
- # @return [Dry::Types::Definition]
22
+ # @return [Dry::Types::Nominal]
21
23
  #
22
24
  # @api public
23
- def self.ForeignKey(relation, type = Types::Int.meta(index: true))
25
+ def self.ForeignKey(relation, type = Types::Integer.meta(index: true))
24
26
  super
25
27
  end
26
28
 
@@ -32,14 +34,14 @@ module ROM
32
34
  # output { Types::Coercible::Hash }
33
35
  # end
34
36
  #
35
- # @return [Dry::Types::Definition]
37
+ # @return [Dry::Types::Nominal]
36
38
  #
37
39
  # @api public
38
40
  def self.define(value_type, &block)
39
41
  TypeDSL.new(value_type).call(&block)
40
42
  end
41
43
 
42
- Serial = Int.meta(primary_key: true)
44
+ Serial = Integer.meta(primary_key: true)
43
45
 
44
46
  Blob = Constructor(Sequel::SQL::Blob, &Sequel::SQL::Blob.method(:new))
45
47
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
- VERSION = '2.5.0'.freeze
5
+ VERSION = '3.3.0'.freeze
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/relation/wrap'
2
4
 
3
5
  module ROM
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/sql/types'
2
4
 
3
5
  module ROM