mysql2 0.4.10 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require 'eventmachine'
4
2
  require 'mysql2'
5
3
 
@@ -17,7 +15,7 @@ module Mysql2
17
15
  detach
18
16
  begin
19
17
  result = @client.async_result
20
- rescue => e
18
+ rescue StandardError => e
21
19
  @deferable.fail(e)
22
20
  else
23
21
  @deferable.succeed(result)
@@ -41,7 +39,7 @@ module Mysql2
41
39
 
42
40
  def query(sql, opts = {})
43
41
  if ::EM.reactor_running?
44
- super(sql, opts.merge(:async => true))
42
+ super(sql, opts.merge(async: true))
45
43
  deferable = ::EM::DefaultDeferrable.new
46
44
  @watch = ::EM.watch(socket, Watcher, self, deferable)
47
45
  @watch.notify_readable = true
@@ -1,32 +1,65 @@
1
- # encoding: UTF-8
2
-
3
1
  module Mysql2
4
2
  class Error < StandardError
5
3
  ENCODE_OPTS = {
6
- :undef => :replace,
7
- :invalid => :replace,
8
- :replace => '?'.freeze,
4
+ undef: :replace,
5
+ invalid: :replace,
6
+ replace: '?'.freeze,
7
+ }.freeze
8
+
9
+ ConnectionError = Class.new(Error)
10
+ TimeoutError = Class.new(Error)
11
+
12
+ CODES = {
13
+ 1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
14
+
15
+ 1044 => ConnectionError, # ER_DBACCESS_DENIED_ERROR
16
+ 1045 => ConnectionError, # ER_ACCESS_DENIED_ERROR
17
+ 1152 => ConnectionError, # ER_ABORTING_CONNECTION
18
+ 1153 => ConnectionError, # ER_NET_PACKET_TOO_LARGE
19
+ 1154 => ConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
20
+ 1155 => ConnectionError, # ER_NET_FCNTL_ERROR
21
+ 1156 => ConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
22
+ 1157 => ConnectionError, # ER_NET_UNCOMPRESS_ERROR
23
+ 1158 => ConnectionError, # ER_NET_READ_ERROR
24
+ 1159 => ConnectionError, # ER_NET_READ_INTERRUPTED
25
+ 1160 => ConnectionError, # ER_NET_ERROR_ON_WRITE
26
+ 1161 => ConnectionError, # ER_NET_WRITE_INTERRUPTED
27
+
28
+ 2001 => ConnectionError, # CR_SOCKET_CREATE_ERROR
29
+ 2002 => ConnectionError, # CR_CONNECTION_ERROR
30
+ 2003 => ConnectionError, # CR_CONN_HOST_ERROR
31
+ 2004 => ConnectionError, # CR_IPSOCK_ERROR
32
+ 2005 => ConnectionError, # CR_UNKNOWN_HOST
33
+ 2006 => ConnectionError, # CR_SERVER_GONE_ERROR
34
+ 2007 => ConnectionError, # CR_VERSION_ERROR
35
+ 2009 => ConnectionError, # CR_WRONG_HOST_INFO
36
+ 2012 => ConnectionError, # CR_SERVER_HANDSHAKE_ERR
37
+ 2013 => ConnectionError, # CR_SERVER_LOST
38
+ 2020 => ConnectionError, # CR_NET_PACKET_TOO_LARGE
39
+ 2026 => ConnectionError, # CR_SSL_CONNECTION_ERROR
40
+ 2027 => ConnectionError, # CR_MALFORMED_PACKET
41
+ 2047 => ConnectionError, # CR_CONN_UNKNOW_PROTOCOL
42
+ 2048 => ConnectionError, # CR_INVALID_CONN_HANDLE
43
+ 2049 => ConnectionError, # CR_UNUSED_1
9
44
  }.freeze
10
45
 
11
46
  attr_reader :error_number, :sql_state
12
47
 
13
48
  # Mysql gem compatibility
14
- alias_method :errno, :error_number
15
- alias_method :error, :message
49
+ alias errno error_number
50
+ alias error message
16
51
 
17
- def initialize(msg)
18
- @server_version ||= nil
52
+ def initialize(msg, server_version = nil, error_number = nil, sql_state = nil)
53
+ @server_version = server_version
54
+ @error_number = error_number
55
+ @sql_state = sql_state ? sql_state.encode(ENCODE_OPTS) : nil
19
56
 
20
57
  super(clean_message(msg))
21
58
  end
22
59
 
23
60
  def self.new_with_args(msg, server_version, error_number, sql_state)
24
- err = allocate
25
- err.instance_variable_set('@server_version', server_version)
26
- err.instance_variable_set('@error_number', error_number)
27
- err.instance_variable_set('@sql_state', sql_state.respond_to?(:encode) ? sql_state.encode(ENCODE_OPTS) : sql_state)
28
- err.send(:initialize, msg)
29
- err
61
+ error_class = CODES.fetch(error_number, self)
62
+ error_class.new(msg, server_version, error_number, sql_state)
30
63
  end
31
64
 
32
65
  private
@@ -55,12 +88,8 @@ module Mysql2
55
88
  # encoding, we'll assume UTF-8 and clean the string of anything that's not a
56
89
  # valid UTF-8 character.
57
90
  #
58
- # Except for if we're on 1.8, where we'll do nothing ;)
59
- #
60
- # Returns a valid UTF-8 string in Ruby 1.9+, the original string on Ruby 1.8
91
+ # Returns a valid UTF-8 string.
61
92
  def clean_message(message)
