rdbi 0.9.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/History.txt +13 -0
- data/Manifest.txt +28 -0
- data/{README.rdoc → README.txt} +9 -9
- data/Rakefile +39 -133
- data/lib/rdbi.rb +44 -32
- data/lib/rdbi/database.rb +61 -20
- data/lib/rdbi/driver.rb +4 -1
- data/lib/rdbi/pool.rb +10 -0
- data/lib/rdbi/result.rb +94 -123
- data/lib/rdbi/schema.rb +8 -1
- data/lib/rdbi/statement.rb +22 -3
- data/lib/rdbi/types.rb +2 -0
- data/rdbi.gemspec +2 -4
- data/test/helper.rb +29 -1
- data/test/test_database.rb +95 -6
- data/test/test_pool.rb +11 -2
- data/test/test_rdbi.rb +54 -1
- data/test/test_result.rb +129 -7
- data/test/test_statement.rb +1 -1
- data/test/test_util.rb +2 -4
- metadata +201 -137
- data/.document +0 -5
- data/VERSION +0 -1
data/lib/rdbi/types.rb
CHANGED
data/rdbi.gemspec
CHANGED
@@ -14,14 +14,12 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.email = %q{erik@hollensbe.org}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.
|
17
|
+
"README.txt"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
|
-
".document",
|
21
20
|
"LICENSE",
|
22
|
-
"README.
|
21
|
+
"README.txt",
|
23
22
|
"Rakefile",
|
24
|
-
"VERSION",
|
25
23
|
"docs/external-api.pdf",
|
26
24
|
"docs/external-api.texi",
|
27
25
|
"lib/rdbi.rb",
|
data/test/helper.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
gem 'rdbi-driver-mock'
|
3
|
-
gem 'test-unit'
|
4
3
|
require 'test/unit'
|
5
4
|
|
6
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
@@ -8,6 +7,35 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
8
7
|
require 'rdbi'
|
9
8
|
require 'rdbi/driver/mock'
|
10
9
|
|
10
|
+
# -- Fake an exceptional statement handle
|
11
|
+
module FaultyDB
|
12
|
+
class Error < Exception; end
|
13
|
+
end
|
14
|
+
|
15
|
+
module RDBI
|
16
|
+
class Driver
|
17
|
+
class MockFaulty < RDBI::Driver
|
18
|
+
def initialize(*args)
|
19
|
+
super(MockFaulty::Database, *args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class MockFaulty::Database < Mock::DBH
|
24
|
+
def new_statement(query)
|
25
|
+
MockFaulty::Statement.new(query, self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class MockFaulty::Statement < Mock::STH
|
30
|
+
def execute(*binds)
|
31
|
+
raise ::FaultyDB::Error.new('Deadlocked, de-synchronized, corrupted, invalid, etc.')
|
32
|
+
end
|
33
|
+
alias :execute_modification :execute
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
11
39
|
class Test::Unit::TestCase
|
12
40
|
def mock_connect
|
13
41
|
RDBI.connect(:Mock, :username => 'foo', :password => 'bar')
|
data/test/test_database.rb
CHANGED
@@ -5,6 +5,10 @@ class TestDatabase < Test::Unit::TestCase
|
|
5
5
|
@dbh = mock_connect
|
6
6
|
end
|
7
7
|
|
8
|
+
def teardown
|
9
|
+
@dbh.disconnect
|
10
|
+
end
|
11
|
+
|
8
12
|
def assert_transaction(count)
|
9
13
|
in_transaction = @dbh.instance_variable_get("@in_transaction") ||
|
10
14
|
@dbh.instance_variable_get(:@in_transaction)
|
@@ -38,7 +42,7 @@ class TestDatabase < Test::Unit::TestCase
|
|
38
42
|
raise StandardError, "should call rollback"
|
39
43
|
end
|
40
44
|
|
41
|
-
assert_raises(StandardError
|
45
|
+
assert_raises(StandardError) do
|
42
46
|
@dbh.transaction do |dbh|
|
43
47
|
assert_transaction(1)
|
44
48
|
true
|
@@ -52,7 +56,7 @@ class TestDatabase < Test::Unit::TestCase
|
|
52
56
|
|
53
57
|
@dbh.next_action = proc { |*args| true }
|
54
58
|
|
55
|
-
assert_raises(StandardError
|
59
|
+
assert_raises(StandardError) do
|
56
60
|
@dbh.transaction do |dbh|
|
57
61
|
assert_transaction(1)
|
58
62
|
|
@@ -206,20 +210,52 @@ class TestDatabase < Test::Unit::TestCase
|
|
206
210
|
my_sth = nil
|
207
211
|
my_res = nil
|
208
212
|
|
213
|
+
# ordinary execution
|
209
214
|
@dbh.prepare("some statement") do |sth|
|
210
215
|
assert(sth)
|
211
216
|
assert_respond_to(sth, :execute)
|
212
217
|
res = sth.execute
|
213
218
|
assert(res)
|
214
219
|
my_sth = sth
|
220
|
+
assert(! my_sth.finished?)
|
215
221
|
end
|
216
222
|
|
217
223
|
assert(my_sth.finished?)
|
218
224
|
|
219
|
-
|
220
|
-
|
221
|
-
|
225
|
+
# and exceptional
|
226
|
+
begin
|
227
|
+
@dbh.prepare("some statement") do |sth|
|
228
|
+
assert(sth)
|
229
|
+
assert_respond_to(sth, :execute)
|
230
|
+
res = sth.execute
|
231
|
+
assert(res)
|
232
|
+
my_sth = sth
|
233
|
+
assert(! my_sth.finished?)
|
234
|
+
raise "Blam!"
|
235
|
+
end
|
236
|
+
rescue
|
222
237
|
end
|
238
|
+
|
239
|
+
assert(my_sth.finished?)
|
240
|
+
|
241
|
+
# Database#execute &block
|
242
|
+
|
243
|
+
my_res = nil
|
244
|
+
block_ret = @dbh.execute("some statement") do |res|
|
245
|
+
my_res = res
|
246
|
+
assert(res)
|
247
|
+
assert_kind_of(RDBI::Result, res)
|
248
|
+
"BLOCK RETURN"
|
249
|
+
end
|
250
|
+
|
251
|
+
assert_kind_of(RDBI::Result, my_res)
|
252
|
+
# Result#finish implies Statement#finish, nil'ing
|
253
|
+
assert(my_res.sth.nil?,
|
254
|
+
'Database#execute &p did not finish its Result')
|
255
|
+
assert(!block_ret.kind_of?(RDBI::Result),
|
256
|
+
'Database#execute &block wrongly returned its Result')
|
257
|
+
assert_equal("BLOCK RETURN", block_ret,
|
258
|
+
"Database#execute &block didn't return the return value of &block")
|
223
259
|
end
|
224
260
|
|
225
261
|
def test_09_statement_allocation
|
@@ -239,9 +275,62 @@ class TestDatabase < Test::Unit::TestCase
|
|
239
275
|
@dbh.disconnect
|
240
276
|
end
|
241
277
|
|
242
|
-
def
|
278
|
+
def test_10_execute_finish
|
279
|
+
@dbh.execute("SELECT 1") do |res|
|
280
|
+
# noop
|
281
|
+
end
|
282
|
+
assert(@dbh.last_statement.finished?,
|
283
|
+
"#execute() block did not finish implicit sth")
|
284
|
+
|
285
|
+
res = @dbh.execute("SELECT 2")
|
286
|
+
assert(!@dbh.last_statement.finished?,
|
287
|
+
"#execute() unexpectedly finished implicit sth")
|
288
|
+
assert_nothing_raised do
|
289
|
+
res.finish
|
290
|
+
@dbh.disconnect
|
291
|
+
end
|
292
|
+
|
293
|
+
@dbh = RDBI.connect(:MockFaulty, :user => 'u', :pass => 'p')
|
294
|
+
assert_raises(FaultyDB::Error) do
|
295
|
+
@dbh.execute("ignored") do |not_reached|
|
296
|
+
assert(false, "A block not supposed to be reached was executed")
|
297
|
+
end
|
298
|
+
end
|
299
|
+
assert(@dbh.last_statement.finished?,
|
300
|
+
"Failed execute() &block did not finish statement")
|
301
|
+
|
302
|
+
assert_raises(FaultyDB::Error) do
|
303
|
+
res = @dbh.execute("ignored")
|
304
|
+
res.finish # Not reached
|
305
|
+
end
|
306
|
+
assert(@dbh.last_statement.finished?,
|
307
|
+
"Failed execute() did not finish statement")
|
308
|
+
end
|
309
|
+
|
310
|
+
def test_11_execute_mod_finish
|
311
|
+
@dbh.execute_modification("DROP SCHEMA INFORMATION_SCHEMA")
|
312
|
+
assert(@dbh.last_statement.finished?,
|
313
|
+
"#execute_modification() did not finish implicit sth")
|
314
|
+
|
315
|
+
a = nil
|
316
|
+
@dbh.execute_modification("IGNORED!") do |*blah|
|
317
|
+
a = "Not nil - but this block is not executed"
|
318
|
+
end
|
319
|
+
|
320
|
+
assert(a.nil?, "#execute_modification() invoked a block unexpectedly")
|
321
|
+
assert(@dbh.last_statement.finished?,
|
322
|
+
"#execute_modification() &ignored_block did not finish implicit sth")
|
323
|
+
|
243
324
|
@dbh.disconnect
|
325
|
+
|
326
|
+
@dbh = RDBI.connect(:MockFaulty, :user => 'u', :pass => 'p')
|
327
|
+
assert_raises(FaultyDB::Error) do
|
328
|
+
count = @dbh.execute_modification("ignored")
|
329
|
+
end
|
330
|
+
assert(@dbh.last_statement.finished?,
|
331
|
+
"Failed execute_modification() did not finish statement")
|
244
332
|
end
|
333
|
+
|
245
334
|
end
|
246
335
|
|
247
336
|
# vim: syntax=ruby ts=2 et sw=2 sts=2
|
data/test/test_pool.rb
CHANGED
@@ -20,7 +20,7 @@ class TestPool < Test::Unit::TestCase
|
|
20
20
|
|
21
21
|
def test_03_pooling!
|
22
22
|
pool = create_pool(:test_03)
|
23
|
-
assert_raise(ArgumentError
|
23
|
+
assert_raise(ArgumentError) do
|
24
24
|
6.times do
|
25
25
|
RDBI::Pool[:test_03].add_connection
|
26
26
|
end
|
@@ -160,7 +160,7 @@ class TestPool < Test::Unit::TestCase
|
|
160
160
|
assert_kind_of(RDBI::Pool, pool)
|
161
161
|
end
|
162
162
|
|
163
|
-
|
163
|
+
assert_equal(10, count)
|
164
164
|
assert_respond_to(RDBI::Pool, :map)
|
165
165
|
|
166
166
|
pool = create_pool(:test_08)
|
@@ -182,6 +182,15 @@ class TestPool < Test::Unit::TestCase
|
|
182
182
|
|
183
183
|
assert_respond_to(pool, :map)
|
184
184
|
end
|
185
|
+
|
186
|
+
def test_09_alternative_connect_syntax
|
187
|
+
pool = RDBI::Pool.new("connections!", { :driver => :Mock, :database => ":memory:", :username => "foo" })
|
188
|
+
dbh = pool.get_dbh
|
189
|
+
assert(dbh)
|
190
|
+
assert_kind_of(RDBI::Database, dbh)
|
191
|
+
|
192
|
+
assert_equal([:Mock, { :database => ":memory:", :username => "foo" }], pool.instance_variable_get(:@connect_args))
|
193
|
+
end
|
185
194
|
end
|
186
195
|
|
187
196
|
# vim: syntax=ruby ts=2 et sw=2 sts=2
|
data/test/test_rdbi.rb
CHANGED
@@ -16,7 +16,7 @@ class TestRDBI < Test::Unit::TestCase
|
|
16
16
|
|
17
17
|
assert_raise(ArgumentError) { RDBI.connect(1, :user => :blah) }
|
18
18
|
|
19
|
-
|
19
|
+
assert_raise(ArgumentError) { dbh = RDBI.connect(:Mock) }
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_02_last_dbh
|
@@ -63,6 +63,59 @@ class TestRDBI < Test::Unit::TestCase
|
|
63
63
|
dbh = RDBI.connect_cached(:Mock, :username => :foo, :pool_name => :test_05)
|
64
64
|
assert_equal(RDBI.pool(:test_05).handles[0], dbh)
|
65
65
|
end
|
66
|
+
|
67
|
+
def test_06_connect_block
|
68
|
+
my_dbh = nil
|
69
|
+
|
70
|
+
# Ordinary operation
|
71
|
+
RDBI.connect(:Mock, :username => :foo, :password => :bar) do |dbh|
|
72
|
+
assert(dbh)
|
73
|
+
assert_kind_of(RDBI::Database, dbh)
|
74
|
+
my_dbh = dbh
|
75
|
+
assert(my_dbh.connected?)
|
76
|
+
end
|
77
|
+
assert(! my_dbh.connected?)
|
78
|
+
|
79
|
+
# and exceptional operation
|
80
|
+
begin
|
81
|
+
RDBI.connect(:Mock, :username => :foo, :password => :bar) do |dbh|
|
82
|
+
assert(dbh)
|
83
|
+
assert_kind_of(RDBI::Database, dbh)
|
84
|
+
my_dbh = dbh
|
85
|
+
assert(my_dbh.connected?)
|
86
|
+
raise "Blam!"
|
87
|
+
end
|
88
|
+
rescue
|
89
|
+
end
|
90
|
+
assert(! my_dbh.connected?)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_07_connect_cached_block
|
94
|
+
conn_args = [:Mock, {:username => :foo, :password => :bar}]
|
95
|
+
my_dbh = nil
|
96
|
+
|
97
|
+
# Ordinary operation
|
98
|
+
RDBI.connect_cached(*conn_args) do |dbh|
|
99
|
+
assert(dbh)
|
100
|
+
assert_kind_of(RDBI::Database, dbh)
|
101
|
+
my_dbh = dbh
|
102
|
+
assert(my_dbh.connected?)
|
103
|
+
end
|
104
|
+
assert(my_dbh.connected?)
|
105
|
+
|
106
|
+
# and exceptional operation
|
107
|
+
begin
|
108
|
+
RDBI.connect_cached(*conn_args) do |dbh|
|
109
|
+
assert(dbh)
|
110
|
+
assert_kind_of(RDBI::Database, dbh)
|
111
|
+
my_dbh = dbh
|
112
|
+
assert(my_dbh.connected?)
|
113
|
+
raise "Blam!"
|
114
|
+
end
|
115
|
+
rescue
|
116
|
+
end
|
117
|
+
assert(my_dbh.connected?)
|
118
|
+
end
|
66
119
|
end
|
67
120
|
|
68
121
|
# vim: syntax=ruby ts=2 et sw=2 sts=2
|
data/test/test_result.rb
CHANGED
@@ -15,21 +15,34 @@ class TestResult < Test::Unit::TestCase
|
|
15
15
|
|
16
16
|
def mock_result
|
17
17
|
names = [:zero, :one, :two]
|
18
|
-
res = RDBI::Result.new(
|
18
|
+
res = RDBI::Result.new(
|
19
|
+
@dbh.prepare("foo"),
|
20
|
+
[1],
|
21
|
+
generate_data,
|
22
|
+
RDBI::Schema.new((0..2).to_a.map { |x| RDBI::Column.new(names[x], :integer, :default) }),
|
23
|
+
{ :default => RDBI::Type.filterlist() }
|
24
|
+
)
|
19
25
|
end
|
20
26
|
|
21
27
|
def mock_empty_result
|
22
28
|
names = [:zero, :one, :two]
|
23
|
-
res = RDBI::Result.new(
|
29
|
+
res = RDBI::Result.new(
|
30
|
+
@dbh.prepare("foo"),
|
31
|
+
[1],
|
32
|
+
RDBI::Driver::Mock::Cursor.new([]),
|
33
|
+
RDBI::Schema.new((0..2).to_a.map { |x| RDBI::Column.new(names[x], :integer, :default) }),
|
34
|
+
{ :default => RDBI::Type.filterlist() }
|
35
|
+
)
|
24
36
|
end
|
25
37
|
|
26
38
|
def get_index(res)
|
27
|
-
get_guts(
|
39
|
+
cursor = get_guts(res)[:data]
|
40
|
+
cursor.instance_variable_get("@index") || res.instance_variable_get(:@index)
|
28
41
|
end
|
29
42
|
|
30
43
|
def get_guts(res)
|
31
44
|
h = { }
|
32
|
-
%W[
|
45
|
+
%W[schema binds sth data].collect(&:to_sym).each do |sym|
|
33
46
|
h[sym] = res.instance_variable_get("@#{sym}") || res.instance_variable_get("@#{sym}".to_sym)
|
34
47
|
end
|
35
48
|
|
@@ -76,10 +89,9 @@ class TestResult < Test::Unit::TestCase
|
|
76
89
|
as
|
77
90
|
fetch
|
78
91
|
read
|
79
|
-
|
80
|
-
finish
|
92
|
+
finish
|
81
93
|
].collect(&:to_sym).each do |sym|
|
82
|
-
assert_respond_to(res, sym)
|
94
|
+
assert_respond_to(res, sym, "#{res.class} did not respond to :#{sym}")
|
83
95
|
end
|
84
96
|
|
85
97
|
res.sth.finish
|
@@ -299,6 +311,116 @@ class TestResult < Test::Unit::TestCase
|
|
299
311
|
assert_equal({:zero=>-1, :one=>0, :two=>1}, YAML.load(res.as(:YAML).first))
|
300
312
|
assert_equal({:zero=>8, :one=>9, :two=>10}, YAML.load(res.as(:YAML).last))
|
301
313
|
end
|
314
|
+
|
315
|
+
def test_13_enumerable_as
|
316
|
+
# 'master' dab6270 branch (0.9.1+) returned a Result::Driver for as()
|
317
|
+
|
318
|
+
res = mock_result
|
319
|
+
|
320
|
+
i = -1
|
321
|
+
# Mock result set is:
|
322
|
+
#
|
323
|
+
# ZERO ONE TWO
|
324
|
+
# ==== === ===
|
325
|
+
# -1 0 1
|
326
|
+
# 0 1 2
|
327
|
+
# 1 2 3
|
328
|
+
# ...
|
329
|
+
# 8 9 10
|
330
|
+
#
|
331
|
+
res.as(:Struct).each do |row|
|
332
|
+
assert_kind_of(::Struct, row)
|
333
|
+
assert_equal([i, i+1, i+2], [row.zero, row.one, row.two])
|
334
|
+
i += 1
|
335
|
+
end
|
336
|
+
res.sth.finish
|
337
|
+
end
|
338
|
+
|
339
|
+
def test_14_results_driven_struct
|
340
|
+
# 'master' dab6270 branch (0.9.1+) returned 'raw' rows for #each,
|
341
|
+
# #first and #last
|
342
|
+
|
343
|
+
res = mock_result
|
344
|
+
|
345
|
+
res.as(:Struct)
|
346
|
+
row = res.first # does not advance underlying index
|
347
|
+
assert_kind_of(::Struct, row)
|
348
|
+
assert_equal([-1, 0, 1], [row.zero, row.one, row.two])
|
349
|
+
|
350
|
+
res.each do |r|
|
351
|
+
assert_kind_of(::Struct, r)
|
352
|
+
assert_equal([-1, 0, 1], [r.zero, r.one, r.two])
|
353
|
+
break # Just one row, thank you
|
354
|
+
end
|
355
|
+
|
356
|
+
row = res.last
|
357
|
+
assert_kind_of(::Struct, row)
|
358
|
+
assert_equal([8, 9, 10], [row.zero, row.one, row.two])
|
359
|
+
|
360
|
+
res.sth.finish
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_15_results_driven_csv
|
364
|
+
# 'master' 20917a3 branch (pilcrow/result-as) didn't handle
|
365
|
+
# eof properly for result drivers that didn't return sets of
|
366
|
+
# rows as an array of rows
|
367
|
+
|
368
|
+
res = mock_result
|
369
|
+
|
370
|
+
res.as(:CSV)
|
371
|
+
row = res.first # does not advance underlying index
|
372
|
+
assert_equal("-1,0,1\n", row)
|
373
|
+
|
374
|
+
res.each do |r|
|
375
|
+
assert_equal("-1,0,1\n", r)
|
376
|
+
break # Just one row, thank you
|
377
|
+
end
|
378
|
+
|
379
|
+
row = res.last
|
380
|
+
assert_equal("8,9,10\n", row)
|
381
|
+
|
382
|
+
res.sth.finish
|
383
|
+
end
|
384
|
+
|
385
|
+
def test_16_results_driven_yaml
|
386
|
+
# As for test_15, but with YAML
|
387
|
+
# 'master' 20917a3 branch (pilcrow/result-as) didn't handle
|
388
|
+
# eof properly for result drivers that didn't return sets of
|
389
|
+
# rows as an array of rows
|
390
|
+
|
391
|
+
res = mock_result
|
392
|
+
|
393
|
+
res.as(:YAML)
|
394
|
+
row = res.first # does not advance underlying index
|
395
|
+
assert_equal({:zero=>-1, :one=>0, :two=>1}, YAML.load(row))
|
396
|
+
|
397
|
+
res.each do |r|
|
398
|
+
assert_equal({:zero=>-1, :one=>0, :two=>1}, YAML.load(r))
|
399
|
+
break # Just one row, thank you
|
400
|
+
end
|
401
|
+
|
402
|
+
row = res.last
|
403
|
+
assert_equal({:zero=>8, :one=>9, :two=>10}, YAML.load(row))
|
404
|
+
|
405
|
+
res.sth.finish
|
406
|
+
end
|
407
|
+
|
408
|
+
def test_17_results_as_empty
|
409
|
+
[:Array, :Struct, :YAML, :CSV].each do |rd|
|
410
|
+
res = mock_empty_result
|
411
|
+
res.as(rd).each do |not_reached|
|
412
|
+
assert(false, "each block unexpectedly reached during :#{rd} test")
|
413
|
+
end
|
414
|
+
res.sth.finish
|
415
|
+
|
416
|
+
res = mock_empty_result
|
417
|
+
res.as(rd)
|
418
|
+
assert_nil(res.first, "#first was not nil on empty result set")
|
419
|
+
assert_nil(res.last, "#last was not nil on empty result set")
|
420
|
+
res.sth.finish
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
302
424
|
end
|
303
425
|
|
304
426
|
# vim: syntax=ruby ts=2 et sw=2 sts=2
|