# encoding: UTF-8 module Mysql2 class Error < StandardError REPLACEMENT_CHAR = '?' ENCODE_OPTS = {:undef => :replace, :invalid => :replace, :replace => REPLACEMENT_CHAR} attr_accessor :error_number attr_reader :sql_state attr_writer :server_version # Mysql gem compatibility alias_method :errno, :error_number alias_method :error, :message def initialize(msg, server_version=nil) self.server_version = server_version super(clean_message(msg)) end def sql_state=(state) @sql_state = ''.respond_to?(:encode) ? state.encode(ENCODE_OPTS) : state end private # In MySQL 5.5+ error messages are always constructed server-side as UTF-8 # then returned in the encoding set by the `character_set_results` system # variable. # # See http://dev.mysql.com/doc/refman/5.5/en/charset-errors.html for # more context. # # Before MySQL 5.5 error message template strings are in whatever encoding # is associated with the error message language. # See http://dev.mysql.com/doc/refman/5.1/en/error-message-language.html # for more information. # # The issue is that the user-data inserted in the message could potentially # be in any encoding MySQL supports and is insert into the latin1, euckr or # koi8r string raw. Meaning there's a high probability the string will be # corrupt encoding-wise. # # See http://dev.mysql.com/doc/refman/5.1/en/charset-errors.html for # more information. # # So in an attempt to make sure the error message string is always in a valid # encoding, we'll assume UTF-8 and clean the string of anything that's not a # valid UTF-8 character. # # Except for if we're on 1.8, where we'll do nothing ;) # # Returns a valid UTF-8 string in Ruby 1.9+, the original string on Ruby 1.8 def clean_message(message) return message if !message.respond_to?(:encoding) if @server_version && @server_version > 50500 message.encode(ENCODE_OPTS) else if message.respond_to? :scrub message.scrub(REPLACEMENT_CHAR).encode(ENCODE_OPTS) else # This is ugly as hell but Ruby 1.9 doesn't provide a way to clean a string # and retain it's valid UTF-8 characters, that I know of. new_message = "".force_encoding(Encoding::UTF_8) message.chars.each do |char| if char.valid_encoding? new_message << char else new_message << REPLACEMENT_CHAR end end new_message.encode(ENCODE_OPTS) end end end end end