mysql2 0.4.1 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|