This is an automated email from the ASF dual-hosted git repository.
chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 03c3b4d69 [KYUUBI #5374] JDBC Engine supports ClickHouse
03c3b4d69 is described below
commit 03c3b4d69c8dc980aee321522c1c6ed8e705bb1f
Author: senmiaoliu <[email protected]>
AuthorDate: Tue Apr 2 20:00:01 2024 +0800
[KYUUBI #5374] JDBC Engine supports ClickHouse
# :mag: Description
## Issue References ๐
This pull request fixes #5374
## Describe Your Solution ๐ง
JDBC Engine supports ClickHouse
## Types of changes :bookmark:
- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
## Test Plan ๐งช
#### Behavior Without This Pull Request :coffin:
#### Behavior With This Pull Request :tada:
#### Related Unit Tests
---
# Checklist ๐
- [x] This patch was not authored or co-authored using [Generative
Tooling](https://www.apache.org/legal/generative-tooling.html)
**Be nice. Be informative.**
Closes #6225 from lsm1/branch-support-clickhouse.
Closes #5374
0ce4f6f0b [senmiaoliu] fix style
f6ab022b6 [senmiaoliu] use ck jdbc http jar
dee6a6bdc [senmiaoliu] add it test
aed6b33a9 [senmiaoliu] init clickhouse engine
Authored-by: senmiaoliu <[email protected]>
Signed-off-by: Cheng Pan <[email protected]>
---
docs/configuration/settings.md | 4 +-
externals/kyuubi-jdbc-engine/pom.xml | 13 ++
...i.engine.jdbc.connection.JdbcConnectionProvider | 1 +
...g.apache.kyuubi.engine.jdbc.dialect.JdbcDialect | 1 +
.../clickhouse/ClickHouseConnectionProvider.scala | 30 +++
.../jdbc/clickhouse/ClickHouseSchemaHelper.scala | 21 +++
.../clickhouse/ClickHouseTRowSetGenerator.scala | 55 ++++++
.../engine/jdbc/dialect/ClickHouseDialect.scala | 124 +++++++++++++
.../jdbc/clickhouse/ClickHouseOperationSuite.scala | 203 +++++++++++++++++++++
.../ClickHouseOperationWithEngineSuite.scala | 78 ++++++++
.../jdbc/clickhouse/ClickHouseSessionSuite.scala | 39 ++++
.../jdbc/clickhouse/ClickHouseStatementSuite.scala | 112 ++++++++++++
.../jdbc/clickhouse/WithClickHouseContainer.scala | 31 ++++
.../jdbc/clickhouse/WithClickHouseEngine.scala | 34 ++++
integration-tests/kyuubi-jdbc-it/pom.xml | 21 +++
.../jdbc/clickhouse/OperationWithServerSuite.scala | 27 +++
.../jdbc/clickhouse/SessionWithServerSuite.scala | 27 +++
.../jdbc/clickhouse/StatementWithServerSuite.scala | 27 +++
.../WithKyuubiServerAndClickHouseContainer.scala | 61 +++++++
.../org/apache/kyuubi/config/KyuubiConf.scala | 7 +-
.../operation/meta/ResultSetSchemaConstant.scala | 6 +
pom.xml | 14 ++
22 files changed, 932 insertions(+), 4 deletions(-)
diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md
index 95c7e82a0..0dbe3a8fb 100644
--- a/docs/configuration/settings.md
+++ b/docs/configuration/settings.md
@@ -158,7 +158,7 @@ You can configure the Kyuubi properties in
`$KYUUBI_HOME/conf/kyuubi-defaults.co
| kyuubi.engine.jdbc.connection.password | <undefined>
| The password is used for connecting to server
[...]
| kyuubi.engine.jdbc.connection.propagateCredential | false
| Whether to use the session's user and password to connect to database
[...]
| kyuubi.engine.jdbc.connection.properties
|| The additional properties are used for connecting to server
[...]
-| kyuubi.engine.jdbc.connection.provider | <undefined>
| A JDBC connection provider plugin for the Kyuubi Server to establish
a connection to the JDBC URL. The configuration value should be a subclass of
`org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider`. Kyuubi
provides the following built-in implementations: <li>doris: For establishing
Doris connections.</li> <li>mysql: For establishing MySQL connections.</li>
<li>phoenix: For establishing [...]
+| kyuubi.engine.jdbc.connection.provider | <undefined>
| A JDBC connection provider plugin for the Kyuubi Server to establish
a connection to the JDBC URL. The configuration value should be a subclass of
`org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider`. Kyuubi
provides the following built-in implementations: <li>doris: For establishing
Doris connections.</li> <li>mysql: For establishing MySQL connections.</li>
<li>phoenix: For establishing [...]
| kyuubi.engine.jdbc.connection.url | <undefined>
| The server url that engine will connect to
[...]
| kyuubi.engine.jdbc.connection.user | <undefined>
| The user is used for connecting to server
[...]
| kyuubi.engine.jdbc.driver.class | <undefined>
| The driver class for JDBC engine connection
[...]
@@ -205,7 +205,7 @@ You can configure the Kyuubi properties in
`$KYUUBI_HOME/conf/kyuubi-defaults.co
| kyuubi.engine.trino.java.options | <undefined>
| The extra Java options for the Trino query engine
[...]
| kyuubi.engine.trino.memory | 1g
| The heap memory for the Trino query engine
[...]
| kyuubi.engine.trino.operation.incremental.collect | false
| When true, the result will be sequentially calculated and returned to
the trino. It fallback to `kyuubi.operation.incremental.collect`
[...]
-| kyuubi.engine.type | SPARK_SQL
| Specify the detailed engine supported by Kyuubi. The engine type
bindings to SESSION scope. This configuration is experimental. Currently,
available configs are: <ul> <li>SPARK_SQL: specify this engine type will launch
a Spark engine which can provide all the capacity of the Apache Spark. Note,
it's a default engine type.</li> <li>FLINK_SQL: specify this engine type will
launch a Flink engine which c [...]
+| kyuubi.engine.type | SPARK_SQL
| Specify the detailed engine supported by Kyuubi. The engine type
bindings to SESSION scope. This configuration is experimental. Currently,
available configs are: <ul> <li>SPARK_SQL: specify this engine type will launch
a Spark engine which can provide all the capacity of the Apache Spark. Note,
it's a default engine type.</li> <li>FLINK_SQL: specify this engine type will
launch a Flink engine which c [...]
| kyuubi.engine.ui.retainedSessions | 200
| The number of SQL client sessions kept in the Kyuubi Query Engine web
UI.
[...]
| kyuubi.engine.ui.retainedStatements | 200
| The number of statements kept in the Kyuubi Query Engine web UI.
[...]
| kyuubi.engine.ui.stop.enabled | true
| When true, allows Kyuubi engine to be killed from the Spark Web UI.
[...]
diff --git a/externals/kyuubi-jdbc-engine/pom.xml
b/externals/kyuubi-jdbc-engine/pom.xml
index de8832f34..ebaa84ebe 100644
--- a/externals/kyuubi-jdbc-engine/pom.xml
+++ b/externals/kyuubi-jdbc-engine/pom.xml
@@ -70,6 +70,12 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.dimafeng</groupId>
+
<artifactId>testcontainers-scala-clickhouse_${scala.binary.version}</artifactId>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>org.apache.kyuubi</groupId>
<artifactId>${hive.jdbc.artifact}</artifactId>
@@ -94,6 +100,13 @@
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>com.clickhouse</groupId>
+ <artifactId>clickhouse-jdbc</artifactId>
+ <classifier>http</classifier>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider
b/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider
index ddb5edfd8..637de2e1e 100644
---
a/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider
+++
b/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider
@@ -15,6 +15,7 @@
# limitations under the License.
#
+org.apache.kyuubi.engine.jdbc.clickhouse.ClickHouseConnectionProvider
org.apache.kyuubi.engine.jdbc.doris.DorisConnectionProvider
org.apache.kyuubi.engine.jdbc.impala.ImpalaConnectionProvider
org.apache.kyuubi.engine.jdbc.mysql.MySQLConnectionProvider
diff --git
a/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.dialect.JdbcDialect
b/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.dialect.JdbcDialect
index df3adc309..6c0838c0d 100644
---
a/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.dialect.JdbcDialect
+++
b/externals/kyuubi-jdbc-engine/src/main/resources/META-INF/services/org.apache.kyuubi.engine.jdbc.dialect.JdbcDialect
@@ -15,6 +15,7 @@
# limitations under the License.
#
+org.apache.kyuubi.engine.jdbc.dialect.ClickHouseDialect
org.apache.kyuubi.engine.jdbc.dialect.DorisDialect
org.apache.kyuubi.engine.jdbc.dialect.ImpalaDialect
org.apache.kyuubi.engine.jdbc.dialect.MySQLDialect
diff --git
a/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseConnectionProvider.scala
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseConnectionProvider.scala
new file mode 100644
index 000000000..208e6ffb6
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseConnectionProvider.scala
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider
+
+class ClickHouseConnectionProvider extends JdbcConnectionProvider {
+
+ override val name: String = classOf[ClickHouseConnectionProvider].getName
+
+ override val driverClass: String = ClickHouseConnectionProvider.driverClass
+}
+
+object ClickHouseConnectionProvider {
+ val driverClass: String = "com.clickhouse.jdbc.ClickHouseDriver"
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseSchemaHelper.scala
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseSchemaHelper.scala
new file mode 100644
index 000000000..cbd21b832
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseSchemaHelper.scala
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import org.apache.kyuubi.engine.jdbc.schema.SchemaHelper
+
+class ClickHouseSchemaHelper extends SchemaHelper {}
diff --git
a/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseTRowSetGenerator.scala
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseTRowSetGenerator.scala
new file mode 100644
index 000000000..fd0e24e8f
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseTRowSetGenerator.scala
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import java.sql.Types.{ARRAY, OTHER}
+
+import org.apache.kyuubi.engine.jdbc.schema.DefaultJdbcTRowSetGenerator
+import org.apache.kyuubi.shaded.hive.service.rpc.thrift.{TColumn, TColumnValue}
+
+class ClickHouseTRowSetGenerator extends DefaultJdbcTRowSetGenerator {
+ override def toTinyIntTColumn(rows: Seq[Seq[_]], ordinal: Int): TColumn = {
+ super.asByteTColumn(rows, ordinal)
+ }
+
+ override def toVarcharTColumn(rows: Seq[Seq[_]], ordinal: Int): TColumn = {
+ val colHead = if (rows.isEmpty) None else rows.head(ordinal)
+ colHead match {
+ case _: String => super.toVarcharTColumn(rows, ordinal)
+ case _ =>
+ asStringTColumn(rows, ordinal, convertFunc = (row, ordinal) =>
String.valueOf(row(ordinal)))
+ }
+ }
+
+ override def toTinyIntTColumnValue(row: Seq[_], ordinal: Int): TColumnValue
= {
+ super.asByteTColumnValue(row, ordinal)
+ }
+
+ override def toHiveString(data: Any, sqlType: Int): String =
+ (data, sqlType) match {
+ case (array: Array[_], ARRAY) => arrayToString(array)
+ case (array: Array[_], OTHER) => arrayToString(array)
+ case (other, _) => super.toHiveString(other, sqlType)
+ }
+
+ private def arrayToString(arr: Any): String = {
+ arr match {
+ case a: Array[_] => "[" + a.map(arrayToString).mkString(", ") + "]"
+ case x => x.toString
+ }
+ }
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/dialect/ClickHouseDialect.scala
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/dialect/ClickHouseDialect.scala
new file mode 100644
index 000000000..535e2578c
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/dialect/ClickHouseDialect.scala
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.dialect
+
+import java.util
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ArrayBuffer
+
+import org.apache.commons.lang3.StringUtils
+
+import org.apache.kyuubi.engine.jdbc.clickhouse.{ClickHouseSchemaHelper,
ClickHouseTRowSetGenerator}
+import org.apache.kyuubi.engine.jdbc.schema.{JdbcTRowSetGenerator,
SchemaHelper}
+import org.apache.kyuubi.operation.meta.ResultSetSchemaConstant.{COLUMN_NAME,
TABLE_CATALOG, TABLE_NAME, TABLE_SCHEMA, TABLE_TYPE}
+import org.apache.kyuubi.session.Session
+
+class ClickHouseDialect extends JdbcDialect {
+ override def name(): String = "clickhouse"
+
+ override def getTRowSetGenerator(): JdbcTRowSetGenerator = new
ClickHouseTRowSetGenerator
+
+ override def getSchemaHelper(): SchemaHelper = new ClickHouseSchemaHelper
+
+ override def getTablesQuery(
+ catalog: String,
+ schema: String,
+ tableName: String,
+ tableTypes: util.List[String]): String = {
+ val tTypes =
+ if (tableTypes == null || tableTypes.isEmpty) {
+ Set("BASE TABLE", "SYSTEM VIEW", "VIEW")
+ } else {
+ tableTypes.asScala.toSet
+ }
+ val query = new StringBuilder(
+ s"""
+ |SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE,
+ |TABLE_ROWS, DATA_LENGTH,
+ |TABLE_COLLATION, TABLE_COMMENT
+ |FROM INFORMATION_SCHEMA.TABLES
+ |""".stripMargin)
+
+ val filters = ArrayBuffer[String]()
+ if (StringUtils.isNotBlank(catalog)) {
+ filters += s"$TABLE_CATALOG = '$catalog'"
+ }
+
+ if (StringUtils.isNotBlank(schema)) {
+ filters += s"$TABLE_SCHEMA LIKE '$schema'"
+ }
+
+ if (StringUtils.isNotBlank(tableName)) {
+ filters += s"$TABLE_NAME LIKE '$tableName'"
+ }
+
+ if (tTypes.nonEmpty) {
+ filters += s"(${
+ tTypes.map { tableType => s"$TABLE_TYPE = '$tableType'" }
+ .mkString(" OR ")
+ })"
+ }
+
+ if (filters.nonEmpty) {
+ query.append(" WHERE ")
+ query.append(filters.mkString(" AND "))
+ }
+
+ query.toString()
+ }
+
+ override def getColumnsQuery(
+ session: Session,
+ catalogName: String,
+ schemaName: String,
+ tableName: String,
+ columnName: String): String = {
+ val query = new StringBuilder(
+ """
+ |SELECT
+
|`TABLE_CATALOG`,`TABLE_SCHEMA`,`TABLE_NAME`,`COLUMN_NAME`,`ORDINAL_POSITION`,
+ |`COLUMN_DEFAULT`,`IS_NULLABLE`,`DATA_TYPE`,`CHARACTER_MAXIMUM_LENGTH`,
+
|`CHARACTER_OCTET_LENGTH`,`NUMERIC_PRECISION`,`NUMERIC_PRECISION_RADIX`,
+
|`NUMERIC_SCALE`,`DATETIME_PRECISION`,`CHARACTER_SET_CATALOG`,`CHARACTER_SET_SCHEMA`,
+
|`CHARACTER_SET_NAME`,`COLLATION_CATALOG`,`COLLATION_SCHEMA`,`COLLATION_NAME`,
+ |`DOMAIN_CATALOG`,`DOMAIN_SCHEMA`,`DOMAIN_NAME`, `EXTRA`,
`COLUMN_COMMENT`, `COLUMN_TYPE`
+ |FROM information_schema.columns
+ |""".stripMargin)
+
+ val filters = ArrayBuffer[String]()
+ if (StringUtils.isNotEmpty(catalogName)) {
+ filters += s"$TABLE_CATALOG = '$catalogName'"
+ }
+ if (StringUtils.isNotEmpty(schemaName)) {
+ filters += s"$TABLE_SCHEMA LIKE '$schemaName'"
+ }
+ if (StringUtils.isNotEmpty(tableName)) {
+ filters += s"$TABLE_NAME LIKE '$tableName'"
+ }
+ if (StringUtils.isNotEmpty(columnName)) {
+ filters += s"$COLUMN_NAME LIKE '$columnName'"
+ }
+
+ if (filters.nonEmpty) {
+ query.append(" WHERE ")
+ query.append(filters.mkString(" AND "))
+ }
+
+ query.toString()
+ }
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseOperationSuite.scala
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseOperationSuite.scala
new file mode 100644
index 000000000..3bea5a6d1
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseOperationSuite.scala
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import java.sql.ResultSet
+
+import scala.collection.mutable.ArrayBuffer
+
+import org.apache.kyuubi.operation.HiveJDBCTestHelper
+import org.apache.kyuubi.operation.meta.ResultSetSchemaConstant._
+
+abstract class ClickHouseOperationSuite extends WithClickHouseEngine with
HiveJDBCTestHelper {
+ test("clickhouse - get tables") {
+ case class Table(catalog: String, schema: String, tableName: String,
tableType: String)
+
+ withJdbcStatement() { statement =>
+ val meta = statement.getConnection.getMetaData
+ val resultBuffer = ArrayBuffer[Table]()
+
+ var tables = meta.getTables(null, null, null, null)
+ while (tables.next()) {
+ resultBuffer +=
+ Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ }
+ assert(resultBuffer.contains(Table(
+ "INFORMATION_SCHEMA",
+ "INFORMATION_SCHEMA",
+ "TABLES",
+ "VIEW")))
+ assert(resultBuffer.contains(Table(
+ "INFORMATION_SCHEMA",
+ "INFORMATION_SCHEMA",
+ "VIEWS",
+ "VIEW")))
+ resultBuffer.clear()
+
+ statement.execute("create database if not exists db1")
+ statement.execute("create table db1.test1(id bigInt)
ENGINE=File(TabSeparated)")
+ statement.execute("create table db1.test2(id bigint)
ENGINE=File(TabSeparated)")
+
+ statement.execute("create database if not exists db2")
+ statement.execute("create table db2.test1(id bigint)
ENGINE=File(TabSeparated)")
+ statement.execute("create table db2.test2(id bigint)
ENGINE=File(TabSeparated)")
+
+ statement.execute("create view db1.view1 as select id from db1.test1")
+
+ tables = meta.getTables(null, "db1", "test1", Array("BASE TABLE"))
+ while (tables.next()) {
+ val table = Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ assert(table == Table("db1", "db1", "test1", "BASE TABLE"))
+ }
+
+ tables = meta.getTables("db1", "db1", null, null)
+ while (tables.next()) {
+ resultBuffer += Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ }
+ assert(resultBuffer.contains(Table("db1", "db1", "test2", "BASE TABLE")))
+ resultBuffer.clear()
+
+ tables = meta.getTables(null, null, "test1", null)
+ while (tables.next()) {
+ resultBuffer += Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ }
+ assert(resultBuffer.contains(Table("db1", "db1", "test1", "BASE TABLE")))
+ assert(resultBuffer.contains(Table("db2", "db2", "test1", "BASE TABLE")))
+ resultBuffer.clear()
+
+ tables = meta.getTables(null, "db%", "test1", null)
+ while (tables.next()) {
+ resultBuffer += Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ }
+ assert(resultBuffer.contains(Table("db1", "db1", "test1", "BASE TABLE")))
+ assert(resultBuffer.contains(Table("db2", "db2", "test1", "BASE TABLE")))
+ resultBuffer.clear()
+
+ tables = meta.getTables(null, "db2", "test%", null)
+ while (tables.next()) {
+ resultBuffer += Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ }
+ assert(resultBuffer.contains(Table("db2", "db2", "test1", "BASE TABLE")))
+ assert(resultBuffer.contains(Table("db2", "db2", "test2", "BASE TABLE")))
+ resultBuffer.clear()
+
+ tables = meta.getTables(null, "fake_db", "test1", null)
+ assert(!tables.next())
+
+ tables = meta.getTables(null, "db1", null, Array("VIEW"))
+ while (tables.next()) {
+ val table = Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ assert(table == Table("db1", "db1", "view1", "VIEW"))
+ }
+
+ tables = meta.getTables(null, null, null, Array("VIEW", "BASE TABLE"))
+ while (tables.next()) {
+ resultBuffer += Table(
+ tables.getString(TABLE_CATALOG),
+ tables.getString(CK_TABLE_SCHEMA),
+ tables.getString(TABLE_NAME),
+ tables.getString(TABLE_TYPE))
+ }
+ assert(resultBuffer.contains(Table("db1", "db1", "test1", "BASE TABLE")))
+ assert(resultBuffer.contains(Table("db1", "db1", "test2", "BASE TABLE")))
+ assert(resultBuffer.contains(Table("db2", "db2", "test1", "BASE TABLE")))
+ assert(resultBuffer.contains(Table("db2", "db2", "test2", "BASE TABLE")))
+ assert(resultBuffer.contains(Table("db1", "db1", "view1", "VIEW")))
+ resultBuffer.clear()
+
+ statement.execute("drop view db1.view1")
+ statement.execute("drop table db1.test1")
+ statement.execute("drop table db1.test2")
+ statement.execute("drop table db2.test1")
+ statement.execute("drop table db2.test2")
+ statement.execute("drop database db1")
+ statement.execute("drop database db2")
+ }
+ }
+
+ test("clickhouse - get columns") {
+ case class Column(name: String, columnType: String)
+
+ def buildColumn(resultSet: ResultSet): Column = {
+ val columnName = resultSet.getString("COLUMN_NAME")
+ val columnType = resultSet.getString("DATA_TYPE")
+ Column(columnName, columnType)
+ }
+
+ withJdbcStatement() { statement =>
+ val metadata = statement.getConnection.getMetaData
+ statement.execute("create table if not exists test1" +
+ "(id bigint, str1 String, str2 String, age int)
ENGINE=File(TabSeparated)")
+
+ statement.execute("create database db1")
+ statement.execute("create table if not exists db1.test2" +
+ "(id bigint, str1 String) ENGINE=File(TabSeparated)")
+
+ val resultBuffer = ArrayBuffer[Column]()
+ val resultSet1 = metadata.getColumns(null, null, "test1", null)
+ while (resultSet1.next()) {
+ resultBuffer += buildColumn(resultSet1)
+ }
+
+ assert(resultBuffer.contains(Column("id", "Int64")))
+ assert(resultBuffer.contains(Column("str1", "String")))
+ assert(resultBuffer.contains(Column("str2", "String")))
+ assert(resultBuffer.contains(Column("age", "Int32")))
+
+ resultBuffer.clear()
+
+ val resultSet2 = metadata.getColumns(null, null, "test2", null)
+ while (resultSet2.next()) {
+ resultBuffer += buildColumn(resultSet2)
+ }
+ assert(resultBuffer.contains(Column("id", "Int64")))
+ assert(resultBuffer.contains(Column("str1", "String")))
+
+ statement.execute("drop table test1")
+ statement.execute("drop table db1.test2")
+ statement.execute("drop database db1")
+ }
+ }
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseOperationWithEngineSuite.scala
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseOperationWithEngineSuite.scala
new file mode 100644
index 000000000..b5a99cf42
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseOperationWithEngineSuite.scala
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import org.apache.kyuubi.config.KyuubiConf
+import org.apache.kyuubi.engine.jdbc.connection.ConnectionProvider
+import org.apache.kyuubi.operation.HiveJDBCTestHelper
+import org.apache.kyuubi.shaded.hive.service.rpc.thrift._
+
+class ClickHouseOperationWithEngineSuite extends ClickHouseOperationSuite with
HiveJDBCTestHelper {
+
+ override protected def jdbcUrl: String = jdbcConnectionUrl
+
+ test("clickhouse - test for Jdbc engine getInfo") {
+ val metaData = ConnectionProvider.create(kyuubiConf).getMetaData
+
+ withSessionConf(Map(KyuubiConf.SERVER_INFO_PROVIDER.key -> "ENGINE"))()() {
+ withSessionHandle { (client, handle) =>
+ val req = new TGetInfoReq()
+ req.setSessionHandle(handle)
+ req.setInfoType(TGetInfoType.CLI_DBMS_NAME)
+ assert(client.GetInfo(req).getInfoValue.getStringValue ==
metaData.getDatabaseProductName)
+
+ val req2 = new TGetInfoReq()
+ req2.setSessionHandle(handle)
+ req2.setInfoType(TGetInfoType.CLI_DBMS_VER)
+ assert(
+ client.GetInfo(req2).getInfoValue.getStringValue ==
metaData.getDatabaseProductVersion)
+
+ val req3 = new TGetInfoReq()
+ req3.setSessionHandle(handle)
+ req3.setInfoType(TGetInfoType.CLI_MAX_COLUMN_NAME_LEN)
+ assert(client.GetInfo(req3).getInfoValue.getLenValue ==
metaData.getMaxColumnNameLength)
+
+ val req4 = new TGetInfoReq()
+ req4.setSessionHandle(handle)
+ req4.setInfoType(TGetInfoType.CLI_MAX_SCHEMA_NAME_LEN)
+ assert(client.GetInfo(req4).getInfoValue.getLenValue ==
metaData.getMaxSchemaNameLength)
+
+ val req5 = new TGetInfoReq()
+ req5.setSessionHandle(handle)
+ req5.setInfoType(TGetInfoType.CLI_MAX_TABLE_NAME_LEN)
+ assert(client.GetInfo(req5).getInfoValue.getLenValue ==
metaData.getMaxTableNameLength)
+ }
+ }
+ }
+
+ test("clickhouse - JDBC ExecuteStatement operation should contain
operationLog") {
+ withSessionHandle { (client, handle) =>
+ val tExecuteStatementReq = new TExecuteStatementReq()
+ tExecuteStatementReq.setSessionHandle(handle)
+ tExecuteStatementReq.setStatement("SELECT 1")
+ val tExecuteStatementResp = client.ExecuteStatement(tExecuteStatementReq)
+
+ val tFetchResultsReq = new TFetchResultsReq()
+
tFetchResultsReq.setOperationHandle(tExecuteStatementResp.getOperationHandle)
+ tFetchResultsReq.setFetchType(1)
+ tFetchResultsReq.setMaxRows(1)
+
+ val tFetchResultsResp = client.FetchResults(tFetchResultsReq)
+ assert(tFetchResultsResp.getStatus.getStatusCode ===
TStatusCode.SUCCESS_STATUS)
+ }
+ }
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseSessionSuite.scala
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseSessionSuite.scala
new file mode 100644
index 000000000..05a61bd00
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseSessionSuite.scala
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import org.apache.kyuubi.operation.HiveJDBCTestHelper
+
+class ClickHouseSessionSuite extends WithClickHouseEngine with
HiveJDBCTestHelper {
+
+ test("clickhouse - test session") {
+ withJdbcStatement() { statement =>
+ val resultSet = statement.executeQuery(
+ "select '1' as id")
+ val metadata = resultSet.getMetaData
+ for (i <- 1 to metadata.getColumnCount) {
+ assert(metadata.getColumnName(i) == "id")
+ }
+ while (resultSet.next()) {
+ val id = resultSet.getObject(1)
+ assert(id == "1")
+ }
+ }
+ }
+
+ override protected def jdbcUrl: String = jdbcConnectionUrl
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseStatementSuite.scala
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseStatementSuite.scala
new file mode 100644
index 000000000..303614910
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/ClickHouseStatementSuite.scala
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import java.sql.{Date, Timestamp}
+
+import org.apache.kyuubi.operation.HiveJDBCTestHelper
+
+class ClickHouseStatementSuite extends WithClickHouseEngine with
HiveJDBCTestHelper {
+
+ test("clickhouse - test select") {
+ withJdbcStatement("test1") { statement =>
+ statement.execute("create database if not exists db1")
+ statement.execute("use db1")
+ statement.execute(
+ """CREATE TABLE db1.test1(id bigint, name varchar(255), age int)
ENGINE=File(TabSeparated)
+ |""".stripMargin)
+ statement.execute("insert into db1.test1 values(1, 'a', 11)")
+
+ val resultSet1 = statement.executeQuery("select * from db1.test1")
+ while (resultSet1.next()) {
+ val id = resultSet1.getObject(1)
+ assert(id == 1)
+ val name = resultSet1.getObject(2)
+ assert(name == "a")
+ val age = resultSet1.getObject(3)
+ assert(age == 11)
+ }
+ }
+ }
+
+ test("clickhouse - test types") {
+ withJdbcStatement("test1") { statement =>
+ statement.execute("create database if not exists db1")
+ statement.execute("use db1")
+ statement.execute(
+ """ CREATE TABLE db1.type_test(
+ | id bigint,
+ | tiny_col tinyint,
+ | smallint_col smallint,
+ | int_col int,
+ | bigint_col bigint,
+ | largeint_col bigint,
+ | decimal_col decimal(27, 9),
+ | date_col date,
+ | datetime_col datetime,
+ | char_col char,
+ | varchar_col varchar(255),
+ | string_col String,
+ | boolean_col boolean,
+ | double_col double,
+ | float_col float,
+ | x UUID)
+ | ENGINE=File(TabSeparated)
+ |""".stripMargin)
+ statement.execute(
+ """ insert into db1.type_test
+ | (id, tiny_col, smallint_col, int_col, bigint_col, largeint_col,
decimal_col,
+ | date_col, datetime_col, char_col, varchar_col, string_col,
boolean_col,
+ | double_col, float_col, x)
+ | VALUES (1, 2, 3, 4, 5, 6, 7.7,
+ | '2022-05-08', '2022-05-08 17:47:45', 'a', 'Hello', 'Hello,
Kyuubi', true,
+ | 8.8, 9.9, generateUUIDv4())
+ |""".stripMargin)
+ val resultSet1 = statement.executeQuery("select * from db1.type_test")
+ while (resultSet1.next()) {
+ assert(resultSet1.getObject(1) == 1)
+ assert(resultSet1.getObject(2) == 2)
+ assert(resultSet1.getObject(3) == 3)
+ assert(resultSet1.getObject(4) == 4)
+ assert(resultSet1.getObject(5) == 5)
+ assert(resultSet1.getObject(6) == 6)
+ assert(resultSet1.getObject(7) == new
java.math.BigDecimal("7.700000000"))
+ assert(resultSet1.getObject(8) == Date.valueOf("2022-05-08"))
+ assert(resultSet1.getObject(9) == Timestamp.valueOf("2022-05-08
17:47:45"))
+ assert(resultSet1.getObject(10) == "a")
+ assert(resultSet1.getObject(11) == "Hello")
+ assert(resultSet1.getObject(12) == "Hello, Kyuubi")
+ assert(resultSet1.getObject(13) == true)
+ assert(resultSet1.getObject(14) == 8.8)
+ assert(resultSet1.getObject(15) == 9.9)
+ assert(resultSet1.getString(16).length == 36)
+ }
+ }
+ }
+
+ test("clickhouse: test Array") {
+ withJdbcStatement("test1") { statement =>
+ val resultSet1 = statement.executeQuery("SELECT
array(array(1,1),array(1,2))")
+ while (resultSet1.next()) {
+ val array = resultSet1.getObject(1)
+ assert(array == "[[1, 1], [1, 2]]")
+ }
+ }
+ }
+
+ override protected def jdbcUrl: String = jdbcConnectionUrl
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/WithClickHouseContainer.scala
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/WithClickHouseContainer.scala
new file mode 100644
index 000000000..483876cc4
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/WithClickHouseContainer.scala
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import com.dimafeng.testcontainers.ClickHouseContainer
+import org.testcontainers.utility.DockerImageName
+
+import org.apache.kyuubi.engine.jdbc.WithJdbcServerContainer
+
+trait WithClickHouseContainer extends WithJdbcServerContainer {
+
+ private val clickHouseDockerImage = "clickhouse/clickhouse-server:24.3"
+
+ override val containerDef: ClickHouseContainer.Def =
ClickHouseContainer.Def(dockerImageName =
+ DockerImageName.parse(clickHouseDockerImage))
+
+}
diff --git
a/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/WithClickHouseEngine.scala
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/WithClickHouseEngine.scala
new file mode 100644
index 000000000..2cf695d28
--- /dev/null
+++
b/externals/kyuubi-jdbc-engine/src/test/scala/org/apache/kyuubi/engine/jdbc/clickhouse/WithClickHouseEngine.scala
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.engine.jdbc.clickhouse
+
+import org.apache.kyuubi.config.KyuubiConf._
+import org.apache.kyuubi.engine.jdbc.WithJdbcEngine
+
+trait WithClickHouseEngine extends WithJdbcEngine with WithClickHouseContainer
{
+
+ override def withKyuubiConf: Map[String, String] = withContainers {
container =>
+ Map(
+ ENGINE_SHARE_LEVEL.key -> "SERVER",
+ ENGINE_JDBC_CONNECTION_URL.key -> container.jdbcUrl,
+ ENGINE_JDBC_CONNECTION_USER.key -> container.username,
+ ENGINE_JDBC_CONNECTION_PASSWORD.key -> container.password,
+ ENGINE_TYPE.key -> "jdbc",
+ ENGINE_JDBC_SHORT_NAME.key -> "clickhouse",
+ ENGINE_JDBC_DRIVER_CLASS.key -> container.driverClassName)
+ }
+}
diff --git a/integration-tests/kyuubi-jdbc-it/pom.xml
b/integration-tests/kyuubi-jdbc-it/pom.xml
index 651792e9b..f62d2d9af 100644
--- a/integration-tests/kyuubi-jdbc-it/pom.xml
+++ b/integration-tests/kyuubi-jdbc-it/pom.xml
@@ -96,6 +96,19 @@
<artifactId>testcontainers-scala-postgresql_${scala.binary.version}</artifactId>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>com.dimafeng</groupId>
+
<artifactId>testcontainers-scala-clickhouse_${scala.binary.version}</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.clickhouse</groupId>
+ <artifactId>clickhouse-jdbc</artifactId>
+ <classifier>http</classifier>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -140,6 +153,14 @@
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}</outputDirectory>
</artifactItem>
+ <artifactItem>
+ <groupId>com.clickhouse</groupId>
+ <artifactId>clickhouse-jdbc</artifactId>
+
<version>${clickhouse-java.version}</version>
+ <classifier>http</classifier>
+ <overWrite>true</overWrite>
+
<outputDirectory>${project.build.directory}</outputDirectory>
+ </artifactItem>
</artifactItems>
</configuration>
</execution>
diff --git
a/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/OperationWithServerSuite.scala
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/OperationWithServerSuite.scala
new file mode 100644
index 000000000..900a64e33
--- /dev/null
+++
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/OperationWithServerSuite.scala
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.it.jdbc.clickhouse
+
+import org.apache.kyuubi.engine.jdbc.clickhouse.ClickHouseOperationSuite
+
+class OperationWithServerSuite extends ClickHouseOperationSuite
+ with WithKyuubiServerAndClickHouseContainer {
+
+ override protected def jdbcUrl: String = getJdbcUrl
+
+}
diff --git
a/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/SessionWithServerSuite.scala
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/SessionWithServerSuite.scala
new file mode 100644
index 000000000..af98e2152
--- /dev/null
+++
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/SessionWithServerSuite.scala
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.it.jdbc.clickhouse
+
+import org.apache.kyuubi.engine.jdbc.clickhouse.ClickHouseSessionSuite
+
+class SessionWithServerSuite extends ClickHouseSessionSuite
+ with WithKyuubiServerAndClickHouseContainer {
+
+ override protected def jdbcUrl: String = getJdbcUrl
+
+}
diff --git
a/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/StatementWithServerSuite.scala
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/StatementWithServerSuite.scala
new file mode 100644
index 000000000..1d616326d
--- /dev/null
+++
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/StatementWithServerSuite.scala
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.it.jdbc.clickhouse
+
+import org.apache.kyuubi.engine.jdbc.clickhouse.ClickHouseStatementSuite
+
+class StatementWithServerSuite extends ClickHouseStatementSuite
+ with WithKyuubiServerAndClickHouseContainer {
+
+ override protected def jdbcUrl: String = getJdbcUrl
+
+}
diff --git
a/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/WithKyuubiServerAndClickHouseContainer.scala
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/WithKyuubiServerAndClickHouseContainer.scala
new file mode 100644
index 000000000..92c715c69
--- /dev/null
+++
b/integration-tests/kyuubi-jdbc-it/src/test/scala/org/apache/kyuubi/it/jdbc/clickhouse/WithKyuubiServerAndClickHouseContainer.scala
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kyuubi.it.jdbc.clickhouse
+
+import java.nio.file.{Files, Path, Paths}
+import java.time.Duration
+
+import org.apache.kyuubi.WithKyuubiServer
+import org.apache.kyuubi.config.KyuubiConf
+import org.apache.kyuubi.config.KyuubiConf.{ENGINE_IDLE_TIMEOUT,
ENGINE_JDBC_EXTRA_CLASSPATH, KYUUBI_ENGINE_ENV_PREFIX, KYUUBI_HOME}
+import org.apache.kyuubi.engine.jdbc.clickhouse.WithClickHouseEngine
+import org.apache.kyuubi.util.JavaUtils
+
+trait WithKyuubiServerAndClickHouseContainer extends WithKyuubiServer with
WithClickHouseEngine {
+
+ private val kyuubiHome: String =
+ JavaUtils.getCodeSourceLocation(getClass).split("integration-tests").head
+
+ private val clickHouseJdbcConnectorPath: String = {
+ val keyword = "clickhouse-jdbc"
+
+ val jarsDir = Paths.get(kyuubiHome)
+ .resolve("integration-tests")
+ .resolve("kyuubi-jdbc-it")
+ .resolve("target")
+
+ Files.list(jarsDir)
+ .filter { p: Path => p.getFileName.toString contains keyword }
+ .findFirst
+ .orElseThrow { () => new IllegalStateException(s"Can not find $keyword
in $jarsDir.") }
+ .toAbsolutePath
+ .toString
+ }
+
+ override protected val conf: KyuubiConf = {
+ KyuubiConf()
+ .set(s"$KYUUBI_ENGINE_ENV_PREFIX.$KYUUBI_HOME", kyuubiHome)
+ .set(ENGINE_JDBC_EXTRA_CLASSPATH, clickHouseJdbcConnectorPath)
+ .set(ENGINE_IDLE_TIMEOUT, Duration.ofMinutes(1).toMillis)
+ }
+
+ override def beforeAll(): Unit = {
+ val configs = withKyuubiConf
+ configs.foreach(config => conf.set(config._1, config._2))
+ super.beforeAll()
+ }
+}
diff --git
a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
index 78d440a31..9646fec43 100644
--- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
+++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
@@ -2173,7 +2173,7 @@ object KyuubiConf {
" all the capacity of the Hive Server2.</li>" +
" <li>JDBC: specify this engine type will launch a JDBC engine which can
forward" +
" queries to the database system through the certain JDBC driver," +
- " for now, it supports Doris, MySQL, Phoenix, PostgreSQL and
StarRocks.</li>" +
+ " for now, it supports Doris, MySQL, Phoenix, PostgreSQL, StarRocks and
ClickHouse.</li>" +
" <li>CHAT: specify this engine type will launch a Chat engine.</li>" +
"</ul>")
.version("1.4.0")
@@ -3067,7 +3067,8 @@ object KyuubiConf {
"<li>mysql: For establishing MySQL connections.</li> " +
"<li>phoenix: For establishing Phoenix connections.</li> " +
"<li>postgresql: For establishing PostgreSQL connections.</li>" +
- "<li>starrocks: For establishing StarRocks connections.</li>")
+ "<li>starrocks: For establishing StarRocks connections.</li>" +
+ "<li>clickhouse: For establishing clickhouse connections.</li>")
.version("1.6.0")
.stringConf
.transform {
@@ -3081,6 +3082,8 @@ object KyuubiConf {
"org.apache.kyuubi.engine.jdbc.postgresql.PostgreSQLConnectionProvider"
case "StarRocks" | "starrocks" | "StarRocksConnectionProvider" =>
"org.apache.kyuubi.engine.jdbc.starrocks.StarRocksConnectionProvider"
+ case "ClickHouse" | "clickhouse" | "ClickHouseConnectionProvider" =>
+
"org.apache.kyuubi.engine.jdbc.clickhouse.ClickHouseConnectionProvider"
case other => other
}
.createOptional
diff --git
a/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/meta/ResultSetSchemaConstant.scala
b/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/meta/ResultSetSchemaConstant.scala
index d6c262a4b..e627f3bc2 100644
---
a/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/meta/ResultSetSchemaConstant.scala
+++
b/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/meta/ResultSetSchemaConstant.scala
@@ -37,6 +37,12 @@ object ResultSetSchemaConstant {
*/
final val TABLE_SCHEM = "TABLE_SCHEM"
+ /**
+ * String.
+ * CK Schema name
+ */
+ final val CK_TABLE_SCHEMA = "TABLE_SCHEMA"
+
/**
* String.
* Schema name
diff --git a/pom.xml b/pom.xml
index 8aeebd4df..dffa0e194 100644
--- a/pom.xml
+++ b/pom.xml
@@ -190,6 +190,7 @@
<scalatestplus.version>3.2.16.0</scalatestplus.version>
<scopt.version>4.1.0</scopt.version>
<slf4j.version>1.7.36</slf4j.version>
+ <clickhouse-java.version>0.6.0</clickhouse-java.version>
<snakeyaml.version>2.2</snakeyaml.version>
<!--
DO NOT forget to change the following properties when change the
minor version of Spark:
@@ -560,6 +561,12 @@
<version>${testcontainers-scala.version}</version>
</dependency>
+ <dependency>
+ <groupId>com.dimafeng</groupId>
+
<artifactId>testcontainers-scala-clickhouse_${scala.binary.version}</artifactId>
+ <version>${testcontainers-scala.version}</version>
+ </dependency>
+
<dependency>
<groupId>com.dimafeng</groupId>
<artifactId>testcontainers-scala-trino_${scala.binary.version}</artifactId>
@@ -1304,6 +1311,13 @@
<version>${postgresql.version}</version>
</dependency>
+ <dependency>
+ <groupId>com.clickhouse</groupId>
+ <artifactId>clickhouse-jdbc</artifactId>
+ <version>${clickhouse-java.version}</version>
+ <classifier>http</classifier>
+ </dependency>
+
<!-- flink -->
<dependency>
<groupId>org.apache.flink</groupId>