sequel 5.108.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/adapters/amalgalite.rb +34 -8
  3. data/lib/sequel/adapters/jdbc/mssql.rb +28 -5
  4. data/lib/sequel/adapters/jdbc/postgresql.rb +14 -3
  5. data/lib/sequel/adapters/jdbc.rb +2 -1
  6. data/lib/sequel/adapters/postgres.rb +5 -2
  7. data/lib/sequel/adapters/shared/db2.rb +3 -0
  8. data/lib/sequel/adapters/shared/mssql.rb +7 -1
  9. data/lib/sequel/adapters/shared/postgres.rb +19 -990
  10. data/lib/sequel/adapters/shared/sqlanywhere.rb +0 -32
  11. data/lib/sequel/adapters/sqlite.rb +13 -9
  12. data/lib/sequel/adapters/trilogy.rb +2 -1
  13. data/lib/sequel/core.rb +14 -2
  14. data/lib/sequel/database/misc.rb +1 -6
  15. data/lib/sequel/database/transactions.rb +1 -3
  16. data/lib/sequel/dataset/actions.rb +1 -1
  17. data/lib/sequel/dataset/sql.rb +14 -3
  18. data/lib/sequel/extensions/core_refinements.rb +1 -4
  19. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -3
  20. data/lib/sequel/extensions/lit_require_frozen.rb +17 -1
  21. data/lib/sequel/extensions/named_timezones.rb +1 -1
  22. data/lib/sequel/extensions/pg_array.rb +1 -0
  23. data/lib/sequel/extensions/pg_json_ops.rb +4 -2
  24. data/lib/sequel/extensions/s.rb +4 -8
  25. data/lib/sequel/extensions/schema_dumper.rb +6 -2
  26. data/lib/sequel/extensions/symbol_aref.rb +11 -31
  27. data/lib/sequel/extensions/symbol_aref_refinement.rb +1 -4
  28. data/lib/sequel/extensions/symbol_as.rb +1 -2
  29. data/lib/sequel/extensions/symbol_as_refinement.rb +1 -5
  30. data/lib/sequel/model/base.rb +1 -6
  31. data/lib/sequel/plugins/caching.rb +27 -2
  32. data/lib/sequel/plugins/csv_serializer.rb +9 -20
  33. data/lib/sequel/plugins/instance_filters.rb +8 -0
  34. data/lib/sequel/plugins/instance_hooks.rb +19 -0
  35. data/lib/sequel/plugins/mssql_optimistic_locking.rb +9 -0
  36. data/lib/sequel/plugins/optimistic_locking.rb +12 -2
  37. data/lib/sequel/plugins/optimistic_locking_base.rb +14 -0
  38. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +11 -2
  39. data/lib/sequel/plugins/select_on_skipped_update.rb +63 -0
  40. data/lib/sequel/plugins/single_table_inheritance.rb +20 -2
  41. data/lib/sequel/plugins/subset_conditions.rb +5 -16
  42. data/lib/sequel/plugins/throw_failures.rb +1 -1
  43. data/lib/sequel/plugins/validate_associated.rb +7 -1
  44. data/lib/sequel/plugins/validate_associated_context.rb +52 -0
  45. data/lib/sequel/sql.rb +109 -105
  46. data/lib/sequel/version.rb +1 -1
  47. metadata +5 -3
@@ -305,38 +305,6 @@ module Sequel
305
305
  super(sql, :+, args)
306
306
  when :<<, :>>
307
307
  complex_expression_emulate_append(sql, op, args)
308
- when :LIKE, :"NOT LIKE"
309
- sql << '('
310
- literal_append(sql, args[0])
311
- sql << (op == :LIKE ? ' REGEXP ' : ' NOT REGEXP ')
312
- pattern = String.new
313
- last_c = ''
314
- args[1].each_char do |c|
315
- if c == '_' and not pattern.end_with?('\\') and last_c != '\\'
316
- pattern << '.'
317
- elsif c == '%' and not pattern.end_with?('\\') and last_c != '\\'
318
- pattern << '.*'
319
- elsif c == '[' and not pattern.end_with?('\\') and last_c != '\\'
320
- pattern << '\['
321
- elsif c == ']' and not pattern.end_with?('\\') and last_c != '\\'
322
- pattern << '\]'
323
- elsif c == '*' and not pattern.end_with?('\\') and last_c != '\\'
324
- pattern << '\*'
325
- elsif c == '?' and not pattern.end_with?('\\') and last_c != '\\'
326
- pattern << '\?'
327
- else
328
- pattern << c
329
- end
330
- if c == '\\' and last_c == '\\'
331
- last_c = ''
332
- else
333
- last_c = c
334
- end
335
- end
336
- literal_append(sql, pattern)
337
- sql << " ESCAPE "
338
- literal_append(sql, "\\")
339
- sql << ')'
340
308
  when :ILIKE, :"NOT ILIKE"
