sequel 5.19.0 → 5.24.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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +102 -0
  3. data/doc/dataset_filtering.rdoc +15 -0
  4. data/doc/opening_databases.rdoc +5 -1
  5. data/doc/release_notes/5.20.0.txt +89 -0
  6. data/doc/release_notes/5.21.0.txt +87 -0
  7. data/doc/release_notes/5.22.0.txt +48 -0
  8. data/doc/release_notes/5.23.0.txt +56 -0
  9. data/doc/release_notes/5.24.0.txt +56 -0
  10. data/doc/sharding.rdoc +2 -0
  11. data/doc/testing.rdoc +1 -0
  12. data/doc/transactions.rdoc +38 -0
  13. data/lib/sequel/adapters/ado.rb +27 -19
  14. data/lib/sequel/adapters/jdbc.rb +7 -1
  15. data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
  18. data/lib/sequel/adapters/mysql2.rb +2 -3
  19. data/lib/sequel/adapters/shared/mssql.rb +7 -7
  20. data/lib/sequel/adapters/shared/postgres.rb +37 -19
  21. data/lib/sequel/adapters/shared/sqlite.rb +27 -3
  22. data/lib/sequel/adapters/sqlite.rb +1 -1
  23. data/lib/sequel/adapters/tinytds.rb +12 -0
  24. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
  25. data/lib/sequel/database/logging.rb +7 -1
  26. data/lib/sequel/database/query.rb +1 -1
  27. data/lib/sequel/database/schema_generator.rb +12 -3
  28. data/lib/sequel/database/schema_methods.rb +2 -0
  29. data/lib/sequel/database/transactions.rb +57 -5
  30. data/lib/sequel/dataset.rb +4 -2
  31. data/lib/sequel/dataset/actions.rb +3 -2
  32. data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
  33. data/lib/sequel/dataset/query.rb +5 -1
  34. data/lib/sequel/dataset/sql.rb +11 -7
  35. data/lib/sequel/extensions/named_timezones.rb +52 -8
  36. data/lib/sequel/extensions/pg_array.rb +4 -0
  37. data/lib/sequel/extensions/pg_json.rb +387 -123
  38. data/lib/sequel/extensions/pg_range.rb +3 -2
  39. data/lib/sequel/extensions/pg_row.rb +3 -1
  40. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  41. data/lib/sequel/extensions/server_block.rb +15 -4
  42. data/lib/sequel/model/associations.rb +35 -9
  43. data/lib/sequel/model/plugins.rb +104 -0
  44. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  45. data/lib/sequel/plugins/association_pks.rb +14 -4
  46. data/lib/sequel/plugins/association_proxies.rb +3 -2
  47. data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
  48. data/lib/sequel/plugins/composition.rb +13 -9
  49. data/lib/sequel/plugins/finder.rb +2 -2
  50. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  51. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  52. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  53. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
  54. data/lib/sequel/plugins/rcte_tree.rb +6 -0
  55. data/lib/sequel/plugins/static_cache.rb +8 -3
  56. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  57. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  58. data/lib/sequel/plugins/validation_class_methods.rb +5 -3
  59. data/lib/sequel/sql.rb +15 -3
  60. data/lib/sequel/timezones.rb +50 -11
  61. data/lib/sequel/version.rb +1 -1
  62. data/spec/adapters/mssql_spec.rb +24 -0
  63. data/spec/adapters/mysql_spec.rb +0 -5
  64. data/spec/adapters/postgres_spec.rb +319 -1
  65. data/spec/bin_spec.rb +1 -1
  66. data/spec/core/database_spec.rb +123 -2
  67. data/spec/core/dataset_spec.rb +33 -1
  68. data/spec/core/expression_filters_spec.rb +25 -1
  69. data/spec/core/schema_spec.rb +24 -0
  70. data/spec/extensions/class_table_inheritance_spec.rb +30 -8
  71. data/spec/extensions/core_refinements_spec.rb +1 -1
  72. data/spec/extensions/hook_class_methods_spec.rb +22 -0
  73. data/spec/extensions/insert_conflict_spec.rb +103 -0
  74. data/spec/extensions/migration_spec.rb +13 -0
  75. data/spec/extensions/named_timezones_spec.rb +109 -2
  76. data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
  77. data/spec/extensions/pg_json_spec.rb +218 -29
  78. data/spec/extensions/pg_range_spec.rb +76 -9
  79. data/spec/extensions/rcte_tree_spec.rb +6 -0
  80. data/spec/extensions/s_spec.rb +1 -1
  81. data/spec/extensions/schema_dumper_spec.rb +4 -2
  82. data/spec/extensions/server_block_spec.rb +38 -0
  83. data/spec/extensions/spec_helper.rb +8 -1
  84. data/spec/extensions/static_cache_cache_spec.rb +35 -0
  85. data/spec/integration/dataset_test.rb +25 -9
  86. data/spec/integration/plugin_test.rb +42 -0
  87. data/spec/integration/schema_test.rb +7 -2
  88. data/spec/integration/transaction_test.rb +50 -0
  89. data/spec/model/associations_spec.rb +84 -4
  90. data/spec/model/plugins_spec.rb +111 -0
  91. metadata +16 -2
