sequel 3.28.0 → 3.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/CHANGELOG +119 -3
  2. data/Rakefile +5 -3
  3. data/bin/sequel +1 -5
  4. data/doc/model_hooks.rdoc +9 -1
  5. data/doc/opening_databases.rdoc +49 -40
  6. data/doc/prepared_statements.rdoc +27 -6
  7. data/doc/release_notes/3.28.0.txt +2 -2
  8. data/doc/release_notes/3.29.0.txt +459 -0
  9. data/doc/sharding.rdoc +7 -1
  10. data/doc/testing.rdoc +18 -9
  11. data/doc/transactions.rdoc +41 -1
  12. data/lib/sequel/adapters/ado.rb +28 -17
  13. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  14. data/lib/sequel/adapters/amalgalite.rb +11 -7
  15. data/lib/sequel/adapters/db2.rb +122 -70
  16. data/lib/sequel/adapters/dbi.rb +15 -15
  17. data/lib/sequel/adapters/do.rb +5 -36
  18. data/lib/sequel/adapters/do/mysql.rb +0 -5
  19. data/lib/sequel/adapters/do/postgres.rb +0 -5
  20. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  21. data/lib/sequel/adapters/firebird.rb +3 -6
  22. data/lib/sequel/adapters/ibmdb.rb +24 -16
  23. data/lib/sequel/adapters/informix.rb +2 -4
  24. data/lib/sequel/adapters/jdbc.rb +47 -11
  25. data/lib/sequel/adapters/jdbc/as400.rb +5 -24
  26. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  28. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/h2.rb +10 -12
  30. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  31. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  32. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
  34. data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
  36. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  38. data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
  39. data/lib/sequel/adapters/mock.rb +315 -0
  40. data/lib/sequel/adapters/mysql.rb +64 -51
  41. data/lib/sequel/adapters/mysql2.rb +15 -9
  42. data/lib/sequel/adapters/odbc.rb +13 -6
  43. data/lib/sequel/adapters/odbc/db2.rb +0 -4
  44. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  45. data/lib/sequel/adapters/openbase.rb +2 -4
  46. data/lib/sequel/adapters/oracle.rb +333 -51
  47. data/lib/sequel/adapters/postgres.rb +80 -27
  48. data/lib/sequel/adapters/shared/access.rb +0 -6
  49. data/lib/sequel/adapters/shared/db2.rb +13 -15
  50. data/lib/sequel/adapters/shared/firebird.rb +6 -6
  51. data/lib/sequel/adapters/shared/mssql.rb +23 -18
  52. data/lib/sequel/adapters/shared/mysql.rb +6 -6
  53. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  54. data/lib/sequel/adapters/shared/oracle.rb +185 -30
  55. data/lib/sequel/adapters/shared/postgres.rb +35 -18
  56. data/lib/sequel/adapters/shared/progress.rb +0 -6
  57. data/lib/sequel/adapters/shared/sqlite.rb +116 -37
  58. data/lib/sequel/adapters/sqlite.rb +16 -8
  59. data/lib/sequel/adapters/swift.rb +5 -5
  60. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  61. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  62. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  63. data/lib/sequel/adapters/tinytds.rb +13 -10
  64. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
  65. data/lib/sequel/core.rb +40 -0
  66. data/lib/sequel/database/connecting.rb +1 -2
  67. data/lib/sequel/database/dataset.rb +3 -3
  68. data/lib/sequel/database/dataset_defaults.rb +58 -0
  69. data/lib/sequel/database/misc.rb +62 -2
  70. data/lib/sequel/database/query.rb +113 -49
  71. data/lib/sequel/database/schema_methods.rb +7 -2
  72. data/lib/sequel/dataset/actions.rb +37 -19
  73. data/lib/sequel/dataset/features.rb +24 -0
  74. data/lib/sequel/dataset/graph.rb +7 -6
  75. data/lib/sequel/dataset/misc.rb +11 -3
  76. data/lib/sequel/dataset/mutation.rb +2 -3
  77. data/lib/sequel/dataset/prepared_statements.rb +6 -4
  78. data/lib/sequel/dataset/query.rb +46 -15
  79. data/lib/sequel/dataset/sql.rb +28 -4
  80. data/lib/sequel/extensions/named_timezones.rb +5 -0
  81. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  82. data/lib/sequel/model.rb +2 -1
  83. data/lib/sequel/model/associations.rb +115 -33
  84. data/lib/sequel/model/base.rb +91 -31
  85. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  86. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  87. data/lib/sequel/plugins/force_encoding.rb +6 -6
  88. data/lib/sequel/plugins/identity_map.rb +1 -1
  89. data/lib/sequel/plugins/many_through_many.rb +6 -10
  90. data/lib/sequel/plugins/prepared_statements.rb +12 -1
  91. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
  92. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  93. data/lib/sequel/plugins/serialization.rb +6 -1
  94. data/lib/sequel/plugins/sharding.rb +0 -5
  95. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  96. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  97. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  98. data/lib/sequel/timezones.rb +42 -42
  99. data/lib/sequel/version.rb +1 -1
  100. data/spec/adapters/mssql_spec.rb +29 -29
  101. data/spec/adapters/mysql_spec.rb +86 -104
  102. data/spec/adapters/oracle_spec.rb +48 -76
  103. data/spec/adapters/postgres_spec.rb +98 -33
  104. data/spec/adapters/spec_helper.rb +0 -5
  105. data/spec/adapters/sqlite_spec.rb +24 -21
  106. data/spec/core/connection_pool_spec.rb +9 -15
  107. data/spec/core/core_sql_spec.rb +20 -31
  108. data/spec/core/database_spec.rb +491 -227
  109. data/spec/core/dataset_spec.rb +638 -1051
  110. data/spec/core/expression_filters_spec.rb +0 -1
  111. data/spec/core/mock_adapter_spec.rb +378 -0
  112. data/spec/core/object_graph_spec.rb +48 -114
  113. data/spec/core/schema_generator_spec.rb +3 -3
  114. data/spec/core/schema_spec.rb +51 -114
  115. data/spec/core/spec_helper.rb +3 -90
  116. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  117. data/spec/extensions/dataset_associations_spec.rb +199 -0
  118. data/spec/extensions/instance_hooks_spec.rb +71 -0
  119. data/spec/extensions/named_timezones_spec.rb +22 -2
  120. data/spec/extensions/nested_attributes_spec.rb +3 -0
  121. data/spec/extensions/schema_spec.rb +1 -1
  122. data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
  123. data/spec/extensions/serialization_spec.rb +5 -8
  124. data/spec/extensions/spec_helper.rb +4 -0
  125. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  126. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  127. data/spec/integration/associations_test.rb +123 -12
  128. data/spec/integration/dataset_test.rb +140 -47
  129. data/spec/integration/eager_loader_test.rb +19 -21
  130. data/spec/integration/model_test.rb +80 -1
  131. data/spec/integration/plugin_test.rb +179 -128
  132. data/spec/integration/prepared_statement_test.rb +92 -91
  133. data/spec/integration/schema_test.rb +42 -23
  134. data/spec/integration/spec_helper.rb +25 -31
  135. data/spec/integration/timezone_test.rb +38 -12
  136. data/spec/integration/transaction_test.rb +161 -34
  137. data/spec/integration/type_test.rb +3 -3
  138. data/spec/model/association_reflection_spec.rb +83 -7
  139. data/spec/model/associations_spec.rb +393 -676
  140. data/spec/model/base_spec.rb +186 -116
  141. data/spec/model/dataset_methods_spec.rb +7 -27
  142. data/spec/model/eager_loading_spec.rb +343 -867
  143. data/spec/model/hooks_spec.rb +160 -79
  144. data/spec/model/model_spec.rb +118 -165
  145. data/spec/model/plugins_spec.rb +7 -13
  146. data/spec/model/record_spec.rb +138 -207
  147. data/spec/model/spec_helper.rb +10 -73
  148. metadata +14 -8