62
- return message unless message.respond_to?(:encode)
63
-
64
93
  if @server_version && @server_version > 50500
65
94
  message.encode(ENCODE_OPTS)
66
95
  else
@@ -1,5 +1,7 @@
1
1
  module Mysql2
2
2
  class Result
3
+ attr_reader :server_flags
4
+
3
5
  include Enumerable
4
6
  end
5
7
  end
@@ -2,15 +2,9 @@ module Mysql2
2
2
  class Statement
3
3
  include Enumerable
4
4
 
5
- if Thread.respond_to?(:handle_interrupt)
6
- def execute(*args)
7
- Thread.handle_interrupt(::Mysql2::Util::TimeoutError => :never) do
8
- _execute(*args)
9
- end
10
- end
11
- else
12
- def execute(*args)
13
- _execute(*args)
5
+ def execute(*args, **kwargs)
6
+ Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do
7
+ _execute(*args, **kwargs)
14
8
  end
15
9
  end
16
10
  end
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.4.10"
2
+ VERSION = "0.5.0".freeze
3
3
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
  begin
4
3
  require 'eventmachine'
@@ -49,13 +48,13 @@ begin
49
48
  end
50
49
 
51
50
  it "should not swallow exceptions raised in callbacks" do
52
- expect {
51
+ expect do
53
52
  EM.run do
54
53
  client = Mysql2::EM::Client.new DatabaseCredentials['root']
55
54
  defer = client.query "SELECT sleep(0.1) as first_query"
56
55
  defer.callback do
57
56
  client.close
58
- fail 'some error'
57
+ raise 'some error'
59
58
  end
60
59
  defer.errback do
61
60
  # This _shouldn't_ be run, but it needed to prevent the specs from
@@ -63,7 +62,7 @@ begin
63
62
  EM.stop_event_loop
64
63
  end
65
64
  end
66
- }.to raise_error('some error')
65
+ end.to raise_error('some error')
67
66
  end
68
67
 
69
68
  context 'when an exception is raised by the client' do
@@ -123,9 +122,9 @@ begin
123
122
  end
124
123
  EM.add_timer(0.1) do
125
124
  expect(callbacks_run).to eq([:callback])
126
- expect {
125
+ expect do
127
126
  client.close
128
- }.not_to raise_error
127
+ end.not_to raise_error
129
128
  EM.stop_event_loop
130
129
  end
131
130
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  RSpec.describe Mysql2::Client do
@@ -6,46 +5,46 @@ RSpec.describe Mysql2::Client do
6
5
  let(:cnf_file) { File.expand_path('../../my.cnf', __FILE__) }
7
6
 
8
7
  it "should not raise an exception for valid defaults group" do
9
- expect {
10
- new_client(:default_file => cnf_file, :default_group => "test")
11
- }.not_to raise_error
8
+ expect do
9
+ new_client(default_file: cnf_file, default_group: "test")
10
+ end.not_to raise_error
12
11
  end
13
12
 
14
13
  it "should not raise an exception without default group" do
15
- expect {
16
- new_client(:default_file => cnf_file)
17
- }.not_to raise_error
14
+ expect do
15
+ new_client(default_file: cnf_file)
16
+ end.not_to raise_error
18
17
  end
19
18
  end
20
19
 
21
- it "should raise an exception upon connection failure" do
22
- expect {
20
+ it "should raise a Mysql::Error::ConnectionError upon connection failure" do
21
+ expect do
23
22
  # The odd local host IP address forces the mysql client library to
24
23
  # use a TCP socket rather than a domain socket.
25
24
  new_client('host' => '127.0.0.2', 'port' => 999999)
26
- }.to raise_error(Mysql2::Error)
25
+ end.to raise_error(Mysql2::Error::ConnectionError)
27
26
  end
28
27
 
29
28
  it "should raise an exception on create for invalid encodings" do
30
- expect {
31
- new_client(:encoding => "fake")
32
- }.to raise_error(Mysql2::Error)
29
+ expect do
30
+ new_client(encoding: "fake")
31
+ end.to raise_error(Mysql2::Error)
33
32
  end
34
33
 
35
34
  it "should raise an exception on non-string encodings" do
36
- expect {
37
- new_client(:encoding => :fake)
38
- }.to raise_error(TypeError)
35
+ expect do
36
+ new_client(encoding: :fake)
37
+ end.to raise_error(TypeError)
39
38
  end
40
39
 
41
40
  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
+ expect do
42
+ new_client(encoding: "utf8")
43
+ end.not_to raise_error
45
44
 
46
- expect {
47
- new_client(DatabaseCredentials['root'].merge(:encoding => "big5"))
48
- }.not_to raise_error
45
+ expect do
46
+ new_client(DatabaseCredentials['root'].merge(encoding: "big5"))
47
+ end.not_to raise_error
49
48
  end
50
49
 
51
50
  Klient = Class.new(Mysql2::Client) do
@@ -57,18 +56,18 @@ RSpec.describe Mysql2::Client do
57
56
  end
58
57
 
59
58
  it "should accept connect flags and pass them to #connect" do
60
- client = Klient.new :flags => Mysql2::Client::FOUND_ROWS
59
+ client = Klient.new flags: Mysql2::Client::FOUND_ROWS
61
60
  expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to be > 0
62
61
  end
63
62
 
64
63
  it "should parse flags array" do