@@ -17,6 +17,7 @@ describe "pg_range extension" do
17
17
  end
18
18
 
19
19
  endless_range_support = RUBY_VERSION >= '2.6'
20
+ startless_range_support = RUBY_VERSION >= '2.7'
20
21
 
21
22
  it "should set up conversion procs correctly" do
22
23
  cp = @db.conversion_procs
@@ -52,8 +53,19 @@ describe "pg_range extension" do
52
53
 
53
54
  it "should literalize endless Range instances to strings correctly" do
54
55
  @db.literal(eval('1..')).must_equal "'[1,]'"
56
+ @db.literal(eval('1...')).must_equal "'[1,)'"
55
57
  end if endless_range_support
56
58
 
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)'"
62
+ end if startless_range_support
63
+
64
+ it "should literalize startless, endless Range instances to strings correctly" do
65
+ @db.literal(eval('nil..nil')).must_equal "'[,]'"
66
+ @db.literal(eval('nil...nil')).must_equal "'[,)'"
67
+ end if startless_range_support
68
+
57
69
  it "should literalize PGRange instances to strings correctly" do
58
70
  @db.literal(@R.new(1, 2)).must_equal "'[1,2]'"
59
71
  @db.literal(@R.new(true, false)).must_equal "'[true,false]'"
@@ -79,8 +91,19 @@ describe "pg_range extension" do
79
91
 
80
92
  it "should support using endless Range instances as bound variables" do
81
93
  @db.bound_variable_arg(eval('1..'), nil).must_equal "[1,]"
94
+ @db.bound_variable_arg(eval('1...'), nil).must_equal "[1,)"
82
95
  end if endless_range_support
83
96
 
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)"
100
+ end if startless_range_support
101
+
102
+ it "should support using startless, endless Range instances as bound variables" do
103
+ @db.bound_variable_arg(eval('nil..nil'), nil).must_equal "[,]"
104
+ @db.bound_variable_arg(eval('nil...nil'), nil).must_equal "[,)"
105
+ end if startless_range_support
106
+
84
107
  it "should support using PGRange instances as bound variables" do
85
108
  @db.bound_variable_arg(@R.new(1, 2), nil).must_equal "[1,2]"
86
109
  end
@@ -406,12 +429,12 @@ describe "pg_range extension" do
406
429
  @R.new(nil, nil).wont_equal @R.new(nil, nil, :empty=>true)
407
430
  end
408
431
 
409
- it "should only consider empty PGRanges equal if they have the same bounds" do
432
+ it "should only consider PGRanges equal if they have the same bounds" do
410
433
  @R.new(1, 2).must_equal @R.new(1, 2)
411
434
  @R.new(1, 2).wont_equal @R.new(1, 3)
412
435
  end
413
436
 
414
- it "should only consider empty PGRanges equal if they have the same bound exclusions" do
437
+ it "should only consider PGRanges equal if they have the same bound exclusions" do
415
438
  @R.new(1, 2, :exclude_begin=>true).must_equal @R.new(1, 2, :exclude_begin=>true)
416
439
  @R.new(1, 2, :exclude_end=>true).must_equal @R.new(1, 2, :exclude_end=>true)
