colincasey-sequel 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +7 -1
  2. data/doc/advanced_associations.rdoc +614 -0
  3. data/doc/cheat_sheet.rdoc +223 -0
  4. data/doc/dataset_filtering.rdoc +158 -0
  5. data/doc/prepared_statements.rdoc +104 -0
  6. data/doc/release_notes/1.0.txt +38 -0
  7. data/doc/release_notes/1.1.txt +143 -0
  8. data/doc/release_notes/1.3.txt +101 -0
  9. data/doc/release_notes/1.4.0.txt +53 -0
  10. data/doc/release_notes/1.5.0.txt +155 -0
  11. data/doc/release_notes/2.0.0.txt +298 -0
  12. data/doc/release_notes/2.1.0.txt +271 -0
  13. data/doc/release_notes/2.10.0.txt +328 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/doc/schema.rdoc +29 -0
  23. data/doc/sharding.rdoc +113 -0
  24. data/lib/sequel.rb +1 -0
  25. data/lib/sequel_core/adapters/ado.rb +89 -0
  26. data/lib/sequel_core/adapters/db2.rb +143 -0
  27. data/lib/sequel_core/adapters/dbi.rb +112 -0
  28. data/lib/sequel_core/adapters/do/mysql.rb +38 -0
  29. data/lib/sequel_core/adapters/do/postgres.rb +92 -0
  30. data/lib/sequel_core/adapters/do/sqlite.rb +31 -0
  31. data/lib/sequel_core/adapters/do.rb +205 -0
  32. data/lib/sequel_core/adapters/firebird.rb +298 -0
  33. data/lib/sequel_core/adapters/informix.rb +85 -0
  34. data/lib/sequel_core/adapters/jdbc/h2.rb +69 -0
  35. data/lib/sequel_core/adapters/jdbc/mysql.rb +66 -0
  36. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  37. data/lib/sequel_core/adapters/jdbc/postgresql.rb +113 -0
  38. data/lib/sequel_core/adapters/jdbc/sqlite.rb +43 -0
  39. data/lib/sequel_core/adapters/jdbc.rb +491 -0
  40. data/lib/sequel_core/adapters/mysql.rb +369 -0
  41. data/lib/sequel_core/adapters/odbc.rb +174 -0
  42. data/lib/sequel_core/adapters/openbase.rb +68 -0
  43. data/lib/sequel_core/adapters/oracle.rb +107 -0
  44. data/lib/sequel_core/adapters/postgres.rb +456 -0
  45. data/lib/sequel_core/adapters/shared/ms_access.rb +110 -0
  46. data/lib/sequel_core/adapters/shared/mssql.rb +102 -0
  47. data/lib/sequel_core/adapters/shared/mysql.rb +325 -0
  48. data/lib/sequel_core/adapters/shared/oracle.rb +61 -0
  49. data/lib/sequel_core/adapters/shared/postgres.rb +715 -0
  50. data/lib/sequel_core/adapters/shared/progress.rb +31 -0
  51. data/lib/sequel_core/adapters/shared/sqlite.rb +265 -0
  52. data/lib/sequel_core/adapters/sqlite.rb +248 -0
  53. data/lib/sequel_core/connection_pool.rb +258 -0
  54. data/lib/sequel_core/core_ext.rb +217 -0
  55. data/lib/sequel_core/core_sql.rb +202 -0
  56. data/lib/sequel_core/database/schema.rb +164 -0
  57. data/lib/sequel_core/database.rb +691 -0
  58. data/lib/sequel_core/dataset/callback.rb +13 -0
  59. data/lib/sequel_core/dataset/convenience.rb +237 -0
  60. data/lib/sequel_core/dataset/pagination.rb +96 -0
  61. data/lib/sequel_core/dataset/prepared_statements.rb +220 -0
  62. data/lib/sequel_core/dataset/query.rb +41 -0
  63. data/lib/sequel_core/dataset/schema.rb +15 -0
  64. data/lib/sequel_core/dataset/sql.rb +1010 -0
  65. data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
  66. data/lib/sequel_core/dataset/unsupported.rb +43 -0
  67. data/lib/sequel_core/dataset.rb +511 -0
  68. data/lib/sequel_core/deprecated.rb +26 -0
  69. data/lib/sequel_core/exceptions.rb +44 -0
  70. data/lib/sequel_core/migration.rb +212 -0
  71. data/lib/sequel_core/object_graph.rb +230 -0
  72. data/lib/sequel_core/pretty_table.rb +71 -0
  73. data/lib/sequel_core/schema/generator.rb +320 -0
  74. data/lib/sequel_core/schema/sql.rb +325 -0
  75. data/lib/sequel_core/schema.rb +2 -0
  76. data/lib/sequel_core/sql.rb +887 -0
  77. data/lib/sequel_core/version.rb +11 -0
  78. data/lib/sequel_core.rb +172 -0
  79. data/lib/sequel_model/association_reflection.rb +267 -0
  80. data/lib/sequel_model/associations.rb +499 -0
  81. data/lib/sequel_model/base.rb +523 -0
  82. data/lib/sequel_model/caching.rb +82 -0
  83. data/lib/sequel_model/dataset_methods.rb +26 -0
  84. data/lib/sequel_model/eager_loading.rb +370 -0
  85. data/lib/sequel_model/exceptions.rb +7 -0
  86. data/lib/sequel_model/hooks.rb +101 -0
  87. data/lib/sequel_model/inflector.rb +281 -0
  88. data/lib/sequel_model/plugins.rb +62 -0
  89. data/lib/sequel_model/record.rb +568 -0
  90. data/lib/sequel_model/schema.rb +49 -0
  91. data/lib/sequel_model/validations.rb +429 -0
  92. data/lib/sequel_model.rb +91 -0
  93. data/spec/adapters/ado_spec.rb +46 -0
  94. data/spec/adapters/firebird_spec.rb +376 -0
  95. data/spec/adapters/informix_spec.rb +96 -0
  96. data/spec/adapters/mysql_spec.rb +881 -0
  97. data/spec/adapters/oracle_spec.rb +244 -0
  98. data/spec/adapters/postgres_spec.rb +687 -0
  99. data/spec/adapters/spec_helper.rb +10 -0
  100. data/spec/adapters/sqlite_spec.rb +555 -0
  101. data/spec/integration/dataset_test.rb +134 -0
  102. data/spec/integration/eager_loader_test.rb +696 -0
  103. data/spec/integration/prepared_statement_test.rb +130 -0
  104. data/spec/integration/schema_test.rb +180 -0
  105. data/spec/integration/spec_helper.rb +58 -0
  106. data/spec/integration/type_test.rb +96 -0
  107. data/spec/rcov.opts +6 -0
  108. data/spec/sequel_core/connection_pool_spec.rb +526 -0
  109. data/spec/sequel_core/core_ext_spec.rb +156 -0
  110. data/spec/sequel_core/core_sql_spec.rb +522 -0
  111. data/spec/sequel_core/database_spec.rb +1188 -0
  112. data/spec/sequel_core/dataset_spec.rb +3481 -0
  113. data/spec/sequel_core/expression_filters_spec.rb +363 -0
  114. data/spec/sequel_core/migration_spec.rb +261 -0
  115. data/spec/sequel_core/object_graph_spec.rb +272 -0
  116. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  117. data/spec/sequel_core/schema_generator_spec.rb +167 -0
  118. data/spec/sequel_core/schema_spec.rb +780 -0
  119. data/spec/sequel_core/spec_helper.rb +55 -0
  120. data/spec/sequel_core/version_spec.rb +7 -0
  121. data/spec/sequel_model/association_reflection_spec.rb +93 -0
  122. data/spec/sequel_model/associations_spec.rb +1767 -0
  123. data/spec/sequel_model/base_spec.rb +419 -0
  124. data/spec/sequel_model/caching_spec.rb +215 -0
  125. data/spec/sequel_model/dataset_methods_spec.rb +78 -0
  126. data/spec/sequel_model/eager_loading_spec.rb +1165 -0
  127. data/spec/sequel_model/hooks_spec.rb +485 -0
  128. data/spec/sequel_model/inflector_spec.rb +119 -0
  129. data/spec/sequel_model/model_spec.rb +588 -0
  130. data/spec/sequel_model/plugins_spec.rb +80 -0
  131. data/spec/sequel_model/record_spec.rb +1184 -0
  132. data/spec/sequel_model/schema_spec.rb +90 -0
  133. data/spec/sequel_model/spec_helper.rb +78 -0
  134. data/spec/sequel_model/validations_spec.rb +1067 -0
  135. data/spec/spec.opts +0 -0
  136. data/spec/spec_config.rb.example +10 -0
  137. metadata +177 -3