341
309
  super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), args)
342
310
  when :extract
@@ -336,16 +336,20 @@ module Sequel
336
336
  log_sql << sql
337
337
  log_sql << ")"
338
338
  end
339
- if block
340
- log_connection_yield(log_sql, conn, args){cps.execute(ps_args, &block)}
341
- else
342
- log_connection_yield(log_sql, conn, args){cps.execute!(ps_args){|r|}}
343
- case type
344
- when :insert
345
- conn.last_insert_row_id
346
- when :update
347
- conn.changes
339
+ begin
340
+ if block
341
+ log_connection_yield(log_sql, conn, args){cps.execute(ps_args, &block)}
342
+ else
343
+ log_connection_yield(log_sql, conn, args){cps.execute!(ps_args){|r|}}
344
+ case type
345
+ when :insert
346
+ conn.last_insert_row_id
347
+ when :update
348
+ conn.changes
349
+ end
348
350
  end
351
+ ensure
352
+ cps.clear_bindings!
349
353
  end
350
354
  end
351
355
 
@@ -62,7 +62,8 @@ module Sequel
62
62
  private
63
63
 
64
64
  def database_specific_error_class(exception, opts)
65
- if exception.error_code == 1205
65
+ case exception.error_code
66
+ when 1205, 3572
66
67
  DatabaseLockTimeout
67
68
  else
68
69
  super
data/lib/sequel/core.rb CHANGED
@@ -156,7 +156,15 @@ module Sequel
156
156
  # Sequel.extension(:blank)
157
157
  # Sequel.extension(:core_extensions, :named_timezones)
158
158
  def extension(*extensions)
159
- extensions.each{|e| orig_require("sequel/extensions/#{e}")}
159
+ extensions.each do |e|
160
+ e = e.to_s
161
+
162
+ if %r{..[/\\]} =~ e
163
+ raise Error, "cannot use extension with ../ for security reasons"
164
+ end
165
+
166
+ orig_require("sequel/extensions/#{e}")
167
+ end
160
168
  end
161
169
 
162
170
  # The exception classed raised if there is an error parsing JSON.
@@ -203,7 +211,11 @@ module Sequel
203
211
  # Parse the string as JSON and return the result.
204
212
  # This can be overridden to use an alternative json implementation.
205
213
  def parse_json(json)
206
- JSON.parse(json, :create_additions=>false)
214
+ if JSON::VERSION >= '3'
215
+ JSON.parse(json)
216
+ else
217
+ JSON.parse(json, :create_additions=>false)
218
+ end
207
219
  end
208
220
 
209
221
  # If a mutex is given, synchronize access using it. If nil is given, just
@@ -229,12 +229,7 @@ module Sequel
229
229
  end
230
230
 
231
231
  # Disallow dup/clone for Database instances
232
- undef_method :dup, :clone, :initialize_copy
233
- # simplecov:disable
234
- if RUBY_VERSION >= '1.9.3'
235
- # simplecov:enable
236
- undef_method :initialize_clone, :initialize_dup
237
- end
232
+ undef_method :dup, :clone, :initialize_copy, :initialize_clone, :initialize_dup
238
233
 
239
234
  # Cast the given type to a literal type
240
235
  #
@@ -374,9 +374,7 @@ module Sequel
374
374
 
375
375
  # Whether to commit the current transaction. Thread.current.status is
376
376
  # checked because Thread#kill skips rescue blocks (so exception would be
