sequel 5.19.0 → 5.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +102 -0
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +5 -1
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/sharding.rdoc +2 -0
- data/doc/testing.rdoc +1 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/ado.rb +27 -19
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +2 -3
- data/lib/sequel/adapters/shared/mssql.rb +7 -7
- data/lib/sequel/adapters/shared/postgres.rb +37 -19
- data/lib/sequel/adapters/shared/sqlite.rb +27 -3
- data/lib/sequel/adapters/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +12 -3
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset.rb +4 -2
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/query.rb +5 -1
- data/lib/sequel/dataset/sql.rb +11 -7
- data/lib/sequel/extensions/named_timezones.rb +52 -8
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +387 -123
- data/lib/sequel/extensions/pg_range.rb +3 -2
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/extensions/server_block.rb +15 -4
- data/lib/sequel/model/associations.rb +35 -9
- data/lib/sequel/model/plugins.rb +104 -0
- data/lib/sequel/plugins/association_dependencies.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +14 -4
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
- data/lib/sequel/plugins/composition.rb +13 -9
- data/lib/sequel/plugins/finder.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +17 -5
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
- data/lib/sequel/plugins/rcte_tree.rb +6 -0
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/sql.rb +15 -3
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +24 -0
- data/spec/adapters/mysql_spec.rb +0 -5
- data/spec/adapters/postgres_spec.rb +319 -1
- data/spec/bin_spec.rb +1 -1
- data/spec/core/database_spec.rb +123 -2
- data/spec/core/dataset_spec.rb +33 -1
- data/spec/core/expression_filters_spec.rb +25 -1
- data/spec/core/schema_spec.rb +24 -0
- data/spec/extensions/class_table_inheritance_spec.rb +30 -8
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/hook_class_methods_spec.rb +22 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/migration_spec.rb +13 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
- data/spec/extensions/pg_json_spec.rb +218 -29
- data/spec/extensions/pg_range_spec.rb +76 -9
- data/spec/extensions/rcte_tree_spec.rb +6 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/extensions/server_block_spec.rb +38 -0
- data/spec/extensions/spec_helper.rb +8 -1
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/integration/dataset_test.rb +25 -9
- data/spec/integration/plugin_test.rb +42 -0
- data/spec/integration/schema_test.rb +7 -2
- data/spec/integration/transaction_test.rb +50 -0
- data/spec/model/associations_spec.rb +84 -4
- data/spec/model/plugins_spec.rb +111 -0
- metadata +16 -2
data/spec/bin_spec.rb
CHANGED
@@ -86,7 +86,7 @@ Begin creating indexes
|
|
86
86
|
Finished creating indexes
|
87
87
|
Begin adding foreign key constraints
|
88
88
|
Finished adding foreign key constraints
|
89
|
-
Database copy finished in \\d
|
89
|
+
Database copy finished in \\d+\\.\\d+ seconds
|
90
90
|
END
|
91
91
|
DB2.tables.sort_by{|t| t.to_s}.must_equal [:a, :b]
|
92
92
|
DB[:a].all.must_equal [{:a=>1, :name=>'foo'}]
|
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
|
@@ -1140,15 +1159,86 @@ describe "Database#transaction with savepoint support" do
|
|
1140
1159
|
end
|
1141
1160
|
|
1142
1161
|
it "should support after_rollback inside savepoints" do
|
1143
|
-
@db.transaction do
|
1162
|
+
@db.transaction(:rollback=>:always) do
|
1144
1163
|
@db.after_rollback{@db.execute('foo')}
|
1145
1164
|
@db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('bar')}}
|
1146
1165
|
@db.after_rollback{@db.execute('baz')}
|
1147
|
-
raise Sequel::Rollback
|
1148
1166
|
end
|
1149
1167
|
@db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
|
1150
1168
|
end
|
1151
1169
|
|
1170
|
+
it "should run after_commit if savepoint rolled back" do
|
1171
|
+
@db.transaction do
|
1172
|
+
@db.after_commit{@db.execute('foo')}
|
1173
|
+
@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_commit{@db.execute('bar')}}
|
1174
|
+
end
|
1175
|
+
@db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'COMMIT', 'foo', 'bar']
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
it "should not run after_commit if savepoint rolled back and :savepoint option used" do
|
1179
|
+
@db.transaction do
|
1180
|
+
@db.after_commit{@db.execute('foo')}
|
1181
|
+
@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_commit(:savepoint=>true){@db.execute('bar')}}
|
1182
|
+
end
|
1183
|
+
@db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'COMMIT', 'foo']
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
it "should not run after_commit if higher-level savepoint rolled back and :savepoint option used" do
|
1187
|
+
@db.transaction do
|
1188
|
+
@db.after_commit{@db.execute('foo')}
|
1189
|
+
@db.transaction(:savepoint=>true, :rollback=>:always){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){@db.execute('bar')}}}
|
1190
|
+
end
|
1191
|
+
@db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "ROLLBACK TO SAVEPOINT autopoint_1", "COMMIT", "foo"]
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
it "should not run after_commit if transaction rolled back and :savepoint option used" do
|
1195
|
+
@db.transaction(:rollback=>:always) do
|
1196
|
+
@db.after_commit{@db.execute('foo')}
|
1197
|
+
@db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){@db.execute('bar')}}}
|
1198
|
+
end
|
1199
|
+
@db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_1", "ROLLBACK"]
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
it "should run after_rollback if savepoint rolls back" do
|
1203
|
+
@db.transaction(:rollback=>:always) do
|
1204
|
+
@db.after_rollback{@db.execute('foo')}
|
1205
|
+
@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback{@db.execute('bar')}}
|
1206
|
+
@db.after_rollback{@db.execute('baz')}
|
1207
|
+
end
|
1208
|
+
@db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
it "should run after_rollback when savepoint rolls back if :savepoint option used" do
|
1212
|
+
@db.transaction(:rollback=>:always) do
|
1213
|
+
@db.after_rollback{@db.execute('foo')}
|
1214
|
+
@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}
|
1215
|
+
@db.after_rollback{@db.execute('baz')}
|
1216
|
+
end
|
1217
|
+
@db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'bar', 'ROLLBACK', 'foo', 'baz']
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
it "should run after_rollback if savepoint rolled back and :savepoint option used, even if transaction commits" do
|
1221
|
+
@db.transaction do
|
1222
|
+
@db.after_commit{@db.execute('foo')}
|
1223
|
+
@db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}
|
1224
|
+
end
|
1225
|
+
@db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'bar', 'COMMIT', 'foo']
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
it "should run after_rollback if higher-level savepoint rolled back and :savepoint option used" do
|
1229
|
+
@db.transaction do
|
1230
|
+
@db.transaction(:savepoint=>true, :rollback=>:always){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}}
|
1231
|
+
end
|
1232
|
+
@db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "ROLLBACK TO SAVEPOINT autopoint_1", "bar", "COMMIT"]
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
it "should run after_rollback if transaction rolled back and :savepoint option used" do
|
1236
|
+
@db.transaction(:rollback=>:always) do
|
1237
|
+
@db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}}
|
1238
|
+
end
|
1239
|
+
@db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_1", "ROLLBACK", "bar"]
|
1240
|
+
end
|
1241
|
+
|
1152
1242
|
it "should raise an error if you attempt to use after_commit inside a savepoint in a prepared transaction" do
|
1153
1243
|
@db.define_singleton_method(:supports_prepared_transactions?){true}
|
1154
1244
|
proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_commit{@db.execute('foo')}}}}.must_raise(Sequel::Error)
|
@@ -2002,6 +2092,18 @@ describe "Database#typecast_value" do
|
|
2002
2092
|
proc{@db.typecast_value(:datetime, 4)}.must_raise(Sequel::InvalidValue)
|
2003
2093
|
end
|
2004
2094
|
|
2095
|
+
it "should raise an InvalidValue when given an invalid timezone value" do
|
2096
|
+
begin
|
2097
|
+
Sequel.default_timezone = :blah
|
2098
|
+
proc{@db.typecast_value(:datetime, [2019, 2, 3, 4, 5, 6])}.must_raise(Sequel::InvalidValue)
|
2099
|
+
Sequel.datetime_class = DateTime
|
2100
|
+
proc{@db.typecast_value(:datetime, [2019, 2, 3, 4, 5, 6])}.must_raise(Sequel::InvalidValue)
|
2101
|
+
ensure
|
2102
|
+
Sequel.default_timezone = nil
|
2103
|
+
Sequel.datetime_class = Time
|
2104
|
+
end
|
2105
|
+
end
|
2106
|
+
|
2005
2107
|
it "should handle integers with leading 0 as base 10" do
|
2006
2108
|
@db.typecast_value(:integer, "013").must_equal 13
|
2007
2109
|
@db.typecast_value(:integer, "08").must_equal 8
|
@@ -2246,6 +2348,17 @@ describe "Database#typecast_value" do
|
|
2246
2348
|
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).must_equal Time.local(2011, 10, 11, 12, 13, 14)
|
2247
2349
|
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).must_equal Time.local(2011, 10, 11, 12, 13, 14, 500000)
|
2248
2350
|
|
2351
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :offset=>Rational(1, 2)).must_equal Time.new(2011, 10, 11, 12, 13, 14, 43200)
|
2352
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000, :offset=>Rational(1, 2)).must_equal Time.new(2011, 10, 11, 12, 13, 14.5, 43200)
|
2353
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'offset'=>Rational(1, 2)).must_equal Time.new(2011, 10, 11, 12, 13, 14, 43200)
|
2354
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000, 'offset'=>Rational(1, 2)).must_equal Time.new(2011, 10, 11, 12, 13, 14.5, 43200)
|
2355
|
+
|
2356
|
+
Sequel.default_timezone = :utc
|
2357
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).must_equal Time.utc(2011, 10, 11, 12, 13, 14)
|
2358
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).must_equal Time.utc(2011, 10, 11, 12, 13, 14, 500000)
|
2359
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).must_equal Time.utc(2011, 10, 11, 12, 13, 14)
|
2360
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).must_equal Time.utc(2011, 10, 11, 12, 13, 14, 500000)
|
2361
|
+
|
2249
2362
|
Sequel.datetime_class = DateTime
|
2250
2363
|
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).must_equal DateTime.civil(2011, 10, 11, 12, 13, 14)
|
2251
2364
|
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).must_equal DateTime.civil(2011, 10, 11, 12, 13, Rational(29, 2))
|
@@ -2255,8 +2368,16 @@ describe "Database#typecast_value" do
|
|
2255
2368
|
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000, :offset=>Rational(1, 2)).must_equal DateTime.civil(2011, 10, 11, 12, 13, Rational(29, 2), Rational(1, 2))
|
2256
2369
|
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'offset'=>Rational(1, 2)).must_equal DateTime.civil(2011, 10, 11, 12, 13, 14, Rational(1, 2))
|
2257
2370
|
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000, 'offset'=>Rational(1, 2)).must_equal DateTime.civil(2011, 10, 11, 12, 13, Rational(29, 2), Rational(1, 2))
|
2371
|
+
|
2372
|
+
Sequel.default_timezone = :local
|
2373
|
+
offset = Rational(Time.local(2011, 10, 11, 12, 13, 14).utc_offset, 86400)
|
2374
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).must_equal DateTime.civil(2011, 10, 11, 12, 13, 14, offset)
|
2375
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).must_equal DateTime.civil(2011, 10, 11, 12, 13, Rational(29, 2), offset)
|
2376
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).must_equal DateTime.civil(2011, 10, 11, 12, 13, 14, offset)
|
2377
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).must_equal DateTime.civil(2011, 10, 11, 12, 13, Rational(29, 2), offset)
|
2258
2378
|
ensure
|
2259
2379
|
Sequel.datetime_class = Time
|
2380
|
+
Sequel.default_timezone = nil
|
2260
2381
|
end
|
2261
2382
|
end
|
2262
2383
|
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -3434,6 +3434,16 @@ describe "Dataset#multi_insert" do
|
|
3434
3434
|
'COMMIT']
|
3435
3435
|
end
|
3436
3436
|
|
3437
|
+
it "should handle :return=>:primary_key option if dataset has a row_proc" do
|
3438
|
+
@db.autoid = 1
|
3439
|
+
@ds.with_row_proc(lambda{|h| Object.new}).multi_insert(@list, :return=>:primary_key).must_equal [1, 2, 3]
|
3440
|
+
@db.sqls.must_equal ['BEGIN',
|
3441
|
+
"INSERT INTO items (name) VALUES ('abc')",
|
3442
|
+
"INSERT INTO items (name) VALUES ('def')",
|
3443
|
+
"INSERT INTO items (name) VALUES ('ghi')",
|
3444
|
+
'COMMIT']
|
3445
|
+
end
|
3446
|
+
|
3437
3447
|
with_symbol_splitting "should handle splittable symbols for tables" do
|
3438
3448
|
@ds = @ds.from(:sch__tab)
|
3439
3449
|
@ds.multi_insert(@list)
|
@@ -3715,7 +3725,7 @@ end
|
|
3715
3725
|
|
3716
3726
|
describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #execute" do
|
3717
3727
|
before do
|
3718
|
-
@db = Sequel.mock(:servers=>{:read_only=>{}}, :autoid=>1)
|
3728
|
+
@db = Sequel.mock(:servers=>{:read_only=>{}, :r1=>{}}, :autoid=>1)
|
3719
3729
|
@ds = @db[:items]
|
3720
3730
|
end
|
3721
3731
|
|
@@ -3753,6 +3763,18 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #ex
|
|
3753
3763
|
@ds.for_update.send(:execute, 'SELECT 1')
|
3754
3764
|
@db.sqls.must_equal ["SELECT 1"]
|
3755
3765
|
end
|
3766
|
+
|
3767
|
+
[:execute, :execute_dui, :execute_insert, :execute_ddl].each do |meth|
|
3768
|
+
it "##{meth} should respect explicit :server option" do
|
3769
|
+
@ds.send(meth, 'SELECT 1', :server=>:r1)
|
3770
|
+
@db.sqls.must_equal ["SELECT 1 -- r1"]
|
3771
|
+
end
|
3772
|
+
|
3773
|
+
it "##{meth} should respect dataset's :server option if :server option not given" do
|
3774
|
+
@ds.server(:r1).send(meth, 'SELECT 1')
|
3775
|
+
@db.sqls.must_equal ["SELECT 1 -- r1"]
|
3776
|
+
end
|
3777
|
+
end
|
3756
3778
|
end
|
3757
3779
|
|
3758
3780
|
describe "Dataset#with_sql_*" do
|
@@ -4483,6 +4505,16 @@ describe "Sequel timezone support" do
|
|
4483
4505
|
proc{Sequel.database_to_application_timestamp(Object.new)}.must_raise(Sequel::InvalidValue)
|
4484
4506
|
end
|
4485
4507
|
|
4508
|
+
it "should raise an InvalidValue error when the Time class is used and when a bad application timezone is used when attempting to convert timestamps" do
|
4509
|
+
Sequel.application_timezone = :blah
|
4510
|
+
proc{Sequel.database_to_application_timestamp('2009-06-01 10:20:30')}.must_raise(Sequel::InvalidValue)
|
4511
|
+
end
|
4512
|
+
|
4513
|
+
it "should raise an InvalidValue error when the Time class is used and when a bad database timezone is used when attempting to convert timestamps" do
|
4514
|
+
Sequel.database_timezone = :blah
|
4515
|
+
proc{Sequel.database_to_application_timestamp('2009-06-01 10:20:30')}.must_raise(Sequel::InvalidValue)
|
4516
|
+
end
|
4517
|
+
|
4486
4518
|
it "should raise an InvalidValue error when the DateTime class is used and when a bad application timezone is used when attempting to convert timestamps" do
|
4487
4519
|
Sequel.application_timezone = :blah
|
4488
4520
|
Sequel.datetime_class = DateTime
|
@@ -526,11 +526,35 @@ describe "Blockless Ruby Filters" do
|
|
526
526
|
dsc.new(@d.db).literal(Sequel.trim(:a)).must_equal 'trimFOO(lower(a))'
|
527
527
|
end
|
528
528
|
|
529
|
-
it "should endless ranges" do
|
529
|
+
it "should handle endless ranges" do
|
530
530
|
endless = eval('1..')
|
531
531
|
@d.l{x =~ endless}.must_equal '(x >= 1)'
|
532
532
|
@d.l(:x => endless).must_equal '(x >= 1)'
|
533
|
+
|
534
|
+
endless = eval('1...')
|
535
|
+
@d.l{x =~ endless}.must_equal '(x >= 1)'
|
536
|
+
@d.l(:x => endless).must_equal '(x >= 1)'
|
533
537
|
end if RUBY_VERSION >= '2.6'
|
538
|
+
|
539
|
+
it "should handle startless ranges" do
|
540
|
+
endless = eval('..1')
|
541
|
+
@d.l{x =~ endless}.must_equal '(x <= 1)'
|
542
|
+
@d.l(:x => endless).must_equal '(x <= 1)'
|
543
|
+
|
544
|
+
endless = eval('...1')
|
545
|
+
@d.l{x =~ endless}.must_equal '(x < 1)'
|
546
|
+
@d.l(:x => endless).must_equal '(x < 1)'
|
547
|
+
end if RUBY_VERSION >= '2.7'
|
548
|
+
|
549
|
+
it "should handle startless, endless ranges" do
|
550
|
+
endless = eval('nil..nil')
|
551
|
+
@d.l{x =~ endless}.must_equal '(1 = 1)'
|
552
|
+
@d.l(:x => endless).must_equal '(1 = 1)'
|
553
|
+
|
554
|
+
endless = eval('nil...nil')
|
555
|
+
@d.l{x =~ endless}.must_equal '(1 = 1)'
|
556
|
+
@d.l(:x => endless).must_equal '(1 = 1)'
|
557
|
+
end if RUBY_VERSION >= '2.7'
|
534
558
|
end
|
535
559
|
|
536
560
|
describe Sequel::SQL::VirtualRow do
|
data/spec/core/schema_spec.rb
CHANGED
@@ -240,6 +240,24 @@ describe "DB#create_table" do
|
|
240
240
|
@db.sqls.must_equal ["CREATE TABLE cats (id integer, name text, UNIQUE (name) DEFERRABLE INITIALLY IMMEDIATE)"]
|
241
241
|
end
|
242
242
|
|
243
|
+
it "should handle deferred unique column constraints" do
|
244
|
+
@db.create_table(:cats) do
|
245
|
+
integer :id, :unique=>true, :unique_deferrable=>true
|
246
|
+
integer :i, :unique=>true, :unique_deferrable=>:immediate
|
247
|
+
integer :j, :unique=>true, :unique_deferrable=>false
|
248
|
+
end
|
249
|
+
@db.sqls.must_equal ["CREATE TABLE cats (id integer UNIQUE DEFERRABLE INITIALLY DEFERRED, i integer UNIQUE DEFERRABLE INITIALLY IMMEDIATE, j integer UNIQUE NOT DEFERRABLE)"]
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should handle deferred primary key column constraints" do
|
253
|
+
@db.create_table(:cats) do
|
254
|
+
integer :id, :primary_key=>true, :primary_key_deferrable=>true
|
255
|
+
integer :i, :primary_key=>true, :primary_key_deferrable=>:immediate
|
256
|
+
integer :j, :primary_key=>true, :primary_key_deferrable=>false
|
257
|
+
end
|
258
|
+
@db.sqls.must_equal ["CREATE TABLE cats (id integer PRIMARY KEY DEFERRABLE INITIALLY DEFERRED, i integer PRIMARY KEY DEFERRABLE INITIALLY IMMEDIATE, j integer PRIMARY KEY NOT DEFERRABLE)"]
|
259
|
+
end
|
260
|
+
|
243
261
|
it "should accept unsigned definition" do
|
244
262
|
@db.create_table(:cats) do
|
245
263
|
integer :value, :unsigned => true
|
@@ -1777,17 +1795,21 @@ describe "Schema Parser" do
|
|
1777
1795
|
@db.schema(:text).first.last[:type].must_equal :string
|
1778
1796
|
@db.schema(:date).first.last[:type].must_equal :date
|
1779
1797
|
@db.schema(:datetime).first.last[:type].must_equal :datetime
|
1798
|
+
@db.schema(:smalldatetime).first.last[:type].must_equal :datetime
|
1780
1799
|
@db.schema(:timestamp).first.last[:type].must_equal :datetime
|
1781
1800
|
@db.schema(:"timestamp with time zone").first.last[:type].must_equal :datetime
|
1782
1801
|
@db.schema(:"timestamp without time zone").first.last[:type].must_equal :datetime
|
1783
1802
|
@db.schema(:time).first.last[:type].must_equal :time
|
1784
1803
|
@db.schema(:"time with time zone").first.last[:type].must_equal :time
|
1785
1804
|
@db.schema(:"time without time zone").first.last[:type].must_equal :time
|
1805
|
+
@db.schema(:bool).first.last[:type].must_equal :boolean
|
1786
1806
|
@db.schema(:boolean).first.last[:type].must_equal :boolean
|
1787
1807
|
@db.schema(:real).first.last[:type].must_equal :float
|
1788
1808
|
@db.schema(:float).first.last[:type].must_equal :float
|
1809
|
+
@db.schema(:"float unsigned").first.last[:type].must_equal :float
|
1789
1810
|
@db.schema(:double).first.last[:type].must_equal :float
|
1790
1811
|
@db.schema(:"double(1,2)").first.last[:type].must_equal :float
|
1812
|
+
@db.schema(:"double(1,2) unsigned").first.last[:type].must_equal :float
|
1791
1813
|
@db.schema(:"double precision").first.last[:type].must_equal :float
|
1792
1814
|
@db.schema(:number).first.last[:type].must_equal :decimal
|
1793
1815
|
@db.schema(:numeric).first.last[:type].must_equal :decimal
|
@@ -1801,6 +1823,8 @@ describe "Schema Parser" do
|
|
1801
1823
|
@db.schema(:nchar).first.last[:type].must_equal :string
|
1802
1824
|
@db.schema(:nvarchar).first.last[:type].must_equal :string
|
1803
1825
|
@db.schema(:ntext).first.last[:type].must_equal :string
|
1826
|
+
@db.schema(:clob).first.last[:type].must_equal :string
|
1827
|
+
@db.schema(:ntext).first.last[:type].must_equal :string
|
1804
1828
|
@db.schema(:smalldatetime).first.last[:type].must_equal :datetime
|
1805
1829
|
@db.schema(:binary).first.last[:type].must_equal :blob
|
1806
1830
|
@db.schema(:varbinary).first.last[:type].must_equal :blob
|
@@ -5,8 +5,8 @@ describe "class_table_inheritance plugin" do
|
|
5
5
|
@db = Sequel.mock(:numrows=>1, :autoid=>proc{|sql| 1})
|
6
6
|
def @db.supports_schema_parsing?() true end
|
7
7
|
def @db.schema(table, opts={})
|
8
|
-
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
|
9
|
-
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}] ],
|
8
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string, :allow_null=>false}], [:kind, {:type=>:string}]],
|
9
|
+
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer, :allow_null=>false}] ],
|
10
10
|
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
|
11
11
|
:staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
|
12
12
|
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
@@ -23,7 +23,9 @@ describe "class_table_inheritance plugin" do
|
|
23
23
|
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
base = Sequel::Model(@db)
|
27
|
+
base.plugin :auto_validations if @use_auto_validations
|
28
|
+
class ::Employee < base
|
27
29
|
def _save_refresh; @values[:id] = 1 end
|
28
30
|
def self.columns
|
29
31
|
dataset.columns || dataset.opts[:from].first.expression.columns
|
@@ -114,6 +116,26 @@ describe "class_table_inheritance plugin" do
|
|
114
116
|
"SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN ('Ceo'))) AS employees WHERE (id = 1) LIMIT 1"]
|
115
117
|
end
|
116
118
|
|
119
|
+
describe "with auto_validations plugin" do
|
120
|
+
before(:all) do
|
121
|
+
@use_auto_validations = true
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should work" do
|
125
|
+
e = Employee.new
|
126
|
+
e.valid?.must_equal false
|
127
|
+
e.errors.must_equal(:name=>["is not present"])
|
128
|
+
|
129
|
+
e = Manager.new
|
130
|
+
e.valid?.must_equal false
|
131
|
+
e.errors.must_equal(:name=>["is not present"], :num_staff=>["is not present"])
|
132
|
+
|
133
|
+
e = Executive.new
|
134
|
+
e.valid?.must_equal false
|
135
|
+
e.errors.must_equal(:name=>["is not present"], :num_staff=>["is not present"])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
117
139
|
it "should return rows with the current class if sti_key is nil" do
|
118
140
|
Employee.plugin :class_table_inheritance
|
119
141
|
Employee.dataset.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}, {:kind=>'Intern'}]).all.map{|x| x.class}.must_equal [Employee, Employee, Employee, Employee, Employee, Employee]
|
@@ -232,11 +254,11 @@ describe "class_table_inheritance plugin" do
|
|
232
254
|
end
|
233
255
|
|
234
256
|
it "should include schema for columns for tables for ancestor classes" do
|
235
|
-
Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
|
236
|
-
Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer})
|
237
|
-
Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer})
|
238
|
-
Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
|
239
|
-
Intern.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
|
257
|
+
Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string})
|
258
|
+
Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer, :allow_null=>false})
|
259
|
+
Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer, :allow_null=>false}, :num_managers=>{:type=>:integer})
|
260
|
+
Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
|
261
|
+
Intern.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string})
|
240
262
|
end
|
241
263
|
|
242
264
|
it "should use the correct primary key (which should have the same name in all subclasses)" do
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative "spec_helper"
|
2
2
|
|
3
|
-
if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby')
|
3
|
+
if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') || (RUBY_ENGINE == 'jruby' && (JRUBY_VERSION >= '9.3' || (JRUBY_VERSION.match(/\A9\.2\.(\d+)/) && $1.to_i >= 7)))
|
4
4
|
Sequel.extension :core_refinements, :pg_array, :pg_hstore, :pg_row, :pg_range, :pg_row_ops, :pg_range_ops, :pg_array_ops, :pg_hstore_ops, :pg_json, :pg_json_ops
|
5
5
|
using Sequel::CoreRefinements
|
6
6
|
|
@@ -22,6 +22,28 @@ describe Sequel::Model, "hook_class_methods plugin" do
|
|
22
22
|
hooks.values.all?(&:frozen?).must_equal true
|
23
23
|
end
|
24
24
|
|
25
|
+
deprecated ".hook_blocks method should yield each hook block" do
|
26
|
+
c = model_class.call Sequel::Model
|
27
|
+
a = []
|
28
|
+
c.hook_blocks(:before_save){|b| a << b}
|
29
|
+
a.must_equal []
|
30
|
+
|
31
|
+
pr = proc{adds << 'hi'}
|
32
|
+
c.before_save(&pr)
|
33
|
+
a = []
|
34
|
+
c.hook_blocks(:before_save){|b| a << b}
|
35
|
+
a.must_equal [pr]
|
36
|
+
|
37
|
+
c.before_save(&pr)
|
38
|
+
a = []
|
39
|
+
c.hook_blocks(:before_save){|b| a << b}
|
40
|
+
a.must_equal [pr, pr]
|
41
|
+
|
42
|
+
a = []
|
43
|
+
c.hook_blocks(:after_save){|b| a << b}
|
44
|
+
a.must_equal []
|
45
|
+
end
|
46
|
+
|
25
47
|
it "should be definable using a block" do
|
26
48
|
adds = []
|
27
49
|
c = model_class.call Sequel::Model do
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe "insert_conflict plugin" do
|
4
|
+
def model_class(adapter)
|
5
|
+
db = Sequel.mock(:host=>adapter, :fetch=>{:id=>1, :s=>2}, :autoid=>1)
|
6
|
+
db.extend_datasets{def quote_identifiers?; false end}
|
7
|
+
model = Class.new(Sequel::Model)
|
8
|
+
model.dataset = db[:t]
|
9
|
+
model.columns :id, :s, :o
|
10
|
+
model.plugin :insert_conflict
|
11
|
+
db.sqls
|
12
|
+
model
|
13
|
+
end
|
14
|
+
|
15
|
+
def model_class_plugin_first(adapter)
|
16
|
+
model = Class.new(Sequel::Model)
|
17
|
+
model.plugin :insert_conflict
|
18
|
+
model = Class.new(model)
|
19
|
+
db = Sequel.mock(:host=>adapter, :fetch=>{:id=>1, :s=>2}, :autoid=>1)
|
20
|
+
db.extend_datasets{def quote_identifiers?; false end}
|
21
|
+
model.dataset = db[:t]
|
22
|
+
model.columns :id, :s, :o
|
23
|
+
db.sqls
|
24
|
+
model
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should use INSERT ON CONFLICT when inserting on PostgreSQL" do
|
28
|
+
model = model_class(:postgres)
|
29
|
+
model.new(:s=>'A', :o=>1).insert_conflict.save
|
30
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT DO NOTHING RETURNING *"]
|
31
|
+
|
32
|
+
model.new(:s=>'A', :o=>1).insert_conflict(:target=>:s, :update => {:o => Sequel[:excluded][:o]}).save
|
33
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT (s) DO UPDATE SET o = excluded.o RETURNING *"]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should use INSERT ON CONFLICT when inserting on SQLITE" do
|
37
|
+
model = model_class(:sqlite)
|
38
|
+
model.new(:s=>'A', :o=>1).insert_conflict.save
|
39
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT DO NOTHING",
|
40
|
+
"SELECT * FROM t WHERE (id = 1) LIMIT 1"]
|
41
|
+
|
42
|
+
model.new(:s=>'A', :o=>1).insert_conflict(:target=>:s, :update => {:o => Sequel[:excluded][:o]}).save
|
43
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT (s) DO UPDATE SET o = excluded.o",
|
44
|
+
"SELECT * FROM t WHERE (id = 2) LIMIT 1"]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should raise Error if calling insert_conflict on a model instance that isn't new" do
|
48
|
+
m = model_class(:postgres).load(:s=>'A', :o=>1)
|
49
|
+
proc{m.insert_conflict}.must_raise Sequel::Error
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should raise if loading plugin into a model class with a dataset that doesn't support insert_conflict" do
|
53
|
+
model = Class.new(Sequel::Model)
|
54
|
+
model.dataset = Sequel.mock[:t]
|
55
|
+
proc{model.plugin :insert_conflict}.must_raise Sequel::Error
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should work if loading into a model class without a dataset on PostgreSQL" do
|
59
|
+
model = model_class_plugin_first(:postgres)
|
60
|
+
model.new(:s=>'A', :o=>1).insert_conflict.save
|
61
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT DO NOTHING RETURNING *"]
|
62
|
+
|
63
|
+
model.new(:s=>'A', :o=>1).insert_conflict(:target=>:s, :update => {:o => Sequel[:excluded][:o]}).save
|
64
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT (s) DO UPDATE SET o = excluded.o RETURNING *"]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should work if loading into a model class without a dataset on SQLITE" do
|
68
|
+
model = model_class_plugin_first(:sqlite)
|
69
|
+
model.new(:s=>'A', :o=>1).insert_conflict.save
|
70
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT DO NOTHING",
|
71
|
+
"SELECT * FROM t WHERE (id = 1) LIMIT 1"]
|
72
|
+
|
73
|
+
model.new(:s=>'A', :o=>1).insert_conflict(:target=>:s, :update => {:o => Sequel[:excluded][:o]}).save
|
74
|
+
model.db.sqls.must_equal ["INSERT INTO t (s, o) VALUES ('A', 1) ON CONFLICT (s) DO UPDATE SET o = excluded.o",
|
75
|
+
"SELECT * FROM t WHERE (id = 2) LIMIT 1"]
|
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
|
103
|
+
end
|