sequel 5.96.0 → 5.100.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/adapters/amalgalite.rb +16 -1
  3. data/lib/sequel/adapters/postgres.rb +1 -0
  4. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  5. data/lib/sequel/adapters/shared/mysql.rb +1 -1
  6. data/lib/sequel/adapters/shared/postgres.rb +39 -5
  7. data/lib/sequel/adapters/sqlite.rb +15 -1
  8. data/lib/sequel/ast_transformer.rb +2 -0
  9. data/lib/sequel/core.rb +15 -1
  10. data/lib/sequel/database/misc.rb +1 -1
  11. data/lib/sequel/database/schema_generator.rb +4 -1
  12. data/lib/sequel/database/schema_methods.rb +3 -1
  13. data/lib/sequel/dataset/actions.rb +65 -10
  14. data/lib/sequel/dataset/query.rb +1 -1
  15. data/lib/sequel/dataset/sql.rb +13 -6
  16. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  17. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  18. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  19. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  20. data/lib/sequel/extensions/pg_auto_parameterize.rb +2 -2
  21. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  22. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +2 -1
  23. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  24. data/lib/sequel/extensions/pg_json_ops.rb +12 -5
  25. data/lib/sequel/extensions/pg_range.rb +12 -0
  26. data/lib/sequel/extensions/pg_row.rb +2 -2
  27. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  28. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  29. data/lib/sequel/extensions/to_dot.rb +5 -0
  30. data/lib/sequel/model/associations.rb +7 -7
  31. data/lib/sequel/model/base.rb +31 -3
  32. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  33. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  34. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  35. data/lib/sequel/plugins/rcte_tree.rb +5 -5
  36. data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
  37. data/lib/sequel/plugins/split_values.rb +10 -0
  38. data/lib/sequel/plugins/static_cache.rb +13 -0
  39. data/lib/sequel/plugins/subset_static_cache.rb +15 -0
  40. data/lib/sequel/plugins/table_select.rb +7 -0
  41. data/lib/sequel/plugins/unused_associations.rb +7 -1
  42. data/lib/sequel/sql.rb +1 -1
  43. data/lib/sequel/version.rb +1 -1
  44. metadata +5 -2
