pythonからhiveserver2につなごうとしていろいろハマったのでメモっておく

CDH4.5のhiveserver2にpython 2.7+NOSASLでつなごうとしていろいろハマったのでメモっておきます。

Setting Up HiveServer2 - Apache Hive - Apache Software Foundation をみると GitHub - BradRuderman/pyhs2をインストールして使えばいいようだがうまくいかなかった。

具体的には「Required field 'sessionHandle' is unset!」と言われる。

別のライブラリ使ってみるかと思ってGitHub - y-lan/python-hiveserver2: Python binding for HiveServer2を使ったけど「Required field 'client_protocol' is unset!」と言われる。

うっきー。

pyhs2のソースをもとにライブラリ使わずに自分で書いていろいろ試した結果、下記のようなソースでうまく接続出来た。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import sys
import os
 
sys.path.append('/usr/lib/hive/lib/py')
 
from TCLIService import TCLIService
from TCLIService.ttypes import TOpenSessionReq, TGetTablesReq, TFetchResultsReq,\
  TStatusCode, TGetResultSetMetadataReq, TGetColumnsReq, TType,\
  TExecuteStatementReq, TGetOperationStatusReq, TFetchOrientation,\
  TCloseSessionReq, TGetSchemasReq, TCancelOperationReq
 
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
 
def get_value(colValue):
    if colValue.boolVal is not None:
      return colValue.boolVal.value
    elif colValue.byteVal is not None:
      return colValue.byteVal.value
    elif colValue.i16Val is not None:
      return colValue.i16Val.value
    elif colValue.i32Val is not None:
      return colValue.i32Val.value
    elif colValue.i64Val is not None:
      return colValue.i64Val.value
    elif colValue.doubleVal is not None:
      return colValue.doubleVal.value
    elif colValue.stringVal is not None:
      return colValue.stringVal.value
 
socket = TSocket.TSocket('localhost', 10000)
transport = TTransport.TBufferedTransport(socket)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
client = TCLIService.Client(protocol)
transport.open()
request = TOpenSessionReq(username='...')
res = client.OpenSession(request)
session = res.sessionHandle
hql = "select * from ..."
query = TExecuteStatementReq(session, statement=hql, confOverlay={})
res = client.ExecuteStatement(query)
fetch_req = TFetchResultsReq(operationHandle=res.operationHandle, orientation=TFetchOrientation.FETCH_NEXT, maxRows=100)
resultsRes = client.FetchResults(fetch_req)
rows = []
for row in resultsRes.results.rows:
    rowData= []
    for i, col in enumerate(row.colVals):
        rowData.append(get_value(col))
    rows.append(rowData)
    if len(resultsRes.results.rows) == 0:
        break
 
for row in rows:
    print row

NOSASLの場合はusernameを指定して接続しないとダメみたいなんだけどpyhs2だとそれが出来ないという状況でした。
これに関してはとりあえずpull reqestしときました。
https://github.com/BradRuderman/pyhs2/pull/14

もうひとつハマったのは結果を取得してきたときに日本語文字列が?になること。「あああ」だったら「???」になる。

Hive CLIだと問題なかったしshibとbeeswaxでも問題無かった。特にbeeswaxはpythonなのでなんでうまく言っているのか不思議に思ってソースも見たんだけど特に変わったことしてなさそうでした。

諦めてCLIでいくかshibのHTTP API使おうかとも考えたんですけど、hiveからMySQLにselect insertしたくて、そうするとCLIだと外部プロセス起動になって標準出力を扱わないといけないし、shibだとselectした後にpollingして終了を待つ必要があるしで、まあhiveserver2経由で出来た方がいいよねと。
hiveで集計してsqoopでMySQLにもってくるってのもちょっと考えたけどね。

で、まあ、いろいろググった結果ひっかかったのがこれ。この人はどうもperlのhiveserver2クライアント Thrift::API::HiveClient2 - Perl to HiveServer2 Thrift API wrapper - metacpan.org を書いていてこの問題に遭遇したようだ。
Google グループ

「-Dfile.encoding=UTF-8」を指定してhiveserver2を再起動したら無事解決した。やったー。