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