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