65
- client = Klient.new :flags => %w( FOUND_ROWS -PROTOCOL_41 )
64
+ client = Klient.new flags: %w[FOUND_ROWS -PROTOCOL_41]
66
65
  expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
67
66
  expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
68
67
  end
69
68
 
70
69
  it "should parse flags string" do
71
- client = Klient.new :flags => "FOUND_ROWS -PROTOCOL_41"
70
+ client = Klient.new flags: "FOUND_ROWS -PROTOCOL_41"
72
71
  expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
73
72
  expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
74
73
  end
@@ -80,7 +79,8 @@ RSpec.describe Mysql2::Client do
80
79
  Mysql2::Client::LONG_FLAG |
81
80
  Mysql2::Client::TRANSACTIONS |
82
81
  Mysql2::Client::PROTOCOL_41 |
83
- Mysql2::Client::SECURE_CONNECTION
82
+ Mysql2::Client::SECURE_CONNECTION |
83
+ Mysql2::Client::CONNECT_ATTRS
84
84
  expect(client.connect_args.last[6]).to eql(client_flags)
85
85
  end
86
86
 
@@ -135,18 +135,16 @@ RSpec.describe Mysql2::Client do
135
135
 
136
136
  # You may need to adjust the lines below to match your SSL certificate paths
137
137
  ssl_client = nil
138
- expect {
139
- # rubocop:disable Style/TrailingComma
138
+ expect do
140
139
  ssl_client = new_client(
141
140
  'host' => 'mysql2gem.example.com', # must match the certificates
142
141
  :sslkey => '/etc/mysql/client-key.pem',
143
142
  :sslcert => '/etc/mysql/client-cert.pem',
144
143
  :sslca => '/etc/mysql/ca-cert.pem',
145
144
  :sslcipher => 'DHE-RSA-AES256-SHA',
146
- :sslverify => true
145
+ :sslverify => true,
147
146
  )
148
- # rubocop:enable Style/TrailingComma
149
- }.not_to raise_error
147
+ end.not_to raise_error
150
148
 
151
149
  results = Hash[ssl_client.query('SHOW STATUS WHERE Variable_name LIKE "Ssl_%"').map { |x| x.values_at('Variable_name', 'Value') }]
152
150
  expect(results['Ssl_cipher']).not_to be_empty
@@ -166,7 +164,8 @@ RSpec.describe Mysql2::Client do
166
164
  end
167
165
 
168
166
  it "should terminate connections when calling close" do
