本文来自网易云社区

作者:王潘安

执行阶段

launchTask    回到Driver类的runInternal方法,看以下执行过程。在runInternal方法中,执行过程调用了execute方法。execute方法里面的内容很多,但是跟我们有关系的就只有launchTask方法。这个方法里面有这么关键的几步:

     tsk.initialize(conf, plan, cxt);
    TaskResult tskRes = new TaskResult();
    TaskRunner tskRun = new TaskRunner(tsk, tskRes);     cxt.launching(tskRun);     tskRun.runSequential();

跟进runSequential方法发现调用了如下方法:

 exitVal = tsk.executeTask();

接着跟进,发现执行了这段代码:

     int retval = execute(driverContext);

这个execute 方法在执行show create table xx命令时就是执行的DDLTask类中的execute方法。

跟进execute方法找到如下代码:

       ShowCreateTableDesc showCreateTbl = work.getShowCreateTblDesc();
      if (showCreateTbl != null) {
        return showCreateTable(db, showCreateTbl);
      }

查看showCreateTable方法,发现它干的就是把返回结果的字段都拼接成模板,然后把从metastore里面拿到的内容塞进去,最后写到一个临时文件流里面。我们发现,它最后是这样写到文件流的:

 outStream.writeBytes(createTab_stmt.render());

中文在这个地方估计被写成乱码了,于是把它改为:

     outStream.write(createTab_stmt.render().getBytes("UTF-8"));

重新编译一下hive:

 mvn clean package -Phadoop-2 -DskipTests

把编译完成后的hive源码的ql/target目录的hive-exec-1.2.1.jar替换到运行的hive的lib目录中,建一个测试表,不用json序列化反序列化,发现show create table xx命令的字段中文注释正常了。但是如果测试表仍用json序列化和反序列化,那么仍然会出现注释为from deserializer的现象。

我们回到代码,看看在showCreateTable方法中究竟是如何获取字段的注释信息的。找到如下这段代码:

     List<FieldSchema> cols = tbl.getCols();

跟进去发现,如果设置了自定义的序列化与反序列化类,就会执行这行操作:

 return MetaStoreUtils.getFieldsFromDeserializer(getTableName(), getDeserializer());

跟进getFieldsFromDeserializer方法,我们发现如下几行重要代码:

 ObjectInspector oi = deserializer.getObjectInspector();
  List<? extends StructField> fields = ((StructObjectInspector) oi).getAllStructFieldRefs();
  for (int i = 0; i < fields.size(); i++) {
    StructField structField = fields.get(i);
    String fieldName = structField.getFieldName();
    String fieldTypeName = structField.getFieldObjectInspector().getTypeName();
    String fieldComment = determineFieldComment(structField.getFieldComment());     str_fields.add(new FieldSchema(fieldName, fieldTypeName, fieldComment));
  }

也就是说注释是从deserializer中拿出来的。那我们在返回去看看,hive给我们的json deserializer传了什么参数。返回到上一段代码,我们看getDeserializer方法干了什么:

     deserializer = getDeserializerFromMetaStore(false);

我们最好在这打个断点,看看,跟进代码发现执行了:

     return MetaStoreUtils.getDeserializer(SessionState.getSessionConf(), tTable, skipConfError);

然后通过反射建了一个Deserializer的实例,并且调用了它的initialize方法:

       Deserializer deserializer = ReflectionUtil.newInstance(conf.getClassByName(lib).
              asSubclass(Deserializer.class), conf);
       SerDeUtils.initializeSerDeWithoutErrorCheck(deserializer, conf,
                MetaStoreUtils.getTableMetadata(table), null);

在跟进initializeSerDeWithoutErrorCheck方法,发现它执行了:

 deserializer.initialize(conf, createOverlayedProperties(tblProps, partProps));

我们在跟进以下MetaStoreUtils.getTableMetadata(table)发现它执行了MetaStoreUtils.getSchema这个方法。跟进去,我们发现了至关重要的代码,注意所有的奥妙都在这:

     for (FieldSchema col : tblsd.getCols()) {
      if (!first) {
        colNameBuf.append(",");
        colTypeBuf.append(":");
        colComment.append('\0');
      }
      colNameBuf.append(col.getName());
      colTypeBuf.append(col.getType());
      colComment.append((null != col.getComment()) ? col.getComment() : "");
      first = false;
    }
    String colNames = colNameBuf.toString();
    String colTypes = colTypeBuf.toString();
    schema.setProperty(
        org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.META_TABLE_COLUMNS,
        colNames);
    schema.setProperty(
        org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.META_TABLE_COLUMN_TYPES,
        colTypes);
    schema.setProperty("columns.comments", colComment.toString());