@@ -0,0 +1,555 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ unless defined?(SQLITE_DB)
4
+ SQLITE_URL = 'sqlite:/' unless defined? SQLITE_URL
5
+ SQLITE_DB = Sequel.connect(ENV['SEQUEL_SQLITE_SPEC_DB']||SQLITE_URL)
6
+ end
7
+
8
+ context "An SQLite database" do
9
+ before do
10
+ @db = SQLITE_DB
11
+ end
12
+ after do
13
+ Sequel.datetime_class = Time
14
+ end
15
+
16
+ if SQLITE_DB.auto_vacuum == :none
17
+ specify "should support getting pragma values" do
18
+ @db.pragma_get(:auto_vacuum).to_s.should == '0'
19
+ end
20
+
21
+ specify "should support setting pragma values" do
22
+ @db.pragma_set(:auto_vacuum, '1')
23
+ @db.pragma_get(:auto_vacuum).to_s.should == '1'
24
+ @db.pragma_set(:auto_vacuum, '2')
25
+ @db.pragma_get(:auto_vacuum).to_s.should == '2'
26
+ end
27
+
28
+ specify "should support getting and setting the auto_vacuum pragma" do
29
+ @db.auto_vacuum = :full
30
+ @db.auto_vacuum.should == :full
31
+ @db.auto_vacuum = :incremental
32
+ @db.auto_vacuum.should == :incremental
33
+
34
+ proc {@db.auto_vacuum = :invalid}.should raise_error(Sequel::Error)
35
+ end
36
+ end
37
+
38
+ specify "should provide a list of existing tables" do
39
+ @db.drop_table(:testing) rescue nil
40
+ @db.tables.should be_a_kind_of(Array)
41
+ @db.tables.should_not include(:testing)
42
+ @db.create_table! :testing do
43
+ text :name
44
+ end
45
+ @db.tables.should include(:testing)
46
+ end
47
+
48
+ specify "should support getting and setting the synchronous pragma" do
49
+ @db.synchronous = :off
50
+ @db.synchronous.should == :off
51
+ @db.synchronous = :normal
52
+ @db.synchronous.should == :normal
53
+ @db.synchronous = :full
54
+ @db.synchronous.should == :full
55
+
56
+ proc {@db.synchronous = :invalid}.should raise_error(Sequel::Error)
57
+ end
58
+
59
+ specify "should support getting and setting the temp_store pragma" do
60
+ @db.temp_store = :default
61
+ @db.temp_store.should == :default
62
+ @db.temp_store = :file
63
+ @db.temp_store.should == :file
64
+ @db.temp_store = :memory
65
+ @db.temp_store.should == :memory
66
+
67
+ proc {@db.temp_store = :invalid}.should raise_error(Sequel::Error)
68
+ end
69
+
70
+ specify "should be able to execute transactions" do
71
+ @db.transaction do
72
+ @db.create_table!(:t) {text :name}
73
+ end
74
+
75
+ @db.tables.should include(:t)
76
+
77
+ proc {@db.transaction do
78
+ @db.create_table!(:u) {text :name}
79
+ raise ArgumentError
80
+ end}.should raise_error(ArgumentError)
81
+ # no commit
82
+ @db.tables.should_not include(:u)
83
+
84
+ proc {@db.transaction do
85
+ @db.create_table!(:v) {text :name}
86
+ raise Sequel::Error::Rollback
87
+ end}.should_not raise_error
88
+ # no commit
89
+ @db.tables.should_not include(:r)
90
+ end
91
+
92
+ specify "should support nested transactions" do
93
+ @db.transaction do
94
+ @db.transaction do
95
+ @db.create_table!(:t) {text :name}
96
+ end
97
+ end
98
+
99
+ @db.tables.should include(:t)
100
+
101
+ proc {@db.transaction do
102
+ @db.create_table!(:v) {text :name}
103
+ @db.transaction do
104
+ raise Sequel::Error::Rollback # should roll back the top-level transaction
105
+ end
106
+ end}.should_not raise_error
107
+ # no commit
108
+ @db.tables.should_not include(:v)
109
+ end
110
+
111
+ specify "should handle returning inside of transaction by committing" do
112
+ @db.create_table!(:items2){text :name}
113
+ def @db.ret_commit
114
+ transaction do
115
+ self[:items2] << {:name => 'abc'}
116
+ return
117
+ self[:items2] << {:name => 'd'}
118
+ end
119
+ end
120
+ @db[:items2].count.should == 0
121
+ @db.ret_commit
122
+ @db[:items2].count.should == 1
123
+ @db.ret_commit
124
+ @db[:items2].count.should == 2
125
+ proc do
126
+ @db.transaction do
127
+ raise Interrupt, 'asdf'
128
+ end
129
+ end.should raise_error(Interrupt)
130
+
131
+ @db[:items2].count.should == 2
132
+ end
133
+
134
+ specify "should support timestamps and datetimes and respect datetime_class" do
135
+ @db.create_table!(:time){timestamp :t; datetime :d}
136
+ t1 = Time.at(1)
137
+ @db[:time] << {:t => t1, :d => t1.to_i}
138
+ @db[:time] << {:t => t1.to_i, :d => t1}
139
+ @db[:time].map(:t).should == [t1, t1]
140
+ @db[:time].map(:d).should == [t1, t1]
141
+ t2 = t1.iso8601.to_datetime
142
+ Sequel.datetime_class = DateTime
143
+ @db[:time].map(:t).should == [t2, t2]
144
+ @db[:time].map(:d).should == [t2, t2]
145
+ end
146
+
147
+ specify "should support sequential primary keys" do
148
+ @db.create_table!(:with_pk) {primary_key :id; text :name}
149
+ @db[:with_pk] << {:name => 'abc'}
150
+ @db[:with_pk] << {:name => 'def'}
151
+ @db[:with_pk] << {:name => 'ghi'}
152
+ @db[:with_pk].order(:name).all.should == [
153
+ {:id => 1, :name => 'abc'},
154
+ {:id => 2, :name => 'def'},
155
+ {:id => 3, :name => 'ghi'}
156
+ ]
157
+ end
158
+
159
+ specify "should catch invalid SQL errors and raise them as Error" do
160
+ proc {@db.execute 'blah blah'}.should raise_error(Sequel::Error)
161
+ proc {@db.execute_insert 'blah blah'}.should raise_error(Sequel::Error)
162
+ end
163
+
164
+ specify "should not swallow non-SQLite based exceptions" do
165
+ proc {@db.pool.hold{raise Interrupt, "test"}}.should raise_error(Interrupt)
166
+ end
167
+
168
+ specify "should correctly parse the schema" do
169
+ @db.create_table!(:time2) {timestamp :t}
170
+ @db.schema(:time2, :reload=>true).should == [[:t, {:type=>:datetime, :allow_null=>true, :default=>nil, :db_type=>"timestamp", :primary_key=>false}]]
171
+ end
172
+
173
+ specify "should get the schema all database tables if no table name is used" do
174
+ @db.create_table!(:time2) {timestamp :t}
175
+ @db.schema(:time2, :reload=>true).should == @db.schema(nil, :reload=>true)[:time2]
176
+ end
177
+ end
178
+
179
+ context "An SQLite dataset" do
180
+ setup do
181
+ SQLITE_DB.create_table! :items do
182
+ integer :id, :primary_key => true, :auto_increment => true
183
+ text :name
184
+ float :value
185
+ end
186
+ @d = SQLITE_DB[:items]
187
+ @d.delete # remove all records
188
+ end
189
+
190
+ specify "should return the correct records" do
191
+ @d.to_a.should == []
192
+ @d << {:name => 'abc', :value => 1.23}
193
+ @d << {:name => 'abc', :value => 4.56}
194
+ @d << {:name => 'def', :value => 7.89}
195
+ @d.select(:name, :value).to_a.sort_by {|h| h[:value]}.should == [
196
+ {:name => 'abc', :value => 1.23},
197
+ {:name => 'abc', :value => 4.56},
198
+ {:name => 'def', :value => 7.89}
199
+ ]
200
+ end
201
+
202
+ specify "should return the correct record count" do
203
+ @d.count.should == 0
204
+ @d << {:name => 'abc', :value => 1.23}
205
+ @d << {:name => 'abc', :value => 4.56}
206
+ @d << {:name => 'def', :value => 7.89}
207
+ @d.count.should == 3
208
+ end
209
+
210
+ specify "should return the last inserted id when inserting records" do
211
+ id = @d << {:name => 'abc', :value => 1.23}
212
+ id.should == @d.first[:id]
213
+ end
214
+
215
+ specify "should update records correctly" do
216
+ @d << {:name => 'abc', :value => 1.23}
217
+ @d << {:name => 'abc', :value => 4.56}
218
+ @d << {:name => 'def', :value => 7.89}
219
+ @d.filter(:name => 'abc').update(:value => 5.3)
220
+
221
+ # the third record should stay the same
222
+ @d[:name => 'def'][:value].should == 7.89
223
+ @d.filter(:value => 5.3).count.should == 2
224
+ end
225
+
226
+ specify "should delete records correctly" do
227
+ @d << {:name => 'abc', :value => 1.23}
228
+ @d << {:name => 'abc', :value => 4.56}
229
+ @d << {:name => 'def', :value => 7.89}
230
+ @d.filter(:name => 'abc').delete
231
+
232
+ @d.count.should == 1
233
+ @d.first[:name].should == 'def'
234
+ end
235
+
236
+ specify "should handle string pattern matches correctly" do
237
+ @d.literal(:x.like('a')).should == "(x LIKE 'a')"
238
+ @d.literal(~:x.like('a')).should == "NOT (x LIKE 'a')"
239
+ @d.literal(:x.ilike('a')).should == "(x LIKE 'a')"
240
+ @d.literal(~:x.ilike('a')).should == "NOT (x LIKE 'a')"
241
+ end
242
+
243
+ specify "should raise errors if given a regexp pattern match" do
244
+ proc{@d.literal(:x.like(/a/))}.should raise_error(Sequel::Error)
245
+ proc{@d.literal(~:x.like(/a/))}.should raise_error(Sequel::Error)
246
+ proc{@d.literal(:x.like(/a/i))}.should raise_error(Sequel::Error)
247
+ proc{@d.literal(~:x.like(/a/i))}.should raise_error(Sequel::Error)
248
+ end
249
+ end
250
+
251
+ context "An SQLite numeric column" do
252
+ specify "should handle and return BigDecimal values" do
253
+ SQLITE_DB.create_table!(:d){numeric :d}
254
+ d = SQLITE_DB[:d]
255
+ d.insert(:d=>BigDecimal.new('80.0'))
256
+ d.insert(:d=>BigDecimal.new('NaN'))
257
+ d.insert(:d=>BigDecimal.new('Infinity'))
258
+ d.insert(:d=>BigDecimal.new('-Infinity'))
259
+ ds = d.all
260
+ ds.shift.should == {:d=>BigDecimal.new('80.0')}
261
+ ds.map{|x| x[:d].to_s}.should == %w'NaN Infinity -Infinity'
262
+ end
263
+ end
264
+
265
+ context "An SQLite dataset AS clause" do
266
+ specify "should use a string literal for :col___alias" do
267
+ SQLITE_DB.literal(:c___a).should == "c AS 'a'"
268
+ end
269
+
270
+ specify "should use a string literal for :table__col___alias" do
271
+ SQLITE_DB.literal(:t__c___a).should == "t.c AS 'a'"
272
+ end
273
+
274
+ specify "should use a string literal for :column.as(:alias)" do
275
+ SQLITE_DB.literal(:c.as(:a)).should == "c AS 'a'"
276
+ end
277
+
278
+ specify "should use a string literal in the SELECT clause" do
279
+ SQLITE_DB[:t].select(:c___a).sql.should == "SELECT c AS 'a' FROM t"
280
+ end
281
+
282
+ specify "should use a string literal in the FROM clause" do
283
+ SQLITE_DB[:t___a].sql.should == "SELECT * FROM t AS 'a'"
284
+ end
285
+
286
+ specify "should use a string literal in the JOIN clause" do
287
+ SQLITE_DB[:t].join_table(:natural, :j, nil, :a).sql.should == "SELECT * FROM t NATURAL JOIN j AS 'a'"
288
+ end
289
+ end
290
+
291
+ context "An SQLite dataset" do
292
+ setup do
293
+ SQLITE_DB.create_table! :items do
294
+ integer :id, :primary_key => true, :auto_increment => true
295
+ text :name
296
+ float :value
297
+ end
298
+ @d = SQLITE_DB[:items]
299
+ @d.delete # remove all records
300
+ @d << {:name => 'abc', :value => 1.23}
301
+ @d << {:name => 'def', :value => 4.56}
302
+ @d << {:name => 'ghi', :value => 7.89}
303
+ end
304
+
305
+ specify "should correctly return avg" do
306
+ @d.avg(:value).to_s.should == ((1.23 + 4.56 + 7.89) / 3).to_s
307
+ end
308
+
309
+ specify "should correctly return sum" do
310
+ @d.sum(:value).to_s.should == (1.23 + 4.56 + 7.89).to_s
311
+ end
312
+
313
+ specify "should correctly return max" do
314
+ @d.max(:value).to_s.should == 7.89.to_s
315
+ end
316
+
317
+ specify "should correctly return min" do
318
+ @d.min(:value).to_s.should == 1.23.to_s
319
+ end
320
+ end
321
+
322
+ context "SQLite::Dataset#delete" do
323
+ setup do
324
+ SQLITE_DB.create_table! :items do
325
+ integer :id, :primary_key => true, :auto_increment => true
326
+ text :name
327
+ float :value
328
+ end
329
+ @d = SQLITE_DB[:items]
330
+ @d.delete # remove all records
331
+ @d << {:name => 'abc', :value => 1.23}
332
+ @d << {:name => 'def', :value => 4.56}
333
+ @d << {:name => 'ghi', :value => 7.89}
334
+ end
335
+
336
+ specify "should return the number of records affected when filtered" do
337
+ @d.count.should == 3
338
+ @d.filter {:value.sql_number < 3}.delete.should == 1
339
+ @d.count.should == 2
340
+
341
+ @d.filter {:value.sql_number < 3}.delete.should == 0
342
+ @d.count.should == 2
343
+ end
344
+
345
+ specify "should return the number of records affected when unfiltered" do
346
+ @d.count.should == 3
347
+ @d.delete.should == 3
348
+ @d.count.should == 0
349
+
350
+ @d.delete.should == 0
351
+ end
352
+ end
353
+
354
+ context "SQLite::Dataset#update" do
355
+ setup do
356
+ SQLITE_DB.create_table! :items do
357
+ integer :id, :primary_key => true, :auto_increment => true
358
+ text :name
359
+ float :value
360
+ end
361
+ @d = SQLITE_DB[:items]
362
+ @d.delete # remove all records
363
+ @d << {:name => 'abc', :value => 1.23}
364
+ @d << {:name => 'def', :value => 4.56}
365
+ @d << {:name => 'ghi', :value => 7.89}
366
+ end
367
+
368
+ specify "should return the number of records affected" do
369
+ @d.filter(:name => 'abc').update(:value => 2).should == 1
370
+
371
+ @d.update(:value => 10).should == 3
372
+
373
+ @d.filter(:name => 'xxx').update(:value => 23).should == 0
374
+ end
375
+ end
376
+
377
+ context "SQLite dataset" do
378
+ setup do
379
+ SQLITE_DB.create_table! :test do
380
+ integer :id, :primary_key => true, :auto_increment => true
381
+ text :name
382
+ float :value
383
+ end
384
+ SQLITE_DB.create_table! :items do
385
+ integer :id, :primary_key => true, :auto_increment => true
386
+ text :name
387
+ float :value
388
+ end
389
+ @d = SQLITE_DB[:items]
390
+ @d.delete # remove all records
391
+ @d << {:name => 'abc', :value => 1.23}
392
+ @d << {:name => 'def', :value => 4.56}
393
+ @d << {:name => 'ghi', :value => 7.89}
394
+ end
395
+
396
+ teardown do
397
+ SQLITE_DB.drop_table :test
398
+ end
399
+
400
+ specify "should be able to insert from a subquery" do
401
+ SQLITE_DB[:test] << @d
402
+ SQLITE_DB[:test].count.should == 3
403
+ SQLITE_DB[:test].select(:name, :value).order(:value).to_a.should == \
404
+ @d.select(:name, :value).order(:value).to_a
405
+ end
406
+ end
407
+
408
+ context "A SQLite database" do
409
+ setup do
410
+ @db = SQLITE_DB
411
+ @db.create_table! :test2 do
412
+ text :name
413
+ integer :value
414
+ end
415
+ end
416
+
417
+ specify "should support add_column operations" do
418
+ @db.add_column :test2, :xyz, :text
419
+
420
+ @db[:test2].columns.should == [:name, :value, :xyz]
421
+ @db[:test2] << {:name => 'mmm', :value => 111, :xyz=>'000'}
422
+ @db[:test2].first.should == {:name => 'mmm', :value => 111, :xyz=>'000'}
423
+ end
424
+
425
+ specify "should support drop_column operations" do
426
+ @db.drop_column :test2, :value
427
+ @db[:test2].columns.should == [:name]
428
+ @db[:test2] << {:name => 'mmm'}
429
+ @db[:test2].first.should == {:name => 'mmm'}
430
+ end
431
+
432
+ specify "should support drop_column operations in a transaction" do
433
+ @db.transaction{@db.drop_column :test2, :value}
434
+ @db[:test2].columns.should == [:name]
435
+ @db[:test2] << {:name => 'mmm'}
436
+ @db[:test2].first.should == {:name => 'mmm'}
437
+ end
438
+
439
+ specify "should keep column attributes when dropping a column" do
440
+ @db.create_table! :test3 do
441
+ primary_key :id
442
+ text :name
443
+ integer :value
444
+ end
445
+
446
+ # This lame set of additions and deletions are to test that the primary keys
447
+ # don't get messed up when we recreate the database.
448
+ @db[:test3] << { :name => "foo", :value => 1}
449
+ @db[:test3] << { :name => "foo", :value => 2}
450
+ @db[:test3] << { :name => "foo", :value => 3}
451
+ @db[:test3].filter(:id => 2).delete
452
+
453
+ @db.drop_column :test3, :value
454
+
455
+ @db['PRAGMA table_info(?)', :test3][:id][:pk].to_i.should == 1
456
+ @db[:test3].select(:id).all.should == [{:id => 1}, {:id => 3}]
457
+ end
458
+
459
+ specify "should support rename_column operations" do
460
+ @db[:test2].delete
461
+ @db.add_column :test2, :xyz, :text
462
+ @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
463
+
464
+ @db[:test2].columns.should == [:name, :value, :xyz]
465
+ @db.rename_column :test2, :xyz, :zyx, :type => :text
466
+ @db[:test2].columns.should == [:name, :value, :zyx]
467
+ @db[:test2].first[:zyx].should == 'qqqq'
468
+ @db[:test2].count.should eql(1)
469
+ end
470
+
471
+ specify "should preserve defaults when dropping or renaming columns" do
472
+ @db.create_table! :test3 do
473
+ String :s, :default=>'a'
474
+ Integer :i
475
+ end
476
+
477
+ @db[:test3].insert
478
+ @db[:test3].first[:s].should == 'a'
479
+ @db[:test3].delete
480
+ @db.drop_column :test3, :i
481
+ @db[:test3].insert
482
+ @db[:test3].first[:s].should == 'a'
483
+ @db[:test3].delete
484
+ @db.rename_column :test3, :s, :t
485
+ @db[:test3].insert
486
+ @db[:test3].first[:t].should == 'a'
487
+ @db[:test3].delete
488
+ end
489
+
490
+ specify "should handle quoted tables when dropping or renaming columns" do
491
+ @db.quote_identifiers = true
492
+ table_name = "T T"
493
+ @db.drop_table(table_name) rescue nil
494
+ @db.create_table! table_name do
495
+ Integer :"s s"
496
+ Integer :"i i"
497
+ end
498
+
499
+ @db.from(table_name).insert(:"s s"=>1, :"i i"=>2)
500
+ @db.from(table_name).all.should == [{:"s s"=>1, :"i i"=>2}]
501
+ @db.drop_column table_name, :"i i"
502
+ @db.from(table_name).all.should == [{:"s s"=>1}]
503
+ @db.rename_column table_name, :"s s", :"t t"
504
+ @db.from(table_name).all.should == [{:"t t"=>1}]
505
+ end
506
+
507
+ specify "should choose a temporary table name that isn't already used when dropping or renaming columns" do
508
+ @db.create_table! :test3 do
509
+ Integer :h
510
+ Integer :i
511
+ end
512
+ @db.create_table! :test3_backup0 do
513
+ Integer :j
514
+ end
515
+ @db.create_table! :test3_backup1 do
516
+ Integer :k
517
+ end
518
+
519
+ @db[:test3].columns.should == [:h, :i]
520
+ @db[:test3_backup0].columns.should == [:j]
521
+ @db[:test3_backup1].columns.should == [:k]
522
+ sqls = @db.drop_column(:test3, :i)
523
+ sqls.any?{|x| x =~ /test3_backup2/}.should == true
524
+ sqls.any?{|x| x =~ /test3_backup[01]/}.should == false
525
+ @db[:test3].columns.should == [:h]
526
+ @db[:test3_backup0].columns.should == [:j]
527
+ @db[:test3_backup1].columns.should == [:k]
528
+
529
+ @db.create_table! :test3_backup2 do
530
+ Integer :l
531
+ end
532
+
533
+ sqls = @db.rename_column(:test3, :h, :i)
534
+ sqls.any?{|x| x =~ /test3_backup3/}.should == true
535
+ sqls.any?{|x| x =~ /test3_backup[012]/}.should == false
536
+ @db[:test3].columns.should == [:i]
537
+ @db[:test3_backup0].columns.should == [:j]
538
+ @db[:test3_backup1].columns.should == [:k]
539
+ @db[:test3_backup2].columns.should == [:l]
540
+ end
541
+
542
+ specify "should not support set_column_type operations" do
543
+ proc {@db.set_column_type :test2, :value, :integer}.should raise_error(Sequel::Error)
544
+ end
545
+
546
+ specify "should support add_index" do
547
+ @db.add_index :test2, :value, :unique => true
548
+ @db.add_index :test2, [:name, :value]
549
+ end
550
+
551
+ specify "should support drop_index" do
552
+ @db.add_index :test2, :value, :unique => true
553
+ @db.drop_index :test2, :value
554
+ end
555
+ end
@@ -0,0 +1,134 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ describe "Simple Dataset operations" do
4
+ before do
5
+ INTEGRATION_DB.create_table!(:items) do
6
+ primary_key :id
7
+ integer :number
8
+ end
9
+ @ds = INTEGRATION_DB[:items]
10
+ @ds.insert(:number=>10)
11
+ clear_sqls
12
+ end
13
+ after do
14
+ INTEGRATION_DB.drop_table(:items)
15
+ end
16
+
17
+ specify "should insert with a primary key specified" do
18
+ @ds.insert(:id=>100, :number=>20)
19
+ sqls_should_be(/INSERT INTO items \((number, id|id, number)\) VALUES \((100, 20|20, 100)\)/)
20
+ @ds.count.should == 2
21
+ @ds.order(:id).all.should == [{:id=>1, :number=>10}, {:id=>100, :number=>20}]
22
+ end
23
+
24
+ specify "should have insert return primary key value" do
25
+ @ds.insert(:number=>20).should == 2
26
+ sqls_should_be('INSERT INTO items (number) VALUES (20)')
27
+ @ds.filter(:id=>2).first[:number].should == 20
28
+ end
29
+
30
+ specify "should delete correctly" do
31
+ @ds.filter(1=>1).delete.should == 1
32
+ sqls_should_be('DELETE FROM items WHERE (1 = 1)')
33
+ @ds.count.should == 0
34
+ end
35
+
36
+ specify "should update correctly" do
37
+ @ds.update(:number=>:number+1).should == 1
38
+ sqls_should_be('UPDATE items SET number = (number + 1)')
39
+ @ds.all.should == [{:id=>1, :number=>11}]
40
+ end
41
+
42
+ specify "should fetch all results correctly" do
43
+ @ds.all.should == [{:id=>1, :number=>10}]
44
+ sqls_should_be('SELECT * FROM items')
45
+ end
46
+
47
+ specify "should fetch a single row correctly" do
48
+ @ds.first.should == {:id=>1, :number=>10}
49
+ sqls_should_be('SELECT * FROM items LIMIT 1')
50
+ end
51
+ end
52
+
53
+ describe "Simple Dataset operations in transactions" do
54
+ before do
55
+ INTEGRATION_DB.create_table!(:items_insert_in_transaction) do
56
+ primary_key :id
57
+ integer :number
58
+ end
59
+ @ds = INTEGRATION_DB[:items_insert_in_transaction]
60
+ clear_sqls
61
+ end
62
+ after do
63
+ INTEGRATION_DB.drop_table(:items_insert_in_transaction)
64
+ end
65
+
66
+ specify "should insert correctly with a primary key specified inside a transaction" do
67
+ INTEGRATION_DB.transaction do
68
+ @ds.insert(:id=>100, :number=>20)
69
+ sqls_should_be('Transaction.begin', /INSERT INTO items_insert_in_transaction \((number, id|id, number)\) VALUES \((100, 20|20, 100)\)/)
70
+ @ds.count.should == 1
71
+ @ds.order(:id).all.should == [{:id=>100, :number=>20}]
72
+ end
73
+ end
74
+
75
+ specify "should have insert return primary key value inside a transaction" do
76
+ INTEGRATION_DB.transaction do
77
+ @ds.insert(:number=>20).should == 1
78
+ sqls_should_be('Transaction.begin', /INSERT INTO items_insert_in_transaction \(number\) VALUES \(20\)/)
79
+ @ds.count.should == 1
80
+ @ds.order(:id).all.should == [{:id=>1, :number=>20}]
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "Dataset UNION, EXCEPT, and INTERSECT" do
86
+ before do
87
+ INTEGRATION_DB.create_table!(:i1){integer :number}
88
+ INTEGRATION_DB.create_table!(:i2){integer :number}
89
+ INTEGRATION_DB.create_table!(:i3){integer :number}
90
+ @ds1 = INTEGRATION_DB[:i1]
91
+ @ds1.insert(:number=>10)
92
+ @ds1.insert(:number=>20)
93
+ @ds2 = INTEGRATION_DB[:i2]
94
+ @ds2.insert(:number=>10)
95
+ @ds2.insert(:number=>30)
96
+ @ds3 = INTEGRATION_DB[:i3]
97
+ @ds3.insert(:number=>10)
98
+ @ds3.insert(:number=>40)
99
+ clear_sqls
100
+ end
101
+
102
+ specify "should give the correct results for simple UNION, EXCEPT, and INTERSECT" do
103
+ @ds1.union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
104
+ unless @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
105
+ @ds1.except(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'20'
106
+ @ds1.intersect(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'10'
107
+ end
108
+ end
109
+
110
+ specify "should give the correct results for compound UNION, EXCEPT, and INTERSECT" do
111
+ @ds1.union(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
112
+ @ds1.union(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
113
+ unless @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
114
+ @ds1.union(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'20 30'
115
+ @ds1.union(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30'
116
+ @ds1.union(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 '
117
+ @ds1.union(@ds2.intersect(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20'
118
+
119
+ @ds1.except(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 40'
120
+ @ds1.except(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'20'
121
+ @ds1.except(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'20'
122
+ @ds1.except(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20'
123
+ @ds1.except(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w''
124
+ @ds1.except(@ds2.intersect(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'20'
125
+
126
+ @ds1.intersect(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 40'
127
+ @ds1.intersect(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10'
128
+ @ds1.intersect(@ds2).except(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w''
129
+ @ds1.intersect(@ds2.except(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w''
130
+ @ds1.intersect(@ds2).intersect(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10'
131
+ @ds1.intersect(@ds2.intersect(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10'
132
+ end
133
+ end
134
+ end