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                   | &lt;undefined&gt; 
        | 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                   | &lt;undefined&gt; 
        | 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                   | &lt;undefined&gt; 
        | 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                        | &lt;undefined&gt; 
        | The server url that engine will connect to                            
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
 | kyuubi.engine.jdbc.connection.user                       | &lt;undefined&gt; 
        | The user is used for connecting to server                             
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
 | kyuubi.engine.jdbc.driver.class                          | &lt;undefined&gt; 
        | 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                         | &lt;undefined&gt; 
        | 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>

Reply via email to