sequel 5.19.0 → 5.24.0

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