@@ -0,0 +1,151 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The deprecated_associations plugin adds support for
6
+ # deprecating associations. Attempts to use association
7
+ # methods and access association metadata for deprecated
8
+ # associations results in a warning.
9
+ #
10
+ # Album.plugin :deprecated_associations
11
+ # Album.many_to_one :artist, deprecated: true
12
+ # album = Album[1]
13
+ #
14
+ # # Warnings for all of the following calls
15
+ # album.artist
16
+ # album.artist_dataset
17
+ # album.artist = Artist[2]
18
+ # Album.association_reflection(:artist)
19
+ # Album.eager(:artist)
20
+ # Album.eager_graph(:artist)
21
+ # Album.where(artist: Artist[1]).all
22
+ #
23
+ # By default, the plugin issues a single warning per
24
+ # association method or association reflection. See
25
+ # DeprecatedAssociations.configure for options to make
26
+ # deprecated association issue warnings for every access,
27
+ # to include backtraces in warnings, or to raise an
28
+ # exception instead of warning.
29
+ #
30
+ # Note that Model.association_reflections and
31
+ # Model.all_association_reflections will include deprecated
32
+ # associations, and accessing the metadata for deprecated
33
+ # associations through these interfaces not issue warnings.
34
+ #
35
+ # Usage:
36
+ #
37
+ # # Make all models support deprecated associations
38
+ # # (called before loading subclasses)
39
+ # Sequel::Model.plugin :deprecated_associations
40
+ #
41
+ # # Make Album class support deprecated associations
42
+ # Album.plugin :deprecated_associations
43
+ module DeprecatedAssociations
44
+ # Exception class used for deprecated association use when
45
+ # raising exceptions instead of emitting warnings.
46
+ class Access < Sequel::Error; end
47
+
48
+ # Configure the deprecated associations plugin. Options:
49
+ #
50
+ # backtrace: Print backtrace with warning
51
+ # deduplicate: Set to false to emit warnings for every
52
+ # deprecated association method call/access
53
+ # (when not caching associations, this is always false)
54
+ # raise: Raise Sequel::Plugin::DeprecatedAssociations::Access
55
+ # instead of warning
56
+ def self.configure(model, opts = OPTS)
57
+ model.instance_exec do
58
+ (@deprecated_associations_config ||= {}).merge!(opts)
59
+ end
60
+ end
61
+
62
+ module ClassMethods
63
+ # Issue a deprecation warning if the association is deprecated.
64
+ def association_reflection(assoc)
65
+ ref = super
66
+ if ref && ref[:deprecated]
67
+ emit_deprecated_association_warning(ref, nil) do
68
+ "Access of association reflection for deprecated association: class:#{name} association:#{assoc}"
69
+ end
70
+ end
71
+ ref
72
+ end
73
+
74
+ private
75
+
76
+ # Issue a deprecation warning when the defined method is called if the
77
+ # association is deprecated and the method name does not start with the
78
+ # underscore (to avoid not warning twice, once for the public association
79
+ # method and once for the private association method).
80
+ def association_module_def(name, opts=OPTS, &block)
81
+ super
82
+ if opts[:deprecated] && name[0] != "_"
83
+ deprecated_associations_module.module_exec do
84
+ define_method(name) do |*a, &b|
85
+ self.class.send(:emit_deprecated_association_warning, opts, name) do
86
+ "Calling deprecated association method: class:#{self.class.name} association:#{opts[:name]} method:#{name}"
87
+ end
88
+ super(*a, &b)
89
+ end
90
+ alias_method name, name
91
+ end
92
+ end
93
+ nil
94
+ end
95
+
96
+ # Issue a deprecation warning when the defined method is called if the
97
+ # association is deprecated.
98
+ def association_module_delegate_def(name, opts, &block)
99
+ super
100
+ if opts[:deprecated]
101
+ deprecated_associations_module.module_exec do
102
+ define_method(name) do |*a, &b|
103
+ self.class.send(:emit_deprecated_association_warning, opts, name) do
104
+ "Calling deprecated association method: class:#{self.class.name} association:#{opts[:name]} method:#{name}"
105
+ end
106
+ super(*a, &b)
107
+ end
108
+ # :nocov:
109
+ ruby2_keywords(name) if respond_to?(:ruby2_keywords, true)
110
+ # :nocov:
111
+ alias_method(name, name)
112
+ end
113
+ end
114
+ nil
115
+ end
116
+
117
+ # A module to add deprecated association methods to. These methods
118
+ # handle issuing the deprecation warnings, and call super to get the
119
+ # default behavior.
120
+ def deprecated_associations_module
121
+ return @deprecated_associations_module if defined?(@deprecated_associations_module)
122
+ @deprecated_associations_module = Module.new
123
+ include(@deprecated_associations_module)
124
+ @deprecated_associations_module
125
+ end
126
+
127
+ # Emit a deprecation warning, or raise an exception if the :raise
128
+ # plugin option was used.
129
+ def emit_deprecated_association_warning(ref, method)
130
+ config = @deprecated_associations_config
131
+
132
+ raise Access, yield if config[:raise]
133
+
134
+ unless config[:deduplicate] == false
135
+ emit = false
136
+ ref.send(:cached_fetch, [:deprecated_associations, method]) do
137
+ emit = true
138
+ end
139
+ return unless emit
140
+ end
141
+
142
+ if config[:backtrace]
143
+ warn yield, caller(2)
144
+ else
145
+ warn yield, :uplevel => 2
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Sequel
4
4
  module Plugins
5
- # If the model's dataset selects explicit columns and the
5
+ # If the model's dataset selects explicit columns (or table.*) and the
6
6
  # database supports it, the insert_returning_select plugin will
7
7
  # automatically set the RETURNING clause on the dataset used to
8
8
  # insert rows to the columns selected, which allows the default model
@@ -45,6 +45,9 @@ module Sequel
45
45
  ret
46
46
  end