377
- # nil), but the transaction should still be rolled back. On Ruby 1.9 (but
378
- # not 2.0+), the thread status will still be "run", so Thread#kill
379
- # will erroneously commit the transaction, and there isn't a workaround.
377
+ # nil), but the transaction should still be rolled back.
380
378
  def commit_or_rollback_transaction(exception, conn, opts)
381
379
  if exception
382
380
  false
@@ -242,7 +242,7 @@ module Sequel
242
242
  end
243
243
  when 1
244
244
  arg = args[0]
245
- if arg.is_a?(Integer)
245
+ if arg.is_a?(Integer) && arg >= 0
246
246
  res = if block
247
247
  if loader = cached_placeholder_literalizer(:_first_integer_cond_loader) do |pl|
248
248
  where(pl.arg).limit(pl.arg)
@@ -1346,10 +1346,21 @@ module Sequel
1346
1346
  alias delete_returning_sql insert_returning_sql
1347
1347
  alias update_returning_sql insert_returning_sql
1348
1348
 
1349
- # SQL fragment specifying a JOIN type, converts underscores to
1350
- # spaces and upcases.
1349
+ JOIN_TYPE_SQL = {}
1350
+ (CONDITIONED_JOIN_TYPES + UNCONDITIONED_JOIN_TYPES + %i[natural_inner natural_left_outer natural_right_outer natural_full_outer]).each do |join_type|
1351
+ JOIN_TYPE_SQL[join_type] = "#{join_type.to_s.tr('_', ' ').upcase} JOIN".freeze
1352
+ end
1353
+ JOIN_TYPE_SQL[nil] = "JOIN"
1354
+ JOIN_TYPE_SQL.freeze
1355
+ private_constant :JOIN_TYPE_SQL
1356
+
1357
+ # SQL fragment for join type
1351
1358
  def join_type_sql(join_type)
1352
- "#{join_type.to_s.tr('_', ' ').upcase} JOIN"
1359
+ JOIN_TYPE_SQL.fetch(join_type) do
1360
+ # SEQUEL6: Raise error
1361
+ Sequel::Deprecation.deprecate("Unrecognized join type used: #{join_type.inspect}. This will raise an error starting in Sequel 6.")
1362
+ "#{join_type.to_s.tr('_', ' ').upcase} JOIN"
1363
+ end
1353
1364
  end
1354
1365
 
1355
1366
  # Append USING clause for JOIN USING
@@ -10,10 +10,7 @@
10
10
  #
11
11
  # using Sequel::CoreRefinements
12
12
 
13
- # simplecov:disable
14
- raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
15
- # simplecov:enable
16
-
13
+ #
17
14
  module Sequel::CoreRefinements
18
15
  # simplecov:disable
19
16
  include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
@@ -40,9 +40,7 @@
40
40
 
41
41
  module Sequel
42
42
  module DuplicateColumnsHandler
43
- # simplecov:disable
44
- CALLER_ARGS = (RUBY_VERSION >= '2.0' ? [0,1] : [0]).freeze
45
- # simplecov:enable
43
+ CALLER_ARGS = [0,1].freeze
46
44
 
47
45
  # Customize handling of duplicate columns for this dataset.
48
46
  def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless defined?(yield); nil), &block)
@@ -63,6 +63,20 @@ module Sequel
63
63
  class Error < Sequel::Error
64
64
  end
65
65
 
66
+ module BooleanExpressionClassMethods
67
+ private
68
+
69
+ # Do not freeze literal strings in value pairs, so they don't skip
70
+ # the frozen checks.
71
+ def from_value_pair(l, r)
72
+ if LiteralString === r
73
+ new(:'=', l, r)
74
+ else
75
+ super
76
+ end
77
+ end
78
+ end
79
+
66
80
  module DatabaseMethods
67
81
  def self.extended(db)
68
82
  db.extend_datasets(DatasetMethods)
@@ -105,7 +119,7 @@ module Sequel
105
119
  def _check_unfrozen_literal_string(str)
106
120
  return if !str.is_a?(String) || str.frozen?
107
121
 
108
- if str.is_a?(LiteralString)
122
+ if str.is_a?(LiteralString) && str == str.source
109
123
  _check_unfrozen_literal_string(str.source)
110
124
  else
111
125
  raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
@@ -126,6 +140,8 @@ module Sequel
126
140
  end