@@ -6,7 +6,6 @@ String.send(:include, Sequel::SQL::StringMethods)
6
6
  describe "Blockless Ruby Filters" do
7
7
  before do
8
8
  db = Sequel::Database.new
9
- db.quote_identifiers = false
10
9
  @d = db[:items]
11
10
  def @d.l(*args, &block)
12
11
  literal(filter_expr(*args, &block))
@@ -0,0 +1,378 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel Mock Adapter" do
4
+ specify "should have an adapter method" do
5
+ db = Sequel.mock
6
+ db.should be_a_kind_of(Sequel::Mock::Database)
7
+ db.adapter_scheme.should == :mock
8
+ end
9
+
10
+ specify "should each not return any rows by default" do
11
+ called = false
12
+ Sequel.mock[:t].each{|r| called = true}
13
+ called.should be_false
14
+ end
15
+
16
+ specify "should return 0 for update and delete by default" do
17
+ Sequel.mock[:t].update(:a=>1).should == 0
18
+ Sequel.mock[:t].delete.should == 0
19
+ end
20
+
21
+ specify "should return nil for insert by default" do
22
+ Sequel.mock[:t].insert(:a=>1).should be_nil
23
+ end
24
+
25
+ specify "should be able to set the rows returned by each using :fetch option with a single hash" do
26
+ rs = []
27
+ db = Sequel.mock(:fetch=>{:a=>1})
28
+ db[:t].each{|r| rs << r}
29
+ rs.should == [{:a=>1}]
30
+ db[:t].each{|r| rs << r}
31
+ rs.should == [{:a=>1}, {:a=>1}]
32
+ db[:t].each{|r| r[:a] = 2; rs << r}
33
+ rs.should == [{:a=>1}, {:a=>1}, {:a=>2}]
34
+ db[:t].each{|r| rs << r}
35
+ rs.should == [{:a=>1}, {:a=>1}, {:a=>2}, {:a=>1}]
36
+ end
37
+
38
+ specify "should be able to set the rows returned by each using :fetch option with an array of hashes" do
39
+ rs = []
40
+ db = Sequel.mock(:fetch=>[{:a=>1}, {:a=>2}])
41
+ db[:t].each{|r| rs << r}
42
+ rs.should == [{:a=>1}, {:a=>2}]
43
+ db[:t].each{|r| rs << r}
44
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>1}, {:a=>2}]
45
+ db[:t].each{|r| r[:a] += 2; rs << r}
46
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
47
+ db[:t].each{|r| rs << r}
48
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}, {:a=>1}, {:a=>2}]
49
+ end
50
+
51
+ specify "should be able to set the rows returned by each using :fetch option with an array or arrays of hashes" do
52
+ rs = []
53
+ db = Sequel.mock(:fetch=>[[{:a=>1}, {:a=>2}], [{:a=>3}, {:a=>4}]])
54
+ db[:t].each{|r| rs << r}
55
+ rs.should == [{:a=>1}, {:a=>2}]
56
+ db[:t].each{|r| rs << r}
57
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
58
+ db[:t].each{|r| rs << r}
59
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
60
+ end
61
+
62
+ specify "should be able to set the rows returned by each using :fetch option with a proc that takes sql" do
63
+ rs = []
64
+ db = Sequel.mock(:fetch=>proc{|sql| sql =~ /FROM t/ ? {:b=>1} : [{:a=>1}, {:a=>2}]})
65
+ db[:t].each{|r| rs << r}
66
+ rs.should == [{:b=>1}]
67
+ db[:b].each{|r| rs << r}
68
+ rs.should == [{:b=>1}, {:a=>1}, {:a=>2}]
69
+ db[:t].each{|r| r[:b] += 1; rs << r}
70
+ db[:b].each{|r| r[:a] += 2; rs << r}
71
+ rs.should == [{:b=>1}, {:a=>1}, {:a=>2}, {:b=>2}, {:a=>3}, {:a=>4}]
72
+ db[:t].each{|r| rs << r}
73
+ db[:b].each{|r| rs << r}
74
+ rs.should == [{:b=>1}, {:a=>1}, {:a=>2}, {:b=>2}, {:a=>3}, {:a=>4}, {:b=>1}, {:a=>1}, {:a=>2}]
75
+ end
76
+
77
+ specify "should have a fetch= method for setting rows returned by each after the fact" do
78
+ rs = []
79
+ db = Sequel.mock
80
+ db.fetch = {:a=>1}
81
+ db[:t].each{|r| rs << r}
82
+ rs.should == [{:a=>1}]
83
+ db[:t].each{|r| rs << r}
84
+ rs.should == [{:a=>1}] * 2
85
+ end
86
+
87
+ specify "should be able to set an exception to raise by setting the :fetch option to an exception class " do
88
+ db = Sequel.mock(:fetch=>ArgumentError)
89
+ proc{db[:t].all}.should raise_error(Sequel::DatabaseError)
90
+ begin
91
+ db[:t].all
92
+ rescue => e
93
+ end
94
+ e.should be_a_kind_of(Sequel::DatabaseError)
95
+ e.wrapped_exception.should be_a_kind_of(ArgumentError)
96
+ end
97
+
98
+ specify "should be able to set separate kinds of results for fetch using an array" do
99
+ rs = []
100
+ db = Sequel.mock(:fetch=>[{:a=>1}, [{:a=>2}, {:a=>3}], proc{|s| {:a=>4}}, proc{|s| }, nil, ArgumentError])
101
+ db[:t].each{|r| rs << r}
102
+ rs.should == [{:a=>1}]
103
+ db[:t].each{|r| rs << r}
104
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>3}]
105
+ db[:t].each{|r| rs << r}
106
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
107
+ db[:t].each{|r| rs << r}
108
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
109
+ db[:t].each{|r| rs << r}
110
+ rs.should == [{:a=>1}, {:a=>2}, {:a=>3}, {:a=>4}]
111
+ proc{db[:t].all}.should raise_error(Sequel::DatabaseError)
112
+ end
113
+
114
+ specify "should be able to set the rows returned by each on a per dataset basis using _fetch" do
115
+ rs = []
116
+ db = Sequel.mock(:fetch=>{:a=>1})
117
+ ds = db[:t]
118
+ ds.each{|r| rs << r}
119
+ rs.should == [{:a=>1}]
120
+ ds._fetch = {:b=>2}
121
+ ds.each{|r| rs << r}
122
+ rs.should == [{:a=>1}, {:b=>2}]
123
+ end
124
+
125
+ specify "should be able to set the number of rows modified by update and delete using :numrows option as an integer" do
126
+ db = Sequel.mock(:numrows=>2)
127
+ db[:t].update(:a=>1).should == 2
128
+ db[:t].delete.should == 2
129
+ db[:t].update(:a=>1).should == 2
130
+ db[:t].delete.should == 2
131
+ end
132
+
133
+ specify "should be able to set the number of rows modified by update and delete using :numrows option as an array of integers" do
134
+ db = Sequel.mock(:numrows=>[2, 1])
135
+ db[:t].update(:a=>1).should == 2
136
+ db[:t].delete.should == 1
137
+ db[:t].update(:a=>1).should == 0
138
+ db[:t].delete.should == 0
139
+ end
140
+
141
+ specify "should be able to set the number of rows modified by update and delete using :numrows option as a proc" do
142
+ db = Sequel.mock(:numrows=>proc{|sql| sql =~ / t/ ? 2 : 1})
143
+ db[:t].update(:a=>1).should == 2
144
+ db[:t].delete.should == 2
145
+ db[:b].update(:a=>1).should == 1
146
+ db[:b].delete.should == 1
147
+ end
148
+
149
+ specify "should be able to set an exception to raise by setting the :numrows option to an exception class " do
150
+ db = Sequel.mock(:numrows=>ArgumentError)
151
+ proc{db[:t].update(:a=>1)}.should raise_error(Sequel::DatabaseError)
152
+ begin
153
+ db[:t].delete
154
+ rescue => e
155
+ end
156
+ e.should be_a_kind_of(Sequel::DatabaseError)
157
+ e.wrapped_exception.should be_a_kind_of(ArgumentError)
158
+ end
159
+
160
+ specify "should be able to set separate kinds of results for numrows using an array" do
161
+ db = Sequel.mock(:numrows=>[1, proc{|s| 2}, nil, ArgumentError])
162
+ db[:t].delete.should == 1
163
+ db[:t].update(:a=>1).should == 2
164
+ db[:t].delete.should == 0
165
+ proc{db[:t].delete}.should raise_error(Sequel::DatabaseError)
166
+ end
167
+
168
+ specify "should have a numrows= method to set the number of rows modified by update and delete after the fact" do
169
+ db = Sequel.mock
170
+ db.numrows = 2
171
+ db[:t].update(:a=>1).should == 2
172
+ db[:t].delete.should == 2
173
+ db[:t].update(:a=>1).should == 2
174
+ db[:t].delete.should == 2
175
+ end
176
+
177
+ specify "should be able to set the number of rows modified by update and delete on a per dataset basis" do
178
+ db = Sequel.mock(:numrows=>2)
179
+ ds = db[:t]
180
+ ds.update(:a=>1).should == 2
181
+ ds.delete.should == 2
182
+ ds.numrows = 3
183
+ ds.update(:a=>1).should == 3
184
+ ds.delete.should == 3
185
+ end
186
+
187
+ specify "should be able to set the autogenerated primary key returned by insert using :autoid option as an integer" do
188
+ db = Sequel.mock(:autoid=>1)
189
+ db[:t].insert(:a=>1).should == 1
190
+ db[:t].insert(:a=>1).should == 2
191
+ db[:t].insert(:a=>1).should == 3
192
+ end
193
+
194
+ specify "should be able to set the autogenerated primary key returned by insert using :autoid option as an array of integers" do
195
+ db = Sequel.mock(:autoid=>[1, 3, 5])
196
+ db[:t].insert(:a=>1).should == 1
197
+ db[:t].insert(:a=>1).should == 3
198
+ db[:t].insert(:a=>1).should == 5
199
+ db[:t].insert(:a=>1).should be_nil
200
+ end
201
+
202
+ specify "should be able to set the autogenerated primary key returned by insert using :autoid option as a proc" do
203
+ db = Sequel.mock(:autoid=>proc{|sql| sql =~ /INTO t / ? 2 : 1})
204
+ db[:t].insert(:a=>1).should == 2
205
+ db[:t].insert(:a=>1).should == 2
206
+ db[:b].insert(:a=>1).should == 1
207
+ db[:b].insert(:a=>1).should == 1
208
+ end
209
+
210
+ specify "should be able to set an exception to raise by setting the :autoid option to an exception class " do
211
+ db = Sequel.mock(:autoid=>ArgumentError)
212
+ proc{db[:t].insert(:a=>1)}.should raise_error(Sequel::DatabaseError)
213
+ begin
214
+ db[:t].insert
215
+ rescue => e
216
+ end
217
+ e.should be_a_kind_of(Sequel::DatabaseError)
218
+ e.wrapped_exception.should be_a_kind_of(ArgumentError)
219
+ end
220
+
221
+ specify "should be able to set separate kinds of results for autoid using an array" do
222
+ db = Sequel.mock(:autoid=>[1, proc{|s| 2}, nil, ArgumentError])
223
+ db[:t].insert.should == 1
224
+ db[:t].insert.should == 2
225
+ db[:t].insert.should == nil
226
+ proc{db[:t].insert}.should raise_error(Sequel::DatabaseError)
227
+ end
228
+
229
+ specify "should have an autoid= method to set the autogenerated primary key returned by insert after the fact" do
230
+ db = Sequel.mock
231
+ db.autoid = 1
232
+ db[:t].insert(:a=>1).should == 1
233
+ db[:t].insert(:a=>1).should == 2
234
+ db[:t].insert(:a=>1).should == 3
235
+ end
236
+
237
+ specify "should be able to set the autogenerated primary key returned by insert on a per dataset basis" do
238
+ db = Sequel.mock(:autoid=>1)
239
+ ds = db[:t]
240
+ ds.insert(:a=>1).should == 1
241
+ ds.autoid = 5
242
+ ds.insert(:a=>1).should == 5
243
+ ds.insert(:a=>1).should == 6
244
+ db[:t].insert(:a=>1).should == 2
245
+ end
246
+
247
+ specify "should be able to set the columns to set in the dataset as an array of symbols" do
248
+ db = Sequel.mock(:columns=>[:a, :b])
249
+ db[:t].columns.should == [:a, :b]
250
+ db.sqls.should == ["SELECT * FROM t LIMIT 1"]
251
+ ds = db[:t]
252
+ ds.all
253
+ db.sqls.should == ["SELECT * FROM t"]
254
+ ds.columns.should == [:a, :b]
255
+ db.sqls.should == []
256
+ db[:t].columns.should == [:a, :b]
257
+ end
258
+
259
+ specify "should be able to set the columns to set in the dataset as an array of arrays of symbols" do
260
+ db = Sequel.mock(:columns=>[[:a, :b], [:c, :d]])
261
+ db[:t].columns.should == [:a, :b]
262
+ db[:t].columns.should == [:c, :d]
263
+ end
264
+
265
+ specify "should be able to set the columns to set in the dataset as a proc" do
266
+ db = Sequel.mock(:columns=>proc{|sql| (sql =~ / t/) ? [:a, :b] : [:c, :d]})
267
+ db[:b].columns.should == [:c, :d]
268
+ db[:t].columns.should == [:a, :b]
269
+ end
270
+
271
+ specify "should have a columns= method to set the columns to set after the fact" do
272
+ db = Sequel.mock
273
+ db.columns = [[:a, :b], [:c, :d]]
274
+ db[:t].columns.should == [:a, :b]
275
+ db[:t].columns.should == [:c, :d]
276
+ end
277
+
278
+ specify "should keep a record of all executed SQL in #sqls" do
279
+ db = Sequel.mock
280
+ db[:t].all
281
+ db[:b].delete
282
+ db[:c].insert(:a=>1)
283
+ db[:d].update(:a=>1)
284
+ db.sqls.should == ['SELECT * FROM t', 'DELETE FROM b', 'INSERT INTO c (a) VALUES (1)', 'UPDATE d SET a = 1']
285
+ end
286
+
287
+ specify "should clear sqls on retrieval" do
288
+ db = Sequel.mock
289
+ db[:t].all
290
+ db.sqls.should == ['SELECT * FROM t']
291
+ db.sqls.should == []
292
+ end
293
+
294
+ specify "should also log SQL executed to the given loggers" do
295
+ a = []
296
+ def a.method_missing(m, *x) push(*x) end
297
+ db = Sequel.mock(:loggers=>[a])
298
+ db[:t].all
299
+ db[:b].delete
300
+ db[:c].insert(:a=>1)
301
+ db[:d].update(:a=>1)
302
+ a.should == ['SELECT * FROM t', 'DELETE FROM b', 'INSERT INTO c (a) VALUES (1)', 'UPDATE d SET a = 1']
303
+ end
304
+
305
+ specify "should correctly handle transactions" do
306
+ db = Sequel.mock
307
+ db.transaction{db[:a].all}
308
+ db.sqls.should == ['BEGIN', 'SELECT * FROM a', 'COMMIT']
309
+ db.transaction{db[:a].all; raise Sequel::Rollback}
310
+ db.sqls.should == ['BEGIN', 'SELECT * FROM a', 'ROLLBACK']
311
+ proc{db.transaction{db[:a].all; raise ArgumentError}}.should raise_error(ArgumentError)
312
+ db.sqls.should == ['BEGIN', 'SELECT * FROM a', 'ROLLBACK']
313
+ proc{db.transaction(:rollback=>:reraise){db[:a].all; raise Sequel::Rollback}}.should raise_error(Sequel::Rollback)
314
+ db.sqls.should == ['BEGIN', 'SELECT * FROM a', 'ROLLBACK']
315
+ db.transaction(:rollback=>:always){db[:a].all}
316
+ db.sqls.should == ['BEGIN', 'SELECT * FROM a', 'ROLLBACK']
317
+ db.transaction{db.transaction{db[:a].all; raise Sequel::Rollback}}
318
+ db.sqls.should == ['BEGIN', 'SELECT * FROM a', 'ROLLBACK']
319
+ db.transaction{db.transaction(:savepoint=>true){db[:a].all; raise Sequel::Rollback}}
320
+ db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'SELECT * FROM a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'COMMIT']
321
+ db.transaction{db.transaction(:savepoint=>true){db[:a].all}; raise Sequel::Rollback}
322
+ db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'SELECT * FROM a', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK']
323
+ end
324
+
325
+ specify "should correctly handle transactions when sharding" do
326
+ db = Sequel.mock(:servers=>{:test=>{}})
327
+ db.transaction{db.transaction(:server=>:test){db[:a].all; db[:t].server(:test).all}}
328
+ db.sqls.should == ['BEGIN', 'BEGIN -- test', 'SELECT * FROM a', 'SELECT * FROM t -- test', 'COMMIT -- test', 'COMMIT']
329
+ end
330
+
331
+ specify "should yield a mock connection object from synchronize" do
332
+ c = Sequel.mock.synchronize{|conn| conn}
333
+ c.should be_a_kind_of(Sequel::Mock::Connection)
334
+ end
335
+
336
+ specify "should deal correctly with sharding" do
337
+ db = Sequel.mock(:servers=>{:test=>{}})
338
+ c1 = db.synchronize{|conn| conn}
339
+ c2 = db.synchronize(:test){|conn| conn}
340
+ c1.server.should == :default
341
+ c2.server.should == :test
342
+ end
343
+
344
+ specify "should disconnect correctly" do
345
+ db = Sequel.mock
346
+ db.test_connection
347
+ proc{db.disconnect}.should_not raise_error
348
+ end
349
+
350
+ specify "should accept :extend option for extending the object with a module" do
351
+ Sequel.mock(:extend=>Module.new{def foo(v) v * 2 end}).foo(3).should == 6
352
+ end
353
+
354
+ specify "should accept :sqls option for where to store the SQL queries" do
355
+ a = []
356
+ Sequel.mock(:sqls=>a)[:t].all
357
+ a.should == ['SELECT * FROM t']
358
+ end
359
+
360
+ specify "should include :append option in SQL if it is given" do
361
+ db = Sequel.mock(:append=>'a')
362
+ db[:t].all
363
+ db.sqls.should == ['SELECT * FROM t -- a']
364
+ end
365
+
366
+ specify "should append :arguments option to execute to the SQL if present" do
367
+ db = Sequel.mock
368
+ db.execute('SELECT * FROM t', :arguments=>[1, 2])
369
+ db.sqls.should == ['SELECT * FROM t -- args: [1, 2]']
370
+ end
371
+
372
+ specify "should have Dataset#columns take columns to set and return self" do
373
+ db = Sequel.mock
374
+ ds = db[:t].columns(:id, :a, :b)
375
+ ds.should be_a_kind_of(Sequel::Mock::Dataset)
376
+ ds.columns.should == [:id, :a, :b]
377
+ end
378
+ end
@@ -2,16 +2,21 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
2
 