47
47
 
48
+ RETURN_ALL = [Sequel::Dataset::WILDCARD].freeze
49
+ private_constant :RETURN_ALL
50
+
48
51
  # Determine the columns to use for the returning clause, or return nil
49
52
  # if they can't be determined and a returning clause should not be
50
53
  # added automatically.
@@ -52,6 +55,12 @@ module Sequel
52
55
  return unless ds.supports_returning?(:insert)
53
56
  return unless values = ds.opts[:select]
54
57
 
58
+ # SELECT table.* -> RETURNING *
59
+ if values.length == 1 && values[0].is_a?(Sequel::SQL::ColumnAll)
60
+ return RETURN_ALL
61
+ end
62
+
63
+ # SELECT column1, table.column2, ... -> RETURNING column1, column2, ...
55
64
  values = values.map{|v| ds.unqualified_column_for(v)}
56
65
  if values.all?
57
66
  values
@@ -541,7 +541,7 @@ module Sequel
541
541
  if (assoc_pks = obj.get_column_value(key)) && !assoc_pks.empty?
542
542
  Sequel[pk=>assoc_pks.to_a]
543
543
  end
544
- when Array
544
+ when Array, Set
545
545
  if (assoc_pks = obj.map{|o| o.get_column_value(key)}.flatten.compact.uniq) && !assoc_pks.empty?
546
546
  Sequel[pk=>assoc_pks]
547
547
  end
@@ -563,7 +563,7 @@ module Sequel
563
563
  if pkv = obj.get_column_value(ref.primary_key_method)
564
564
  Sequel.pg_array_op(key).contains(Sequel.pg_array([pkv], ref.array_type))
565
565
  end
566
- when Array
566
+ when Array, Set
567
567
  if (pkvs = obj.map{|o| o.get_column_value(ref.primary_key_method)}.compact) && !pkvs.empty?
568
568
  Sequel.pg_array(key).overlaps(Sequel.pg_array(pkvs, ref.array_type))
569
569
  end
@@ -111,7 +111,7 @@ module Sequel
111
111
  ancestor_base_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
112
112
  descendant_base_case_columns = key_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
113
113
  recursive_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::QualifiedIdentifier.new(t, ka_)} + c_all
114
- extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.values.delete(ka_)]}}
114
+ extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.remove_key!(ka_)]}}
115
115
  else
116
116
  key_present = key_conv = lambda{|m| m[key]}
117
117
  prkey_conv = lambda{|m| m[prkey]}
@@ -119,7 +119,7 @@ module Sequel
119
119
  ancestor_base_case_columns = [SQL::AliasedExpression.new(prkey, ka)] + c_all
120
120
  descendant_base_case_columns = [SQL::AliasedExpression.new(key, ka)] + c_all
121
121
  recursive_case_columns = [SQL::QualifiedIdentifier.new(t, ka)] + c_all
122
- extract_key_alias = lambda{|m| bd_conv[m.values.delete(ka)]}
122
+ extract_key_alias = lambda{|m| bd_conv[m.remove_key!(ka)]}
123
123
  end
124
124
 
125
125
  parent = opts.merge(opts.fetch(:parent, OPTS)).fetch(:name, :parent)
@@ -200,7 +200,7 @@ module Sequel
200
200
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
201
201
  opk = prkey_conv[obj]
202
202
  if idm_obj = parent_map[opk]
203
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
203
+ key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
204
204
  obj = idm_obj
205
205
  else
206
206
  obj.associations[parent] = nil
@@ -307,12 +307,12 @@ module Sequel
307
307
  ds = ds.select_append(ka) unless ds.opts[:select] == nil
308
308
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>OPTS)) do |obj|
309
309
  if level
310
- no_cache = no_cache_level == obj.values.delete(la)
310
+ no_cache = no_cache_level == obj.remove_key!(la)
311
311
  end
312
312
 
313
313
  opk = prkey_conv[obj]
314
314
  if idm_obj = parent_map[opk]
315
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
315
+ key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
316
316
  obj = idm_obj
317
317
  else
