do_mysql 0.2.4 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,65 +1,66 @@
1
- if `uname -sr` =~ /^Darwin/
2
- ENV["RC_ARCHS"] = `uname -m`.chomp
3
- unless File.exists?("/usr/local/mysql/lib/mysql")
4
- `sudo ln -s /usr/local/mysql/lib /usr/local/mysql/lib/mysql` rescue nil
5
- end
6
- end
1
+ if RUBY_PLATFORM =~ /darwin/
2
+ ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
7
3
 
8
- # Figure out where mysql_config is
9
- if !`which mysql_config`.chomp.empty?
10
- @mysql_config_bin = "mysql_config"
11
- elsif !`which mysql_config5`.chomp.empty?
12
- @mysql_config_bin = "mysql_config5"
13
- else
14
- puts "Cannot find mysql_config in your path. Please enter a location: "
15
- location = gets.chomp
16
- if File.exists?(location)
17
- @mysql_config_bin = location
18
- else
19
- puts "Cannot find that file. Exiting."
20
- exit
21
- end
4
+ # On PowerPC the defaults are fine
5
+ ENV["RC_ARCHS"] = '' if `uname -m` =~ /^Power Macintosh/
22
6
  end
23
7
 
24
8
  require 'mkmf'
25
9
 
26
- def config_value(type)
27
- ENV["MYSQL_#{type.upcase}"] || mysql_config(type)
10
+ # All instances of mysql_config on PATH ...
11
+ def mysql_config_paths
12
+ ENV['PATH'].split(File::PATH_SEPARATOR).collect do |path|
13
+ [ "#{path}/mysql_config", "#{path}/mysql_config5" ].
14
+ detect { |bin| File.exist?(bin) }
15
+ end
28
16
  end
29
17
 
30
- @mysql_config = {}
31
-
32
- def mysql_config(type)
33
- return @mysql_config[type] if @mysql_config[type]
18
+ # The first mysql_config binary on PATH ...
19
+ def default_mysql_config_path
20
+ mysql_config_paths.compact.first
21
+ end
34
22
 
35
- sout = `#{@mysql_config_bin} --#{type}`
36
-
37
- unless $?.success?
38
- raise "mysql_config not found"
23
+ def default_prefix
24
+ if mc = default_mysql_config_path
25
+ File.dirname(File.dirname(mc))
26
+ else
27
+ "/usr/local"
39
28
  end
40
-
41
- @mysql_config[type] = sout.chomp[2..-1]
42
- @mysql_config[type]
43
29
  end
44
30
 
45
- $inc, $lib = dir_config('mysql', config_value('include'), config_value('libs_r'))
46
-
47
- def have_build_env
31
+ # Allow overriding path to mysql_config on command line using:
32
+ # ruby extconf.rb --with-mysql-config=/path/to/mysql_config
33
+ if RUBY_PLATFORM =~ /mswin|mingw/
34
+ dir_config('mysql')
35
+ have_header 'mysql.h' || exit(1)
36
+ have_library 'libmysql' || exit(1)
37
+ have_func('mysql_query', 'mysql.h') || exit(1)
38
+ have_func('mysql_ssl_set', 'mysql.h')
39
+ elsif mc = with_config('mysql-config', default_mysql_config_path)
40
+ mc = default_mysql_config_path if mc == true
41
+ cflags = `#{mc} --cflags`.chomp
42
+ exit 1 if $? != 0
43
+ libs = `#{mc} --libs`.chomp
44
+ exit 1 if $? != 0
45
+ $CPPFLAGS += ' ' + cflags
46
+ $libs = libs + " " + $libs
47
+ else
48
+ inc, lib = dir_config('mysql', default_prefix)
48
49
  libs = ['m', 'z', 'socket', 'nsl']
49
- while not find_library('mysqlclient', "mysql_query", config_value('libs'), $lib, "#{$lib}/mysql") do
50
- exit 1 if libs.empty?
51
- have_library(libs.shift)
52
- end
53
- true
50
+ lib_dirs =
51
+ [ lib, "/usr/lib", "/usr/local/lib", "/opt/local/lib" ].collect do |path|
52
+ [ path, "#{path}/mysql", "#{path}/mysql5/mysql" ]
53
+ end
54
+ find_library('mysqlclient', 'mysql_query', *lib_dirs.flatten) || exit(1)
55
+ find_header('mysql.h', *lib_dirs.flatten.map { |p| p.gsub('/lib', '/include') })
54
56
  end