3
3
  describe Sequel::Dataset, " graphing" do
4
4
  before do
5
- dbc = Class.new
6
- @db = dbc.new
7
- @ds1 = Sequel::Dataset.new(@db).from(:points)
8
- @ds2 = Sequel::Dataset.new(@db).from(:lines)
9
- @ds3 = Sequel::Dataset.new(@db).from(:graphs)
10
- dss = {:points=>@ds1, :lines=>@ds2, :graphs=>@ds3}
11
- dbc.send(:define_method, :[]){|ds| dss[ds]}
12
- def @ds1.columns; [:id, :x, :y] end
13
- def @ds2.columns; [:id, :x, :y, :graph_id] end
14
- def @ds3.columns; [:id, :name, :x, :y, :lines_x] end
5
+ @db = Sequel.mock(:columns=>proc do |sql|
6
+ case sql
7
+ when /points/
8
+ [:id, :x, :y]
9
+ when /lines/
10
+ [:id, :x, :y, :graph_id]
11
+ else
12
+ [:id, :name, :x, :y, :lines_x]
13
+ end
14
+ end)
15
+ @ds1 = @db.from(:points)
16
+ @ds2 = @db.from(:lines)
17
+ @ds3 = @db.from(:graphs)
18
+ [@ds1, @ds2, @ds3].each{|ds| ds.columns}
19
+ @db.sqls
15
20
  end
