rdbi-driver-mysql 0.9.1

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