mysql2 0.3.18 → 0.4.2

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +63 -12
  5. data/examples/eventmachine.rb +1 -1
  6. data/examples/threaded.rb +4 -6
  7. data/ext/mysql2/client.c +170 -175
  8. data/ext/mysql2/client.h +21 -1
  9. data/ext/mysql2/extconf.rb +95 -35
  10. data/ext/mysql2/infile.c +2 -2
  11. data/ext/mysql2/mysql2_ext.c +1 -0
  12. data/ext/mysql2/mysql2_ext.h +5 -6
  13. data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
  14. data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
  15. data/ext/mysql2/result.c +494 -132
  16. data/ext/mysql2/result.h +12 -6
  17. data/ext/mysql2/statement.c +494 -0
  18. data/ext/mysql2/statement.h +19 -0
  19. data/lib/mysql2/client.rb +68 -22
  20. data/lib/mysql2/console.rb +1 -1
  21. data/lib/mysql2/em.rb +5 -6
  22. data/lib/mysql2/error.rb +18 -27
  23. data/lib/mysql2/field.rb +3 -0
  24. data/lib/mysql2/statement.rb +17 -0
  25. data/lib/mysql2/version.rb +1 -1
  26. data/lib/mysql2.rb +38 -18
  27. data/spec/em/em_spec.rb +21 -21
  28. data/spec/mysql2/client_spec.rb +393 -351
  29. data/spec/mysql2/error_spec.rb +37 -36
  30. data/spec/mysql2/result_spec.rb +213 -208
  31. data/spec/mysql2/statement_spec.rb +684 -0
  32. data/spec/spec_helper.rb +7 -0
  33. data/spec/ssl/ca-cert.pem +17 -0
  34. data/spec/ssl/ca-key.pem +27 -0
  35. data/spec/ssl/ca.cnf +22 -0
  36. data/spec/ssl/cert.cnf +22 -0
  37. data/spec/ssl/client-cert.pem +17 -0
  38. data/spec/ssl/client-key.pem +27 -0
  39. data/spec/ssl/client-req.pem +15 -0
  40. data/spec/ssl/gen_certs.sh +48 -0
  41. data/spec/ssl/pkcs8-client-key.pem +28 -0
  42. data/spec/ssl/pkcs8-server-key.pem +28 -0
  43. data/spec/ssl/server-cert.pem +17 -0
  44. data/spec/ssl/server-key.pem +27 -0
  45. data/spec/ssl/server-req.pem +15 -0
  46. data/support/mysql_enc_to_ruby.rb +7 -8
  47. data/support/ruby_enc_to_mysql.rb +1 -1
  48. metadata +41 -46
@@ -1,77 +1,82 @@
1
1
  # encoding: UTF-8
2
2
  require 'spec_helper'
3
3
 
4
- describe Mysql2::Client do
4
+ RSpec.describe Mysql2::Client do
5
5
  context "using defaults file" do
6
6
  let(:cnf_file) { File.expand_path('../../my.cnf', __FILE__) }
7
7
 
8
8
  it "should not raise an exception for valid defaults group" do
9
- lambda {
9
+ expect {
10
10
  opts = DatabaseCredentials['root'].merge(:default_file => cnf_file, :default_group => "test")
11
11
  @client = Mysql2::Client.new(opts)
12
- }.should_not raise_error(Mysql2::Error)
12
+ }.not_to raise_error
13
13
  end
14
14
 
15
15
  it "should not raise an exception without default group" do
16
- lambda {
16
+ expect {
17
17
  @client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:default_file => cnf_file))
18
- }.should_not raise_error(Mysql2::Error)
18
+ }.not_to raise_error
19
19
  end
20
20
  end
21
21
 
22
22
  it "should raise an exception upon connection failure" do
23
- lambda {
23
+ expect {
24
24
  # The odd local host IP address forces the mysql client library to
25
25
  # use a TCP socket rather than a domain socket.
26
26
  Mysql2::Client.new DatabaseCredentials['root'].merge('host' => '127.0.0.2', 'port' => 999999)
27
- }.should raise_error(Mysql2::Error)
27
+ }.to raise_error(Mysql2::Error)
28
28
  end
29
29
 
30
- if defined? Encoding
31
- it "should raise an exception on create for invalid encodings" do
32
- lambda {
33
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "fake"))
34
- }.should raise_error(Mysql2::Error)
35
- end
30
+ it "should raise an exception on create for invalid encodings" do
31
+ expect {
32
+ Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "fake"))
33
+ }.to raise_error(Mysql2::Error)
34
+ end
36
35
 
37
- it "should not raise an exception on create for a valid encoding" do
38
- lambda {
39
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
40
- }.should_not raise_error(Mysql2::Error)
36
+ it "should not raise an exception on create for a valid encoding" do
37
+ expect {
38
+ Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
39
+ }.not_to raise_error
41
40
 
42
- lambda {
43
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "big5"))
44
- }.should_not raise_error(Mysql2::Error)
41
+ expect {
42
+ Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "big5"))
43
+ }.not_to raise_error
44
+ end
45
+
46
+ Klient = Class.new(Mysql2::Client) do
47
+ attr_reader :connect_args
48
+ def connect(*args)
49
+ @connect_args ||= []
50
+ @connect_args << args
45
51
  end
46
52
  end
47
53
 
48
54
  it "should accept connect flags and pass them to #connect" do
49
- klient = Class.new(Mysql2::Client) do
50
- attr_reader :connect_args
51
- def connect *args
52
- @connect_args ||= []
53
- @connect_args << args
54
- end
55
- end
56
- client = klient.new :flags => Mysql2::Client::FOUND_ROWS
57
- (client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).should be_true
55
+ client = Klient.new :flags => Mysql2::Client::FOUND_ROWS
56
+ expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to be > 0
57
+ end
58
+
59
+ it "should parse flags array" do
60
+ client = Klient.new :flags => %w( FOUND_ROWS -PROTOCOL_41 )
61
+ expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
62
+ expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
63
+ end
64
+
65
+ it "should parse flags string" do
66
+ client = Klient.new :flags => "FOUND_ROWS -PROTOCOL_41"
67
+ expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
68
+ expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
58
69
  end
59
70
 
60
71
  it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
61
- klient = Class.new(Mysql2::Client) do
62
- attr_reader :connect_args
63
- def connect *args
64
- @connect_args ||= []
65
- @connect_args << args
66
- end
67
- end
68
- client = klient.new
69
- (client.connect_args.last[6] & (Mysql2::Client::REMEMBER_OPTIONS |
70
- Mysql2::Client::LONG_PASSWORD |
71
- Mysql2::Client::LONG_FLAG |
72
- Mysql2::Client::TRANSACTIONS |
73
- Mysql2::Client::PROTOCOL_41 |
74
- Mysql2::Client::SECURE_CONNECTION)).should be_true
72
+ client = Klient.new
73
+ client_flags = Mysql2::Client::REMEMBER_OPTIONS |
74
+ Mysql2::Client::LONG_PASSWORD |
75
+ Mysql2::Client::LONG_FLAG |
76
+ Mysql2::Client::TRANSACTIONS |
77
+ Mysql2::Client::PROTOCOL_41 |
78
+ Mysql2::Client::SECURE_CONNECTION
79
+ expect(client.connect_args.last[6]).to eql(client_flags)
75
80
  end