16
21
 
17
22
  it "#graph should not modify the current dataset's opts" do
@@ -187,144 +192,73 @@ describe Sequel::Dataset, " graphing" do
187
192
  end
188
193
 
189
194
  it "#graph_each should split the result set into component tables" do
190
- ds = @ds1.graph(@ds2, :x=>:id)
191
- def ds.fetch_rows(sql, &block)
192
- yield({:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7})
193
- end
194
- results = ds.all
195
- results.length.should == 1
196
- results.first.should == {:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}}
197
-
198
- ds = @ds1.graph(@ds2, :x=>:id).graph(@ds3, :id=>:graph_id)
199
- def ds.fetch_rows(sql, &block)
200
- yield({:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>8, :name=>9, :graphs_x=>10, :graphs_y=>11, :graphs_lines_x=>12})
201
- end
202
- results = ds.all
203
- results.length.should == 1
204
- results.first.should == {:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graphs=>{:id=>8, :name=>9, :x=>10, :y=>11, :lines_x=>12}}
195
+ @db.fetch = [[{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7}],
196
+ [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>8, :name=>9, :graphs_x=>10, :graphs_y=>11, :graphs_lines_x=>12}],
197
+ [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graph_id_0=>8, :graph_x=>9, :graph_y=>10, :graph_graph_id=>11}]]
205
198
 
