背景

公司 hive 连接方式调整,手上有些古老代码,仍在使用着 SQLAlchemy + PyHive 的连接方式。建立连接到方式如下

1
2
3
4
5
6
from sqlalchemy.engine import create_engine


test_db = create_engine('hive://{}@{}:{}/{}?auth={}'.format(
    HIVE_CONFIG['user'], HIVE_CONFIG['host'], HIVE_CONFIG['port'], 'test', HIVE_CONFIG['auth'], HIVE_CONFIG['user']),
)

其中 Auth 发生了变化,从 PLAIN 切换为 NOSASL

发生了以下问题

问题定位

  • 首先问题中报错是 User Hive 没有权限。
  • 当时看到这个报错就有点晕,这就很奇怪了,我改了 host、port 和 auth,怎么 user 是没变的,怎么就从我之前的 user 换成一个默认 user hive。
  • 跟踪了一下发现了这里
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Connection(object):
    def __init__(self, host, port=10000, username=None, database='default', auth='NONE', configuration=None):
        # 其他初始化代码
        
        if auth == 'NOSASL':
            # NOSASL corresponds to hive.server2.authentication=NOSASL in hive-site.xml
            self._transport = thrift.transport.TTransport.TBufferedTransport(socket)
        elif # 其他 auth 分支 ...

        # 协议信息处理

        try:
            # 问题就出现在这里,TOpenSessionReq 中没有传递 username,导致请求 hive 的过程中,使用了默认的用户 hive
            self._transport.open()
            open_session_req = ttypes.TOpenSessionReq(
                client_protocol=protocol_version,
                configuration=configuration,
            )
            # 其他逻辑
        except:
            self._transport.close()
            raise

其中 TOpenSessionReq 在小于 5.1 版本的情况下,是不传递 username 的。仅在 NONE 的情况下,通过 TSaslClientTransport 写入 username。从而之前的鉴权模式,username 是正确的,调整成为 NOSASL 后,出现问题

修改方式,TOpenSessionReq 中传递 usernmae

其中完整代码见 https://github.com/dropbox/PyHive/blob/v0.2.1/pyhive/hive.py#L69-L119

该问题修复

  • 如果代码可以使用 PyHive 大于 5.1 版本以上的业务,升级即可
  • 我这里有点不行,由于 5.1 要求 thrift 大于 10.0.0 不能使用,所有只能在 2.1 上加一个改动
  • 一个新的库,用于处理这个逻辑 https://pypi.org/simple/pyhive-hack/