417
440
  @R.new(1, 2, :exclude_begin=>true).wont_equal @R.new(1, 2, :exclude_end=>true)
@@ -427,16 +450,39 @@ describe "pg_range extension" do
427
450
 
428
451
  it "should not consider a PGRange equal with a Range if it can't be expressed as a range" do
429
452
  @R.new(nil, nil).wont_be :==, (1..2)
453
+ if startless_range_support
454
+ @R.new(nil, nil, :exclude_begin=>true).wont_be :==, eval('nil..nil')
455
+ end
430
456
  end
431
457
 
432
458
  it "should consider PGRanges equal with a endless Range they represent" do
433
459
  @R.new(1, nil).must_be :==, eval('1..')
434
- end if endless_range_support
435
-
436
- it "should not consider a PGRange equal with a Range if it can't be expressed as a range" do
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..')
437
463
  @R.new(1, nil).wont_be :==, eval('2..')
464
+ @R.new(1, nil, :exclude_end=>true).wont_be :==, eval('2...')
438
465
  end if endless_range_support
439
466
 
467
+ 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')
474
+ end if startless_range_support
475
+
476
+ it "should consider PGRanges equal with a startless, endless Range they represent" do
477
+ @R.new(nil, nil).must_be :==, eval('nil..nil')
478
+ @R.new(nil, nil, :exclude_end=>true).must_be :==, eval('nil...nil')
479
+ @R.new(nil, nil).wont_be :==, eval('nil...nil')
480
+ @R.new(nil, nil, :exclude_end=>true).wont_be :==, eval('nil..nil')
481
+ @R.new(nil, nil).wont_be :==, eval('nil..1')
482
+ @R.new(nil, nil).wont_be :==, eval('1..nil')
483
+ @R.new(1, nil).wont_be :==, eval('nil..nil')
484
+ end if startless_range_support
485
+
440
486
  it "should not consider a PGRange equal to other objects" do
441
487
  @R.new(nil, nil).wont_equal 1
442
488
  end
@@ -444,7 +490,6 @@ describe "pg_range extension" do
444
490
  it "should have #=== be true if given an equal PGRange" do
445
491
  @R.new(1, 2).must_be :===, @R.new(1, 2)
446
492
  @R.new(1, 2).wont_be :===, @R.new(1, 3)
447
-
448
493
  end
449
494
 
450
495
  it "should have #=== be true if it would be true for the Range represented by the PGRange" do
@@ -453,7 +498,7 @@ describe "pg_range extension" do
453
498
  end
454
499
 
455
500
  it "should have #=== be false if the PGRange cannot be represented by a Range" do
456
- @R.new(nil, nil).wont_be :===, 1.5
501
+ @R.new(1, 2, :exclude_begin=>true).wont_be :===, 1.5
457
502
  end
458
503
 
459
504
  it "should have #empty? indicate whether the range is empty" do
@@ -471,7 +516,6 @@ describe "pg_range extension" do
471
516
  end
472
517
 
473
518
  it "should have #to_range raise an exception if the PGRange cannot be represented by a Range" do
474
- proc{@R.new(nil, 1).to_range}.must_raise(Sequel::Error)
475
519
  proc{@R.new(0, 1, :exclude_begin=>true).to_range}.must_raise(Sequel::Error)
476
520
  proc{@R.empty.to_range}.must_raise(Sequel::Error)
477
521
  end
@@ -488,6 +532,22 @@ describe "pg_range extension" do
488
532
  proc{@R.new(1, nil).to_range}.must_raise(Sequel::Error)
489
533
  end unless endless_range_support
490
534
 
535
+ it "should have #to_range return the represented range for startless ranges" do
536
+ @R.new(nil, 1).to_range.must_be :==, eval('..1')
537
+ end if startless_range_support
538
+
539
+ it "should have #to_range raise an exception for startless ranges" do
540
+ proc{@R.new(nil, 1).to_range}.must_raise(Sequel::Error)
541
+ end unless startless_range_support
542
+
543
+ it "should have #to_range return the represented range for startless, endless ranges" do
544
+ @R.new(nil, nil).to_range.must_be :==, eval('nil..nil')
545
+ end if startless_range_support
546
+
547
+ it "should have #to_range raise an exception for startless, endless ranges" do
548
+ proc{@R.new(nil, nil).to_range}.must_raise(Sequel::Error)
549
+ end unless startless_range_support
550
+
491
551
  it "should have #to_range cache the returned value" do
