sequel 5.15.0 → 5.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +28 -0
- data/bin/sequel +4 -0
- data/doc/opening_databases.rdoc +1 -1
- data/doc/release_notes/5.16.0.txt +110 -0
- data/doc/transactions.rdoc +48 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +14 -28
- data/lib/sequel/adapters/mysql.rb +2 -6
- data/lib/sequel/adapters/shared/sqlite.rb +16 -2
- data/lib/sequel/database/transactions.rb +66 -13
- data/lib/sequel/dataset/sql.rb +12 -1
- data/lib/sequel/extensions/migration.rb +4 -3
- data/lib/sequel/model/associations.rb +15 -3
- data/lib/sequel/model/base.rb +5 -3
- data/lib/sequel/plugins/class_table_inheritance.rb +6 -4
- data/lib/sequel/plugins/nested_attributes.rb +4 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/bin_spec.rb +7 -0
- data/spec/core/database_spec.rb +98 -1
- data/spec/core/expression_filters_spec.rb +13 -1
- data/spec/extensions/class_table_inheritance_spec.rb +45 -1
- data/spec/extensions/migration_spec.rb +15 -4
- data/spec/extensions/nested_attributes_spec.rb +40 -0
- data/spec/extensions/pg_array_associations_spec.rb +8 -8
- data/spec/extensions/pg_array_ops_spec.rb +5 -5
- data/spec/extensions/pg_hstore_ops_spec.rb +9 -9
- data/spec/extensions/pg_row_ops_spec.rb +2 -2
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_agg_spec.rb +1 -1
- data/spec/integration/dataset_test.rb +1 -1
- data/spec/integration/transaction_test.rb +199 -0
- data/spec/model/associations_spec.rb +31 -0
- data/spec/model/base_spec.rb +49 -0
- metadata +5 -4
@@ -510,6 +510,46 @@ describe "NestedAttributes plugin" do
|
|
510
510
|
["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
|
511
511
|
end
|
512
512
|
|
513
|
+
it "should not attempt to validate nested attributes twice for one_to_one associations when creating them" do
|
514
|
+
@Artist.nested_attributes :first_album
|
515
|
+
validated = []
|
516
|
+
@Album.class_eval do
|
517
|
+
define_method(:validate) do
|
518
|
+
super()
|
519
|
+
validated << self
|
520
|
+
end
|
521
|
+
end
|
522
|
+
a = @Artist.new(:name=>'Ar', :first_album_attributes=>{:name=>'Al'})
|
523
|
+
@db.sqls.must_equal []
|
524
|
+
validated.length.must_equal 0
|
525
|
+
a.save
|
526
|
+
validated.length.must_equal 1
|
527
|
+
check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
|
528
|
+
"UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
|
529
|
+
"INSERT INTO albums (name, artist_id) VALUES ('Al', 1)")
|
530
|
+
end
|
531
|
+
|
532
|
+
it "should not clear reciprocal association before saving new one_to_one associated object" do
|
533
|
+
@Artist.one_to_one :first_album, :clone=>:first_album, :reciprocal=>:artist
|
534
|
+
@Artist.nested_attributes :first_album
|
535
|
+
assoc = []
|
536
|
+
@Album.class_eval do
|
537
|
+
define_method(:after_save) do
|
538
|
+
super()
|
539
|
+
assoc << associations[:artist]
|
540
|
+
end
|
541
|
+
end
|
542
|
+
a = @Artist.new(:name=>'Ar', :first_album_attributes=>{:name=>'Al'})
|
543
|
+
@db.sqls.must_equal []
|
544
|
+
assoc.must_be_empty
|
545
|
+
a.save
|
546
|
+
assoc.length.must_equal 1
|
547
|
+
assoc.first.must_be_kind_of(@Artist)
|
548
|
+
check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
|
549
|
+
"UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
|
550
|
+
"INSERT INTO albums (name, artist_id) VALUES ('Al', 1)")
|
551
|
+
end
|
552
|
+
|
513
553
|
it "should not save if nested attribute is not valid and should include nested attribute validation errors in the main object's validation errors" do
|
514
554
|
@Artist.class_eval do
|
515
555
|
def validate
|
@@ -143,10 +143,10 @@ describe Sequel::Model, "pg_array_associations" do
|
|
143
143
|
@c1.pg_array_to_many :tags, :clone=>:tags, :primary_key=>Sequel.*(:id, 3), :primary_key_method=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
|
144
144
|
@c2.many_to_pg_array :artists, :clone=>:artists, :primary_key=>Sequel.*(:id, 3), :primary_key_method=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
|
145
145
|
|
146
|
-
@c1.filter(:tags=>@o2).sql.must_equal "SELECT * FROM artists WHERE (artists.tag_ids[1:2] @> ARRAY[6]::integer[])"
|
146
|
+
@c1.filter(:tags=>@o2).sql.must_equal "SELECT * FROM artists WHERE ((artists.tag_ids)[1:2] @> ARRAY[6]::integer[])"
|
147
147
|
@c2.filter(:artists=>@o1).sql.must_equal "SELECT * FROM tags WHERE ((tags.id * 3) IN (3, 6, 9))"
|
148
|
-
@c1.filter(:tags=>@c2.where(:id=>1)).sql.must_equal "SELECT * FROM artists WHERE coalesce((artists.tag_ids[1:2] && (SELECT array_agg((tags.id * 3)) FROM tags WHERE (id = 1))), false)"
|
149
|
-
@c2.filter(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (EXISTS (SELECT 1 FROM (SELECT artists.tag_ids[1:2] AS key FROM artists WHERE (id = 1)) AS t1 WHERE ((tags.id * 3) = any(key))))"
|
148
|
+
@c1.filter(:tags=>@c2.where(:id=>1)).sql.must_equal "SELECT * FROM artists WHERE coalesce(((artists.tag_ids)[1:2] && (SELECT array_agg((tags.id * 3)) FROM tags WHERE (id = 1))), false)"
|
149
|
+
@c2.filter(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (EXISTS (SELECT 1 FROM (SELECT (artists.tag_ids)[1:2] AS key FROM artists WHERE (id = 1)) AS t1 WHERE ((tags.id * 3) = any(key))))"
|
150
150
|
end
|
151
151
|
|
152
152
|
it "should raise an error if associated model does not have a primary key, and :primary_key is not specified" do
|
@@ -168,7 +168,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
168
168
|
|
169
169
|
it "should support a :key_column option" do
|
170
170
|
@c2.many_to_pg_array :artists, :clone=>:artists, :key_column=>Sequel.pg_array(:tag_ids)[1..2], :key=>:tag2_ids
|
171
|
-
@o2.artists_dataset.sql.must_equal "SELECT * FROM artists WHERE (artists.tag_ids[1:2] @> ARRAY[2]::integer[])"
|
171
|
+
@o2.artists_dataset.sql.must_equal "SELECT * FROM artists WHERE ((artists.tag_ids)[1:2] @> ARRAY[2]::integer[])"
|
172
172
|
end
|
173
173
|
|
174
174
|
it "should support a :primary_key option" do
|
@@ -211,7 +211,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
211
211
|
@c1.pg_array_to_many :tags, :clone=>:tags, :dataset=>proc{Tag.where(:id=>tag_ids.map{|x| x*2})}
|
212
212
|
@c2.many_to_pg_array :artists, :clone=>:artists, :dataset=>proc{Artist.where(Sequel.pg_array(Sequel.pg_array(:tag_ids)[1..2]).contains([id]))}
|
213
213
|
@o1.tags_dataset.sql.must_equal "SELECT * FROM tags WHERE (id IN (2, 4, 6))"
|
214
|
-
@o2.artists_dataset.sql.must_equal "SELECT * FROM artists WHERE (tag_ids[1:2] @> ARRAY[2])"
|
214
|
+
@o2.artists_dataset.sql.must_equal "SELECT * FROM artists WHERE ((tag_ids)[1:2] @> ARRAY[2])"
|
215
215
|
end
|
216
216
|
|
217
217
|
it "should support a :limit option" do
|
@@ -274,7 +274,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
274
274
|
|
275
275
|
a = @c2.eager(:artists).all
|
276
276
|
a.must_equal [@o2]
|
277
|
-
@db.sqls.must_equal ["SELECT * FROM tags", "SELECT * FROM artists WHERE (artists.tag_ids[1:2] && ARRAY[6]::integer[])"]
|
277
|
+
@db.sqls.must_equal ["SELECT * FROM tags", "SELECT * FROM artists WHERE ((artists.tag_ids)[1:2] && ARRAY[6]::integer[])"]
|
278
278
|
a.first.artists.must_equal [@o1]
|
279
279
|
@db.sqls.must_equal []
|
280
280
|
end
|
@@ -461,13 +461,13 @@ describe Sequel::Model, "pg_array_associations" do
|
|
461
461
|
|
462
462
|
a = @c1.eager_graph(:tags).all
|
463
463
|
a.must_equal [@o1]
|
464
|
-
@db.sqls.must_equal ["SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON (artists.tag_ids[1:2] @> ARRAY[(tags.id * 3)])"]
|
464
|
+
@db.sqls.must_equal ["SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON ((artists.tag_ids)[1:2] @> ARRAY[(tags.id * 3)])"]
|
465
465
|
a.first.tags.must_equal [@o2]
|
466
466
|
@db.sqls.must_equal []
|
467
467
|
|
468
468
|
a = @c2.eager_graph(:artists).all
|
469
469
|
a.must_equal [@o2]
|
470
|
-
@db.sqls.must_equal ["SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON (artists.tag_ids[1:2] @> ARRAY[tags.id3])"]
|
470
|
+
@db.sqls.must_equal ["SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON ((artists.tag_ids)[1:2] @> ARRAY[tags.id3])"]
|
471
471
|
a.first.artists.must_equal [@o1]
|
472
472
|
@db.sqls.must_equal []
|
473
473
|
end
|
@@ -17,12 +17,12 @@ describe "Sequel::Postgres::ArrayOp" do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it "#[] should support subscript access" do
|
20
|
-
@db.literal(@a[1]).must_equal "a[1]"
|
21
|
-
@db.literal(@a[1][2]).must_equal "a[1][2]"
|
20
|
+
@db.literal(@a[1]).must_equal "(a)[1]"
|
21
|
+
@db.literal(@a[1][2]).must_equal "(a)[1][2]"
|
22
22
|
end
|
23
23
|
|
24
24
|
it "#[] with a range should return an ArrayOp" do
|
25
|
-
@db.literal(@a[1..2].any).must_equal "ANY(a[1:2])"
|
25
|
+
@db.literal(@a[1..2].any).must_equal "ANY((a)[1:2])"
|
26
26
|
end
|
27
27
|
|
28
28
|
it "#any should use the ANY method" do
|
@@ -52,12 +52,12 @@ describe "Sequel::Postgres::ArrayOp" do
|
|
52
52
|
|
53
53
|
it "#remove should remove the element from the array" do
|
54
54
|
@db.literal(@a.remove(1)).must_equal "array_remove(a, 1)"
|
55
|
-
@db.literal(@a.remove(1)[2]).must_equal "array_remove(a, 1)[2]"
|
55
|
+
@db.literal(@a.remove(1)[2]).must_equal "(array_remove(a, 1))[2]"
|
56
56
|
end
|
57
57
|
|
58
58
|
it "#remove should replace the element in the array with another" do
|
59
59
|
@db.literal(@a.replace(1, 2)).must_equal "array_replace(a, 1, 2)"
|
60
|
-
@db.literal(@a.replace(1, 2)[3]).must_equal "array_replace(a, 1, 2)[3]"
|
60
|
+
@db.literal(@a.replace(1, 2)[3]).must_equal "(array_replace(a, 1, 2))[3]"
|
61
61
|
end
|
62
62
|
|
63
63
|
it "#unshift should use the || operator in prepend mode" do
|
@@ -43,7 +43,7 @@ describe "Sequel::Postgres::HStoreOp" do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it "#[] should return a PGArrayOp if given an array" do
|
46
|
-
@ds.literal(@h[%w'a'][0]).must_equal "(h -> ARRAY['a'])[0]"
|
46
|
+
@ds.literal(@h[%w'a'][0]).must_equal "((h -> ARRAY['a']))[0]"
|
47
47
|
end
|
48
48
|
|
49
49
|
it "#[] should not return a PGArrayOp if given an array but pg_array_op is not supported" do
|
@@ -58,11 +58,11 @@ describe "Sequel::Postgres::HStoreOp" do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
it "#[] should return a PGArrayOp if given a PGArray" do
|
61
|
-
@ds.literal(@h[Sequel.pg_array(%w'a')][0]).must_equal "(h -> ARRAY['a'])[0]"
|
61
|
+
@ds.literal(@h[Sequel.pg_array(%w'a')][0]).must_equal "((h -> ARRAY['a']))[0]"
|
62
62
|
end
|
63
63
|
|
64
64
|
it "#[] should return a PGArrayOp if given a PGArrayOp" do
|
65
|
-
@ds.literal(@h[Sequel.pg_array_op(:a)][0]).must_equal "(h -> a)[0]"
|
65
|
+
@ds.literal(@h[Sequel.pg_array_op(:a)][0]).must_equal "((h -> a))[0]"
|
66
66
|
end
|
67
67
|
|
68
68
|
it "#[] should return a string expression" do
|
@@ -157,8 +157,8 @@ describe "Sequel::Postgres::HStoreOp" do
|
|
157
157
|
end
|
158
158
|
|
159
159
|
it "#keys and #akeys should return PGArrayOps" do
|
160
|
-
@ds.literal(@h.keys[0]).must_equal "akeys(h)[0]"
|
161
|
-
@ds.literal(@h.akeys[0]).must_equal "akeys(h)[0]"
|
160
|
+
@ds.literal(@h.keys[0]).must_equal "(akeys(h))[0]"
|
161
|
+
@ds.literal(@h.akeys[0]).must_equal "(akeys(h))[0]"
|
162
162
|
end
|
163
163
|
|
164
164
|
it "#populate should use the populate_record function" do
|
@@ -194,7 +194,7 @@ describe "Sequel::Postgres::HStoreOp" do
|
|
194
194
|
end
|
195
195
|
|
196
196
|
it "#to_array should return a PGArrayOp" do
|
197
|
-
@ds.literal(@h.to_array[0]).must_equal "hstore_to_array(h)[0]"
|
197
|
+
@ds.literal(@h.to_array[0]).must_equal "(hstore_to_array(h))[0]"
|
198
198
|
end
|
199
199
|
|
200
200
|
it "#to_matrix should use the hstore_to_matrix function" do
|
@@ -202,7 +202,7 @@ describe "Sequel::Postgres::HStoreOp" do
|
|
202
202
|
end
|
203
203
|
|
204
204
|
it "#to_matrix should return a PGArrayOp" do
|
205
|
-
@ds.literal(@h.to_matrix[0]).must_equal "hstore_to_matrix(h)[0]"
|
205
|
+
@ds.literal(@h.to_matrix[0]).must_equal "(hstore_to_matrix(h))[0]"
|
206
206
|
end
|
207
207
|
|
208
208
|
it "#values and #avals should use the avals function" do
|
@@ -211,8 +211,8 @@ describe "Sequel::Postgres::HStoreOp" do
|
|
211
211
|
end
|
212
212
|
|
213
213
|
it "#values and #avals should return PGArrayOps" do
|
214
|
-
@ds.literal(@h.values[0]).must_equal "avals(h)[0]"
|
215
|
-
@ds.literal(@h.avals[0]).must_equal "avals(h)[0]"
|
214
|
+
@ds.literal(@h.values[0]).must_equal "(avals(h))[0]"
|
215
|
+
@ds.literal(@h.avals[0]).must_equal "(avals(h))[0]"
|
216
216
|
end
|
217
217
|
|
218
218
|
it "should have Sequel.hstore_op return HStoreOp instances as-is" do
|
@@ -18,11 +18,11 @@ describe "Sequel::Postgres::PGRowOp" do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
it "#[] should support array access if not given an identifier" do
|
21
|
-
@db.literal(@a[:b][1]).must_equal "(a).b[1]"
|
21
|
+
@db.literal(@a[:b][1]).must_equal "((a).b)[1]"
|
22
22
|
end
|
23
23
|
|
24
24
|
it "#[] should be chainable with array access" do
|
25
|
-
@db.literal(@a[1][:b]).must_equal "(a[1]).b"
|
25
|
+
@db.literal(@a[1][:b]).must_equal "((a)[1]).b"
|
26
26
|
end
|
27
27
|
|
28
28
|
it "#splat should return a splatted argument inside parentheses" do
|
@@ -15,7 +15,7 @@ describe "Sequel sql_expr extension" do
|
|
15
15
|
@ds.literal(s+1).must_equal "(foo + 1)"
|
16
16
|
@ds.literal(s & true).must_equal "(foo AND 't')"
|
17
17
|
@ds.literal(s < 1).must_equal "(foo < 1)"
|
18
|
-
@ds.literal(s.sql_subscript(1)).must_equal "foo[1]"
|
18
|
+
@ds.literal(s.sql_subscript(1)).must_equal "(foo)[1]"
|
19
19
|
@ds.literal(s.like('a')).must_equal "(foo LIKE 'a' ESCAPE '\\')"
|
20
20
|
@ds.literal(s.as(:a)).must_equal "foo AS a"
|
21
21
|
@ds.literal(s.cast(Integer)).must_equal "CAST(foo AS integer)"
|
@@ -85,6 +85,6 @@ describe "string_agg extension" do
|
|
85
85
|
ds.literal(@sa1.cast(:b)).must_equal "CAST(string_agg(c, ',') AS b)"
|
86
86
|
ds.literal(@sa1.desc).must_equal "string_agg(c, ',') DESC"
|
87
87
|
ds.literal(@sa1 =~ /a/).must_equal "(string_agg(c, ',') ~ 'a')"
|
88
|
-
ds.literal(@sa1.sql_subscript(1)).must_equal "string_agg(c, ',')[1]"
|
88
|
+
ds.literal(@sa1.sql_subscript(1)).must_equal "(string_agg(c, ','))[1]"
|
89
89
|
end
|
90
90
|
end
|
@@ -953,7 +953,7 @@ if DB.dataset.supports_window_functions?
|
|
953
953
|
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}]
|
954
954
|
end
|
955
955
|
|
956
|
-
cspecify "should give correct results for aggregate window functions with offsets for RANGES", :mssql, [proc{DB.server_version < 110000}, :postgres] do
|
956
|
+
cspecify "should give correct results for aggregate window functions with offsets for RANGES", :mssql, :sqlite, [proc{DB.server_version < 110000}, :postgres] do
|
957
957
|
@ds.select(:id){sum(:amount).over(:order=>:group_id, :frame=>{:type=>:range, :start=>1}).as(:sum)}.all.
|
958
958
|
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}]
|
959
959
|
@ds.select(:id){sum(:amount).over(:order=>:group_id, :frame=>{:type=>:range, :start=>0, :end=>1}).as(:sum)}.all.
|
@@ -82,8 +82,207 @@ describe "Database transactions" do
|
|
82
82
|
end}.must_raise(Interrupt)
|
83
83
|
@d.count.must_equal 0
|
84
84
|
end
|
85
|
+
|
86
|
+
it "should support rollback_on_exit" do
|
87
|
+
@db.transaction do
|
88
|
+
@d.insert(:name => 'abc', :value => 1)
|
89
|
+
@db.rollback_on_exit
|
90
|
+
end
|
91
|
+
@d.must_be_empty
|
92
|
+
|
93
|
+
catch(:foo) do
|
94
|
+
@db.transaction do
|
95
|
+
@d.insert(:name => 'abc', :value => 1)
|
96
|
+
@db.rollback_on_exit
|
97
|
+
throw :foo
|
98
|
+
end
|
99
|
+
end
|
100
|
+
@d.must_be_empty
|
101
|
+
|
102
|
+
lambda do
|
103
|
+
@db.transaction do
|
104
|
+
@d.insert(:name => 'abc', :value => 1)
|
105
|
+
@db.rollback_on_exit
|
106
|
+
return true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@d.must_be_empty
|
110
|
+
|
111
|
+
@db.transaction do
|
112
|
+
@d.insert(:name => 'abc', :value => 1)
|
113
|
+
@db.rollback_on_exit
|
114
|
+
@db.rollback_on_exit(:cancel=>true)
|
115
|
+
end
|
116
|
+
@d.count.must_equal 1
|
117
|
+
|
118
|
+
@d.delete
|
119
|
+
@db.transaction do
|
120
|
+
@d.insert(:name => 'abc', :value => 1)
|
121
|
+
@db.rollback_on_exit(:cancel=>true)
|
122
|
+
end
|
123
|
+
@d.count.must_equal 1
|
124
|
+
|
125
|
+
@d.delete
|
126
|
+
@db.transaction do
|
127
|
+
@d.insert(:name => 'abc', :value => 1)
|
128
|
+
@db.rollback_on_exit
|
129
|
+
@db.rollback_on_exit(:cancel=>true)
|
130
|
+
@db.rollback_on_exit
|
131
|
+
end
|
132
|
+
@d.must_be_empty
|
133
|
+
end
|
85
134
|
|
86
135
|
if DB.supports_savepoints?
|
136
|
+
it "should support rollback_on_exit inside savepoints" do
|
137
|
+
@db.transaction do
|
138
|
+
@d.insert(:name => 'abc', :value => 1)
|
139
|
+
@db.transaction(:savepoint=>true) do
|
140
|
+
@d.insert(:name => 'def', :value => 2)
|
141
|
+
@db.rollback_on_exit
|
142
|
+
end
|
143
|
+
end
|
144
|
+
@d.must_be_empty
|
145
|
+
|
146
|
+
@db.transaction do
|
147
|
+
@d.insert(:name => 'abc', :value => 1)
|
148
|
+
@db.transaction(:savepoint=>true) do
|
149
|
+
@d.insert(:name => 'def', :value => 2)
|
150
|
+
@db.rollback_on_exit
|
151
|
+
@db.transaction(:savepoint=>true) do
|
152
|
+
@d.insert(:name => 'ghi', :value => 3)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
@d.must_be_empty
|
157
|
+
|
158
|
+
@db.transaction do
|
159
|
+
@d.insert(:name => 'abc', :value => 1)
|
160
|
+
@db.transaction(:savepoint=>true) do
|
161
|
+
@d.insert(:name => 'def', :value => 2)
|
162
|
+
@db.transaction(:savepoint=>true) do
|
163
|
+
@db.rollback_on_exit
|
164
|
+
@d.insert(:name => 'ghi', :value => 3)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
@d.must_be_empty
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should support rollback_on_exit with :savepoint option" do
|
172
|
+
@db.transaction do
|
173
|
+
@d.insert(:name => 'abc', :value => 1)
|
174
|
+
@db.transaction(:savepoint=>true) do
|
175
|
+
@d.insert(:name => 'def', :value => 2)
|
176
|
+
@db.rollback_on_exit(:savepoint=>true)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
@d.select_order_map(:value).must_equal [1]
|
180
|
+
|
181
|
+
@d.delete
|
182
|
+
@db.transaction do
|
183
|
+
@d.insert(:name => 'abc', :value => 1)
|
184
|
+
@db.transaction(:savepoint=>true) do
|
185
|
+
@d.insert(:name => 'def', :value => 2)
|
186
|
+
@db.rollback_on_exit(:savepoint=>true)
|
187
|
+
@db.transaction(:savepoint=>true) do
|
188
|
+
@db.rollback_on_exit(:savepoint=>true)
|
189
|
+
@d.insert(:name => 'ghi', :value => 3)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
@d.select_order_map(:value).must_equal [1]
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should support rollback_on_exit with :savepoint=>Integer" do
|
197
|
+
@db.transaction do
|
198
|
+
@d.insert(:name => 'abc', :value => 1)
|
199
|
+
@db.transaction(:savepoint=>true) do
|
200
|
+
@d.insert(:name => 'def', :value => 2)
|
201
|
+
@db.rollback_on_exit(:savepoint=>2)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
@d.must_be_empty
|
205
|
+
|
206
|
+
@db.transaction do
|
207
|
+
@d.insert(:name => 'abc', :value => 1)
|
208
|
+
@db.transaction(:savepoint=>true) do
|
209
|
+
@d.insert(:name => 'def', :value => 2)
|
210
|
+
@db.rollback_on_exit(:savepoint=>3)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
@d.must_be_empty
|
214
|
+
|
215
|
+
@db.transaction do
|
216
|
+
@d.insert(:name => 'abc', :value => 1)
|
217
|
+
@db.transaction(:savepoint=>true) do
|
218
|
+
@d.insert(:name => 'def', :value => 2)
|
219
|
+
@db.transaction(:savepoint=>true) do
|
220
|
+
@db.rollback_on_exit(:savepoint=>2)
|
221
|
+
@d.insert(:name => 'ghi', :value => 3)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
@d.select_order_map(:value).must_equal [1]
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should support rollback_on_exit with :savepoint=>Integer and :cancel" do
|
229
|
+
@db.transaction do
|
230
|
+
@d.insert(:name => 'abc', :value => 1)
|
231
|
+
@db.transaction(:savepoint=>true) do
|
232
|
+
@db.rollback_on_exit(:savepoint=>true)
|
233
|
+
@d.insert(:name => 'def', :value => 2)
|
234
|
+
@db.transaction(:savepoint=>true) do
|
235
|
+
@db.rollback_on_exit(:savepoint=>2, :cancel=>true)
|
236
|
+
@d.insert(:name => 'ghi', :value => 3)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
@d.select_order_map(:value).must_equal [1, 2, 3]
|
241
|
+
|
242
|
+
@d.delete
|
243
|
+
@db.transaction do
|
244
|
+
@db.rollback_on_exit(:savepoint=>true)
|
245
|
+
@d.insert(:name => 'abc', :value => 1)
|
246
|
+
@db.transaction(:savepoint=>true) do
|
247
|
+
@db.rollback_on_exit(:savepoint=>true)
|
248
|
+
@d.insert(:name => 'def', :value => 2)
|
249
|
+
@db.transaction(:savepoint=>true) do
|
250
|
+
@db.rollback_on_exit(:savepoint=>3, :cancel=>true)
|
251
|
+
@d.insert(:name => 'ghi', :value => 3)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
@d.select_order_map(:value).must_equal [1, 2, 3]
|
256
|
+
|
257
|
+
@d.delete
|
258
|
+
@db.transaction do
|
259
|
+
@d.insert(:name => 'abc', :value => 1)
|
260
|
+
@db.rollback_on_exit(:savepoint=>true)
|
261
|
+
@db.transaction(:savepoint=>true) do
|
262
|
+
@d.insert(:name => 'def', :value => 2)
|
263
|
+
@db.transaction(:savepoint=>true) do
|
264
|
+
@db.rollback_on_exit(:savepoint=>4, :cancel=>true)
|
265
|
+
@d.insert(:name => 'ghi', :value => 3)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
@d.select_order_map(:value).must_equal [1, 2, 3]
|
270
|
+
|
271
|
+
@d.delete
|
272
|
+
@db.transaction do
|
273
|
+
@d.insert(:name => 'abc', :value => 1)
|
274
|
+
@db.transaction(:savepoint=>true) do
|
275
|
+
@db.rollback_on_exit(:savepoint=>2)
|
276
|
+
@d.insert(:name => 'def', :value => 2)
|
277
|
+
@db.transaction(:savepoint=>true) do
|
278
|
+
@db.rollback_on_exit(:savepoint=>2, :cancel=>true)
|
279
|
+
@d.insert(:name => 'ghi', :value => 3)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
@d.must_be_empty
|
284
|
+
end
|
285
|
+
|
87
286
|
it "should handle table_exists? failures inside transactions" do
|
88
287
|
@db.transaction do
|
89
288
|
@d.insert(:name => '1')
|
@@ -1084,6 +1084,37 @@ describe Sequel::Model, "one_to_one" do
|
|
1084
1084
|
DB.sqls.must_equal []
|
1085
1085
|
end
|
1086
1086
|
|
1087
|
+
it "should have setter not unset reciprocal during save if reciprocal is the same as current" do
|
1088
|
+
@c2.many_to_one :parent, :class => @c2, :key=>:parent_id
|
1089
|
+
@c2.one_to_one :child, :class => @c2, :key=>:parent_id, :reciprocal=>:parent
|
1090
|
+
|
1091
|
+
d = @c2.new(:id => 1)
|
1092
|
+
e = @c2.new(:id => 2)
|
1093
|
+
e2 = @c2.new(:id => 3)
|
1094
|
+
e3 = @c2.new(:id => 4)
|
1095
|
+
d.associations[:parent] = e
|
1096
|
+
e.associations[:child] = d
|
1097
|
+
e2.associations[:child] = d
|
1098
|
+
e3.associations[:child] = e
|
1099
|
+
assoc = nil
|
1100
|
+
d.define_singleton_method(:after_save) do
|
1101
|
+
super()
|
1102
|
+
assoc = associations
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def e.set_associated_object_if_same?; true; end
|
1106
|
+
e.child = d
|
1107
|
+
assoc.must_equal(:parent=>e)
|
1108
|
+
|
1109
|
+
def e2.set_associated_object_if_same?; true; end
|
1110
|
+
e2.child = e
|
1111
|
+
assoc.must_equal(:parent=>nil)
|
1112
|
+
|
1113
|
+
d.associations.clear
|
1114
|
+
e3.child = d
|
1115
|
+
assoc.must_equal({})
|
1116
|
+
end
|
1117
|
+
|
1087
1118
|
it "should not add associations methods directly to class" do
|
1088
1119
|
@c2.one_to_one :parent, :class => @c2
|
1089
1120
|
@c2.instance_methods.must_include(:parent)
|