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