169
- expect {
167
+ # rubocop:disable Lint/AmbiguousBlockAssociation
168
+ expect do
170
169
  client = Mysql2::Client.new(DatabaseCredentials['root'])
171
170
  connection_id = client.thread_id
172
171
  client.close
@@ -180,27 +179,64 @@ RSpec.describe Mysql2::Client do
180
179
  sleep(0.1)
181
180
  end
182
181
  expect(closed).to eq(true)
183
- }.to_not change {
182
+ end.to_not change {
184
183
  @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a
185
184
  }
185
+ # rubocop:enable Lint/AmbiguousBlockAssociation
186
186
  end
187
187
 
188
188
  it "should not leave dangling connections after garbage collection" do
189
189
  run_gc
190
- expect {
191
- expect {
190
+ # rubocop:disable Lint/AmbiguousBlockAssociation
191
+ expect do
192
+ expect do
192
193
  10.times do
193
194
  Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
194
195
  end
195
- }.to change {
196
+ end.to change {
196
197
  @client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
197
198
  }.by(10)
198
199
 
199
200
  run_gc
200
- }.to_not change {
201
+ end.to_not change {
201
202
  @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
202
203
  @client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
203
204
  }
205
+ # rubocop:enable Lint/AmbiguousBlockAssociation
206
+ end
207
+
208
+ context "#set_server_option" do
209
+ let(:client) do
210
+ new_client.tap do |client|
211
+ client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
212
+ end
213
+ end
214
+
215
+ it 'returns true when multi_statements is enable' do
216
+ expect(client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)).to be true
217
+ end
218
+
219
+ it 'returns true when multi_statements is disable' do
220
+ expect(client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)).to be true
221
+ end
222
+
223
+ it 'returns false when multi_statements is neither OPTION_MULTI_STATEMENTS_OFF or OPTION_MULTI_STATEMENTS_ON' do
224
+ expect(client.set_server_option(344)).to be false
225
+ end
226
+
227
+ it 'enables multiple-statement' do
228
+ client.query("SELECT 1;SELECT 2;")
229
+
230
+ expect(client.next_result).to be true
231
+ expect(client.store_result.first).to eql('2' => 2)
232
+ expect(client.next_result).to be false
233
+ end
234
+
235
+ it 'disables multiple-statement' do
236
+ client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
237
+
238
+ expect { client.query("SELECT 1;SELECT 2;") }.to raise_error(Mysql2::Error)
239
+ end
204
240
  end
205
241
 
206
242
  context "#automatic_close" do
@@ -211,24 +247,24 @@ RSpec.describe Mysql2::Client do
211
247
  if RUBY_PLATFORM =~ /mingw|mswin/
212
248
  it "cannot be disabled" do
213
249
  expect do
214
- client = new_client(:automatic_close => false)
250
+ client = new_client(automatic_close: false)
215
251
  expect(client.automatic_close?).to be(true)
216
252
  end.to output(/always closed by garbage collector/).to_stderr
217
253
 
218
254
  expect do
219
- client = new_client(:automatic_close => true)
255
+ client = new_client(automatic_close: true)
220
256
  expect(client.automatic_close?).to be(true)
221
257
  end.to_not output(/always closed by garbage collector/).to_stderr
222
258
 
223
259
  expect do
224
- client = new_client(:automatic_close => true)
260
+ client = new_client(automatic_close: true)
225
261
  client.automatic_close = false
226
262
  expect(client.automatic_close?).to be(true)
227
263
  end.to output(/always closed by garbage collector/).to_stderr
228
264
  end
229
265
  else
230
266
  it "can be configured" do
231
- client = new_client(:automatic_close => false)
267
+ client = new_client(automatic_close: false)
232
268
  expect(client.automatic_close?).to be(false)
233
269
  end
234
270
 
@@ -272,9 +308,9 @@ RSpec.describe Mysql2::Client do
272
308
  database = 1235
273
309
  @client.query "CREATE DATABASE IF NOT EXISTS `#{database}`"
274
310
 
275
- expect {
311
+ expect do
276
312
  new_client('database' => database)
277
- }.not_to raise_error
313
+ end.not_to raise_error
278
314
 
279
315
  @client.query "DROP DATABASE IF EXISTS `#{database}`"
280
316
  end
@@ -285,9 +321,9 @@ RSpec.describe Mysql2::Client do
285
321
 
286
322
  it "should be able to close properly" do
287
323
  expect(@client.close).to be_nil
288
- expect {
324
+ expect do
289
325
  @client.query "SELECT 1"
290
- }.to raise_error(Mysql2::Error)
326
+ end.to raise_error(Mysql2::Error)
291
327
  end
292
328
 
293
329
  context "#closed?" do
@@ -302,11 +338,11 @@ RSpec.describe Mysql2::Client do
302
338
  end
303
339
 
304
340
  it "should not try to query closed mysql connection" do
305
- client = new_client(:reconnect => true)
341
+ client = new_client(reconnect: true)
306
342
  expect(client.close).to be_nil
307
- expect {
343
+ expect do
308
344
  client.query "SELECT 1"
309
- }.to raise_error(Mysql2::Error)
345
+ end.to raise_error(Mysql2::Error)
310
346
  end
311
347
 
312
348
  it "should respond to #query" do
@@ -355,7 +391,7 @@ RSpec.describe Mysql2::Client do
355
391
  # # 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).
356
392
  @client.query("INSERT INTO infoTest (blah) VALUES (1234),(4535)")
357
393
 
358
- expect(@client.query_info).to eql(:records => 2, :duplicates => 0, :warnings => 0)
394
+ expect(@client.query_info).to eql(records: 2, duplicates: 0, warnings: 0)
359
395
  expect(@client.query_info_string).to eq('Records: 2 Duplicates: 0 Warnings: 0')
360
396
 
361
397
  @client.query "DROP TABLE infoTest"
@@ -365,7 +401,7 @@ RSpec.describe Mysql2::Client do
365
401
 
366
402
  context ":local_infile" do
367
403
  before(:all) do
368
- new_client(:local_infile => true) do |client|
404
+ new_client(local_infile: true) do |client|
369
405
  local = client.query "SHOW VARIABLES LIKE 'local_infile'"
370
406
  local_enabled = local.any? { |x| x['Value'] == 'ON' }
371
407
  skip("DON'T WORRY, THIS TEST PASSES - but LOCAL INFILE is not enabled in your MySQL daemon.") unless local_enabled
@@ -387,24 +423,24 @@ RSpec.describe Mysql2::Client do
387
423
  end
388
424
 
389
425
  it "should raise an error when local_infile is disabled" do
390
- client = new_client(:local_infile => false)
391
- expect {
426
+ client = new_client(local_infile: false)
427
+ expect do
392
428
  client.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
393
- }.to raise_error(Mysql2::Error, /command is not allowed/)
429
+ end.to raise_error(Mysql2::Error, /command is not allowed/)
394
430
  end
395
431
 
396
432
  it "should raise an error when a non-existent file is loaded" do
397
- client = new_client(:local_infile => true)
398
- expect {
433
+ client = new_client(local_infile: true)
434
+ expect do
399
435
  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')
436
+ end.to raise_error(Mysql2::Error, 'No such file or directory: this/file/is/not/here')
401
437
  end
402
438
 
403
439
  it "should LOAD DATA LOCAL INFILE" do
404
- client = new_client(:local_infile => true)
440
+ client = new_client(local_infile: true)
405
441
  client.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
406
442
  info = client.query_info
407
- expect(info).to eql(:records => 1, :deleted => 0, :skipped => 0, :warnings => 0)
443
+ expect(info).to eql(records: 1, deleted: 0, skipped: 0, warnings: 0)
408
444
 
409
445
  result = client.query "SELECT * FROM infileTest"
410
446
  expect(result.first).to eql('id' => 1, 'foo' => 'Hello', 'bar' => 'World')
@@ -412,57 +448,77 @@ RSpec.describe Mysql2::Client do
412
448
  end
413
449
 
414
450
  it "should expect connect_timeout to be a positive integer" do
415
- expect {
416
- new_client(:connect_timeout => -1)
417
- }.to raise_error(Mysql2::Error)
451
+ expect do
452
+ new_client(connect_timeout: -1)
453
+ end.to raise_error(Mysql2::Error)
418
454
  end
419
455
 
420
456
  it "should expect read_timeout to be a positive integer" do
421
- expect {
422
- new_client(:read_timeout => -1)
423
- }.to raise_error(Mysql2::Error)
457
+ expect do
458
+ new_client(read_timeout: -1)
459
+ end.to raise_error(Mysql2::Error)
424
460
  end
425
461
 
426
462
  it "should expect write_timeout to be a positive integer" do
427
- expect {
428
- new_client(:write_timeout => -1)
429
- }.to raise_error(Mysql2::Error)
463
+ expect do
464
+ new_client(write_timeout: -1)
465
+ end.to raise_error(Mysql2::Error)
430
466
  end
431
467
 
432
468
  it "should allow nil read_timeout" do
433
- client = new_client(:read_timeout => nil)
469
+ client = new_client(read_timeout: nil)
434
470
 
435
471
  expect(client.read_timeout).to be_nil
436
472
  end
437
473
 
474
+ it "should set default program_name in connect_attrs" do
475
+ client = new_client
476
+ if Mysql2::Client::CONNECT_ATTRS.zero? || client.server_info[:version].match(/10.[01].\d+-MariaDB/)
477
+ pending('Both client and server versions must be MySQL 5.6 or MariaDB 10.2 or later.')
478
+ end
479
+ result = client.query("SELECT attr_value FROM performance_schema.session_account_connect_attrs WHERE processlist_id = connection_id() AND attr_name = 'program_name'")
480
+ expect(result.first['attr_value']).to eq($PROGRAM_NAME)
481
+ end
482
+
483
+ it "should set custom connect_attrs" do
484
+ client = new_client(connect_attrs: { program_name: 'my_program_name', foo: 'fooval', bar: 'barval' })
485
+ if Mysql2::Client::CONNECT_ATTRS.zero? || client.server_info[:version].match(/10.[01].\d+-MariaDB/)
486
+ pending('Both client and server versions must be MySQL 5.6 or MariaDB 10.2 or later.')
487
+ end
488
+ results = Hash[client.query("SELECT * FROM performance_schema.session_account_connect_attrs WHERE processlist_id = connection_id()").map { |x| x.values_at('ATTR_NAME', 'ATTR_VALUE') }]
489
+ expect(results['program_name']).to eq('my_program_name')
490
+ expect(results['foo']).to eq('fooval')
491
+ expect(results['bar']).to eq('barval')
492
+ end
493
+
438
494
  context "#query" do
439
495
  it "should let you query again if iterating is finished when streaming" do
440
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).each.to_a
496
+ @client.query("SELECT 1 UNION SELECT 2", stream: true, cache_rows: false).each.to_a
441
497
 
442
- expect {
443
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false)
444
- }.to_not raise_error
498
+ expect do
499
+ @client.query("SELECT 1 UNION SELECT 2", stream: true, cache_rows: false)
500
+ end.to_not raise_error
445
501
  end