55
57
 
56
- required_libraries = []
57
- desired_functions = %w(mysql_ssl_set)
58
+ unless RUBY_PLATFORM =~ /mswin|mingw/
59
+ have_header 'mysql.h'
60
+ have_library 'mysqlclient' || exit(1)
61
+ have_func 'mysql_query' || exit(1)
62
+ have_func 'mysql_ssl_set'
63
+ end
58
64
 
59
- if have_build_env
60
- $CFLAGS << ' -Wall '
61
- dir_config("mysql_c")
62
- create_makefile("mysql_c")
63
- else
64
- puts 'Could not find MySQL build environment (libraries & headers): Makefile not created'
65
- end
65
+ $CFLAGS << ' -Wall ' unless RUBY_PLATFORM =~ /mswin/
66
+ create_makefile('do_mysql_ext')
@@ -1,258 +1,26 @@
1
- require 'mysql_c'
1
+ require 'rubygems'
2
2
  require 'data_objects'
3
-
4
- module DataObject
5
- module Mysql
6
- TYPES = Hash[*Mysql_c.constants.select {|x| x.include?("MYSQL_TYPE")}.map {|x| [Mysql_c.const_get(x), x.gsub(/^MYSQL_TYPE_/, "")]}.flatten]
7
-
8
- QUOTE_STRING = "\""
9
- QUOTE_COLUMN = "`"
10
-
11
- class Connection < DataObject::Connection
12
-
13
- attr_reader :db
14
-
15
- def initialize(connection_string)
16
- @state = STATE_CLOSED
17
- @connection_string = connection_string
18
- opts = connection_string.split(" ")
19
- opts.each do |opt|
20
- k, v = opt.split("=")
21
- raise ArgumentError, "you specified an invalid connection component: #{opt}" unless k && v
22
- instance_variable_set("@#{k}", v)
23
- end
24
- end
25
-
26
- def change_database(database_name)
27
- @dbname = database_name
28
- @connection_string.gsub(/db_name=[^ ]*/, "db_name=#{database_name}")
29
- end
30
-
31
- def open
32
- @db = Mysql_c.mysql_init(nil)
33
- raise ConnectionFailed, "could not allocate a MySQL connection" unless @db
34
- conn = Mysql_c.mysql_real_connect(@db, @host, @user, @password, @dbname, @port || 0, @socket, @flags || 0)
35
- raise ConnectionFailed, "Unable to connect to database with provided connection string. \n#{Mysql_c.mysql_error(@db)}" unless conn
36
- @state = STATE_OPEN
37
- true
38
- end
39
-
40
- def close
41
- if @state == STATE_OPEN
42
- Mysql_c.mysql_close(@db)
43
- @state = STATE_CLOSED
44
- true
45
- else
46
- false
47
- end
48
- end
49
-
50
- def create_command(text)
51
- Command.new(self, text)
52
- end
53
-
54
- def begin_transaction
55
- Transaction.new(self)
56
- end
57
-
58
- end
59
-
60
- class Field
61
- attr_reader :name, :type
62
-
63
- def initialize(ptr)
64
- @name, @type = ptr.name.to_s, ptr.type.to_s
65
- end
66
- end
67
-
68
- class Transaction
69
-
70
- attr_reader :connection
71
-
72
- def initialize(conn)
73
- @connection = conn
74
- exec_sql("BEGIN")
75
- end
76
-
77
- # Commits the transaction
78
- def commit
79
- exec_sql("COMMIT")
80
- end
81
-
82
- # Rolls back the transaction
83
- def rollback(savepoint = nil)
84
- raise NotImplementedError, "MySQL does not support savepoints" if savepoint
85
- exec_sql("ROLLBACK")
86
- end
87
-
88
- # Creates a savepoint for rolling back later (not commonly supported)
89
- def save(name)
90
- raise NotImplementedError, "MySQL does not support savepoints"
91
- end
92
-
93
- def create_command(*args)
94
- @connection.create_command(*args)
95
- end
96
-
97
- protected
98
-
99
- def exec_sql(sql)
100
- @connection.logger.debug(sql)
101
- Mysql_c.mysql_query(@connection.db, sql)
102
- end
103
-
104
- end
105
-
106
- class Reader < DataObject::Reader
107
-
108
- def initialize(db, reader)
109
- @reader = reader
110
- unless @reader
111
- if Mysql_c.mysql_field_count(db) == 0
112
- @records_affected = Mysql_c.mysql_affected_rows(db)
113
- close
114
- else
115
- raise UnknownError, "An unknown error has occured while trying to process a MySQL query.\n#{Mysql_c.mysql_error(db)}"
116
- end
117
- else
118
- @field_count = @reader.field_count
119
- @state = STATE_OPEN
120
-
121
- @native_fields, @fields = Mysql_c.mysql_c_fetch_field_types(@reader, @field_count), Mysql_c.mysql_c_fetch_field_names(@reader, @field_count)
122
-
123
- raise UnknownError, "An unknown error has occured while trying to process a MySQL query. There were no fields in the resultset\n#{Mysql_c.mysql_error(db)}" if @native_fields.empty?
124
-
125
- @has_rows = !(@row = Mysql_c.mysql_c_fetch_row(@reader)).nil?
3
+ require 'do_jdbc-support' if RUBY_PLATFORM =~ /java/ # generic, shared JDBC support code
4
+ require 'do_mysql_ext' # the C/Java extension for this DO driver
5
+ require 'do_mysql/transaction'
6
+
7
+ if RUBY_PLATFORM =~ /java/
8
+ require 'do_jdbc/mysql' # the JDBC driver, packaged as a gem
9
+
10
+ # Another way of loading the JDBC Class. This seems to be more relaible
11
+ # than Class.forName() within the data_objects.Connection Java class,
12
+ # which is currently not working as expected.
13
+ require 'java'
14
+ import 'com.mysql.jdbc.Driver'
15
+
16
+ module DataObjects
17
+ module Mysql
18
+ class Connection
19
+ def self.pool_size
20
+ 20
126
21
  end
