Ruby C扩展异常消息编码

时间:2015-11-25 08:35:53

标签: ruby exception encoding odbc

您好我最近使用ruby-odbc处理。

根据我的逻辑,ruby-odbc引发了异常,Active Record lib应该抓住它。但是这个异常消息的编码是ASCII-8BIT,这将引发另一个编码不兼容的异常。我现在不想修改Active Record或者改用tiny_tds(现在)。

所以我想知道是否有办法可以将异常消息的编码更改为给定的编码?

我尝试在传递给rb_raise之前转换错误消息,但它不起作用。结果仍然是ASCII-8BIT。 所以我做了一些愚蠢的事情导致anystring来自ruby c扩展是所有ASCII-8BIT还是我在转换期间犯了错误? 有什么想法吗?

=================追加整个背景=============== 我修改并重新编译了基于cloudvolumes / ruby​​-odbc的odbc.c文件,主要是在这个函数中:

static VALUE  stmt_prep_int(int argc, VALUE *argv, VALUE self, int mode)

对于以下所有句子:

rb_raise(Cerror, "%s",msg); 

我换成了:

rb_odbc_raise_error(Cerror,  msg);

这个新功能定义为:

static VALUE rb_odbc_raise_error(VALUE err,char * msg){
VALUE e;
VALUE vmsg = rb_str_new2(msg);
#ifdef USE_RB_ENC
rb_enc_associate_index(vmsg,rb_enc );
msg = rb_string_value_cstr(&vmsg);
#endif
e = rb_exc_new2(err,msg);
rb_exc_raise(e);
return Qnil;
}

注意,USE_RB_ENC和rb_enc(utf8)的定义来自repo:https://github.com/cloudvolumes/ruby-odbc

将引发异常的ruby脚本如下所示:

begin
  cfg = ActiveRecord::Base.connection_config
  if cfg && cfg[:adapter] == "sqlserver"
    ActiveRecord::Base.connection_pool.with_connection do |connection|
      domain_name   = ENV['USERDOMAIN']
      computer_name = "#{ENV['COMPUTERNAME']}中文$"
      name = "#{domain_name}\\#{computer_name}"
      puts "Granting database access to #{name.encode(Encoding.locale_charmap)}"
      connection.execute("CREATE LOGIN [#{name}] FROM WINDOWS")
  end
else
  puts "Database is not configured or is not SQL Server"
end
rescue Interrupt
  raise
rescue Exception => e
  #do something.
end

此脚本应引发

的例外
"42000 (15401) [Microsoft][ODBC SQL Server Driver][SQL Server]Windows NT user or group 'ZHUO\APPMANAGERDEV中文$' not found. Check the name again.".

但是,当下面的 activerecord-3.2.21 / lib / active_record / connection_adapters / abstract_adater.rb 捕获到异常时,由于rails设置为utf-8且e.message具有编码ASCII-8BIT,会有一个提示:

" Exception Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and UTF-8".

因此根本看不到原始错误消息。

    def log(sql, name = "SQL", binds = [])
      @instrumenter.instrument(
        "sql.active_record",
        :sql           => sql,
        :name          => name,
        :connection_id => object_id,
        :binds         => binds) { yield }
    rescue Exception => e
      message = "#{e.class.name}: #{e.message}: #{sql}"
      @logger.debug message if @logger
      exception = translate_exception(e, message)
      exception.set_backtrace e.backtrace
      raise exception
    end

1 个答案:

答案 0 :(得分:2)

您可以使用rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)