492
552
  @r1.to_range.must_be_same_as(@r1.to_range)
493
553
  end
@@ -507,7 +567,6 @@ describe "pg_range extension" do
507
567
  end
508
568
 
509
569
  it "should have #valid_ruby_range? return false if the PGRange cannot be represented as a Range" do
510
- @R.new(nil, 1).valid_ruby_range?.must_equal false
511
570
  @R.new(0, 1, :exclude_begin=>true).valid_ruby_range?.must_equal false
512
571
  @R.empty.valid_ruby_range?.must_equal false
513
572
  end
@@ -515,5 +574,13 @@ describe "pg_range extension" do
515
574
  it "should have #valid_ruby_range return #{endless_range_support} for endless ranges" do
516
575
  @R.new(1, nil).valid_ruby_range?.must_equal(endless_range_support)
517
576
  end
577
+
578
+ it "should have #valid_ruby_range return #{startless_range_support} for endless ranges" do
579
+ @R.new(nil, 1).valid_ruby_range?.must_equal(startless_range_support)
580
+ end
581
+
582
+ it "should have #valid_ruby_range return #{startless_range_support} for startless, endless ranges" do
583
+ @R.new(nil, nil).valid_ruby_range?.must_equal(startless_range_support)
584
+ end
518
585
  end
519
586
  end
@@ -254,6 +254,12 @@ describe Sequel::Model, "rcte_tree" do
254
254
  @db.sqls.must_equal ["SELECT * FROM nodes",
255
255
  'WITH t AS (SELECT parent_id AS x_root_x, nodes.* FROM nodes WHERE ((parent_id IN (2, 6, 7)) AND (i = 1)) UNION ALL SELECT t.x_root_x, nodes.* FROM nodes INNER JOIN t ON (t.id = nodes.parent_id) WHERE (i = 1)) SELECT * FROM t AS nodes WHERE (i = 1)']
256
256
  end
257
+
258
+ it "should disallow eager graphing of ancestors and descendants" do
259
+ @c.plugin :rcte_tree
260
+ proc{@c.eager_graph(:ancestors)}.must_raise Sequel::Error
261
+ proc{@c.eager_graph(:descendants)}.must_raise Sequel::Error
262
+ end
257
263
  end
258
264
 
259
265
  describe Sequel::Model, "rcte_tree with composite keys" do
@@ -29,7 +29,7 @@ describe "s extension as refinement" do
29
29
  end
30
30
 
31
31
 
32
- if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') # || (RUBY_VERSION >= '2.3.0' && RUBY_ENGINE == 'jruby')
32
+ 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)))
33
33
  using Sequel::S
34
34
 
35
35
  describe "s extension as refinement" do
@@ -800,14 +800,16 @@ END_MIG
800
800
  it "should convert mysql types to ruby types" do
801
801
  def @d.schema(t, *o)
802
802
  i = 0
803
- ['double(15,2)', 'double(7,1) unsigned'].map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
803
+ ['float unsigned', 'double(15,2)', 'double(7,1) unsigned'].map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
804
804
  end
805
805
  @d.dump_table_schema(:x).must_equal((<<END_MIG).chomp)
806
806
  create_table(:x) do
807
807
  Float :c1
808
808
  Float :c2
809
+ Float :c3
809
810
 
810
- check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:c2), 0)
811
+ check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:c1), 0)
812
+ check Sequel::SQL::BooleanExpression.new(:>=, Sequel::SQL::Identifier.new(:c3), 0)
811
813
  end
812
814
  END_MIG
813
815
  end
@@ -95,3 +95,41 @@ describe "Database#with_server multi threaded" do
95
95
  end
96
96
  end
97
97
 