127
22
  end
128
-
129
- def close
130
- if @state == STATE_OPEN
131
- Mysql_c.mysql_free_result(@reader)
132
- @state = STATE_CLOSED
133
- true
134
- else
135
- false
136
- end
137
- end
138
-
139
- def name(col)
140
- super
141
- @fields[col]
142
- end
143
-
144
- def get_index(name)
145
- super
146
- @fields.index(name)
147
- end
148
-
149
- def null?(idx)
150
- super
151
- @row[idx] == nil
152
- end
153
-
154
- def current_row
155
- @row
156
- end
157
-
158
- def item(idx)
159
- super
160
- typecast(@row[idx], idx)
161
- end
162
-
163
- def next
164
- super
165
- @row = Mysql_c.mysql_c_fetch_row(@reader)
166
- close if @row.nil?
167
- @row ? true : nil
168
- end
169
-
170
- def each
171
- return unless has_rows?
172
-
173
- while(true) do
174
- yield
175
- break unless self.next
176
- end
177
- end
178
-
179
- protected
180
- def native_type(col)
181
- super
182
- TYPES[@native_fields[col].type]
183
- end
184
-
185
- def typecast(val, idx)
186
- return nil if val.nil? || val == "NULL"
187
- field = @native_fields[idx]
188
- case TYPES[field]
189
- when "NULL"
190
- nil
191
- when "TINY"
192
- val != "0"
193
- when "BIT"
194
- val.to_i(2)
195
- when "SHORT", "LONG", "INT24", "LONGLONG"
196
- val == '' ? nil : val.to_i
197
- when "DECIMAL", "NEWDECIMAL", "FLOAT", "DOUBLE", "YEAR"
198
- val.to_f
199
- when "TIMESTAMP", "DATETIME"
200
- DateTime.parse(val) rescue nil
201
- when "TIME"
202
- DateTime.parse(val).to_time rescue nil
203
- when "DATE"
204
- Date.parse(val) rescue nil
205
- else
206
- val
207
- end
208
- end
209
- end
210
-
211
- class Command < DataObject::Command
212
-
213
- def execute_reader(*args)
214
- super
215
- sql = escape_sql(args)
216
- @connection.logger.debug { sql }
217
- result = Mysql_c.mysql_query(@connection.db, sql)
218
- # TODO: Real Error
219
- raise QueryError, "Your query failed.\n#{Mysql_c.mysql_error(@connection.db)}\n#{@text}" unless result == 0
220
- reader = Reader.new(@connection.db, Mysql_c.mysql_use_result(@connection.db))
221
- if block_given?
222
- result = yield(reader)
223
- reader.close
224
- result
225
- else
226
- reader
227
- end
228
- end
229
-
230
- def execute_non_query(*args)
231
- super
232
- sql = escape_sql(args)
233
- @connection.logger.debug { sql }
234
- result = Mysql_c.mysql_query(@connection.db, sql)
235
- raise QueryError, "Your query failed.\n#{Mysql_c.mysql_error(@connection.db)}\n#{@text}" unless result == 0
236
- reader = Mysql_c.mysql_store_result(@connection.db)
237
- raise QueryError, "You called execute_non_query on a query: #{@text}" if reader
238
- rows_affected = Mysql_c.mysql_affected_rows(@connection.db)
239
- Mysql_c.mysql_free_result(reader)
240
- return ResultData.new(@connection, rows_affected, Mysql_c.mysql_insert_id(@connection.db))
241
- end
242
-
243
- def quote_time(value)
244
- # TIMESTAMP() used for both time and datetime columns
245
- quote_datetime(value)
246
- end
247
-
248
- def quote_datetime(value)
249
- "TIMESTAMP('#{value.strftime("%Y-%m-%d %H:%M:%S")}')"
250
- end
251
-
252
- def quote_date(value)
253
- "DATE('#{value.strftime("%Y-%m-%d")}')"
254
- end
255
23
  end