76
81
 
77
82
  it "should execute init command" do
@@ -79,7 +84,7 @@ describe Mysql2::Client do
79
84
  options[:init_command] = "SET @something = 'setting_value';"
80
85
  client = Mysql2::Client.new(options)
81
86
  result = client.query("SELECT @something;")
82
- result.first['@something'].should eq('setting_value')
87
+ expect(result.first['@something']).to eq('setting_value')
83
88
  end
84
89
 
85
90
  it "should send init_command after reconnect" do
@@ -89,17 +94,14 @@ describe Mysql2::Client do
89
94
  client = Mysql2::Client.new(options)
90
95
 
91
96
  result = client.query("SELECT @something;")
92
- result.first['@something'].should eq('setting_value')
97
+ expect(result.first['@something']).to eq('setting_value')
93
98
 
94
99
  # get the current connection id
95
100
  result = client.query("SELECT CONNECTION_ID()")
96
101
  first_conn_id = result.first['CONNECTION_ID()']
97
102
 
98
103
  # break the current connection
99
- begin
100
- client.query("KILL #{first_conn_id}")
101
- rescue Mysql2::Error
102
- end
104
+ expect { client.query("KILL #{first_conn_id}") }.to raise_error(Mysql2::Error)
103
105
 
104
106
  client.ping # reconnect now
105
107
 
@@ -108,53 +110,75 @@ describe Mysql2::Client do
108
110
  second_conn_id = result.first['CONNECTION_ID()']
109
111
 
110
112
  # confirm reconnect by checking the new connection id
111
- first_conn_id.should_not == second_conn_id
113
+ expect(first_conn_id).not_to eq(second_conn_id)
112
114
 
113
115
  # At last, check that the init command executed
114
116
  result = client.query("SELECT @something;")
115
- result.first['@something'].should eq('setting_value')
117
+ expect(result.first['@something']).to eq('setting_value')
116
118
  end
117
119
 
118
120
  it "should have a global default_query_options hash" do
119
- Mysql2::Client.should respond_to(:default_query_options)
121
+ expect(Mysql2::Client).to respond_to(:default_query_options)
120
122
  end
121
123
 
122
124
  it "should be able to connect via SSL options" do
123
125
  ssl = @client.query "SHOW VARIABLES LIKE 'have_ssl'"
124
- ssl_uncompiled = ssl.any? {|x| x['Value'] == 'OFF'}
126
+ ssl_uncompiled = ssl.any? { |x| x['Value'] == 'OFF' }
125
127
  pending("DON'T WORRY, THIS TEST PASSES - but SSL is not compiled into your MySQL daemon.") if ssl_uncompiled
126
- ssl_disabled = ssl.any? {|x| x['Value'] == 'DISABLED'}
128
+ ssl_disabled = ssl.any? { |x| x['Value'] == 'DISABLED' }
127
129
  pending("DON'T WORRY, THIS TEST PASSES - but SSL is not enabled in your MySQL daemon.") if ssl_disabled
128
130
 
129
131
  # You may need to adjust the lines below to match your SSL certificate paths
130
132
  ssl_client = nil
131
- lambda {
133
+ expect {
134
+ # rubocop:disable Style/TrailingComma
132
135
  ssl_client = Mysql2::Client.new(
133
- :sslkey => '/etc/mysql/client-key.pem',
134
- :sslcert => '/etc/mysql/client-cert.pem',
135
- :sslca => '/etc/mysql/ca-cert.pem',
136
- :sslcapath => '/etc/mysql/',
137
- :sslcipher => 'DHE-RSA-AES256-SHA'
136
+ DatabaseCredentials['root'].merge(
137
+ 'host' => 'mysql2gem.example.com', # must match the certificates
138
+ :sslkey => '/etc/mysql/client-key.pem',
139
+ :sslcert => '/etc/mysql/client-cert.pem',
140
+ :sslca => '/etc/mysql/ca-cert.pem',
141
+ :sslcipher => 'DHE-RSA-AES256-SHA',
142
+ :sslverify => true
143
+ )
138
144
  )
139
- }.should_not raise_error(Mysql2::Error)
145
+ # rubocop:enable Style/TrailingComma
146
+ }.not_to raise_error
140
147
 
141
148
  results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a
142
- results[0]['Variable_name'].should eql('Ssl_cipher')
143
- results[0]['Value'].should_not be_nil
144
- results[0]['Value'].should be_kind_of(String)
145
- results[0]['Value'].should_not be_empty
149
+ expect(results[0]['Variable_name']).to eql('Ssl_cipher')
150
+ expect(results[0]['Value']).not_to be_nil
151
+ expect(results[0]['Value']).to be_an_instance_of(String)
152
+ expect(results[0]['Value']).not_to be_empty
146
153
 
147
- results[1]['Variable_name'].should eql('Ssl_version')
148
- results[1]['Value'].should_not be_nil
149
- results[1]['Value'].should be_kind_of(String)
150
- results[1]['Value'].should_not be_empty
154
+ expect(results[1]['Variable_name']).to eql('Ssl_version')
155
+ expect(results[1]['Value']).not_to be_nil
156
+ expect(results[1]['Value']).to be_an_instance_of(String)
157
+ expect(results[1]['Value']).not_to be_empty
151
158
 
152
159
  ssl_client.close
153
160
  end
154
161
 
162
+ def run_gc
163
+ if defined?(Rubinius)
164
+ GC.run(true)
165
+ else
166
+ GC.start
167
+ end
168
+ sleep(0.5)
169
+ end
170
+
171
+ it "should terminate connections when calling close" do
172
+ expect {
173
+ Mysql2::Client.new(DatabaseCredentials['root']).close
174
+ }.to_not change {
175
+ @client.query("SHOW STATUS LIKE 'Aborted_clients'").first['Value'].to_i
176
+ }
177
+ end
178
+
155
179
  it "should not leave dangling connections after garbage collection" do
156
- GC.start
157
- sleep 0.300 # Let GC do its work
180
+ run_gc
181
+
158
182
  client = Mysql2::Client.new(DatabaseCredentials['root'])
159
183
  before_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
160
184
 
@@ -162,69 +186,70 @@ describe Mysql2::Client do
162
186
  Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
163
187
  end
164
188
  after_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
165
- after_count.should == before_count + 10
189
+ expect(after_count).to eq(before_count + 10)
166
190
 
167
- GC.start
168
- sleep 0.300 # Let GC do its work
191
+ run_gc
169
192
  final_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
170
- final_count.should == before_count
193
+ expect(final_count).to eq(before_count)
171
194
  end
172
195
 