98
+ describe "Database#with_server with invalid servers" do
99
+ def sqls(server)
100
+ @db.with_server(server) do
101
+ @db[:t].all
102
+ @db[:t].insert
103
+ @db[:t].update(:a=>1)
104
+ @db[:t].delete
105
+ end
106
+ @db.sqls
107
+ end
108
+
109
+ it "when single threaded and no servers_hash" do
110
+ @db = Sequel.mock(:single_threaded=>true, :servers=>{:a=>{}}).extension(:server_block)
111
+ sqls(:a).must_equal ["SELECT * FROM t -- a", "INSERT INTO t DEFAULT VALUES -- a", "UPDATE t SET a = 1 -- a", "DELETE FROM t -- a"]
112
+ sqls(:c).must_equal ["SELECT * FROM t", "INSERT INTO t DEFAULT VALUES", "UPDATE t SET a = 1", "DELETE FROM t"]
113
+ end
114
+
115
+ it "when multi-threaded and no servers_hash" do
116
+ @db = Sequel.mock(:servers=>{:a=>{}}).extension(:server_block)
117
+ sqls(:a).must_equal ["SELECT * FROM t -- a", "INSERT INTO t DEFAULT VALUES -- a", "UPDATE t SET a = 1 -- a", "DELETE FROM t -- a"]
118
+ sqls(:c).must_equal ["SELECT * FROM t", "INSERT INTO t DEFAULT VALUES", "UPDATE t SET a = 1", "DELETE FROM t"]
119
+ end
120
+
121
+ it "when single threaded and servers_hash" do
122
+ @db = Sequel.mock(:single_threaded=>true, :servers=>{:a=>{}, :b=>{}}, :servers_hash=>Hash.new{|_,k| raise}.merge!(:c=>:b)).extension(:server_block)
123
+ sqls(:a).must_equal ["SELECT * FROM t -- a", "INSERT INTO t DEFAULT VALUES -- a", "UPDATE t SET a = 1 -- a", "DELETE FROM t -- a"]
124
+ sqls(:c).must_equal ["SELECT * FROM t -- b", "INSERT INTO t DEFAULT VALUES -- b", "UPDATE t SET a = 1 -- b", "DELETE FROM t -- b"]
125
+ proc{sqls(:d)}.must_raise(RuntimeError)
126
+ end
127
+
128
+ it "when multi-threaded and servers_hash" do
129
+ @db = Sequel.mock(:servers=>{:a=>{}, :b=>{}}, :servers_hash=>Hash.new{|_,k| raise}.merge!(:c=>:b)).extension(:server_block)
130
+ sqls(:a).must_equal ["SELECT * FROM t -- a", "INSERT INTO t DEFAULT VALUES -- a", "UPDATE t SET a = 1 -- a", "DELETE FROM t -- a"]
131
+ sqls(:c).must_equal ["SELECT * FROM t -- b", "INSERT INTO t DEFAULT VALUES -- b", "UPDATE t SET a = 1 -- b", "DELETE FROM t -- b"]
132
+ proc{sqls(:d)}.must_raise(RuntimeError)
133
+ end
134
+ end
135
+
@@ -16,6 +16,11 @@ require_relative "../../lib/sequel"
16
16
 
17
17
  require_relative '../deprecation_helper'
18
18
 
19
+ if ENV['SEQUEL_TZINFO_VERSION']
20
+ # Allow forcing specific TZInfo versions, useful when testing
21
+ gem 'tzinfo', ENV['SEQUEL_TZINFO_VERSION']
22
+ end
23
+
19
24
  begin
20
25
  # Attempt to load ActiveSupport blank extension and inflector first, so Sequel
21
26
  # can override them.
@@ -26,7 +31,9 @@ rescue LoadError
26
31
  nil
27
32
  end
28
33
 
29
- Sequel.extension :core_refinements if RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby'
34
+ 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)))
35
+ Sequel.extension :core_refinements
36
+ end
30
37
 
31
38
  class << Sequel::Model
32
39
  attr_writer :db_schema
@@ -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
@@ -957,7 +957,7 @@ if DB.dataset.supports_window_functions?
957
957
  must_equal [{:sum=>110, :id=>1}, {:sum=>1100, :id=>2}, {:sum=>11000, :id=>3}, {:sum=>110000, :id=>4}, {:sum=>100000, :id=>5}, {:sum=>nil, :id=>6}]
