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
         |