sequel_core 1.0

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