958
958
  end
959
959
 
960
- cspecify "should give correct results for aggregate window functions with offsets for RANGES", :mssql, :sqlite, [proc{DB.server_version < 110000}, :postgres] do
960
+ cspecify "should give correct results for aggregate window functions with offsets for RANGES", :mssql, [proc{DB.sqlite_version < 32800}, :sqlite], [proc{DB.server_version < 110000}, :postgres] do
961
961
  @ds.select(:id){sum(:amount).over(:order=>:group_id, :frame=>{:type=>:range, :start=>1}).as(:sum)}.all.
962
962
  must_equal [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
963
963
  @ds.select(:id){sum(:amount).over(:order=>:group_id, :frame=>{:type=>:range, :start=>0, :end=>1}).as(:sum)}.all.
@@ -1109,6 +1109,12 @@ describe "Sequel::Dataset#import and #multi_insert :return=>:primary_key " do
1109
1109
  @ds.order(:id).map([:id, :i]).must_equal [[1, 10], [2, 20], [3, 30], [4, 40], [5, 50], [6, 60]]
1110
1110
  end
1111
1111
 
1112
+ it "should handle dataset with row_proc" do
1113
+ ds = @ds.with_row_proc(lambda{|h| Object.new})
1114
+ ds.multi_insert([{:i=>10}, {:i=>20}, {:i=>30}], :return=>:primary_key).must_equal [1, 2, 3]
1115
+ ds.import([:i], [[40], [50], [60]], :return=>:primary_key).must_equal [4, 5, 6]
1116
+ end
1117
+
1112
1118
  it "should return primary key values when :slice is used" do
1113
1119
  @ds.multi_insert([{:i=>10}, {:i=>20}, {:i=>30}], :return=>:primary_key, :slice=>2).must_equal [1, 2, 3]
1114
1120
  @ds.import([:i], [[40], [50], [60]], :return=>:primary_key, :slice=>2).must_equal [4, 5, 6]
@@ -1545,15 +1551,25 @@ describe "Sequel::Dataset DSL support" do
1545
1551
  @ds.exclude(:a=>[20, 10]).all.must_equal []
1546
1552
  end
1547
1553
 
1548
- it "should work with ranges as hash values" do
1554
+ it "should work with endless ranges as hash values" do
1549
1555
  @ds.insert(20, 10)
1550
- @ds.filter(:a=>(10..30)).all.must_equal [{:a=>20, :b=>10}]
1551
- @ds.filter(:a=>(25..30)).all.must_equal []
1552
- @ds.filter(:a=>(10..15)).all.must_equal []
1553
- @ds.exclude(:a=>(10..30)).all.must_equal []
1554
- @ds.exclude(:a=>(25..30)).all.must_equal [{:a=>20, :b=>10}]
1555
- @ds.exclude(:a=>(10..15)).all.must_equal [{:a=>20, :b=>10}]
1556
- end
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}]
1560
+ end if RUBY_VERSION >= '2.6'
1561
+
1562
+ it "should work with startless ranges as hash values" do
1563
+ @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 []
1570
+
1571
+ @ds.filter(:a=>eval('nil..nil')).all.must_equal [{:a=>20, :b=>10}]
1572
+ end if RUBY_VERSION >= '2.7'
1557
1573
 
1558
1574
  it "should work with CASE statements" do
1559
1575
  @ds.insert(20, 10)
@@ -30,6 +30,7 @@ describe "Class Table Inheritance Plugin" do
30
30
  end
31
31
  class ::Manager < Employee
32
32
  one_to_many :staff_members, :class=>:Staff
33
+ one_to_one :first_staff_member, :clone=>:staff_members, :order=>:id
33
34
  end
34
35
  class ::Executive < Manager
35
36
  end
@@ -150,6 +151,8 @@ describe "Class Table Inheritance Plugin" do
150
151
  m = Staff.first.manager
151
152
  m.must_equal Manager[@i4]
152
153
  m.must_be_kind_of(Executive)
154
+ Staff.first.update(:manager => Manager[@i3])
155
+ Staff.first.manager.must_equal Manager[@i3]
153
156
  end
154
157
 