446
502
 
447
503
  it "should not let you query again if iterating is not finished when streaming" do
448
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).first
504
+ @client.query("SELECT 1 UNION SELECT 2", stream: true, cache_rows: false).first
449
505
 
450
- expect {
451
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false)
452
- }.to raise_exception(Mysql2::Error)
506
+ expect do
507
+ @client.query("SELECT 1 UNION SELECT 2", stream: true, cache_rows: false)
508
+ end.to raise_exception(Mysql2::Error)
453
509
  end
454
510
 
455
511
  it "should only accept strings as the query parameter" do
456
- expect {
512
+ expect do
457
513
  @client.query ["SELECT 'not right'"]
458
- }.to raise_error(TypeError)
514
+ end.to raise_error(TypeError)
459
515
  end
460
516
 
461
517
  it "should not retain query options set on a query for subsequent queries, but should retain it in the result" do
462
- result = @client.query "SELECT 1", :something => :else
518
+ result = @client.query "SELECT 1", something: :else
463
519
  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))
520
+ expect(result.instance_variable_get('@query_options')).to eql(@client.query_options.merge(something: :else))
521
+ expect(@client.instance_variable_get('@current_query_options')).to eql(@client.query_options.merge(something: :else))
466
522
 
467
523
  result = @client.query "SELECT 1"
468
524
  expect(result.instance_variable_get('@query_options')).to eql(@client.query_options)
@@ -470,7 +526,7 @@ RSpec.describe Mysql2::Client do
470
526
  end
471
527
 
472
528
  it "should allow changing query options for subsequent queries" do
473
- @client.query_options.merge!(:something => :else)
529
+ @client.query_options[:something] = :else
474
530
  result = @client.query "SELECT 1"
475
531
  expect(@client.query_options[:something]).to eql(:else)
476
532
  expect(result.instance_variable_get('@query_options')[:something]).to eql(:else)
@@ -485,19 +541,19 @@ RSpec.describe Mysql2::Client do
485
541
  end
486
542
 
487
543
  it "should be able to return results as an array" do
488
- expect(@client.query("SELECT 1", :as => :array).first).to be_an_instance_of(Array)
489
- @client.query("SELECT 1").each(:as => :array)
544
+ expect(@client.query("SELECT 1", as: :array).first).to be_an_instance_of(Array)
545
+ @client.query("SELECT 1").each(as: :array)
490
546
  end
491
547
 
492
548
  it "should be able to return results with symbolized keys" do