173
- if Process.respond_to?(:fork)
174
- it "should not close connections when running in a child process" do
175
- GC.start
176
- sleep 1 if defined? Rubinius # Let the rbx GC thread do its work
177
- client = Mysql2::Client.new(DatabaseCredentials['root'])
178
-
179
- fork do
180
- client.query('SELECT 1')
181
- client = nil
182
- GC.start
183
- sleep 1 if defined? Rubinius # Let the rbx GC thread do its work
184
- end
196
+ it "should not close connections when running in a child process" do
197
+ pending("fork is not available on this platform") unless Process.respond_to?(:fork)
185
198
 
186
- Process.wait
199
+ run_gc
200
+ client = Mysql2::Client.new(DatabaseCredentials['root'])
201
+
202
+ # this empty `fork` call fixes this tests on RBX; without it, the next
203
+ # `fork` call hangs forever. WTF?
204
+ fork {}
187
205
 
188
- # this will throw an error if the underlying socket was shutdown by the
189
- # child's GC
190
- expect { client.query('SELECT 1') }.to_not raise_exception
206
+ fork do
207
+ client.query('SELECT 1')
208
+ client = nil
209
+ run_gc
191
210
  end
211
+
212
+ Process.wait
213
+
214
+ # this will throw an error if the underlying socket was shutdown by the
215
+ # child's GC
216
+ expect { client.query('SELECT 1') }.to_not raise_exception
192
217
  end
193
218
 
194
219
  it "should be able to connect to database with numeric-only name" do
195
- lambda {
196
- creds = DatabaseCredentials['numericuser']
197
- @client.query "CREATE DATABASE IF NOT EXISTS `#{creds['database']}`"
198
- @client.query "GRANT ALL ON `#{creds['database']}`.* TO #{creds['username']}@`#{creds['host']}`"
199
- client = Mysql2::Client.new creds
200
- @client.query "DROP DATABASE IF EXISTS `#{creds['database']}`"
201
- }.should_not raise_error
220
+ creds = DatabaseCredentials['numericuser']
221
+ @client.query "CREATE DATABASE IF NOT EXISTS `#{creds['database']}`"
222
+ @client.query "GRANT ALL ON `#{creds['database']}`.* TO #{creds['username']}@`#{creds['host']}`"
223
+
224
+ expect { Mysql2::Client.new(creds) }.not_to raise_error
225
+
226
+ @client.query "DROP DATABASE IF EXISTS `#{creds['database']}`"
202
227
  end
203
228
 
204
229
  it "should respond to #close" do
205
- @client.should respond_to(:close)
230
+ expect(@client).to respond_to(:close)
206
231
  end
207
232
 
208
233
  it "should be able to close properly" do
209
- @client.close.should be_nil
210
- lambda {
234
+ expect(@client.close).to be_nil
235
+ expect {
211
236
  @client.query "SELECT 1"
212
- }.should raise_error(Mysql2::Error)
237
+ }.to raise_error(Mysql2::Error)
213
238
  end
214
239
 
215
240
  it "should respond to #query" do
216
- @client.should respond_to(:query)
241
+ expect(@client).to respond_to(:query)
217
242
  end
218
243
 
219
244
  it "should respond to #warning_count" do
220
- @client.should respond_to(:warning_count)
245
+ expect(@client).to respond_to(:warning_count)
221
246
  end
222
247
 
223
248
  context "#warning_count" do
224
249
  context "when no warnings" do
225
250
  it "should 0" do
226
251
  @client.query('select 1')
227
- @client.warning_count.should == 0
252
+ expect(@client.warning_count).to eq(0)
228
253
  end
229
254
  end
230
255
  context "when has a warnings" do
@@ -232,21 +257,21 @@ describe Mysql2::Client do
232
257
  # "the statement produces extra information that can be viewed by issuing a SHOW WARNINGS"
233
258
  # http://dev.mysql.com/doc/refman/5.0/en/explain-extended.html
234
259
  @client.query("explain extended select 1")
235
- @client.warning_count.should > 0
260
+ expect(@client.warning_count).to be > 0
236
261
  end
237
262
  end
238
263
  end
239
264
 
240
265
  it "should respond to #query_info" do
241
- @client.should respond_to(:query_info)
266
+ expect(@client).to respond_to(:query_info)
242
267
  end
243
268
 
244
269
  context "#query_info" do
245
270
  context "when no info present" do
246
271
  it "should 0" do
247
272
  @client.query('select 1')
248
- @client.query_info.should be_empty
249
- @client.query_info_string.should be_nil
273
+ expect(@client.query_info).to be_empty
274
+ expect(@client.query_info_string).to be_nil
250
275
  end
251
276
  end
252
277
  context "when has some info" do
@@ -258,8 +283,8 @@ describe Mysql2::Client do
258
283
  # # Note that mysql_info() returns a non-NULL value for INSERT ... VALUES only for the multiple-row form of the statement (that is, only if multiple value lists are specified).
259
284
  @client.query("INSERT INTO infoTest (blah) VALUES (1234),(4535)")
260
285
 
261
- @client.query_info.should eql({:records => 2, :duplicates => 0, :warnings => 0})
262
- @client.query_info_string.should eq('Records: 2 Duplicates: 0 Warnings: 0')
286
+ expect(@client.query_info).to eql(:records => 2, :duplicates => 0, :warnings => 0)
287
+ expect(@client.query_info_string).to eq('Records: 2 Duplicates: 0 Warnings: 0')
263
288
 
264
289
  @client.query "DROP TABLE infoTest"
265
290
  end
@@ -270,7 +295,7 @@ describe Mysql2::Client do
270
295
  before(:all) do
271
296
  @client_i = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => true)
272
297
  local = @client_i.query "SHOW VARIABLES LIKE 'local_infile'"
273
- local_enabled = local.any? {|x| x['Value'] == 'ON'}
298
+ local_enabled = local.any? { |x| x['Value'] == 'ON' }
274
299
  pending("DON'T WORRY, THIS TEST PASSES - but LOCAL INFILE is not enabled in your MySQL daemon.") unless local_enabled
275
300
 
276
301
  @client_i.query %[
@@ -288,43 +313,43 @@ describe Mysql2::Client do
288
313
 
289
314
  it "should raise an error when local_infile is disabled" do
290
315
  client = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => false)
291
- lambda {
316
+ expect {
292
317
  client.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
293
- }.should raise_error(Mysql2::Error, %r{command is not allowed})
318
+ }.to raise_error(Mysql2::Error, /command is not allowed/)
294
319
  end
295
320
 
296
321
  it "should raise an error when a non-existent file is loaded" do
297
- lambda {
322
+ expect {
298
323
  @client_i.query "LOAD DATA LOCAL INFILE 'this/file/is/not/here' INTO TABLE infileTest"
299
- }.should_not raise_error(Mysql2::Error, %r{file not found: this/file/is/not/here})
324
+ }.to raise_error(Mysql2::Error, 'No such file or directory: this/file/is/not/here')
300
325
  end
301
326
 
302
327
  it "should LOAD DATA LOCAL INFILE" do
303
328
  @client_i.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
304
329
  info = @client_i.query_info
