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