Skip to content

Commit 1b92d80

Browse files
yehereulysses-you
authored andcommitted
[KYUUBI #3934] Compatiable with Trino rest dto
### _Why are the changes needed?_ close #3934 ### _How was this patch tested?_ - [x] Add some test cases that check the changes thoroughly including negative and positive cases if possible - [ ] Add screenshots for manual tests if appropriate - [x] [Run test](https://kyuubi.readthedocs.io/en/master/develop_tools/testing.html#running-tests) locally before make a pull request Closes #4182 from yehere/kyuubi-3934. Closes #3934 ced64e2 [yehere] [KYUUBI #3934] Add more result types support 3ef8523 [yehere] [KYUUBI #3934] Optimization for code review 69e1f44 [yehere] [KYUUBI #3934] Merge the test class to TrinoContextSuite 4f0a015 [yehere] [KYUUBI #3934] Merge the class to TrinoContext 7c9473f [yehere] [KYUUBI #3934] Format style, with Copyright Profiles 2023f3c [yehere] [KYUUBI #3934] Format and add test case a2243b4 [yehere] [KYUUBI #3934] Compatiable with Trino rest dto Authored-by: yehere <867171931@qq.com> Signed-off-by: ulyssesyou <ulyssesyou@apache.org>
1 parent f62a7ac commit 1b92d80

File tree

2 files changed

+310
-19
lines changed

2 files changed

+310
-19
lines changed

kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/TrinoContext.scala

Lines changed: 218 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,38 @@
1818
package org.apache.kyuubi.server.trino.api
1919

2020
import java.io.UnsupportedEncodingException
21-
import java.net.{URLDecoder, URLEncoder}
21+
import java.net.{URI, URLDecoder, URLEncoder}
22+
import java.util
2223
import javax.ws.rs.core.{HttpHeaders, Response}
2324

2425
import scala.collection.JavaConverters._
2526

27+
import io.trino.client.{ClientStandardTypes, ClientTypeSignature, Column, QueryError, QueryResults, StatementStats, Warning}
2628
import io.trino.client.ProtocolHeaders.TRINO_HEADERS
27-
import io.trino.client.QueryResults
29+
import org.apache.hive.service.rpc.thrift.{TGetResultSetMetadataResp, TRowSet, TTypeId}
30+
31+
import org.apache.kyuubi.operation.OperationStatus
2832

2933
/**
3034
* The description and functionality of trino request
3135
* and response's context
3236
*
33-
* @param user Specifies the session user, must be supplied with every query
34-
* @param timeZone The timezone for query processing
37+
* @param user Specifies the session user, must be supplied with every query
38+
* @param timeZone The timezone for query processing
3539
* @param clientCapabilities Exclusive for trino server
36-
* @param source This supplies the name of the software that submitted the query,
37-
* e.g. `trino-jdbc` or `trino-cli` by default
38-
* @param catalog The catalog context for query processing, will be set response
39-
* @param schema The schema context for query processing
40-
* @param language The language to use when processing the query and formatting results,
41-
* formatted as a Java Locale string, e.g., en-US for US English
42-
* @param traceToken Trace token for correlating requests across systems
43-
* @param clientInfo Extra information about the client
44-
* @param clientTags Client tags for selecting resource groups. Example: abc,xyz
45-
* @param preparedStatement `preparedStatement` are kv pairs, where the names
46-
* are names of previously prepared SQL statements,
47-
* and the values are keys that identify the
48-
* executable form of the named prepared statements
40+
* @param source This supplies the name of the software that submitted the query,
41+
* e.g. `trino-jdbc` or `trino-cli` by default
42+
* @param catalog The catalog context for query processing, will be set response
43+
* @param schema The schema context for query processing
44+
* @param language The language to use when processing the query and formatting results,
45+
* formatted as a Java Locale string, e.g., en-US for US English
46+
* @param traceToken Trace token for correlating requests across systems
47+
* @param clientInfo Extra information about the client
48+
* @param clientTags Client tags for selecting resource groups. Example: abc,xyz
49+
* @param preparedStatement `preparedStatement` are kv pairs, where the names
50+
* are names of previously prepared SQL statements,
51+
* and the values are keys that identify the
52+
* executable form of the named prepared statements
4953
*/
5054
case class TrinoContext(
5155
user: String,
@@ -63,6 +67,11 @@ case class TrinoContext(
6367

6468
object TrinoContext {
6569

70+
private val defaultWarning: util.List[Warning] = new util.ArrayList[Warning]()
71+
private val GENERIC_INTERNAL_ERROR_CODE = 65536
72+
private val GENERIC_INTERNAL_ERROR_NAME = "GENERIC_INTERNAL_ERROR_NAME"
73+
private val GENERIC_INTERNAL_ERROR_TYPE = "INTERNAL_ERROR"
74+
6675
def apply(headers: HttpHeaders): TrinoContext = {
6776
apply(headers.getRequestHeaders.asScala.toMap.map {
6877
case (k, v) => (k, v.asScala.toList)
@@ -166,4 +175,196 @@ object TrinoContext {
166175
throw new AssertionError(e)
167176
}
168177

178+
def createQueryResults(
179+
queryId: String,
180+
nextUri: URI,
181+
queryHtmlUri: URI,
182+
queryStatus: OperationStatus,
183+
columns: Option[TGetResultSetMetadataResp] = None,
184+
data: Option[TRowSet] = None): QueryResults = {
185+
186+
val columnList = columns match {
187+
case Some(value) => convertTColumn(value)
188+
case None => null
189+
}
190+
val rowList = data match {
191+
case Some(value) => convertTRowSet(value)
192+
case None => null
193+
}
194+
195+
new QueryResults(
196+
queryId,
197+
queryHtmlUri,
198+
nextUri,
199+
nextUri,
200+
columnList,
201+
rowList,
202+
StatementStats.builder.setState(queryStatus.state.name()).setQueued(false)
203+
.setElapsedTimeMillis(0).setQueuedTimeMillis(0).build(),
204+
toQueryError(queryStatus),
205+
defaultWarning,
206+
null,
207+
0L)
208+
}
209+
210+
def convertTColumn(columns: TGetResultSetMetadataResp): util.List[Column] = {
211+
columns.getSchema.getColumns.asScala.map(c => {
212+
val tp = c.getTypeDesc.getTypes.get(0).getPrimitiveEntry.getType match {
213+
case TTypeId.BOOLEAN_TYPE => ClientStandardTypes.BOOLEAN
214+
case TTypeId.TINYINT_TYPE => ClientStandardTypes.TINYINT
215+
case TTypeId.SMALLINT_TYPE => ClientStandardTypes.SMALLINT
216+
case TTypeId.INT_TYPE => ClientStandardTypes.INTEGER
217+
case TTypeId.BIGINT_TYPE => ClientStandardTypes.BIGINT
218+
case TTypeId.FLOAT_TYPE => ClientStandardTypes.DOUBLE
219+
case TTypeId.DOUBLE_TYPE => ClientStandardTypes.DOUBLE
220+
case TTypeId.STRING_TYPE => ClientStandardTypes.VARCHAR
221+
case TTypeId.TIMESTAMP_TYPE => ClientStandardTypes.TIMESTAMP
222+
case TTypeId.BINARY_TYPE => ClientStandardTypes.VARBINARY
223+
case TTypeId.DECIMAL_TYPE => ClientStandardTypes.DECIMAL
224+
case TTypeId.DATE_TYPE => ClientStandardTypes.DATE
225+
case TTypeId.VARCHAR_TYPE => ClientStandardTypes.VARCHAR
226+
case TTypeId.CHAR_TYPE => ClientStandardTypes.CHAR
227+
case TTypeId.INTERVAL_YEAR_MONTH_TYPE => ClientStandardTypes.INTERVAL_YEAR_TO_MONTH
228+
case TTypeId.INTERVAL_DAY_TIME_TYPE => ClientStandardTypes.TIME_WITH_TIME_ZONE
229+
case TTypeId.TIMESTAMPLOCALTZ_TYPE => ClientStandardTypes.TIMESTAMP_WITH_TIME_ZONE
230+
case _ => ClientStandardTypes.VARCHAR
231+
}
232+
new Column(c.getColumnName, tp, new ClientTypeSignature(tp))
233+
}).toList.asJava
234+
}
235+
236+
def convertTRowSet(rowSet: TRowSet): util.List[util.List[Object]] = {
237+
val dataResult = new util.LinkedList[util.List[Object]]
238+
239+
if (rowSet.getColumns == null) {
240+
return rowSet.getRows.asScala
241+
.map(t => t.getColVals.asScala.map(v => v.getFieldValue.asInstanceOf[Object]).asJava)
242+
.asJava
243+
}
244+
245+
rowSet.getColumns.asScala.foreach {
246+
case tColumn if tColumn.isSetBoolVal =>
247+
val nulls = util.BitSet.valueOf(tColumn.getBoolVal.getNulls)
248+
if (dataResult.isEmpty) {
249+
(1 to tColumn.getBoolVal.getValuesSize).foreach(_ =>
250+
dataResult.add(new util.LinkedList[Object]()))
251+
}
252+
253+
tColumn.getBoolVal.getValues.asScala.zipWithIndex.foreach {
254+
case (_, rowIdx) if nulls.get(rowIdx) =>
255+
dataResult.get(rowIdx).add(null)
256+
case (v, rowIdx) =>
257+
dataResult.get(rowIdx).add(v)
258+
}
259+
case tColumn if tColumn.isSetByteVal =>
260+
val nulls = util.BitSet.valueOf(tColumn.getByteVal.getNulls)
261+
if (dataResult.isEmpty) {
262+
(1 to tColumn.getByteVal.getValuesSize).foreach(_ =>
263+
dataResult.add(new util.LinkedList[Object]()))
264+
}
265+
266+
tColumn.getByteVal.getValues.asScala.zipWithIndex.foreach {
267+
case (_, rowIdx) if nulls.get(rowIdx) =>
268+
dataResult.get(rowIdx).add(null)
269+
case (v, rowIdx) =>
270+
dataResult.get(rowIdx).add(v)
271+
}
272+
case tColumn if tColumn.isSetI16Val =>
273+
val nulls = util.BitSet.valueOf(tColumn.getI16Val.getNulls)
274+
if (dataResult.isEmpty) {
275+
(1 to tColumn.getI16Val.getValuesSize).foreach(_ =>
276+
dataResult.add(new util.LinkedList[Object]()))
277+
}
278+
279+
tColumn.getI16Val.getValues.asScala.zipWithIndex.foreach {
280+
case (_, rowIdx) if nulls.get(rowIdx) =>
281+
dataResult.get(rowIdx).add(null)
282+
case (v, rowIdx) =>
283+
dataResult.get(rowIdx).add(v)
284+
}
285+
case tColumn if tColumn.isSetI32Val =>
286+
val nulls = util.BitSet.valueOf(tColumn.getI32Val.getNulls)
287+
if (dataResult.isEmpty) {
288+
(1 to tColumn.getI32Val.getValuesSize).foreach(_ =>
289+
dataResult.add(new util.LinkedList[Object]()))
290+
}
291+
292+
tColumn.getI32Val.getValues.asScala.zipWithIndex.foreach {
293+
case (_, rowIdx) if nulls.get(rowIdx) =>
294+
dataResult.get(rowIdx).add(null)
295+
case (v, rowIdx) =>
296+
dataResult.get(rowIdx).add(v)
297+
}
298+
case tColumn if tColumn.isSetI64Val =>
299+
val nulls = util.BitSet.valueOf(tColumn.getI64Val.getNulls)
300+
if (dataResult.isEmpty) {
301+
(1 to tColumn.getI64Val.getValuesSize).foreach(_ =>
302+
dataResult.add(new util.LinkedList[Object]()))
303+
}
304+
305+
tColumn.getI64Val.getValues.asScala.zipWithIndex.foreach {
306+
case (_, rowIdx) if nulls.get(rowIdx) =>
307+
dataResult.get(rowIdx).add(null)
308+
case (v, rowIdx) =>
309+
dataResult.get(rowIdx).add(v)
310+
}
311+
case tColumn if tColumn.isSetDoubleVal =>
312+
val nulls = util.BitSet.valueOf(tColumn.getDoubleVal.getNulls)
313+
if (dataResult.isEmpty) {
314+
(1 to tColumn.getDoubleVal.getValuesSize).foreach(_ =>
315+
dataResult.add(new util.LinkedList[Object]()))
316+
}
317+
318+
tColumn.getDoubleVal.getValues.asScala.zipWithIndex.foreach {
319+
case (_, rowIdx) if nulls.get(rowIdx) =>
320+
dataResult.get(rowIdx).add(null)
321+
case (v, rowIdx) =>
322+
dataResult.get(rowIdx).add(v)
323+
}
324+
case tColumn if tColumn.isSetBinaryVal =>
325+
val nulls = util.BitSet.valueOf(tColumn.getBinaryVal.getNulls)
326+
if (dataResult.isEmpty) {
327+
(1 to tColumn.getBinaryVal.getValuesSize).foreach(_ =>
328+
dataResult.add(new util.LinkedList[Object]()))
329+
}
330+
331+
tColumn.getBinaryVal.getValues.asScala.zipWithIndex.foreach {
332+
case (_, rowIdx) if nulls.get(rowIdx) =>
333+
dataResult.get(rowIdx).add(null)
334+
case (v, rowIdx) =>
335+
dataResult.get(rowIdx).add(v)
336+
}
337+
case tColumn =>
338+
val nulls = util.BitSet.valueOf(tColumn.getStringVal.getNulls)
339+
if (dataResult.isEmpty) {
340+
(1 to tColumn.getStringVal.getValuesSize).foreach(_ =>
341+
dataResult.add(new util.LinkedList[Object]()))
342+
}
343+
344+
tColumn.getStringVal.getValues.asScala.zipWithIndex.foreach {
345+
case (_, rowIdx) if nulls.get(rowIdx) =>
346+
dataResult.get(rowIdx).add(null)
347+
case (v, rowIdx) =>
348+
dataResult.get(rowIdx).add(v)
349+
}
350+
}
351+
dataResult
352+
}
353+
354+
def toQueryError(queryStatus: OperationStatus): QueryError = {
355+
val exception = queryStatus.exception
356+
if (exception.isEmpty) {
357+
null
358+
} else {
359+
new QueryError(
360+
exception.get.getMessage,
361+
queryStatus.state.name(),
362+
GENERIC_INTERNAL_ERROR_CODE,
363+
GENERIC_INTERNAL_ERROR_NAME,
364+
GENERIC_INTERNAL_ERROR_TYPE,
365+
null,
366+
null)
367+
}
368+
}
369+
169370
}

kyuubi-server/src/test/scala/org/apache/kyuubi/server/trino/api/TrinoContextSuite.scala

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,24 @@
1717

1818
package org.apache.kyuubi.server.trino.api
1919

20+
import java.net.URI
2021
import java.time.ZoneId
22+
import javax.ws.rs.core.MediaType
23+
24+
import scala.collection.JavaConverters._
2125

2226
import io.trino.client.ProtocolHeaders.TRINO_HEADERS
27+
import org.apache.hive.service.rpc.thrift.TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V9
28+
import org.scalatest.concurrent.PatienceConfiguration.Timeout
29+
import org.scalatest.time.SpanSugar.convertIntToGrainOfTime
30+
31+
import org.apache.kyuubi.{KyuubiFunSuite, RestFrontendTestHelper}
32+
import org.apache.kyuubi.events.KyuubiOperationEvent
33+
import org.apache.kyuubi.operation.{FetchOrientation, OperationHandle}
34+
import org.apache.kyuubi.operation.OperationState.{FINISHED, OperationState}
2335

24-
import org.apache.kyuubi.KyuubiFunSuite
36+
class TrinoContextSuite extends KyuubiFunSuite with RestFrontendTestHelper {
2537

26-
class TrinoContextSuite extends KyuubiFunSuite {
2738
import TrinoContext._
2839

2940
test("create trino request context with header") {
@@ -67,4 +78,83 @@ class TrinoContextSuite extends KyuubiFunSuite {
6778
assert(actual == expectedTrinoContext)
6879
}
6980

81+
test("test convert") {
82+
val opHandle = getOpHandle("select 1")
83+
val opHandleStr = opHandle.identifier.toString
84+
checkOpState(opHandleStr, FINISHED)
85+
86+
val metadataResp = fe.be.getResultSetMetadata(opHandle)
87+
val tRowSet = fe.be.fetchResults(opHandle, FetchOrientation.FETCH_NEXT, 1000, false)
88+
val status = fe.be.getOperationStatus(opHandle)
89+
90+
val uri = new URI("sfdsfsdfdsf")
91+
val results = TrinoContext
92+
.createQueryResults("/xdfd/xdf", uri, uri, status, Option(metadataResp), Option(tRowSet))
93+
94+
print(results.toString)
95+
assert(results.getColumns.get(0).getType.equals("integer"))
96+
assert(results.getData.asScala.last.get(0) == 1)
97+
}
98+
99+
test("test convert from table") {
100+
initSql("CREATE DATABASE IF NOT EXISTS INIT_DB")
101+
initSql(
102+
"CREATE TABLE IF NOT EXISTS INIT_DB.test(a int, b double, c String," +
103+
"d BOOLEAN,e DATE,f TIMESTAMP,g ARRAY<String>,h DECIMAL," +
104+
"i MAP<String,String>) USING PARQUET;")
105+
initSql(
106+
"INSERT INTO INIT_DB.test VALUES (1,2.2,'3',true,current_date()," +
107+
"current_timestamp(),array('1','2'),2.0, map('m','p') )")
108+
109+
val opHandle = getOpHandle("SELECT * FROM INIT_DB.test")
110+
val opHandleStr = opHandle.identifier.toString
111+
checkOpState(opHandleStr, FINISHED)
112+
113+
val metadataResp = fe.be.getResultSetMetadata(opHandle)
114+
val tRowSet = fe.be.fetchResults(opHandle, FetchOrientation.FETCH_NEXT, 1000, false)
115+
val status = fe.be.getOperationStatus(opHandle)
116+
117+
val uri = new URI("sfdsfsdfdsf")
118+
val results = TrinoContext
119+
.createQueryResults("/xdfd/xdf", uri, uri, status, Option(metadataResp), Option(tRowSet))
120+
121+
print(results.toString)
122+
assert(results.getColumns.get(0).getType.equals("integer"))
123+
assert(results.getData.asScala.last.get(0) != null)
124+
}
125+
126+
def getOpHandleStr(statement: String = "show tables"): String = {
127+
getOpHandle(statement).identifier.toString
128+
}
129+
130+
def getOpHandle(statement: String = "show tables"): OperationHandle = {
131+
val sessionHandle = fe.be.openSession(
132+
HIVE_CLI_SERVICE_PROTOCOL_V9,
133+
"admin",
134+
"123456",
135+
"localhost",
136+
Map("testConfig" -> "testValue"))
137+
138+
if (statement.nonEmpty) {
139+
fe.be.executeStatement(sessionHandle, statement, Map.empty, runAsync = false, 30000)
140+
} else {
141+
fe.be.getCatalogs(sessionHandle)
142+
}
143+
}
144+
145+
private def checkOpState(opHandleStr: String, state: OperationState): Unit = {
146+
eventually(Timeout(30.seconds)) {
147+
val response = webTarget.path(s"api/v1/operations/$opHandleStr/event")
148+
.request(MediaType.APPLICATION_JSON_TYPE).get()
149+
assert(response.getStatus === 200)
150+
val operationEvent = response.readEntity(classOf[KyuubiOperationEvent])
151+
assert(operationEvent.state === state.name())
152+
}
153+
}
154+
155+
private def initSql(sql: String): Unit = {
156+
val initOpHandle = getOpHandle(sql)
157+
val initOpHandleStr = initOpHandle.identifier.toString
158+
checkOpState(initOpHandleStr, FINISHED)
159+
}
70160
}

0 commit comments

Comments
 (0)