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
@@ -162,6 +162,39 @@ describe "Sequel::Plugins::Dirty" do
|
|
162
162
|
@o.save
|
163
163
|
@o.column_changes.must_equal({})
|
164
164
|
end
|
165
|
+
|
166
|
+
it "should work with the typecast_on_load plugin" do
|
167
|
+
@c.instance_variable_set(:@db_schema, :initial=>{:type=>:integer})
|
168
|
+
@c.plugin :typecast_on_load, :initial
|
169
|
+
|
170
|
+
@o = @c.call(:initial=>'1')
|
171
|
+
@o.column_changes.must_equal({})
|
172
|
+
@o.save
|
173
|
+
@o.previous_changes.must_equal({})
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should have column_changes work with the typecast_on_load in after hooks" do
|
177
|
+
@c.instance_variable_set(:@db_schema, :initial=>{:type=>:integer})
|
178
|
+
@c.plugin :typecast_on_load, :initial
|
179
|
+
|
180
|
+
@o = @c.new
|
181
|
+
@o.initial = 1
|
182
|
+
@o.column_changes.must_equal({:initial=>[nil, 1]})
|
183
|
+
column_changes_in_after_save = nil
|
184
|
+
@o.define_singleton_method(:after_save) do
|
185
|
+
column_changes_in_after_save = column_changes
|
186
|
+
super()
|
187
|
+
end
|
188
|
+
@db.fetch = {:initial=>1}
|
189
|
+
@o.save
|
190
|
+
column_changes_in_after_save.must_equal({:initial=>[nil, 1]})
|
191
|
+
|
192
|
+
@o.initial = 2
|
193
|
+
@o.column_changes.must_equal({:initial=>[1, 2]})
|
194
|
+
@o.save
|
195
|
+
column_changes_in_after_save.must_equal({:initial=>[1, 2]})
|
196
|
+
@o.previous_changes.must_equal({:initial=>[1, 2]})
|
197
|
+
end
|
165
198
|
end
|
166
199
|
|
167
200
|
describe "with existing instance" do
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe "exclude_or_null extension" do
|
4
|
+
before do
|
5
|
+
@ds = Sequel.mock[:t].extension(:exclude_or_null)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should add condition where a is false or NULL" do
|
9
|
+
@ds.exclude_or_null(:a).sql.must_equal "SELECT * FROM t WHERE NOT coalesce(a, 'f')"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not effect normal exclude" do
|
13
|
+
@ds.exclude(:a).sql.must_equal "SELECT * FROM t WHERE NOT a"
|
14
|
+
end
|
15
|
+
end
|
@@ -74,4 +74,30 @@ describe "insert_conflict plugin" do
|
|
74
74
|
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT (s) DO UPDATE SET o = excluded.o",
|
75
75
|
"SELECT * FROM t WHERE (id = 2) LIMIT 1"]
|
76
76
|
end
|
77
|
+
|
78
|
+
it "should work if the prepared_statements plugin is loaded before" do
|
79
|
+
db = Sequel.mock(:host=>'sqlite', :fetch=>{:id=>1, :s=>2}, :autoid=>1, :numrows=>1)
|
80
|
+
db.extend_datasets{def quote_identifiers?; false end}
|
81
|
+
model = Class.new(Sequel::Model)
|
82
|
+
model.dataset = db[:t]
|
83
|
+
model.columns :id, :s
|
84
|
+
model.plugin :prepared_statements
|
85
|
+
model.plugin :insert_conflict
|
86
|
+
db.sqls
|
87
|
+
model.create(:s=>'a').update(:s=>'b')
|
88
|
+
db.sqls.must_equal ["INSERT INTO t (s) VALUES ('a')", "SELECT * FROM t WHERE (id = 1) LIMIT 1", "UPDATE t SET s = 'b' WHERE (id = 1)"]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should work if the prepared_statements plugin is loaded after" do
|
92
|
+
db = Sequel.mock(:host=>'postgres', :fetch=>{:id=>1, :s=>2}, :autoid=>1, :numrows=>1)
|
93
|
+
db.extend_datasets{def quote_identifiers?; false end}
|
94
|
+
model = Class.new(Sequel::Model)
|
95
|
+
model.dataset = db[:t]
|
96
|
+
model.columns :id, :s
|
97
|
+
model.plugin :insert_conflict
|
98
|
+
model.plugin :prepared_statements
|
99
|
+
db.sqls
|
100
|
+
model.create(:s=>'a').update(:s=>'b')
|
101
|
+
db.sqls.must_equal ["INSERT INTO t (s) VALUES ('a') RETURNING *", "UPDATE t SET s = 'b' WHERE (id = 1)"]
|
102
|
+
end
|
77
103
|
end
|
@@ -492,6 +492,54 @@ describe "NestedAttributes plugin" do
|
|
492
492
|
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 10)"]
|
493
493
|
end
|
494
494
|
|
495
|
+
it "should raise a NoExistingObject error if object to be updated no longer exists, if the :require_modification=>true option is used" do
|
496
|
+
@Artist.nested_attributes :albums, :require_modification=>true, :destroy=>true
|
497
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
498
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
499
|
+
ar.associations[:albums] = [al]
|
500
|
+
ar.set(:albums_attributes=>[{:id=>10, :name=>'L'}])
|
501
|
+
@db.sqls.must_equal []
|
502
|
+
@db.numrows = [1, 0]
|
503
|
+
proc{ar.save}.must_raise Sequel::NoExistingObject
|
504
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'L' WHERE (id = 10)"]
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should not raise an Error if object to be updated no longer exists, if the :require_modification=>false option is used" do
|
508
|
+
@Artist.nested_attributes :albums, :require_modification=>false, :destroy=>true
|
509
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
510
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
511
|
+
ar.associations[:albums] = [al]
|
512
|
+
ar.set(:albums_attributes=>[{:id=>10, :name=>'L'}])
|
513
|
+
@db.sqls.must_equal []
|
514
|
+
@db.numrows = [1, 0]
|
515
|
+
ar.save
|
516
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'L' WHERE (id = 10)"]
|
517
|
+
end
|
518
|
+
|
519
|
+
it "should raise a NoExistingObject error if object to be deleted no longer exists, if the :require_modification=>true option is used" do
|
520
|
+
@Artist.nested_attributes :albums, :require_modification=>true, :destroy=>true
|
521
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
522
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
523
|
+
ar.associations[:albums] = [al]
|
524
|
+
ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
|
525
|
+
@db.sqls.must_equal []
|
526
|
+
@db.numrows = [1, 0]
|
527
|
+
proc{ar.save}.must_raise Sequel::NoExistingObject
|
528
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
|
529
|
+
end
|
530
|
+
|
531
|
+
it "should not raise an Error if object to be deleted no longer exists, if the :require_modification=>false option is used" do
|
532
|
+
@Artist.nested_attributes :albums, :require_modification=>false, :destroy=>true
|
533
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
534
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
535
|
+
ar.associations[:albums] = [al]
|
536
|
+
ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
|
537
|
+
@db.sqls.must_equal []
|
538
|
+
@db.numrows = [1, 0]
|
539
|
+
ar.save
|
540
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
|
541
|
+
end
|
542
|
+
|
495
543
|
it "should not attempt to validate nested attributes twice for one_to_many associations when creating them" do
|
496
544
|
@Artist.nested_attributes :albums
|
497
545
|
validated = []
|
@@ -83,9 +83,9 @@ describe "Sequel::Postgres::ArrayOp" do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
it "#to_string/join should use the array_to_string function" do
|
86
|
-
@db.literal(@a.to_string).must_equal "array_to_string(a, ''
|
87
|
-
@db.literal(@a.join).must_equal "array_to_string(a, ''
|
88
|
-
@db.literal(@a.join(':')).must_equal "array_to_string(a, ':'
|
86
|
+
@db.literal(@a.to_string).must_equal "array_to_string(a, '')"
|
87
|
+
@db.literal(@a.join).must_equal "array_to_string(a, '')"
|
88
|
+
@db.literal(@a.join(':')).must_equal "array_to_string(a, ':')"
|
89
89
|
@db.literal(@a.join(':', '*')).must_equal "array_to_string(a, ':', '*')"
|
90
90
|
end
|
91
91
|
|
@@ -169,4 +169,41 @@ describe "pg_auto_constraint_validations plugin" do
|
|
169
169
|
proc{o.update(:i=>12)}.must_raise Sequel::ValidationFailed
|
170
170
|
o.errors.must_equal(:i=>['foo bar'])
|
171
171
|
end
|
172
|
+
|
173
|
+
it "should handle dumping cached metadata and loading metadata from cache" do
|
174
|
+
cache_file = "spec/files/pgacv-spec-#{$$}.cache"
|
175
|
+
begin
|
176
|
+
@ds = @db[:items]
|
177
|
+
@ds.send(:columns=, [:id, :i])
|
178
|
+
@db.fetch = @metadata_results.dup
|
179
|
+
c = Sequel::Model(@ds)
|
180
|
+
def c.name; 'Foo' end
|
181
|
+
@db.sqls
|
182
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
183
|
+
@db.sqls.length.must_equal 5
|
184
|
+
|
185
|
+
o = c.new(:i=>12)
|
186
|
+
@set_error[Sequel::CheckConstraintViolation, :constraint=>'items_i_id_check']
|
187
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
188
|
+
|
189
|
+
c.dump_pg_auto_constraint_validations_cache
|
190
|
+
|
191
|
+
@db.fetch = []
|
192
|
+
c = Sequel::Model(@ds)
|
193
|
+
def c.name; 'Foo' end
|
194
|
+
@db.sqls
|
195
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
196
|
+
@db.sqls.must_be_empty
|
197
|
+
|
198
|
+
o = c.new(:i=>12)
|
199
|
+
@set_error[Sequel::CheckConstraintViolation, :constraint=>'items_i_id_check']
|
200
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
201
|
+
ensure
|
202
|
+
File.delete(cache_file) if File.file?(cache_file)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should raise error if attempting to dump cached metadata when not using caching" do
|
207
|
+
proc{@c.dump_pg_auto_constraint_validations_cache}.must_raise Sequel::Error
|
208
|
+
end
|
172
209
|
end
|
@@ -286,4 +286,71 @@ describe "Sequel::Postgres::JSONOp" do
|
|
286
286
|
it "should allow transforming JSONBHash instances into ArrayOp instances" do
|
287
287
|
@db.literal(Sequel.pg_jsonb('a'=>1).op['a']).must_equal "('{\"a\":1}'::jsonb -> 'a')"
|
288
288
|
end
|
289
|
+
|
290
|
+
it "#path_exists should use the @? operator" do
|
291
|
+
@l[@jb.path_exists('$')].must_equal "(j @? '$')"
|
292
|
+
end
|
293
|
+
|
294
|
+
it "#path_exists result should be a boolean expression" do
|
295
|
+
@jb.path_exists('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
296
|
+
end
|
297
|
+
|
298
|
+
it "#path_match should use the @@ operator" do
|
299
|
+
@l[@jb.path_match('$')].must_equal "(j @@ '$')"
|
300
|
+
end
|
301
|
+
|
302
|
+
it "#path_match result should be a boolean expression" do
|
303
|
+
@jb.path_match('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
304
|
+
end
|
305
|
+
|
306
|
+
it "#path_exists! should use the jsonb_path_exists function" do
|
307
|
+
@l[@jb.path_exists!('$')].must_equal "jsonb_path_exists(j, '$')"
|
308
|
+
@l[@jb.path_exists!('$', '{"x":2}')].must_equal "jsonb_path_exists(j, '$', '{\"x\":2}')"
|
309
|
+
@l[@jb.path_exists!('$', x: 2)].must_equal "jsonb_path_exists(j, '$', '{\"x\":2}')"
|
310
|
+
@l[@jb.path_exists!('$', {x: 2}, true)].must_equal "jsonb_path_exists(j, '$', '{\"x\":2}', true)"
|
311
|
+
end
|
312
|
+
|
313
|
+
it "#path_exists! result should be a boolean expression" do
|
314
|
+
@jb.path_exists!('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
315
|
+
end
|
316
|
+
|
317
|
+
it "#path_match! should use the jsonb_path_match function" do
|
318
|
+
@l[@jb.path_match!('$')].must_equal "jsonb_path_match(j, '$')"
|
319
|
+
@l[@jb.path_match!('$', '{"x":2}')].must_equal "jsonb_path_match(j, '$', '{\"x\":2}')"
|
320
|
+
@l[@jb.path_match!('$', x: 2)].must_equal "jsonb_path_match(j, '$', '{\"x\":2}')"
|
321
|
+
@l[@jb.path_match!('$', {x: 2}, true)].must_equal "jsonb_path_match(j, '$', '{\"x\":2}', true)"
|
322
|
+
end
|
323
|
+
|
324
|
+
it "#path_match! result should be a boolean expression" do
|
325
|
+
@jb.path_match!('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
326
|
+
end
|
327
|
+
|
328
|
+
it "#path_query should use the jsonb_path_query function" do
|
329
|
+
@l[@jb.path_query('$')].must_equal "jsonb_path_query(j, '$')"
|
330
|
+
@l[@jb.path_query('$', '{"x":2}')].must_equal "jsonb_path_query(j, '$', '{\"x\":2}')"
|
331
|
+
@l[@jb.path_query('$', x: 2)].must_equal "jsonb_path_query(j, '$', '{\"x\":2}')"
|
332
|
+
@l[@jb.path_query('$', {x: 2}, true)].must_equal "jsonb_path_query(j, '$', '{\"x\":2}', true)"
|
333
|
+
end
|
334
|
+
|
335
|
+
it "#path_query_array should use the jsonb_path_query_array function" do
|
336
|
+
@l[@jb.path_query_array('$')].must_equal "jsonb_path_query_array(j, '$')"
|
337
|
+
@l[@jb.path_query_array('$', '{"x":2}')].must_equal "jsonb_path_query_array(j, '$', '{\"x\":2}')"
|
338
|
+
@l[@jb.path_query_array('$', x: 2)].must_equal "jsonb_path_query_array(j, '$', '{\"x\":2}')"
|
339
|
+
@l[@jb.path_query_array('$', {x: 2}, true)].must_equal "jsonb_path_query_array(j, '$', '{\"x\":2}', true)"
|
340
|
+
end
|
341
|
+
|
342
|
+
it "#path_query_array result should be a JSONBOp" do
|
343
|
+
@l[@jb.path_query_array('$').path_query_array('$')].must_equal "jsonb_path_query_array(jsonb_path_query_array(j, '$'), '$')"
|
344
|
+
end
|
345
|
+
|
346
|
+
it "#path_query_first should use the jsonb_path_query_first function" do
|
347
|
+
@l[@jb.path_query_first('$')].must_equal "jsonb_path_query_first(j, '$')"
|
348
|
+
@l[@jb.path_query_first('$', '{"x":2}')].must_equal "jsonb_path_query_first(j, '$', '{\"x\":2}')"
|
349
|
+
@l[@jb.path_query_first('$', x: 2)].must_equal "jsonb_path_query_first(j, '$', '{\"x\":2}')"
|
350
|
+
@l[@jb.path_query_first('$', {x: 2}, true)].must_equal "jsonb_path_query_first(j, '$', '{\"x\":2}', true)"
|
351
|
+
end
|
352
|
+
|
353
|
+
it "#path_query_first result should be a JSONBOp" do
|
354
|
+
@l[@jb.path_query_first('$').path_query_first('$')].must_equal "jsonb_path_query_first(jsonb_path_query_first(j, '$'), '$')"
|
355
|
+
end
|
289
356
|
end
|
@@ -52,13 +52,13 @@ describe "pg_range extension" do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should literalize endless Range instances to strings correctly" do
|
55
|
-
@db.literal(eval('1..')).must_equal "'[1,]'"
|
56
|
-
@db.literal(eval('1...')).must_equal "'[1,)'"
|
55
|
+
@db.literal(eval('(1..)')).must_equal "'[1,]'"
|
56
|
+
@db.literal(eval('(1...)')).must_equal "'[1,)'"
|
57
57
|
end if endless_range_support
|
58
58
|
|
59
59
|
it "should literalize startless Range instances to strings correctly" do
|
60
|
-
@db.literal(eval('..1')).must_equal "'[,1]'"
|
61
|
-
@db.literal(eval('...1')).must_equal "'[,1)'"
|
60
|
+
@db.literal(eval('(..1)')).must_equal "'[,1]'"
|
61
|
+
@db.literal(eval('(...1)')).must_equal "'[,1)'"
|
62
62
|
end if startless_range_support
|
63
63
|
|
64
64
|
it "should literalize startless, endless Range instances to strings correctly" do
|
@@ -90,13 +90,13 @@ describe "pg_range extension" do
|
|
90
90
|
end
|
91
91
|
|
92
92
|
it "should support using endless Range instances as bound variables" do
|
93
|
-
@db.bound_variable_arg(eval('1..'), nil).must_equal "[1,]"
|
94
|
-
@db.bound_variable_arg(eval('1...'), nil).must_equal "[1,)"
|
93
|
+
@db.bound_variable_arg(eval('(1..)'), nil).must_equal "[1,]"
|
94
|
+
@db.bound_variable_arg(eval('(1...)'), nil).must_equal "[1,)"
|
95
95
|
end if endless_range_support
|
96
96
|
|
97
97
|
it "should support using startless Range instances as bound variables" do
|
98
|
-
@db.bound_variable_arg(eval('..1'), nil).must_equal "[,1]"
|
99
|
-
@db.bound_variable_arg(eval('...1'), nil).must_equal "[,1)"
|
98
|
+
@db.bound_variable_arg(eval('(..1)'), nil).must_equal "[,1]"
|
99
|
+
@db.bound_variable_arg(eval('(...1)'), nil).must_equal "[,1)"
|
100
100
|
end if startless_range_support
|
101
101
|
|
102
102
|
it "should support using startless, endless Range instances as bound variables" do
|
@@ -113,7 +113,7 @@ describe "pg_range extension" do
|
|
113
113
|
end
|
114
114
|
|
115
115
|
it "should support using arrays of endless Range instances as bound variables" do
|
116
|
-
@db.bound_variable_arg([eval('1..'), eval('2..')], nil).must_equal '{"[1,]","[2,]"}'
|
116
|
+
@db.bound_variable_arg([eval('(1..)'), eval('(2..)')], nil).must_equal '{"[1,]","[2,]"}'
|
117
117
|
end if endless_range_support
|
118
118
|
|
119
119
|
it "should support using PGRange instances as bound variables" do
|
@@ -136,6 +136,20 @@ describe "pg_range extension" do
|
|
136
136
|
s[1][1][:ruby_default].must_equal Sequel::Postgres::PGRange.new(1, 5, :exclude_end=>true, :db_type=>'int4range')
|
137
137
|
end
|
138
138
|
|
139
|
+
it "should work correctly in hashes" do
|
140
|
+
h = Hash.new(1)
|
141
|
+
h[@R.new(1, 2)] = 2
|
142
|
+
h[@R.new(nil, nil, :empty => true)] = 3
|
143
|
+
h[@R.new(1, 2)].must_equal 2
|
144
|
+
h[@R.new(1, 3)].must_equal 1
|
145
|
+
h[@R.new(2, 2)].must_equal 1
|
146
|
+
h[@R.new(1, 2, :exclude_begin => true)].must_equal 1
|
147
|
+
h[@R.new(1, 2, :exclude_end => true)].must_equal 1
|
148
|
+
h[@R.new(1, 2, :db_type => :int)].must_equal 1
|
149
|
+
h[@R.new(nil, nil, :empty => true)].must_equal 3
|
150
|
+
h[@R.new(nil, nil, :empty => true, :db_type => :int)].must_equal 1
|
151
|
+
end
|
152
|
+
|
139
153
|
describe "database typecasting" do
|
140
154
|
before do
|
141
155
|
@o = @R.new(1, 2, :db_type=>'int4range')
|
@@ -456,21 +470,21 @@ describe "pg_range extension" do
|
|
456
470
|
end
|
457
471
|
|
458
472
|
it "should consider PGRanges equal with a endless Range they represent" do
|
459
|
-
@R.new(1, nil).must_be :==, eval('1..')
|
460
|
-
@R.new(1, nil, :exclude_end=>true).must_be :==, eval('1...')
|
461
|
-
@R.new(1, nil).wont_be :==, eval('1...')
|
462
|
-
@R.new(1, nil, :exclude_end=>true).wont_be :==, eval('1..')
|
463
|
-
@R.new(1, nil).wont_be :==, eval('2..')
|
464
|
-
@R.new(1, nil, :exclude_end=>true).wont_be :==, eval('2...')
|
473
|
+
@R.new(1, nil).must_be :==, eval('(1..)')
|
474
|
+
@R.new(1, nil, :exclude_end=>true).must_be :==, eval('(1...)')
|
475
|
+
@R.new(1, nil).wont_be :==, eval('(1...)')
|
476
|
+
@R.new(1, nil, :exclude_end=>true).wont_be :==, eval('(1..)')
|
477
|
+
@R.new(1, nil).wont_be :==, eval('(2..)')
|
478
|
+
@R.new(1, nil, :exclude_end=>true).wont_be :==, eval('(2...)')
|
465
479
|
end if endless_range_support
|
466
480
|
|
467
481
|
it "should consider PGRanges equal with a startless Range they represent" do
|
468
|
-
@R.new(nil, 1).must_be :==, eval('..1')
|
469
|
-
@R.new(nil, 1, :exclude_end=>true).must_be :==, eval('...1')
|
470
|
-
@R.new(nil, 1).wont_be :==, eval('...1')
|
471
|
-
@R.new(nil, 1, :exclude_end=>true).wont_be :==, eval('..1')
|
472
|
-
@R.new(nil, 1).wont_be :==, eval('..2')
|
473
|
-
@R.new(nil, 1, :exclude_end=>true).wont_be :==, eval('...2')
|
482
|
+
@R.new(nil, 1).must_be :==, eval('(..1)')
|
483
|
+
@R.new(nil, 1, :exclude_end=>true).must_be :==, eval('(...1)')
|
484
|
+
@R.new(nil, 1).wont_be :==, eval('(...1)')
|
485
|
+
@R.new(nil, 1, :exclude_end=>true).wont_be :==, eval('(..1)')
|
486
|
+
@R.new(nil, 1).wont_be :==, eval('(..2)')
|
487
|
+
@R.new(nil, 1, :exclude_end=>true).wont_be :==, eval('(...2)')
|
474
488
|
end if startless_range_support
|
475
489
|
|
476
490
|
it "should consider PGRanges equal with a startless, endless Range they represent" do
|
@@ -186,4 +186,12 @@ describe "sharding plugin" do
|
|
186
186
|
["UPDATE albums SET artist_id = 2, name = 'RF' WHERE (id = 1) -- s1", "UPDATE albums SET name = 'RF', artist_id = 2 WHERE (id = 1) -- s1"].must_include(sqls.slice!(2))
|
187
187
|
sqls.must_equal ["SELECT * FROM albums LIMIT 1 -- s2", "BEGIN -- s1", "COMMIT -- s1"]
|
188
188
|
end
|
189
|
+
|
190
|
+
it "should have objects retrieved from a specific shard using with_server from server_block extension" do
|
191
|
+
album = @db.extension(:server_block).with_server(:s1) do
|
192
|
+
@Album.first
|
193
|
+
end
|
194
|
+
album.update(:name=>'MO')
|
195
|
+
@db.sqls.must_equal ["SELECT * FROM albums LIMIT 1 -- s1", "UPDATE albums SET name = 'MO' WHERE (id = 1) -- s1"]
|
196
|
+
end
|
189
197
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe "static_cache_cache plugin" do
|
4
|
+
before do
|
5
|
+
@db = Sequel.mock
|
6
|
+
@db.fetch = [{:id=>1, :name=>'A'}, {:id=>2, :name=>'B'}]
|
7
|
+
@c = Class.new(Sequel::Model(@db[:t]))
|
8
|
+
def @c.name; 'Foo' end
|
9
|
+
@c.columns :id, :name
|
10
|
+
@file = "spec/files/static_cache_cache-spec-#{$$}.cache"
|
11
|
+
end
|
12
|
+
after do
|
13
|
+
File.delete(@file) if File.file?(@file)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should allow dumping and loading static cache rows from a cache file" do
|
17
|
+
@c.plugin :static_cache_cache, @file
|
18
|
+
@db.sqls
|
19
|
+
@c.plugin :static_cache
|
20
|
+
@db.sqls.must_equal ['SELECT * FROM t']
|
21
|
+
@c.all.must_equal [@c.load(:id=>1, :name=>'A'), @c.load(:id=>2, :name=>'B')]
|
22
|
+
|
23
|
+
@c.dump_static_cache_cache
|
24
|
+
|
25
|
+
@db.fetch = []
|
26
|
+
c = Class.new(Sequel::Model(@db[:t]))
|
27
|
+
def c.name; 'Foo' end
|
28
|
+
c.columns :id, :name
|
29
|
+
@c.plugin :static_cache_cache, @file
|
30
|
+
@db.sqls
|
31
|
+
@c.plugin :static_cache
|
32
|
+
@db.sqls.must_be_empty
|
33
|
+
@c.all.must_equal [@c.load(:id=>1, :name=>'A'), @c.load(:id=>2, :name=>'B')]
|
34
|
+
end
|
35
|
+
end
|
data/spec/guards_helper.rb
CHANGED
@@ -92,6 +92,17 @@ describe "Simple Dataset operations" do
|
|
92
92
|
@ds.from_self(:alias=>:items).graph(@ds.from_self, {:id=>:id}, :table_alias=>:b).extension(:graph_each).all.must_equal [{:items=>{:id=>1, :number=>10}, :b=>{:id=>1, :number=>10}}]
|
93
93
|
end
|
94
94
|
|
95
|
+
cspecify "should have insert and update work with Sequel::DEFAULT", :sqlite do
|
96
|
+
@db.create_table!(:items) do
|
97
|
+
Integer :number, :default=>10
|
98
|
+
end
|
99
|
+
@ds.insert(:number=>Sequel::DEFAULT)
|
100
|
+
@ds.select_map(:number).must_equal [10]
|
101
|
+
@ds.insert(:number=>20)
|
102
|
+
@ds.update(:number=>Sequel::DEFAULT)
|
103
|
+
@ds.select_map(:number).must_equal [10, 10]
|
104
|
+
end
|
105
|
+
|
95
106
|
cspecify "should have insert work correctly when inserting a row with all NULL values", :hsqldb do
|
96
107
|
@db.create_table!(:items) do
|
97
108
|
String :name
|
@@ -443,6 +454,20 @@ describe Sequel::Dataset do
|
|
443
454
|
@d.extension(:sequel_4_dataset_methods).interval(:value).to_i.must_equal 6
|
444
455
|
end
|
445
456
|
|
457
|
+
it "should support or emulate filtered aggregate functions" do
|
458
|
+
@d.insert(:name => 'abc', :value => 123)
|
459
|
+
@d.insert(:name => 'abc', :value => 456)
|
460
|
+
@d.insert(:name => 'def', :value => 324)
|
461
|
+
@d.get{count.function.*.filter{value > 100}}.must_equal 3
|
462
|
+
@d.get{count.function.*.filter{value > 200}}.must_equal 2
|
463
|
+
@d.get{count.function.*.filter{value > 400}}.must_equal 1
|
464
|
+
@d.get{count.function.*.filter{value > 500}}.must_equal 0
|
465
|
+
@d.get{count(:value).filter{value > 100}}.must_equal 3
|
466
|
+
@d.get{count(:value).filter{value > 200}}.must_equal 2
|
467
|
+
@d.get{count(:value).filter{value > 400}}.must_equal 1
|
468
|
+
@d.get{count(:value).filter{value > 500}}.must_equal 0
|
469
|
+
end
|
470
|
+
|
446
471
|
it "should return the correct records" do
|
447
472
|
@d.to_a.must_equal []
|
448
473
|
@d.insert(:name => 'abc', :value => 123)
|
@@ -568,22 +593,21 @@ describe Sequel::Dataset do
|
|
568
593
|
end
|
569
594
|
|
570
595
|
describe "Simple Dataset operations" do
|
571
|
-
before do
|
596
|
+
before(:all) do
|
572
597
|
DB.create_table!(:items) do
|
573
598
|
Integer :number
|
574
599
|
TrueClass :flag
|
575
600
|
end
|
576
|
-
@ds = DB[:items]
|
601
|
+
@ds = DB[:items].order(:number)
|
602
|
+
@ds.insert(:number=>1, :flag=>true)
|
603
|
+
@ds.insert(:number=>2, :flag=>false)
|
604
|
+
@ds.insert(:number=>3, :flag=>nil)
|
577
605
|
end
|
578
|
-
after do
|
606
|
+
after(:all) do
|
579
607
|
DB.drop_table?(:items)
|
580
608
|
end
|
581
609
|
|
582
610
|
it "should deal with boolean conditions correctly" do
|
583
|
-
@ds.insert(:number=>1, :flag=>true)
|
584
|
-
@ds.insert(:number=>2, :flag=>false)
|
585
|
-
@ds.insert(:number=>3, :flag=>nil)
|
586
|
-
@ds = @ds.order(:number)
|
587
611
|
@ds.filter(:flag=>true).map(:number).must_equal [1]
|
588
612
|
@ds.filter(:flag=>false).map(:number).must_equal [2]
|
589
613
|
@ds.filter(:flag=>nil).map(:number).must_equal [3]
|
@@ -591,6 +615,22 @@ describe "Simple Dataset operations" do
|
|
591
615
|
@ds.exclude(:flag=>false).map(:number).must_equal [1, 3]
|
592
616
|
@ds.exclude(:flag=>nil).map(:number).must_equal [1, 2]
|
593
617
|
end
|
618
|
+
|
619
|
+
cspecify "should deal with boolean equality conditions correctly", :derby do
|
620
|
+
@ds.filter(true=>:flag).map(:number).must_equal [1]
|
621
|
+
@ds.filter(false=>:flag).map(:number).must_equal [2]
|
622
|
+
@ds.filter(nil=>:flag).map(:number).must_equal []
|
623
|
+
@ds.exclude(true=>:flag).map(:number).must_equal [2]
|
624
|
+
@ds.exclude(false=>:flag).map(:number).must_equal [1]
|
625
|
+
@ds.exclude(nil=>:flag).map(:number).must_equal []
|
626
|
+
end
|
627
|
+
|
628
|
+
cspecify "should have exclude_or_null work correctly", :mssql, :derby, :oracle, :db2, :sqlanywhere do
|
629
|
+
@ds = @ds.extension(:exclude_or_null)
|
630
|
+
@ds.exclude_or_null(true=>:flag).map(:number).must_equal [2, 3]
|
631
|
+
@ds.exclude_or_null(false=>:flag).map(:number).must_equal [1, 3]
|
632
|
+
@ds.exclude_or_null(nil=>:flag).map(:number).must_equal [1, 2, 3]
|
633
|
+
end
|
594
634
|
end
|
595
635
|
|
596
636
|
describe "Simple Dataset operations in transactions" do
|
@@ -1553,20 +1593,20 @@ describe "Sequel::Dataset DSL support" do
|
|
1553
1593
|
|
1554
1594
|
it "should work with endless ranges as hash values" do
|
1555
1595
|
@ds.insert(20, 10)
|
1556
|
-
@ds.filter(:a=>eval('30..')).all.must_equal []
|
1557
|
-
@ds.filter(:a=>eval('20...')).all.must_equal [{:a=>20, :b=>10}]
|
1558
|
-
@ds.filter(:a=>eval('20..')).all.must_equal [{:a=>20, :b=>10}]
|
1559
|
-
@ds.filter(:a=>eval('10..')).all.must_equal [{:a=>20, :b=>10}]
|
1596
|
+
@ds.filter(:a=>eval('(30..)')).all.must_equal []
|
1597
|
+
@ds.filter(:a=>eval('(20...)')).all.must_equal [{:a=>20, :b=>10}]
|
1598
|
+
@ds.filter(:a=>eval('(20..)')).all.must_equal [{:a=>20, :b=>10}]
|
1599
|
+
@ds.filter(:a=>eval('(10..)')).all.must_equal [{:a=>20, :b=>10}]
|
1560
1600
|
end if RUBY_VERSION >= '2.6'
|
1561
1601
|
|
1562
1602
|
it "should work with startless ranges as hash values" do
|
1563
1603
|
@ds.insert(20, 10)
|
1564
|
-
@ds.filter(:a=>eval('..30')).all.must_equal [{:a=>20, :b=>10}]
|
1565
|
-
@ds.filter(:a=>eval('...30')).all.must_equal [{:a=>20, :b=>10}]
|
1566
|
-
@ds.filter(:a=>eval('..20')).all.must_equal [{:a=>20, :b=>10}]
|
1567
|
-
@ds.filter(:a=>eval('...20')).all.must_equal []
|
1568
|
-
@ds.filter(:a=>eval('..10')).all.must_equal []
|
1569
|
-
@ds.filter(:a=>eval('...10')).all.must_equal []
|
1604
|
+
@ds.filter(:a=>eval('(..30)')).all.must_equal [{:a=>20, :b=>10}]
|
1605
|
+
@ds.filter(:a=>eval('(...30)')).all.must_equal [{:a=>20, :b=>10}]
|
1606
|
+
@ds.filter(:a=>eval('(..20)')).all.must_equal [{:a=>20, :b=>10}]
|
1607
|
+
@ds.filter(:a=>eval('(...20)')).all.must_equal []
|
1608
|
+
@ds.filter(:a=>eval('(..10)')).all.must_equal []
|
1609
|
+
@ds.filter(:a=>eval('(...10)')).all.must_equal []
|
1570
1610
|
|
1571
1611
|
@ds.filter(:a=>eval('nil..nil')).all.must_equal [{:a=>20, :b=>10}]
|
1572
1612
|
end if RUBY_VERSION >= '2.7'
|