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
@@ -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
@@ -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
 
@@ -0,0 +1,63 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # This plugin changes the default model behavior so that for existing
6
+ # objects, if Model#save_changes skips the save as the object is not
7
+ # modified, or if the UPDATE query is skipped as there were no columns
8
+ # to update, Sequel will issue a SELECT query to make sure the object
9
+ # still exists. This is only done if require_modification is set for
10
+ # the object, so that these cases will raise the same error that
11
+ # Sequel would raise if the UPDATE require was run but did not modify
12
+ # a row.
13
+ #
14
+ # class Item < Sequel::Model
15
+ # plugin :select_on_skipped_update
16
+ # end
17
+ #
18
+ # item = Item[1]
19
+ #
20
+ # # Delete the item from the database
21
+ # Item.where(id: 1).delete
22
+ #
23
+ # # By default, this would return nil, but with the plugin,
24
+ # # this raises Sequel::NoExistingRow.
25
+ # item.save_changes
26
+ #
27
+ # # By default, this would return the object without issuing the
28
+ # # UPDATE query (as the columns to update is empty. With the plugin,
29
+ # # this raises Sequel::NoExistingRow.
30
+ # item.save(columns: [])
31
+ module SelectOnSkippedUpdate
32
+ COUNT = Sequel.function(:count).*.as(:count)
33
+ private_constant :COUNT
34
+
35
+ module InstanceMethods
36
+ # If an UPDATE statement was skipped as the object wasn't modified, run a SELECT query.
37
+ def save_changes(opts=OPTS)
38
+ ret = super
39
+ run_select_on_skipped_update if ret.nil? && require_modification
40
+ ret
41
+ end
42
+
43
+ private
44
+
45
+ # If an UPDATE statement was skipped as there were no columns to update, run a SELECT query.
46
+ def _update_columns(columns)
47
+ ret = super
48
+ run_select_on_skipped_update if ret.nil? && columns.empty? && require_modification
49
+ ret
50
+ end
51
+
52
+ # Run a SELECT query on the dataset to determine whether the object still exists in the database.
53
+ # This will result in an exception if a single row is not modified.
54
+ def run_select_on_skipped_update
55
+ ds = _update_dataset
56
+ unless ds.select(COUNT).single_value! == 1
57
+ raise(NoMatchingRow, "Attempt to select object did not result in a single row modification")
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -12,6 +12,11 @@ module Sequel
12
12
  # argument to the plugin) holds the class name as a string. However,
13
13
  # you can override this by using the <tt>:model_map</tt> option and/or
14
14
  # the <tt>:key_map</tt> option.
15
+ #
16
+ # The +sti_key+ column can be set via mass assignment, just like any
17
+ # non-primary key column. Make sure to follow the recommendations in the
18
+ # {mass assignment guide}[rdoc-ref:doc/mass_assignment.rdoc]
19
+ # to ensure this column is not set with untrusted data.
15
20
  #
16
21
  # You should only load this plugin in the parent class, not in the subclasses.
17
22
  #
@@ -84,6 +89,7 @@ module Sequel
84
89
  @sti_key_array = nil
85
90
  @sti_key = key
86
91
  @sti_dataset = dataset
92
+ @sti_root_class = self
87
93
  @sti_model_map = opts[:model_map] || lambda{|v| v if v && v != ''}
88
94
  @sti_key_map = if km = opts[:key_map]
89
95
  if km.is_a?(Hash)
@@ -150,7 +156,7 @@ module Sequel
150
156
  # This defaults to a lookup in the key map.
151
157
  attr_reader :sti_key_chooser
152
158
 
153
- Plugins.inherited_instance_variables(self, :@sti_dataset=>nil, :@sti_key=>nil, :@sti_key_map=>nil, :@sti_model_map=>nil, :@sti_key_chooser=>nil)
159
+ Plugins.inherited_instance_variables(self, :@sti_dataset=>nil, :@sti_key=>nil, :@sti_key_map=>nil, :@sti_model_map=>nil, :@sti_key_chooser=>nil, :@sti_root_class=>nil)
154
160
 
155
161
  # Freeze STI information when freezing model class. Note that
156
162
  # because of how STI works, you should not freeze an STI subclass
@@ -221,11 +227,23 @@ module Sequel
221
227
  # Return a class object. If a class is given, return it directly.
