sequel 5.23.0 → 5.28.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 +62 -0
- data/README.rdoc +1 -1
- data/doc/cheat_sheet.rdoc +1 -0
- data/doc/postgresql.rdoc +2 -2
- data/doc/release_notes/5.24.0.txt +56 -0
- 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/testing.rdoc +11 -6
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -13
- data/lib/sequel/adapters/mysql2.rb +0 -1
- data/lib/sequel/adapters/shared/mssql.rb +4 -2
- data/lib/sequel/adapters/shared/postgres.rb +30 -7
- data/lib/sequel/adapters/shared/sqlite.rb +7 -2
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/schema_generator.rb +11 -2
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/dataset/features.rb +6 -0
- 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/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +89 -30
- data/lib/sequel/plugins/sharding.rb +11 -5
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- 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 +145 -17
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/bin_spec.rb +1 -1
- data/spec/core/database_spec.rb +20 -1
- data/spec/core/expression_filters_spec.rb +26 -7
- data/spec/core/schema_spec.rb +18 -0
- 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/dirty_spec.rb +33 -0
- data/spec/extensions/exclude_or_null_spec.rb +15 -0
- data/spec/extensions/insert_conflict_spec.rb +26 -0
- data/spec/extensions/nested_attributes_spec.rb +48 -0
- data/spec/extensions/pg_array_ops_spec.rb +3 -3
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +37 -0
- 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/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/guards_helper.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/spec_helper.rb +1 -1
- metadata +35 -3
@@ -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
|
@@ -211,18 +211,23 @@ module Sequel
|
|
211
211
|
# Reload the cache for this model by retrieving all of the instances in the dataset
|
212
212
|
# freezing them, and populating the cached array and hash.
|
213
213
|
def load_cache
|
214
|
-
|
214
|
+
@all = load_static_cache_rows
|
215
215
|
h = {}
|
216
|
-
|
216
|
+
@all.each do |o|
|
217
217
|
o.errors.freeze
|
218
218
|
h[o.pk.freeze] = o.freeze
|
219
219
|
end
|
220
|
-
@all = a.freeze
|
221
220
|
@cache = h.freeze
|
222
221
|
end
|
223
222
|
|
224
223
|
private
|
225
224
|
|
225
|
+
# Load the static cache rows from the database.
|
226
|
+
def load_static_cache_rows
|
227
|
+
ret = super if defined?(super)
|
228
|
+
ret || dataset.all.freeze
|
229
|
+
end
|
230
|
+
|
226
231
|
# Return the frozen object with the given pk, or nil if no such object exists
|
227
232
|
# in the cache, without issuing a database query.
|
228
233
|
def primary_key_lookup(pk)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The static_cache_cache plugin allows for caching the row content for subclasses
|
6
|
+
# that use the static cache plugin (or just the current class). Using this plugin
|
7
|
+
# can avoid the need to query the database every time loading the plugin into a
|
8
|
+
# model, which can save time when you have a lot of models using the static_cache
|
9
|
+
# plugin.
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
#
|
13
|
+
# # Make all model subclasses that use the static_cache plugin use
|
14
|
+
# # the cached values in the given file
|
15
|
+
# Sequel::Model.plugin :static_cache_cache, "static_cache.cache"
|
16
|
+
#
|
17
|
+
# # Make the AlbumType model the cached values in the given file,
|
18
|
+
# # should be loaded before the static_cache plugin
|
19
|
+
# AlbumType.plugin :static_cache_cache, "static_cache.cache"
|
20
|
+
module StaticCacheCache
|
21
|
+
def self.configure(model, file)
|
22
|
+
model.instance_variable_set(:@static_cache_cache_file, file)
|
23
|
+
model.instance_variable_set(:@static_cache_cache, File.exist?(file) ? Marshal.load(File.read(file)) : {})
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
# Dump the in-memory cached rows to the cache file.
|
28
|
+
def dump_static_cache_cache
|
29
|
+
File.open(@static_cache_cache_file, 'wb'){|f| f.write(Marshal.dump(@static_cache_cache))}
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
Plugins.inherited_instance_variables(self, :@static_cache_cache_file=>nil, :@static_cache_cache=>nil)
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Load the rows for the model from the cache if available.
|
38
|
+
# If not available, load the rows from the database, and
|
39
|
+
# then update the cache with the raw rows.
|
40
|
+
def load_static_cache_rows
|
41
|
+
if rows = Sequel.synchronize{@static_cache_cache[name]}
|
42
|
+
rows.map{|row| call(row)}.freeze
|
43
|
+
else
|
44
|
+
rows = dataset.all.freeze
|
45
|
+
raw_rows = rows.map(&:values)
|
46
|
+
Sequel.synchronize{@static_cache_cache[name] = raw_rows}
|
47
|
+
rows
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
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 = 28
|
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}
|
@@ -227,6 +227,24 @@ describe "PostgreSQL", '#create_table' do
|
|
227
227
|
@db.convert_serial_to_identity(:tmp_dolls, :column=>:id)
|
228
228
|
end if DB.server_version >= 100002 && DB.get{current_setting('is_superuser')} == 'on'
|
229
229
|
|
230
|
+
it "should support creating generated columns" do
|
231
|
+
@db.create_table(:tmp_dolls){Integer :a; Integer :b; Integer :c, :generated_always_as=>Sequel[:a] * 2 + :b + 1}
|
232
|
+
@db[:tmp_dolls].insert(:a=>100, :b=>10)
|
233
|
+
@db[:tmp_dolls].select_order_map([:a, :b, :c]).must_equal [[100, 10, 211]]
|
234
|
+
end if DB.server_version >= 120000
|
235
|
+
|
236
|
+
it "should support deferred primary key and unique constraints on columns" do
|
237
|
+
@db.create_table(:tmp_dolls){primary_key :id, :primary_key_deferrable=>true; Integer :i, :unique=>true, :unique_deferrable=>true}
|
238
|
+
@db[:tmp_dolls].insert(:i=>10)
|
239
|
+
DB.transaction do
|
240
|
+
@db[:tmp_dolls].insert(:id=>1, :i=>1)
|
241
|
+
@db[:tmp_dolls].insert(:id=>10, :i=>10)
|
242
|
+
@db[:tmp_dolls].where(:i=>1).update(:id=>2)
|
243
|
+
@db[:tmp_dolls].where(:id=>10).update(:i=>2)
|
244
|
+
end
|
245
|
+
@db[:tmp_dolls].select_order_map([:id, :i]).must_equal [[1, 10], [2, 1], [10, 2]]
|
246
|
+
end if DB.server_version >= 90000
|
247
|
+
|
230
248
|
it "should support pg_loose_count extension" do
|
231
249
|
@db.extension :pg_loose_count
|
232
250
|
@db.create_table(:tmp_dolls){text :name}
|
@@ -350,6 +368,14 @@ describe "PostgreSQL", 'INSERT ON CONFLICT' do
|
|
350
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
|
351
369
|
@ds.all.must_equal [{:a=>1, :b=>5, :c=>5, :c_is_unique=>false}]
|
352
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
|
353
379
|
end if DB.server_version >= 90500
|
354
380
|
|
355
381
|
describe "A PostgreSQL database" do
|
@@ -589,10 +615,6 @@ describe "A PostgreSQL dataset" do
|
|
589
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
|
590
616
|
end if DB.server_version >= 90400
|
591
617
|
|
592
|
-
it "should support filtered aggregate functions" do
|
593
|
-
@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
|
594
|
-
end if DB.server_version >= 90400
|
595
|
-
|
596
618
|
it "should support functions with ordinality" do
|
597
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]]
|
598
620
|
end if DB.server_version >= 90400
|
@@ -1246,7 +1268,7 @@ describe "A PostgreSQL database" do
|
|
1246
1268
|
end
|
1247
1269
|
|
1248
1270
|
it "should support indexes with index type" do
|
1249
|
-
@db.create_table(:posts){
|
1271
|
+
@db.create_table(:posts){box :geom; index :geom, :type => 'gist'}
|
1250
1272
|
end
|
1251
1273
|
|
1252
1274
|
it "should support unique indexes with index type" do
|
@@ -2678,6 +2700,8 @@ describe 'PostgreSQL array handling' do
|
|
2678
2700
|
if @db.server_version >= 90000
|
2679
2701
|
@ds.get(Sequel.pg_array(:i5).join).must_equal '15'
|
2680
2702
|
@ds.get(Sequel.pg_array(:i5).join(':')).must_equal '1:5'
|
2703
|
+
end
|
2704
|
+
if @db.server_version >= 90100
|
2681
2705
|
@ds.get(Sequel.pg_array(:i5).join(':', '*')).must_equal '1:*:5'
|
2682
2706
|
end
|
2683
2707
|
if @db.server_version >= 90300
|
@@ -3323,6 +3347,65 @@ describe 'PostgreSQL json type' do
|
|
3323
3347
|
@db.from(jo.each_text).select_order_map(:key).must_equal %w'a b'
|
3324
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\}\}/)
|
3325
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
|
+
|
3326
3409
|
Sequel.extension :pg_row_ops
|
3327
3410
|
@db.create_table!(:items) do
|
3328
3411
|
Integer :a
|
@@ -3602,19 +3685,19 @@ describe 'PostgreSQL range types' do
|
|
3602
3685
|
end if uses_pg_or_jdbc
|
3603
3686
|
|
3604
3687
|
it 'handle endless ranges' do
|
3605
|
-
@db.get(Sequel.cast(eval('1...'), :int4range)).must_be :==, eval('1...')
|
3606
|
-
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('2...')
|
3607
|
-
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('1..')
|
3608
|
-
@db.get(Sequel.cast(eval('2...'), :int4range)).must_be :==, eval('2...')
|
3609
|
-
@db.get(Sequel.cast(eval('2...'), :int4range)).wont_be :==, eval('2..')
|
3610
|
-
@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...)')
|
3611
3694
|
end if RUBY_VERSION >= '2.6'
|
3612
3695
|
|
3613
3696
|
it 'handle startless ranges' do
|
3614
|
-
@db.get(Sequel.cast(eval('...1'), :int4range)).must_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3615
|
-
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 2, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3616
|
-
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_end=>true, :db_type=>"int4range")
|
3617
|
-
@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")
|
3618
3701
|
end if RUBY_VERSION >= '2.7'
|
3619
3702
|
|
3620
3703
|
it 'handle startless ranges' do
|
@@ -4345,4 +4428,49 @@ describe "pg_auto_constraint_validations plugin" do
|
|
4345
4428
|
proc{o.save}.must_raise Sequel::ValidationFailed
|
4346
4429
|
o.errors.must_equal(:i=>['is invalid'], :id=>['is invalid'])
|
4347
4430
|
end
|
4348
|
-
|
4431
|
+
|
4432
|
+
it "should handle dumping cached metadata and loading metadata from cache" do
|
4433
|
+
cache_file = "spec/files/pgacv-#{$$}.cache"
|
4434
|
+
begin
|
4435
|
+
c = Class.new(Sequel::Model)
|
4436
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
4437
|
+
c1 = Class.new(c)
|
4438
|
+
def c1.name; 'Foo' end
|
4439
|
+
c1.dataset = DB[:test1]
|
4440
|
+
c2 = Class.new(c)
|
4441
|
+
def c2.name; 'Bar' end
|
4442
|
+
c2.dataset = DB[:test2]
|
4443
|
+
c1.unrestrict_primary_key
|
4444
|
+
c2.unrestrict_primary_key
|
4445
|
+
|
4446
|
+
o = c1.new(:id=>5, :i=>12)
|
4447
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4448
|
+
o.errors.must_equal(:i=>['is invalid'])
|
4449
|
+
o = c2.new(:test2_id=>4, :test1_id=>2)
|
4450
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4451
|
+
o.errors.must_equal(:test1_id=>['is invalid'])
|
4452
|
+
|
4453
|
+
c.dump_pg_auto_constraint_validations_cache
|
4454
|
+
|
4455
|
+
c = Class.new(Sequel::Model)
|
4456
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
4457
|
+
c1 = Class.new(c)
|
4458
|
+
def c1.name; 'Foo' end
|
4459
|
+
c1.dataset = DB[:test1]
|
4460
|
+
c2 = Class.new(c)
|
4461
|
+
def c2.name; 'Bar' end
|
4462
|
+
c2.dataset = DB[:test2]
|
4463
|
+
c1.unrestrict_primary_key
|
4464
|
+
c2.unrestrict_primary_key
|
4465
|
+
|
4466
|
+
o = c1.new(:id=>5, :i=>12)
|
4467
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4468
|
+
o.errors.must_equal(:i=>['is invalid'])
|
4469
|
+
o = c2.new(:test2_id=>4, :test1_id=>2)
|
4470
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4471
|
+
o.errors.must_equal(:test1_id=>['is invalid'])
|
4472
|
+
ensure
|
4473
|
+
File.delete(cache_file) if File.file?(cache_file)
|
4474
|
+
end
|
4475
|
+
end
|
4476
|
+
end if DB.respond_to?(:error_info) && DB.server_version >= 90300
|
@@ -623,7 +623,7 @@ describe "SQLite", 'INSERT ON CONFLICT' do
|
|
623
623
|
@ds.insert_conflict(:target=>:a).insert(1, 3, 4, false)
|
624
624
|
@ds.insert_conflict(:target=>:c, :conflict_where=>:c_is_unique).insert(11, 12, 3, true)
|
625
625
|
@ds.all.must_equal [{:a=>1, :b=>2, :c=>3, :c_is_unique=>false}, {:a=>10, :b=>11, :c=>3, :c_is_unique=>true}]
|
626
|
-
end
|
626
|
+
end unless DB.adapter_scheme == :amalgalite
|
627
627
|
|
628
628
|
it "Dataset#insert_ignore and insert_conflict should work with multi_insert/import" do
|
629
629
|
@ds.insert(1, 2, 3, false)
|
data/spec/bin_spec.rb
CHANGED
@@ -25,7 +25,7 @@ DB2 = Sequel.connect("#{CONN_PREFIX}#{BIN_SPEC_DB2}", :test=>false)
|
|
25
25
|
|
26
26
|
ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
|
27
27
|
gem 'minitest'
|
28
|
-
require 'minitest/autorun'
|
28
|
+
require 'minitest/global_expectations/autorun'
|
29
29
|
|
30
30
|
describe "bin/sequel" do
|
31
31
|
def bin(opts={})
|
data/spec/core/database_spec.rb
CHANGED
@@ -264,6 +264,25 @@ describe "Database#log_connection_yield" do
|
|
264
264
|
@o.logs.first.first.must_equal :info
|
265
265
|
@o.logs.first.last.must_match(/\A\(\d\.\d{6}s\) blah; \[1, 2\]\z/)
|
266
266
|
end
|
267
|
+
|
268
|
+
it "should log without a logger defined by forcing skip_logging? to return false" do
|
269
|
+
@db.logger = nil
|
270
|
+
@db.extend(Module.new do
|
271
|
+
def skip_logging?
|
272
|
+
false
|
273
|
+
end
|
274
|
+
|
275
|
+
def log_duration(*)
|
276
|
+
self.did_log = true
|
277
|
+
end
|
278
|
+
|
279
|
+
attr_accessor :did_log
|
280
|
+
end)
|
281
|
+
|
282
|
+
@db.log_connection_yield('some sql', @conn) {}
|
283
|
+
|
284
|
+
@db.did_log.must_equal true
|
285
|
+
end
|
267
286
|
end
|
268
287
|
|
269
288
|
describe "Database#uri" do
|
@@ -2448,7 +2467,7 @@ describe "Database#typecast_value" do
|
|
2448
2467
|
@db.typecast_value(:date, 'a')
|
2449
2468
|
true.must_equal false
|
2450
2469
|
rescue Sequel::InvalidValue => e
|
2451
|
-
e.inspect.
|
2470
|
+
e.inspect.must_match(/\A#\<Sequel::InvalidValue: (Argument|Date::)Error: invalid date\>\z/)
|
2452
2471
|
end
|
2453
2472
|
end
|
2454
2473
|
end
|
@@ -214,8 +214,13 @@ describe "Blockless Ruby Filters" do
|
|
214
214
|
@d.lit(1 + Sequel.lit('?', :x)).must_equal '(1 + x)'
|
215
215
|
end
|
216
216
|
|
217
|
-
it "should
|
218
|
-
|
217
|
+
it "should not break Date/DateTime equality" do
|
218
|
+
(Date.today == Sequel.expr(:x)).must_equal false
|
219
|
+
(DateTime.now == Sequel.expr(:x)).must_equal false
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should have coerce return array if called on a non-numeric" do
|
223
|
+
Sequel.expr(:x).coerce(:a).must_equal [Sequel.expr(:x), :a]
|
219
224
|
end
|
220
225
|
|
221
226
|
it "should support AND conditions via &" do
|
@@ -348,6 +353,11 @@ describe "Blockless Ruby Filters" do
|
|
348
353
|
@d.l({:x => Sequel::FALSE}).must_equal '(x IS FALSE)'
|
349
354
|
@d.l({:x => Sequel::SQLTRUE}).must_equal '(x IS TRUE)'
|
350
355
|
@d.l({:x => Sequel::SQLFALSE}).must_equal '(x IS FALSE)'
|
356
|
+
|
357
|
+
@d.l({:x => Sequel::CURRENT_DATE}).must_equal '(x = CURRENT_DATE)'
|
358
|
+
@d.l({:x => Sequel::CURRENT_TIME}).must_equal '(x = CURRENT_TIME)'
|
359
|
+
@d.l({:x => Sequel::CURRENT_TIMESTAMP}).must_equal '(x = CURRENT_TIMESTAMP)'
|
360
|
+
@d.l({:x => Sequel::DEFAULT}).must_equal '(x = DEFAULT)'
|
351
361
|
end
|
352
362
|
|
353
363
|
it "should support negation of SQL::Constants" do
|
@@ -527,21 +537,21 @@ describe "Blockless Ruby Filters" do
|
|
527
537
|
end
|
528
538
|
|
529
539
|
it "should handle endless ranges" do
|
530
|
-
endless = eval('1..')
|
540
|
+
endless = eval('(1..)')
|
531
541
|
@d.l{x =~ endless}.must_equal '(x >= 1)'
|
532
542
|
@d.l(:x => endless).must_equal '(x >= 1)'
|
533
543
|
|
534
|
-
endless = eval('1...')
|
544
|
+
endless = eval('(1...)')
|
535
545
|
@d.l{x =~ endless}.must_equal '(x >= 1)'
|
536
546
|
@d.l(:x => endless).must_equal '(x >= 1)'
|
537
547
|
end if RUBY_VERSION >= '2.6'
|
538
548
|
|
539
549
|
it "should handle startless ranges" do
|
540
|
-
endless = eval('..1')
|
550
|
+
endless = eval('(..1)')
|
541
551
|
@d.l{x =~ endless}.must_equal '(x <= 1)'
|
542
552
|
@d.l(:x => endless).must_equal '(x <= 1)'
|
543
553
|
|
544
|
-
endless = eval('...1')
|
554
|
+
endless = eval('(...1)')
|
545
555
|
@d.l{x =~ endless}.must_equal '(x < 1)'
|
546
556
|
@d.l(:x => endless).must_equal '(x < 1)'
|
547
557
|
end if RUBY_VERSION >= '2.7'
|
@@ -736,14 +746,23 @@ describe Sequel::SQL::VirtualRow do
|
|
736
746
|
@d.l{mode.function.within_group(:a, :b)}.must_equal 'mode() WITHIN GROUP (ORDER BY "a", "b")'
|
737
747
|
end
|
738
748
|
|
749
|
+
it "should handle emualted filtered aggregate function calls" do
|
750
|
+
@d.l{count.function.*.filter(Sequel.&(:a, :b))}.must_equal 'count((CASE WHEN ("a" AND "b") THEN 1 ELSE NULL END))'
|
751
|
+
@d.l{count.function.*.filter(:a=>1)}.must_equal 'count((CASE WHEN ("a" = 1) THEN 1 ELSE NULL END))'
|
752
|
+
@d.l{count(:a).filter{b > 1}}.must_equal 'count((CASE WHEN ("b" > 1) THEN "a" ELSE NULL END))'
|
753
|
+
@d.l{count(:a).filter(:a=>1){b > 1}}.must_equal 'count((CASE WHEN (("a" = 1) AND ("b" > 1)) THEN "a" ELSE NULL END))'
|
754
|
+
end
|
755
|
+
|
739
756
|
it "should handle filtered aggregate function calls" do
|
757
|
+
@d = @d.with_extend{def supports_filtered_aggregates?; true end}
|
740
758
|
@d.l{count.function.*.filter(Sequel.&(:a, :b))}.must_equal 'count(*) FILTER (WHERE ("a" AND "b"))'
|
741
759
|
@d.l{count.function.*.filter(:a=>1)}.must_equal 'count(*) FILTER (WHERE ("a" = 1))'
|
742
760
|
@d.l{count.function.*.filter{b > 1}}.must_equal 'count(*) FILTER (WHERE ("b" > 1))'
|
743
761
|
@d.l{count.function.*.filter(:a=>1){b > 1}}.must_equal 'count(*) FILTER (WHERE (("a" = 1) AND ("b" > 1)))'
|
744
762
|
end
|
745
763
|
|
746
|
-
it "should handle
|
764
|
+
it "should handle filtered ordered-set and hypothetical-set function calls" do
|
765
|
+
@d = @d.with_extend{def supports_filtered_aggregates?; true end}
|
747
766
|
@d.l{mode.function.within_group(:a).filter(:a=>1)}.must_equal 'mode() WITHIN GROUP (ORDER BY "a") FILTER (WHERE ("a" = 1))'
|
748
767
|
end
|
749
768
|
|