sequel 5.106.0 → 5.109.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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +5 -5
  3. data/lib/sequel/adapters/ado.rb +1 -1
  4. data/lib/sequel/adapters/amalgalite.rb +34 -8
  5. data/lib/sequel/adapters/ibmdb.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
  7. data/lib/sequel/adapters/jdbc/mssql.rb +28 -5
  8. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +14 -3
  10. data/lib/sequel/adapters/jdbc.rb +4 -3
  11. data/lib/sequel/adapters/mock.rb +1 -1
  12. data/lib/sequel/adapters/mysql.rb +7 -2
  13. data/lib/sequel/adapters/mysql2.rb +5 -4
  14. data/lib/sequel/adapters/postgres.rb +42 -39
  15. data/lib/sequel/adapters/shared/db2.rb +5 -2
  16. data/lib/sequel/adapters/shared/mssql.rb +11 -5
  17. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  18. data/lib/sequel/adapters/shared/postgres.rb +80 -92
  19. data/lib/sequel/adapters/shared/sqlanywhere.rb +0 -32
  20. data/lib/sequel/adapters/shared/sqlite.rb +8 -7
  21. data/lib/sequel/adapters/sqlite.rb +13 -9
  22. data/lib/sequel/adapters/trilogy.rb +2 -1
  23. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
  24. data/lib/sequel/adapters/utils/mysql_mysql2.rb +11 -1
  25. data/lib/sequel/connection_pool/sharded_single.rb +1 -1
  26. data/lib/sequel/connection_pool/sharded_threaded.rb +7 -5
  27. data/lib/sequel/connection_pool/sharded_timed_queue.rb +3 -3
  28. data/lib/sequel/connection_pool/threaded.rb +9 -7
  29. data/lib/sequel/connection_pool/timed_queue.rb +2 -2
  30. data/lib/sequel/connection_pool.rb +3 -3
  31. data/lib/sequel/core.rb +44 -24
  32. data/lib/sequel/database/connecting.rb +1 -1
  33. data/lib/sequel/database/features.rb +1 -1
  34. data/lib/sequel/database/misc.rb +6 -11
  35. data/lib/sequel/database/query.rb +2 -2
  36. data/lib/sequel/database/schema_generator.rb +7 -5
  37. data/lib/sequel/database/schema_methods.rb +2 -2
  38. data/lib/sequel/database/transactions.rb +5 -7
  39. data/lib/sequel/dataset/actions.rb +2 -2
  40. data/lib/sequel/dataset/features.rb +4 -4
  41. data/lib/sequel/dataset/graph.rb +1 -1
  42. data/lib/sequel/dataset/misc.rb +3 -3
  43. data/lib/sequel/dataset/prepared_statements.rb +2 -2
  44. data/lib/sequel/dataset/query.rb +8 -8
  45. data/lib/sequel/dataset/sql.rb +32 -13
  46. data/lib/sequel/dataset.rb +2 -2
  47. data/lib/sequel/deprecated.rb +2 -2
  48. data/lib/sequel/exceptions.rb +2 -2
  49. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  50. data/lib/sequel/extensions/async_thread_pool.rb +3 -3
  51. data/lib/sequel/extensions/auto_cast_date_and_time.rb +2 -2
  52. data/lib/sequel/extensions/blank.rb +2 -2
  53. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  54. data/lib/sequel/extensions/core_refinements.rb +7 -10
  55. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  56. data/lib/sequel/extensions/datetime_parse_to_time.rb +2 -2
  57. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -3
  58. data/lib/sequel/extensions/graph_each.rb +1 -1
  59. data/lib/sequel/extensions/index_caching.rb +1 -1
  60. data/lib/sequel/extensions/inflector.rb +5 -5
  61. data/lib/sequel/extensions/is_distinct_from.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +20 -4
  63. data/lib/sequel/extensions/migration.rb +21 -7
  64. data/lib/sequel/extensions/named_timezones.rb +9 -9
  65. data/lib/sequel/extensions/pg_array.rb +10 -8
  66. data/lib/sequel/extensions/pg_array_ops.rb +69 -14
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +2 -2
  68. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +3 -3
  69. data/lib/sequel/extensions/pg_enum.rb +2 -2
  70. data/lib/sequel/extensions/pg_extended_date_support.rb +5 -5
  71. data/lib/sequel/extensions/pg_extended_integer_support.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore.rb +2 -2
  73. data/lib/sequel/extensions/pg_hstore_ops.rb +5 -5
  74. data/lib/sequel/extensions/pg_inet.rb +2 -2
  75. data/lib/sequel/extensions/pg_inet_ops.rb +2 -2
  76. data/lib/sequel/extensions/pg_interval.rb +6 -6
  77. data/lib/sequel/extensions/pg_json.rb +4 -4
  78. data/lib/sequel/extensions/pg_json_ops.rb +15 -13
  79. data/lib/sequel/extensions/pg_range.rb +6 -6
  80. data/lib/sequel/extensions/pg_range_ops.rb +7 -7
  81. data/lib/sequel/extensions/pg_row.rb +4 -4
  82. data/lib/sequel/extensions/pg_row_ops.rb +6 -6
  83. data/lib/sequel/extensions/provenance.rb +2 -2
  84. data/lib/sequel/extensions/query.rb +2 -2
  85. data/lib/sequel/extensions/s.rb +6 -10
  86. data/lib/sequel/extensions/schema_dumper.rb +10 -6
  87. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +2 -0
  88. data/lib/sequel/extensions/sql_comments.rb +1 -1
  89. data/lib/sequel/extensions/sql_log_normalizer.rb +1 -1
  90. data/lib/sequel/extensions/sqlite_json_ops.rb +2 -2
  91. data/lib/sequel/extensions/string_agg.rb +5 -5
  92. data/lib/sequel/extensions/symbol_aref.rb +12 -32
  93. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -5
  94. data/lib/sequel/extensions/symbol_as.rb +1 -2
  95. data/lib/sequel/extensions/symbol_as_refinement.rb +1 -5
  96. data/lib/sequel/extensions/transaction_connection_validator.rb +1 -1
  97. data/lib/sequel/model/associations.rb +17 -17
  98. data/lib/sequel/model/base.rb +18 -15
  99. data/lib/sequel/model/inflections.rb +1 -1
  100. data/lib/sequel/model/plugins.rb +2 -2
  101. data/lib/sequel/model.rb +2 -2
  102. data/lib/sequel/plugins/association_pks.rb +1 -1
  103. data/lib/sequel/plugins/association_proxies.rb +4 -4
  104. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +2 -0
  105. data/lib/sequel/plugins/caching.rb +28 -3
  106. data/lib/sequel/plugins/class_table_inheritance.rb +1 -1
  107. data/lib/sequel/plugins/column_encryption.rb +10 -10
  108. data/lib/sequel/plugins/concurrent_eager_loading.rb +2 -2
  109. data/lib/sequel/plugins/csv_serializer.rb +9 -20
  110. data/lib/sequel/plugins/deprecated_associations.rb +2 -2
  111. data/lib/sequel/plugins/eager_each.rb +1 -1
  112. data/lib/sequel/plugins/finder.rb +1 -1
  113. data/lib/sequel/plugins/forbid_lazy_load.rb +3 -3
  114. data/lib/sequel/plugins/insert_conflict.rb +1 -1
  115. data/lib/sequel/plugins/insert_returning_select.rb +1 -1
  116. data/lib/sequel/plugins/instance_filters.rb +8 -0
  117. data/lib/sequel/plugins/instance_hooks.rb +19 -0
  118. data/lib/sequel/plugins/json_serializer.rb +4 -4
  119. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  120. data/lib/sequel/plugins/many_through_many.rb +1 -1
  121. data/lib/sequel/plugins/mssql_optimistic_locking.rb +9 -0
  122. data/lib/sequel/plugins/optimistic_locking.rb +12 -2
  123. data/lib/sequel/plugins/optimistic_locking_base.rb +14 -0
  124. data/lib/sequel/plugins/pg_array_associations.rb +4 -4
  125. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -3
  126. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +2 -2
  127. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +11 -2
  128. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  129. data/lib/sequel/plugins/select_on_skipped_update.rb +63 -0
  130. data/lib/sequel/plugins/serialization.rb +1 -1
  131. data/lib/sequel/plugins/sharding.rb +1 -1
  132. data/lib/sequel/plugins/single_table_inheritance.rb +21 -3
  133. data/lib/sequel/plugins/skip_saving_columns.rb +2 -2
  134. data/lib/sequel/plugins/split_values.rb +1 -1
  135. data/lib/sequel/plugins/sql_comments.rb +8 -8
  136. data/lib/sequel/plugins/subclasses.rb +6 -6
  137. data/lib/sequel/plugins/subset_conditions.rb +5 -16
  138. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  139. data/lib/sequel/plugins/throw_failures.rb +3 -3
  140. data/lib/sequel/plugins/unused_associations.rb +5 -5
  141. data/lib/sequel/plugins/validate_associated.rb +7 -1
  142. data/lib/sequel/plugins/validate_associated_context.rb +52 -0
  143. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  144. data/lib/sequel/plugins/xml_serializer.rb +2 -0
  145. data/lib/sequel/sql.rb +120 -116
  146. data/lib/sequel/version.rb +1 -1
  147. metadata +5 -3
