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