206
- ds = @ds1.graph(@ds2, :x=>:id).graph(@ds2, {:y=>:points__id}, :table_alias=>:graph)
207
- def ds.fetch_rows(sql, &block)
208
- yield({:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graph_id_0=>8, :graph_x=>9, :graph_y=>10, :graph_graph_id=>11})
209
- end
210
- results = ds.all
211
- results.length.should == 1
212
- results.first.should == {:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graph=>{:id=>8, :x=>9, :y=>10, :graph_id=>11}}
199
+ @ds1.graph(@ds2, :x=>:id).all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}}]
200
+ @ds1.graph(@ds2, :x=>:id).graph(@ds3, :id=>:graph_id).all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graphs=>{:id=>8, :name=>9, :x=>10, :y=>11, :lines_x=>12}}]
201
+ @ds1.graph(@ds2, :x=>:id).graph(@ds2, {:y=>:points__id}, :table_alias=>:graph).all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graph=>{:id=>8, :x=>9, :y=>10, :graph_id=>11}}]
213
202
  end
214
203
 
215
204
  it "#ungraphed should remove the splitting of result sets into component tables" do
216
- ds = @ds1.graph(@ds2, :x=>:id).ungraphed
217
- def ds.fetch_rows(sql, &block)
218
- yield({:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7})
219
- end
220
- ds.all.should == [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7}]
205
+ @db.fetch = {:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7}
206
+ @ds1.graph(@ds2, :x=>:id).ungraphed.all.should == [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7}]
221
207
  end