@@ -64,7 +64,7 @@ module Sequel
64
64
  array = [].freeze
65
65
 
66
66
  if RUBY_VERSION < '2.6'
67
- # :nocov:
67
+ # simplecov:disable
68
68
 
69
69
  # Default proc used to determine whether to send the method to the dataset.
70
70
  # If the array would respond to it, sends it to the array instead of the dataset.
@@ -75,7 +75,7 @@ module Sequel
75
75
  end
76
76
  !array_method
77
77
  end
78
- # :nocov:
78
+ # simplecov:enable
79
79
  else
80
80
  DEFAULT_PROXY_TO_DATASET = proc{|opts| !array.respond_to?(opts[:method])}
81
81
  end
@@ -99,9 +99,9 @@ module Sequel
99
99
  end
100
100
  v.public_send(meth, *args, &block)
101
101
  end
102
- # :nocov:
102
+ # simplecov:disable
103
103
  ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
104
- # :nocov:
104
+ # simplecov:enable
105
105
  end
106
106
 
107
107
  module ClassMethods
@@ -1,5 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ # SEQUEL6: Merge into default associations code
4
+
3
5
  module Sequel
4
6
  module Plugins
5
7
  # The auto_restrict_eager_graph plugin will automatically disallow the use
@@ -29,6 +29,13 @@ module Sequel
29
29
  #
