sequel_core 1.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.
Files changed (57) hide show
  1. data/CHANGELOG +1003 -0
  2. data/COPYING +18 -0
  3. data/README +81 -0
  4. data/Rakefile +176 -0
  5. data/bin/sequel +41 -0
  6. data/lib/sequel_core.rb +59 -0
  7. data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
  8. data/lib/sequel_core/adapters/ado.rb +100 -0
  9. data/lib/sequel_core/adapters/db2.rb +158 -0
  10. data/lib/sequel_core/adapters/dbi.rb +126 -0
  11. data/lib/sequel_core/adapters/informix.rb +87 -0
  12. data/lib/sequel_core/adapters/jdbc.rb +108 -0
  13. data/lib/sequel_core/adapters/mysql.rb +269 -0
  14. data/lib/sequel_core/adapters/odbc.rb +145 -0
  15. data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
  16. data/lib/sequel_core/adapters/openbase.rb +90 -0
  17. data/lib/sequel_core/adapters/oracle.rb +99 -0
  18. data/lib/sequel_core/adapters/postgres.rb +519 -0
  19. data/lib/sequel_core/adapters/sqlite.rb +192 -0
  20. data/lib/sequel_core/array_keys.rb +296 -0
  21. data/lib/sequel_core/connection_pool.rb +152 -0
  22. data/lib/sequel_core/core_ext.rb +59 -0
  23. data/lib/sequel_core/core_sql.rb +191 -0
  24. data/lib/sequel_core/database.rb +433 -0
  25. data/lib/sequel_core/dataset.rb +409 -0
  26. data/lib/sequel_core/dataset/convenience.rb +321 -0
  27. data/lib/sequel_core/dataset/sequelizer.rb +354 -0
  28. data/lib/sequel_core/dataset/sql.rb +586 -0
  29. data/lib/sequel_core/exceptions.rb +45 -0
  30. data/lib/sequel_core/migration.rb +191 -0
  31. data/lib/sequel_core/model.rb +8 -0
  32. data/lib/sequel_core/pretty_table.rb +73 -0
  33. data/lib/sequel_core/schema.rb +8 -0
  34. data/lib/sequel_core/schema/schema_generator.rb +131 -0
  35. data/lib/sequel_core/schema/schema_sql.rb +131 -0
  36. data/lib/sequel_core/worker.rb +58 -0
  37. data/spec/adapters/informix_spec.rb +139 -0
  38. data/spec/adapters/mysql_spec.rb +330 -0
  39. data/spec/adapters/oracle_spec.rb +130 -0
  40. data/spec/adapters/postgres_spec.rb +189 -0
  41. data/spec/adapters/sqlite_spec.rb +345 -0
  42. data/spec/array_keys_spec.rb +679 -0
  43. data/spec/connection_pool_spec.rb +356 -0
  44. data/spec/core_ext_spec.rb +67 -0
  45. data/spec/core_sql_spec.rb +301 -0
  46. data/spec/database_spec.rb +812 -0
  47. data/spec/dataset_spec.rb +2381 -0
  48. data/spec/migration_spec.rb +261 -0
  49. data/spec/pretty_table_spec.rb +66 -0
  50. data/spec/rcov.opts +4 -0
  51. data/spec/schema_generator_spec.rb +86 -0
  52. data/spec/schema_spec.rb +230 -0
  53. data/spec/sequelizer_spec.rb +448 -0
  54. data/spec/spec.opts +5 -0
  55. data/spec/spec_helper.rb +44 -0
  56. data/spec/worker_spec.rb +96 -0
  57. metadata +162 -0
