本文来自网易云社区
作者:王潘安
执行阶段
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('