30
30
  # You should not use this plugin if you are using sharding and there are different
31
31
  # rows for the same primary key on different shards.
32
+ #
33
+ # Note that if you are using a composite key, and multiple columns in the composite
34
+ # key could potentially contain the character ",", you should override the
35
+ # +composite_cache_key+ class method to return an an unambiguous string identifying
36
+ # the specific primary key argument given. For consistency, if you would like to
37
+ # modify the cache key string used for scalar primary keys, you can override the
38
+ # +scalar_cache_key+ class method.
32
39
  #
33
40
  # Usage:
34
41
  #
@@ -53,7 +60,7 @@ module Sequel
53
60
  end
54
61
 
55
62
  module ClassMethods
56
- # If true, ignores exceptions when gettings cached records (the memcached API).
63
+ # If true, ignores exceptions when getting cached records (the memcached API).
57
64
  attr_reader :cache_ignore_exceptions
58
65
 
59
66
  # The cache store object for the model, which should implement the
@@ -81,8 +88,26 @@ module Sequel
81
88
 
82
89
  # Return a key string for the given primary key.
83
90
  def cache_key(pk)
84
- raise(Error, 'no primary key for this record') unless pk.is_a?(Array) ? pk.all? : pk
85
- "#{cache_key_prefix}:#{Array(pk).join(',')}"
91
+ case pk
92
+ when Array
93
+ raise(Error, 'no primary key for this record') unless pk.all?
94
+ "#{cache_key_prefix}:#{composite_cache_key(pk)}"
95
+ else
96
+ raise(Error, 'no primary key for this record') unless pk
97
+ "#{cache_key_prefix}:#{scalar_cache_key(pk)}"
98
+ end
99
+ end
100
+
101
+ # Return an appropriate cache key string for a valid scalar primary key.
102
+ # By default, converts the primary key to a string.
103
+ def scalar_cache_key(pk)
104
+ pk.to_s
105
+ end
106
+
107
+ # Return an appropriate cache key string for a valid composite primary key.
108
+ # By default, joins with the parts of the compsite key with ",".
109
+ def composite_cache_key(pk)
110
+ pk.join(',')
86
111
  end
87
112
 
88
113
  Plugins.inherited_instance_variables(self, :@cache_store=>nil, :@cache_ttl=>nil, :@cache_ignore_exceptions=>nil)
@@ -79,7 +79,7 @@ module Sequel
79
79
  # subclass instances that only have column entries for the columns in the
80
80
  # superclass table. Calling the column method on the subclass instance for
81
81
  # a column not in the superclass table will cause a query to the database
82
- # to get the value for that column. If the subclass instance was retreived
82
+ # to get the value for that column. If the subclass instance was retrieved
83
83
  # using Dataset#all, the query to the database will attempt to load the column
