问题
由于传递 hive 分区数据到 cos,传递完数据后,需要修改 hive 表分区的 location。此时,有个问题需要考虑,hive 分区字段的类型,支持很多种,是不是一定得 严格按照 分区字段的实际类型,去写 修改 hive 分区 location 的 alter 语句呢?如果严格需要,那么就不得不连接 hive meta 数据库,做一些多表关联,才能获取对应的 分区类型信息,sql 如下:
SELECT
TBLS.TBL_NAME AS data_name,
PARTITION_KEYS.PKEY_NAME AS partition_name,
PARTITION_KEYS.PKEY_TYPE AS partition_type_name,
ifnull(PARTITION_KEYS.PKEY_COMMENT, '') AS partition_comment,
PARTITION_KEYS.INTEGER_IDX AS partition_position
FROM
TBLS
INNER JOIN DBS ON TBLS.DB_ID = DBS.DB_ID
INNER JOIN PARTITION_KEYS ON TBLS.TBl_id = PARTITION_KEYS.TBL_ID
WHERE
DBS.name = #{dbName}
ORDER BY
data_name,
partition_position ASC
使用这种方式,直接查询 hive meta 数据库,存在安全和性能风险,不是优选方案,因此,需要查看 hive 源码,研究下 alter 分区的 location 的底层实现原理。
源码分析
1.测试表 test_part 建表语句如下,包含两个 int 类型分区字段和一个string 类型的分区字段
2.远程 Debug 如下 sql
(1)year 按 int 类型的值指定
(2)month 按 string 类型指定
alter table test_part partition(year=2020, month='10', type='test') set location 'xxxx'
3.代码执行
(1)CliDriver main 方法是入口,执行的 sql 按行读取,交给 processLine 执行
DEBUG 信息
(2)processLine 方法,按照分号拆分 sql 命令,依次执行
(3)sql 会一层层传递处理(由于代码逻辑较多,此处只写和该问题相关的逻辑),最终,会调用 DDLTask 的 alterTable 方法
DEBUG 信息
(4)通过断点,可以看到,在 hive 内部是用 String 类型保存的 partSpec 信息,所以修改分区的时候,不需要区分类型(month 本来是 int 类型的,应该写为 month=10,而不是 month='10',但是由于底层用的 string 保存,所以写成 month=10 或 month='10' 都会被保存为 month='10', 所以,这两种写法都能执行成功)
写法 1:
alter table test_part partition(year=2020, month='10', type='test') set location 'xxxx';写法 2:
alter table test_part partition(year=2020, month=10, type='test') set location 'xxxx';
底层实际是按照 string 保存的 partSpec 信息
(5)那么这个 partSpec 哪里设置的?
传入的 sql 会被 Driver 进行 compile:首先生成 ASTNode,然后 analyze ASTNode(调用 analyzeInternal)
analyzeInternal 方法内部调用 getValidatedPartSpec
而 getValidatedPartSpec 会调用 getPartSpec 方法,读取 ASTNode 上的节点信息(保存了 分区字段),然后保存到 HashMap<String, String> 中
总结
hive 内部是用 String 类型保存的 partSpec 信息,所以写 alter 分区的 sql 语句时,语法上不区分区类型,统一用 string 是可以正常执行的(注意:分区的值,必须是对应类型的,值的类型是会被检测的,否则会抛出类型转换异常错误)