318
318
  obj.associations[childrena] = [] unless no_cache
@@ -0,0 +1,49 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The single_statement_dataset_destroy plugin makes the
6
+ # model dataset.destroy method delete all rows in a
7
+ # single DELETE statement. It runs all before_destroy
8
+ # hooks before the DELETE, and all after_destroy hooks
9
+ # after the delete.
10
+ #
11
+ # This is not compatible with around_destroy hooks,
12
+ # so if the model is using custom around_destroy hooks,
13
+ # dataset.destroy falls back to a separate DELETE statement
14
+ # per row.
15
+ #
16
+ # Usage:
17
+ #
18
+ # # Make all model subclasses use a single DELETE
19
+ # # statement for dataset.destroy
20
+ # Sequel::Model.plugin :single_statement_dataset_destroy
21
+ #
22
+ # # Make the Album class use a single DELETE
23
+ # # statement for dataset.destroy
24
+ # Album.plugin :single_statement_dataset_destroy
25
+ module SingleStatementDatasetDestroy
26
+ module DatasetMethods
27
+ # Destroy all rows in a single DELETE statement. Run the before_destroy
28
+ # hooks for all rows before the DELETE, and all after_destroy hooks
29
+ # for all rows after the DELETE. If the model uses an around_destroy
30
+ # hook, fallback to using a separate DELETE statement per row.
31
+ def destroy
32
+ return super unless model.instance_method(:around_destroy).owner == Sequel::Model::InstanceMethods
33
+
34
+ db.transaction do
35
+ rows = all
36
+ rows.each(&:before_destroy)
37
+ expected_rows = rows.length
38
+ n = delete
39
+ unless n == expected_rows
40
+ raise Error, "dataset changed during destroy, expected rows: #{expected_rows}, actual rows: #{n}"
41
+ end
42
+ rows.each(&:after_destroy)
43
+ n
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -54,6 +54,16 @@ module Sequel
54
54
  end
55
55
  end
56
56
 
57
+ # Remove the key from noncolumn values if it is present there. If it is not
58
+ # present there, then use the default behavior of removing it from values.
59
+ def remove_key!(key)
60
+ if @noncolumn_values && @noncolumn_values.key?(key)
61
+ @noncolumn_values.delete(key)
62
+ else
63
+ super
64
+ end
65
+ end
66
+
57
67
  # Check all entries in the values hash. If any of the keys are not columns,
58
68
  # move the entry into the noncolumn_values hash.
59
69
  def split_noncolumn_values
@@ -176,6 +176,19 @@ module Sequel
176
176
  h
177
177
  end
178
178
 
179
+ # Use the cache instead of a query to get the results.
180
+ def as_set(column)
181
+ set = Set.new
182
+
183
+ if column.is_a?(Array)
184
+ @all.each{|r| set.add(r.values.values_at(*column))}
185
+ else
186
+ @all.each{|r| set.add(r[column])}
187
+ end
188
+
189
+ set
190
+ end
191
+
179
192
  # Alias of as_hash for backwards compatibility.
180
193
  def to_hash(*a)
181
194
  as_hash(*a)
@@ -226,6 +226,21 @@ module Sequel
226
226
  h
227
227
  end
228
228
 
229
+ # Use the cache instead of a query to get the results.
230
+ def as_set(column)
231
+ return super unless all = @cache[:subset_static_cache_all]
232
+
233
+ set = Set.new
234
+
235
+ if column.is_a?(Array)
236
+ all.each{|r| set.add(r.values.values_at(*column))}
237
+ else
238
+ all.each{|r| set.add(r[column])}
239
+ end
240
+
241
+ set
242
+ end
243
+
229
244
  # Alias of as_hash for backwards compatibility.
230
245
  def to_hash(*a)
231
246
  as_hash(*a)
@@ -9,6 +9,13 @@ module Sequel
9
9
  # in the result sets (and possibly overwrite columns in the
10
10
  # current model with the same name).
11
11
  #