493
- expect(@client.query("SELECT 1", :symbolize_keys => true).first.keys[0]).to be_an_instance_of(Symbol)
549
+ expect(@client.query("SELECT 1", symbolize_keys: true).first.keys[0]).to be_an_instance_of(Symbol)
494
550
  end
495
551
 
496
552
  it "should require an open connection" do
497
553
  @client.close
498
- expect {
554
+ expect do
499
555
  @client.query "SELECT 1"
500
- }.to raise_error(Mysql2::Error)
556
+ end.to raise_error(Mysql2::Error)
501
557
  end
502
558
 
503
559
  it "should detect closed connection on query read error" do
@@ -508,37 +564,37 @@ RSpec.describe Mysql2::Client do
508
564
  supervisor.query("KILL #{connection_id}")
509
565
  end.close
510
566
  end
511
- expect {
567
+ expect do
512
568
  @client.query("SELECT SLEEP(1)")
513
- }.to raise_error(Mysql2::Error, /Lost connection to MySQL server/)
569
+ end.to raise_error(Mysql2::Error, /Lost connection to MySQL server/)
514
570
 
515
571
  if RUBY_PLATFORM !~ /mingw|mswin/
516
- expect {
572
+ expect do
517
573
  @client.socket
518
- }.to raise_error(Mysql2::Error, 'MySQL client is not connected')
574
+ end.to raise_error(Mysql2::Error, 'MySQL client is not connected')
519
575
  end
520
576
  end
521
577
 
522
578
  if RUBY_PLATFORM !~ /mingw|mswin/
523
579
  it "should not allow another query to be sent without fetching a result first" do
524
- @client.query("SELECT 1", :async => true)
525
- expect {
580
+ @client.query("SELECT 1", async: true)
581
+ expect do
526
582
  @client.query("SELECT 1")
527
- }.to raise_error(Mysql2::Error)
583
+ end.to raise_error(Mysql2::Error)
528
584
  end
529
585
 
530
586
  it "should describe the thread holding the active query" do
531
- thr = Thread.new { @client.query("SELECT 1", :async => true) }
587
+ thr = Thread.new { @client.query("SELECT 1", async: true) }
532
588
 
533
589
  thr.join
534
590
  expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, Regexp.new(Regexp.escape(thr.inspect)))
535
591
  end
536
592
 
537
593
  it "should timeout if we wait longer than :read_timeout" do
538
- client = new_client(:read_timeout => 0)
539
- expect {
594
+ client = new_client(read_timeout: 0)
595
+ expect do
540
596
  client.query('SELECT SLEEP(0.1)')
541
- }.to raise_error(Mysql2::Error)
597
+ end.to raise_error(Mysql2::Error::TimeoutError)
542
598
  end
543
599
 
544
600
  # XXX this test is not deterministic (because Unix signal handling is not)
@@ -572,22 +628,18 @@ RSpec.describe Mysql2::Client do
572
628
  end
573
629
 
574
630
  it "#socket should return a Fixnum (file descriptor from C)" do
575
- expect(@client.socket).to be_an_instance_of(Fixnum)
631
+ expect(@client.socket).to be_an_instance_of(0.class)
576
632
  expect(@client.socket).not_to eql(0)
577
633
  end
578
634
 
579
635
  it "#socket should require an open connection" do
580
636
  @client.close
581
- expect {
637
+ expect do
582
638
  @client.socket
583
- }.to raise_error(Mysql2::Error)
639
+ end.to raise_error(Mysql2::Error)
584
640
  end
585
641
 
586
642
  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
-
591
643
  # attempt to break the connection
592
644
  stmt = @client.prepare('SELECT SLEEP(?)')
593
645
  expect { Timeout.timeout(0.1) { stmt.execute(0.2) } }.to raise_error(Timeout::Error)
@@ -608,7 +660,7 @@ RSpec.describe Mysql2::Client do
608
660
  pending('MySQL 5.5 on OSX is afflicted by an unknown bug that breaks this test. See #633 and #634.')
609
661
  end
610
662
 
611
- client = new_client(:reconnect => true)
663
+ client = new_client(reconnect: true)
612
664
 
613
665
  expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
614
666
  expect { client.query('SELECT 1') }.to_not raise_error
@@ -635,7 +687,7 @@ RSpec.describe Mysql2::Client do
635
687
  sleep_time = 0.5
636
688
 
637
689
  # Note that each thread opens its own database connection
638
- threads = 5.times.map do
690
+ threads = Array.new(5) do
639
691
  Thread.new do
640
692
  new_client do |client|
641
693
  client.query("SELECT SLEEP(#{sleep_time})")
@@ -652,19 +704,12 @@ RSpec.describe Mysql2::Client do
652
704
  end
653
705
 
654
706
  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.")
656
707
  # should immediately return nil
657
- expect(@client.query("SELECT sleep(0.1)", :async => true)).to eql(nil)
708
+ expect(@client.query("SELECT sleep(0.1)", async: true)).to eql(nil)
658
709
 
659
- io_wrapper = IO.for_fd(@client.socket, :autoclose => false)
710
+ io_wrapper = IO.for_fd(@client.socket, autoclose: false)
660
711
  loops = 0
661
- loop do
662
- if IO.select([io_wrapper], nil, nil, 0.05)
663
- break
664
- else
665
- loops += 1
666
- end
667
- end
712
+ loops += 1 until IO.select([io_wrapper], nil, nil, 0.05)
668
713
 
669
714
  # make sure we waited some period of time