305
- info.should eql({:records => 1, :deleted => 0, :skipped => 0, :warnings => 0})
330
+ expect(info).to eql(:records => 1, :deleted => 0, :skipped => 0, :warnings => 0)
306
331
 
307
332
  result = @client_i.query "SELECT * FROM infileTest"
308
- result.first.should eql({'id' => 1, 'foo' => 'Hello', 'bar' => 'World'})
333
+ expect(result.first).to eql('id' => 1, 'foo' => 'Hello', 'bar' => 'World')
309
334
  end
310
335
  end
311
336
 
312
337
  it "should expect connect_timeout to be a positive integer" do
313
- lambda {
338
+ expect {
314
339
  Mysql2::Client.new(:connect_timeout => -1)
315
- }.should raise_error(Mysql2::Error)
340
+ }.to raise_error(Mysql2::Error)
316
341
  end
317
342
 
318
343
  it "should expect read_timeout to be a positive integer" do
319
- lambda {
344
+ expect {
320
345
  Mysql2::Client.new(:read_timeout => -1)
321
- }.should raise_error(Mysql2::Error)
346
+ }.to raise_error(Mysql2::Error)
322
347
  end
323
348
 
324
349
  it "should expect write_timeout to be a positive integer" do
325
- lambda {
350
+ expect {
326
351
  Mysql2::Client.new(:write_timeout => -1)
327
- }.should raise_error(Mysql2::Error)
352
+ }.to raise_error(Mysql2::Error)
328
353
  end
329
354
 
330
355
  context "#query" do
@@ -333,7 +358,7 @@ describe Mysql2::Client do
333
358
 
334
359
  expect {
335
360
  @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false)
336
- }.to_not raise_exception(Mysql2::Error)
361
+ }.to_not raise_error
337
362
  end
338
363
 
339
364
  it "should not let you query again if iterating is not finished when streaming" do
@@ -345,200 +370,196 @@ describe Mysql2::Client do
345
370
  end
346
371
 
347
372
  it "should only accept strings as the query parameter" do
348
- lambda {
373
+ expect {
349
374
  @client.query ["SELECT 'not right'"]
350
- }.should raise_error(TypeError)
375
+ }.to raise_error(TypeError)
351
376
  end
352
377
 
353
378
  it "should not retain query options set on a query for subsequent queries, but should retain it in the result" do
354
379
  result = @client.query "SELECT 1", :something => :else
355
- @client.query_options[:something].should be_nil
356
- result.instance_variable_get('@query_options').should eql(@client.query_options.merge(:something => :else))
357
- @client.instance_variable_get('@current_query_options').should eql(@client.query_options.merge(:something => :else))
380
+ expect(@client.query_options[:something]).to be_nil
381
+ expect(result.instance_variable_get('@query_options')).to eql(@client.query_options.merge(:something => :else))
382
+ expect(@client.instance_variable_get('@current_query_options')).to eql(@client.query_options.merge(:something => :else))
358
383
 
359
384
  result = @client.query "SELECT 1"
360
- result.instance_variable_get('@query_options').should eql(@client.query_options)
361
- @client.instance_variable_get('@current_query_options').should eql(@client.query_options)
385
+ expect(result.instance_variable_get('@query_options')).to eql(@client.query_options)
386
+ expect(@client.instance_variable_get('@current_query_options')).to eql(@client.query_options)
362
387
  end
363
388
 
364
389
  it "should allow changing query options for subsequent queries" do
365
390
  @client.query_options.merge!(:something => :else)
366
391
  result = @client.query "SELECT 1"
367
- @client.query_options[:something].should eql(:else)
368
- result.instance_variable_get('@query_options')[:something].should eql(:else)
392
+ expect(@client.query_options[:something]).to eql(:else)
393
+ expect(result.instance_variable_get('@query_options')[:something]).to eql(:else)
369
394
 
370
395
  # Clean up after this test
371
396
  @client.query_options.delete(:something)
372
- @client.query_options[:something].should be_nil
397
+ expect(@client.query_options[:something]).to be_nil
373
398
  end
374
399
 
375
400
  it "should return results as a hash by default" do
376
- @client.query("SELECT 1").first.class.should eql(Hash)
401
+ expect(@client.query("SELECT 1").first).to be_an_instance_of(Hash)
377
402
  end
378
403
 
379
404
  it "should be able to return results as an array" do
380
- @client.query("SELECT 1", :as => :array).first.class.should eql(Array)
405
+ expect(@client.query("SELECT 1", :as => :array).first).to be_an_instance_of(Array)
381
406
  @client.query("SELECT 1").each(:as => :array)
382
407
  end
383
408
 
384
409
  it "should be able to return results with symbolized keys" do
385
- @client.query("SELECT 1", :symbolize_keys => true).first.keys[0].class.should eql(Symbol)
410
+ expect(@client.query("SELECT 1", :symbolize_keys => true).first.keys[0]).to be_an_instance_of(Symbol)
386
411
  end
387
412
 
388
413
  it "should require an open connection" do
389
414
  @client.close
390
- lambda {
415
+ expect {
391
416
  @client.query "SELECT 1"
392
- }.should raise_error(Mysql2::Error)
417
+ }.to raise_error(Mysql2::Error)
393
418
  end
394
419
 
395
420
  if RUBY_PLATFORM !~ /mingw|mswin/
396
421
  it "should not allow another query to be sent without fetching a result first" do
397
422
  @client.query("SELECT 1", :async => true)
398
- lambda {
423
+ expect {
399
424
  @client.query("SELECT 1")
400
- }.should raise_error(Mysql2::Error)
425
+ }.to raise_error(Mysql2::Error)
401
426
  end
402
427
 
403
428
  it "should describe the thread holding the active query" do
404
429
  thr = Thread.new { @client.query("SELECT 1", :async => true) }
405
430
 
406
431
  thr.join
407
- begin
408
- @client.query("SELECT 1")
409
- rescue Mysql2::Error => e
410
- message = e.message
411
- end
412
- re = Regexp.escape(thr.inspect)
413
- message.should match(Regexp.new(re))
432
+ expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, Regexp.new(Regexp.escape(thr.inspect)))
414
433
  end
415
434
 
416
435
  it "should timeout if we wait longer than :read_timeout" do
417
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => 1))
418
- lambda {
419
- client.query("SELECT sleep(2)")
420
- }.should raise_error(Mysql2::Error)
436
+ client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => 0))
437
+ expect {
438
+ client.query('SELECT SLEEP(0.1)')
439
+ }.to raise_error(Mysql2::Error)
421
440
  end
422
441
 
