注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

mysql中查看blob内容  

2015-06-20 21:13:00|  分类: 数据库 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、需求
在不少数据库中都是用了blob字段,这些字段让程序解析的时候比较紧凑,扩展性也很强,但是通过mysql客户端查看数据时非常不方便,使用select无法展示出内部结构,这样在没有对应代码执行真正加载解析时就非常麻烦。
二、使用空格替换内存0操作
在mysql的代码中可以看到,大部分输出是通过tee_write输出的,函数中的一些代码为mysql-5.6.23\client\mysql.cc:
void tee_write(FILE *file, const char *s, size_t slen, int flags)
{
#ifdef __WIN__
  my_bool is_console= my_win_is_console_cached(file);
#endif
  const char *se;
  for (se= s + slen; s < se; s++)
  {
    const char *t;

    if (flags & MY_PRINT_MB)
    {
      int mblen;
      if (use_mb(charset_info) &&
          (mblen= my_ismbchar(charset_info, s, se)))
      {
#ifdef __WIN__
        if (is_console)
          my_win_console_write(charset_info, s, mblen);
        else
#endif
        fwrite(s, 1, mblen, file);
        if (opt_outfile)
          fwrite(s, 1, mblen, OUTFILE);
        s+= mblen - 1;
        continue;
      }
    }

    if ((flags & MY_PRINT_XML) && (t= array_value(xmlmeta, *s)))
      tee_fputs(t, file);
    else if ((flags & MY_PRINT_SPS_0) && *s == '\0')//MY_PRINT_SPS_0是默认置位的,所以内存零是被转义成了空格代替而不是打印出真正的0值。这也是我们默认看到的情况,就是blob中会出现大量的空格,只是它们通常在黑色的屏幕下十分不显眼,所以大家几乎没有人注意到它们
      tee_putc((int) ' ', file);   // This makes everything hard
    else if ((flags & MY_PRINT_ESC_0) && *s == '\0')
      tee_fputs("\\0", file);      // This makes everything hard
    else if ((flags & MY_PRINT_CTRL) && *s == '\t')
      tee_fputs("\\t", file);      // This would destroy tab format
    else if ((flags & MY_PRINT_CTRL) && *s == '\n')
      tee_fputs("\\n", file);      // This too
    else if ((flags & MY_PRINT_CTRL) && *s == '\\')
      tee_fputs("\\\\", file);
    else
    {
#ifdef __WIN__
      if (is_console)
        my_win_console_putc(charset_info, (int) *s);
      else
#endif
      putc((int) *s, file);
      if (opt_outfile)
        putc((int) *s, OUTFILE);
    }
  }
}
三、显示为真正的内存0
从上面的代码中可以看到,在没有任何flags的时候,可以打印出真正的零值,再一路看下去,需要在mysql启动时设置了--raw和--silent标志位,并且select的时候不能使用-G选项:
com_go(String *buffer,char *line __attribute__((unused)))
init_pager();
if (opt_html)
 print_table_data_html(result);
else if (opt_xml)
 print_table_data_xml(result);
  else if (vertical || (auto_vertical_output && (terminal_width < get_result_width(result))))
 print_table_data_vertically(result);
else if (opt_silent && verbose <= 2 && !output_tables)
 print_tab_data(result);
else
 print_table_data(result);

print_tab_data-->>safe_put_field

static void
safe_put_field(const char *pos,ulong length)
{
  if (!pos)
    tee_fputs("NULL", PAGER);
  else
  {
    int flags= MY_PRINT_MB | (opt_raw_data ? 0 : (MY_PRINT_ESC_0 | MY_PRINT_CTRL));
    /* Can't use tee_fputs(), it stops with NUL characters. */
    tee_write(PAGER, pos, length, flags);
  }
}
打印出来之后可以在windows下log输出到文件,然后文件再16进制显示,在windows下,几乎任何一个稍微强大的主流编辑器都有16进制显示的功能。或者在mysql下直接执行 mysql -rse 来select输出,再把输出管道到hexdump -C中,就可以看到16进制了,但是可以看到这种方法无论是操作还是查看都非常不方便。
四、mysql内置hex函数
mysql-5.6.23\sql\item_create.cc
  { { C_STRING_WITH_LEN("HEX") }, BUILDER(Create_func_hex)},

Item*
Create_func_hex::create(THD *thd, Item *arg1)
{
  return new (thd->mem_root) Item_func_hex(arg1);
}

String *Item_func_hex::val_str_ascii(String *str)
{
  String *res;
  DBUG_ASSERT(fixed == 1);
  if (args[0]->result_type() != STRING_RESULT)
  {
    ulonglong dec;
    char ans[65],*ptr;
    /* Return hex of unsigned longlong value */
    if (args[0]->result_type() == REAL_RESULT ||
        args[0]->result_type() == DECIMAL_RESULT)
    {
      double val= args[0]->val_real();
      if ((val <= (double) LONGLONG_MIN) || 
          (val >= (double) (ulonglong) ULONGLONG_MAX))
        dec=  ~(longlong) 0;
      else
        dec= (ulonglong) (val + (val > 0 ? 0.5 : -0.5));
    }
    else
      dec= (ulonglong) args[0]->val_int();

    if ((null_value= args[0]->null_value))
      return 0;
    
    if (!(ptr= longlong2str(dec, ans, 16)) ||
        str->copy(ans,(uint32) (ptr - ans),
        &my_charset_numeric))
      return make_empty_result(); // End of memory
    return str;
  }

  /* Convert given string to a hex string, character by character */
  res= args[0]->val_str(str);
  if (!res || tmp_value.alloc(res->length()*2+1))
  {
    null_value=1;
    return 0;
  }
  null_value=0;
  tmp_value.length(res->length()*2);
  tmp_value.set_charset(&my_charset_latin1);

  octet2hex((char*) tmp_value.ptr(), res->ptr(), res->length());
  return &tmp_value;
}

对于blob类型,它的派生关系为
class Item_blob :public Item_partition_func_safe_string
class Item_partition_func_safe_string: public Item_string

class Item_string :public Item_basic_constant

  type_conversion_status save_in_field(Field *field, bool no_conversions);
  enum Item_result result_type () const { return STRING_RESULT; }
  enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
所以不会进入Item_func_hex::val_str_ascii函数开始的 if (args[0]->result_type() != STRING_RESULT)分支。

五、总结
当然最好的方法还是使用hex(blobfield)这种方式来展示,如果是直接select blobfield,那只要把输出里面的空格换成零就可以了,虽然在securecrt里这些原是二进制可能有乱码,但是securecrt的日志输出中还是内存值,把这个日志文件用16进制显示就可以了,或者设置pager为hexdump -C。
  评论这张
 
阅读(381)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017