670
715
  expect(loops >= 1).to be true
@@ -676,15 +721,15 @@ RSpec.describe Mysql2::Client do
676
721
 
677
722
  context "Multiple results sets" do
678
723
  before(:each) do
679
- @multi_client = new_client(:flags => Mysql2::Client::MULTI_STATEMENTS)
724
+ @multi_client = new_client(flags: Mysql2::Client::MULTI_STATEMENTS)
680
725
  end
681
726
 
682
727
  it "should raise an exception when one of multiple statements fails" do
683
728
  result = @multi_client.query("SELECT 1 AS 'set_1'; SELECT * FROM invalid_table_name; SELECT 2 AS 'set_2';")
684
729
  expect(result.first['set_1']).to be(1)
685
- expect {
730
+ expect do
686
731
  @multi_client.next_result
687
- }.to raise_error(Mysql2::Error)
732
+ end.to raise_error(Mysql2::Error)
688
733
  expect(@multi_client.next_result).to be false
689
734
  end
690
735
 
@@ -706,17 +751,17 @@ RSpec.describe Mysql2::Client do
706
751
 
707
752
  it "will raise on query if there are outstanding results to read" do
708
753
  @multi_client.query("SELECT 1; SELECT 2; SELECT 3")
709
- expect {
754
+ expect do
710
755
  @multi_client.query("SELECT 4")
711
- }.to raise_error(Mysql2::Error)
756
+ end.to raise_error(Mysql2::Error)
712
757
  end
713
758
 
714
759
  it "#abandon_results! should work" do
715
760
  @multi_client.query("SELECT 1; SELECT 2; SELECT 3")
716
761
  @multi_client.abandon_results!
717
- expect {
762
+ expect do
718
763
  @multi_client.query("SELECT 4")
719
- }.not_to raise_error
764
+ end.not_to raise_error
720
765
  end
721
766
 
722
767
  it "#more_results? should work" do
@@ -752,9 +797,9 @@ RSpec.describe Mysql2::Client do
752
797
 
753
798
  if RUBY_PLATFORM =~ /mingw|mswin/
754
799
  it "#socket should raise as it's not supported" do
