# encoding: UTF-8
require './spec/spec_helper.rb'

RSpec.describe Mysql2::Statement do
  before :each do
    @client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
  end

  it "should create a statement" do
    statement = nil
    expect { statement = @client.prepare 'SELECT 1' }.to change {
      @client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
    }.by(1)
    expect(statement).to be_an_instance_of(Mysql2::Statement)
  end

  it "should raise an exception when server disconnects" do
    @client.close
    expect { @client.prepare 'SELECT 1' }.to raise_error(Mysql2::Error)
  end

  it "should tell us the param count" do
    statement = @client.prepare 'SELECT ?, ?'
    expect(statement.param_count).to eq(2)

    statement2 = @client.prepare 'SELECT 1'
    expect(statement2.param_count).to eq(0)
  end

  it "should tell us the field count" do
    statement = @client.prepare 'SELECT ?, ?'
    expect(statement.field_count).to eq(2)

    statement2 = @client.prepare 'SELECT 1'
    expect(statement2.field_count).to eq(1)
  end

  it "should let us execute our statement" do
    statement = @client.prepare 'SELECT 1'
    expect(statement.execute).not_to eq(nil)
  end

  it "should raise an exception without a block" do
    statement = @client.prepare 'SELECT 1'
    expect { statement.execute.each }.to raise_error(LocalJumpError)
  end

  it "should tell us the result count" do
    statement = @client.prepare 'SELECT 1'
    result = statement.execute
    expect(result.count).to eq(1)
  end

  it "should let us iterate over results" do
    statement = @client.prepare 'SELECT 1'
    result = statement.execute
    rows = []
    result.each { |r| rows << r }
    expect(rows).to eq([{ "1" => 1 }])
  end

  it "should keep its result after other query" do
    @client.query 'USE test'
    @client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int)'
    @client.query 'INSERT INTO mysql2_stmt_q (a) VALUES (1), (2)'
    stmt = @client.prepare('SELECT a FROM mysql2_stmt_q WHERE a = ?')
    result1 = stmt.execute(1)
    result2 = stmt.execute(2)
    expect(result2.first).to eq("a" => 2)
    expect(result1.first).to eq("a" => 1)
    @client.query 'DROP TABLE IF EXISTS mysql2_stmt_q'
  end

  it "should be reusable 1000 times" do
    statement = @client.prepare 'SELECT 1'
    1000.times do
      result = statement.execute
      expect(result.to_a.length).to eq(1)
    end
  end

  it "should be reusable 10000 times" do
    statement = @client.prepare 'SELECT 1'
    10000.times do
      result = statement.execute
      expect(result.to_a.length).to eq(1)
    end
  end

  it "should handle comparisons and likes" do
    @client.query 'USE test'
    @client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int, b varchar(10))'
    @client.query 'INSERT INTO mysql2_stmt_q (a, b) VALUES (1, "Hello"), (2, "World")'
    statement = @client.prepare 'SELECT * FROM mysql2_stmt_q WHERE a < ?'
    results = statement.execute(2)
    expect(results.first).to eq("a" => 1, "b" => "Hello")

    statement = @client.prepare 'SELECT * FROM mysql2_stmt_q WHERE b LIKE ?'
    results = statement.execute('%orld')
    expect(results.first).to eq("a" => 2, "b" => "World")

    @client.query 'DROP TABLE IF EXISTS mysql2_stmt_q'
  end

  it "should select dates" do
    statement = @client.prepare 'SELECT NOW()'
    result = statement.execute
    expect(result.first.first[1]).to be_an_instance_of(Time)
  end

  it "should tell us about the fields" do
    statement = @client.prepare 'SELECT 1 as foo, 2'
    statement.execute
    list = statement.fields
    expect(list.length).to eq(2)
    expect(list.first).to eq('foo')
    expect(list[1]).to eq('2')
  end

  it "should update a DECIMAL value passing a BigDecimal" do
    @client.query 'USE test'
    @client.query 'DROP TABLE IF EXISTS mysql2_stmt_decimal_test'
    @client.query 'CREATE TABLE mysql2_stmt_decimal_test (decimal_test DECIMAL(10,3))'

    @client.prepare("INSERT INTO mysql2_stmt_decimal_test VALUES (?)").execute(BigDecimal.new("123.45"))

    test_result = @client.query("SELECT * FROM mysql2_stmt_decimal_test").first
    expect(test_result['decimal_test']).to eql(123.45)
  end

  context "utf8_db" do
    before(:each) do
      @client.query("DROP DATABASE IF EXISTS test_mysql2_stmt_utf8")
      @client.query("CREATE DATABASE test_mysql2_stmt_utf8")
      @client.query("USE test_mysql2_stmt_utf8")
      @client.query("CREATE TABLE テーブル (整数 int, 文字列 varchar(32)) charset=utf8")
      @client.query("INSERT INTO テーブル (整数, 文字列) VALUES (1, 'イチ'), (2, '弐'), (3, 'さん')")
    end

    after(:each) do
      @client.query("DROP DATABASE test_mysql2_stmt_utf8")
    end

    it "should be able to retrieve utf8 field names correctly" do
      stmt = @client.prepare 'SELECT * FROM `テーブル`'
      expect(stmt.fields).to eq(%w(整数 文字列))
      result = stmt.execute

      expect(result.to_a).to eq([{ "整数" => 1, "文字列" => "イチ" }, { "整数" => 2, "文字列" => "弐" }, { "整数" => 3, "文字列" => "さん" }])
    end

    it "should be able to retrieve utf8 param query correctly" do
      stmt = @client.prepare 'SELECT 整数 FROM テーブル WHERE 文字列 = ?'
      expect(stmt.param_count).to eq(1)

      result = stmt.execute 'イチ'

      expect(result.to_a).to eq([{ "整数" => 1 }])
    end

    it "should be able to retrieve query with param in different encoding correctly" do
      stmt = @client.prepare 'SELECT 整数 FROM テーブル WHERE 文字列 = ?'
      expect(stmt.param_count).to eq(1)

      param = 'イチ'.encode("EUC-JP")
      result = stmt.execute param

      expect(result.to_a).to eq([{ "整数" => 1 }])
    end
  end if defined? Encoding

  context "streaming result" do
    it "should be able to stream query result" do
      n = 1
      stmt = @client.prepare("SELECT 1 UNION SELECT 2")

      @client.query_options.merge!(:stream => true, :cache_rows => false, :as => :array)

      stmt.execute.each do |r|
        case n
        when 1
          expect(r).to eq([1])
        when 2
          expect(r).to eq([2])
        else
          violated "returned more than two rows"
        end
        n += 1
      end
    end
  end

  context "#each" do
    # note: The current impl. of prepared statement requires results to be cached on #execute except for streaming queries
    #       The drawback of this is that args of Result#each is ignored...

    it "should yield rows as hash's" do
      @result = @client.prepare("SELECT 1").execute
      @result.each do |row|
        expect(row).to be_an_instance_of(Hash)
      end
    end

    it "should yield rows as hash's with symbol keys if :symbolize_keys was set to true" do
      @client.query_options[:symbolize_keys] = true
      @result = @client.prepare("SELECT 1").execute
      @result.each do |row|
        expect(row.keys.first).to be_an_instance_of(Symbol)
      end
      @client.query_options[:symbolize_keys] = false
    end

    it "should be able to return results as an array" do
      @client.query_options[:as] = :array

      @result = @client.prepare("SELECT 1").execute
      @result.each do |row|
        expect(row).to be_an_instance_of(Array)
      end

      @client.query_options[:as] = :hash
    end

    it "should cache previously yielded results by default" do
      @result = @client.prepare("SELECT 1").execute
      expect(@result.first.object_id).to eql(@result.first.object_id)
    end

    it "should yield different value for #first if streaming" do
      @client.query_options[:stream] = true
      @client.query_options[:cache_rows] = false

      result = @client.prepare("SELECT 1 UNION SELECT 2").execute
      expect(result.first).not_to eql(result.first)

      @client.query_options[:stream] = false
      @client.query_options[:cache_rows] = true
    end

    it "should yield the same value for #first if streaming is disabled" do
      @client.query_options[:stream] = false
      result = @client.prepare("SELECT 1 UNION SELECT 2").execute
      expect(result.first).to eql(result.first)
    end

    it "should throw an exception if we try to iterate twice when streaming is enabled" do
      @client.query_options[:stream] = true
      @client.query_options[:cache_rows] = false

      result = @client.prepare("SELECT 1 UNION SELECT 2").execute

      expect {
        result.each {}
        result.each {}
      }.to raise_exception(Mysql2::Error)

      @client.query_options[:stream] = false
      @client.query_options[:cache_rows] = true
    end
  end

  context "#fields" do
    before(:each) do
      @client.query "USE test"
      @test_result = @client.prepare("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").execute
    end

    it "method should exist" do
      expect(@test_result).to respond_to(:fields)
    end

    it "should return an array of field names in proper order" do
      result = @client.prepare("SELECT 'a', 'b', 'c'").execute
      expect(result.fields).to eql(%w(a b c))
    end
  end

  context "row data type mapping" do
    before(:each) do
      @client.query "USE test"
      @test_result = @client.prepare("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").execute.first
    end

    it "should return nil for a NULL value" do
      expect(@test_result['null_test']).to be_an_instance_of(NilClass)
      expect(@test_result['null_test']).to eql(nil)
    end

    it "should return String for a BIT(64) value" do
      expect(@test_result['bit_test']).to be_an_instance_of(String)
      expect(@test_result['bit_test']).to eql("\000\000\000\000\000\000\000\005")
    end

    it "should return String for a BIT(1) value" do
      expect(@test_result['single_bit_test']).to be_an_instance_of(String)
      expect(@test_result['single_bit_test']).to eql("\001")
    end

    it "should return Fixnum for a TINYINT value" do
      expect([Fixnum, Bignum]).to include(@test_result['tiny_int_test'].class)
      expect(@test_result['tiny_int_test']).to eql(1)
    end

    it "should return TrueClass or FalseClass for a TINYINT value if :cast_booleans is enabled" do
      @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (1)'
      id1 = @client.last_id
      @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (0)'
      id2 = @client.last_id
      @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (-1)'
      id3 = @client.last_id

      result1 = @client.query 'SELECT bool_cast_test FROM mysql2_test WHERE bool_cast_test = 1 LIMIT 1', :cast_booleans => true
      result2 = @client.query 'SELECT bool_cast_test FROM mysql2_test WHERE bool_cast_test = 0 LIMIT 1', :cast_booleans => true
      result3 = @client.query 'SELECT bool_cast_test FROM mysql2_test WHERE bool_cast_test = -1 LIMIT 1', :cast_booleans => true
      expect(result1.first['bool_cast_test']).to be true
      expect(result2.first['bool_cast_test']).to be false
      expect(result3.first['bool_cast_test']).to be true

      @client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2},#{id3})"
    end

    it "should return TrueClass or FalseClass for a BIT(1) value if :cast_booleans is enabled" do
      @client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (1)'
      id1 = @client.last_id
      @client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (0)'
      id2 = @client.last_id

      result1 = @client.query "SELECT single_bit_test FROM mysql2_test WHERE id = #{id1}", :cast_booleans => true
      result2 = @client.query "SELECT single_bit_test FROM mysql2_test WHERE id = #{id2}", :cast_booleans => true
      expect(result1.first['single_bit_test']).to be true
      expect(result2.first['single_bit_test']).to be false

      @client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2})"
    end

    it "should return Fixnum for a SMALLINT value" do
      expect([Fixnum, Bignum]).to include(@test_result['small_int_test'].class)
      expect(@test_result['small_int_test']).to eql(10)
    end

    it "should return Fixnum for a MEDIUMINT value" do
      expect([Fixnum, Bignum]).to include(@test_result['medium_int_test'].class)
      expect(@test_result['medium_int_test']).to eql(10)
    end

    it "should return Fixnum for an INT value" do
      expect([Fixnum, Bignum]).to include(@test_result['int_test'].class)
      expect(@test_result['int_test']).to eql(10)
    end

    it "should return Fixnum for a BIGINT value" do
      expect([Fixnum, Bignum]).to include(@test_result['big_int_test'].class)
      expect(@test_result['big_int_test']).to eql(10)
    end

    it "should return Fixnum for a YEAR value" do
      expect([Fixnum, Bignum]).to include(@test_result['year_test'].class)
      expect(@test_result['year_test']).to eql(2009)
    end

    it "should return BigDecimal for a DECIMAL value" do
      expect(@test_result['decimal_test']).to be_an_instance_of(BigDecimal)
      expect(@test_result['decimal_test']).to eql(10.3)
    end

    it "should return Float for a FLOAT value" do
      expect(@test_result['float_test']).to be_an_instance_of(Float)
      expect(@test_result['float_test']).to be_within(1e-5).of(10.3)
    end

    it "should return Float for a DOUBLE value" do
      expect(@test_result['double_test']).to be_an_instance_of(Float)
      expect(@test_result['double_test']).to eql(10.3)
    end

    it "should return Time for a DATETIME value when within the supported range" do
      expect(@test_result['date_time_test']).to be_an_instance_of(Time)
      expect(@test_result['date_time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00')
    end

    if 1.size == 4 # 32bit
      klass = if RUBY_VERSION =~ /1.8/
        DateTime
      else
        Time
      end

      it "should return DateTime when timestamp is < 1901-12-13 20:45:52" do
        # 1901-12-13T20:45:52 is the min for 32bit Ruby 1.8
        r = @client.query("SELECT CAST('1901-12-13 20:45:51' AS DATETIME) as test")
        expect(r.first['test']).to be_an_instance_of(klass)
      end

      it "should return DateTime when timestamp is > 2038-01-19T03:14:07" do
        # 2038-01-19T03:14:07 is the max for 32bit Ruby 1.8
        r = @client.query("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test")
        expect(r.first['test']).to be_an_instance_of(klass)
      end
    elsif 1.size == 8 # 64bit
      if RUBY_VERSION =~ /1.8/
        it "should return Time when timestamp is > 0138-12-31 11:59:59" do
          r = @client.query("SELECT CAST('0139-1-1 00:00:00' AS DATETIME) as test")
          expect(r.first['test']).to be_an_instance_of(Time)
        end

        it "should return DateTime when timestamp is < 0139-1-1T00:00:00" do
          r = @client.query("SELECT CAST('0138-12-31 11:59:59' AS DATETIME) as test")
          expect(r.first['test']).to be_an_instance_of(DateTime)
        end

        it "should return Time when timestamp is > 2038-01-19T03:14:07" do
          r = @client.query("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test")
          expect(r.first['test']).to be_an_instance_of(Time)
        end
      else
        it "should return Time when timestamp is < 1901-12-13 20:45:52" do
          r = @client.query("SELECT CAST('1901-12-13 20:45:51' AS DATETIME) as test")
          expect(r.first['test']).to be_an_instance_of(Time)
        end

        it "should return Time when timestamp is > 2038-01-19T03:14:07" do
          r = @client.query("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test")
          expect(r.first['test']).to be_an_instance_of(Time)
        end
      end
    end

    it "should return Time for a TIMESTAMP value when within the supported range" do
      expect(@test_result['timestamp_test']).to be_an_instance_of(Time)
      expect(@test_result['timestamp_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00')
    end

    it "should return Time for a TIME value" do
      expect(@test_result['time_test']).to be_an_instance_of(Time)
      expect(@test_result['time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2000-01-01 11:44:00')
    end

    it "should return Date for a DATE value" do
      expect(@test_result['date_test']).to be_an_instance_of(Date)
      expect(@test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04')
    end

    it "should return String for an ENUM value" do
      expect(@test_result['enum_test']).to be_an_instance_of(String)
      expect(@test_result['enum_test']).to eql('val1')
    end

    it "should raise an error given an invalid DATETIME" do
      expect { @client.query("SELECT CAST('1972-00-27 00:00:00' AS DATETIME) as bad_datetime").each }.to \
        raise_error(Mysql2::Error, "Invalid date in field 'bad_datetime': 1972-00-27 00:00:00")
    end

    context "string encoding for ENUM values" do
      before { pending('Encoding is undefined') unless defined?(Encoding) }

      it "should default to the connection's encoding if Encoding.default_internal is nil" do
        with_internal_encoding nil do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['enum_test'].encoding).to eql(Encoding::UTF_8)

          client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
          result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['enum_test'].encoding).to eql(Encoding::US_ASCII)
          client2.close
        end
      end

      it "should use Encoding.default_internal" do
        with_internal_encoding Encoding::UTF_8 do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['enum_test'].encoding).to eql(Encoding.default_internal)
        end

        with_internal_encoding Encoding::ASCII do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['enum_test'].encoding).to eql(Encoding.default_internal)
        end
      end
    end

    it "should return String for a SET value" do
      expect(@test_result['set_test']).to be_an_instance_of(String)
      expect(@test_result['set_test']).to eql('val1,val2')
    end

    context "string encoding for SET values" do
      before { pending('Encoding is undefined') unless defined?(Encoding) }

      it "should default to the connection's encoding if Encoding.default_internal is nil" do
        with_internal_encoding nil do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['set_test'].encoding).to eql(Encoding::UTF_8)

          client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
          result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['set_test'].encoding).to eql(Encoding::US_ASCII)
          client2.close
        end
      end

      it "should use Encoding.default_internal" do
        with_internal_encoding Encoding::UTF_8 do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['set_test'].encoding).to eql(Encoding.default_internal)
        end

        with_internal_encoding Encoding::ASCII do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['set_test'].encoding).to eql(Encoding.default_internal)
        end
      end
    end

    it "should return String for a BINARY value" do
      expect(@test_result['binary_test']).to be_an_instance_of(String)
      expect(@test_result['binary_test']).to eql("test#{"\000" * 6}")
    end

    context "string encoding for BINARY values" do
      before { pending('Encoding is undefined') unless defined?(Encoding) }

      it "should default to binary if Encoding.default_internal is nil" do
        with_internal_encoding nil do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
        end
      end

      it "should not use Encoding.default_internal" do
        with_internal_encoding Encoding::UTF_8 do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
        end

        with_internal_encoding Encoding::ASCII do
          result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
          expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
        end
      end
    end

    {
      'char_test' => 'CHAR',
      'varchar_test' => 'VARCHAR',
      'varbinary_test' => 'VARBINARY',
      'tiny_blob_test' => 'TINYBLOB',
      'tiny_text_test' => 'TINYTEXT',
      'blob_test' => 'BLOB',
      'text_test' => 'TEXT',
      'medium_blob_test' => 'MEDIUMBLOB',
      'medium_text_test' => 'MEDIUMTEXT',
      'long_blob_test' => 'LONGBLOB',
      'long_text_test' => 'LONGTEXT',
    }.each do |field, type|
      it "should return a String for #{type}" do
        expect(@test_result[field]).to be_an_instance_of(String)
        expect(@test_result[field]).to eql("test")
      end

      context "string encoding for #{type} values" do
        before { pending('Encoding is undefined') unless defined?(Encoding) }

        if %w(VARBINARY TINYBLOB BLOB MEDIUMBLOB LONGBLOB).include?(type)
          it "should default to binary if Encoding.default_internal is nil" do
            with_internal_encoding nil do
              result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
              expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
            end
          end

          it "should not use Encoding.default_internal" do
            with_internal_encoding Encoding::UTF_8 do
              result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
              expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
            end

            with_internal_encoding Encoding::ASCII do
              result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
              expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
            end
          end
        else
          it "should default to utf-8 if Encoding.default_internal is nil" do
            with_internal_encoding nil do
              result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
              expect(result[field].encoding).to eql(Encoding::UTF_8)

              client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
              result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
              expect(result[field].encoding).to eql(Encoding::US_ASCII)
              client2.close
            end
          end

          it "should use Encoding.default_internal" do
            with_internal_encoding Encoding::UTF_8 do
              result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
              expect(result[field].encoding).to eql(Encoding.default_internal)
            end

            with_internal_encoding Encoding::ASCII do
              result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
              expect(result[field].encoding).to eql(Encoding.default_internal)
            end
          end
        end
      end
    end
  end

  context 'last_id' do
    before(:each) do
      @client.query 'USE test'
      @client.query 'CREATE TABLE IF NOT EXISTS lastIdTest (`id` BIGINT NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))'
    end

    after(:each) do
      @client.query 'DROP TABLE lastIdTest'
    end

    it 'should return last insert id' do
      stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
      expect(stmt.last_id).to eq 0
      stmt.execute 1
      expect(stmt.last_id).to eq 1
    end

    it 'should handle bigint ids' do
      stmt = @client.prepare 'INSERT INTO lastIdTest (id, blah) VALUES (?, ?)'
      stmt.execute 5000000000, 5000
      expect(stmt.last_id).to eql(5000000000)

      stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
      stmt.execute 5001
      expect(stmt.last_id).to eql(5000000001)
    end
  end

  context 'affected_rows' do
    before :each do
      @client.query 'USE test'
      @client.query 'CREATE TABLE IF NOT EXISTS lastIdTest (`id` BIGINT NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))'
    end

    after :each do
      @client.query 'DROP TABLE lastIdTest'
    end

    it 'should return number of rows affected by an insert' do
      stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
      expect(stmt.affected_rows).to eq 0
      stmt.execute 1
      expect(stmt.affected_rows).to eq 1
    end

    it 'should return number of rows affected by an update' do
      stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
      stmt.execute 1
      expect(stmt.affected_rows).to eq 1
      stmt.execute 2
      expect(stmt.affected_rows).to eq 1

      stmt = @client.prepare 'UPDATE lastIdTest SET blah=? WHERE blah=?'
      stmt.execute 0, 1
      expect(stmt.affected_rows).to eq 1
    end

    it 'should return number of rows affected by a delete' do
      stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
      stmt.execute 1
      expect(stmt.affected_rows).to eq 1
      stmt.execute 2
      expect(stmt.affected_rows).to eq 1

      stmt = @client.prepare 'DELETE FROM lastIdTest WHERE blah=?'
      stmt.execute 1
      expect(stmt.affected_rows).to eq 1
    end
  end

  context 'close' do
    it 'should free server resources' do
      stmt = @client.prepare 'SELECT 1'
      expect { stmt.close }.to change {
        @client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
      }.by(-1)
    end

    it 'should raise an error on subsequent execution' do
      stmt = @client.prepare 'SELECT 1'
      stmt.close
      expect { stmt.execute }.to raise_error(Mysql2::Error, /Invalid statement handle/)
    end
  end
end
