mysql2 0.3.21 → 0.4.10

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