@@ -0,0 +1,812 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ context "A new Database" do
4
+ setup do
5
+ @db = Sequel::Database.new(1 => 2, :logger => 3)
6
+ end
7
+
8
+ specify "should receive options" do
9
+ @db.opts.should == {1 => 2, :logger => 3}
10
+ end
11
+
12
+ specify "should set the logger from opts[:logger]" do
13
+ @db.logger.should == 3
14
+ end
15
+
16
+ specify "should create a connection pool" do
17
+ @db.pool.should be_a_kind_of(Sequel::ConnectionPool)
18
+ @db.pool.max_size.should == 4
19
+
20
+ Sequel::Database.new(:max_connections => 10).pool.max_size.should == 10
21
+ end
22
+
23
+ specify "should pass the supplied block to the connection pool" do
24
+ cc = nil
25
+ d = Sequel::Database.new {1234}
26
+ d.synchronize {|c| cc = c}
27
+ cc.should == 1234
28
+ end
29
+ end
30
+
31
+ context "Database#connect" do
32
+ specify "should raise Sequel::Error::NotImplemented" do
33
+ proc {Sequel::Database.new.connect}.should raise_error(NotImplementedError)
34
+ end
35
+ end
36
+
37
+ context "Database#disconnect" do
38
+ specify "should raise Sequel::Error::NotImplemented" do
39
+ proc {Sequel::Database.new.disconnect}.should raise_error(NotImplementedError)
40
+ end
41
+ end
42
+
43
+ context "Database#uri" do
44
+ setup do
45
+ @c = Class.new(Sequel::Database) do
46
+ set_adapter_scheme :mau
47
+ end
48
+
49
+ @db = Sequel('mau://user:pass@localhost:9876/maumau')
50
+ end
51
+
52
+ specify "should return the connection URI for the database" do
53
+ @db.uri.should == 'mau://user:pass@localhost:9876/maumau'
54
+ end
55
+ end
56
+
57
+ context "Database.adapter_scheme" do
58
+ specify "should return the database schema" do
59
+ Sequel::Database.adapter_scheme.should be_nil
60
+
61
+ @c = Class.new(Sequel::Database) do
62
+ set_adapter_scheme :mau
63
+ end
64
+
65
+ @c.adapter_scheme.should == :mau
66
+ end
67
+ end
68
+
69
+ context "Database#dataset" do
70
+ setup do
71
+ @db = Sequel::Database.new
72
+ @ds = @db.dataset
73
+ end
74
+
75
+ specify "should provide a blank dataset through #dataset" do
76
+ @ds.should be_a_kind_of(Sequel::Dataset)
77
+ @ds.opts.should == {}
78
+ @ds.db.should be(@db)
79
+ end
80
+
81
+ specify "should provide a #from dataset" do
82
+ d = @db.from(:mau)
83
+ d.should be_a_kind_of(Sequel::Dataset)
84
+ d.sql.should == 'SELECT * FROM mau'
85
+
86
+ e = @db[:miu]
87
+ e.should be_a_kind_of(Sequel::Dataset)
88
+ e.sql.should == 'SELECT * FROM miu'
89
+ end
90
+
91
+ specify "should provide a filtered #from dataset if a block is given" do
92
+ d = @db.from(:mau) {:x > 100}
93
+ d.should be_a_kind_of(Sequel::Dataset)
94
+ d.sql.should == 'SELECT * FROM mau WHERE (x > 100)'
95
+ end
96
+
97
+ specify "should provide a #select dataset" do
98
+ d = @db.select(:a, :b, :c).from(:mau)
99
+ d.should be_a_kind_of(Sequel::Dataset)
100
+ d.sql.should == 'SELECT a, b, c FROM mau'
101
+ end
102
+ end
103
+
104
+ context "Database#execute" do
105
+ specify "should raise Sequel::Error::NotImplemented" do
106
+ proc {Sequel::Database.new.execute('blah blah')}.should raise_error(NotImplementedError)
107
+ proc {Sequel::Database.new << 'blah blah'}.should raise_error(NotImplementedError)
108
+ end
109
+ end
110
+
111
+ context "Database#<<" do
112
+ setup do
113
+ @c = Class.new(Sequel::Database) do
114
+ define_method(:execute) {|sql| sql}
115
+ end
116
+ @db = @c.new({})
117
+ end
118
+
119
+ specify "should pass the supplied sql to #execute" do
120
+ (@db << "DELETE FROM items").should == "DELETE FROM items"
121
+ end
122
+
123
+ specify "should accept an array and convert it to SQL" do
124
+ a = %[
125
+ --
126
+ CREATE TABLE items (a integer, /*b integer*/
127
+ b text, c integer);
128
+ DROP TABLE old_items;
129
+ ].split($/)
130
+ (@db << a).should ==
131
+ "CREATE TABLE items (a integer, b text, c integer); DROP TABLE old_items;"
132
+ end
133
+
134
+ specify "should remove comments and whitespace from arrays" do
135
+ s = %[
136
+ --
137
+ CREATE TABLE items (a integer, /*b integer*/
138
+ b text, c integer); \r\n
139
+ DROP TABLE old_items;
140
+ ].split($/)
141
+ (@db << s).should ==
142
+ "CREATE TABLE items (a integer, b text, c integer); DROP TABLE old_items;"
143
+ end
144
+
145
+ specify "should not remove comments and whitespace from strings" do
146
+ s = "INSERT INTO items VALUES ('---abc')"
147
+ (@db << s).should == s
148
+ end
149
+ end
150
+
151
+ context "Database#synchronize" do
152
+ setup do
153
+ @db = Sequel::Database.new(:max_connections => 1)
154
+ @db.pool.connection_proc = proc {12345}
155
+ end
156
+
157
+ specify "should wrap the supplied block in pool.hold" do
158
+ stop = false
159
+ c1, c2 = nil
160
+ t1 = Thread.new {@db.synchronize {|c| c1 = c; while !stop;sleep 0.1;end}}
161
+ while !c1;end
162
+ c1.should == 12345
163
+ t2 = Thread.new {@db.synchronize {|c| c2 = c}}
164
+ sleep 0.2
165
+ @db.pool.available_connections.should be_empty
166
+ c2.should be_nil
167
+ stop = true
168
+ t1.join
169
+ sleep 0.1
170
+ c2.should == 12345
171
+ t2.join
172
+ end
173
+ end
174
+
175
+ context "Database#test_connection" do
176
+ setup do
177
+ @db = Sequel::Database.new
178
+ @test = nil
179
+ @db.pool.connection_proc = proc {@test = rand(100)}
180
+ end
181
+
182
+ specify "should call pool#hold" do
183
+ @db.test_connection
184
+ @test.should_not be_nil
185
+ end
186
+
187
+ specify "should return true if successful" do
188
+ @db.test_connection.should be_true
189
+ end
190
+ end
191
+
192
+ class DummyDataset < Sequel::Dataset
193
+ def first
194
+ raise if @opts[:from] == [:a]
195
+ true
196
+ end
197
+ end
198
+
199
+ class DummyDatabase < Sequel::Database
200
+ attr_reader :sqls
201
+
202
+ def execute(sql)
203
+ @sqls ||= []
204
+ @sqls << sql
205
+ end
206
+
207
+ def transaction; yield; end
208
+
209
+ def dataset
210
+ DummyDataset.new(self)
211
+ end
212
+ end
213
+
214
+ context "Database#create_table" do
215
+ setup do
216
+ @db = DummyDatabase.new
217
+ end
218
+
219
+ specify "should construct proper SQL" do
220
+ @db.create_table :test do
221
+ primary_key :id, :integer, :null => false
222
+ column :name, :text
223
+ index :name, :unique => true
224
+ end
225
+ @db.sqls.should == [
226
+ 'CREATE TABLE test (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
227
+ 'CREATE UNIQUE INDEX test_name_index ON test (name)'
228
+ ]
229
+ end
230
+ end
231
+
232
+ context "Database#alter_table" do
233
+ setup do
234
+ @db = DummyDatabase.new
235
+ end
236
+
237
+ specify "should construct proper SQL" do
238
+ @db.alter_table :xyz do
239
+ add_column :aaa, :text, :null => false, :unique => true
240
+ drop_column :bbb
241
+ rename_column :ccc, :ddd
242
+ set_column_type :eee, :integer
243
+ set_column_default :hhh, 'abcd'
244
+
245
+ add_index :fff, :unique => true
246
+ drop_index :ggg
247
+ end
248
+
249
+ @db.sqls.should == [
250
+ 'ALTER TABLE xyz ADD COLUMN aaa text UNIQUE NOT NULL',
251
+ 'ALTER TABLE xyz DROP COLUMN bbb',
252
+ 'ALTER TABLE xyz RENAME COLUMN ccc TO ddd',
253
+ 'ALTER TABLE xyz ALTER COLUMN eee TYPE integer',
254
+ "ALTER TABLE xyz ALTER COLUMN hhh SET DEFAULT 'abcd'",
255
+
256
+ 'CREATE UNIQUE INDEX xyz_fff_index ON xyz (fff)',
257
+ 'DROP INDEX xyz_ggg_index'
258
+ ]
259
+ end
260
+ end
261
+
262
+ context "Database#add_column" do
263
+ setup do
264
+ @db = DummyDatabase.new
265
+ end
266
+
267
+ specify "should construct proper SQL" do
268
+ @db.add_column :test, :name, :text, :unique => true
269
+ @db.sqls.should == [
270
+ 'ALTER TABLE test ADD COLUMN name text UNIQUE'
271
+ ]
272
+ end
273
+ end
274
+
275
+ context "Database#drop_column" do
276
+ setup do
277
+ @db = DummyDatabase.new
278
+ end
279
+
280
+ specify "should construct proper SQL" do
281
+ @db.drop_column :test, :name
282
+ @db.sqls.should == [
283
+ 'ALTER TABLE test DROP COLUMN name'
284
+ ]
285
+ end
286
+ end
287
+
288
+ context "Database#rename_column" do
289
+ setup do
290
+ @db = DummyDatabase.new
291
+ end
292
+
293
+ specify "should construct proper SQL" do
294
+ @db.rename_column :test, :abc, :def
295
+ @db.sqls.should == [
296
+ 'ALTER TABLE test RENAME COLUMN abc TO def'
297
+ ]
298
+ end
299
+ end
300
+
301
+ context "Database#set_column_type" do
302
+ setup do
303
+ @db = DummyDatabase.new
304
+ end
305
+
306
+ specify "should construct proper SQL" do
307
+ @db.set_column_type :test, :name, :integer
308
+ @db.sqls.should == [
309
+ 'ALTER TABLE test ALTER COLUMN name TYPE integer'
310
+ ]
311
+ end
312
+ end
313
+
314
+ context "Database#set_column_default" do
315
+ setup do
316
+ @db = DummyDatabase.new
317
+ end
318
+
319
+ specify "should construct proper SQL" do
320
+ @db.set_column_default :test, :name, 'zyx'
321
+ @db.sqls.should == [
322
+ "ALTER TABLE test ALTER COLUMN name SET DEFAULT 'zyx'"
323
+ ]
324
+ end
325
+ end
326
+
327
+ context "Database#add_index" do
328
+ setup do
329
+ @db = DummyDatabase.new
330
+ end
331
+
332
+ specify "should construct proper SQL" do
333
+ @db.add_index :test, :name, :unique => true
334
+ @db.sqls.should == [
335
+ 'CREATE UNIQUE INDEX test_name_index ON test (name)'
336
+ ]
337
+ end
338
+
339
+ specify "should accept multiple columns" do
340
+ @db.add_index :test, [:one, :two]
341
+ @db.sqls.should == [
342
+ 'CREATE INDEX test_one_two_index ON test (one, two)'
343
+ ]
344
+ end
345
+ end
346
+
347
+ context "Database#drop_index" do
348
+ setup do
349
+ @db = DummyDatabase.new
350
+ end
351
+
352
+ specify "should construct proper SQL" do
353
+ @db.drop_index :test, :name
354
+ @db.sqls.should == [
355
+ 'DROP INDEX test_name_index'
356
+ ]
357
+ end
358
+
359
+ end
360
+
361
+ class Dummy2Database < Sequel::Database
362
+ attr_reader :sql
363
+ def execute(sql); @sql = sql; end
364
+ def transaction; yield; end
365
+ end
366
+
367
+ context "Database#drop_table" do
368
+ setup do
369
+ @db = DummyDatabase.new
370
+ end
371
+
372
+ specify "should construct proper SQL" do
373
+ @db.drop_table :test
374
+ @db.sqls.should == ['DROP TABLE test']
375
+ end
376
+
377
+ specify "should accept multiple table names" do
378
+ @db.drop_table :a, :bb, :ccc
379
+ @db.sqls.should == [
380
+ 'DROP TABLE a',
381
+ 'DROP TABLE bb',
382
+ 'DROP TABLE ccc'
383
+ ]
384
+ end
385
+ end
386
+
387
+ context "Database#rename_table" do
388
+ setup do
389
+ @db = DummyDatabase.new
390
+ end
391
+
392
+ specify "should construct proper SQL" do
393
+ @db.rename_table :abc, :xyz
394
+ @db.sqls.should == ['ALTER TABLE abc RENAME TO xyz']
395
+ end
396
+ end
397
+
398
+ context "Database#table_exists?" do
399
+ setup do
400
+ @db = DummyDatabase.new
401
+ @db.stub!(:tables).and_return([:a, :b])
402
+ @db2 = DummyDatabase.new
403
+ Sequel::Dataset.stub!(:first).and_return(nil)
404
+ end
405
+
406
+ specify "should use Database#tables if available" do
407
+ @db.table_exists?(:a).should be_true
408
+ @db.table_exists?(:b).should be_true
409
+ @db.table_exists?(:c).should be_false
410
+ end
411
+
412
+ specify "should otherwise try to select the first record from the table's dataset" do
413
+ @db2.table_exists?(:a).should be_false
414
+ @db2.table_exists?(:b).should be_true
415
+ end
416
+ end
417
+
418
+
419
+ class Dummy3Database < Sequel::Database
420
+ attr_reader :sql, :transactions
421
+ def execute(sql); @sql ||= []; @sql << sql; end
422
+
423
+ class DummyConnection
424
+ def initialize(db); @db = db; end
425
+ def execute(sql); @db.execute(sql); end
426
+ end
427
+ end
428
+
429
+ context "Database#transaction" do
430
+ setup do
431
+ @db = Dummy3Database.new
432
+ @db.pool.connection_proc = proc {Dummy3Database::DummyConnection.new(@db)}
433
+ end
434
+
435
+ specify "should wrap the supplied block with BEGIN + COMMIT statements" do
436
+ @db.transaction {@db.execute 'DROP TABLE test;'}
437
+ @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
438
+ end
439
+
440
+ specify "should issue ROLLBACK if an exception is raised, and re-raise" do
441
+ @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
442
+ @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
443
+
444
+ proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
445
+ end
446
+
447
+ specify "should issue ROLLBACK if rollback! is called in the transaction" do
448
+ @db.transaction do
449
+ @db.drop_table(:a)
450
+ rollback!
451
+ @db.drop_table(:b)
452
+ end
453
+
454
+ @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
455
+ end
456
+
457
+ specify "should be re-entrant" do
458
+ stop = false
459
+ cc = nil
460
+ t = Thread.new do
461
+ @db.transaction {@db.transaction {@db.transaction {|c|
462
+ cc = c
463
+ while !stop; sleep 0.1; end
464
+ }}}
465
+ end
466
+ while cc.nil?; sleep 0.1; end
467
+ cc.should be_a_kind_of(Dummy3Database::DummyConnection)
468
+ @db.transactions.should == [t]
469
+ stop = true
470
+ t.join
471
+ @db.transactions.should be_empty
472
+ end
473
+ end
474
+
475
+ class Sequel::Database
476
+ def self.get_adapters; @@adapters; end
477
+ end
478
+
479
+ context "A Database adapter with a scheme" do
480
+ setup do
481
+ class CCC < Sequel::Database
482
+ set_adapter_scheme :ccc
483
+ end
484
+ end
485
+
486
+ specify "should be registered in adapters" do
487
+ Sequel::Database.get_adapters[:ccc].should == CCC
488
+ end
489
+
490
+ specify "should be instantiated when its scheme is specified" do
491
+ c = Sequel::Database.connect('ccc://localhost/db')
492
+ c.should be_a_kind_of(CCC)
493
+ c.opts[:host].should == 'localhost'
494
+ c.opts[:database].should == 'db'
495
+ end
496
+
497
+ specify "should be accessible through Sequel.connect" do
498
+ c = Sequel.connect 'ccc://localhost/db'
499
+ c.should be_a_kind_of(CCC)
500
+ c.opts[:host].should == 'localhost'
501
+ c.opts[:database].should == 'db'
502
+ end
503
+
504
+ specify "should be accessible through Sequel.open" do
505
+ c = Sequel.open 'ccc://localhost/db'
506
+ c.should be_a_kind_of(CCC)
507
+ c.opts[:host].should == 'localhost'
508
+ c.opts[:database].should == 'db'
509
+ end
510
+
511
+ specify "should be accessible through Sequel()" do
512
+ c = Sequel('ccc://localhost/db')
513
+ c.should be_a_kind_of(CCC)
514
+ c.opts[:host].should == 'localhost'
515
+ c.opts[:database].should == 'db'
516
+ end
517
+
518
+ specify "should be accessible through Sequel.<adapter>" do
519
+ # invalid parameters
520
+ proc {Sequel.ccc('abc', 'def')}.should raise_error(Sequel::Error)
521
+
522
+ c = Sequel.ccc('mydb')
523
+ c.should be_a_kind_of(CCC)
524
+ c.opts.should == {:database => 'mydb'}
525
+
526
+ c = Sequel.ccc('mydb', :host => 'localhost')
527
+ c.should be_a_kind_of(CCC)
528
+ c.opts.should == {:database => 'mydb', :host => 'localhost'}
529
+
530
+ c = Sequel.ccc
531
+ c.should be_a_kind_of(CCC)
532
+ c.opts.should == {}
533
+
534
+ c = Sequel.ccc(:database => 'mydb', :host => 'localhost')
535
+ c.should be_a_kind_of(CCC)
536
+ c.opts.should == {:database => 'mydb', :host => 'localhost'}
537
+ end
538
+
539
+ specify "should be accessible through Sequel.connect with options" do
540
+ c = Sequel.connect(:adapter => :ccc, :database => 'mydb')
541
+ c.should be_a_kind_of(CCC)
542
+ c.opts.should == {:adapter => :ccc, :database => 'mydb'}
543
+ end
544
+ end
545
+
546
+ context "An unknown database scheme" do
547
+ specify "should raise an error in Sequel::Database.connect" do
548
+ proc {Sequel::Database.connect('ddd://localhost/db')}.should raise_error(Sequel::Error::AdapterNotFound)
549
+ end
550
+
551
+ specify "should raise an error in Sequel.connect" do
552
+ proc {Sequel.connect('ddd://localhost/db')}.should raise_error(Sequel::Error::AdapterNotFound)
553
+ end
554
+
555
+ specify "should raise an error in Sequel.open" do
556
+ proc {Sequel.open('ddd://localhost/db')}.should raise_error(Sequel::Error::AdapterNotFound)
557
+ end
558
+ end
559
+
560
+ context "A broken adapter (lib is there but the class is not)" do
561
+ setup do
562
+ @fn = File.join(File.dirname(__FILE__), '../lib/sequel_core/adapters/blah.rb')
563
+ FileUtils.touch(@fn)
564
+ end
565
+
566
+ teardown do
567
+ FileUtils.rm(@fn)
568
+ end
569
+
570
+ specify "should raise an error" do
571
+ proc {Sequel.connect('blah://blow')}.should raise_error(Sequel::Error::AdapterNotFound)
572
+ end
573
+ end
574
+
575
+ context "Database#uri_to_options" do
576
+ specify "should convert a URI to an options hash" do
577
+ h = Sequel::Database.uri_to_options(URI.parse('ttt://uuu:ppp@192.168.60.1:1234/blah'))
578
+ h[:user].should == 'uuu'
579
+ h[:password].should == 'ppp'
580
+ h[:host].should == '192.168.60.1'
581
+ h[:port].should == 1234
582
+ h[:database].should == 'blah'
583
+ end
584
+ end
585
+
586
+ context "A single threaded database" do
587
+ teardown do
588
+ Sequel::Database.single_threaded = false
589
+ end
590
+
591
+ specify "should use a SingleThreadedPool instead of a ConnectionPool" do
592
+ db = Sequel::Database.new(:single_threaded => true)
593
+ db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
594
+ end
595
+
596
+ specify "should be constructable using :single_threaded => true option" do
597
+ db = Sequel::Database.new(:single_threaded => true)
598
+ db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
599
+ end
600
+
601
+ specify "should be constructable using Database.single_threaded = true" do
602
+ Sequel::Database.single_threaded = true
603
+ db = Sequel::Database.new
604
+ db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
605
+ end
606
+
607
+ specify "should be constructable using Sequel.single_threaded = true" do
608
+ Sequel.single_threaded = true
609
+ db = Sequel::Database.new
610
+ db.pool.should be_a_kind_of(Sequel::SingleThreadedPool)
611
+ end
612
+ end
613
+
614
+ context "A single threaded database" do
615
+ setup do
616
+ conn = 1234567
617
+ @db = Sequel::Database.new(:single_threaded => true) do
618
+ conn += 1
619
+ end
620
+ end
621
+
622
+ specify "should invoke connection_proc only once" do
623
+ @db.pool.hold {|c| c.should == 1234568}
624
+ @db.pool.hold {|c| c.should == 1234568}
625
+ end
626
+
627
+ specify "should convert an Exception into a RuntimeError" do
628
+ db = Sequel::Database.new(:single_threaded => true) do
629
+ raise Exception
630
+ end
631
+
632
+ proc {db.pool.hold {|c|}}.should raise_error(RuntimeError)
633
+ end
634
+ end
635
+
636
+ context "A database" do
637
+ setup do
638
+ Sequel::Database.single_threaded = false
639
+ end
640
+
641
+ teardown do
642
+ Sequel::Database.single_threaded = false
643
+ end
644
+
645
+ specify "should be either single_threaded? or multi_threaded?" do
646
+ db = Sequel::Database.new(:single_threaded => true)
647
+ db.should be_single_threaded
648
+ db.should_not be_multi_threaded
649
+
650
+ db = Sequel::Database.new(:max_options => 1)
651
+ db.should_not be_single_threaded
652
+ db.should be_multi_threaded
653
+
654
+ db = Sequel::Database.new
655
+ db.should_not be_single_threaded
656
+ db.should be_multi_threaded
657
+
658
+ Sequel::Database.single_threaded = true
659
+
660
+ db = Sequel::Database.new
661
+ db.should be_single_threaded
662
+ db.should_not be_multi_threaded
663
+
664
+ db = Sequel::Database.new(:max_options => 4)
665
+ db.should be_single_threaded
666
+ db.should_not be_multi_threaded
667
+ end
668
+
669
+ specify "should accept a logger object" do
670
+ db = Sequel::Database.new
671
+ s = "I'm a logger"
672
+ db.logger = s
673
+ db.logger.should be(s)
674
+ db.logger = nil
675
+ db.logger.should be_nil
676
+ end
677
+ end
678
+
679
+ context "Database#dataset" do
680
+ setup do
681
+ @db = Sequel::Database.new
682
+ end
683
+
684
+ specify "should delegate to Dataset#query if block is provided" do
685
+ @d = @db.query {select :x; from :y}
686
+ @d.should be_a_kind_of(Sequel::Dataset)
687
+ @d.sql.should == "SELECT x FROM y"
688
+ end
689
+ end
690
+
691
+ context "Database#fetch" do
692
+ setup do
693
+ @db = Sequel::Database.new
694
+ c = Class.new(Sequel::Dataset) do
695
+ def fetch_rows(sql); yield({:sql => sql}); end
696
+ end
697
+ @db.meta_def(:dataset) {c.new(self)}
698
+ end
699
+
700
+ specify "should create a dataset and invoke its fetch_rows method with the given sql" do
701
+ sql = nil
702
+ @db.fetch('select * from xyz') {|r| sql = r[:sql]}
703
+ sql.should == 'select * from xyz'
704
+ end
705
+
706
+ specify "should format the given sql with any additional arguments" do
707
+ sql = nil
708
+ @db.fetch('select * from xyz where x = ? and y = ?', 15, 'abc') {|r| sql = r[:sql]}
709
+ sql.should == "select * from xyz where x = 15 and y = 'abc'"
710
+
711
+ # and Aman Gupta's example
712
+ @db.fetch('select name from table where name = ? or id in (?)',
713
+ 'aman', [3,4,7]) {|r| sql = r[:sql]}
714
+ sql.should == "select name from table where name = 'aman' or id in (3, 4, 7)"
715
+ end
716
+
717
+ specify "should return the dataset if no block is given" do
718
+ @db.fetch('select * from xyz').should be_a_kind_of(Sequel::Dataset)
719
+
720
+ @db.fetch('select a from b').map {|r| r[:sql]}.should == ['select a from b']
721
+
722
+ @db.fetch('select c from d').inject([]) {|m, r| m << r; m}.should == \
723
+ [{:sql => 'select c from d'}]
724
+ end
725
+
726
+ specify "should return a dataset that always uses the given sql for SELECTs" do
727
+ ds = @db.fetch('select * from xyz')
728
+ ds.select_sql.should == 'select * from xyz'
729
+ ds.sql.should == 'select * from xyz'
730
+
731
+ ds.filter! {:price < 100}
732
+ ds.select_sql.should == 'select * from xyz'
733
+ ds.sql.should == 'select * from xyz'
734
+ end
735
+ end
736
+
737
+ context "Database#[]" do
738
+ setup do
739
+ @db = Sequel::Database.new
740
+ end
741
+
742
+ specify "should return a dataset when symbols are given" do
743
+ ds = @db[:items]
744
+ ds.class.should == Sequel::Dataset
745
+ ds.opts[:from].should == [:items]
746
+ end
747
+
748
+ specify "should return an enumerator when a string is given" do
749
+ c = Class.new(Sequel::Dataset) do
750
+ def fetch_rows(sql); yield({:sql => sql}); end
751
+ end
752
+ @db.meta_def(:dataset) {c.new(self)}
753
+
754
+ sql = nil
755
+ @db['select * from xyz where x = ? and y = ?', 15, 'abc'].each {|r| sql = r[:sql]}
756
+ sql.should == "select * from xyz where x = 15 and y = 'abc'"
757
+ end
758
+ end
759
+
760
+ context "Database#create_view" do
761
+ setup do
762
+ @db = DummyDatabase.new
763
+ end
764
+
765
+ specify "should construct proper SQL with raw SQL" do
766
+ @db.create_view :test, "SELECT * FROM xyz"
767
+ @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
768
+ end
769
+
770
+ specify "should construct proper SQL with dataset" do
771
+ @db.create_view :test, @db[:items].select(:a, :b).order(:c)
772
+ @db.sqls.should == ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
773
+ end
774
+ end
775
+
776
+ context "Database#create_or_replace_view" do
777
+ setup do
778
+ @db = DummyDatabase.new
779
+ end
780
+
781
+ specify "should construct proper SQL with raw SQL" do
782
+ @db.create_or_replace_view :test, "SELECT * FROM xyz"
783
+ @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT * FROM xyz']
784
+ end
785
+
786
+ specify "should construct proper SQL with dataset" do
787
+ @db.create_or_replace_view :test, @db[:items].select(:a, :b).order(:c)
788
+ @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
789
+ end
790
+ end
791
+
792
+ context "Database#drop_view" do
793
+ setup do
794
+ @db = DummyDatabase.new
795
+ end
796
+
797
+ specify "should construct proper SQL" do
798
+ @db.drop_view :test
799
+ @db.sqls.should == ['DROP VIEW test']
800
+ end
801
+ end
802
+
803
+ # TODO: beaf this up with specs for all supported ops
804
+ context "Database#alter_table_sql" do
805
+ setup do
806
+ @db = DummyDatabase.new
807
+ end
808
+
809
+ specify "should raise error for an invalid op" do
810
+ proc {@db.alter_table_sql(:mau, :op => :blah)}.should raise_error(Sequel::Error)
811
+ end
812
+ end