222
208
 
223
209
  it "#graph_each should give a nil value instead of a hash when all values for a table are nil" do
224
- ds = @ds1.graph(@ds2, :x=>:id)
225
- def ds.fetch_rows(sql, &block)
226
- yield({:id=>1,:x=>2,:y=>3,:lines_id=>nil,:lines_x=>nil,:lines_y=>nil,:graph_id=>nil})
227
- end
228
- results = ds.all
229
- results.length.should == 1
230
- results.first.should == {:points=>{:id=>1, :x=>2, :y=>3}, :lines=>nil}
210
+ @db.fetch = [[{:id=>1,:x=>2,:y=>3,:lines_id=>nil,:lines_x=>nil,:lines_y=>nil,:graph_id=>nil}],
211
+ [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>nil, :name=>nil, :graphs_x=>nil, :graphs_y=>nil, :graphs_lines_x=>nil},
212
+ {:id=>2,:x=>4,:y=>5,:lines_id=>nil,:lines_x=>nil,:lines_y=>nil,:graph_id=>nil, :graphs_id=>nil, :name=>nil, :graphs_x=>nil, :graphs_y=>nil, :graphs_lines_x=>nil},
213
+ {:id=>3,:x=>5,:y=>6,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>7, :name=>8, :graphs_x=>9, :graphs_y=>10, :graphs_lines_x=>11},
214
+ {:id=>3,:x=>5,:y=>6,:lines_id=>7,:lines_x=>5,:lines_y=>8,:graph_id=>9, :graphs_id=>9, :name=>10, :graphs_x=>10, :graphs_y=>11, :graphs_lines_x=>12}]]
231
215
 