127
141
  end
128
142
 
143
+ SQL::BooleanExpression.extend(LitRequireFrozen::BooleanExpressionClassMethods)
144
+
129
145
  Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
130
146
  Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
131
147
  end
@@ -69,7 +69,7 @@ module Sequel
69
69
 
70
70
  if RUBY_VERSION >= '2.6'
71
71
  # Whether Time.at with :nsec and :in is broken. True on JRuby < 9.3.9.0.
72
- BROKEN_TIME_AT_WITH_NSEC = defined?(JRUBY_VERSION) && (JRUBY_VERSION < '9.3' || (JRUBY_VERSION < '9.4' && JRUBY_VERSION.split('.')[2].to_i < 9))
72
+ BROKEN_TIME_AT_WITH_NSEC = defined?(JRUBY_VERSION) && (JRUBY_VERSION.to_r < Rational('9.3') || (JRUBY_VERSION.to_r < Rational('9.4') && JRUBY_VERSION.split('.')[2].to_i < 9))
73
73
  private_constant :BROKEN_TIME_AT_WITH_NSEC
74
74
 
75
75
  # Convert the given input Time (which must be in UTC) to the given input timezone,
@@ -390,6 +390,7 @@ module Sequel
390
390
  elsif char == '{'
391
391
  raise Sequel::Error, "invalid array, opening brace with existing recorded data" unless @recorded.empty?
392
392
 
393
+ raise Sequel::Error, "cannot parse array with more than 6 dimensions" if @stack.length >= 6
393
394
  # Start of new array, add it to the stack
394
395
  new = []
395
396
  @stack.last << new
@@ -984,7 +984,8 @@ module Sequel
984
984
  comma = true
985
985
  end
986
986
  ds.literal_append(sql, v)
987
- sql << " AS " << k.to_s
987
+ sql << " AS "
988
+ ds.send(:identifier_append, sql, k)
988
989
  end
989
990
  end
990
991
  end
@@ -1254,7 +1255,8 @@ module Sequel
1254
1255
  comma = true
1255
1256
  end
1256
1257
  ds.literal_append(sql, v)
1257
- sql << " AS " << k.to_s
1258
+ sql << " AS "
1259
+ ds.send(:identifier_append, sql, k)
1258
1260
  end
1259
1261
  end
1260
1262
 
@@ -29,8 +29,7 @@
29
29
  #
30
30
  # Object.send(:include, Sequel::S)
31
31
  #
32
- # If you are using Ruby 2+, and you would like to use refinements, you
33
- # can use Sequel::S as a refinement, in which case the private #S method
32
+ # You can use also Sequel::S as a refinement, in which case the private #S method
34
33
  # will be available on all objects while the refinement is active.
35
34
  #
36
35
  # using Sequel::S
@@ -39,7 +38,6 @@
39
38
  #
40
39
  # Related module: Sequel::S
41
40
 
42
-
43
41
  #
44
42
  module Sequel::S
45
43
  private
@@ -50,11 +48,9 @@ module Sequel::S
50
48
  end
51
49
 
52
50
  # simplecov:disable
53
- if RUBY_VERSION >= '2.0.0'
54
- include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
51
+ include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
55
52
  # simplecov:enable
56
- refine Object do
57
- send include_meth, Sequel::S
58
- end
53
+ refine Object do
54
+ send include_meth, Sequel::S
59
55
  end
60
56
  end
@@ -200,8 +200,12 @@ END_MIG
200
200
 
201
201
  # Recreate the column in the passed Schema::CreateTableGenerator from the given name and parsed database schema.
202
202
  def recreate_column(name, schema, gen, options)
203
+ db_type = schema[:db_type]
204
+ db_type = literal(db_type) if options[:same_db] && database_type == :sqlite
205
+
203
206
  if options[:single_pk] && schema_autoincrementing_primary_key?(schema)
204
- type_hash = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
207
+ type_hash = options[:same_db] ? {:type=>db_type} : column_schema_to_ruby_type(schema)
208
+
205
209
  [:table, :key, :on_delete, :on_update, :deferrable].each{|f| type_hash[f] = schema[f] if schema[f]}
206
210
  if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"} || type_hash == {:type=>"INTEGER"}
207
211
  type_hash.delete(:type)