也就是说,Hive是给序列化反序列化类传了注释的信息,首先注释的信息是以\0分割的,其次它是放在key值为columns.comments的property中。

Hive-JSON-Serde调试

然后我们就要打开Hive-JSON-Serde看看它怎么处理这些信息的。很容易找到类JsonSerDe。看看它的initialize方法:

         String columnNameProperty = tbl.getProperty(Constants.LIST_COLUMNS);
        String columnTypeProperty = tbl.getProperty(Constants.LIST_COLUMN_TYPES);

它根本就没有拿注释信息!然后看它怎么生成的rowObjectInspector:

 rowObjectInspector = (StructObjectInspector) JsonObjectInspectorFactory
                .getJsonObjectInspectorFromTypeInfo(rowTypeInfo, options);

跟入getJsonObjectInspectorFromTypeInfo方法,找到:

                     result = JsonObjectInspectorFactory.getJsonStructObjectInspector(fieldNames,
                            fieldObjectInspectors, options);

接着跟进去,发现:

             result = new JsonStructObjectInspector(structFieldNames,
                    structFieldObjectInspectors, options);

我们看看这个JsonStructObjectInspector类,它是继承的StandardStructObjectInspector,它的构造函数调用了父类的:

   protected StandardStructObjectInspector(List<String> structFieldNames,
      List<ObjectInspector> structFieldObjectInspectors) {
    init(structFieldNames, structFieldObjectInspectors, null);
  }

一看init函数最后传入的参数是null,就知道问题出在这了,这个父类其实还有另外一个构造方法:

   protected StandardStructObjectInspector(List<String> structFieldNames,
      List<ObjectInspector> structFieldObjectInspectors,
      List<String> structFieldComments) {
    init(structFieldNames, structFieldObjectInspectors, structFieldComments);
  }

也就是说,它是允许传入注释信息的。那么我们的思路就明确了,第一,把未解析出来的注释信息解析出来。第二,把这个注释信息传入JsonStructObjectInspector的构造函数中:

 String columnCommentProperty = tbl.getProperty("columns.comments");
 if (columnCommentProperty != null){
        if (columnCommentProperty.length() == 0) {
            columnComments = new ArrayList<String>();
        } else {
            columnComments = Arrays.asList(columnCommentProperty.split("\0", columnNames.size()));
        }
 }

这里有一点要注意:在StandardStructObjectInspector类中,它会强制检查字段数与注释数相等,所以在做split操作时,一定要传2个参数,把注释为空的字段补全,否则要出bug。后面的操作就是把这个注释传参到各个函数中去,这里就不在多述。

然后把JsonStructObjectInspector的构造函数改为:

       public JsonStructObjectInspector(List<String> structFieldNames,
            List<ObjectInspector> structFieldObjectInspectors, List<String> structFieldComments, JsonStructOIOptions opts) {
        super(structFieldNames, structFieldObjectInspectors, structFieldComments);
        
        options = opts;
    }

最后,重新编译Hive-JSON-Serde:

 mvn -Phdp23 clean package

这段代码改动比较多。

3.总结

其实Hive中文注释乱码就两个原因造成的。一个是Hive在写注释到流中时,没有把编码格式转为UTF-8。在第三方插件Hive-JSON-Serde中,没有将注释保存下来,如果注释为空,Hive会自动补上from deserializer的字符串。

  因此只需要改动下面一小点即可,首先在Hive的源码中,找到ql目录,找到org.apache.hadoop.hive.ql.exec中的DDLTask类,找到showCreateTable方法。修改第2110行的代码:

 outStream.writeBytes(createTab_stmt.render());

为:

     outStream.write(createTab_stmt.render().getBytes("UTF-8"));

  对于Hive-JSON-Serde来说,则是在它的JsonSerDe类的initialize方法中加入解析字段注释的代码:

  String columnCommentProperty = tbl.getProperty("columns.comments");
 if (columnCommentProperty != null){
        if (columnCommentProperty.length() == 0) {
            columnComments = new ArrayList<String>();
        } else {
            columnComments = Arrays.asList(columnCommentProperty.split("\0", columnNames.size()));
        }
 }

并且在构建rowObjectInspector的时候将注释信息传入:

         rowObjectInspector = (StructObjectInspector) JsonObjectInspectorFactory
                .getJsonObjectInspectorFromTypeInfo(rowTypeInfo, columnComments, options);

然后把JsonStructObjectInspector的构造函数改为:

        public JsonStructObjectInspector(List<String> structFieldNames,
            List<ObjectInspector> structFieldObjectInspectors, List<String> structFieldComments, JsonStructOIOptions opts) {
        super(structFieldNames, structFieldObjectInspectors, structFieldComments);
        
        options = opts;
    }