155
158
  it "should handle eagerly loading many_to_one relationships" do
@@ -164,6 +167,18 @@ describe "Class Table Inheritance Plugin" do
164
167
 
165
168
  it "should handle one_to_many relationships" do
166
169
  Executive.first(:name=>'Ex').staff_members.must_equal [Staff[@i2]]
170
+ i6 = @db[:employees].insert(:name=>'S2', :kind=>'Staff')
171
+ @db[:staff].insert(:id=>i6, :manager_id=>@i4)
172
+ Executive.first(:name=>'Ex').add_staff_member(i6)
173
+ Executive.first(:name=>'Ex').staff_members{|ds| ds.order(:id)}.must_equal [Staff[@i2], Staff[i6]]
174
+ end
175
+
176
+ it "should handle one_to_many relationships" do
177
+ Executive.first(:name=>'Ex').first_staff_member.must_equal Staff[@i2]
178
+ i6 = @db[:employees].insert(:name=>'S2', :kind=>'Staff')
179
+ @db[:staff].insert(:id=>i6, :manager_id=>@i4)
180
+ Executive.first(:name=>'Ex').first_staff_member = Staff[i6]
181
+ Executive.first(:name=>'Ex').staff_members.must_equal [Staff[i6]]
167
182
  end
168
183
 
169
184
  it "should handle eagerly loading one_to_many relationships" do
@@ -2379,3 +2394,30 @@ describe "string_agg extension" do
2379
2394
  @ds.select_group(:id).select_append(Sequel.string_agg(:s).order(:s).distinct.as(:v)).map([:id, :v]).must_equal [[1, 'a,b,c'], [2, 'aa,bb']]
2380
2395
  end
2381
2396
  end
2397
+
2398
+ describe "insert_conflict plugin" do
2399
+ before(:all) do
2400
+ @db = DB
2401
+ @db.create_table!(:ic_test) do
2402
+ primary_key :id
2403
+ String :s, :unique=>true
2404
+ Integer :o
2405
+ end
2406
+ @model = Class.new(Sequel::Model)
2407
+ @model.set_dataset @db[:ic_test]
2408
+ @model.plugin :insert_conflict
2409
+ end
2410
+ after(:all) do
2411
+ @db.drop_table?(:ic_test)
2412
+ end
2413
+
2414
+ it "should allow Model#insert_conflict to work" do
2415
+ ic_opts = {:target=>:s, :update => {:o => Sequel[:excluded][:o]}}
2416
+ @model.new(:s=>'A', :o=>1).insert_conflict(ic_opts).save
2417
+ @model.select_order_map([:s, :o]).must_equal [['A', 1]]
2418
+ @model.new(:s=>'A', :o=>2).insert_conflict(ic_opts).save
2419
+ @model.select_order_map([:s, :o]).must_equal [['A', 2]]
2420
+ @model.new(:s=>'B', :o=>3).insert_conflict(ic_opts).save
2421
+ @model.select_order_map([:s, :o]).must_equal [['A', 2], ['B', 3]]
2422
+ end
2423
+ end if (DB.database_type == :postgres && DB.server_version >= 90500) || (DB.database_type == :sqlite && DB.sqlite_version >= 32400)
@@ -750,10 +750,15 @@ describe "Database schema modifiers" do
750
750
  @db.create_table!(:items) do
751
751
  primary_key :id
752
752
  Integer :i, :default=>20
753
+ Integer :j, :default=>10
754
+ String :s, :default=>'a'
753
755
  end
754
- @ds.insert(:i=>10)
756
+ @ds.insert(:i=>10, :j=>20, :s=>'b')
755
757
  @db.drop_column(:items, :i)
756
- @db.schema(:items, :reload=>true).map{|x| x.first}.must_equal [:id]
758
+ @db.schema(:items, :reload=>true).map{|x| x.first}.must_equal [:id, :j, :s]
759
+ @ds.first.must_equal(:id=>1, :j=>20, :s=>'b')
760
+ @ds.insert
761
+ @ds.first(:id=>2).must_equal(:id=>2, :j=>10, :s=>'a')
757
762
  end
758
763
 
759
764
  it "should remove foreign key columns from tables correctly" do