rdbi-driver-mysql 0.9.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Erik Hollensbe
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ = rdbi-dbd-mysql
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Running tests
16
+
17
+ Tests make use of the RDBI::DBRC role 'mysql_test' to test against a local database.
18
+
19
+ == Copyright
20
+
21
+ Copyright (c) 2010 Erik Hollensbe. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rdbi-driver-mysql"
8
+ gem.summary = %Q{mysql gem-based driver for RDBI}
9
+ gem.description = %Q{mysql gem-based driver for RDBI}
10
+ gem.email = "erik@hollensbe.org"
11
+ gem.homepage = "http://github.com/RDBI/rdbi-driver-mysql"
12
+ gem.authors = ["Erik Hollensbe"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ gem.add_dependency 'rdbi'
15
+ gem.add_dependency 'mysql', '>= 2.8.1'
16
+
17
+ gem.add_development_dependency 'test-unit'
18
+ gem.add_development_dependency 'rdoc'
19
+ gem.add_development_dependency 'rdbi-dbrc'
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
24
+ end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/**/test_*.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => :check_dependencies
47
+
48
+ task :default => :test
49
+
50
+ require 'rdoc/task'
51
+ RDoc::Task.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "rdbi-dbd-mysql #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.1
@@ -0,0 +1,430 @@
1
+ require 'rdbi'
2
+ require 'epoxy'
3
+ require 'methlab'
4
+
5
+ gem 'mysql', '= 2.8.1'
6
+ require 'mysql'
7
+
8
+ class RDBI::Driver::MySQL < RDBI::Driver
9
+ def initialize( *args )
10
+ super( Database, *args )
11
+ end
12
+ end
13
+
14
+ class RDBI::Driver::MySQL < RDBI::Driver
15
+ # XXX basically taken verbatim from DBI. someone kill me now.
16
+ TYPE_MAP = { }
17
+
18
+ ::Mysql::Field.constants.grep(/^TYPE_/).each do |const|
19
+ mysql_type = Mysql::Field.const_get(const) # numeric type code
20
+ TYPE_MAP[mysql_type] =
21
+ case const.to_s
22
+ when 'TYPE_TINY', 'TYPE_CHAR'
23
+ # XXX gonna break. design fix.
24
+ 'tinyint'
25
+ when 'TYPE_SHORT'
26
+ 'smallint'
27
+ when 'TYPE_INT24'
28
+ 'mediumint'
29
+ when 'TYPE_LONG'
30
+ 'integer'
31
+ when 'TYPE_LONGLONG'
32
+ 'bigint'
33
+ when 'TYPE_FLOAT'
34
+ 'float'
35
+ when 'TYPE_DOUBLE'
36
+ 'double'
37
+ when 'TYPE_VAR_STRING', 'TYPE_STRING'
38
+ 'varchar'
39
+ when 'TYPE_DATE', 'TYPE_TIME', 'TYPE_DATETIME', 'TYPE_TIMESTAMP'
40
+ 'datetime'
41
+ when 'TYPE_TINY_BLOB'
42
+ 'tinyblob'
43
+ when 'TYPE_MEDIUM_BLOB'
44
+ 'mediumblob'
45
+ when 'TYPE_LONG_BLOB'
46
+ 'longblob'
47
+ when 'TYPE_GEOMETRY'
48
+ 'blob'
49
+ when 'TYPE_YEAR',
50
+ 'TYPE_DECIMAL',
51
+ 'TYPE_BLOB',
52
+ 'TYPE_ENUM',
53
+ 'TYPE_SET',
54
+ 'TYPE_SET',
55
+ 'TYPE_BIT',
56
+ 'TYPE_NULL'
57
+ const.to_s.sub(/^TYPE_/, '').downcase
58
+ else
59
+ 'unknown'
60
+ end
61
+ end
62
+
63
+ class Database < RDBI::Database
64
+ extend MethLab
65
+
66
+ attr_reader :my_conn
67
+ attr_accessor :cast_booleans
68
+
69
+ def initialize(*args)
70
+ super(*args)
71
+
72
+ args = args[0]
73
+
74
+ self.database_name = args[:database] || args[:dbname] || args[:db]
75
+
76
+ username = args[:username] || args[:user]
77
+ password = args[:password] || args[:pass]
78
+
79
+ # FIXME flags?
80
+
81
+ raise ArgumentError, "database name not provided" unless self.database_name
82
+ raise ArgumentError, "username not provided" unless username
83
+
84
+ @cast_booleans = args[:cast_booleans] || false
85
+
86
+ @my_conn = if args[:host] || args[:hostname]
87
+ Mysql.connect(
88
+ args[:host] || args[:hostname],
89
+ username,
90
+ password,
91
+ self.database_name,
92
+ args[:port]
93
+ )
94
+ elsif args[:sock] || args[:socket]
95
+ Mysql.connect(
96
+ nil,
97
+ username,
98
+ password,
99
+ self.database_name,
100
+ nil,
101
+ args[:sock] || args[:socket]
102
+ )
103
+ else
104
+ raise ArgumentError, "either :host, :hostname, :socket, or :sock must be provided as a connection argument"
105
+ end
106
+
107
+ @preprocess_quoter = proc do |x, named, indexed|
108
+ @my_conn.quote((named[x] || indexed[x]).to_s)
109
+ end
110
+ end
111
+
112
+ def disconnect
113
+ @my_conn.close
114
+ super
115
+ end
116
+
117
+ def transaction(&block)
118
+ if in_transaction?
119
+ raise RDBI::TransactionError.new( "Already in transaction (not supported by MySQL)" )
120
+ end
121
+ @my_conn.autocommit(false)
122
+ super &block
123
+ @my_conn.autocommit(true)
124
+ end
125
+
126
+ def rollback
127
+ if ! in_transaction?
128
+ raise RDBI::TransactionError.new( "Cannot rollback when not in a transaction" )
129
+ end
130
+ @my_conn.rollback
131
+ super
132
+ end
133
+
134
+ def commit
135
+ if ! in_transaction?
136
+ raise RDBI::TransactionError.new( "Cannot commit when not in a transaction" )
137
+ end
138
+ @my_conn.commit
139
+ super
140
+ end
141
+
142
+ def new_statement(query)
143
+ Statement.new(query, self)
144
+ end
145
+
146
+ def table_schema( table_name )
147
+ info_row = execute(
148
+ "SELECT table_type FROM information_schema.tables WHERE table_schema = ? and table_name = ?",
149
+ self.database_name,
150
+ table_name.to_s
151
+ ).fetch( :first )
152
+ if info_row.nil?
153
+ return nil
154
+ end
155
+
156
+ sch = RDBI::Schema.new( [], [] )
157
+ sch.tables << table_name.to_sym
158
+
159
+ case info_row[ 0 ]
160
+ when 'BASE TABLE'
161
+ sch.type = :table
162
+ when 'VIEW'
163
+ sch.type = :view
164
+ end
165
+
166
+ execute( "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = ? and table_name = ?",
167
+ self.database_name,
168
+ table_name.to_s
169
+ ).fetch( :all ).each do |row|
170
+ col = RDBI::Column.new
171
+ col.name = row[0].to_sym
172
+ col.type = row[1].to_sym
173
+ # TODO: ensure this ruby_type is solid, especially re: dates and times
174
+ col.ruby_type = row[1].to_sym
175
+ col.nullable = row[2] == "YES"
176
+ sch.columns << col
177
+ end
178
+
179
+ sch
180
+ end
181
+
182
+ def schema
183
+ schemata = []
184
+ execute( "SELECT table_name FROM information_schema.tables where table_schema = ?", self.database_name ).fetch( :all ).each do |row|
185
+ schemata << table_schema( row[0] )
186
+ end
187
+ schemata
188
+ end
189
+
190
+ def ping
191
+ begin
192
+ @my_conn.ping
193
+ return 1
194
+ rescue
195
+ raise RDBI::DisconnectedError, "not connected"
196
+ end
197
+ end
198
+ end
199
+
200
+ #
201
+ # Due to mysql statement handles and result sets being tightly coupled,
202
+ # RDBI::Database#execute may require a full fetch of the result set for any
203
+ # of this to work.
204
+ #
205
+ # If you *must* use execute, use the block form, which will wait to close any
206
+ # statement handles. Performance will differ sharply.
207
+ #
208
+ class Cursor < RDBI::Cursor
209
+ def initialize(handle)
210
+ super(handle)
211
+ @index = 0
212
+ end
213
+
214
+ def fetch(count=1)
215
+ return [] if last_row?
216
+ a = []
217
+ count.times { a.push(next_row) }
218
+ return a
219
+ end
220
+
221
+ def next_row
222
+ val = if @array_handle
223
+ @array_handle[@index]
224
+ else
225
+ @handle.fetch
226
+ end
227
+
228
+ @index += 1
229
+ val
230
+ end
231
+
232
+ def result_count
233
+ if @array_handle
234
+ @array_handle.size
235
+ else
236
+ @handle.num_rows
237
+ end
238
+ end
239
+
240
+ def affected_count
241
+ if @array_handle
242
+ 0
243
+ else
244
+ @handle.affected_rows
245
+ end
246
+ end
247
+
248
+ def first
249
+ if @array_handle
250
+ @array_handle.first
251
+ else
252
+ cnt = @handle.row_tell
253
+ @handle.data_seek(0)
254
+ res = @handle.fetch
255
+ @handle.data_seek(cnt)
256
+ res
257
+ end
258
+ end
259
+
260
+ def last
261
+ if @array_handle
262
+ @array_handle.last
263
+ else
264
+ cnt = @handle.row_tell
265
+ @handle.data_seek(@handle.num_rows)
266
+ res = @handle.fetch
267
+ @handle.data_seek(cnt)
268
+ res
269
+ end
270
+ end
271
+
272
+ def rest
273
+ oindex, @index = [@index, @handle.num_rows] rescue [@index, @array_handle.size]
274
+ fetch_range(oindex, @index)
275
+ end
276
+
277
+ def all
278
+ fetch_range(0, (@handle.num_rows rescue @array_handle.size))
279
+ end
280
+
281
+ def [](index)
282
+ @array_handle[index]
283
+ end
284
+
285
+ def last_row?
286
+ if @array_handle
287
+ @index == @array_handle.size
288
+ else
289
+ @handle.eof?
290
+ end
291
+ end
292
+
293
+ def rewind
294
+ @index = 0
295
+ unless @array_handle
296
+ @handle.data_seek(0)
297
+ end
298
+ end
299
+
300
+ def empty?
301
+ @array_handle.empty?
302
+ end
303
+
304
+ def finish
305
+ @handle.free_result
306
+ end
307
+
308
+ def coerce_to_array
309
+ unless @array_handle
310
+ @array_handle = []
311
+ begin
312
+ @handle.num_rows.times { @array_handle.push(@handle.fetch) }
313
+ rescue
314
+ end
315
+ end
316
+ end
317
+
318
+ protected
319
+
320
+ def fetch_range(start, stop)
321
+ if @array_handle
322
+ @array_handle[start, stop]
323
+ else
324
+ ary = []
325
+
326
+ @handle.data_seek(start)
327
+ (stop - start).times do
328
+ @handle.fetch
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ class Statement < RDBI::Statement
335
+ extend MethLab
336
+
337
+ attr_reader :my_query
338
+
339
+ def initialize(query, dbh)
340
+ super(query, dbh)
341
+
342
+ ep = Epoxy.new(query)
343
+ @index_map = ep.indexed_binds
344
+
345
+ # FIXME straight c'n'p from postgres, not sure it's needed.
346
+ query = ep.quote(@index_map.compact.inject({}) { |x,y| x.merge({ y => nil }) }) { |x| '?' }
347
+
348
+ @my_query = dbh.my_conn.prepare(query)
349
+ # FIXME type maps
350
+ @output_type_map = RDBI::Type.create_type_hash( RDBI::Type::Out )
351
+
352
+ manipulate_type_maps
353
+ end
354
+
355
+ def new_execution(*binds)
356
+ # FIXME move to RDBI::Util or something.
357
+ hashes, binds = binds.partition { |x| x.kind_of?(Hash) }
358
+ hash = hashes.inject({}) { |x, y| x.merge(y) }
359
+ hash.keys.each do |key|
360
+ if index = @index_map.index(key)
361
+ binds.insert(index, hash[key])
362
+ end
363
+ end
364
+
365
+ res = @my_query.execute(*binds)
366
+
367
+ columns = []
368
+ metadata = res.result_metadata rescue nil
369
+
370
+ if metadata
371
+ columns = res.result_metadata.fetch_fields.collect do |col|
372
+ RDBI::Column.new(
373
+ col.name.to_sym,
374
+ map_type(col),
375
+ map_type(col).to_sym,
376
+ col.length,
377
+ col.decimals,
378
+ !col.is_not_null?
379
+ )
380
+ end
381
+ end
382
+
383
+ schema = RDBI::Schema.new columns
384
+ [ Cursor.new(res), schema, @output_type_map ]
385
+ end
386
+
387
+ def finish
388
+ @my_query.close rescue nil
389
+ super
390
+ end
391
+
392
+ protected
393
+
394
+ def map_type(col)
395
+ if col.is_num? && col.length == 1
396
+ 'boolean'
397
+ else
398
+ TYPE_MAP[col.type]
399
+ end
400
+ end
401
+
402
+ def manipulate_type_maps
403
+ datetime_check = proc { |x| x.kind_of?(::Mysql::Time) }
404
+ zone = DateTime.now.zone
405
+ # XXX yep. slow as snot.
406
+ datetime_conv = proc { |x| DateTime.parse(x.to_s + " #{zone}") }
407
+
408
+ @output_type_map[:datetime] = RDBI::Type.filterlist(TypeLib::Filter.new(datetime_check, datetime_conv))
409
+
410
+ @input_type_map[TrueClass] = TypeLib::Filter.new(
411
+ RDBI::Type::Checks::IS_BOOLEAN,
412
+ proc { |x| 1 }
413
+ )
414
+
415
+ @input_type_map[FalseClass] = TypeLib::Filter.new(
416
+ RDBI::Type::Checks::IS_BOOLEAN,
417
+ proc { |x| 0 }
418
+ )
419
+
420
+ if dbh.cast_booleans
421
+ boolean_filter = TypeLib::Filter.new(
422
+ proc { |x| x == 1 or x == 0 },
423
+ proc { |x| x == 1 }
424
+ )
425
+
426
+ @output_type_map[:boolean] = boolean_filter
427
+ end
428
+ end
429
+ end
430
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ gem 'test-unit'
3
+ require 'test/unit'
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ require 'rdbi/driver/mysql'
8
+ gem 'rdbi-dbrc'
9
+ require 'rdbi-dbrc'
10
+
11
+ class Test::Unit::TestCase
12
+
13
+ attr_accessor :dbh
14
+
15
+ SQL = [
16
+ %q[drop table if exists test],
17
+ %q[drop table if exists integer_test],
18
+ %q[create table integer_test (id integer) ENGINE=InnoDB],
19
+ %q[drop table if exists foo],
20
+ %q[create table foo (bar integer) ENGINE=InnoDB],
21
+ %q[drop table if exists datetime_test],
22
+ %q[create table datetime_test (item datetime) ENGINE=InnoDB],
23
+ %q[drop table if exists boolean_test],
24
+ %q[create table boolean_test (id integer, item boolean) ENGINE=InnoDB],
25
+ %q[drop table if exists bar],
26
+ %q[create table bar (foo varchar(255), bar integer) ENGINE=InnoDB],
27
+ ]
28
+
29
+ def init_database
30
+ self.dbh = connect unless self.dbh and self.dbh.connected?
31
+
32
+ SQL.each do |sql|
33
+ dbh.execute(sql)
34
+ end
35
+
36
+ self.dbh
37
+ end
38
+
39
+ def connect
40
+ RDBI::DBRC.connect(:mysql_test)
41
+ end
42
+
43
+ def role
44
+ RDBI::DBRC.roles[:mysql_test]
45
+ end
46
+
47
+ def setup
48
+ init_database
49
+ end
50
+
51
+ def teardown
52
+ dbh.disconnect if dbh and dbh.connected?
53
+ end
54
+ end
@@ -0,0 +1,54 @@
1
+ require 'helper'
2
+
3
+ class TestConnect < Test::Unit::TestCase
4
+ def test_01_connect
5
+ dbh = connect
6
+ assert(dbh)
7
+ assert(dbh.connected?)
8
+ assert(dbh.database_name)
9
+
10
+ assert_equal(dbh.database_name, role[:database])
11
+ end
12
+
13
+ def test_02_connect_exceptions
14
+ e_database = ArgumentError.new("database name not provided")
15
+ e_username = ArgumentError.new("username not provided")
16
+ e_connect = ArgumentError.new("either :host, :hostname, :socket, or :sock must be provided as a connection argument")
17
+
18
+ base_args = {
19
+ :host => "localhost",
20
+ :hostname => "localhost",
21
+ :port => 3306,
22
+ :username => "foreal",
23
+ :password => "notreally",
24
+ :database => "foobar",
25
+ :sock => "/tmp/shit",
26
+ :socket => "/tmp/shit",
27
+ }
28
+
29
+ args = base_args.dup
30
+ args.delete(:database)
31
+ assert_raises(e_database) { RDBI.connect(:MySQL, args) }
32
+
33
+ args = base_args.dup
34
+ args.delete(:username)
35
+ assert_raises(e_username) { RDBI.connect(:MySQL, args) }
36
+
37
+ args = base_args.dup
38
+ args.delete(:host)
39
+ args.delete(:hostname)
40
+ args.delete(:sock)
41
+ args.delete(:socket)
42
+ assert_raises(e_connect) { RDBI.connect(:MySQL, args) }
43
+ end
44
+
45
+ def test_03_disconnection
46
+ dbh = connect
47
+ assert(dbh)
48
+ assert(dbh.connected?)
49
+ dbh.disconnect
50
+ assert(!dbh.connected?)
51
+
52
+ assert_raises(Mysql::Error.new("MySQL server has gone away")) { dbh.instance_variable_get(:@my_conn).ping }
53
+ end
54
+ end
@@ -0,0 +1,163 @@
1
+ require 'helper'
2
+
3
+ class TestDatabase < Test::Unit::TestCase
4
+ def test_01_connect
5
+ assert dbh
6
+ assert_kind_of( RDBI::Driver::MySQL::Database, dbh )
7
+ assert_kind_of( RDBI::Database, dbh )
8
+ assert_equal( dbh.database_name, role[:database] )
9
+ dbh.disconnect
10
+ assert ! dbh.connected?
11
+ end
12
+
13
+ def test_02_ping
14
+ my_role = role.dup
15
+ driver = my_role.delete(:driver)
16
+
17
+ assert_kind_of(Numeric, dbh.ping)
18
+ assert_kind_of(Numeric, RDBI.ping(driver, my_role))
19
+ dbh.disconnect
20
+
21
+ assert_raises(RDBI::DisconnectedError.new("not connected")) do
22
+ dbh.ping
23
+ end
24
+
25
+ # XXX This should still work because it connects. Obviously, testing a
26
+ # downed database is gonna be pretty hard.
27
+ assert_kind_of(Numeric, RDBI.ping(driver, my_role))
28
+ end
29
+
30
+ def test_03_execute
31
+ self.dbh = init_database
32
+ res = dbh.execute( "insert into foo (bar) values (?)", 1 )
33
+ assert res
34
+ assert_kind_of( RDBI::Result, res )
35
+ assert_equal( 1, res.affected_count )
36
+
37
+ res = dbh.execute( "select * from foo" )
38
+ assert res
39
+ assert_kind_of( RDBI::Result, res )
40
+ assert_equal( [[1]], res.fetch(:all) )
41
+
42
+ rows = res.as( :Struct ).fetch( :all )
43
+ row = rows[ 0 ]
44
+ assert_equal( 1, row.bar )
45
+ end
46
+
47
+ def test_04_transaction
48
+ self.dbh = init_database
49
+
50
+ dbh.transaction do
51
+ assert dbh.in_transaction?
52
+ 5.times { dbh.execute( "insert into foo (bar) values (?)", 1 ) }
53
+ dbh.rollback
54
+ assert ! dbh.in_transaction?
55
+ end
56
+
57
+ assert ! dbh.in_transaction?
58
+
59
+ assert_equal( [], dbh.execute("select * from foo").fetch(:all) )
60
+
61
+ dbh.transaction do
62
+ assert dbh.in_transaction?
63
+ 5.times { dbh.execute("insert into foo (bar) values (?)", 1) }
64
+ assert_equal( [[1]] * 5, dbh.execute("select * from foo").fetch(:all) )
65
+ dbh.commit
66
+ assert ! dbh.in_transaction?
67
+ end
68
+
69
+ assert ! dbh.in_transaction?
70
+
71
+ assert_equal( [[1]] * 5, dbh.execute("select * from foo").fetch(:all) )
72
+
73
+ dbh.transaction do
74
+ assert dbh.in_transaction?
75
+ assert_raises( RDBI::TransactionError ) do
76
+ dbh.transaction do
77
+ end
78
+ end
79
+ end
80
+
81
+ # Not in a transaction
82
+
83
+ assert_raises( RDBI::TransactionError ) do
84
+ dbh.rollback
85
+ end
86
+
87
+ assert_raises( RDBI::TransactionError ) do
88
+ dbh.commit
89
+ end
90
+ end
91
+
92
+ def test_05_preprocess_query
93
+ self.dbh = init_database
94
+ assert_equal(
95
+ "insert into foo (bar) values (1)",
96
+ dbh.preprocess_query( "insert into foo (bar) values (?)", 1 )
97
+ )
98
+ end
99
+
100
+ def test_06_schema
101
+ self.dbh = init_database
102
+
103
+ dbh.execute( "insert into bar (foo, bar) values (?, ?)", "foo", 1 )
104
+ res = dbh.execute( "select * from bar" )
105
+
106
+ assert res
107
+ assert res.schema
108
+ assert_kind_of( RDBI::Schema, res.schema )
109
+ assert res.schema.columns
110
+ res.schema.columns.each { |x| assert_kind_of(RDBI::Column, x) }
111
+ end
112
+
113
+ def test_07_table_schema
114
+ self.dbh = init_database
115
+ assert_respond_to( dbh, :table_schema )
116
+
117
+ schema = dbh.table_schema( :foo )
118
+ columns = schema.columns
119
+ assert_equal columns.size, 1
120
+ c = columns[ 0 ]
121
+ assert_equal c.name, :bar
122
+ assert_equal c.type, :int
123
+
124
+ schema = dbh.table_schema( :bar )
125
+ columns = schema.columns
126
+ assert_equal columns.size, 2
127
+ columns.each do |c|
128
+ case c.name
129
+ when :foo
130
+ assert_equal c.type, :varchar
131
+ when :bar
132
+ assert_equal c.type, :int
133
+ end
134
+ end
135
+
136
+ assert_nil dbh.table_schema( :non_existent )
137
+ end
138
+
139
+ def test_08_basic_schema
140
+ self.dbh = init_database
141
+ assert_respond_to( dbh, :schema )
142
+ schema = dbh.schema.sort_by { |x| x.tables[0].to_s }
143
+
144
+ tables = [ :bar, :boolean_test, :datetime_test, :foo, :integer_test ]
145
+ columns = {
146
+ :bar => { :foo => :varchar, :bar => :int },
147
+ :foo => { :bar => :int },
148
+ :integer_test => { :id => :int },
149
+ :datetime_test => { :item => :datetime },
150
+ :boolean_test => { :id => :int, :item => :tinyint },
151
+ }
152
+
153
+ schema.each_with_index do |sch, x|
154
+ assert_kind_of( RDBI::Schema, sch )
155
+ assert_equal( sch.tables[0], tables[x] )
156
+
157
+ sch.columns.each do |col|
158
+ assert_kind_of( RDBI::Column, col )
159
+ assert_equal( columns[ tables[x] ][ col.name ], col.type )
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,22 @@
1
+ require 'helper'
2
+
3
+ class TestStatement < Test::Unit::TestCase
4
+ def test_01_statement_creation_and_finish
5
+ sth = dbh.prepare("create table `test` (id integer)")
6
+ assert(sth)
7
+ assert(!sth.finished?)
8
+ assert_equal(sth.query, "create table `test` (id integer)")
9
+ sth.finish
10
+ assert(sth.finished?)
11
+ end
12
+
13
+ def test_02_statement_execution
14
+ sth = dbh.prepare("insert into integer_test (id) values (?)")
15
+ assert(sth)
16
+ assert_equal(sth.query, "insert into integer_test (id) values (?)")
17
+ sth.execute(1)
18
+ # FIXME affected rows
19
+ sth.finish
20
+ assert_equal([[1]], dbh.execute("select * from integer_test").fetch(:all))
21
+ end
22
+ end
@@ -0,0 +1,73 @@
1
+ require 'helper'
2
+
3
+ class TestTypes < Test::Unit::TestCase
4
+ def test_01_booleans
5
+ dbh.cast_booleans = true
6
+ res = dbh.execute( "SELECT true" )
7
+ assert_equal true, res.fetch(:first)[0]
8
+
9
+ res = dbh.execute( "SELECT false" )
10
+ assert_equal false, res.fetch(:first)[0]
11
+
12
+ dbh.cast_booleans = false
13
+ res = dbh.execute( "SELECT true" )
14
+ assert_equal 1, res.fetch(:first)[0]
15
+
16
+ res = dbh.execute( "SELECT false" )
17
+ assert_equal 0, res.fetch(:first)[0]
18
+
19
+ dbh.execute("insert into boolean_test (id, item) values (?, ?)", 0, true);
20
+ dbh.execute("insert into boolean_test (id, item) values (?, ?)", 1, false);
21
+
22
+ dbh.cast_booleans = true
23
+ row = dbh.execute("select id, item from boolean_test where id=?", 0).fetch(:first)
24
+ assert_equal(0, row[0])
25
+ assert_equal(true, row[1])
26
+
27
+ row = dbh.execute("select id, item from boolean_test where id=?", 1).fetch(:first)
28
+ assert_equal(1, row[0])
29
+ assert_equal(false, row[1])
30
+
31
+ dbh.cast_booleans = false
32
+ row = dbh.execute("select id, item from boolean_test where id=?", 0).fetch(:first)
33
+ assert_equal(0, row[0])
34
+ assert_equal(1, row[1])
35
+
36
+ row = dbh.execute("select id, item from boolean_test where id=?", 1).fetch(:first)
37
+ assert_equal(1, row[0])
38
+ assert_equal(0, row[1])
39
+ end
40
+
41
+ def test_02_general
42
+ dbh.execute( "insert into foo (bar) values (?)", 1 )
43
+
44
+ res = dbh.execute( "select count(*) from foo" )
45
+ assert res
46
+ assert_kind_of( RDBI::Result, res )
47
+ assert_equal( [[1]], res.fetch(:all) )
48
+ row = res.as( :Array ).fetch( :first )
49
+ assert_equal 1, row[ 0 ]
50
+
51
+ res = dbh.execute( "SELECT 5" )
52
+ assert res
53
+ assert_kind_of( RDBI::Result, res )
54
+ row = res.as( :Array ).fetch( :first )
55
+ assert_equal 5, row[ 0 ]
56
+
57
+ time_str = DateTime.now.strftime( "%Y-%m-%d %H:%M:%S %z" )
58
+ res = dbh.execute( "SELECT 5, 'hello', cast('#{time_str}' as datetime)" )
59
+ assert res
60
+ assert_kind_of( RDBI::Result, res )
61
+ row = res.fetch( :all )[ 0 ]
62
+ assert_equal 5, row[ 0 ]
63
+ assert_equal 'hello', row[ 1 ]
64
+ assert_equal DateTime.parse( time_str ), row[ 2 ]
65
+ end
66
+
67
+ def test_03_datetime
68
+ dt = DateTime.now
69
+ dbh.execute("insert into datetime_test (item) values (?)", dt)
70
+ row = dbh.execute("select item from datetime_test limit 1").fetch(:first)
71
+ assert_equal(dt.to_s, row[0].to_s)
72
+ end
73
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rdbi-driver-mysql
3
+ version: !ruby/object:Gem::Version
4
+ hash: 57
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 1
10
+ version: 0.9.1
11
+ platform: ruby
12
+ authors:
13
+ - Erik Hollensbe
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-21 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rdbi
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: mysql
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 45
44
+ segments:
45
+ - 2
46
+ - 8
47
+ - 1
48
+ version: 2.8.1
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: test-unit
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rdoc
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: rdbi-dbrc
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ type: :development
92
+ version_requirements: *id005
93
+ description: mysql gem-based driver for RDBI
94
+ email: erik@hollensbe.org
95
+ executables: []
96
+
97
+ extensions: []
98
+
99
+ extra_rdoc_files:
100
+ - LICENSE
101
+ - README.rdoc
102
+ files:
103
+ - .document
104
+ - .gitignore
105
+ - LICENSE
106
+ - README.rdoc
107
+ - Rakefile
108
+ - VERSION
109
+ - lib/rdbi/driver/mysql.rb
110
+ - test/helper.rb
111
+ - test/test_connect.rb
112
+ - test/test_database.rb
113
+ - test/test_statement.rb
114
+ - test/test_types.rb
115
+ has_rdoc: true
116
+ homepage: http://github.com/RDBI/rdbi-driver-mysql
117
+ licenses: []
118
+
119
+ post_install_message:
120
+ rdoc_options:
121
+ - --charset=UTF-8
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ hash: 3
130
+ segments:
131
+ - 0
132
+ version: "0"
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ hash: 3
139
+ segments:
140
+ - 0
141
+ version: "0"
142
+ requirements: []
143
+
144
+ rubyforge_project:
145
+ rubygems_version: 1.3.7
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: mysql gem-based driver for RDBI
149
+ test_files:
150
+ - test/helper.rb
151
+ - test/test_connect.rb
152
+ - test/test_database.rb
153
+ - test/test_statement.rb
154
+ - test/test_types.rb