mysql2 0.4.1 → 0.4.6
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 +58 -15
- data/ext/mysql2/client.c +190 -50
- data/ext/mysql2/client.h +1 -10
- data/ext/mysql2/extconf.rb +59 -15
- data/ext/mysql2/result.c +52 -37
- data/ext/mysql2/result.h +3 -2
- data/ext/mysql2/statement.c +118 -35
- data/ext/mysql2/statement.h +1 -0
- data/lib/mysql2/client.rb +42 -7
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +0 -6
- data/spec/mysql2/client_spec.rb +124 -70
- data/spec/mysql2/error_spec.rb +1 -1
- data/spec/mysql2/result_spec.rb +9 -0
- data/spec/mysql2/statement_spec.rb +81 -6
- data/spec/ssl/gen_certs.sh +1 -1
- metadata +3 -3
data/ext/mysql2/statement.h
CHANGED
data/lib/mysql2/client.rb
CHANGED
@@ -10,7 +10,7 @@ module Mysql2
|
|
10
10
|
:symbolize_keys => false, # return field names as symbols instead of strings
|
11
11
|
:database_timezone => :local, # timezone Mysql2 will assume datetime objects are stored in
|
12
12
|
:application_timezone => nil, # timezone Mysql2 will convert to before handing the object back to the caller
|
13
|
-
:cache_rows => true, # tells Mysql2 to use
|
13
|
+
:cache_rows => true, # tells Mysql2 to use its internal row cache for results
|
14
14
|
:connect_flags => REMEMBER_OPTIONS | LONG_PASSWORD | LONG_FLAG | TRANSACTIONS | PROTOCOL_41 | SECURE_CONNECTION,
|
15
15
|
:cast => true,
|
16
16
|
:default_file => nil,
|
@@ -19,6 +19,7 @@ module Mysql2
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize(opts = {})
|
22
|
+
fail Mysql2::Error, "Options parameter must be a Hash" unless opts.is_a? Hash
|
22
23
|
opts = Mysql2::Util.key_hash_as_symbols(opts)
|
23
24
|
@read_timeout = nil
|
24
25
|
@query_options = self.class.default_query_options.dup
|
@@ -30,13 +31,13 @@ module Mysql2
|
|
30
31
|
opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
|
31
32
|
|
32
33
|
# TODO: stricter validation rather than silent massaging
|
33
|
-
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command].each do |key|
|
34
|
+
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close].each do |key|
|
34
35
|
next unless opts.key?(key)
|
35
36
|
case key
|
36
|
-
when :reconnect, :local_infile, :secure_auth
|
37
|
+
when :reconnect, :local_infile, :secure_auth, :automatic_close
|
37
38
|
send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
|
38
39
|
when :connect_timeout, :read_timeout, :write_timeout
|
39
|
-
send(:"#{key}=", opts[key].
|
40
|
+
send(:"#{key}=", Integer(opts[key])) unless opts[key].nil?
|
40
41
|
else
|
41
42
|
send(:"#{key}=", opts[key])
|
42
43
|
end
|
@@ -47,11 +48,20 @@ module Mysql2
|
|
47
48
|
|
48
49
|
ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
|
49
50
|
ssl_set(*ssl_options) if ssl_options.any?
|
51
|
+
self.ssl_mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
|
52
|
+
|
53
|
+
case opts[:flags]
|
54
|
+
when Array
|
55
|
+
flags = parse_flags_array(opts[:flags], @query_options[:connect_flags])
|
56
|
+
when String
|
57
|
+
flags = parse_flags_array(opts[:flags].split(' '), @query_options[:connect_flags])
|
58
|
+
when Integer
|
59
|
+
flags = @query_options[:connect_flags] | opts[:flags]
|
60
|
+
else
|
61
|
+
flags = @query_options[:connect_flags]
|
62
|
+
end
|
50
63
|
|
51
64
|
# SSL verify is a connection flag rather than a mysql_ssl_set option
|
52
|
-
flags = 0
|
53
|
-
flags |= @query_options[:connect_flags]
|
54
|
-
flags |= opts[:flags] if opts[:flags]
|
55
65
|
flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify] && ssl_options.any?
|
56
66
|
|
57
67
|
if [:user, :pass, :hostname, :dbname, :db, :sock].any? { |k| @query_options.key?(k) }
|
@@ -79,6 +89,31 @@ module Mysql2
|
|
79
89
|
connect user, pass, host, port, database, socket, flags
|
80
90
|
end
|
81
91
|
|
92
|
+
def parse_ssl_mode(mode)
|
93
|
+
m = mode.to_s.upcase
|
94
|
+
if m.start_with?('SSL_MODE_')
|
95
|
+
return Mysql2::Client.const_get(m) if Mysql2::Client.const_defined?(m)
|
96
|
+
else
|
97
|
+
x = 'SSL_MODE_' + m
|
98
|
+
return Mysql2::Client.const_get(x) if Mysql2::Client.const_defined?(x)
|
99
|
+
end
|
100
|
+
warn "Unknown MySQL ssl_mode flag: #{mode}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse_flags_array(flags, initial = 0)
|
104
|
+
flags.reduce(initial) do |memo, f|
|
105
|
+
fneg = f.start_with?('-') ? f[1..-1] : nil
|
106
|
+
if fneg && fneg =~ /^\w+$/ && Mysql2::Client.const_defined?(fneg)
|
107
|
+
memo & ~ Mysql2::Client.const_get(fneg)
|
108
|
+
elsif f && f =~ /^\w+$/ && Mysql2::Client.const_defined?(f)
|
109
|
+
memo | Mysql2::Client.const_get(f)
|
110
|
+
else
|
111
|
+
warn "Unknown MySQL connection flag: '#{f}'"
|
112
|
+
memo
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
82
117
|
if Thread.respond_to?(:handle_interrupt)
|
83
118
|
def query(sql, options = {})
|
84
119
|
Thread.handle_interrupt(::Mysql2::Util::TimeoutError => :never) do
|
data/lib/mysql2/version.rb
CHANGED
data/spec/mysql2/client_spec.rb
CHANGED
@@ -33,6 +33,12 @@ RSpec.describe Mysql2::Client do
|
|
33
33
|
}.to raise_error(Mysql2::Error)
|
34
34
|
end
|
35
35
|
|
36
|
+
it "should raise an exception on non-string encodings" do
|
37
|
+
expect {
|
38
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => :fake))
|
39
|
+
}.to raise_error(TypeError)
|
40
|
+
end
|
41
|
+
|
36
42
|
it "should not raise an exception on create for a valid encoding" do
|
37
43
|
expect {
|
38
44
|
Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
|
@@ -43,27 +49,33 @@ RSpec.describe Mysql2::Client do
|
|
43
49
|
}.not_to raise_error
|
44
50
|
end
|
45
51
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
@connect_args << args
|
52
|
-
end
|
52
|
+
Klient = Class.new(Mysql2::Client) do
|
53
|
+
attr_reader :connect_args
|
54
|
+
def connect(*args)
|
55
|
+
@connect_args ||= []
|
56
|
+
@connect_args << args
|
53
57
|
end
|
54
|
-
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should accept connect flags and pass them to #connect" do
|
61
|
+
client = Klient.new :flags => Mysql2::Client::FOUND_ROWS
|
55
62
|
expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to be > 0
|
56
63
|
end
|
57
64
|
|
65
|
+
it "should parse flags array" do
|
66
|
+
client = Klient.new :flags => %w( FOUND_ROWS -PROTOCOL_41 )
|
67
|
+
expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
|
68
|
+
expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should parse flags string" do
|
72
|
+
client = Klient.new :flags => "FOUND_ROWS -PROTOCOL_41"
|
73
|
+
expect(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).to eql(Mysql2::Client::FOUND_ROWS)
|
74
|
+
expect(client.connect_args.last[6] & Mysql2::Client::PROTOCOL_41).to eql(0)
|
75
|
+
end
|
76
|
+
|
58
77
|
it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
|
59
|
-
|
60
|
-
attr_reader :connect_args
|
61
|
-
def connect(*args)
|
62
|
-
@connect_args ||= []
|
63
|
-
@connect_args << args
|
64
|
-
end
|
65
|
-
end
|
66
|
-
client = klient.new
|
78
|
+
client = Klient.new
|
67
79
|
client_flags = Mysql2::Client::REMEMBER_OPTIONS |
|
68
80
|
Mysql2::Client::LONG_PASSWORD |
|
69
81
|
Mysql2::Client::LONG_FLAG |
|
@@ -139,16 +151,12 @@ RSpec.describe Mysql2::Client do
|
|
139
151
|
# rubocop:enable Style/TrailingComma
|
140
152
|
}.not_to raise_error
|
141
153
|
|
142
|
-
results = ssl_client.query(
|
143
|
-
expect(results[
|
144
|
-
expect(results[
|
145
|
-
expect(results[0]['Value']).to be_an_instance_of(String)
|
146
|
-
expect(results[0]['Value']).not_to be_empty
|
154
|
+
results = Hash[ssl_client.query('SHOW STATUS WHERE Variable_name LIKE "Ssl_%"').map { |x| x.values_at('Variable_name', 'Value') }]
|
155
|
+
expect(results['Ssl_cipher']).not_to be_empty
|
156
|
+
expect(results['Ssl_version']).not_to be_empty
|
147
157
|
|
148
|
-
expect(
|
149
|
-
expect(results[
|
150
|
-
expect(results[1]['Value']).to be_an_instance_of(String)
|
151
|
-
expect(results[1]['Value']).not_to be_empty
|
158
|
+
expect(ssl_client.ssl_cipher).not_to be_empty
|
159
|
+
expect(results['Ssl_cipher']).to eql(ssl_client.ssl_cipher)
|
152
160
|
|
153
161
|
ssl_client.close
|
154
162
|
end
|
@@ -166,58 +174,107 @@ RSpec.describe Mysql2::Client do
|
|
166
174
|
expect {
|
167
175
|
Mysql2::Client.new(DatabaseCredentials['root']).close
|
168
176
|
}.to_not change {
|
169
|
-
@client.query("SHOW STATUS LIKE '
|
177
|
+
@client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
|
178
|
+
@client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
|
170
179
|
}
|
171
180
|
end
|
172
181
|
|
173
182
|
it "should not leave dangling connections after garbage collection" do
|
174
183
|
run_gc
|
184
|
+
expect {
|
185
|
+
expect {
|
186
|
+
10.times do
|
187
|
+
Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
|
188
|
+
end
|
189
|
+
}.to change {
|
190
|
+
@client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
|
191
|
+
}.by(10)
|
175
192
|
|
176
|
-
|
177
|
-
|
193
|
+
run_gc
|
194
|
+
}.to_not change {
|
195
|
+
@client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
|
196
|
+
@client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
|
197
|
+
}
|
198
|
+
end
|
178
199
|
|
179
|
-
|
180
|
-
|
200
|
+
context "#automatic_close" do
|
201
|
+
it "is enabled by default" do
|
202
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
203
|
+
expect(client.automatic_close?).to be(true)
|
181
204
|
end
|
182
|
-
after_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
|
183
|
-
expect(after_count).to eq(before_count + 10)
|
184
205
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
206
|
+
if RUBY_PLATFORM =~ /mingw|mswin/
|
207
|
+
it "cannot be disabled" do
|
208
|
+
expect do
|
209
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
|
210
|
+
expect(client.automatic_close?).to be(true)
|
211
|
+
end.to output(/always closed by garbage collector/).to_stderr
|
189
212
|
|
190
|
-
|
191
|
-
|
213
|
+
expect do
|
214
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => true))
|
215
|
+
expect(client.automatic_close?).to be(true)
|
216
|
+
end.to_not output(/always closed by garbage collector/).to_stderr
|
192
217
|
|
193
|
-
|
194
|
-
|
218
|
+
expect do
|
219
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => true))
|
220
|
+
client.automatic_close = false
|
221
|
+
expect(client.automatic_close?).to be(true)
|
222
|
+
end.to output(/always closed by garbage collector/).to_stderr
|
223
|
+
end
|
224
|
+
else
|
225
|
+
it "can be configured" do
|
226
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
|
227
|
+
expect(client.automatic_close?).to be(false)
|
228
|
+
end
|
195
229
|
|
196
|
-
|
197
|
-
|
198
|
-
|
230
|
+
it "can be assigned" do
|
231
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
232
|
+
client.automatic_close = false
|
233
|
+
expect(client.automatic_close?).to be(false)
|
199
234
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
235
|
+
client.automatic_close = true
|
236
|
+
expect(client.automatic_close?).to be(true)
|
237
|
+
|
238
|
+
client.automatic_close = nil
|
239
|
+
expect(client.automatic_close?).to be(false)
|
240
|
+
|
241
|
+
client.automatic_close = 9
|
242
|
+
expect(client.automatic_close?).to be(true)
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should not close connections when running in a child process" do
|
246
|
+
run_gc
|
247
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
248
|
+
client.automatic_close = false
|
249
|
+
|
250
|
+
# this empty `fork` call fixes this tests on RBX; without it, the next
|
251
|
+
# `fork` call hangs forever. WTF?
|
252
|
+
fork {}
|
205
253
|
|
206
|
-
|
254
|
+
fork do
|
255
|
+
client.query('SELECT 1')
|
256
|
+
client = nil
|
257
|
+
run_gc
|
258
|
+
end
|
259
|
+
|
260
|
+
Process.wait
|
207
261
|
|
208
|
-
|
209
|
-
|
210
|
-
|
262
|
+
# this will throw an error if the underlying socket was shutdown by the
|
263
|
+
# child's GC
|
264
|
+
expect { client.query('SELECT 1') }.to_not raise_exception
|
265
|
+
end
|
266
|
+
end
|
211
267
|
end
|
212
268
|
|
213
269
|
it "should be able to connect to database with numeric-only name" do
|
214
|
-
|
215
|
-
@client.query "CREATE DATABASE IF NOT EXISTS `#{
|
216
|
-
@client.query "GRANT ALL ON `#{creds['database']}`.* TO #{creds['username']}@`#{creds['host']}`"
|
270
|
+
database = 1235
|
271
|
+
@client.query "CREATE DATABASE IF NOT EXISTS `#{database}`"
|
217
272
|
|
218
|
-
expect {
|
273
|
+
expect {
|
274
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge('database' => database))
|
275
|
+
}.not_to raise_error
|
219
276
|
|
220
|
-
@client.query "DROP DATABASE IF EXISTS `#{
|
277
|
+
@client.query "DROP DATABASE IF EXISTS `#{database}`"
|
221
278
|
end
|
222
279
|
|
223
280
|
it "should respond to #close" do
|
@@ -330,22 +387,28 @@ RSpec.describe Mysql2::Client do
|
|
330
387
|
|
331
388
|
it "should expect connect_timeout to be a positive integer" do
|
332
389
|
expect {
|
333
|
-
Mysql2::Client.new(:connect_timeout => -1)
|
390
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge(:connect_timeout => -1))
|
334
391
|
}.to raise_error(Mysql2::Error)
|
335
392
|
end
|
336
393
|
|
337
394
|
it "should expect read_timeout to be a positive integer" do
|
338
395
|
expect {
|
339
|
-
Mysql2::Client.new(:read_timeout => -1)
|
396
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => -1))
|
340
397
|
}.to raise_error(Mysql2::Error)
|
341
398
|
end
|
342
399
|
|
343
400
|
it "should expect write_timeout to be a positive integer" do
|
344
401
|
expect {
|
345
|
-
Mysql2::Client.new(:write_timeout => -1)
|
402
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge(:write_timeout => -1))
|
346
403
|
}.to raise_error(Mysql2::Error)
|
347
404
|
end
|
348
405
|
|
406
|
+
it "should allow nil read_timeout" do
|
407
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => nil))
|
408
|
+
|
409
|
+
expect(client.read_timeout).to be_nil
|
410
|
+
end
|
411
|
+
|
349
412
|
context "#query" do
|
350
413
|
it "should let you query again if iterating is finished when streaming" do
|
351
414
|
@client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).each.to_a
|
@@ -475,15 +538,6 @@ RSpec.describe Mysql2::Client do
|
|
475
538
|
}.to raise_error(Mysql2::Error)
|
476
539
|
end
|
477
540
|
|
478
|
-
it 'should be impervious to connection-corrupting timeouts in #query' do
|
479
|
-
pending('`Thread.handle_interrupt` is not defined') unless Thread.respond_to?(:handle_interrupt)
|
480
|
-
# attempt to break the connection
|
481
|
-
expect { Timeout.timeout(0.1) { @client.query('SELECT SLEEP(0.2)') } }.to raise_error(Timeout::Error)
|
482
|
-
|
483
|
-
# expect the connection to not be broken
|
484
|
-
expect { @client.query('SELECT 1') }.to_not raise_error
|
485
|
-
end
|
486
|
-
|
487
541
|
it 'should be impervious to connection-corrupting timeouts in #execute' do
|
488
542
|
# the statement handle gets corrupted and will segfault the tests if interrupted,
|
489
543
|
# so we can't even use pending on this test, really have to skip it on older Rubies.
|
@@ -501,7 +555,7 @@ RSpec.describe Mysql2::Client do
|
|
501
555
|
context 'when a non-standard exception class is raised' do
|
502
556
|
it "should close the connection when an exception is raised" do
|
503
557
|
expect { Timeout.timeout(0.1, ArgumentError) { @client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
|
504
|
-
expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, '
|
558
|
+
expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, 'MySQL client is not connected')
|
505
559
|
end
|
506
560
|
|
507
561
|
it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
|
data/spec/mysql2/error_spec.rb
CHANGED
data/spec/mysql2/result_spec.rb
CHANGED
@@ -22,6 +22,10 @@ RSpec.describe Mysql2::Result do
|
|
22
22
|
expect(@result).to respond_to(:each)
|
23
23
|
end
|
24
24
|
|
25
|
+
it "should respond to #free" do
|
26
|
+
expect(@result).to respond_to(:free)
|
27
|
+
end
|
28
|
+
|
25
29
|
it "should raise a Mysql2::Error exception upon a bad query" do
|
26
30
|
expect {
|
27
31
|
@client.query "bad sql"
|
@@ -78,6 +82,11 @@ RSpec.describe Mysql2::Result do
|
|
78
82
|
expect(result.first.object_id).not_to eql(result.first.object_id)
|
79
83
|
end
|
80
84
|
|
85
|
+
it "should be able to iterate a second time even if cache_rows is disabled" do
|
86
|
+
result = @client.query "SELECT 1 UNION SELECT 2", :cache_rows => false
|
87
|
+
expect(result.to_a).to eql(result.to_a)
|
88
|
+
end
|
89
|
+
|
81
90
|
it "should yield different value for #first if streaming" do
|
82
91
|
result = @client.query "SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false
|
83
92
|
expect(result.first).not_to eql(result.first)
|
@@ -6,11 +6,13 @@ RSpec.describe Mysql2::Statement do
|
|
6
6
|
@client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
|
7
7
|
end
|
8
8
|
|
9
|
+
def stmt_count
|
10
|
+
@client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
|
11
|
+
end
|
12
|
+
|
9
13
|
it "should create a statement" do
|
10
14
|
statement = nil
|
11
|
-
expect { statement = @client.prepare 'SELECT 1' }.to change
|
12
|
-
@client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
|
13
|
-
}.by(1)
|
15
|
+
expect { statement = @client.prepare 'SELECT 1' }.to change(&method(:stmt_count)).by(1)
|
14
16
|
expect(statement).to be_an_instance_of(Mysql2::Statement)
|
15
17
|
end
|
16
18
|
|
@@ -59,6 +61,26 @@ RSpec.describe Mysql2::Statement do
|
|
59
61
|
expect(rows).to eq([{ "1" => 1 }])
|
60
62
|
end
|
61
63
|
|
64
|
+
it "should handle bignum but in int64_t" do
|
65
|
+
stmt = @client.prepare('SELECT ? AS max, ? AS min')
|
66
|
+
int64_max = (1 << 63) - 1
|
67
|
+
int64_min = -(1 << 63)
|
68
|
+
result = stmt.execute(int64_max, int64_min)
|
69
|
+
expect(result.to_a).to eq(['max' => int64_max, 'min' => int64_min])
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should handle bignum but beyond int64_t" do
|
73
|
+
stmt = @client.prepare('SELECT ? AS max1, ? AS max2, ? AS max3, ? AS min1, ? AS min2, ? AS min3')
|
74
|
+
int64_max1 = (1 << 63)
|
75
|
+
int64_max2 = (1 << 64) - 1
|
76
|
+
int64_max3 = 1 << 64
|
77
|
+
int64_min1 = -(1 << 63) - 1
|
78
|
+
int64_min2 = -(1 << 64) + 1
|
79
|
+
int64_min3 = -0xC000000000000000
|
80
|
+
result = stmt.execute(int64_max1, int64_max2, int64_max3, int64_min1, int64_min2, int64_min3)
|
81
|
+
expect(result.to_a).to eq(['max1' => int64_max1, 'max2' => int64_max2, 'max3' => int64_max3, 'min1' => int64_min1, 'min2' => int64_min2, 'min3' => int64_min3])
|
82
|
+
end
|
83
|
+
|
62
84
|
it "should keep its result after other query" do
|
63
85
|
@client.query 'USE test'
|
64
86
|
@client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int)'
|
@@ -108,6 +130,35 @@ RSpec.describe Mysql2::Statement do
|
|
108
130
|
expect(result.first.first[1]).to be_an_instance_of(Time)
|
109
131
|
end
|
110
132
|
|
133
|
+
it "should prepare Date values" do
|
134
|
+
now = Date.today
|
135
|
+
statement = @client.prepare('SELECT ? AS a')
|
136
|
+
result = statement.execute(now)
|
137
|
+
expect(result.first['a'].to_s).to eql(now.strftime('%F'))
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should prepare Time values with microseconds" do
|
141
|
+
now = Time.now
|
142
|
+
statement = @client.prepare('SELECT ? AS a')
|
143
|
+
result = statement.execute(now)
|
144
|
+
if RUBY_VERSION =~ /1.8/
|
145
|
+
expect(result.first['a'].strftime('%F %T %z')).to eql(now.strftime('%F %T %z'))
|
146
|
+
else
|
147
|
+
expect(result.first['a'].strftime('%F %T.%6N %z')).to eql(now.strftime('%F %T.%6N %z'))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should prepare DateTime values with microseconds" do
|
152
|
+
now = DateTime.now
|
153
|
+
statement = @client.prepare('SELECT ? AS a')
|
154
|
+
result = statement.execute(now)
|
155
|
+
if RUBY_VERSION =~ /1.8/
|
156
|
+
expect(result.first['a'].strftime('%F %T %z')).to eql(now.strftime('%F %T %z'))
|
157
|
+
else
|
158
|
+
expect(result.first['a'].strftime('%F %T.%6N %z')).to eql(now.strftime('%F %T.%6N %z'))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
111
162
|
it "should tell us about the fields" do
|
112
163
|
statement = @client.prepare 'SELECT 1 as foo, 2'
|
113
164
|
statement.execute
|
@@ -117,6 +168,32 @@ RSpec.describe Mysql2::Statement do
|
|
117
168
|
expect(list[1]).to eq('2')
|
118
169
|
end
|
119
170
|
|
171
|
+
it "should handle as a decimal binding a BigDecimal" do
|
172
|
+
stmt = @client.prepare('SELECT ? AS decimal_test')
|
173
|
+
test_result = stmt.execute(BigDecimal.new("123.45")).first
|
174
|
+
expect(test_result['decimal_test']).to be_an_instance_of(BigDecimal)
|
175
|
+
expect(test_result['decimal_test']).to eql(123.45)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should update a DECIMAL value passing a BigDecimal" do
|
179
|
+
@client.query 'USE test'
|
180
|
+
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_decimal_test'
|
181
|
+
@client.query 'CREATE TABLE mysql2_stmt_decimal_test (decimal_test DECIMAL(10,3))'
|
182
|
+
|
183
|
+
@client.prepare("INSERT INTO mysql2_stmt_decimal_test VALUES (?)").execute(BigDecimal.new("123.45"))
|
184
|
+
|
185
|
+
test_result = @client.query("SELECT * FROM mysql2_stmt_decimal_test").first
|
186
|
+
expect(test_result['decimal_test']).to eql(123.45)
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should warn but still work if cache_rows is set to false" do
|
190
|
+
@client.query_options.merge!(:cache_rows => false)
|
191
|
+
statement = @client.prepare 'SELECT 1'
|
192
|
+
result = nil
|
193
|
+
expect { result = statement.execute.to_a }.to output(/:cache_rows is forced for prepared statements/).to_stderr
|
194
|
+
expect(result.length).to eq(1)
|
195
|
+
end
|
196
|
+
|
120
197
|
context "utf8_db" do
|
121
198
|
before(:each) do
|
122
199
|
@client.query("DROP DATABASE IF EXISTS test_mysql2_stmt_utf8")
|
@@ -670,9 +747,7 @@ RSpec.describe Mysql2::Statement do
|
|
670
747
|
context 'close' do
|
671
748
|
it 'should free server resources' do
|
672
749
|
stmt = @client.prepare 'SELECT 1'
|
673
|
-
expect { stmt.close }.to change
|
674
|
-
@client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
|
675
|
-
}.by(-1)
|
750
|
+
expect { stmt.close }.to change(&method(:stmt_count)).by(-1)
|
676
751
|
end
|
677
752
|
|
678
753
|
it 'should raise an error on subsequent execution' do
|