423
- if !defined? Rubinius
424
- # XXX this test is not deterministic (because Unix signal handling is not)
425
- # and may fail on a loaded system
426
- it "should run signal handlers while waiting for a response" do
427
- mark = {}
428
- trap(:USR1) { mark[:USR1] = Time.now }
429
- begin
430
- mark[:START] = Time.now
431
- pid = fork do
432
- sleep 1 # wait for client "SELECT sleep(2)" query to start
433
- Process.kill(:USR1, Process.ppid)
434
- sleep # wait for explicit kill to prevent GC disconnect
435
- end
436
- @client.query("SELECT sleep(2)")
437
- mark[:END] = Time.now
438
- mark.include?(:USR1).should be_true
439
- (mark[:USR1] - mark[:START]).should >= 1
440
- (mark[:USR1] - mark[:START]).should < 1.3
441
- (mark[:END] - mark[:USR1]).should > 0.9
442
- (mark[:END] - mark[:START]).should >= 2
443
- (mark[:END] - mark[:START]).should < 2.3
444
- Process.kill(:TERM, pid)
445
- Process.waitpid2(pid)
446
- ensure
447
- trap(:USR1, 'DEFAULT')
442
+ # XXX this test is not deterministic (because Unix signal handling is not)
443
+ # and may fail on a loaded system
444
+ it "should run signal handlers while waiting for a response" do
445
+ kill_time = 0.1
446
+ query_time = 2 * kill_time
447
+
448
+ mark = {}
449
+
450
+ begin
451
+ trap(:USR1) { mark.store(:USR1, Time.now) }
452
+ pid = fork do
453
+ sleep kill_time # wait for client query to start
454
+ Process.kill(:USR1, Process.ppid)
455
+ sleep # wait for explicit kill to prevent GC disconnect
448
456
  end
457
+ mark.store(:QUERY_START, Time.now)
458
+ @client.query("SELECT SLEEP(#{query_time})")
459
+ mark.store(:QUERY_END, Time.now)
460
+ ensure
461
+ Process.kill(:TERM, pid)
462
+ Process.waitpid2(pid)
463
+ trap(:USR1, 'DEFAULT')
449
464
  end
465
+
466
+ # the query ran uninterrupted
467
+ expect(mark.fetch(:QUERY_END) - mark.fetch(:QUERY_START)).to be_within(0.02).of(query_time)
468
+ # signals fired while the query was running
469
+ expect(mark.fetch(:USR1)).to be_between(mark.fetch(:QUERY_START), mark.fetch(:QUERY_END))
450
470
  end
451
471
 
452
472
  it "#socket should return a Fixnum (file descriptor from C)" do
453
- @client.socket.class.should eql(Fixnum)
454
- @client.socket.should_not eql(0)
473
+ expect(@client.socket).to be_an_instance_of(Fixnum)
474
+ expect(@client.socket).not_to eql(0)
455
475
  end
456
476
 
457
477
  it "#socket should require an open connection" do
458
478
  @client.close
459
- lambda {
479
+ expect {
460
480
  @client.socket
461
- }.should raise_error(Mysql2::Error)
481
+ }.to raise_error(Mysql2::Error)
462
482
  end
463
483
 
464
- it "should close the connection when an exception is raised" do
465
- begin
466
- Timeout.timeout(1, Timeout::Error) do
467
- @client.query("SELECT sleep(2)")
468
- end
469
- rescue Timeout::Error
470
- end
484
+ it 'should be impervious to connection-corrupting timeouts in #query' do
485
+ pending('`Thread.handle_interrupt` is not defined') unless Thread.respond_to?(:handle_interrupt)
486
+ # attempt to break the connection
487
+ expect { Timeout.timeout(0.1) { @client.query('SELECT SLEEP(0.2)') } }.to raise_error(Timeout::Error)
471
488
 
472
- lambda {
473
- @client.query("SELECT 1")
474
- }.should raise_error(Mysql2::Error, 'closed MySQL connection')
489
+ # expect the connection to not be broken
490
+ expect { @client.query('SELECT 1') }.to_not raise_error
475
491
  end
476
492
 
477
- it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
478
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
479
- begin
480
- Timeout.timeout(1, Timeout::Error) do
481
- client.query("SELECT sleep(2)")
482
- end
483
- rescue Timeout::Error
484
- end
493
+ it 'should be impervious to connection-corrupting timeouts in #execute' do
494
+ # the statement handle gets corrupted and will segfault the tests if interrupted,
495
+ # so we can't even use pending on this test, really have to skip it on older Rubies.
496
+ skip('`Thread.handle_interrupt` is not defined') unless Thread.respond_to?(:handle_interrupt)
485
497
 
486
- lambda {
487
- client.query("SELECT 1")
488
- }.should_not raise_error(Mysql2::Error)
498
+ # attempt to break the connection
499
+ stmt = @client.prepare('SELECT SLEEP(?)')
500
+ expect { Timeout.timeout(0.1) { stmt.execute(0.2) } }.to raise_error(Timeout::Error)
501
+ stmt.close
502
+
503
+ # expect the connection to not be broken
504
+ expect { @client.query('SELECT 1') }.to_not raise_error
489
505
  end
490
506
 