84
84
  # values for all subclass instances that were retrieved. For example:
85
85
  #
@@ -1,8 +1,8 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- # :nocov:
3
+ # simplecov:disable
4
4
  raise(Sequel::Error, "Sequel column_encryption plugin requires ruby 2.3 or greater") unless RUBY_VERSION >= '2.3'
5
- # :nocov:
5
+ # simplecov:enable
6
6
 
7
7
  require 'openssl'
8
8
 
@@ -22,13 +22,13 @@ begin
22
22
  cipher.key = '1'*32
23
23
  cipher.auth_data = ''
24
24
  cipher.auth_tag = auth_tag
25
- # :nocov:
25
+ # simplecov:disable
26
26
  unless (cipher.update(cipher_text) << cipher.final) == '2'
27
27
  raise OpenSSL::Cipher::CipherError
28
28
  end
29
29
  rescue RuntimeError, OpenSSL::Cipher::CipherError
30
30
  raise LoadError, "Sequel column_encryption plugin requires a working aes-256-gcm cipher"
31
- # :nocov:
31
+ # simplecov:enable
32
32
  end
33
33
 
34
34
  require 'securerandom'
@@ -279,7 +279,7 @@ module Sequel
279
279
  #
280
280
  # = Unsupported Features
281
281
  #
282
- # The following features are delibrately not supported:
282
+ # The following features are deliberately not supported:
283
283
  #
284
284
  # == Compression
285
285
  #
@@ -475,12 +475,12 @@ module Sequel
475
475
  def decode64(str)
476
476
  str.unpack1("m0")
477
477
  end
478
- # :nocov:
478
+ # simplecov:disable
479
479
  else
480
480
  def decode64(str)
481
481
  str.unpack("m0")[0]
482
482
  end
483
- # :nocov:
483
+ # simplecov:enable
484
484
  end
485
485
 
486
486
  def urlsafe_encode64(bin)
@@ -493,7 +493,7 @@ module Sequel
493
493
  decode64(str.tr("-_", "+/"))
494
494
  end
495
495
 
496
- # An array of strings, one for each configured encryption key, to find encypted values matching
496
+ # An array of strings, one for each configured encryption key, to find encrypted values matching
497
497
  # the given data and search format.
498
498
  def _search_prefixes(data, search_type)
499
499
  @key_map.map do |key_id, (key, _)|
@@ -610,7 +610,7 @@ module Sequel
610
610
  end
611
611
  end
612
612
 
613
- # This stores four callables for handling encyption, decryption, data searching,
613
+ # This stores four callables for handling encryption, decryption, data searching,
614
614
  # and key searching. One of these is created for each encrypted column.
615
615
  ColumnEncryptionMetadata = Struct.new(:encryptor, :decryptor, :data_searcher, :key_searcher) # :nodoc:
616
616
 
@@ -702,7 +702,7 @@ module Sequel
702
702
 
703
703
  module InstanceMethods
704
704
  # Reencrypt the model if needed. Looks at all of the models encrypted columns
705
- # and if any were encypted with older keys or a different format, reencrypt
705
+ # and if any were encrypted with older keys or a different format, reencrypt
706
706
  # with the current key and format and save the object. Returns the object
707
707
  # if reencryption was needed, or nil if reencryption was not needed.
708
708
  def reencrypt
@@ -69,8 +69,8 @@ module Sequel
69
69
  # To keep things simple, you can use +Sequel.synchronize_with+ to only
70
70
  # use this mutex if it is available. You want to use the mutex around the
71
71
  # code that initializes the associations (usually to +nil+ or <tt>[]</tt>),
72
- # and also around the code that sets the associatied objects appropriately
73
- # after they have been retreived. You do not want to use the mutex around
72
+ # and also around the code that sets the associated objects appropriately
73
+ # after they have been retrieved. You do not want to use the mutex around
74
74
  # the code that loads the objects, since that will prevent concurrent loading.
75
75
  # So after the changes, the custom eager loader would look like this:
76
76
  #
@@ -73,23 +73,12 @@ module Sequel
73
73
  end
74
74
  end
75
75
 
