mysql2 0.3.14 → 0.3.15
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 +65 -25
- data/ext/mysql2/client.c +20 -15
- data/ext/mysql2/client.h +4 -2
- data/ext/mysql2/infile.c +119 -0
- data/ext/mysql2/infile.h +1 -0
- data/ext/mysql2/mysql2_ext.h +1 -0
- data/ext/mysql2/result.c +14 -13
- data/lib/mysql2/error.rb +72 -6
- data/lib/mysql2/version.rb +1 -1
- data/spec/mysql2/client_spec.rb +114 -20
- data/spec/mysql2/error_spec.rb +57 -47
- data/spec/mysql2/result_spec.rb +131 -94
- data/spec/spec_helper.rb +9 -0
- data/spec/test_data +1 -0
- metadata +6 -2
data/lib/mysql2/error.rb
CHANGED
@@ -1,15 +1,81 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
module Mysql2
|
2
4
|
class Error < StandardError
|
3
|
-
|
5
|
+
REPLACEMENT_CHAR = '?'
|
6
|
+
ENCODE_OPTS = {:undef => :replace, :invalid => :replace, :replace => REPLACEMENT_CHAR}
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
@error_number = nil
|
8
|
-
@sql_state = nil
|
9
|
-
end
|
8
|
+
attr_accessor :error_number, :sql_state
|
9
|
+
attr_writer :server_version
|
10
10
|
|
11
11
|
# Mysql gem compatibility
|
12
12
|
alias_method :errno, :error_number
|
13
13
|
alias_method :error, :message
|
14
|
+
|
15
|
+
def initialize(msg, server_version=nil)
|
16
|
+
self.server_version = server_version
|
17
|
+
|
18
|
+
super(clean_message(msg))
|
19
|
+
end
|
20
|
+
|
21
|
+
if "".respond_to? :encode
|
22
|
+
def sql_state=(state)
|
23
|
+
@sql_state = state.encode(ENCODE_OPTS)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# In MySQL 5.5+ error messages are always constructed server-side as UTF-8
|
30
|
+
# then returned in the encoding set by the `character_set_results` system
|
31
|
+
# variable.
|
32
|
+
#
|
33
|
+
# See http://dev.mysql.com/doc/refman/5.5/en/charset-errors.html for
|
34
|
+
# more contetx.
|
35
|
+
#
|
36
|
+
# Before MySQL 5.5 error message template strings are in whatever encoding
|
37
|
+
# is associated with the error message language.
|
38
|
+
# See http://dev.mysql.com/doc/refman/5.1/en/error-message-language.html
|
39
|
+
# for more information.
|
40
|
+
#
|
41
|
+
# The issue is that the user-data inserted in the message could potentially
|
42
|
+
# be in any encoding MySQL supports and is insert into the latin1, euckr or
|
43
|
+
# koi8r string raw. Meaning there's a high probability the string will be
|
44
|
+
# corrupt encoding-wise.
|
45
|
+
#
|
46
|
+
# See http://dev.mysql.com/doc/refman/5.1/en/charset-errors.html for
|
47
|
+
# more information.
|
48
|
+
#
|
49
|
+
# So in an attempt to make sure the error message string is always in a valid
|
50
|
+
# encoding, we'll assume UTF-8 and clean the string of anything that's not a
|
51
|
+
# valid UTF-8 character.
|
52
|
+
#
|
53
|
+
# Except for if we're on 1.8, where we'll do nothing ;)
|
54
|
+
#
|
55
|
+
# Returns a valid UTF-8 string in Ruby 1.9+, the original string on Ruby 1.8
|
56
|
+
def clean_message(message)
|
57
|
+
return message if !message.respond_to?(:encoding)
|
58
|
+
|
59
|
+
if @server_version && @server_version > 50500
|
60
|
+
message.encode(ENCODE_OPTS)
|
61
|
+
else
|
62
|
+
if message.respond_to? :scrub
|
63
|
+
message.scrub(REPLACEMENT_CHAR).encode(ENCODE_OPTS)
|
64
|
+
else
|
65
|
+
# This is ugly as hell but Ruby 1.9 doesn't provide a way to clean a string
|
66
|
+
# and retain it's valid UTF-8 characters, that I know of.
|
67
|
+
|
68
|
+
new_message = "".force_encoding(Encoding::UTF_8)
|
69
|
+
message.chars.each do |char|
|
70
|
+
if char.valid_encoding?
|
71
|
+
new_message << char
|
72
|
+
else
|
73
|
+
new_message << REPLACEMENT_CHAR
|
74
|
+
end
|
75
|
+
end
|
76
|
+
new_message.encode(ENCODE_OPTS)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
14
80
|
end
|
15
81
|
end
|
data/lib/mysql2/version.rb
CHANGED
data/spec/mysql2/client_spec.rb
CHANGED
@@ -7,13 +7,14 @@ describe Mysql2::Client do
|
|
7
7
|
|
8
8
|
it "should not raise an exception for valid defaults group" do
|
9
9
|
lambda {
|
10
|
-
|
10
|
+
opts = DatabaseCredentials['root'].merge(:default_file => cnf_file, :default_group => "test")
|
11
|
+
@client = Mysql2::Client.new(opts)
|
11
12
|
}.should_not raise_error(Mysql2::Error)
|
12
13
|
end
|
13
14
|
|
14
15
|
it "should not raise an exception without default group" do
|
15
16
|
lambda {
|
16
|
-
@client = Mysql2::Client.new(:default_file => cnf_file)
|
17
|
+
@client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:default_file => cnf_file))
|
17
18
|
}.should_not raise_error(Mysql2::Error)
|
18
19
|
end
|
19
20
|
end
|
@@ -78,7 +79,10 @@ describe Mysql2::Client do
|
|
78
79
|
end
|
79
80
|
|
80
81
|
it "should be able to connect via SSL options" do
|
81
|
-
|
82
|
+
ssl = @client.query "SHOW VARIABLES LIKE 'have_%ssl'"
|
83
|
+
ssl_enabled = ssl.any? {|x| x['Value'] == 'ENABLED'}
|
84
|
+
pending("DON'T WORRY, THIS TEST PASSES - but SSL is not enabled in your MySQL daemon.") unless ssl_enabled
|
85
|
+
pending("DON'T WORRY, THIS TEST PASSES - but you must update the SSL cert paths in this test and remove this pending state.")
|
82
86
|
ssl_client = nil
|
83
87
|
lambda {
|
84
88
|
ssl_client = Mysql2::Client.new(
|
@@ -104,6 +108,24 @@ describe Mysql2::Client do
|
|
104
108
|
ssl_client.close
|
105
109
|
end
|
106
110
|
|
111
|
+
it "should not leave dangling connections after garbage collection" do
|
112
|
+
GC.start
|
113
|
+
sleep 0.300 # Let GC do its work
|
114
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
115
|
+
before_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
|
116
|
+
|
117
|
+
10.times do
|
118
|
+
Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
|
119
|
+
end
|
120
|
+
after_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
|
121
|
+
after_count.should == before_count + 10
|
122
|
+
|
123
|
+
GC.start
|
124
|
+
sleep 0.300 # Let GC do its work
|
125
|
+
final_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
|
126
|
+
final_count.should == before_count
|
127
|
+
end
|
128
|
+
|
107
129
|
it "should be able to connect to database with numeric-only name" do
|
108
130
|
lambda {
|
109
131
|
creds = DatabaseCredentials['numericuser']
|
@@ -179,6 +201,49 @@ describe Mysql2::Client do
|
|
179
201
|
end
|
180
202
|
end
|
181
203
|
|
204
|
+
context ":local_infile" do
|
205
|
+
before(:all) do
|
206
|
+
@client_i = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => true)
|
207
|
+
local = @client_i.query "SHOW VARIABLES LIKE 'local_infile'"
|
208
|
+
local_enabled = local.any? {|x| x['Value'] == 'ON'}
|
209
|
+
pending("DON'T WORRY, THIS TEST PASSES - but LOCAL INFILE is not enabled in your MySQL daemon.") unless local_enabled
|
210
|
+
|
211
|
+
@client_i.query %[
|
212
|
+
CREATE TABLE IF NOT EXISTS infileTest (
|
213
|
+
id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
214
|
+
foo VARCHAR(10),
|
215
|
+
bar MEDIUMTEXT
|
216
|
+
)
|
217
|
+
]
|
218
|
+
end
|
219
|
+
|
220
|
+
after(:all) do
|
221
|
+
@client_i.query "DROP TABLE infileTest"
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should raise an error when local_infile is disabled" do
|
225
|
+
client = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => false)
|
226
|
+
lambda {
|
227
|
+
client.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
|
228
|
+
}.should raise_error(Mysql2::Error, %r{command is not allowed})
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should raise an error when a non-existent file is loaded" do
|
232
|
+
lambda {
|
233
|
+
@client_i.query "LOAD DATA LOCAL INFILE 'this/file/is/not/here' INTO TABLE infileTest"
|
234
|
+
}.should_not raise_error(Mysql2::Error, %r{file not found: this/file/is/not/here})
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should LOAD DATA LOCAL INFILE" do
|
238
|
+
@client_i.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
|
239
|
+
info = @client_i.query_info
|
240
|
+
info.should eql({:records => 1, :deleted => 0, :skipped => 0, :warnings => 0})
|
241
|
+
|
242
|
+
result = @client_i.query "SELECT * FROM infileTest"
|
243
|
+
result.first.should eql({'id' => 1, 'foo' => 'Hello', 'bar' => 'World'})
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
182
247
|
it "should expect connect_timeout to be a positive integer" do
|
183
248
|
lambda {
|
184
249
|
Mysql2::Client.new(:connect_timeout => -1)
|
@@ -332,6 +397,7 @@ describe Mysql2::Client do
|
|
332
397
|
end
|
333
398
|
|
334
399
|
it "should close the connection when an exception is raised" do
|
400
|
+
pending "Ruby 2.1 has changed Timeout behavior." if RUBY_VERSION =~ /2.1/
|
335
401
|
begin
|
336
402
|
Timeout.timeout(1) do
|
337
403
|
@client.query("SELECT sleep(2)")
|
@@ -345,6 +411,7 @@ describe Mysql2::Client do
|
|
345
411
|
end
|
346
412
|
|
347
413
|
it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
|
414
|
+
pending "Ruby 2.1 has changed Timeout behavior." if RUBY_VERSION =~ /2.1/
|
348
415
|
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
|
349
416
|
begin
|
350
417
|
Timeout.timeout(1) do
|
@@ -359,6 +426,7 @@ describe Mysql2::Client do
|
|
359
426
|
end
|
360
427
|
|
361
428
|
it "should handle Timeouts without leaving the connection hanging if reconnect is set to true after construction true" do
|
429
|
+
pending "Ruby 2.1 has changed Timeout behavior." if RUBY_VERSION =~ /2.1/
|
362
430
|
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
363
431
|
begin
|
364
432
|
Timeout.timeout(1) do
|
@@ -433,6 +501,15 @@ describe Mysql2::Client do
|
|
433
501
|
@multi_client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:flags => Mysql2::Client::MULTI_STATEMENTS))
|
434
502
|
end
|
435
503
|
|
504
|
+
it "should raise an exception when one of multiple statements fails" do
|
505
|
+
result = @multi_client.query("SELECT 1 as 'set_1'; SELECT * FROM invalid_table_name;SELECT 2 as 'set_2';")
|
506
|
+
result.first['set_1'].should be(1)
|
507
|
+
lambda {
|
508
|
+
@multi_client.next_result
|
509
|
+
}.should raise_error(Mysql2::Error)
|
510
|
+
@multi_client.next_result.should be_false
|
511
|
+
end
|
512
|
+
|
436
513
|
it "returns multiple result sets" do
|
437
514
|
@multi_client.query( "select 1 as 'set_1'; select 2 as 'set_2'").first.should eql({ 'set_1' => 1 })
|
438
515
|
|
@@ -579,18 +656,22 @@ describe Mysql2::Client do
|
|
579
656
|
if defined? Encoding
|
580
657
|
context "strings returned by #info" do
|
581
658
|
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
582
|
-
|
583
|
-
|
659
|
+
with_internal_encoding nil do
|
660
|
+
@client.info[:version].encoding.should eql(Encoding.find('utf-8'))
|
584
661
|
|
585
|
-
|
586
|
-
|
662
|
+
client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
|
663
|
+
client2.info[:version].encoding.should eql(Encoding.find('us-ascii'))
|
664
|
+
end
|
587
665
|
end
|
588
666
|
|
589
667
|
it "should use Encoding.default_internal" do
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
668
|
+
with_internal_encoding 'utf-8' do
|
669
|
+
@client.info[:version].encoding.should eql(Encoding.default_internal)
|
670
|
+
end
|
671
|
+
|
672
|
+
with_internal_encoding 'us-ascii' do
|
673
|
+
@client.info[:version].encoding.should eql(Encoding.default_internal)
|
674
|
+
end
|
594
675
|
end
|
595
676
|
end
|
596
677
|
end
|
@@ -618,18 +699,22 @@ describe Mysql2::Client do
|
|
618
699
|
if defined? Encoding
|
619
700
|
context "strings returned by #server_info" do
|
620
701
|
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
621
|
-
|
622
|
-
|
702
|
+
with_internal_encoding nil do
|
703
|
+
@client.server_info[:version].encoding.should eql(Encoding.find('utf-8'))
|
623
704
|
|
624
|
-
|
625
|
-
|
705
|
+
client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
|
706
|
+
client2.server_info[:version].encoding.should eql(Encoding.find('us-ascii'))
|
707
|
+
end
|
626
708
|
end
|
627
709
|
|
628
710
|
it "should use Encoding.default_internal" do
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
711
|
+
with_internal_encoding 'utf-8' do
|
712
|
+
@client.server_info[:version].encoding.should eql(Encoding.default_internal)
|
713
|
+
end
|
714
|
+
|
715
|
+
with_internal_encoding 'us-ascii' do
|
716
|
+
@client.server_info[:version].encoding.should eql(Encoding.default_internal)
|
717
|
+
end
|
633
718
|
end
|
634
719
|
end
|
635
720
|
end
|
@@ -647,7 +732,7 @@ describe Mysql2::Client do
|
|
647
732
|
context 'write operations api' do
|
648
733
|
before(:each) do
|
649
734
|
@client.query "USE test"
|
650
|
-
@client.query "CREATE TABLE IF NOT EXISTS lastIdTest (`id`
|
735
|
+
@client.query "CREATE TABLE IF NOT EXISTS lastIdTest (`id` BIGINT NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
|
651
736
|
end
|
652
737
|
|
653
738
|
after(:each) do
|
@@ -674,6 +759,15 @@ describe Mysql2::Client do
|
|
674
759
|
@client.query "UPDATE lastIdTest SET blah=4321 WHERE id=1"
|
675
760
|
@client.affected_rows.should eql(1)
|
676
761
|
end
|
762
|
+
|
763
|
+
it "#last_id should handle BIGINT auto-increment ids above 32 bits" do
|
764
|
+
# The id column type must be BIGINT. Surprise: INT(x) is limited to 32-bits for all values of x.
|
765
|
+
# Insert a row with a given ID, this should raise the auto-increment state
|
766
|
+
@client.query "INSERT INTO lastIdTest (id, blah) VALUES (5000000000, 5000)"
|
767
|
+
@client.last_id.should eql(5000000000)
|
768
|
+
@client.query "INSERT INTO lastIdTest (blah) VALUES (5001)"
|
769
|
+
@client.last_id.should eql(5000000001)
|
770
|
+
end
|
677
771
|
end
|
678
772
|
|
679
773
|
it "should respond to #thread_id" do
|
data/spec/mysql2/error_spec.rb
CHANGED
@@ -1,72 +1,82 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
|
2
3
|
require 'spec_helper'
|
3
4
|
|
4
5
|
describe Mysql2::Error do
|
5
|
-
|
6
|
-
begin
|
7
|
-
@err_client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
|
8
|
-
@err_client.query("HAHAHA")
|
9
|
-
rescue Mysql2::Error => e
|
10
|
-
@error = e
|
11
|
-
ensure
|
12
|
-
@err_client.close
|
13
|
-
end
|
6
|
+
let(:client) { Mysql2::Client.new(DatabaseCredentials['root']) }
|
14
7
|
|
8
|
+
let :error do
|
15
9
|
begin
|
16
|
-
|
17
|
-
@err_client2.query("HAHAHA")
|
10
|
+
client.query("HAHAHA")
|
18
11
|
rescue Mysql2::Error => e
|
19
|
-
|
12
|
+
error = e
|
20
13
|
ensure
|
21
|
-
|
14
|
+
client.close
|
22
15
|
end
|
23
|
-
end
|
24
16
|
|
25
|
-
|
26
|
-
@error.should respond_to(:error_number)
|
17
|
+
error
|
27
18
|
end
|
28
19
|
|
29
|
-
it "
|
30
|
-
|
31
|
-
|
20
|
+
it "responds to error_number and sql_state, with aliases" do
|
21
|
+
error.should respond_to(:error_number)
|
22
|
+
error.should respond_to(:sql_state)
|
32
23
|
|
33
|
-
|
34
|
-
|
35
|
-
|
24
|
+
# Mysql gem compatibility
|
25
|
+
error.should respond_to(:errno)
|
26
|
+
error.should respond_to(:error)
|
36
27
|
end
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
|
29
|
+
if "".respond_to? :encoding
|
30
|
+
let :error do
|
31
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
32
|
+
begin
|
33
|
+
client.query("\xE9\x80\xA0\xE5\xAD\x97")
|
34
|
+
rescue Mysql2::Error => e
|
35
|
+
error = e
|
36
|
+
ensure
|
37
|
+
client.close
|
38
|
+
end
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
40
|
+
error
|
41
|
+
end
|
42
|
+
|
43
|
+
let :bad_err do
|
44
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
45
|
+
begin
|
46
|
+
client.query("\xE5\xC6\x7D\x1F")
|
47
|
+
rescue Mysql2::Error => e
|
48
|
+
error = e
|
49
|
+
ensure
|
50
|
+
client.close
|
50
51
|
end
|
52
|
+
|
53
|
+
error
|
51
54
|
end
|
52
55
|
|
53
|
-
it "
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
56
|
+
it "returns error messages as UTF-8 by default" do
|
57
|
+
with_internal_encoding nil do
|
58
|
+
error.message.encoding.should eql(Encoding::UTF_8)
|
59
|
+
error.message.valid_encoding?
|
60
|
+
|
61
|
+
bad_err.message.encoding.should eql(Encoding::UTF_8)
|
62
|
+
bad_err.message.valid_encoding?
|
63
|
+
|
64
|
+
bad_err.message.should include("??}\u001F")
|
60
65
|
end
|
61
66
|
end
|
62
67
|
|
63
|
-
it "
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
it "returns sql state as ASCII" do
|
69
|
+
error.sql_state.encoding.should eql(Encoding::US_ASCII)
|
70
|
+
error.sql_state.valid_encoding?
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns error messages and sql state in Encoding.default_internal if set" do
|
74
|
+
with_internal_encoding 'UTF-16LE' do
|
75
|
+
error.message.encoding.should eql(Encoding.default_internal)
|
76
|
+
error.message.valid_encoding?
|
77
|
+
|
78
|
+
bad_err.message.encoding.should eql(Encoding.default_internal)
|
79
|
+
bad_err.message.valid_encoding?
|
70
80
|
end
|
71
81
|
end
|
72
82
|
end
|
data/spec/mysql2/result_spec.rb
CHANGED
@@ -6,35 +6,6 @@ describe Mysql2::Result do
|
|
6
6
|
@result = @client.query "SELECT 1"
|
7
7
|
end
|
8
8
|
|
9
|
-
it "should maintain a count while streaming" do
|
10
|
-
result = @client.query('SELECT 1')
|
11
|
-
|
12
|
-
result.count.should eql(1)
|
13
|
-
result.each.to_a
|
14
|
-
result.count.should eql(1)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should set the actual count of rows after streaming" do
|
18
|
-
@client.query "USE test"
|
19
|
-
result = @client.query("SELECT * FROM mysql2_test", :stream => true, :cache_rows => false)
|
20
|
-
result.count.should eql(0)
|
21
|
-
result.each {|r| }
|
22
|
-
result.count.should eql(1)
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should not yield nil at the end of streaming" do
|
26
|
-
result = @client.query('SELECT * FROM mysql2_test', :stream => true)
|
27
|
-
result.each { |r| r.should_not be_nil}
|
28
|
-
end
|
29
|
-
|
30
|
-
it "#count should be zero for rows after streaming when there were no results " do
|
31
|
-
@client.query "USE test"
|
32
|
-
result = @client.query("SELECT * FROM mysql2_test WHERE null_test IS NOT NULL", :stream => true, :cache_rows => false)
|
33
|
-
result.count.should eql(0)
|
34
|
-
result.each.to_a
|
35
|
-
result.count.should eql(0)
|
36
|
-
end
|
37
|
-
|
38
9
|
it "should have included Enumerable" do
|
39
10
|
Mysql2::Result.ancestors.include?(Enumerable).should be_true
|
40
11
|
end
|
@@ -121,7 +92,6 @@ describe Mysql2::Result do
|
|
121
92
|
|
122
93
|
context "#fields" do
|
123
94
|
before(:each) do
|
124
|
-
@client.query "USE test"
|
125
95
|
@test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1")
|
126
96
|
end
|
127
97
|
|
@@ -135,9 +105,59 @@ describe Mysql2::Result do
|
|
135
105
|
end
|
136
106
|
end
|
137
107
|
|
108
|
+
context "streaming" do
|
109
|
+
it "should maintain a count while streaming" do
|
110
|
+
result = @client.query('SELECT 1')
|
111
|
+
|
112
|
+
result.count.should eql(1)
|
113
|
+
result.each.to_a
|
114
|
+
result.count.should eql(1)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should set the actual count of rows after streaming" do
|
118
|
+
result = @client.query("SELECT * FROM mysql2_test", :stream => true, :cache_rows => false)
|
119
|
+
result.count.should eql(0)
|
120
|
+
result.each {|r| }
|
121
|
+
result.count.should eql(1)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should not yield nil at the end of streaming" do
|
125
|
+
result = @client.query('SELECT * FROM mysql2_test', :stream => true, :cache_rows => false)
|
126
|
+
result.each { |r| r.should_not be_nil}
|
127
|
+
end
|
128
|
+
|
129
|
+
it "#count should be zero for rows after streaming when there were no results" do
|
130
|
+
result = @client.query("SELECT * FROM mysql2_test WHERE null_test IS NOT NULL", :stream => true, :cache_rows => false)
|
131
|
+
result.count.should eql(0)
|
132
|
+
result.each.to_a
|
133
|
+
result.count.should eql(0)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should raise an exception if streaming ended due to a timeout" do
|
137
|
+
# Create an extra client instance, since we're going to time it out
|
138
|
+
client = Mysql2::Client.new DatabaseCredentials['root']
|
139
|
+
client.query "CREATE TEMPORARY TABLE streamingTest (val BINARY(255))"
|
140
|
+
|
141
|
+
# Insert enough records to force the result set into multiple reads
|
142
|
+
# (the BINARY type is used simply because it forces full width results)
|
143
|
+
10000.times do |i|
|
144
|
+
client.query "INSERT INTO streamingTest (val) VALUES ('Foo #{i}')"
|
145
|
+
end
|
146
|
+
|
147
|
+
client.query "SET net_write_timeout = 1"
|
148
|
+
res = client.query "SELECT * FROM streamingTest", :stream => true, :cache_rows => false
|
149
|
+
|
150
|
+
lambda {
|
151
|
+
res.each_with_index do |row, i|
|
152
|
+
# Exhaust the first result packet then trigger a timeout
|
153
|
+
sleep 2 if i > 0 && i % 1000 == 0
|
154
|
+
end
|
155
|
+
}.should raise_error(Mysql2::Error, /Lost connection/)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
138
159
|
context "row data type mapping" do
|
139
160
|
before(:each) do
|
140
|
-
@client.query "USE test"
|
141
161
|
@test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
142
162
|
end
|
143
163
|
|
@@ -318,24 +338,27 @@ describe Mysql2::Result do
|
|
318
338
|
if defined? Encoding
|
319
339
|
context "string encoding for ENUM values" do
|
320
340
|
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
341
|
+
with_internal_encoding nil do
|
342
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
343
|
+
result['enum_test'].encoding.should eql(Encoding.find('utf-8'))
|
344
|
+
|
345
|
+
client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
|
346
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
347
|
+
result['enum_test'].encoding.should eql(Encoding.find('us-ascii'))
|
348
|
+
client2.close
|
349
|
+
end
|
330
350
|
end
|
331
351
|
|
332
352
|
it "should use Encoding.default_internal" do
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
353
|
+
with_internal_encoding 'utf-8' do
|
354
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
355
|
+
result['enum_test'].encoding.should eql(Encoding.default_internal)
|
356
|
+
end
|
357
|
+
|
358
|
+
with_internal_encoding 'us-ascii' do
|
359
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
360
|
+
result['enum_test'].encoding.should eql(Encoding.default_internal)
|
361
|
+
end
|
339
362
|
end
|
340
363
|
end
|
341
364
|
end
|
@@ -348,24 +371,27 @@ describe Mysql2::Result do
|
|
348
371
|
if defined? Encoding
|
349
372
|
context "string encoding for SET values" do
|
350
373
|
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
374
|
+
with_internal_encoding nil do
|
375
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
376
|
+
result['set_test'].encoding.should eql(Encoding.find('utf-8'))
|
377
|
+
|
378
|
+
client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
|
379
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
380
|
+
result['set_test'].encoding.should eql(Encoding.find('us-ascii'))
|
381
|
+
client2.close
|
382
|
+
end
|
360
383
|
end
|
361
384
|
|
362
385
|
it "should use Encoding.default_internal" do
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
386
|
+
with_internal_encoding 'utf-8' do
|
387
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
388
|
+
result['set_test'].encoding.should eql(Encoding.default_internal)
|
389
|
+
end
|
390
|
+
|
391
|
+
with_internal_encoding 'us-ascii' do
|
392
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
393
|
+
result['set_test'].encoding.should eql(Encoding.default_internal)
|
394
|
+
end
|
369
395
|
end
|
370
396
|
end
|
371
397
|
end
|
@@ -378,18 +404,22 @@ describe Mysql2::Result do
|
|
378
404
|
if defined? Encoding
|
379
405
|
context "string encoding for BINARY values" do
|
380
406
|
it "should default to binary if Encoding.default_internal is nil" do
|
381
|
-
|
382
|
-
|
383
|
-
|
407
|
+
with_internal_encoding nil do
|
408
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
409
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
410
|
+
end
|
384
411
|
end
|
385
412
|
|
386
413
|
it "should not use Encoding.default_internal" do
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
414
|
+
with_internal_encoding 'utf-8' do
|
415
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
416
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
417
|
+
end
|
418
|
+
|
419
|
+
with_internal_encoding 'us-ascii' do
|
420
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
421
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
422
|
+
end
|
393
423
|
end
|
394
424
|
end
|
395
425
|
end
|
@@ -416,39 +446,46 @@ describe Mysql2::Result do
|
|
416
446
|
context "string encoding for #{type} values" do
|
417
447
|
if ['VARBINARY', 'TINYBLOB', 'BLOB', 'MEDIUMBLOB', 'LONGBLOB'].include?(type)
|
418
448
|
it "should default to binary if Encoding.default_internal is nil" do
|
419
|
-
|
420
|
-
|
421
|
-
|
449
|
+
with_internal_encoding nil do
|
450
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
451
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
452
|
+
end
|
422
453
|
end
|
423
454
|
|
424
455
|
it "should not use Encoding.default_internal" do
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
456
|
+
with_internal_encoding 'utf-8' do
|
457
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
458
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
459
|
+
end
|
460
|
+
|
461
|
+
with_internal_encoding 'us-ascii' do
|
462
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
463
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
464
|
+
end
|
431
465
|
end
|
432
466
|
else
|
433
467
|
it "should default to utf-8 if Encoding.default_internal is nil" do
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
468
|
+
with_internal_encoding nil do
|
469
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
470
|
+
result[field].encoding.should eql(Encoding.find('utf-8'))
|
471
|
+
|
472
|
+
client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
|
473
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
474
|
+
result[field].encoding.should eql(Encoding.find('us-ascii'))
|
475
|
+
client2.close
|
476
|
+
end
|
443
477
|
end
|
444
478
|
|
445
479
|
it "should use Encoding.default_internal" do
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
480
|
+
with_internal_encoding 'utf-8' do
|
481
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
482
|
+
result[field].encoding.should eql(Encoding.default_internal)
|
483
|
+
end
|
484
|
+
|
485
|
+
with_internal_encoding 'us-ascii' do
|
486
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
487
|
+
result[field].encoding.should eql(Encoding.default_internal)
|
488
|
+
end
|
452
489
|
end
|
453
490
|
end
|
454
491
|
end
|