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