76
- # Avoid keyword argument separation warnings on Ruby 2.7, while still
77
- # being compatible with 1.9.
78
- if RUBY_VERSION >= "2.0"
79
- instance_eval(<<-END, __FILE__, __LINE__+1)
80
- def self.csv_call(*args, opts, &block)
81
- CSV.send(*args, **opts, &block)
82
- end
83
- END
84
- else
85
- # :nocov:
86
- # :nodoc:
87
- def self.csv_call(*args, opts, &block)
88
- CSV.send(*args, opts, &block)
89
- end
90
- # :nodoc:
91
- # :nocov:
76
+ # simplecov:disable
77
+ def self.csv_call(*args, opts, &block)
78
+ Sequel::Deprecation.deprecate("Sequel::Plugins::CsvSerializer.csv_call will be removed in Sequel 6.")
79
+ CSV.send(*args, **opts, &block)
92
80
  end
81
+ # simplecov:enable
93
82
 
94
83
  module ClassMethods
95
84
  # The default opts to use when serializing model objects to CSV
@@ -97,7 +86,7 @@ module Sequel
97
86
 
98
87
  # Attempt to parse an array of instances from the given CSV string
99
88
  def array_from_csv(csv, opts = OPTS)
100
- CsvSerializer.csv_call(:parse, csv, process_csv_serializer_opts(opts)).map do |row|
89
+ CSV.parse(csv, **process_csv_serializer_opts(opts)).map do |row|
101
90
  row = row.to_hash
102
91
  row.delete(nil)
103
92
  new(row)
@@ -147,7 +136,7 @@ module Sequel
147
136
  # :headers :: The headers to use for the CSV line. Use nil for a header
148
137
  # to specify the column should be ignored.
149
138
  def from_csv(csv, opts = OPTS)
150
- row = CsvSerializer.csv_call(:parse_line, csv, model.process_csv_serializer_opts(opts)).to_hash
139
+ row = CSV.parse_line(csv, **model.process_csv_serializer_opts(opts)).to_hash
151
140
  row.delete(nil)
152
141
  set(row)
153
142
  end
@@ -165,7 +154,7 @@ module Sequel
165
154
  opts = model.process_csv_serializer_opts(opts)
166
155
  headers = opts[:headers]
167
156
 
168
- CsvSerializer.csv_call(:generate, model.process_csv_serializer_opts(opts)) do |csv|
157
+ CSV.generate(**model.process_csv_serializer_opts(opts)) do |csv|
169
158
  csv << headers.map{|k| public_send(k)}
170
159
  end
171
160
  end
@@ -184,7 +173,7 @@ module Sequel
184
173
  items = opts.delete(:array) || self
185
174
  headers = opts[:headers]
186
175
 
187
- CsvSerializer.csv_call(:generate, opts) do |csv|
176
+ CSV.generate(**opts) do |csv|
188
177
  items.each do |object|
189
178
  csv << headers.map{|header| object.public_send(header)}
190
179
  end
@@ -105,9 +105,9 @@ module Sequel
105
105
  end
106
106
  super(*a, &b)
107
107
  end
108
- # :nocov:
108
+ # simplecov:disable
109
109
  ruby2_keywords(name) if respond_to?(:ruby2_keywords, true)
110
- # :nocov:
110
+ # simplecov:enable
111
111
  alias_method(name, name)
112
112
  end
113
113
  end
@@ -77,7 +77,7 @@ module Sequel
77
77
 
78
78
  private
79
79
 
80
- # Wether to use all when each is called, true when eager loading
80
+ # Whether to use all when each is called, true when eager loading
81
81
  # unless the flag has already been set.
82
82
  def use_eager_all?
83
83
  (opts[:eager] || opts[:eager_graph]) && !opts[:all_called]
@@ -237,7 +237,7 @@ module Sequel
237
237
  end
238
238
  end
239
239
 
240
- # Clear any finders when reseting the instance dataset
240
+ # Clear any finders when resetting the instance dataset
241
241
  def reset_instance_dataset
242
242
  Sequel.synchronize{@finders.clear} if @finders && !@finders.frozen?
243
243
  super
@@ -9,7 +9,7 @@ module Sequel
9
9
  # The main reason for doing this is it makes it easier to detect
10
10
  # N+1 query issues. Note that Sequel also offers a
11
11
  # tactical_eager_loading plugin which will automatically eagerly
12
- # load associations for all objects retrived in the same query
12
+ # load associations for all objects retrieved in the same query
13
13
  # if any object would attempt to lazily load an association. That
14
14
  # approach may be simpler if you are trying to prevent N+1 issues,
15
15
  # though it does retain more objects in memory.
@@ -125,9 +125,9 @@ module Sequel
125
125
  # an association, allow lazy loading that association, since the
126
126
  # lazy association load will use a hash table lookup and not a query.
127
127
  def allow_lazy_load_for_static_cache_associations
128
- # :nocov:
128
+ # simplecov:disable
129
129
  if defined?(::Sequel::Plugins::StaticCache::ClassMethods)
