mysql2 0.3.18 → 0.4.4

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