sequel 5.24.0 → 5.29.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|