130
- # :nocov:
130
+ # simplecov:enable
131
131
  @association_reflections.each_value do |ref|
132
132
  if ref.associated_class.is_a?(::Sequel::Plugins::StaticCache::ClassMethods)
133
133
  ref[:forbid_lazy_load] = false
@@ -19,7 +19,7 @@ module Sequel
19
19
  #
20
20
  # This example will try to insert the album, but if there is an existing
21
21
  # album with the name 'Foo', this will update the copies_sold attribute
22
- # for that album. See the PostgreSQL and SQLite adapter documention for
22
+ # for that album. See the PostgreSQL and SQLite adapter documentation for
23
23
  # the options you can pass to the insert_conflict method.
24
24
  #
25
25
  # You should not attempt to use this plugin to ignore conflicts when
@@ -32,7 +32,7 @@ module Sequel
32
32
 
33
33
  private
34
34
 
35
- # When reseting the instance dataset, also reset the instance_insert_dataset.
35
+ # When resetting the instance dataset, also reset the instance_insert_dataset.
36
36
  def reset_instance_dataset
37
37
  ret = super
38
38
  return unless ds = @instance_dataset
@@ -80,6 +80,14 @@ module Sequel
80
80
  instance_filters << [args, block]
81
81
  end
82
82
 
83
+ # Clear instance filters when saving changes even if the
84
+ # object wasn't modified.
85
+ def save_changes(opts=OPTS)
86
+ ret = super
87
+ clear_instance_filters if ret.nil?
88
+ ret
89
+ end
90
+
83
91
  private
84
92
 
85
93
  # If there are any instance filters, make sure not to use the
@@ -93,6 +93,25 @@ module Sequel
93
93
  super
94
94
  end
95
95
 
96
+ # Run and clear save/update/validation hooks when saving changes even if the
97
+ # object wasn't modified.
98
+ def save_changes(opts=OPTS)
99
+ ret = super
100
+ if ret.nil? && @instance_hooks
101
+ run_before_instance_hooks(:before_validation)
102
+ run_before_instance_hooks(:after_validation)
103
+ run_before_instance_hooks(:before_save)
104
+ run_before_instance_hooks(:before_update)
105
+ ret = super
106
+ run_before_instance_hooks(:after_update)
107
+ run_before_instance_hooks(:after_save)
108
+ [:before_validation, :after_validation, :before_save, :before_update, :after_update, :after_save].each do |type|
109
+ @instance_hooks.delete(type)
110
+ end
111
+ end
112
+ ret
113
+ end
114
+
96
115
  private
97
116
 
98
117
  # Add the block as an instance level hook. For before hooks, add it to
@@ -128,7 +128,7 @@ module Sequel
128
128
  end
129
129
 
130
130
  # SEQUEL6: Remove
131
- # :nocov:
131
+ # simplecov:disable
132
132
  class Literal
133
133
  def initialize(json)
134
134
  @json = json
@@ -138,7 +138,7 @@ module Sequel
138
138
  @json
139
139
  end
140
140
  end
141
- # :nocov:
141
+ # simplecov:enable
142
142
  Sequel::Deprecation.deprecate_constant(self, :Literal)
143
143
 
144
144
  # Convert the given object to a JSON data structure using the given arguments.
@@ -151,11 +151,11 @@ module Sequel
151
151
  else
152
152
  begin
153
153
  Sequel.parse_json(Sequel.object_to_json(obj, *args, &block))
154
- # :nocov:
154
+ # simplecov:disable
155
155
  rescue Sequel.json_parser_error_class
156
156
  # Support for old Ruby code that only supports parsing JSON object/array
157
157
  Sequel.parse_json(Sequel.object_to_json([obj], *args, &block))[0]
158
- # :nocov:
158
+ # simplecov:enable
159
159
  end
160
160
  end
161
161
  end
@@ -5,7 +5,7 @@ module Sequel
5
5
  # The lazy_attributes plugin allows users to easily set that some attributes
6
6
  # should not be loaded by default when loading model objects. If the attribute
7
7
  # is needed after the instance has been retrieved, a database query is made to
8
- # retreive the value of the attribute.
8
+ # retrieve the value of the attribute.
9
9
  #
10
10
  # This plugin depends on the tactical_eager_loading plugin, and allows you to
11
11
  # eagerly load lazy attributes for all objects retrieved with the current object.
@@ -243,7 +243,7 @@ module Sequel
243
243
  # :conditions :: Extra conditions to add to the JOIN ON clause. Must be a hash or array of two pairs.