@@ -220,7 +224,7 @@ END_MIG
220
224
  end
221
225
  else
222
226
  col_opts = if options[:same_db]
223
- h = {:type=>schema[:db_type]}
227
+ h = {:type=>db_type}
224
228
  if database_type == :mysql && h[:type].start_with?("timestamp")
225
229
  h[:null] = true
226
230
  end
@@ -15,41 +15,21 @@
15
15
  #
16
16
  # Sequel.extension :symbol_aref
17
17
  #
18
- # If you are using Ruby 2+, and you would like to use refinements, there
19
- # is a refinement version of this in the symbol_aref_refinement extension.
18
+ # There is a refinement version of this in the symbol_aref_refinement extension.
20
19
  #
21
20
  # Related module: Sequel::SymbolAref
22
21
 
23
- if RUBY_VERSION >= '2.0'
24
- module Sequel::SymbolAref
25
- def [](v)
26
- case v
27
- when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier
28
- Sequel::SQL::QualifiedIdentifier.new(self, v)
29
- else
30
- super
31
- end
22
+ module Sequel::SymbolAref
23
+ def [](v)
24
+ case v
25
+ when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier
26
+ Sequel::SQL::QualifiedIdentifier.new(self, v)
27
+ else
28
+ super
32
29
  end
33
30
  end
31
+ end
34
32
 
35
- class Symbol
36
- prepend Sequel::SymbolAref
37
- end
38
- # simplecov:disable
39
- else
40
- class Symbol
41
- if method_defined?(:[])
42
- alias_method :aref_before_sequel, :[]
43
- end
44
-
45
- def [](v)
46
- case v
47
- when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier
48
- Sequel::SQL::QualifiedIdentifier.new(self, v)
49
- else
50
- aref_before_sequel(v)
51
- end
52
- end
53
- end
33
+ class Symbol
34
+ prepend Sequel::SymbolAref
54
35
  end
55
- # simplecov:enable
@@ -25,10 +25,7 @@
25
25
  #
26
26
  # Related module: Sequel::SymbolAref
27
27
 
28
- # simplecov:disable
29
- raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
30
- # simplecov:enable
31
-
28
+ #
32
29
  module Sequel::SymbolAref
33
30
  refine Symbol do
34
31
  def [](v)
@@ -14,8 +14,7 @@
14
14
  #
15
15
  # Sequel.extension :symbol_as
16
16
  #
17
- # If you are using Ruby 2+, and you would like to use refinements, there
18
- # is a refinement version of this in the symbol_as_refinement extension.
17
+ # There is a refinement version of this in the symbol_as_refinement extension.
19
18
 
20
19
  #
21
20
  class Symbol
@@ -23,10 +23,7 @@
23
23
  #
24
24
  # Related module: Sequel::SymbolAs
25
25
 
26
- # simplecov:disable
27
- raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
28
- # simplecov:enable
29
-
26
+ #
30
27
  module Sequel::SymbolAs
31
28
  refine Symbol do
32
29
  def as(aliaz, columns=nil)
@@ -34,4 +31,3 @@ module Sequel::SymbolAs
34
31
  end
35
32
  end
36
33
  end
37
-
@@ -2433,12 +2433,7 @@ END
2433
2433
  plugin self
2434
2434
  def_initialize_nil_instance_variables
2435
2435
 
2436
- singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
2437
- # simplecov:disable
2438
- if RUBY_VERSION >= '1.9.3'
2439
- # simplecov:enable
2440
- singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
2441
- end
2436
+ singleton_class.send(:undef_method, :dup, :clone, :initialize_copy, :initialize_clone, :initialize_dup)
2442
2437
 
2443
2438
  # simplecov:disable
2444
2439
  if defined?(Sequel::Postgres::SEQUEL_PG_VERSION_INTEGER) && Sequel::Postgres::SEQUEL_PG_VERSION_INTEGER >= 11800
@@ -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
  #
@@ -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)
@@ -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
- # simplecov:disable
86
- # :nodoc:
87
- def self.csv_call(*args, opts, &block)
88
- CSV.send(*args, opts, &block)
89
- end
90
- # :nodoc:
91
- # simplecov:enable
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
@@ -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
@@ -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