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.
- data/CHANGELOG +1003 -0
- data/COPYING +18 -0
- data/README +81 -0
- data/Rakefile +176 -0
- data/bin/sequel +41 -0
- data/lib/sequel_core.rb +59 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
- data/lib/sequel_core/adapters/ado.rb +100 -0
- data/lib/sequel_core/adapters/db2.rb +158 -0
- data/lib/sequel_core/adapters/dbi.rb +126 -0
- data/lib/sequel_core/adapters/informix.rb +87 -0
- data/lib/sequel_core/adapters/jdbc.rb +108 -0
- data/lib/sequel_core/adapters/mysql.rb +269 -0
- data/lib/sequel_core/adapters/odbc.rb +145 -0
- data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
- data/lib/sequel_core/adapters/openbase.rb +90 -0
- data/lib/sequel_core/adapters/oracle.rb +99 -0
- data/lib/sequel_core/adapters/postgres.rb +519 -0
- data/lib/sequel_core/adapters/sqlite.rb +192 -0
- data/lib/sequel_core/array_keys.rb +296 -0
- data/lib/sequel_core/connection_pool.rb +152 -0
- data/lib/sequel_core/core_ext.rb +59 -0
- data/lib/sequel_core/core_sql.rb +191 -0
- data/lib/sequel_core/database.rb +433 -0
- data/lib/sequel_core/dataset.rb +409 -0
- data/lib/sequel_core/dataset/convenience.rb +321 -0
- data/lib/sequel_core/dataset/sequelizer.rb +354 -0
- data/lib/sequel_core/dataset/sql.rb +586 -0
- data/lib/sequel_core/exceptions.rb +45 -0
- data/lib/sequel_core/migration.rb +191 -0
- data/lib/sequel_core/model.rb +8 -0
- data/lib/sequel_core/pretty_table.rb +73 -0
- data/lib/sequel_core/schema.rb +8 -0
- data/lib/sequel_core/schema/schema_generator.rb +131 -0
- data/lib/sequel_core/schema/schema_sql.rb +131 -0
- data/lib/sequel_core/worker.rb +58 -0
- data/spec/adapters/informix_spec.rb +139 -0
- data/spec/adapters/mysql_spec.rb +330 -0
- data/spec/adapters/oracle_spec.rb +130 -0
- data/spec/adapters/postgres_spec.rb +189 -0
- data/spec/adapters/sqlite_spec.rb +345 -0
- data/spec/array_keys_spec.rb +679 -0
- data/spec/connection_pool_spec.rb +356 -0
- data/spec/core_ext_spec.rb +67 -0
- data/spec/core_sql_spec.rb +301 -0
- data/spec/database_spec.rb +812 -0
- data/spec/dataset_spec.rb +2381 -0
- data/spec/migration_spec.rb +261 -0
- data/spec/pretty_table_spec.rb +66 -0
- data/spec/rcov.opts +4 -0
- data/spec/schema_generator_spec.rb +86 -0
- data/spec/schema_spec.rb +230 -0
- data/spec/sequelizer_spec.rb +448 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/worker_spec.rb +96 -0
- 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
|