244
244
  # :join_type :: The join type to use for the join, defaults to :left_outer.
245
245
  # :only_conditions :: Conditions to use for the join instead of the ones specified by the keys.
246
- # opts :: The options for the associaion. Takes the same options as many_to_many.
246
+ # opts :: The options for the association. Takes the same options as many_to_many.
247
247
  def many_through_many(name, through, opts=OPTS, &block)
248
248
  associate(:many_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block)
249
249
  end
@@ -22,6 +22,13 @@ module Sequel
22
22
  #
23
23
  # plugin :mssql_optimistic_locking, lock_column: :column_name
24
24
  #
25
+ # By default, the plugin allows using the lock column setter method to
26
+ # either increase or decrease the lock column value. To prevent increases,
27
+ # which could potentially be used to skip an update when it update should
28
+ # be made, you can use the +:prevent_increase+ plugin option:
29
+ #
30
+ # plugin :mssql_optimistic_locking, prevent_increase: true
31
+ #
25
32
  # This plugin relies on the instance_filters plugin.
26
33
  module MssqlOptimisticLocking
27
34
  # Load the instance_filters plugin into the model.
@@ -32,6 +39,8 @@ module Sequel
32
39
  # Set the lock column
33
40
  def self.configure(model, opts=OPTS)
34
41
  model.lock_column = opts[:lock_column] || model.lock_column || :timestamp
42
+ # SEQUEL6: Make prevent_increase true by default
43
+ model.send(:prevent_lock_column_increase!) if opts[:prevent_increase]
35
44
  end
36
45
 
37
46
  module InstanceMethods
@@ -20,6 +20,13 @@ module Sequel
20
20
  #
21
21
  # plugin :optimistic_locking, lock_column: :version
22
22
  #
23
+ # By default, the plugin allows using the lock column setter method to
24
+ # either increase or decrease the lock column value. To prevent increases,
25
+ # which could potentially be used to skip an update when it update should
26
+ # be made, you can use the +:prevent_increase+ plugin option:
27
+ #
28
+ # plugin :optimistic_locking, prevent_increase: true
29
+ #
23
30
  # This plugin relies on the instance_filters plugin.
24
31
  module OptimisticLocking
25
32
  # Exception class raised when trying to update or destroy a stale object.
@@ -32,6 +39,8 @@ module Sequel
32
39
  # Set the lock column
33
40
  def self.configure(model, opts=OPTS)
34
41
  model.lock_column = opts[:lock_column] || model.lock_column || :lock_version
42
+ # SEQUEL6: Make prevent_increase true by default
43
+ model.send(:prevent_lock_column_increase!) if opts[:prevent_increase]
35
44
  end
36
45
 
37
46
  module InstanceMethods
@@ -42,9 +51,10 @@ module Sequel
42
51
  def _update_columns(columns)
43
52
  lc = model.lock_column
44
53
  lcv = get_column_value(lc)
45
- columns[lc] = lcv + 1
54
+ new_lcv = columns[lc] = lcv + 1
46
55
  super
47
- set_column_value("#{lc}=", lcv + 1)
56
+ set_column_value("#{lc}=", nil)
57
+ set_column_value("#{lc}=", new_lcv)
48
58
  changed_columns.delete(lc)
49
59
  nil
50
60
  end
@@ -12,6 +12,20 @@ module Sequel
12
12
  module ClassMethods
13
13
  # The column holding the version of the lock
14
14
  attr_accessor :lock_column
15
+
16
+ private
17
+
18
+ # Prevent increasing the value of the lock column. If the lock column
19
+ # has an existing value, do not allow setting a value that is higher
20
+ # than it.
21
+ def prevent_lock_column_increase!
22
+ lc = lock_column
23
+ define_method(:"#{lc}=") do |v|
24
+ unless v && (lv = send(lc)) && v > lv
25
+ super(v)
26
+ end
27
+ end
28
+ end
15
29
 
16
30
  Plugins.inherited_instance_variables(self, :@lock_column=>nil)
17
31
  end
@@ -534,9 +534,9 @@ module Sequel
534
534
  def many_to_pg_array_association_filter_expression(op, ref, obj)
535
535
  pk = ref.qualify(model.table_name, ref.primary_key)
536
536
  key = ref[:key]
537
- # :nocov:
537
+ # simplecov:disable
538
538
  expr = case obj
539
- # :nocov:
539
+ # simplecov:enable
540
540
  when Sequel::Model