491
- it "should handle Timeouts without leaving the connection hanging if reconnect is set to true after construction true" do
492
- client = Mysql2::Client.new(DatabaseCredentials['root'])
493
- begin
494
- Timeout.timeout(1, Timeout::Error) do
495
- client.query("SELECT sleep(2)")
496
- end
497
- rescue Timeout::Error
507
+ context 'when a non-standard exception class is raised' do
508
+ it "should close the connection when an exception is raised" do
509
+ expect { Timeout.timeout(0.1, ArgumentError) { @client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
510
+ expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, 'closed MySQL connection')
498
511
  end
499
512
 
500
- lambda {
501
- client.query("SELECT 1")
502
- }.should raise_error(Mysql2::Error)
513
+ it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
514
+ if RUBY_PLATFORM.include?('darwin') && Mysql2::Client.info.fetch(:version).start_with?('5.5')
515
+ pending('libmysqlclient 5.5 on OSX is afflicted by an unknown bug that breaks this test. See #633 and #634.')
516
+ end
503
517
 
504
- client.reconnect = true
518
+ client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
505
519
 
506
- begin
507
- Timeout.timeout(1, Timeout::Error) do
508
- client.query("SELECT sleep(2)")
509
- end
510
- rescue Timeout::Error
520
+ expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
521
+ expect { client.query('SELECT 1') }.to_not raise_error
511
522
  end
512
523
 
513
- lambda {
514
- client.query("SELECT 1")
515
- }.should_not raise_error(Mysql2::Error)
524
+ it "should handle Timeouts without leaving the connection hanging if reconnect is set to true after construction" do
525
+ if RUBY_PLATFORM.include?('darwin') && Mysql2::Client.info.fetch(:version).start_with?('5.5')
526
+ pending('libmysqlclient 5.5 on OSX is afflicted by an unknown bug that breaks this test. See #633 and #634.')
527
+ end
528
+
529
+ client = Mysql2::Client.new(DatabaseCredentials['root'])
530
+
531
+ expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
532
+ expect { client.query('SELECT 1') }.to raise_error(Mysql2::Error)
516
533
 
534
+ client.reconnect = true
535
+
536
+ expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
537
+ expect { client.query('SELECT 1') }.to_not raise_error
538
+ end
517
539
  end
518
540
 
519
541
  it "threaded queries should be supported" do
520
- threads, results = [], {}
521
- lock = Mutex.new
522
- connect = lambda{
523
- Mysql2::Client.new(DatabaseCredentials['root'])
524
- }
525
- Timeout.timeout(0.7) do
526
- 5.times {
527
- threads << Thread.new do
528
- result = connect.call.query("SELECT sleep(0.5) as result")
529
- lock.synchronize do
530
- results[Thread.current.object_id] = result
531
- end
532
- end
533
- }
542
+ sleep_time = 0.5
543
+
544
+ # Note that each thread opens its own database connection
545
+ threads = 5.times.map do
546
+ Thread.new do
547
+ client = Mysql2::Client.new(DatabaseCredentials.fetch('root'))
548
+ client.query("SELECT SLEEP(#{sleep_time})")
549
+ Thread.current.object_id
550
+ end
534
551
  end
535
- threads.each{|t| t.join }
536
- results.keys.sort.should eql(threads.map{|t| t.object_id }.sort)
552
+
553
+ # This timeout demonstrates that the threads are sleeping concurrently:
554
+ # In the serial case, the timeout would fire and the test would fail
555
+ values = Timeout.timeout(sleep_time * 1.1) { threads.map(&:value) }
556
+
557
+ expect(values).to match_array(threads.map(&:object_id))
537
558
  end
538
559
 
539
560
  it "evented async queries should be supported" do
540
561
  # should immediately return nil
541
- @client.query("SELECT sleep(0.1)", :async => true).should eql(nil)
562
+ expect(@client.query("SELECT sleep(0.1)", :async => true)).to eql(nil)
542
563
 
543
564
  io_wrapper = IO.for_fd(@client.socket)
544
565
  loops = 0
@@ -551,10 +572,10 @@ describe Mysql2::Client do
551
572
  end
552
573
 
553
574
  # make sure we waited some period of time
554
- (loops >= 1).should be_true
575
+ expect(loops >= 1).to be true
555
576
 
556
577
  result = @client.async_result
557
- result.class.should eql(Mysql2::Result)
578
+ expect(result).to be_an_instance_of(Mysql2::Result)
558
579
  end
559
580
  end
560
581
 
@@ -565,230 +586,251 @@ describe Mysql2::Client do
565
586
 
566
587
  it "should raise an exception when one of multiple statements fails" do
567
588
  result = @multi_client.query("SELECT 1 AS 'set_1'; SELECT * FROM invalid_table_name; SELECT 2 AS 'set_2';")
568
- result.first['set_1'].should be(1)
569
- lambda {
589
+ expect(result.first['set_1']).to be(1)
590
+ expect {
570
591
  @multi_client.next_result
571
- }.should raise_error(Mysql2::Error)
572
- @multi_client.next_result.should be_false
592
+ }.to raise_error(Mysql2::Error)
593
+ expect(@multi_client.next_result).to be false
573
594
  end
574
595
 
575
596
  it "returns multiple result sets" do
576
- @multi_client.query("SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'").first.should eql({ 'set_1' => 1 })
597
+ expect(@multi_client.query("SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'").first).to eql('set_1' => 1)
577
598
 
578
- @multi_client.next_result.should be_true
579
- @multi_client.store_result.first.should eql({ 'set_2' => 2 })
599
+ expect(@multi_client.next_result).to be true
600
+ expect(@multi_client.store_result.first).to eql('set_2' => 2)
580
601
 
581
- @multi_client.next_result.should be_false
602
+ expect(@multi_client.next_result).to be false
582
603
  end
583
604
 
584
605
  it "does not interfere with other statements" do
585
606
  @multi_client.query("SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'")
586
- while( @multi_client.next_result )
587
- @multi_client.store_result
588
- end
607
+ @multi_client.store_result while @multi_client.next_result
589
608
 
590
- @multi_client.query("SELECT 3 AS 'next'").first.should == { 'next' => 3 }
609
+ expect(@multi_client.query("SELECT 3 AS 'next'").first).to eq('next' => 3)
591
610
  end
592
611
 
593
612
  it "will raise on query if there are outstanding results to read" do
594
613
  @multi_client.query("SELECT 1; SELECT 2; SELECT 3")
595
- lambda {
614
+ expect {
596
615
  @multi_client.query("SELECT 4")
597
- }.should raise_error(Mysql2::Error)
616
+ }.to raise_error(Mysql2::Error)
598
617
  end
599
618
 
600
619
  it "#abandon_results! should work" do
601
620
  @multi_client.query("SELECT 1; SELECT 2; SELECT 3")
602
621
  @multi_client.abandon_results!
603
- lambda {
622
+ expect {
604
623
  @multi_client.query("SELECT 4")
605
- }.should_not raise_error(Mysql2::Error)
624
+ }.not_to raise_error
606
625
  end
607
626
 
608
627
  it "#more_results? should work" do
609
628
  @multi_client.query("SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'")
610
- @multi_client.more_results?.should be_true
629
+ expect(@multi_client.more_results?).to be true
611
630
 
612
631
  @multi_client.next_result
613
632
  @multi_client.store_result
614
633
 
615
- @multi_client.more_results?.should be_false
634
+ expect(@multi_client.more_results?).to be false
635
+ end
636
+
637
+ it "#more_results? should work with stored procedures" do
638
+ @multi_client.query("DROP PROCEDURE IF EXISTS test_proc")
639
+ @multi_client.query("CREATE PROCEDURE test_proc() BEGIN SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'; END")
640
+ expect(@multi_client.query("CALL test_proc()").first).to eql('set_1' => 1)
641
+ expect(@multi_client.more_results?).to be true
642
+
643
+ @multi_client.next_result
644
+ expect(@multi_client.store_result.first).to eql('set_2' => 2)
645
+
646
+ @multi_client.next_result
647
+ expect(@multi_client.store_result).to be_nil # this is the result from CALL itself
648
+
649
+ expect(@multi_client.more_results?).to be false
616
650
  end
617
651
  end
618
652
  end
619
653
 
620
654
  it "should respond to #socket" do
621
- @client.should respond_to(:socket)
655
+ expect(@client).to respond_to(:socket)
622
656
  end
623
657
 
624
658
  if RUBY_PLATFORM =~ /mingw|mswin/
625
659
  it "#socket should raise as it's not supported" do
626
- lambda {
660
+ expect {
627
661
  @client.socket
628
- }.should raise_error(Mysql2::Error)
662
+ }.to raise_error(Mysql2::Error)
629
663
  end
630
664
  end
631
665
 
632
666
  it "should respond to escape" do
633
- Mysql2::Client.should respond_to(:escape)
667
+ expect(Mysql2::Client).to respond_to(:escape)
634
668
  end
635
669
 
636
670
  context "escape" do
637
671
  it "should return a new SQL-escape version of the passed string" do
638
- Mysql2::Client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
672
+ expect(Mysql2::Client.escape("abc'def\"ghi\0jkl%mno")).to eql("abc\\'def\\\"ghi\\0jkl%mno")
639
673
  end
640
674
 
641
675
  it "should return the passed string if nothing was escaped" do
642
676
  str = "plain"
643
- Mysql2::Client.escape(str).object_id.should eql(str.object_id)
677
+ expect(Mysql2::Client.escape(str).object_id).to eql(str.object_id)
644
678
  end
645
679
 
646
680
  it "should not overflow the thread stack" do
647
- lambda {
681
+ expect {
648
682
  Thread.new { Mysql2::Client.escape("'" * 256 * 1024) }.join
649
- }.should_not raise_error(SystemStackError)
683
+ }.not_to raise_error
650
684
  end
651
685
 
652
686
  it "should not overflow the process stack" do
653
- lambda {
687
+ expect {
654
688
  Thread.new { Mysql2::Client.escape("'" * 1024 * 1024 * 4) }.join
655
- }.should_not raise_error(SystemStackError)
689
+ }.not_to raise_error
656
690
  end
657
691
 
658
692
  unless RUBY_VERSION =~ /1.8/
659
693
  it "should carry over the original string's encoding" do
660
694
  str = "abc'def\"ghi\0jkl%mno"
661
695
  escaped = Mysql2::Client.escape(str)
662
- escaped.encoding.should eql(str.encoding)
696
+ expect(escaped.encoding).to eql(str.encoding)
663
697
 
664
698
  str.encode!('us-ascii')
665
699
  escaped = Mysql2::Client.escape(str)
666
- escaped.encoding.should eql(str.encoding)
700
+ expect(escaped.encoding).to eql(str.encoding)
667
701
  end
668
702
  end
669
703
  end
670
704
 
671
705
  it "should respond to #escape" do
672
- @client.should respond_to(:escape)
706
+ expect(@client).to respond_to(:escape)
673
707
  end
674
708
 
675
709
  context "#escape" do
676
710
  it "should return a new SQL-escape version of the passed string" do
677
- @client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
711
+ expect(@client.escape("abc'def\"ghi\0jkl%mno")).to eql("abc\\'def\\\"ghi\\0jkl%mno")
678
712
  end
679
713
 
680
714
  it "should return the passed string if nothing was escaped" do
681
715
  str = "plain"
682
- @client.escape(str).object_id.should eql(str.object_id)
716
+ expect(@client.escape(str).object_id).to eql(str.object_id)
683
717
  end
684
718
 
685
719
  it "should not overflow the thread stack" do
686
- lambda {
720
+ expect {
687
721
  Thread.new { @client.escape("'" * 256 * 1024) }.join
688
- }.should_not raise_error(SystemStackError)
722
+ }.not_to raise_error
689
723
  end
690
724
 
691
725
  it "should not overflow the process stack" do
692
- lambda {
726
+ expect {
693
727
  Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
694
- }.should_not raise_error(SystemStackError)
728
+ }.not_to raise_error
695
729
  end
696
730
 
697
731
  it "should require an open connection" do
698
732
  @client.close
699
- lambda {
733
+ expect {
700
734
  @client.escape ""
701
- }.should raise_error(Mysql2::Error)
735
+ }.to raise_error(Mysql2::Error)
736
+ end
737
+
738
+ context 'when mysql encoding is not utf8' do
739
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
740
+
741
+ let(:client) { Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "ujis")) }
742
+
743
+ it 'should return a internal encoding string if Encoding.default_internal is set' do
744
+ with_internal_encoding Encoding::UTF_8 do
745
+ expect(client.escape("\u{30C6}\u{30B9}\u{30C8}")).to eq "\u{30C6}\u{30B9}\u{30C8}"
746
+ expect(client.escape("\u{30C6}'\u{30B9}\"\u{30C8}")).to eq "\u{30C6}\\'\u{30B9}\\\"\u{30C8}"
747
+ end
748
+ end
702
749
  end