12
+ # Note that by default on databases that supporting RETURNING,
13
+ # using this plugin will cause instance creations
14
+ # to use two queries (insert and refresh) instead of a single
15
+ # query using RETURNING. You can use the insert_returning_select
16
+ # plugin to automatically use RETURNING for instance creations
17
+ # for models using this plugin.
18
+ #
12
19
  # Usage:
13
20
  #
14
21
  # # Make all model subclasses select table.*
@@ -198,7 +198,8 @@ module Sequel
198
198
  #
199
199
  # Since this plugin is based on coverage information, if you do
200
200
  # not have tests that cover all usage of associations in your
201
- # application, you can end up with coverage that shows the
201
+ # application, or you are mocking the related association methods
202
+ # and not calling them, you can end up with coverage that shows the
202
203
  # association is not used, when it is used in code that is not
203
204
  # covered. The output of plugin can still be useful in such cases,
204
205
  # as long as you are manually checking it. However, you should
@@ -208,6 +209,8 @@ module Sequel
208
209
  # option for any association that you know is used. If an
209
210
  # association uses the :is_used association option, this plugin
210
211
  # will not modify it if the :modify_associations option is used.
212
+ # It will also not report the association as unused if the :is_used
213
+ # association option is present.
211
214
  #
212
215
  # This plugin does not handle anonymous classes. Any unused
213
216
  # associations defined in anonymous classes will not be
@@ -362,6 +365,9 @@ module Sequel
362
365
  # looks in the class's overridable_methods_module
363
366
  next if ref[:methods_module]
364
367
 
368
+ # Do not report associations if they are explicitly marked as used.
369
+ next if ref[:is_used]
370
+
365
371
  info = {}
366
372
  if reflection_data.include?(assoc.to_s)
367
373
  info[:used] = [:reflection]
data/lib/sequel/sql.rb CHANGED
@@ -1108,7 +1108,7 @@ module Sequel
1108
1108
  else
1109
1109
  new(:'=', 1, 1)
1110
1110
  end
1111
- when ::Array
1111
+ when ::Array, ::Set
1112
1112
  r = r.dup.freeze unless r.frozen?
1113
1113
  new(:IN, l, r)
1114
1114
  when ::String
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 96
9
+ MINOR = 100
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.96.0
4
+ version: 5.100.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
@@ -250,6 +250,7 @@ files:
250
250
  - lib/sequel/extensions/pg_array.rb
251
251
  - lib/sequel/extensions/pg_array_ops.rb
252
252
  - lib/sequel/extensions/pg_auto_parameterize.rb
253
+ - lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb
253
254
  - lib/sequel/extensions/pg_auto_parameterize_in_array.rb
254
255
  - lib/sequel/extensions/pg_enum.rb
255
256
  - lib/sequel/extensions/pg_extended_date_support.rb
@@ -342,6 +343,7 @@ files:
342
343
  - lib/sequel/plugins/def_dataset_method.rb
343
344
  - lib/sequel/plugins/defaults_setter.rb
344
345
  - lib/sequel/plugins/delay_add_association.rb
346
+ - lib/sequel/plugins/deprecated_associations.rb
345
347
  - lib/sequel/plugins/dirty.rb
346
348
  - lib/sequel/plugins/eager_each.rb
347
349
  - lib/sequel/plugins/eager_graph_eager.rb
@@ -384,6 +386,7 @@ files:
384
386
  - lib/sequel/plugins/serialization.rb
385
387
  - lib/sequel/plugins/serialization_modification_detection.rb
386
388
  - lib/sequel/plugins/sharding.rb
389
+ - lib/sequel/plugins/single_statement_dataset_destroy.rb
387
390
  - lib/sequel/plugins/single_table_inheritance.rb
388
391
  - lib/sequel/plugins/singular_table_names.rb
389
392
  - lib/sequel/plugins/skip_create_refresh.rb
@@ -449,7 +452,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
449
452
  - !ruby/object:Gem::Version
450
453
  version: '0'
451
454
  requirements: []
452
- rubygems_version: 3.6.9
455
+ rubygems_version: 4.0.3
453
456
  specification_version: 4
454
457
  summary: The Database Toolkit for Ruby
455
458
  test_files: []