541
541
  if (assoc_pks = obj.get_column_value(key)) && !assoc_pks.empty?
542
542
  Sequel[pk=>assoc_pks.to_a]
@@ -556,9 +556,9 @@ module Sequel
556
556
  # Support filtering by pg_array_to_many associations using a subquery.
557
557
  def pg_array_to_many_association_filter_expression(op, ref, obj)
558
558
  key = ref.qualify(model.table_name, ref[:key_column])
559
- # :nocov:
559
+ # simplecov:disable
560
560
  expr = case obj
561
- # :nocov:
561
+ # simplecov:enable
562
562
  when Sequel::Model
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))
@@ -261,9 +261,9 @@ module Sequel
261
261
  messages = model.pg_auto_constraint_validations_messages
262
262
 
263
263
  unless override
264
- # :nocov:
264
+ # simplecov:disable
265
265
  case e
266
- # :nocov:
266
+ # simplecov:enable
267
267
  when Sequel::NotNullConstraintViolation
268
268
  if column = info[:column]
269
269
  add_pg_constraint_validation_error([m.call(column)], messages[:not_null])
@@ -297,7 +297,7 @@ module Sequel
297
297
  end
298
298
  end
299
299
  rescue
300
- # If there is an error trying to conver the constraint violation
300
+ # If there is an error trying to convert the constraint violation
301
301
  # into a validation failure, it's best to just raise the constraint
302
302
  # violation. This can make debugging the above block of code more
303
303
  # difficult.
@@ -45,7 +45,7 @@ module Sequel
45
45
  key = key.column
46
46
  end
47
47
 
48
- # :nocov:
48
+ # simplecov:disable
49
49
  # many_to_pg_array association type does not need changes, as it
50
50
  # already converts the values to a typed postgres array, it does
51
51
  # not call the code that uses :eager_loading_predicate_transform.
@@ -54,7 +54,7 @@ module Sequel
54
54
  # unless it is one of these types, but external association types
55
55
  # could potentially reach it.
56
56
  sch = case ref[:type]
57
- # :nocov:
57
+ # simplecov:enable
58
58
  when :many_to_one, :one_to_one, :one_to_many, :pg_array_to_many
59
59
  ref.associated_class.db_schema
60
60
  when :many_to_many, :one_through_one
@@ -21,6 +21,13 @@ module Sequel
21
21
  # optimistic locking. The disadvantage is that testing can be
22
22
  # more difficult if you are modifying the underlying row between
23
23
  # when a model is retrieved and when it is saved.
24
+
25
+ # By default, the plugin allows using the +xmin=+ setter method to
26
+ # either increase or decrease the lock value. To prevent increases,
27
+ # which could potentially be used to skip an update when it update should
28
+ # be made, you can use the +:prevent_increase+ plugin argument:
29
+ #
30
+ # plugin :pg_xmin_optimistic_locking, prevent_increase: true
24
31
  #
25
32
  # This plugin may not work with the class_table_inheritance plugin.
26
33
  #
@@ -29,7 +36,7 @@ module Sequel
29
36
  WILDCARD = LiteralString.new('*').freeze
30
37
 
31
38
  # Define the xmin column accessor
32
- def self.apply(model)
39
+ def self.apply(model, prevent_increase: false)
33
40
  model.instance_exec do
34
41
  plugin(:optimistic_locking_base)
35
42
  @lock_column = :xmin
@@ -39,9 +46,11 @@ module Sequel
39
46
 
40
47
  # Update the dataset to append the xmin column if it is usable
41
48
  # and there is a dataset for the model.
42
- def self.configure(model)
49
+ def self.configure(model, prevent_increase: false)
43
50
  model.instance_exec do
44
51
  set_dataset(@dataset) if @dataset
52
+ # SEQUEL6: Make prevent_increase true by default
53
+ prevent_lock_column_increase! if prevent_increase
45
54
  end
46
55
  end
47
56
 
@@ -181,11 +181,11 @@ module Sequel
181
181
  # Disabling usage of prepared statements for insert_select on SQLite seems to be the
182
182
  # simplest way to workaround the problem.
183
183
  db.database_type != :sqlite
184
- # :nocov:
184
+ # simplecov:disable
185
185
  when :delete, :refresh
186
186
  Sequel::Deprecation.deprecate("The :delete and :refresh prepared statement types", "There should be no need to check if these types are supported")
187
187
  false
188
- # :nocov:
188
+ # simplecov:enable
189
189
  else
190
190
  raise Error, "unsupported type used: #{type.inspect}"
191
191
  end