222
228
  # Treat strings and symbols as class names. If nil is given or
223
229
  # an invalid class name string or symbol is used, return self.
230
+ # If the class is valid but is not a subclass of the class that
231
+ # loaded the single_table_inheritance plugin, use the class that
232
+ # loaded the plugin.
224
233
  # Raise an error for other types.
225
234
  def sti_class(v)
226
235
  case v
227
236
  when String, Symbol
228
- constantize(v) rescue self
237
+ begin
238
+ klass = constantize(v)
239
+ if Class === klass && @sti_root_class >= klass
240
+ klass
241
+ else
242
+ @sti_root_class
243
+ end
244
+ rescue
245
+ self
246
+ end
229
247
  when nil
230
248
  self
231
249
  when Class
@@ -105,22 +105,11 @@ module Sequel
105
105
 
106
106
  private
107
107
 
108
- if RUBY_VERSION >= '2'
109
- # Backbone of #where_any and #where_all
110
- def _where_any_all(meth, name, args)
111
- ds = model.dataset
112
- # #bind used here because the dataset module may not yet be included in the model's dataset
113
- where(name, Sequel.send(meth, *args.map{|a| self.instance_method(:"#{a}_conditions").bind(ds).call}))
114
- end
115
- else
116
- # Cannot bind module method to arbitrary objects in Ruby 1.9.
117
- # simplecov:disable
118
- def _where_any_all(meth, name, args)
119
- ds = model.dataset.clone
120
- ds.extend(self)
121
- where(name, Sequel.send(meth, *args.map{|a| ds.send(:"#{a}_conditions")}))
122
- end
123
- # simplecov:enable
108
+ # Backbone of #where_any and #where_all
109
+ def _where_any_all(meth, name, args)
110
+ ds = model.dataset
111
+ # #bind used here because the dataset module may not yet be included in the model's dataset
112
+ where(name, Sequel.send(meth, *args.map{|a| self.instance_method(:"#{a}_conditions").bind(ds).call}))
124
113
  end
125
114
  end
126
115
  end
@@ -64,7 +64,7 @@ module Sequel
64
64
  end
65
65
  end
66
66
 
67
- if RUBY_VERSION >= '2.2' && (!defined?(JRUBY_VERSION) || JRUBY_VERSION > '9.1')
67
+ if RUBY_VERSION >= '2.2' && (!defined?(JRUBY_VERSION) || JRUBY_VERSION.to_r > Rational('9.1'))
68
68
  # Throw HookFailed with the generated error. If the throw is not
69
69
  # caught, just return the originally generated error.
70
70
  def hook_failed_error(msg)
@@ -43,6 +43,12 @@ module Sequel
43
43
  module InstanceMethods
44
44
  private
45
45
 
46
+ # Whether the associated object is valid. By default, the same as calling
47
+ # +valid?+ on the object, but exists so it can be overridden.
48
+ def associated_object_valid?(reflection, obj)
49
+ obj.valid?
50
+ end
51
+
46
52
  # Delay validating the associated object until validating the current object.
47
53
  def delay_validate_associated_object(reflection, obj)
48
54
  after_validation_hook{validate_associated_object(reflection, obj)}
@@ -62,7 +68,7 @@ module Sequel
62
68
  end
63
69
  end
64
70
 
65
- unless obj.valid?
71
+ unless associated_object_valid?(reflection, obj)
66
72
  if ignore_key_errors
67
73
  # Ignore errors on the key column in the associated object. This column
68
74
  # will be set when saving to a presumably valid value using a column
@@ -0,0 +1,52 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The validate_associated_context plugin extends the validate_associated
6
+ # plugin to work with the validation_contexts plugin. It ensures associated
7
+ # objects are validated using the validation context of the current object.
8
+ #
9
+ # This plugin loads the validate_associated and validation_contexts plugins.
10
+ # The associated model must also use the validation_contexts plugin in order
11
+ # to access the validation context.
12
+ #
13
+ # class Album < Sequel::Model
14
+ # plugin :validate_associated_context
15
+ # many_to_one :artist
16
+ #
17
+ # def validate
18
+ # super
19
+ # validate_associated_object(model.association_reflection(:artist), artist) if artist
20
+ # end
21
+ # end
22
+ #
23
+ # class Artist < Sequel::Model
24
+ # plugin :validation_contexts
25
+ # def validate
26
+ # super
27
+ # errors.add(:name, 'not present') if name.nil? && validation_context == :publish
28
+ # end
29
+ # end
30
+ #
31
+ # album = Album.new(artist: Artist.new)
32
+ # album.valid? # => true
33
+ # album.valid?(validation_context: :publish) # => false
34
+ module ValidateAssociatedContext
35
+ # Depend on the validate_associated and validation_contexts plugins.
36
+ def self.apply(model)
37
+ model.plugin :validate_associated
38
+ model.plugin :validation_contexts
39
+ end
40
+
41
+ module InstanceMethods
42
+ private
43
+
44
+ # Whether the associated object is valid, using the validation context
45
+ # of the current object.
46
+ def associated_object_valid?(reflection, obj)
47
+ obj.valid?(:validation_context=>validation_context)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/sequel/sql.rb CHANGED
@@ -1060,123 +1060,127 @@ module Sequel
1060
1060
  # in a boolean value in SQL.
1061
1061
  class BooleanExpression < ComplexExpression
1062
1062
  include BooleanMethods
1063
-
1064
- # Take pairs of values (e.g. a hash or array of two element arrays)
1065
- # and converts it to a +BooleanExpression+. The operator and args
1066
- # used depends on the case of the right (2nd) argument:
1067
- #
1068
- # 0..10 :: left >= 0 AND left <= 10
1069
- # [1,2] :: left IN (1,2)
1070
- # nil :: left IS NULL
1071
- # true :: left IS TRUE
1072
- # false :: left IS FALSE
1073
- # /as/ :: left ~ 'as'
1074
- # :blah :: left = blah
1075
- # 'blah' :: left = 'blah'
1076
- #
1077
- # If multiple arguments are given, they are joined with the op given (AND
1078
- # by default, OR possible). If negate is set to true,
1079
- # all subexpressions are inverted before used. Therefore, the following
1080
- # expressions are equivalent:
1081
- #
1082
- # ~from_value_pairs(hash)
1083
- # from_value_pairs(hash, :OR, true)
1084
- def self.from_value_pairs(pairs, op=:AND, negate=false)
1085
- pairs = pairs.map{|l,r| from_value_pair(l, r)}
1086
- pairs.map!{|ce| invert(ce)} if negate
1087
- pairs.length == 1 ? pairs[0] : new(op, *pairs)
1088
- end
1089
-
1090
- # Return a BooleanExpression based on the right side of the pair.
1091
- def self.from_value_pair(l, r)
1092
- case r
1093
- when Range
1094
- unless r.begin.nil?
1095
- begin_expr = new(:>=, l, r.begin)
1096
- end
1097
- unless r.end.nil?
1098
- end_expr = new(r.exclude_end? ? :< : :<=, l, r.end)
1099
- end
1100
- if begin_expr
1101
- if end_expr
1102
- new(:AND, begin_expr, end_expr)
1063
+
1064
+ module ClassMethods
1065
+ # Take pairs of values (e.g. a hash or array of two element arrays)
1066
+ # and converts it to a +BooleanExpression+. The operator and args
1067
+ # used depends on the case of the right (2nd) argument:
1068
+ #
1069
+ # 0..10 :: left >= 0 AND left <= 10
1070
+ # [1,2] :: left IN (1,2)
1071
+ # nil :: left IS NULL
1072
+ # true :: left IS TRUE
1073
+ # false :: left IS FALSE
1074
+ # /as/ :: left ~ 'as'
1075
+ # :blah :: left = blah
1076
+ # 'blah' :: left = 'blah'
1077
+ #
1078
+ # If multiple arguments are given, they are joined with the op given (AND
1079
+ # by default, OR possible). If negate is set to true,
1080
+ # all subexpressions are inverted before used. Therefore, the following
1081
+ # expressions are equivalent:
1082
+ #
1083
+ # ~from_value_pairs(hash)
1084
+ # from_value_pairs(hash, :OR, true)
1085
+ def from_value_pairs(pairs, op=:AND, negate=false)
1086
+ pairs = pairs.map{|l,r| from_value_pair(l, r)}
1087
+ pairs.map!{|ce| invert(ce)} if negate
1088
+ pairs.length == 1 ? pairs[0] : new(op, *pairs)
1089
+ end
1090
+
1091
+ # Return a BooleanExpression based on the right side of the pair.
1092
+ def from_value_pair(l, r)
1093
+ case r
1094
+ when Range
1095
+ unless r.begin.nil?
1096
+ begin_expr = new(:>=, l, r.begin)
1097
+ end
1098
+ unless r.end.nil?
1099
+ end_expr = new(r.exclude_end? ? :< : :<=, l, r.end)
1100
+ end
1101
+ if begin_expr
1102
+ if end_expr
1103
+ new(:AND, begin_expr, end_expr)
1104
+ else
1105
+ begin_expr
1106
+ end
1107
+ elsif end_expr
1108
+ end_expr
1103
1109
  else
1104
- begin_expr
1110
+ new(:'=', 1, 1)
1105
1111
  end
1106
- elsif end_expr
1107
- end_expr
1108
- else
1109
- new(:'=', 1, 1)
1110
- end
1111
- when ::Array, ::Set
1112
- r = r.dup.freeze unless r.frozen?
1113
- new(:IN, l, r)
1114
- when ::String
1115
- r = r.dup.freeze unless r.frozen?
1116
- new(:'=', l, r)
1117
- when ::Sequel::Dataset
1118
- new(:IN, l, r)
1119
- when NegativeBooleanConstant
1120
- new(:"IS NOT", l, r.constant)
1121
- when BooleanConstant
1122
- new(:IS, l, r.constant)
1123
- when NilClass, TrueClass, FalseClass
1124
- new(:IS, l, r)
1125
- when Regexp
1126
- StringExpression.like(l, r)
1127
- when DelayedEvaluation
1128
- Sequel.delay{|ds| from_value_pair(l, r.call(ds))}
1129
- when Dataset::PlaceholderLiteralizer::Argument
1130
- prev_transform = r.instance_variable_get(:@transformer)
1131
- r.transform do |v|
1132
- if prev_transform
1133
- v = prev_transform.call(v)
1112
+ when ::Array, ::Set
1113
+ r = r.dup.freeze unless r.frozen?
1114
+ new(:IN, l, r)
1115
+ when ::String
1116
+ r = r.dup.freeze unless r.frozen?
1117
+ new(:'=', l, r)
1118
+ when ::Sequel::Dataset
1119
+ new(:IN, l, r)
1120
+ when NegativeBooleanConstant
1121
+ new(:"IS NOT", l, r.constant)
1122
+ when BooleanConstant
1123
+ new(:IS, l, r.constant)
1124
+ when NilClass, TrueClass, FalseClass
1125
+ new(:IS, l, r)
1126
+ when Regexp
1127
+ StringExpression.like(l, r)
1128
+ when DelayedEvaluation
1129
+ Sequel.delay{|ds| from_value_pair(l, r.call(ds))}
1130
+ when Dataset::PlaceholderLiteralizer::Argument
1131
+ prev_transform = r.instance_variable_get(:@transformer)
1132
+ r.transform do |v|
1133
+ if prev_transform
1134
+ v = prev_transform.call(v)
1135
+ end
1136
+ from_value_pair(l, v)
1134
1137
  end
1135
- from_value_pair(l, v)
1138
+ else
1139
+ new(:'=', l, r)
1136
1140
  end
1137
- else
1138
- new(:'=', l, r)
1139
1141
  end
1140
- end
1141
- private_class_method :from_value_pair
1142
-
1143
- # Invert the expression, if possible. If the expression cannot
1144
- # be inverted, raise an error. An inverted expression should match everything that the
1145
- # uninverted expression did not match, and vice-versa, except for possible issues with
1146
- # SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
1147
- #
1148
- # BooleanExpression.invert(:a) # NOT "a"
1149
- def self.invert(ce)
1150
- case ce
1151
- when BooleanExpression
1152
- case op = ce.op
1153
- when :AND, :OR
1154
- BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
1155
- when :IN, :"NOT IN"
1156
- BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1157
- else
1158
- if ce.args.length == 2
1159
- case ce.args[1]
1160
- when Function, LiteralString, PlaceholderLiteralString
1161
- # Special behavior to not push down inversion in this case because doing so
1162
- # can result in incorrect behavior for ANY/SOME/ALL operators.
1163
- BooleanExpression.new(:NOT, ce)
1142
+ private :from_value_pair
1143
+
1144
+ # Invert the expression, if possible. If the expression cannot
1145
+ # be inverted, raise an error. An inverted expression should match everything that the
1146
+ # uninverted expression did not match, and vice-versa, except for possible issues with
1147
+ # SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
1148
+ #
1149
+ # BooleanExpression.invert(:a) # NOT "a"
1150
+ def invert(ce)
1151
+ case ce
1152
+ when BooleanExpression
1153
+ case op = ce.op
1154
+ when :AND, :OR
1155
+ BooleanExpression.new(ComplexExpression::OPERATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
1156
+ when :IN, :"NOT IN"
1157
+ BooleanExpression.new(ComplexExpression::OPERATOR_INVERSIONS[op], *ce.args.dup)
1158
+ else
1159
+ if ce.args.length == 2
1160
+ case ce.args[1]
1161
+ when Function, LiteralString, PlaceholderLiteralString
1162
+ # Special behavior to not push down inversion in this case because doing so
1163
+ # can result in incorrect behavior for ANY/SOME/ALL operators.
1164
+ BooleanExpression.new(:NOT, ce)
1165
+ else
1166
+ BooleanExpression.new(ComplexExpression::OPERATOR_INVERSIONS[op], *ce.args.dup)
1167
+ end
1164
1168
  else
1165
- BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1169
+ BooleanExpression.new(ComplexExpression::OPERATOR_INVERSIONS[op], *ce.args.dup)
1166
1170
  end
1167
- else
1168
- BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1169
1171
  end
1172
+ when StringExpression, NumericExpression
1173
+ raise(Sequel::Error, "cannot invert #{ce.inspect}")
1174
+ when Constant
1175
+ ComplexExpression::CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
1176
+ else
1177
+ BooleanExpression.new(:NOT, ce)
1170
1178
  end
1171
- when StringExpression, NumericExpression
1172
- raise(Sequel::Error, "cannot invert #{ce.inspect}")
1173
- when Constant
1174
- CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
1175
- else
1176
- BooleanExpression.new(:NOT, ce)
1177
1179
  end
1178
1180
  end
1179
1181
 
1182
+ extend ClassMethods
1183
+
1180
1184
  # Always use an AND operator for & on BooleanExpressions
1181
1185
  def &(ce)
1182
1186
  BooleanExpression.new(:AND, self, ce)
@@ -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 = 108
9
+ MINOR = 109
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.108.0
4
+ version: 5.109.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
@@ -386,6 +386,7 @@ files:
386
386
  - lib/sequel/plugins/primary_key_lookup_check_values.rb
387
387
  - lib/sequel/plugins/rcte_tree.rb
388
388
  - lib/sequel/plugins/require_valid_schema.rb
389
+ - lib/sequel/plugins/select_on_skipped_update.rb
389
390
  - lib/sequel/plugins/serialization.rb
390
391
  - lib/sequel/plugins/serialization_modification_detection.rb
391
392
  - lib/sequel/plugins/sharding.rb
@@ -416,6 +417,7 @@ files:
416
417
  - lib/sequel/plugins/update_refresh.rb
417
418
  - lib/sequel/plugins/uuid.rb
418
419
  - lib/sequel/plugins/validate_associated.rb
420
+ - lib/sequel/plugins/validate_associated_context.rb
419
421
  - lib/sequel/plugins/validation_class_methods.rb
420
422
  - lib/sequel/plugins/validation_contexts.rb
421
423
  - lib/sequel/plugins/validation_helpers.rb
@@ -448,14 +450,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
448
450
  requirements:
449
451
  - - ">="
450
452
  - !ruby/object:Gem::Version
451
- version: 1.9.2
453
+ version: '2.0'
452
454
  required_rubygems_version: !ruby/object:Gem::Requirement
453
455
  requirements:
454
456
  - - ">="
455
457
  - !ruby/object:Gem::Version
456
458
  version: '0'
457
459
  requirements: []
458
- rubygems_version: 4.0.16
460
+ rubygems_version: 4.0.20
459
461
  specification_version: 4
460
462
  summary: The Database Toolkit for Ruby
461
463
  test_files: []