703
750
  end
704
751
 
705
752
  it "should respond to #info" do
706
- @client.should respond_to(:info)
753
+ expect(@client).to respond_to(:info)
707
754
  end
708
755
 
709
756
  it "#info should return a hash containing the client version ID and String" do
710
757
  info = @client.info
711
- info.class.should eql(Hash)
712
- info.should have_key(:id)
713
- info[:id].class.should eql(Fixnum)
714
- info.should have_key(:version)
715
- info[:version].class.should eql(String)
758
+ expect(info).to be_an_instance_of(Hash)
759
+ expect(info).to have_key(:id)
760
+ expect(info[:id]).to be_an_instance_of(Fixnum)
761
+ expect(info).to have_key(:version)
762
+ expect(info[:version]).to be_an_instance_of(String)
716
763
  end
717
764
 
718
- if defined? Encoding
719
- context "strings returned by #info" do
720
- it "should default to the connection's encoding if Encoding.default_internal is nil" do
721
- with_internal_encoding nil do
722
- @client.info[:version].encoding.should eql(Encoding.find('utf-8'))
765
+ context "strings returned by #info" do
766
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
723
767
 
724
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
725
- client2.info[:version].encoding.should eql(Encoding.find('us-ascii'))
726
- end
727
- end
768
+ it "should be tagged as ascii" do
769
+ expect(@client.info[:version].encoding).to eql(Encoding::US_ASCII)
770
+ expect(@client.info[:header_version].encoding).to eql(Encoding::US_ASCII)
771
+ end
772
+ end
728
773
 
729
- it "should use Encoding.default_internal" do
730
- with_internal_encoding 'utf-8' do
731
- @client.info[:version].encoding.should eql(Encoding.default_internal)
732
- end
774
+ context "strings returned by .info" do
775
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
733
776
 
734
- with_internal_encoding 'us-ascii' do
735
- @client.info[:version].encoding.should eql(Encoding.default_internal)
736
- end
737
- end
777
+ it "should be tagged as ascii" do
778
+ expect(Mysql2::Client.info[:version].encoding).to eql(Encoding::US_ASCII)
779
+ expect(Mysql2::Client.info[:header_version].encoding).to eql(Encoding::US_ASCII)
738
780
  end
739
781
  end
740
782
 
741
783
  it "should respond to #server_info" do
742
- @client.should respond_to(:server_info)
784
+ expect(@client).to respond_to(:server_info)
743
785
  end
744
786
 
745
787
  it "#server_info should return a hash containing the client version ID and String" do
746
788
  server_info = @client.server_info
747
- server_info.class.should eql(Hash)
748
- server_info.should have_key(:id)
749
- server_info[:id].class.should eql(Fixnum)
750
- server_info.should have_key(:version)
751
- server_info[:version].class.should eql(String)
789
+ expect(server_info).to be_an_instance_of(Hash)
790
+ expect(server_info).to have_key(:id)
791
+ expect(server_info[:id]).to be_an_instance_of(Fixnum)
792
+ expect(server_info).to have_key(:version)
793
+ expect(server_info[:version]).to be_an_instance_of(String)
752
794
  end
753
795
 
754
796
  it "#server_info should require an open connection" do
755
797
  @client.close