755
- expect {
800
+ expect do
756
801
  @client.socket
757
- }.to raise_error(Mysql2::Error, /Raw access to the mysql file descriptor isn't supported on Windows/)
802
+ end.to raise_error(Mysql2::Error, /Raw access to the mysql file descriptor isn't supported on Windows/)
758
803
  end
759
804
  end
760
805
 
@@ -773,27 +818,25 @@ RSpec.describe Mysql2::Client do
773
818
  end
774
819
 
775
820
  it "should not overflow the thread stack" do
776
- expect {
821
+ expect do
777
822
  Thread.new { Mysql2::Client.escape("'" * 256 * 1024) }.join
778
- }.not_to raise_error
823
+ end.not_to raise_error
779
824
  end
780
825
 
781
826
  it "should not overflow the process stack" do
782
- expect {
827
+ expect do
783
828
  Thread.new { Mysql2::Client.escape("'" * 1024 * 1024 * 4) }.join
784
- }.not_to raise_error
829
+ end.not_to raise_error
785
830
  end
786
831
 
787
- unless RUBY_VERSION =~ /1.8/
788
- it "should carry over the original string's encoding" do
789
- str = "abc'def\"ghi\0jkl%mno"
790
- escaped = Mysql2::Client.escape(str)
791
- expect(escaped.encoding).to eql(str.encoding)
832
+ it "should carry over the original string's encoding" do
833
+ str = "abc'def\"ghi\0jkl%mno"
834
+ escaped = Mysql2::Client.escape(str)
835
+ expect(escaped.encoding).to eql(str.encoding)
792
836
 
793
- str.encode!('us-ascii')
794
- escaped = Mysql2::Client.escape(str)
795
- expect(escaped.encoding).to eql(str.encoding)
796
- end
837
+ str.encode!('us-ascii')
838
+ escaped = Mysql2::Client.escape(str)
839
+ expect(escaped.encoding).to eql(str.encoding)
797
840
  end
798
841
  end
799
842
 
@@ -812,28 +855,26 @@ RSpec.describe Mysql2::Client do
812
855
  end
813
856
 
814
857
  it "should not overflow the thread stack" do
815
- expect {
858
+ expect do
816
859
  Thread.new { @client.escape("'" * 256 * 1024) }.join
817
- }.not_to raise_error
860
+ end.not_to raise_error
818
861
  end
819
862
 
820
863
  it "should not overflow the process stack" do
821
- expect {
864
+ expect do
822
865
  Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
823
- }.not_to raise_error
866
+ end.not_to raise_error
824
867
  end
825
868
 
826
869
  it "should require an open connection" do
827
870
  @client.close
828
- expect {
871
+ expect do
829
872
  @client.escape ""
830
- }.to raise_error(Mysql2::Error)
873
+ end.to raise_error(Mysql2::Error)
831
874
  end
832
875
 
833
876
  context 'when mysql encoding is not utf8' do
834
- before { pending('Encoding is undefined') unless defined?(Encoding) }
835
-
836
- let(:client) { new_client(:encoding => "ujis") }
877
+ let(:client) { new_client(encoding: "ujis") }
837
878
 
838
879
  it 'should return a internal encoding string if Encoding.default_internal is set' do
839
880
  with_internal_encoding Encoding::UTF_8 do
@@ -852,14 +893,12 @@ RSpec.describe Mysql2::Client do
852
893
  info = @client.info
853
894
  expect(info).to be_an_instance_of(Hash)
854
895
  expect(info).to have_key(:id)
855
- expect(info[:id]).to be_an_instance_of(Fixnum)
896
+ expect(info[:id]).to be_an_instance_of(0.class)
856
897
  expect(info).to have_key(:version)
857
898
  expect(info[:version]).to be_an_instance_of(String)
858
899
  end
859
900
 
860
901
  context "strings returned by #info" do
861
- before { pending('Encoding is undefined') unless defined?(Encoding) }
862
-
863
902
  it "should be tagged as ascii" do
864
903
  expect(@client.info[:version].encoding).to eql(Encoding::US_ASCII)
865
904
  expect(@client.info[:header_version].encoding).to eql(Encoding::US_ASCII)
@@ -867,8 +906,6 @@ RSpec.describe Mysql2::Client do
867
906
  end
868
907
 
869
908
  context "strings returned by .info" do
870
- before { pending('Encoding is undefined') unless defined?(Encoding) }
871
-
872
909
  it "should be tagged as ascii" do
873
910
  expect(Mysql2::Client.info[:version].encoding).to eql(Encoding::US_ASCII)
874
911
  expect(Mysql2::Client.info[:header_version].encoding).to eql(Encoding::US_ASCII)
@@ -883,26 +920,24 @@ RSpec.describe Mysql2::Client do
883
920
  server_info = @client.server_info
884
921
  expect(server_info).to be_an_instance_of(Hash)
885
922
  expect(server_info).to have_key(:id)
886
- expect(server_info[:id]).to be_an_instance_of(Fixnum)
923
+ expect(server_info[:id]).to be_an_instance_of(0.class)
887
924
  expect(server_info).to have_key(:version)
888
925
  expect(server_info[:version]).to be_an_instance_of(String)
889
926
  end
890
927
 
891
928
  it "#server_info should require an open connection" do
892
929
  @client.close
893
- expect {
930
+ expect do
894
931
  @client.server_info
895
- }.to raise_error(Mysql2::Error)
932
+ end.to raise_error(Mysql2::Error)
896
933
  end
897
934
 
898
935
  context "strings returned by #server_info" do
899
- before { pending('Encoding is undefined') unless defined?(Encoding) }
900
-
901
936
  it "should default to the connection's encoding if Encoding.default_internal is nil" do
902
937
  with_internal_encoding nil do
903
938
  expect(@client.server_info[:version].encoding).to eql(Encoding::UTF_8)
904
939
 
905
- client2 = new_client(:encoding => 'ascii')
940
+ client2 = new_client(encoding: 'ascii')
906
941
  expect(client2.server_info[:version].encoding).to eql(Encoding::ASCII)
907
942
  end
908
943
  end
@@ -918,14 +953,14 @@ RSpec.describe Mysql2::Client do
918
953
  end
919
954
  end
920
955
 
921
- it "should raise a Mysql2::Error exception upon connection failure" do
922
- expect {
923
- new_client(:host => "localhost", :username => 'asdfasdf8d2h', :password => 'asdfasdfw42')
924
- }.to raise_error(Mysql2::Error)
956
+ it "should raise a Mysql2::Error::ConnectionError exception upon connection failure due to invalid credentials" do
957
+ expect do
958
+ new_client(host: 'localhost', username: 'asdfasdf8d2h', password: 'asdfasdfw42')
959
+ end.to raise_error(Mysql2::Error::ConnectionError)
925
960
 
926
- expect {
961
+ expect do
927
962
  new_client(DatabaseCredentials['root'])
928
- }.not_to raise_error
963
+ end.not_to raise_error
929
964
  end
930
965
 
931
966
  context 'write operations api' do
@@ -974,7 +1009,7 @@ RSpec.describe Mysql2::Client do
974
1009
  end
975
1010
 
976
1011
  it "#thread_id should be a Fixnum" do
977
- expect(@client.thread_id).to be_an_instance_of(Fixnum)
1012
+ expect(@client.thread_id).to be_an_instance_of(0.class)
978
1013
  end
979
1014
 
980
1015
  it "should respond to #ping" do
@@ -1010,9 +1045,9 @@ RSpec.describe Mysql2::Client do
1010
1045
  end
1011
1046
 
1012
1047
  it "should raise a Mysql2::Error when the database doesn't exist" do
1013
- expect {
1048
+ expect do
1014
1049
  @client.select_db("nopenothere")
1015
- }.to raise_error(Mysql2::Error)
1050
+ end.to raise_error(Mysql2::Error)
1016
1051
  end
1017
1052
 
1018
1053
  it "should return the database switched to" do
@@ -1027,13 +1062,11 @@ RSpec.describe Mysql2::Client do
1027
1062
  end
1028
1063
 
1029
1064
  it "should be able to connect using plaintext password" do
1030
- client = new_client(:enable_cleartext_plugin => true)
1065
+ client = new_client(enable_cleartext_plugin: true)
1031
1066
  client.query('SELECT 1')
1032
1067
  end
1033
1068
 
1034
- unless RUBY_VERSION =~ /1.8/
1035
- it "should respond to #encoding" do
1036
- expect(@client).to respond_to(:encoding)
1037
- end
1069
+ it "should respond to #encoding" do
1070
+ expect(@client).to respond_to(:encoding)
1038
1071
  end
1039
1072
  end