232
- ds = @ds1.graph(@ds2, :x=>:id).graph(@ds3, :id=>:graph_id)
233
- def ds.fetch_rows(sql, &block)
234
- yield({:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>nil, :name=>nil, :graphs_x=>nil, :graphs_y=>nil, :graphs_lines_x=>nil})
235
- yield({:id=>2,:x=>4,:y=>5,:lines_id=>nil,:lines_x=>nil,:lines_y=>nil,:graph_id=>nil, :graphs_id=>nil, :name=>nil, :graphs_x=>nil, :graphs_y=>nil, :graphs_lines_x=>nil})
236
- yield({:id=>3,:x=>5,:y=>6,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7, :graphs_id=>7, :name=>8, :graphs_x=>9, :graphs_y=>10, :graphs_lines_x=>11})
237
- yield({:id=>3,:x=>5,:y=>6,:lines_id=>7,:lines_x=>5,:lines_y=>8,:graph_id=>9, :graphs_id=>9, :name=>10, :graphs_x=>10, :graphs_y=>11, :graphs_lines_x=>12})
238
- end
239
- results = ds.all
240
- results.length.should == 4
241
- results[0].should == {:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graphs=>nil}
242
- results[1].should == {:points=>{:id=>2, :x=>4, :y=>5}, :lines=>nil, :graphs=>nil}
243
- results[2].should == {:points=>{:id=>3, :x=>5, :y=>6}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graphs=>{:id=>7, :name=>8, :x=>9, :y=>10, :lines_x=>11}}
244
- results[3].should == {:points=>{:id=>3, :x=>5, :y=>6}, :lines=>{:id=>7, :x=>5, :y=>8, :graph_id=>9}, :graphs=>{:id=>9, :name=>10, :x=>10, :y=>11, :lines_x=>12}}
216
+ @ds1.graph(@ds2, :x=>:id).all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>nil}]
217
+ @ds1.graph(@ds2, :x=>:id).graph(@ds3, :id=>:graph_id).all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graphs=>nil},
218
+ {:points=>{:id=>2, :x=>4, :y=>5}, :lines=>nil, :graphs=>nil},
219
+ {:points=>{:id=>3, :x=>5, :y=>6}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graphs=>{:id=>7, :name=>8, :x=>9, :y=>10, :lines_x=>11}},
220
+ {:points=>{:id=>3, :x=>5, :y=>6}, :lines=>{:id=>7, :x=>5, :y=>8, :graph_id=>9}, :graphs=>{:id=>9, :name=>10, :x=>10, :y=>11, :lines_x=>12}}]
245
221
  end
246
222
 
247
223
  it "#graph_each should not give a nil value instead of a hash when any value for a table is false" do
248
- ds = @ds1.graph(@ds2, :x=>:id)
249
- def ds.fetch_rows(sql, &block)
250
- block.call(:id=>1,:x=>2,:y=>3,:lines_id=>nil,:lines_x=>false,:lines_y=>nil,:graph_id=>nil)
251
- end
252
- ds.all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>nil, :x=>false, :y=>nil, :graph_id=>nil}}]
224
+ @db.fetch = {:id=>1,:x=>2,:y=>3,:lines_id=>nil,:lines_x=>false,:lines_y=>nil,:graph_id=>nil}
225
+ @ds1.graph(@ds2, :x=>:id).all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>nil, :x=>false, :y=>nil, :graph_id=>nil}}]
253
226
  end
254
227
 
255
228
  it "#graph_each should not included tables graphed with the :select => false option in the result set" do
