我自己实现了一个SparkSQL的SQL Server服务,兼容了Hive的JDBC Driver,也就可通过beeline直接去连接,如果仔细看Java的JDBC标准,里面关于SQL的执行是有2类接口的:
execute和executeQuery,前者返回一个true/false,后者会封装成一个ResultSet,可获取对应的结果集。
按照正常理解来说,execute对应的场景是不关心结果集的情况,例如ddl语句,而executeQuery则是处理查询情况,需要对结果集进行处理。

因此在后台实现thrift 接口的时候,对于ddl类的操作,我直接将结果置空,且设置setHasResultSet为false,但是这样处理后,在beeline的客户端始终会出现这样的错误:

987541 [pool-28-thread-5] INFO  - Processing EXECUTE_STATEMENT  statement: EXECUTE_STATEMENT
987541 [pool-28-thread-5] INFO - Processing EXECUTE_STATEMENT statement: EXECUTE_STATEMENT
987541 [pool-28-thread-5] INFO - Execute in full collect mode
987547 [pool-28-thread-5] INFO - Processing EXECUTE_STATEMENT statement: EXECUTE_STATEMENT , time taken: 0.006 seconds
Error: (state=,code=3)

虽然结果没有问题,但是始终会有一个显示的错误,因为beeline后面使用的也是beeline的jdbc driver,因此我使用源码去调试的时候,发现了问题,在hive 的jdbc实现逻辑里面,executeQuery直接调用了execute:

@Override
public ResultSet executeQuery(String sql) throws SQLException {
if (!execute(sql)) {
throw new SQLException("The query did not generate a result set!");
}
return resultSet;
}

且这里有一个重点是如果返回是false,则会直接抛错:The query did not generate a result set!这个地方让我觉得有点郁闷,在我去看execute的时候发现client在调用thrift的ExecuteStatement接口执行SQL后会再去调用GetOperationStatus来获取当前执行的状态:

transportLock.lock();
try {
statusResp = client.GetOperationStatus(statusReq);
} finally {
transportLock.unlock();
}

这几行代码都在在一个类似while true的循环里面,直到整个操作结束,然后开始获取结果集,这里重点就来了:

// The query should be completed by now
if (!stmtHandle.isHasResultSet()) {
return false;
}

也就是如果这里的isHasResultSet是false,那么就意味着execute那就会直接抛出异常,也就意味着,对于后端的状态来说,isHasResultSet这个值必须返回是true,因此后端在进行thrift的结果集的封装的时候,需要做如下处理:

val operationHandle = new TOperationHandle
operationHandle.setOperationType(TOperationType.EXECUTE_STATEMENT)
operationHandle.setHasResultSet(false)
operationHandle.setHasResultSetIsSet(true)
operationHandle.setOperationId(req.getSessionHandle.getSessionId)
resp.setOperationHandle(operationHandle)
resp.setStatus(new TStatus(TStatusCode.SUCCESS_STATUS))

正常来说这样设置就行了,也就是setHasResultSet=false代表没有返回集,setHasResultSetIsSet代表这个值被设置,但是实际发现这样并不行,因为setHasResultSetIsSet是和setHasResultSet关联一起的。

导致最后的解决方案是,如果是没有返回结果集的DDL执行,我得手动的生成一个类似这样的返回:

0: jdbc:hive2://127.0.0.1:xxx/fcbai> use default;
207015 [pool-28-thread-5] INFO - Processing EXECUTE_STATEMENT statement: EXECUTE_STATEMENT
207015 [pool-28-thread-5] INFO - Processing EXECUTE_STATEMENT statement: EXECUTE_STATEMENT
207016 [pool-28-thread-5] INFO - Execute in full collect mode
207023 [pool-28-thread-5] INFO - Processing EXECUTE_STATEMENT statement: EXECUTE_STATEMENT , time taken: 0.008 seconds
+---------+
| Status |
+---------+
| Ok |
+---------+
1 row selected (2.017 seconds)

这也算是Hive实现的一个不那么完美的地方吧。


扫码手机观看或分享: