sequel 5.24.0 → 5.29.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.
- checksums.yaml +4 -4
- data/CHANGELOG +58 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/doc/cheat_sheet.rdoc +1 -0
- data/doc/postgresql.rdoc +2 -2
- data/doc/release_notes/5.25.0.txt +32 -0
- data/doc/release_notes/5.26.0.txt +35 -0
- data/doc/release_notes/5.27.0.txt +21 -0
- data/doc/release_notes/5.28.0.txt +16 -0
- data/doc/release_notes/5.29.0.txt +22 -0
- data/doc/testing.rdoc +11 -6
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/postgres.rb +5 -1
- data/lib/sequel/adapters/shared/mssql.rb +4 -2
- data/lib/sequel/adapters/shared/mysql.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +15 -0
- data/lib/sequel/adapters/shared/sqlite.rb +7 -2
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/transactions.rb +3 -3
- data/lib/sequel/dataset/features.rb +6 -0
- data/lib/sequel/dataset/misc.rb +2 -2
- data/lib/sequel/dataset/query.rb +15 -2
- data/lib/sequel/dataset/sql.rb +17 -4
- data/lib/sequel/extensions/any_not_empty.rb +45 -0
- data/lib/sequel/extensions/exclude_or_null.rb +68 -0
- data/lib/sequel/extensions/pg_array_ops.rb +10 -6
- data/lib/sequel/extensions/pg_enum.rb +4 -1
- data/lib/sequel/extensions/pg_json.rb +1 -1
- data/lib/sequel/extensions/pg_json_ops.rb +124 -0
- data/lib/sequel/extensions/pg_range.rb +9 -0
- data/lib/sequel/extensions/sql_comments.rb +2 -2
- data/lib/sequel/model/base.rb +12 -5
- data/lib/sequel/plugins/association_multi_add_remove.rb +83 -0
- data/lib/sequel/plugins/caching.rb +3 -0
- data/lib/sequel/plugins/csv_serializer.rb +26 -9
- data/lib/sequel/plugins/dirty.rb +3 -9
- data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
- data/lib/sequel/plugins/json_serializer.rb +15 -4
- data/lib/sequel/plugins/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/sharding.rb +11 -5
- data/lib/sequel/plugins/throw_failures.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +3 -2
- data/lib/sequel/sql.rb +4 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +82 -17
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/bin_spec.rb +1 -1
- data/spec/core/database_spec.rb +1 -1
- data/spec/core/dataset_spec.rb +0 -3
- data/spec/core/expression_filters_spec.rb +26 -7
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core_extensions_spec.rb +1 -1
- data/spec/extensions/any_not_empty_spec.rb +23 -0
- data/spec/extensions/association_multi_add_remove_spec.rb +1041 -0
- data/spec/extensions/caller_logging_spec.rb +1 -1
- data/spec/extensions/dirty_spec.rb +33 -0
- data/spec/extensions/empty_failure_backtraces_spec.rb +60 -0
- data/spec/extensions/exclude_or_null_spec.rb +15 -0
- data/spec/extensions/json_serializer_spec.rb +10 -0
- data/spec/extensions/named_timezones_spec.rb +5 -5
- data/spec/extensions/nested_attributes_spec.rb +48 -0
- data/spec/extensions/pg_array_ops_spec.rb +3 -3
- data/spec/extensions/pg_json_ops_spec.rb +67 -0
- data/spec/extensions/pg_range_spec.rb +35 -21
- data/spec/extensions/sharding_spec.rb +8 -0
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/guards_helper.rb +1 -1
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/dataset_test.rb +57 -17
- data/spec/integration/plugin_test.rb +1 -1
- data/spec/integration/schema_test.rb +9 -0
- data/spec/integration/spec_helper.rb +7 -1
- data/spec/model/plugins_spec.rb +2 -2
- data/spec/model/spec_helper.rb +1 -1
- data/spec/sequel_warning.rb +1 -0
- metadata +35 -3
data/lib/sequel/model/base.rb
CHANGED
@@ -1069,7 +1069,7 @@ module Sequel
|
|
1069
1069
|
@new = true
|
1070
1070
|
@modified = true
|
1071
1071
|
initialize_set(values)
|
1072
|
-
|
1072
|
+
_clear_changed_columns(:initialize)
|
1073
1073
|
yield self if block_given?
|
1074
1074
|
end
|
1075
1075
|
|
@@ -1626,6 +1626,13 @@ module Sequel
|
|
1626
1626
|
def _changed_columns
|
1627
1627
|
@changed_columns ||= []
|
1628
1628
|
end
|
1629
|
+
|
1630
|
+
# Clear the changed columns. Reason is the reason for clearing
|
1631
|
+
# the columns, and should be one of: :initialize, :refresh, :create
|
1632
|
+
# or :update.
|
1633
|
+
def _clear_changed_columns(_reason)
|
1634
|
+
_changed_columns.clear
|
1635
|
+
end
|
1629
1636
|
|
1630
1637
|
# Do the deletion of the object's dataset, and check that the row
|
1631
1638
|
# was actually deleted.
|
@@ -1716,7 +1723,7 @@ module Sequel
|
|
1716
1723
|
# is used for reading newly inserted values from the database
|
1717
1724
|
def _refresh(dataset)
|
1718
1725
|
_refresh_set_values(_refresh_get(dataset) || raise(NoExistingObject, "Record not found"))
|
1719
|
-
|
1726
|
+
_clear_changed_columns(:refresh)
|
1720
1727
|
end
|
1721
1728
|
|
1722
1729
|
# Get the row of column data from the database.
|
@@ -1754,7 +1761,7 @@ module Sequel
|
|
1754
1761
|
@this = nil
|
1755
1762
|
@new = false
|
1756
1763
|
@modified = false
|
1757
|
-
pk ? _save_refresh :
|
1764
|
+
pk ? _save_refresh : _clear_changed_columns(:create)
|
1758
1765
|
after_create
|
1759
1766
|
true
|
1760
1767
|
end
|
@@ -1771,7 +1778,7 @@ module Sequel
|
|
1771
1778
|
cc.clear
|
1772
1779
|
else
|
1773
1780
|
columns_updated = _save_update_all_columns_hash
|
1774
|
-
|
1781
|
+
_clear_changed_columns(:update)
|
1775
1782
|
end
|
1776
1783
|
else # update only the specified columns
|
1777
1784
|
columns = Array(columns)
|
@@ -1798,7 +1805,7 @@ module Sequel
|
|
1798
1805
|
# can be overridden to avoid the refresh.
|
1799
1806
|
def _save_refresh
|
1800
1807
|
_save_set_values(_refresh_get(this.server?(:default)) || raise(NoExistingObject, "Record not found"))
|
1801
|
-
|
1808
|
+
_clear_changed_columns(:create)
|
1802
1809
|
end
|
1803
1810
|
|
1804
1811
|
# Set values to the provided hash. Called after a create,
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The association_multi_add_remove plugin allows adding, removing and setting
|
6
|
+
# multiple associated objects in a single method call.
|
7
|
+
# By default Sequel::Model defines singular <tt>add_*</tt> and <tt>remove_*</tt>
|
8
|
+
# methods that operate on a single associated object, this adds plural forms
|
9
|
+
# that operate on multiple associated objects. Example:
|
10
|
+
#
|
11
|
+
# artist.albums # => [album1]
|
12
|
+
# artist.add_albums([album2, album3])
|
13
|
+
# artist.albums # => [album1, album2, album3]
|
14
|
+
# artist.remove_albums([album3, album1])
|
15
|
+
# artist.albums # => [album2]
|
16
|
+
# artist.albums = [album2, album3]
|
17
|
+
# artist.albums # => [album2, album3]
|
18
|
+
#
|
19
|
+
# It can handle all situations that the normal singular methods handle, but there is
|
20
|
+
# no attempt to optimize behavior, so using these methods will not improve performance.
|
21
|
+
#
|
22
|
+
# The add/remove/set methods defined by this plugin use a transaction,
|
23
|
+
# so if one add/remove/set fails and raises an exception, all adds/removes/set
|
24
|
+
# will be rolled back. If you are using database sharding and want to save
|
25
|
+
# to a specific shard, call Model#set_server to set the server for this instance,
|
26
|
+
# as the transaction will be opened on that server.
|
27
|
+
#
|
28
|
+
# You can customize the method names used for adding/removing multiple associated
|
29
|
+
# objects using the :multi_add_method and :multi_remove_method association options.
|
30
|
+
#
|
31
|
+
# Usage:
|
32
|
+
#
|
33
|
+
# # Allow adding/removing/setting multiple associated objects in a single call
|
34
|
+
# # for all model subclass instances (called before loading subclasses):
|
35
|
+
# Sequel::Model.plugin :association_multi_add_remove
|
36
|
+
#
|
37
|
+
# # Allow adding/removing/setting multiple associated objects in a single call
|
38
|
+
# # for Album instances (called before defining associations in the class):
|
39
|
+
# Album.plugin :association_multi_add_remove
|
40
|
+
module AssociationMultiAddRemove
|
41
|
+
module ClassMethods
|
42
|
+
# Define the methods use to add/remove/set multiple associated objects
|
43
|
+
# in a single method call.
|
44
|
+
def def_association_instance_methods(opts)
|
45
|
+
super
|
46
|
+
|
47
|
+
if opts[:adder]
|
48
|
+
add_method = opts[:add_method]
|
49
|
+
multi_add_method = opts[:multi_add_method] || :"add_#{opts[:name]}"
|
50
|
+
multi_add_method = nil if add_method == multi_add_method
|
51
|
+
if multi_add_method
|
52
|
+
association_module_def(multi_add_method, opts) do |objs, *args|
|
53
|
+
db.transaction(:server=>@server){objs.map{|obj| send(add_method, obj, *args)}.compact}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if opts[:remover]
|
59
|
+
remove_method = opts[:remove_method]
|
60
|
+
multi_remove_method = opts[:multi_remove_method] || :"remove_#{opts[:name]}"
|
61
|
+
multi_remove_method = nil if remove_method == multi_remove_method
|
62
|
+
if multi_remove_method
|
63
|
+
association_module_def(multi_remove_method, opts) do |objs, *args|
|
64
|
+
db.transaction(:server=>@server){objs.map{|obj| send(remove_method, obj, *args)}.compact}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if multi_add_method && multi_remove_method
|
70
|
+
association_module_def(:"#{opts[:name]}=", opts) do |objs, *args|
|
71
|
+
db.transaction(:server=>@server) do
|
72
|
+
existing_objs = send(opts.association_method)
|
73
|
+
send(multi_remove_method, (existing_objs - objs), *args)
|
74
|
+
send(multi_add_method, (objs - existing_objs), *args)
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -26,6 +26,9 @@ module Sequel
|
|
26
26
|
# * Model.with_pk!
|
27
27
|
# * Model.[] # when argument is not hash or nil
|
28
28
|
# * many_to_one association method # without dynamic callback, when primary key matches
|
29
|
+
#
|
30
|
+
# You should not use this plugin if you are using sharding and there are different
|
31
|
+
# rows for the same primary key on different shards.
|
29
32
|
#
|
30
33
|
# Usage:
|
31
34
|
#
|
@@ -65,8 +65,6 @@ module Sequel
|
|
65
65
|
# # Add CSV output capability to Album class instances
|
66
66
|
# Album.plugin :csv_serializer
|
67
67
|
module CsvSerializer
|
68
|
-
CSV = Object.const_defined?(:CSV) ? ::CSV : ::FasterCSV
|
69
|
-
|
70
68
|
# Set up the column readers to do deserialization and the column writers
|
71
69
|
# to save the value in deserialized_values
|
72
70
|
def self.configure(model, opts = OPTS)
|
@@ -75,13 +73,29 @@ module Sequel
|
|
75
73
|
end
|
76
74
|
end
|
77
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
|
+
# :nodoc:
|
86
|
+
def self.csv_call(*args, opts, &block)
|
87
|
+
CSV.send(*args, opts, &block)
|
88
|
+
end
|
89
|
+
# :nodoc:
|
90
|
+
end
|
91
|
+
|
78
92
|
module ClassMethods
|
79
93
|
# The default opts to use when serializing model objects to CSV
|
80
94
|
attr_reader :csv_serializer_opts
|
81
95
|
|
82
96
|
# Attempt to parse an array of instances from the given CSV string
|
83
97
|
def array_from_csv(csv, opts = OPTS)
|
84
|
-
|
98
|
+
CsvSerializer.csv_call(:parse, csv, process_csv_serializer_opts(opts)).map do |row|
|
85
99
|
row = row.to_hash
|
86
100
|
row.delete(nil)
|
87
101
|
new(row)
|
@@ -108,7 +122,8 @@ module Sequel
|
|
108
122
|
opts_cols = opts.delete(:columns)
|
109
123
|
opts_include = opts.delete(:include)
|
110
124
|
opts_except = opts.delete(:except)
|
111
|
-
|
125
|
+
only = opts.delete(:only)
|
126
|
+
opts[:headers] ||= Array(only || opts_cols || columns) + Array(opts_include) - Array(opts_except)
|
112
127
|
opts
|
113
128
|
end
|
114
129
|
|
@@ -130,7 +145,7 @@ module Sequel
|
|
130
145
|
# :headers :: The headers to use for the CSV line. Use nil for a header
|
131
146
|
# to specify the column should be ignored.
|
132
147
|
def from_csv(csv, opts = OPTS)
|
133
|
-
row =
|
148
|
+
row = CsvSerializer.csv_call(:parse_line, csv, model.process_csv_serializer_opts(opts)).to_hash
|
134
149
|
row.delete(nil)
|
135
150
|
set(row)
|
136
151
|
end
|
@@ -146,9 +161,10 @@ module Sequel
|
|
146
161
|
# attributes to include in the CSV output.
|
147
162
|
def to_csv(opts = OPTS)
|
148
163
|
opts = model.process_csv_serializer_opts(opts)
|
164
|
+
headers = opts[:headers]
|
149
165
|
|
150
|
-
|
151
|
-
csv <<
|
166
|
+
CsvSerializer.csv_call(:generate, model.process_csv_serializer_opts(opts)) do |csv|
|
167
|
+
csv << headers.map{|k| public_send(k)}
|
152
168
|
end
|
153
169
|
end
|
154
170
|
end
|
@@ -164,10 +180,11 @@ module Sequel
|
|
164
180
|
def to_csv(opts = OPTS)
|
165
181
|
opts = model.process_csv_serializer_opts({:columns=>columns}.merge!(opts))
|
166
182
|
items = opts.delete(:array) || self
|
183
|
+
headers = opts[:headers]
|
167
184
|
|
168
|
-
|
185
|
+
CsvSerializer.csv_call(:generate, opts) do |csv|
|
169
186
|
items.each do |object|
|
170
|
-
csv <<
|
187
|
+
csv << headers.map{|header| object.public_send(header)}
|
171
188
|
end
|
172
189
|
end
|
173
190
|
end
|
data/lib/sequel/plugins/dirty.rb
CHANGED
@@ -159,9 +159,9 @@ module Sequel
|
|
159
159
|
|
160
160
|
private
|
161
161
|
|
162
|
-
# Reset
|
163
|
-
def
|
164
|
-
reset_initial_values
|
162
|
+
# Reset initial values when clearing changed columns
|
163
|
+
def _clear_changed_columns(reason)
|
164
|
+
reset_initial_values if reason == :initialize || reason == :refresh
|
165
165
|
super
|
166
166
|
end
|
167
167
|
|
@@ -214,12 +214,6 @@ module Sequel
|
|
214
214
|
self
|
215
215
|
end
|
216
216
|
|
217
|
-
# Reset the initial values when initializing.
|
218
|
-
def initialize_set(h)
|
219
|
-
super
|
220
|
-
reset_initial_values
|
221
|
-
end
|
222
|
-
|
223
217
|
# Array holding column symbols that were not present initially. This is necessary
|
224
218
|
# to differentiate between values that were not present and values that were
|
225
219
|
# present but equal to nil.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The empty_failure_backtraces plugin uses empty backtraces when raising HookFailed and ValidationFailed
|
6
|
+
# exceptions. This can be significantly faster, and if you are using these exceptions for
|
7
|
+
# flow control, you do not need the backtraces. This plugin is about 10% faster on CRuby
|
8
|
+
# and 10-15x faster on JRuby 9.2.7.0+. This does not have an effect on JRuby <9.2.7.0.
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
#
|
12
|
+
# # Make all model subclass instances use empty backtraces for HookFailed
|
13
|
+
# # and ValidationFailed exceptions (called before loading subclasses)
|
14
|
+
# Sequel::Model.plugin :empty_failure_backtraces
|
15
|
+
#
|
16
|
+
# # Make the Album class use empty backtraces for HookFailed and ValidationFailed exceptions
|
17
|
+
# Album.plugin :empty_failure_backtraces
|
18
|
+
module EmptyFailureBacktraces
|
19
|
+
module InstanceMethods
|
20
|
+
private
|
21
|
+
|
22
|
+
# Use empty backtrace for HookFailed exceptions.
|
23
|
+
def hook_failed_error(msg)
|
24
|
+
e = super
|
25
|
+
e.set_backtrace([])
|
26
|
+
e
|
27
|
+
end
|
28
|
+
|
29
|
+
# Use empty backtrace for ValidationFailed exceptions.
|
30
|
+
def validation_failed_error
|
31
|
+
e = super
|
32
|
+
e.set_backtrace([])
|
33
|
+
e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -369,6 +369,13 @@ module Sequel
|
|
369
369
|
end
|
370
370
|
|
371
371
|
module DatasetMethods
|
372
|
+
# Store default options used when calling to_json on this dataset.
|
373
|
+
# These options take precedence over the class level options,
|
374
|
+
# and can be overridden by passing options directly to to_json.
|
375
|
+
def json_serializer_opts(opts=OPTS)
|
376
|
+
clone(:json_serializer_opts=>opts)
|
377
|
+
end
|
378
|
+
|
372
379
|
# Return a JSON string representing an array of all objects in
|
373
380
|
# this dataset. Takes the same options as the instance
|
374
381
|
# method, and passes them to every instance. Additionally,
|
@@ -386,11 +393,15 @@ module Sequel
|
|
386
393
|
# object. If set to a string, wraps the collection in
|
387
394
|
# a root object using the string as the key.
|
388
395
|
def to_json(*a)
|
389
|
-
|
390
|
-
|
396
|
+
opts = model.json_serializer_opts
|
397
|
+
|
398
|
+
if ds_opts = @opts[:json_serializer_opts]
|
399
|
+
opts = opts.merge(ds_opts)
|
400
|
+
end
|
401
|
+
|
402
|
+
if (arg = a.first).is_a?(Hash)
|
403
|
+
opts = opts.merge(arg)
|
391
404
|
a = []
|
392
|
-
else
|
393
|
-
opts = model.json_serializer_opts
|
394
405
|
end
|
395
406
|
|
396
407
|
case collection_root = opts[:root]
|
@@ -113,6 +113,10 @@ module Sequel
|
|
113
113
|
# value, the attribute hash is ignored.
|
114
114
|
# :remove :: Allow disassociation of nested records (can remove the associated
|
115
115
|
# object from the parent object, but not destroy the associated object).
|
116
|
+
# :require_modification :: Whether to require modification of nested objects when
|
117
|
+
# updating or deleting them (checking that a single row was
|
118
|
+
# updated). By default, uses the default require_modification
|
119
|
+
# setting for the nested object.
|
116
120
|
# :transform :: A proc to transform attribute hashes before they are
|
117
121
|
# passed to associated object. Takes two arguments, the parent object and
|
118
122
|
# the attribute hash. Uses the return value as the new attribute hash.
|
@@ -282,6 +286,9 @@ module Sequel
|
|
282
286
|
obj = Array(public_send(reflection[:name])).find{|x| Array(x.pk).map(&:to_s) == pk}
|
283
287
|
end
|
284
288
|
if obj
|
289
|
+
unless (require_modification = meta[:require_modification]).nil?
|
290
|
+
obj.require_modification = require_modification
|
291
|
+
end
|
285
292
|
attributes = attributes.dup.delete_if{|k,v| str_keys.include? k.to_s}
|
286
293
|
if meta[:destroy] && klass.db.send(:typecast_value_boolean, attributes.delete(:_delete) || attributes.delete('_delete'))
|
287
294
|
nested_attributes_remove(meta, obj, :destroy=>true)
|
@@ -107,12 +107,18 @@ module Sequel
|
|
107
107
|
# previous row_proc, but calls set_server on the output of that row_proc,
|
108
108
|
# ensuring that objects retrieved by a specific shard know which shard they
|
109
109
|
# are tied to.
|
110
|
-
def
|
111
|
-
|
112
|
-
if rp
|
113
|
-
|
110
|
+
def row_proc
|
111
|
+
rp = super
|
112
|
+
if rp
|
113
|
+
case server = db.pool.send(:pick_server, opts[:server])
|
114
|
+
when nil, :default, :read_only
|
115
|
+
# nothing
|
116
|
+
else
|
117
|
+
old_rp = rp
|
118
|
+
rp = proc{|r| old_rp.call(r).set_server(server)}
|
119
|
+
end
|
114
120
|
end
|
115
|
-
|
121
|
+
rp
|
116
122
|
end
|
117
123
|
end
|
118
124
|
end
|
@@ -41,7 +41,9 @@ module Sequel
|
|
41
41
|
# Typecast values using #load_typecast when the values are retrieved
|
42
42
|
# from the database.
|
43
43
|
def call(values)
|
44
|
-
super.load_typecast
|
44
|
+
o = super.load_typecast
|
45
|
+
o.send(:_clear_changed_columns, :initialize)
|
46
|
+
o
|
45
47
|
end
|
46
48
|
|
47
49
|
# Freeze typecast on load columns when freezing model class.
|
@@ -63,7 +65,6 @@ module Sequel
|
|
63
65
|
set_column_value("#{c}=", v)
|
64
66
|
end
|
65
67
|
end
|
66
|
-
_changed_columns.clear
|
67
68
|
self
|
68
69
|
end
|
69
70
|
|
data/lib/sequel/sql.rb
CHANGED
@@ -788,8 +788,10 @@ module Sequel
|
|
788
788
|
def coerce(other)
|
789
789
|
if other.is_a?(Numeric)
|
790
790
|
[SQL::NumericExpression.new(:NOOP, other), self]
|
791
|
-
|
791
|
+
elsif defined?(super)
|
792
792
|
super
|
793
|
+
else
|
794
|
+
[self, other]
|
793
795
|
end
|
794
796
|
end
|
795
797
|
|
@@ -1315,6 +1317,7 @@ module Sequel
|
|
1315
1317
|
CURRENT_DATE = Constant.new(:CURRENT_DATE)
|
1316
1318
|
CURRENT_TIME = Constant.new(:CURRENT_TIME)
|
1317
1319
|
CURRENT_TIMESTAMP = Constant.new(:CURRENT_TIMESTAMP)
|
1320
|
+
DEFAULT = Constant.new(:DEFAULT)
|
1318
1321
|
SQLTRUE = TRUE = BooleanConstant.new(true)
|
1319
1322
|
SQLFALSE = FALSE = BooleanConstant.new(false)
|
1320
1323
|
NULL = BooleanConstant.new(nil)
|
data/lib/sequel/version.rb
CHANGED
@@ -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 =
|
9
|
+
MINOR = 29
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
@@ -70,7 +70,7 @@ describe "PostgreSQL", '#create_table' do
|
|
70
70
|
|
71
71
|
it "should create an unlogged table" do
|
72
72
|
@db.create_table(:unlogged_dolls, :unlogged => true){text :name}
|
73
|
-
end
|
73
|
+
end if DB.server_version >= 90100
|
74
74
|
|
75
75
|
it "should create a table inheriting from another table" do
|
76
76
|
@db.create_table(:unlogged_dolls){text :name}
|
@@ -368,6 +368,14 @@ describe "PostgreSQL", 'INSERT ON CONFLICT' do
|
|
368
368
|
@ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>6}, :update_where=>{Sequel[:ic_test][:b]=>4}).insert(1, 3, 4).must_be_nil
|
369
369
|
@ds.all.must_equal [{:a=>1, :b=>5, :c=>5, :c_is_unique=>false}]
|
370
370
|
end
|
371
|
+
|
372
|
+
it "Dataset#insert_conflict should support table aliases" do
|
373
|
+
@ds = @db[Sequel[:ic_test].as(:foo)]
|
374
|
+
@ds.insert(1, 2, 5)
|
375
|
+
proc{@ds.insert(1, 3, 4)}.must_raise Sequel::UniqueConstraintViolation
|
376
|
+
@ds.insert_conflict(:target=>:a, :update=>{:b=>Sequel[:foo][:c] + Sequel[:excluded][:c]}).insert(1, 7, 10)
|
377
|
+
@ds.all.must_equal [{:a=>1, :b=>15, :c=>5, :c_is_unique=>false}]
|
378
|
+
end
|
371
379
|
end if DB.server_version >= 90500
|
372
380
|
|
373
381
|
describe "A PostgreSQL database" do
|
@@ -607,10 +615,6 @@ describe "A PostgreSQL dataset" do
|
|
607
615
|
@d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{mode.function.within_group(:a)}.must_equal 1
|
608
616
|
end if DB.server_version >= 90400
|
609
617
|
|
610
|
-
it "should support filtered aggregate functions" do
|
611
|
-
@d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{count(:a).filter(:a=>1)}.must_equal 2
|
612
|
-
end if DB.server_version >= 90400
|
613
|
-
|
614
618
|
it "should support functions with ordinality" do
|
615
619
|
@d.from{generate_series(1,10,3).with_ordinality}.select_map([:generate_series, :ordinality]).must_equal [[1, 1], [4, 2], [7, 3], [10, 4]]
|
616
620
|
end if DB.server_version >= 90400
|
@@ -1264,7 +1268,7 @@ describe "A PostgreSQL database" do
|
|
1264
1268
|
end
|
1265
1269
|
|
1266
1270
|
it "should support indexes with index type" do
|
1267
|
-
@db.create_table(:posts){
|
1271
|
+
@db.create_table(:posts){box :geom; index :geom, :type => 'gist'}
|
1268
1272
|
end
|
1269
1273
|
|
1270
1274
|
it "should support unique indexes with index type" do
|
@@ -2696,6 +2700,8 @@ describe 'PostgreSQL array handling' do
|
|
2696
2700
|
if @db.server_version >= 90000
|
2697
2701
|
@ds.get(Sequel.pg_array(:i5).join).must_equal '15'
|
2698
2702
|
@ds.get(Sequel.pg_array(:i5).join(':')).must_equal '1:5'
|
2703
|
+
end
|
2704
|
+
if @db.server_version >= 90100
|
2699
2705
|
@ds.get(Sequel.pg_array(:i5).join(':', '*')).must_equal '1:*:5'
|
2700
2706
|
end
|
2701
2707
|
if @db.server_version >= 90300
|
@@ -3341,6 +3347,65 @@ describe 'PostgreSQL json type' do
|
|
3341
3347
|
@db.from(jo.each_text).select_order_map(:key).must_equal %w'a b'
|
3342
3348
|
@db.from(jo.each_text).order(:key).where(:key=>'b').get(:value).gsub(' ', '').must_match(/\{"d":\{"e":3\},"c":2\}|\{"c":2,"d":\{"e":3\}\}/)
|
3343
3349
|
|
3350
|
+
if DB.server_version >= 120000 && json_type == :jsonb
|
3351
|
+
@db.get(jo.path_exists('$.b.d.e')).must_equal true
|
3352
|
+
@db.get(jo.path_exists('$.b.d.f')).must_equal false
|
3353
|
+
|
3354
|
+
@db.get(jo.path_exists!('$.b.d.e')).must_equal true
|
3355
|
+
@db.get(jo.path_exists!('$.b.d.f')).must_equal false
|
3356
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal true
|
3357
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', '{"x":4}')).must_equal false
|
3358
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', x: 2)).must_equal true
|
3359
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', x: 4)).must_equal false
|
3360
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal true
|
3361
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_equal false
|
3362
|
+
|
3363
|
+
@db.get(jo.path_match('$.b.d.e')).must_be_nil
|
3364
|
+
@db.get(jo.path_match('$.b.d.f')).must_be_nil
|
3365
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>true}}).op.path_match('$.b.d.e')).must_equal true
|
3366
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>false}}).op.path_match('$.b.d.e')).must_equal false
|
3367
|
+
|
3368
|
+
proc{@db.get(jo.path_match!('$.b.d.e'))}.must_raise(Sequel::DatabaseError)
|
3369
|
+
proc{@db.get(jo.path_match!('$.b.d.f'))}.must_raise(Sequel::DatabaseError)
|
3370
|
+
@db.get(jo.path_match!('$.b.d.e', {}, true)).must_be_nil
|
3371
|
+
@db.get(jo.path_match!('$.b.d.f', {}, true)).must_be_nil
|
3372
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>true}}).op.path_match!('$.b.d.e')).must_equal true
|
3373
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>false}}).op.path_match!('$.b.d.e')).must_equal false
|
3374
|
+
@db.get(jo.path_match!('$.b.d.e > $x', '{"x":2}')).must_equal true
|
3375
|
+
@db.get(jo.path_match!('$.b.d.e > $x', '{"x":4}')).must_equal false
|
3376
|
+
@db.get(jo.path_match!('$.b.d.e > $x', x: 2)).must_equal true
|
3377
|
+
@db.get(jo.path_match!('$.b.d.e > $x', x: 4)).must_equal false
|
3378
|
+
@db.get(jo.path_match!('$.b.d.e > $x', {x: 2}, false)).must_equal true
|
3379
|
+
@db.get(jo.path_match!('$.b.d.e > $x', {x: 4}, true)).must_equal false
|
3380
|
+
|
3381
|
+
@db.get(jo.path_query_first('$.b.d.e')).must_equal 3
|
3382
|
+
@db.get(jo.path_query_first('$.b.d.f')).must_be_nil
|
3383
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal 3
|
3384
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', '{"x":4}')).must_be_nil
|
3385
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', x: 2)).must_equal 3
|
3386
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', x: 4)).must_be_nil
|
3387
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal 3
|
3388
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_be_nil
|
3389
|
+
|
3390
|
+
@db.get(jo.path_query_array('$.b.d.e')).must_equal [3]
|
3391
|
+
@db.get(jo.path_query_array('$.b.d.f')).must_equal []
|
3392
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal [3]
|
3393
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', '{"x":4}')).must_equal []
|
3394
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', x: 2)).must_equal [3]
|
3395
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', x: 4)).must_equal []
|
3396
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal [3]
|
3397
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_equal []
|
3398
|
+
|
3399
|
+
@db.from(jo.path_query('$.b.d.e').as(:a, [:b])).get(:b).must_equal 3
|
3400
|
+
@db.from(jo.path_query('$.b.d.f').as(:a, [:b])).get(:b).must_be_nil
|
3401
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', '{"x":2}').as(:a, [:b])).get(:b).must_equal 3
|
3402
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', '{"x":4}').as(:a, [:b])).get(:b).must_be_nil
|
3403
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', x: 2).as(:a, [:b])).get(:b).must_equal 3
|
3404
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', x: 4).as(:a, [:b])).get(:b).must_be_nil
|
3405
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', {x: 2}, true).as(:a, [:b])).get(:b).must_equal 3
|
3406
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', {x: 4}, false).as(:a, [:b])).get(:b).must_be_nil
|
3407
|
+
end
|
3408
|
+
|
3344
3409
|
Sequel.extension :pg_row_ops
|
3345
3410
|
@db.create_table!(:items) do
|
3346
3411
|
Integer :a
|
@@ -3620,19 +3685,19 @@ describe 'PostgreSQL range types' do
|
|
3620
3685
|
end if uses_pg_or_jdbc
|
3621
3686
|
|
3622
3687
|
it 'handle endless ranges' do
|
3623
|
-
@db.get(Sequel.cast(eval('1...'), :int4range)).must_be :==, eval('1...')
|
3624
|
-
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('2...')
|
3625
|
-
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('1..')
|
3626
|
-
@db.get(Sequel.cast(eval('2...'), :int4range)).must_be :==, eval('2...')
|
3627
|
-
@db.get(Sequel.cast(eval('2...'), :int4range)).wont_be :==, eval('2..')
|
3628
|
-
@db.get(Sequel.cast(eval('2...'), :int4range)).wont_be :==, eval('1...')
|
3688
|
+
@db.get(Sequel.cast(eval('(1...)'), :int4range)).must_be :==, eval('(1...)')
|
3689
|
+
@db.get(Sequel.cast(eval('(1...)'), :int4range)).wont_be :==, eval('(2...)')
|
3690
|
+
@db.get(Sequel.cast(eval('(1...)'), :int4range)).wont_be :==, eval('(1..)')
|
3691
|
+
@db.get(Sequel.cast(eval('(2...)'), :int4range)).must_be :==, eval('(2...)')
|
3692
|
+
@db.get(Sequel.cast(eval('(2...)'), :int4range)).wont_be :==, eval('(2..)')
|
3693
|
+
@db.get(Sequel.cast(eval('(2...)'), :int4range)).wont_be :==, eval('(1...)')
|
3629
3694
|
end if RUBY_VERSION >= '2.6'
|
3630
3695
|
|
3631
3696
|
it 'handle startless ranges' do
|
3632
|
-
@db.get(Sequel.cast(eval('...1'), :int4range)).must_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3633
|
-
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 2, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3634
|
-
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_end=>true, :db_type=>"int4range")
|
3635
|
-
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :db_type=>"int4range")
|
3697
|
+
@db.get(Sequel.cast(eval('(...1)'), :int4range)).must_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3698
|
+
@db.get(Sequel.cast(eval('(...1)'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 2, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3699
|
+
@db.get(Sequel.cast(eval('(...1)'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_end=>true, :db_type=>"int4range")
|
3700
|
+
@db.get(Sequel.cast(eval('(...1)'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :db_type=>"int4range")
|
3636
3701
|
end if RUBY_VERSION >= '2.7'
|
3637
3702
|
|
3638
3703
|
it 'handle startless ranges' do
|
@@ -4408,4 +4473,4 @@ describe "pg_auto_constraint_validations plugin" do
|
|
4408
4473
|
File.delete(cache_file) if File.file?(cache_file)
|
4409
4474
|
end
|
4410
4475
|
end
|
4411
|
-
end if DB.respond_to?(:error_info)
|
4476
|
+
end if DB.respond_to?(:error_info) && DB.server_version >= 90300
|