256
-
257
24
  end
25
+
258
26
  end
@@ -0,0 +1,44 @@
1
+
2
+ module DataObjects
3
+
4
+ module Mysql
5
+
6
+ class Transaction < DataObjects::Transaction
7
+
8
+ def finalize_transaction
9
+ cmd = "XA END '#{id}'"
10
+ connection.create_command(cmd).execute_non_query
11
+ end
12
+
13
+ def begin
14
+ cmd = "XA START '#{id}'"
15
+ connection.create_command(cmd).execute_non_query
16
+ end
17
+
18
+ def commit
19
+ cmd = "XA COMMIT '#{id}'"
20
+ connection.create_command(cmd).execute_non_query
21
+ end
22
+
23
+ def rollback
24
+ finalize_transaction
25
+ cmd = "XA ROLLBACK '#{id}'"
26
+ connection.create_command(cmd).execute_non_query
27
+ end
28
+
29
+ def rollback_prepared
30
+ cmd = "XA ROLLBACK '#{id}'"
31
+ connection.create_command(cmd).execute_non_query
32
+ end
33
+
34
+ def prepare
35
+ finalize_transaction
36
+ cmd = "XA PREPARE '#{id}'"
37
+ connection.create_command(cmd).execute_non_query
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,241 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataObjects::Mysql do
5
+ include MysqlSpecHelpers
6
+
7
+ before :all do
8
+ setup_test_environment
9
+ end
10
+
11
+ it "should expose the proper DataObjects classes" do
12
+ DataObjects::Mysql.const_get('Connection').should_not be_nil
13
+ DataObjects::Mysql.const_get('Command').should_not be_nil
14
+ DataObjects::Mysql.const_get('Result').should_not be_nil
15
+ DataObjects::Mysql.const_get('Reader').should_not be_nil
16
+ end
17
+
18
+ it "should connect successfully via TCP" do
19
+ pending "Problems parsing regular connection URIs vs. JDBC URLs" if JRUBY
20
+ connection = DataObjects::Mysql::Connection.new("mysql://root@127.0.0.1:3306/do_mysql_test")
21
+ connection.should_not be_using_socket
22
+ end
23
+
24
+ #
25
+ # I comment this out partly to raise the issue for discussion. Socket files are afaik not supported under windows. Does this
26
+ # mean that we should test for it on unix boxes but not on windows boxes? Or does it mean that it should not be speced at all?
27
+ # It's not really a requirement, since all architectures that support MySQL also supports TCP connectsion, ne?
28
+ #
29
+ # it "should connect successfully via the socket file" do
30
+ # @connection = DataObjects::Mysql::Connection.new("mysql://root@localhost:3306/do_mysql_test/?socket=#{SOCKET_PATH}")
31
+ # @connection.should be_using_socket
32
+ # end
33
+
34
+ it "should return the current character set" do
35
+ pending "Problems parsing regular connection URIs vs. JDBC URLs" if JRUBY
36
+ connection = DataObjects::Mysql::Connection.new("mysql://root@localhost:3306/do_mysql_test")
37
+ connection.character_set.should == "utf8"
38
+ end
39
+
40
+ it "should support changing the character set" do
41
+ pending "Problems parsing regular connection URIs vs. JDBC URLs" if JRUBY
42
+ connection = DataObjects::Mysql::Connection.new("mysql://root@localhost:3306/do_mysql_test/?charset=latin1")
43
+ connection.character_set.should == "latin1"
44
+
45
+ @connection = DataObjects::Mysql::Connection.new("mysql://root@localhost:3306/do_mysql_test/?charset=utf8")
46
+ @connection.character_set.should == "utf8"
47
+ end
48
+
49
+ it "should raise an error when opened with an invalid server uri" do
50
+ pending "Problems parsing regular connection URIs vs. JDBC URLs" if JRUBY
51
+ def connecting_with(uri)
52
+ lambda { DataObjects::Mysql::Connection.new(uri) }
53
+ end
54
+
55
+ # Missing database name
56
+ connecting_with("mysql://root@localhost:3306/").should raise_error(MysqlError)
57
+
58
+ # Wrong port
59
+ connecting_with("mysql://root@localhost:666/").should raise_error(MysqlError)
60
+
61
+ # Bad Username
62
+ connecting_with("mysql://baduser@localhost:3306/").should raise_error(MysqlError)
63
+
64
+ # Bad Password
65
+ connecting_with("mysql://root:wrongpassword@localhost:3306/").should raise_error(MysqlError)
66
+
67
+ # Bad Database Name
68
+ connecting_with("mysql://root@localhost:3306/bad_database").should raise_error(MysqlError)
69
+
70
+ #
71
+ # Again, should socket even be speced if we don't support it across all platforms?
72
+ #
73
+ # Invalid Socket Path
74
+ #connecting_with("mysql://root@localhost:3306/do_mysql_test/?socket=/invalid/path/mysql.sock").should raise_error(MysqlError)
75
+ end
76
+ end
77
+
78
+ describe DataObjects::Mysql::Connection do
79
+ include MysqlSpecHelpers
80
+
81
+ before :all do
82
+ setup_test_environment
83
+ end
84
+
85
+ it "should raise an error when attempting to execute a bad query" do
86
+ lambda { @connection.create_command("INSERT INTO non_existant_table (tester) VALUES (1)").execute_non_query }.should raise_error(MysqlError)
87
+ lambda { @connection.create_command("SELECT * FROM non_existant table").execute_reader }.should raise_error(MysqlError)
88
+ end
89
+
90
+ end
91
+
92
+ describe DataObjects::Mysql::Reader do
93
+ include MysqlSpecHelpers
94
+
95
+ before :all do
96
+ setup_test_environment
97
+ end
98
+
99
+ it "should raise an error when you pass too many or too few types for the expected result set" do
100
+ lambda { select("SELECT name, fired_at FROM users", [String, DateTime, Integer]) }.should raise_error(MysqlError)
101
+ end
102
+
103
+ it "shouldn't raise an error when you pass NO types for the expected result set" do
104
+ lambda { select("SELECT name, fired_at FROM users", nil) }.should_not raise_error(MysqlError)
105
+ end
106
+
107
+ it "should return the proper number of fields" do
108
+ id = insert("INSERT INTO users (name) VALUES ('Billy Bob')")
109
+ select("SELECT id, name, fired_at FROM users WHERE id = ?", nil, id) do |reader|
110
+ reader.fields.size.should == 3
111
+ end
112
+ end
113
+
114
+ it "should raise an exception if .values is called after reading all available rows" do
115
+ select("SELECT * FROM widgets LIMIT 2") do |reader|
116
+ # select already calls next once for us
117
+ reader.next!
118
+ reader.next!
119
+
120
+ lambda { reader.values }.should raise_error(MysqlError)
121
+ end
122
+ end
123
+
124
+ it "should fetch the proper number of rows" do
125
+ ids = [
126
+ insert("INSERT INTO users (name) VALUES ('Slappy Wilson')"),
127
+ insert("INSERT INTO users (name) VALUES ('Jumpy Jones')")
128
+ ]
129
+
130
+ select("SELECT * FROM users WHERE id IN ?", nil, ids) do |reader|
131
+ # select already calls next once for us
132
+ reader.next!.should == true
133
+ reader.next!.should be_nil
134
+ end
135
+ end
136
+
137
+ it "should contain tainted strings" do
138
+ id = insert("INSERT INTO users (name) VALUES ('Cuppy Canes')")
139
+
140
+ select("SELECT name FROM users WHERE id = ?", nil, id) do |reader|
141
+ reader.values.first.should be_tainted
142
+ end
143
+
144
+ end
145
+
146
+ it "should return DB nulls as nil" do
147
+ id = insert("INSERT INTO users (name) VALUES (NULL)")
148
+ select("SELECT name from users WHERE name is null") do |reader|
149
+ reader.values[0].should == nil
150
+ end
151
+ end
152
+
153
+ it "should not convert empty strings to null" do
154
+ id = insert("INSERT INTO users (name) VALUES ('')")
155
+ select("SELECT name FROM users WHERE id = ?", [String], id) do |reader|
156
+ reader.values.first.should == ''
157
+ end
158
+ end
159
+
160
+ describe "Date, Time, and DateTime" do
161
+
162
+ it "should return DateTimes using the current locale's Time Zone" do
163
+ date = DateTime.now
164
+ id = insert("INSERT INTO users (name, fired_at) VALUES (?, ?)", 'Sam', date)
165
+ select("SELECT fired_at FROM users WHERE id = ?", [DateTime], id) do |reader|
166
+ reader.values.last.to_s.should == date.to_s
167
+ end
168
+ exec("DELETE FROM users WHERE id = ?", id)
169
+ end
170
+
171
+ now = DateTime.now
172
+
173
+ dates = [
174
+ now.new_offset( (-11 * 3600).to_r / 86400), # GMT -11:00
175
+ now.new_offset( (-9 * 3600 + 10 * 60).to_r / 86400), # GMT -9:10, contrived
176
+ now.new_offset( (-8 * 3600).to_r / 86400), # GMT -08:00
177
+ now.new_offset( (+3 * 3600).to_r / 86400), # GMT +03:00
178
+ now.new_offset( (+5 * 3600 + 30 * 60).to_r / 86400) # GMT +05:30 (New Delhi)
179
+ ]
180
+
181
+ dates.each do |date|
182
+ it "should return #{date.to_s} offset to the current locale's Time Zone if they were inserted using a different timezone" do
183
+ pending "We don't support non-local date input yet"
184
+
185
+ dates.each do |date|
186
+ id = insert("INSERT INTO users (name, fired_at) VALUES (?, ?)", 'Sam', date)
187
+
188
+ select("SELECT fired_at FROM users WHERE id = ?", [DateTime], id) do |reader|
189
+ reader.values.last.to_s.should == now.to_s
190
+ end
191
+
192
+ exec("DELETE FROM users WHERE id = ?", id)
193
+ end
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+ describe "executing a non-query" do
200
+ it "should return a Result" do
201
+ command = @connection.create_command("INSERT INTO invoices (invoice_number) VALUES ('1234')")
202
+ result = command.execute_non_query
203
+ result.should be_kind_of(DataObjects::Mysql::Result)
204
+ end
205
+
206
+ it "should be able to determine the affected_rows" do
207
+ command = @connection.create_command("INSERT INTO invoices (invoice_number) VALUES ('1234')")
208
+ result = command.execute_non_query
209
+ result.to_i.should == 1
210
+ end
211
+
212
+ it "should yield the last inserted id" do
213
+ @connection.create_command("TRUNCATE TABLE invoices").execute_non_query
214
+
215
+ result = @connection.create_command("INSERT INTO invoices (invoice_number) VALUES ('1234')").execute_non_query
216
+ result.insert_id.should == 1
217
+
218
+ result = @connection.create_command("INSERT INTO invoices (invoice_number) VALUES ('3456')").execute_non_query
219
+ result.insert_id.should == 2
220
+ end
221
+
222
+ it "should be able to determine the affected_rows" do
223
+ [
224
+ "TRUNCATE TABLE invoices",
225
+ "INSERT INTO invoices (invoice_number) VALUES ('1234')",
226
+ "INSERT INTO invoices (invoice_number) VALUES ('1234')"
227
+ ].each { |q| @connection.create_command(q).execute_non_query }
228
+
229
+ result = @connection.create_command("UPDATE invoices SET invoice_number = '3456'").execute_non_query
230
+ result.to_i.should == 2
231
+ end
232
+
233
+ it "should raise an error when executing an invalid query" do
234
+ command = @connection.create_command("UPDwhoopsATE invoices SET invoice_number = '3456'")
235
+
236
+ lambda { command.execute_non_query }.should raise_error(Exception)
237
+ end
238
+
239
+ end
240
+
241
+ end