相关阅读:Hive中文注释乱码解决方案(1)

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 wireshark抓包分析——TCP/IP协议

Hive中文注释乱码解决方案(2)的更多相关文章

  1. Hive中文注释乱码解决方案

    本文来自网易云社区 作者:王潘安 快速解决方法 目前的hive客户端在执行desc tablexxx和show create table xxx命令的时候,字段的中文注释会出现乱码情况,如(????) ...

  2. 【原创】大叔经验分享(34)hive中文注释乱码

    在hive中查看表结构时中文注释乱码,分为两种情况,一种是desc $table,一种是show create table $table 1 数据库字符集 检查 mysql> show vari ...

  3. Ubuntu下Matlab代码中中文注释乱码解决方案

    环境:Ubuntu18.04,Matlab R2017b. 把matlab文件从windows拷贝到Ubuntu中,打开发现原先的中文注释全部乱码.真正原因是因为windows中.m文件采用的是gbk ...

  4. Intellij IDEA 导入 eclipese项目之后,中文注释乱码解决方案

    首先,看导入后整个IJ界面: 可以看到注释是乱码,要解决问题就跟我开始做吧,看右下角有个"UTF-8",点一下选择"GBk",选择"Reload&qu ...

  5. myeclipse10中文注释乱码问题

    将别人的项目或JAVA文件导入到自己的Eclipse中时,常常会出现JAVA文件的中文注释变成乱码的情况,主要原因就是别人的IDE编码格式和自己的Eclipse编码格式不同. 总结网上的建议和自己的体 ...

  6. Eclipse中文注释乱码解决

    将别人的项目或JAVA文件导入到自己的Eclipse中时,常常会出现JAVA文件的中文注释变成乱码的情况,主要原因就是别人的IDE编码格式和自己的Eclipse编码格式不同. 总结网上的建议和自己的体 ...

  7. MyEclipse中文注释乱码解决

    MyEclipse中文注释乱码解决 将别人的项目或JAVA文件导入到自己的Eclipse中时,常常会出现JAVA文件的中文注释变成乱码的情况,主要原因就是别人的IDE编码格式和自己的Eclipse编码 ...

  8. css中文字体乱码解决方案

    css中文字体乱码解决方案:把css编码和html页面编码统一起来.如果html页面是utf-8.css.js也统一成utf-8编码.还有一个避免中文乱码的办法就是把中文字体写成英文来表示 css中文 ...

  9. Source Insight中文注释乱码、字体大小、等宽解决方法

    中文注释乱码解决方法: 用记事本打开源文件,然后,选择文件->另存为,编码选为”ANSI“   字体的调整: Source Insight 菜单栏选择Options->Document O ...

随机推荐

  1. 如何正确在IDEA 里maven构建的项目中引入lib的jar包(图文详解)

    不多说,直接上干货! 问题详情 以下是我,maven构建出来的最新spark2.2.0-bin-hadoop2.6的项目. 有些依赖包,maven还是无法一次性满足,所以,得手动加入lib的jar包. ...

  2. android开发学习 ------- android studio 同时用svn和git 进行代码管理 出现的问题

    svn和git的工作机制:  SVN 是集中式或者有中心式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要 ...

  3. [牛客网试题] Test.main() 函数执行后的输出是()

    public class Test { public static void main(String [] args){ System.out.println(new B().getValue()); ...

  4. MFC程序添加快捷键

    [问题提出]     有的程序需要自定义组合键完成一定功能,如何实现? [解决方法]     RegisterHotKey函数原型及说明:     BOOL RegisterHotKey(     H ...

  5. python中中括号中的负数

    >>> a="a,b,c,d,e">>> a.split(",")[0:2]['a', 'b']>>> a ...

  6. COGS 133. [USACO Mar08] 牛跑步

    ★★★   输入文件:cowjog.in   输出文件:cowjog.out   简单对比时间限制:1 s   内存限制:128 MB Bessie准备用从牛棚跑到池塘的方法来锻炼. 但是因为她懒,她 ...

  7. 洛谷 P2617 Dynamic Ranking

    题目描述 给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤ ...

  8. openstack 存储节点按照报错Device /dev/sdb not found (or ignored by filtering).

    root@dell-PowerEdge-T30:~# pvcreate /dev/sdb  Device /dev/sdb not found (or ignored by filtering).首页 ...

  9. cannot load such file -- bundler/setup解决

    sudo gem install bundler bundle update celluloid

  10. 利用enum4linux 445端口+wordpress插件任意文件上传的一次渗透

    探测内网80端口发现目标IP 目标使用Apache  2.4.7web服务中间件 使用linux    Ubuntu系统 使用御剑扫描了目录 目录扫描到了 http://192.168.31.236/ ...