256
- ds = @ds1.graph(:lines, {:x=>:id}, :select=>false).graph(:graphs, :id=>:graph_id)
257
- def ds.fetch_rows(sql, &block)
258
- yield({:id=>1,:x=>2,:y=>3,:graphs_id=>8, :name=>9, :graphs_x=>10, :graphs_y=>11, :lines_x=>12})
259
- end
260
- results = ds.all
261
- results.length.should == 1
262
- results.first.should == {:points=>{:id=>1, :x=>2, :y=>3}, :graphs=>{:id=>8, :name=>9, :x=>10, :y=>11, :lines_x=>12}}
229
+ @db.fetch = {:id=>1,:x=>2,:y=>3,:graphs_id=>8, :name=>9, :graphs_x=>10, :graphs_y=>11, :lines_x=>12}
230
+ @ds1.graph(:lines, {:x=>:id}, :select=>false).graph(:graphs, :id=>:graph_id).all.should == [{:points=>{:id=>1, :x=>2, :y=>3}, :graphs=>{:id=>8, :name=>9, :x=>10, :y=>11, :lines_x=>12}}]
263
231
  end
264
232
 
265
233
  it "#graph_each should only include the columns selected with #set_graph_aliases and #add_graph_aliases, if called" do
266
- ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :x], :y=>[:lines, :y])
267
- def ds.fetch_rows(sql, &block)
268
- yield({:x=>2,:y=>3})
269
- end
270
- results = ds.all
271
- results.length.should == 1
272
- results.first.should == {:points=>{:x=>2}, :lines=>{:y=>3}}
234
+ @db.fetch = [nil, [{:x=>2,:y=>3}], nil, [{:x=>2}], [{:x=>2, :q=>18}]]
273
235
 
236
+ @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :x], :y=>[:lines, :y]).all.should == [{:points=>{:x=>2}, :lines=>{:y=>3}}]
274
237
  ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :x])
275
- def ds.fetch_rows(sql, &block)
276
- yield({:x=>2})
277
- end
278
- results = ds.all
279
- results.length.should == 1
280
- results.first.should == {:points=>{:x=>2}, :lines=>nil}
281
-
238
+ ds.all.should == [{:points=>{:x=>2}, :lines=>nil}]
282
239
  ds = ds.add_graph_aliases(:q=>[:points, :r, 18])
283
- def ds.fetch_rows(sql, &block)
284
- yield({:x=>2, :q=>18})
285
- end
286
240
  ds.all.should == [{:points=>{:x=>2, :r=>18}, :lines=>nil}]
287
241
  end
288
242
 
289
243
  it "#graph_each should correctly map values when #set_graph_aliases is used with a third argument for each entry" do
290
- ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :z1, 2], :y=>[:lines, :z2, :random.sql_function])
291
- def ds.fetch_rows(sql, &block)
292
- yield({:x=>2,:y=>3})
293
- end
294
- results = ds.all
295
- results.length.should == 1
296
- results.first.should == {:points=>{:z1=>2}, :lines=>{:z2=>3}}
244
+ @db.fetch = [nil, {:x=>2,:y=>3}]
245
+ @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :z1, 2], :y=>[:lines, :z2, :random.sql_function]).all.should == [{:points=>{:z1=>2}, :lines=>{:z2=>3}}]
297
246
  end
298
247
 
299
248
  it "#graph_each should correctly map values when #set_graph_aliases is used with a single argument for each entry" do
300
- ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points], :y=>[:lines])
301
- def ds.fetch_rows(sql, &block)
302
- yield({:x=>2,:y=>3})
303
- end
304
- results = ds.all
305
- results.length.should == 1
306
- results.first.should == {:points=>{:x=>2}, :lines=>{:y=>3}}
249
+ @db.fetch = [nil, {:x=>2,:y=>3}]
250
+ @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points], :y=>[:lines]).all.should == [{:points=>{:x=>2}, :lines=>{:y=>3}}]
307
251
  end
308
252
 
309
253
  it "#graph_each should correctly map values when #set_graph_aliases is used with a symbol for each entry" do
310
- ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>:points, :y=>:lines)
311
- def ds.fetch_rows(sql, &block)
312
- yield({:x=>2,:y=>3})
313
- end
314
- results = ds.all
315
- results.length.should == 1
316
- results.first.should == {:points=>{:x=>2}, :lines=>{:y=>3}}
254
+ @db.fetch = [nil, {:x=>2,:y=>3}]
255
+ @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>:points, :y=>:lines).all.should == [{:points=>{:x=>2}, :lines=>{:y=>3}}]
317
256
  end
318
257
 
319
258
  it "#graph_each should run the row_proc for graphed datasets" do
320
259
  @ds1.row_proc = proc{|h| h.keys.each{|k| h[k] *= 2}; h}
321
260
  @ds2.row_proc = proc{|h| h.keys.each{|k| h[k] *= 3}; h}
322
- ds = @ds1.graph(@ds2, :x=>:id)
323
- def ds.fetch_rows(sql, &block)
324
- yield({:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7})
325
- end
326
- results = ds.all
327
- results.length.should == 1
328
- results.first.should == {:points=>{:id=>2, :x=>4, :y=>6}, :lines=>{:id=>12, :x=>15, :y=>18, :graph_id=>21}}
261
+ @db.fetch = {:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7}
262
+ @ds1.graph(@ds2, :x=>:id).all.should == [{:points=>{:id=>2, :x=>4, :y=>6}, :lines=>{:id=>12, :x=>15, :y=>18, :graph_id=>21}}]
329
263
  end
330
264
  end