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