756
- lambda {
798
+ expect {
757
799
  @client.server_info
758
- }.should raise_error(Mysql2::Error)
800
+ }.to raise_error(Mysql2::Error)
759
801
  end
760
802
 
761
- if defined? Encoding
762
- context "strings returned by #server_info" do
763
- it "should default to the connection's encoding if Encoding.default_internal is nil" do
764
- with_internal_encoding nil do
765
- @client.server_info[:version].encoding.should eql(Encoding.find('utf-8'))
803
+ context "strings returned by #server_info" do
804
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
766
805
 
767
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
768
- client2.server_info[:version].encoding.should eql(Encoding.find('us-ascii'))
769
- end
806
+ it "should default to the connection's encoding if Encoding.default_internal is nil" do
807
+ with_internal_encoding nil do
808
+ expect(@client.server_info[:version].encoding).to eql(Encoding::UTF_8)
809
+
810
+ client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
811
+ expect(client2.server_info[:version].encoding).to eql(Encoding::ASCII)
770
812
  end
813
+ end
771
814
 
772
- it "should use Encoding.default_internal" do
773
- with_internal_encoding 'utf-8' do
774
- @client.server_info[:version].encoding.should eql(Encoding.default_internal)
775
- end
815
+ it "should use Encoding.default_internal" do
816
+ with_internal_encoding Encoding::UTF_8 do
817
+ expect(@client.server_info[:version].encoding).to eql(Encoding.default_internal)
818
+ end
776
819
 
777
- with_internal_encoding 'us-ascii' do
778
- @client.server_info[:version].encoding.should eql(Encoding.default_internal)
779
- end
820
+ with_internal_encoding Encoding::ASCII do
821
+ expect(@client.server_info[:version].encoding).to eql(Encoding.default_internal)
780
822
  end
781
823
  end
782
824
  end
783
825
 
784
826
  it "should raise a Mysql2::Error exception upon connection failure" do
785
- lambda {
827
+ expect {
786
828
  Mysql2::Client.new :host => "localhost", :username => 'asdfasdf8d2h', :password => 'asdfasdfw42'
787
- }.should raise_error(Mysql2::Error)
829
+ }.to raise_error(Mysql2::Error)
788
830
 
789
- lambda {
831
+ expect {
790
832
  Mysql2::Client.new DatabaseCredentials['root']
791
- }.should_not raise_error(Mysql2::Error)
833
+ }.not_to raise_error
792
834
  end
793
835
 
794
836
  context 'write operations api' do
@@ -802,46 +844,46 @@ describe Mysql2::Client do
802
844
  end
803
845
 
804
846
  it "should respond to #last_id" do
805
- @client.should respond_to(:last_id)
847
+ expect(@client).to respond_to(:last_id)
806
848
  end
807
849
 
808
850
  it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
809
- @client.last_id.should eql(0)
851
+ expect(@client.last_id).to eql(0)
810
852
  @client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
811
- @client.last_id.should eql(1)
853
+ expect(@client.last_id).to eql(1)
812
854
  end
813
855
 
814
856
  it "should respond to #last_id" do
815
- @client.should respond_to(:last_id)
857
+ expect(@client).to respond_to(:last_id)
816
858
  end
817
859
 
818
860
  it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
819
861
  @client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
820
- @client.affected_rows.should eql(1)
862
+ expect(@client.affected_rows).to eql(1)
821
863
  @client.query "UPDATE lastIdTest SET blah=4321 WHERE id=1"
822
- @client.affected_rows.should eql(1)
864
+ expect(@client.affected_rows).to eql(1)
823
865
  end
824
866
 
825
867
  it "#last_id should handle BIGINT auto-increment ids above 32 bits" do
826
868
  # The id column type must be BIGINT. Surprise: INT(x) is limited to 32-bits for all values of x.
827
869
  # Insert a row with a given ID, this should raise the auto-increment state
828
870
  @client.query "INSERT INTO lastIdTest (id, blah) VALUES (5000000000, 5000)"
829
- @client.last_id.should eql(5000000000)
871
+ expect(@client.last_id).to eql(5000000000)
830
872
  @client.query "INSERT INTO lastIdTest (blah) VALUES (5001)"
831
- @client.last_id.should eql(5000000001)
873
+ expect(@client.last_id).to eql(5000000001)
832
874
  end
833
875
  end
834
876
 
835
877
  it "should respond to #thread_id" do
836
- @client.should respond_to(:thread_id)
878
+ expect(@client).to respond_to(:thread_id)
837
879
  end
838
880
 
839
881
  it "#thread_id should be a Fixnum" do
840
- @client.thread_id.class.should eql(Fixnum)
882
+ expect(@client.thread_id).to be_an_instance_of(Fixnum)
841
883
  end
842
884
 
843
885
  it "should respond to #ping" do
844
- @client.should respond_to(:ping)
886
+ expect(@client).to respond_to(:ping)
845
887
  end
846
888
 
847
889
  context "select_db" do
@@ -860,38 +902,38 @@ describe Mysql2::Client do
860
902
  end
861
903
 
862
904
  it "should respond to #select_db" do
863
- @client.should respond_to(:select_db)
905
+ expect(@client).to respond_to(:select_db)
864
906
  end
865
907
 
866
908
  it "should switch databases" do
867
909
  @client.select_db("test_selectdb_0")
868
- @client.query("SHOW TABLES").first.values.first.should eql("test0")
910
+ expect(@client.query("SHOW TABLES").first.values.first).to eql("test0")
869
911
  @client.select_db("test_selectdb_1")
870
- @client.query("SHOW TABLES").first.values.first.should eql("test1")
912
+ expect(@client.query("SHOW TABLES").first.values.first).to eql("test1")
871
913
  @client.select_db("test_selectdb_0")
872
- @client.query("SHOW TABLES").first.values.first.should eql("test0")
914
+ expect(@client.query("SHOW TABLES").first.values.first).to eql("test0")
873
915
  end
874
916
 
875
917
  it "should raise a Mysql2::Error when the database doesn't exist" do
876
- lambda {
918
+ expect {
877
919
  @client.select_db("nopenothere")
878
- }.should raise_error(Mysql2::Error)
920
+ }.to raise_error(Mysql2::Error)
879
921
  end
880
922
 
881
923
  it "should return the database switched to" do
882
- @client.select_db("test_selectdb_1").should eq("test_selectdb_1")
924
+ expect(@client.select_db("test_selectdb_1")).to eq("test_selectdb_1")
883
925
  end
884
926
  end
885
927
 
886
928
  it "#thread_id should return a boolean" do
887
- @client.ping.should eql(true)
929
+ expect(@client.ping).to eql(true)
888
930
  @client.close
889
- @client.ping.should eql(false)
931
+ expect(@client.ping).to eql(false)
890
932
  end
891
933
 
892
934
  unless RUBY_VERSION =~ /1.8/
893
935
  it "should respond to #encoding" do
894
- @client.should respond_to(:encoding)
936
+ expect(@client).to respond_to(:encoding)
895
937
  end
896
938
  end
897
939
  end