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