You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ya...@apache.org on 2023/05/29 04:09:54 UTC

[kyuubi-service-rpc] branch main created (now 658e9ef)

This is an automated email from the ASF dual-hosted git repository.

yao pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/kyuubi-service-rpc.git


      at 658e9ef  nit

This branch includes the following new commits:

     new a4fe7ff  initial commit
     new 658e9ef  nit

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[kyuubi-service-rpc] 01/02: initial commit

Posted by ya...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/kyuubi-service-rpc.git

commit a4fe7ff681e97e7dae5fd9b7f4c6348b1ea88304
Author: Kent Yao <ya...@apache.org>
AuthorDate: Mon May 29 12:06:42 2023 +0800

    initial commit
---
 .gitignore                                         |  12 +
 .idea/.gitignore                                   |   8 +
 .idea/codeStyles/Project.xml                       |  53 +++
 .idea/codeStyles/codeStyleConfig.xml               |   5 +
 .idea/copyright/apache.xml                         |   6 +
 .idea/encodings.xml                                |  15 +
 .idea/jpa-buddy.xml                                |   6 +
 .idea/misc.xml                                     |  17 +
 .idea/protoeditor.xml                              |  47 ++
 .idea/rSettings.xml                                |   6 +
 .idea/sbt.xml                                      |   6 +
 .idea/scopes/apache.xml                            |   3 +
 .idea/thriftCompiler.xml                           |   6 +
 .idea/vcs.xml                                      |   6 +
 README.md                                          |   6 +
 health-proto/pom.xml                               |  80 ++++
 .../org/apache/kyuubi/grpc/health/health.proto     |  66 +++
 jdbc-grpc-client/pom.xml                           |  73 +++
 .../java/org/apache/kyuubi/grpc/GrpcUtils.java     |   5 +
 .../apache/kyuubi/grpc/client/JdbcGrpcClient.java  |  81 ++++
 .../grpc/client/SimpleBlockingJdbcClient.java      | 394 +++++++++++++++++
 .../org/apache/kyuubi/grpc/DummyJdbcService.java   |  40 ++
 .../apache/kyuubi/grpc/TestConnectionService.java  | 492 +++++++++++++++++++++
 .../SimpleBlockingJdbcServiceClientTest.java       | 261 +++++++++++
 jdbc-proto/README.md                               |  26 ++
 jdbc-proto/pom.xml                                 |  68 +++
 .../org/apache/kyuubi/grpc/common/errors.proto     |  43 ++
 .../org/apache/kyuubi/grpc/jdbc/connection.proto   | 300 +++++++++++++
 .../org/apache/kyuubi/grpc/jdbc/request.proto      | 345 +++++++++++++++
 .../org/apache/kyuubi/grpc/jdbc/response.proto     |  46 ++
 .../org/apache/kyuubi/grpc/jdbc/schema.proto       | 103 +++++
 .../org/apache/kyuubi/grpc/jdbc/service.proto      |  54 +++
 pom.xml                                            | 136 ++++++
 33 files changed, 2815 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f4bef8b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.idea/
+.idea/codeStyles/*
+.idea/**/
+*.iml
+*.ipr
+*.iws
+*.tgz
+*.log
+*.log.*
+*.
+target/
+
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..ea95349
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,53 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <JetCodeStyleSettings>
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </JetCodeStyleSettings>
+    <ScalaCodeStyleSettings>
+      <option name="addFullQualifiedImports" value="false" />
+      <option name="importLayout">
+        <array>
+          <option value="java" />
+          <option value="_______ blank line _______" />
+          <option value="scala" />
+          <option value="_______ blank line _______" />
+          <option value="all other imports" />
+          <option value="_______ blank line _______" />
+          <option value="org.apache.kyuubi.*" />
+          <option value="_______ blank line _______" />
+        </array>
+      </option>
+      <option name="sortAsScalastyle" value="true" />
+      <option name="SPACE_AFTER_MODIFIERS_CONSTRUCTOR" value="true" />
+      <option name="SPACE_BEFORE_INFIX_METHOD_CALL_PARENTHESES" value="true" />
+      <option name="SPACE_BEFORE_INFIX_OPERATOR_LIKE_METHOD_CALL_PARENTHESES" value="false" />
+    </ScalaCodeStyleSettings>
+    <editorconfig>
+      <option name="ENABLED" value="false" />
+    </editorconfig>
+    <codeStyleSettings language="JAVA">
+      <indentOptions>
+        <option name="INDENT_SIZE" value="2" />
+        <option name="CONTINUATION_INDENT_SIZE" value="2" />
+        <option name="TAB_SIZE" value="2" />
+      </indentOptions>
+    </codeStyleSettings>
+    <codeStyleSettings language="Scala">
+      <option name="KEEP_LINE_BREAKS" value="false" />
+      <option name="KEEP_BLANK_LINES_IN_CODE" value="0" />
+      <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
+      <option name="BLANK_LINES_AROUND_CLASS" value="0" />
+      <arrangement>
+        <groups>
+          <group>
+            <type>DEPENDENT_METHODS</type>
+            <order>DEPTH_FIRST</order>
+          </group>
+        </groups>
+      </arrangement>
+    </codeStyleSettings>
+    <codeStyleSettings language="kotlin">
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </codeStyleSettings>
+  </code_scheme>
+</component>
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/.idea/copyright/apache.xml b/.idea/copyright/apache.xml
new file mode 100644
index 0000000..2cff3d1
--- /dev/null
+++ b/.idea/copyright/apache.xml
@@ -0,0 +1,6 @@
+<component name="CopyrightManager">
+  <copyright>
+    <option name="notice" value="Licensed to the Apache Software Foundation (ASF) under one or more&#10;contributor license agreements.  See the NOTICE file distributed with&#10;this work for additional information regarding copyright ownership.&#10;The ASF licenses this file to You under the Apache License, Version 2.0&#10;(the &quot;License&quot;); you may not use this file except in compliance with&#10;the License.  You may obtain a copy of the License at&#10;&#10;   http://www.apache [...]
+    <option name="myName" value="apache" />
+  </copyright>
+</component>
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..5c552d0
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/health-proto/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/jdbc-grpc-client/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/jdbc-grpc-client/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/jdbc-grpc-client/target/classes/generated-sources/protobuf/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/jdbc-proto/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/kyuubi-health-proto/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/kyuubi-jdbc-proto/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/kyuubi-service-grpc/target/generated-sources/protobuf/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/jpa-buddy.xml b/.idea/jpa-buddy.xml
new file mode 100644
index 0000000..966d5f5
--- /dev/null
+++ b/.idea/jpa-buddy.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JpaBuddyIdeaProjectConfig">
+    <option name="renamerInitialized" value="true" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..010c400
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="jpab" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/protoeditor.xml b/.idea/protoeditor.xml
new file mode 100644
index 0000000..e300808
--- /dev/null
+++ b/.idea/protoeditor.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProtobufLanguageSettings">
+    <option name="autoConfigEnabled" value="false" />
+    <option name="importPathEntries">
+      <list>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/src/main/resources" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/src/test/java" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/target/generated-sources/annotations" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/target/generated-sources/protobuf/java" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/target/generated-sources/protobuf/java" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/src/main/java" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/target/generated-test-sources/test-annotations" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/target/generated-sources/protobuf/grpc-java" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/../Library/Caches/JetBrains/IntelliJIdea2022.3/protoeditor" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-service-grpc/src/main/protobuf" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/kyuubi-jdbc-proto/src/main/protobuf" />
+        </ImportPathEntry>
+        <ImportPathEntry>
+          <option name="location" value="file://$PROJECT_DIR$/jdbc-proto/src/main/protobuf" />
+        </ImportPathEntry>
+      </list>
+    </option>
+    <option name="descriptorPath" value="google/protobuf/descriptor.proto" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/rSettings.xml b/.idea/rSettings.xml
new file mode 100644
index 0000000..6d7112b
--- /dev/null
+++ b/.idea/rSettings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RSettings">
+    <option name="interpreterPath" value="/usr/local/bin/R" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/sbt.xml b/.idea/sbt.xml
new file mode 100644
index 0000000..2018743
--- /dev/null
+++ b/.idea/sbt.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ScalaSbtSettings">
+    <option name="customVMPath" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/scopes/apache.xml b/.idea/scopes/apache.xml
new file mode 100644
index 0000000..1cca035
--- /dev/null
+++ b/.idea/scopes/apache.xml
@@ -0,0 +1,3 @@
+<component name="DependencyValidationManager">
+  <scope name="apache" pattern="" />
+</component>
\ No newline at end of file
diff --git a/.idea/thriftCompiler.xml b/.idea/thriftCompiler.xml
new file mode 100644
index 0000000..7bc123c
--- /dev/null
+++ b/.idea/thriftCompiler.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ThriftCompiler">
+    <compilers />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f7b794f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+# Apache Kyuubi Service RPC
+
+## Introduction
+
+Apache Kyuubi Service RPC is an RPC layer based on [Grpc](https://grpc.io/),
+It is designed to provide a unified RPC interface for different services in Kyuubi project.
diff --git a/health-proto/pom.xml b/health-proto/pom.xml
new file mode 100644
index 0000000..fe57525
--- /dev/null
+++ b/health-proto/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.kyuubi</groupId>
+        <artifactId>kyuubi-service-rpc</artifactId>
+        <version>0.1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>kyuubi-health-proto</artifactId>
+    <name>Kyuubi Health Service Protobuf Definition</name>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-protobuf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-stub</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.xolstice.maven.plugins</groupId>
+                <artifactId>protobuf-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-protoc-gen</id>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <phase>generate-sources</phase>
+                        <configuration>
+                            <sources>
+                                <source>${project.build.outputDirectory}/generated-sources/protobuf</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/health-proto/src/main/protobuf/org/apache/kyuubi/grpc/health/health.proto b/health-proto/src/main/protobuf/org/apache/kyuubi/grpc/health/health.proto
new file mode 100644
index 0000000..3bf48e0
--- /dev/null
+++ b/health-proto/src/main/protobuf/org/apache/kyuubi/grpc/health/health.proto
@@ -0,0 +1,66 @@
+// 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.
+
+// E.g. https://github.com/grpc/grpc/blob/master/doc/health-checking.md
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "org.apache.kyuubi.grpc.health";
+
+/* Request the services list that support reporting health status
+ * at the server-side
+ */
+message GetServicesReq {
+}
+
+message GetServicesResp {
+  repeated string services = 1;
+}
+
+message HealthCheckReq {
+  repeated string services = 1;
+}
+
+enum HealthStatus {
+  UNKNOWN = 0; // unable to do health check
+  HEALTHY = 1;
+  UNHEALTHY = 2;
+  NOT_FOUND = 3;
+}
+
+message HealthCheckResponse {
+  map<string, HealthStatus> statuses = 1;
+}
+
+service Health {
+  /* Request the services list that support reporting health status
+   * at the server-side
+   */
+  rpc GetServices(GetServicesReq) returns(GetServicesResp);
+
+  /* Query the health statuses of all registered services from the server-side.
+   * and a deadline should be set on the rpc.
+   * The client can optionally chose the service names(ids) it wants to query
+   * for health statuses.
+   */
+  rpc Check(HealthCheckReq) returns (HealthCheckResponse);
+
+  /* Perform a streaming health-check. The server will immediately send back a message
+   * indicating the current health status. It will then subsequently send a new message
+   * whenever the service's serving status changes.
+   */
+  rpc Watch(HealthCheckReq) returns (stream HealthCheckResponse);
+}
diff --git a/jdbc-grpc-client/pom.xml b/jdbc-grpc-client/pom.xml
new file mode 100644
index 0000000..95b8d8b
--- /dev/null
+++ b/jdbc-grpc-client/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.kyuubi</groupId>
+        <artifactId>kyuubi-service-rpc</artifactId>
+        <version>0.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>kyuubi-jdbc-grpc-client</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.kyuubi</groupId>
+            <artifactId>kyuubi-jdbc-proto</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-protobuf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-stub</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.2</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>2.1.214</version>
+        </dependency>
+    </dependencies>
+
+
+</project>
\ No newline at end of file
diff --git a/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/GrpcUtils.java b/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/GrpcUtils.java
new file mode 100644
index 0000000..3a72e5d
--- /dev/null
+++ b/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/GrpcUtils.java
@@ -0,0 +1,5 @@
+package org.apache.kyuubi.grpc;
+
+public class GrpcUtils {
+  final static Status OK = Status.newBuilder().setStatusCode(StatusCode.OK).setSqlState("00000").build();
+}
diff --git a/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/client/JdbcGrpcClient.java b/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/client/JdbcGrpcClient.java
new file mode 100644
index 0000000..e310057
--- /dev/null
+++ b/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/client/JdbcGrpcClient.java
@@ -0,0 +1,81 @@
+package org.apache.kyuubi.grpc.client;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.kyuubi.grpc.jdbc.DirectStatusResp;
+import org.apache.kyuubi.grpc.jdbc.connection.*;
+
+public interface JdbcGrpcClient {
+  DirectStatusResp openConnection(Map<String, String> configs, Optional<String> connectionId);
+
+  DirectStatusResp closeConnection(String connectionId);
+
+  DirectStatusResp abortConnection(String connectionId);
+
+  DirectStatusResp setAutoCommit(String connectionId, boolean autoCommit);
+
+  GetAutoCommitResp getAutoCommit(String connectionId);
+
+  NativeSQLResp nativeSQL(String connectionId, String sql);
+
+  DirectStatusResp commit(String connectionId);
+
+  DirectStatusResp rollback(String connectionId);
+
+  DirectStatusResp rollback(String connectionId, int savepointId);
+
+  DirectStatusResp rollback(String connectionId, String savepointName);
+
+  DirectStatusResp rollback(String connectionId, int savepointId, String savepointName);
+
+  DirectStatusResp setReadOnly(String connectionId, boolean readOnly);
+
+  IsReadOnlyResp isReadOnly(String connectionId);
+
+  DirectStatusResp setCatalog(String connectionId, String catalog);
+
+  GetCatalogResp getCatalog(String connectionId);
+
+  DirectStatusResp setTransactionIsolation(String connectionId, int level);
+
+  GetTransactionIsolationResp getTransactionIsolation(String connectionId);
+
+  GetWarningsResp getWarnings(String connectionId);
+
+  DirectStatusResp clearWarnings(String connectionId);
+
+  DirectStatusResp setClientInfo(String connectionId, Map<String, String> info);
+
+  DirectStatusResp setClientInfo(String connectionId, String name, String value);
+
+  GetClientInfoResp getClientInfo(String connectionId);
+
+  DirectStatusResp setTypeMap(String connectionId, Map<String, String> map);
+
+  GetTypeMapResp getTypeMap(String connectionId);
+
+  DirectStatusResp setHoldability(String connectionId, int holdability);
+
+  GetHoldabilityResp getHoldability(String connectionId);
+
+  DirectStatusResp setSchema(String connectionId, String schema);
+
+  GetSchemaResp getSchema(String connectionId);
+
+  DirectStatusResp setNetworkTimeout(String connectionId, int milliseconds);
+
+  GetNetworkTimeoutResp getNetworkTimeout(String connectionId);
+
+  SetSavepointResp setSavepoint(String connectionId);
+
+  SetSavepointResp setSavepoint(String connectionId, String name);
+
+  DirectStatusResp releaseSavepoint(String connectionId, Savepoint savepoint);
+
+  DirectStatusResp setSchema(String connectionId, String schema, String catalog);
+
+  IsValidResp isValid(String connectionId, int timeout);
+
+  DirectStatusResp getCatalogs(String connectionId);
+}
diff --git a/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/client/SimpleBlockingJdbcClient.java b/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/client/SimpleBlockingJdbcClient.java
new file mode 100644
index 0000000..a132594
--- /dev/null
+++ b/jdbc-grpc-client/src/main/java/org/apache/kyuubi/grpc/client/SimpleBlockingJdbcClient.java
@@ -0,0 +1,394 @@
+package org.apache.kyuubi.grpc.client;
+
+import io.grpc.*;
+import org.apache.kyuubi.grpc.jdbc.*;
+import org.apache.kyuubi.grpc.jdbc.JdbcGrpc.JdbcBlockingStub;
+import org.apache.kyuubi.grpc.jdbc.connection.*;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+public class SimpleBlockingJdbcClient implements JdbcGrpcClient {
+  private final Logger logger = Logger.getLogger(this.getClass().getName());
+  private JdbcBlockingStub blockingStub = null;
+  private ConnectionGrpc.ConnectionBlockingStub connectionBlockingStub = null;
+
+  public SimpleBlockingJdbcClient(Channel channel) {
+    blockingStub = JdbcGrpc.newBlockingStub(channel);
+    connectionBlockingStub = ConnectionGrpc.newBlockingStub(channel);
+  }
+
+  public SimpleBlockingJdbcClient(ManagedChannelBuilder builder) {
+    this(builder.build());
+  }
+
+  public SimpleBlockingJdbcClient(String host, int port, ChannelCredentials creds) {
+    this(Grpc.newChannelBuilderForAddress(host, port, creds));
+  }
+
+  public SimpleBlockingJdbcClient(String host, int port) {
+    this(host, port, InsecureChannelCredentials.create());
+  }
+
+  public SimpleBlockingJdbcClient(int port) {
+    this("localhost", port);
+  }
+
+  @Override
+  public DirectStatusResp openConnection(
+    Map<String, String> configs,
+    Optional<String> connectionId) {
+    OpenConnectionReq.Builder builder = OpenConnectionReq.newBuilder();
+    if (connectionId.isPresent()) {
+      logger.info("Reconnecting to server with existing connection" + connectionId.get());
+      builder.setConnectionId(connectionId.get());
+    }
+    OpenConnectionReq req = builder
+      .putAllConfigs(configs)
+      .build();
+    return connectionBlockingStub.openConnection(req);
+  }
+
+  @Override
+  public DirectStatusResp closeConnection(String connectionId) {
+    CloseConnectionReq.Builder builder = CloseConnectionReq.newBuilder();
+    CloseConnectionReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.closeConnection(req);
+  }
+
+  @Override
+  public DirectStatusResp abortConnection(String connectionId) {
+    AbortConnectionReq.Builder builder = AbortConnectionReq.newBuilder();
+    AbortConnectionReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.abortConnection(req);
+  }
+
+  @Override
+  public DirectStatusResp setClientInfo(String connectionId, Map<String, String> info) {
+    SetClientInfoReq.Builder builder = SetClientInfoReq.newBuilder();
+    SetClientInfoReq req = builder
+      .setConnectionId(connectionId)
+      .putAllConfigs(info)
+      .build();
+    return connectionBlockingStub.setClientInfo(req);
+  }
+
+  @Override
+  public DirectStatusResp setClientInfo(String connectionId, String name, String value) {
+    SetClientInfoReq.Builder builder = SetClientInfoReq.newBuilder();
+    SetClientInfoReq req = builder
+      .setConnectionId(connectionId)
+      .putConfigs(name, value)
+      .build();
+    return connectionBlockingStub.setClientInfo(req);
+  }
+
+  @Override
+  public GetClientInfoResp getClientInfo(String connectionId) {
+    GetClientInfoReq.Builder builder = GetClientInfoReq.newBuilder();
+    GetClientInfoReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getClientInfo(req);
+  }
+
+  @Override
+  public DirectStatusResp setTypeMap(String connectionId, Map<String, String> map) {
+    SetTypeMapReq.Builder builder = SetTypeMapReq.newBuilder();
+    SetTypeMapReq req = builder
+      .setConnectionId(connectionId)
+      .putAllTypeToClass(map)
+      .build();
+    return connectionBlockingStub.setTypeMap(req);
+  }
+
+  @Override
+  public GetTypeMapResp getTypeMap(String connectionId) {
+    GetTypeMapReq.Builder builder = GetTypeMapReq.newBuilder();
+    GetTypeMapReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getTypeMap(req);
+  }
+
+  @Override
+  public DirectStatusResp setHoldability(String connectionId, int holdability) {
+    SetHoldabilityReq.Builder builder = SetHoldabilityReq.newBuilder();
+    SetHoldabilityReq req = builder
+      .setConnectionId(connectionId)
+      .setHoldability(holdability)
+      .build();
+    return connectionBlockingStub.setHoldability(req);
+  }
+
+  @Override
+  public GetHoldabilityResp getHoldability(String connectionId) {
+    GetHoldabilityReq.Builder builder = GetHoldabilityReq.newBuilder();
+    GetHoldabilityReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getHoldability(req);
+  }
+
+  @Override
+  public DirectStatusResp setSchema(String connectionId, String schema) {
+    SetSchemaReq.Builder builder = SetSchemaReq.newBuilder();
+    SetSchemaReq req = builder
+      .setConnectionId(connectionId)
+      .setSchema(schema)
+      .build();
+    return connectionBlockingStub.setSchema(req);
+  }
+
+  @Override
+  public GetSchemaResp getSchema(String connectionId) {
+    GetSchemaReq.Builder builder = GetSchemaReq.newBuilder();
+    GetSchemaReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getSchema(req);
+  }
+
+  @Override
+  public DirectStatusResp setNetworkTimeout(String connectionId, int milliseconds) {
+    SetNetworkTimeoutReq.Builder builder = SetNetworkTimeoutReq.newBuilder();
+    SetNetworkTimeoutReq req = builder
+      .setConnectionId(connectionId)
+      .setMilliseconds(milliseconds)
+      .build();
+    return connectionBlockingStub.setNetworkTimeout(req);
+  }
+
+  @Override
+  public GetNetworkTimeoutResp getNetworkTimeout(String connectionId) {
+    GetNetworkTimeoutReq.Builder builder = GetNetworkTimeoutReq.newBuilder();
+    GetNetworkTimeoutReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getNetworkTimeout(req);
+  }
+
+  @Override
+  public SetSavepointResp setSavepoint(String connectionId) {
+    SetSavepointReq.Builder builder = SetSavepointReq.newBuilder();
+    SetSavepointReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.setSavepoint(req);
+  }
+
+  @Override
+  public SetSavepointResp setSavepoint(String connectionId, String name) {
+    SetSavepointReq.Builder builder = SetSavepointReq.newBuilder();
+    SetSavepointReq req = builder
+      .setConnectionId(connectionId)
+      .setSavepointName(name)
+      .build();
+    return connectionBlockingStub.setSavepoint(req);
+  }
+
+  @Override
+  public DirectStatusResp releaseSavepoint(String connectionId, Savepoint savepoint) {
+    ReleaseSavepointReq.Builder builder = ReleaseSavepointReq.newBuilder();
+    ReleaseSavepointReq req = builder
+      .setConnectionId(connectionId)
+      .setSavepoint(savepoint)
+      .build();
+    return connectionBlockingStub.releaseSavepoint(req);
+  }
+
+
+  @Override
+  public DirectStatusResp setSchema(String connectionId, String schema, String catalog) {
+    SetSchemaReq.Builder builder = SetSchemaReq.newBuilder();
+    SetSchemaReq req = builder
+      .setConnectionId(connectionId)
+      .setSchema(schema)
+      .build();
+    return connectionBlockingStub.setSchema(req);
+  }
+
+  @Override
+  public IsValidResp isValid(String connectionId, int timeout) {
+    IsValidReq.Builder builder = IsValidReq.newBuilder();
+    IsValidReq req = builder
+      .setConnectionId(connectionId)
+      .setTimeout(timeout)
+      .build();
+    return connectionBlockingStub.isValid(req);
+  }
+
+  @Override
+  public NativeSQLResp nativeSQL(String connectionId, String sql) {
+    NativeSQLReq.Builder builder = NativeSQLReq.newBuilder();
+    NativeSQLReq req = builder
+      .setConnectionId(connectionId)
+      .setSql(sql)
+      .build();
+    return connectionBlockingStub.nativeSQL(req);
+  }
+
+  @Override
+  public DirectStatusResp setAutoCommit(String connectionId, boolean autoCommit) {
+    SetAutoCommitReq.Builder builder = SetAutoCommitReq.newBuilder();
+    SetAutoCommitReq req = builder
+      .setConnectionId(connectionId)
+      .setAutoCommit(autoCommit)
+      .build();
+    return connectionBlockingStub.setAutoCommit(req);
+  }
+
+  @Override
+  public GetAutoCommitResp getAutoCommit(String connectionId) {
+    GetAutoCommitReq.Builder builder = GetAutoCommitReq.newBuilder();
+    GetAutoCommitReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getAutoCommit(req);
+  }
+
+  @Override
+  public DirectStatusResp commit(String connectionId) {
+    CommitReq.Builder builder = CommitReq.newBuilder();
+    CommitReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.commit(req);
+  }
+
+  @Override
+  public DirectStatusResp rollback(String connectionId) {
+    RollbackReq.Builder builder = RollbackReq.newBuilder();
+    RollbackReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.rollback(req);
+  }
+
+  @Override
+  public DirectStatusResp rollback(String connectionId, int savepointId) {
+    Savepoint sp = Savepoint.newBuilder()
+      .setSavepointId(savepointId)
+      .build();
+    RollbackReq req = RollbackReq.newBuilder()
+      .setConnectionId(connectionId)
+      .setSavepoint(sp)
+      .build();
+    return connectionBlockingStub.rollback(req);
+  }
+
+  @Override
+  public DirectStatusResp rollback(String connectionId, int savepointId, String savepointName) {
+    Savepoint sp = Savepoint.newBuilder()
+      .setSavepointId(savepointId)
+      .setSavepointName(savepointName)
+      .build();
+    RollbackReq req = RollbackReq.newBuilder()
+      .setConnectionId(connectionId)
+      .setSavepoint(sp)
+      .build();
+    return connectionBlockingStub.rollback(req);
+  }
+
+  @Override
+  public DirectStatusResp rollback(String connectionId, String savepointName) {
+    Savepoint sp = Savepoint.newBuilder()
+      .setSavepointName(savepointName)
+      .build();
+    RollbackReq req = RollbackReq.newBuilder()
+      .setConnectionId(connectionId)
+      .setSavepoint(sp)
+      .build();
+    return connectionBlockingStub.rollback(req);
+  }
+
+  @Override
+  public DirectStatusResp setReadOnly(String connectionId, boolean readOnly) {
+    SetReadOnlyReq.Builder builder = SetReadOnlyReq.newBuilder();
+    SetReadOnlyReq req = builder
+      .setConnectionId(connectionId)
+      .setReadOnly(readOnly)
+      .build();
+    return connectionBlockingStub.setReadOnly(req);
+  }
+
+  @Override
+  public IsReadOnlyResp isReadOnly(String connectionId) {
+    IsReadOnlyReq.Builder builder = IsReadOnlyReq.newBuilder();
+    IsReadOnlyReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.isReadOnly(req);
+  }
+
+  @Override
+  public DirectStatusResp setCatalog(String connectionId, String catalog) {
+    SetCatalogReq.Builder builder = SetCatalogReq.newBuilder();
+    SetCatalogReq req = builder
+      .setConnectionId(connectionId)
+      .setCatalog(catalog)
+      .build();
+    return connectionBlockingStub.setCatalog(req);
+  }
+
+  @Override
+  public GetCatalogResp getCatalog(String connectionId) {
+    GetCatalogReq.Builder builder = GetCatalogReq.newBuilder();
+    GetCatalogReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getCatalog(req);
+  }
+
+
+  @Override
+  public DirectStatusResp setTransactionIsolation(String connectionId, int level) {
+    SetTransactionIsolationReq.Builder builder = SetTransactionIsolationReq.newBuilder();
+    SetTransactionIsolationReq req = builder
+      .setConnectionId(connectionId)
+      .setLevel(level)
+      .build();
+    return connectionBlockingStub.setTransactionIsolation(req);
+  }
+
+  @Override
+  public GetTransactionIsolationResp getTransactionIsolation(String connectionId) {
+    GetTransactionIsolationReq.Builder builder = GetTransactionIsolationReq.newBuilder();
+    GetTransactionIsolationReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getTransactionIsolation(req);
+  }
+
+  @Override
+  public DirectStatusResp clearWarnings(String connectionId) {
+    ClearWarningsReq.Builder builder = ClearWarningsReq.newBuilder();
+    ClearWarningsReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.clearWarnings(req);
+  }
+
+  @Override
+  public GetWarningsResp getWarnings(String connectionId) {
+    GetWarningsReq.Builder builder = GetWarningsReq.newBuilder();
+    GetWarningsReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return connectionBlockingStub.getWarnings(req);
+  }
+
+  @Override
+  public DirectStatusResp getCatalogs(String connectionId) {
+    GetCatalogsReq.Builder builder = GetCatalogsReq.newBuilder();
+    GetCatalogsReq req = builder
+      .setConnectionId(connectionId)
+      .build();
+    return blockingStub.getCatalogs(req);
+  }
+}
diff --git a/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/DummyJdbcService.java b/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/DummyJdbcService.java
new file mode 100644
index 0000000..6b30546
--- /dev/null
+++ b/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/DummyJdbcService.java
@@ -0,0 +1,40 @@
+package org.apache.kyuubi.grpc;
+
+import io.grpc.stub.StreamObserver;
+import org.apache.kyuubi.grpc.jdbc.DirectStatusResp;
+import org.apache.kyuubi.grpc.jdbc.GetCatalogsReq;
+import org.apache.kyuubi.grpc.jdbc.JdbcGrpc.JdbcImplBase;
+
+import java.io.IOException;
+
+import static org.apache.kyuubi.grpc.GrpcUtils.OK;
+
+public class DummyJdbcService extends JdbcImplBase {
+
+
+  public DummyJdbcService() throws IOException {
+  }
+
+
+
+  @Override
+  public void getCatalogs(GetCatalogsReq req, StreamObserver<DirectStatusResp> respOb) {
+    DirectStatusResp.Builder builder = DirectStatusResp.newBuilder();
+    try {
+      if (req.getConnectionId().isEmpty()) {
+        throw new IllegalArgumentException("Connection Id can not be empty");
+      } else {
+        builder.setIdentifier("apache kyuubi").setStatus(OK);
+      }
+    } catch (Exception e) {
+      Status status = Status.newBuilder()
+        .setStatusCode(StatusCode.ERROR)
+        .setSqlState("2E000")
+        .setErrorMessage("invalid connection id" + req.getConnectionId())
+        .build();
+      builder.setStatus(status);
+    }
+    respOb.onNext(builder.build());
+    respOb.onCompleted();
+  }
+}
diff --git a/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/TestConnectionService.java b/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/TestConnectionService.java
new file mode 100644
index 0000000..1a9617b
--- /dev/null
+++ b/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/TestConnectionService.java
@@ -0,0 +1,492 @@
+package org.apache.kyuubi.grpc;
+
+
+import io.grpc.stub.StreamObserver;
+import org.apache.kyuubi.grpc.jdbc.*;
+import org.apache.kyuubi.grpc.jdbc.connection.ConnectionGrpc;
+import org.apache.kyuubi.grpc.jdbc.connection.*;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import static org.apache.kyuubi.grpc.GrpcUtils.OK;
+
+public class TestConnectionService extends ConnectionGrpc.ConnectionImplBase {
+
+  private final Path _tempDir = Files.createTempDirectory(getClass().getSimpleName());
+
+  public String defaultCatalogName = _tempDir.getFileName().toString().toUpperCase();
+  private final String jdbcUrl = "jdbc:h2:" + _tempDir + ";MODE=DB2;user=testUser;password=testPass";
+  private Connection _connection = null;
+
+  private final Executor executor = Executors.newSingleThreadExecutor();
+
+  private final Map<Savepoint, java.sql.Savepoint> savepoints = new HashMap<>();
+
+  public void setConnection(Connection _connection) {
+    this._connection = _connection;
+  }
+
+  public Connection getConnection() throws SQLException {
+    if (this._connection == null) {
+      Connection conn = DriverManager.getConnection(jdbcUrl);
+      setConnection(conn);
+      return conn;
+    } else {
+      return _connection;
+    }
+  }
+
+  public TestConnectionService() throws IOException {
+  }
+
+  private Status errorStatus(Exception e) {
+    return Status.newBuilder()
+      .setSqlState("38808")
+      .setErrorMessage(e.getMessage())
+      .setStatusCode(StatusCode.ERROR)
+      .build();
+  }
+
+  private DirectStatusResp error(Exception e) {
+    return DirectStatusResp
+      .newBuilder()
+      .setStatus(errorStatus(e))
+      .build();
+  }
+
+  private DirectStatusResp ok(String id) {
+    return DirectStatusResp
+      .newBuilder()
+      .setStatus(OK)
+      .setIdentifier(id)
+      .build();
+  }
+
+  @Override
+  public void openConnection(OpenConnectionReq req, StreamObserver<DirectStatusResp> respOb) {
+    DirectStatusResp.Builder builder = DirectStatusResp.newBuilder();
+    if (req.getConnectionId().isEmpty()) {
+      builder.setIdentifier("hello, kyuubi");
+    } else {
+      builder.setIdentifier("hello, " + req.getConnectionId());
+    }
+    try {
+      Properties properties = new Properties();
+      properties.putAll(req.getConfigsMap());
+      setConnection(
+        DriverManager.getConnection(jdbcUrl, properties));
+      builder.putExtraInfo("apache", "kyuubi");
+      builder.putExtraInfo("Kyuubi", "Serverless SQL on Lakehouse");
+      builder.setStatus(OK);
+      respOb.onNext(builder.build());
+    } catch (SQLException e) {
+      Status status = Status
+        .newBuilder()
+        .setStatusCode(StatusCode.ERROR)
+        .setErrorMessage(e.getMessage())
+        .setSqlState("0AS86")
+        .build();
+      builder.setStatus(status);
+      respOb.onNext(builder.build());
+    }
+
+    respOb.onCompleted();
+  }
+
+  @Override
+  public void closeConnection(CloseConnectionReq req, StreamObserver<DirectStatusResp> respOb) {
+    DirectStatusResp.Builder builder = DirectStatusResp.newBuilder();
+    try {
+      if (req.getConnectionId().isEmpty()) {
+        throw new IllegalArgumentException("ConnectionId cannot be empty for CloseConnection");
+      } else {
+        builder.setIdentifier(req.getConnectionId());
+      }
+      builder.setStatus(OK);
+    } catch (Exception e) {
+      Status status = Status.newBuilder()
+        .setStatusCode(StatusCode.ERROR)
+        .setSqlState("2E000")
+        .setErrorMessage("invalid connection id" + req.getConnectionId())
+        .build();
+      builder.setStatus(status);
+    }
+    respOb.onNext(builder.build());
+    respOb.onCompleted();
+  }
+
+  @Override
+  public void nativeSQL(NativeSQLReq request, StreamObserver<NativeSQLResp> responseObserver) {
+    NativeSQLResp.Builder builder = NativeSQLResp.newBuilder();
+    try {
+      String nativeSQL = getConnection().nativeSQL(request.getSql());
+      NativeSQLResp resp = builder
+        .setSql(nativeSQL)
+        .setStatus(OK)
+        .build();
+      responseObserver.onNext(resp);
+    } catch (Exception e) {
+      Status status = Status.newBuilder()
+        .setSqlState("38808")
+        .setErrorMessage(e.getMessage())
+        .setStatusCode(StatusCode.ERROR)
+        .build();
+      NativeSQLResp resp = builder.setStatus(status).build();
+      responseObserver.onNext(resp);
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void setAutoCommit(SetAutoCommitReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().setAutoCommit(request.getAutoCommit());
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void getAutoCommit(GetAutoCommitReq request, StreamObserver<GetAutoCommitResp> responseObserver) {
+    GetAutoCommitResp.Builder builder = GetAutoCommitResp.newBuilder();
+    try {
+      boolean autoCommit = getConnection().getAutoCommit();
+      GetAutoCommitResp resp = builder
+        .setStatus(OK)
+        .setAutoCommit(autoCommit)
+        .build();
+      responseObserver.onNext(resp);
+    } catch (SQLException e) {
+      responseObserver.onNext(builder.setStatus(errorStatus(e)).build());
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void commit(CommitReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().commit();
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void rollback(RollbackReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      Connection conn = getConnection();
+      Savepoint savepoint = request.getSavepoint();
+      if (savepoint == Savepoint.getDefaultInstance()) {
+        conn.rollback();
+      } else {
+        conn.rollback(savepoints.get(savepoint));
+      }
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void setReadOnly(SetReadOnlyReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().setReadOnly(request.getReadOnly());
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void isReadOnly(IsReadOnlyReq request, StreamObserver<IsReadOnlyResp> responseObserver) {
+    IsReadOnlyResp.Builder builder = IsReadOnlyResp.newBuilder();
+    try {
+      IsReadOnlyResp resp = builder
+        .setStatus(OK)
+        .setReadOnly(getConnection().isReadOnly())
+        .build();
+      responseObserver.onNext(resp);
+    } catch (SQLException e) {
+      IsReadOnlyResp resp = builder
+        .setStatus(errorStatus(e))
+        .build();
+      responseObserver.onNext(resp);
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void setCatalog(SetCatalogReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().setCatalog(request.getCatalog());
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void getCatalog(GetCatalogReq request, StreamObserver<GetCatalogResp> responseObserver) {
+    GetCatalogResp.Builder builder = GetCatalogResp.newBuilder();
+    try {
+      String catalog = getConnection().getCatalog();
+      GetCatalogResp resp = builder
+        .setStatus(OK)
+        .setCatalog(catalog)
+        .build();
+      responseObserver.onNext(resp);
+    } catch (SQLException e) {
+      GetCatalogResp resp = GetCatalogResp.newBuilder()
+        .setStatus(errorStatus(e))
+        .build();
+      responseObserver.onNext(resp);
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void setTransactionIsolation(SetTransactionIsolationReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().setTransactionIsolation(request.getLevel());
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void getTransactionIsolation(GetTransactionIsolationReq request, StreamObserver<GetTransactionIsolationResp> responseObserver) {
+    GetTransactionIsolationResp.Builder builder = GetTransactionIsolationResp.newBuilder();
+    try {
+      int level = getConnection().getTransactionIsolation();
+      GetTransactionIsolationResp resp = builder
+        .setStatus(OK)
+        .setLevel(level)
+        .build();
+      responseObserver.onNext(resp);
+    } catch (SQLException e) {
+      GetTransactionIsolationResp resp = builder
+        .setStatus(errorStatus(e))
+        .build();
+      responseObserver.onNext(resp);
+    }
+    responseObserver.onCompleted();
+  }
+
+  public static SQLWarning buildWarning(java.sql.SQLWarning cur) {
+    SQLWarning.Builder builder = SQLWarning.newBuilder();
+    if (cur.getMessage() != null) {
+      builder.setReason(cur.getMessage());
+    }
+    if (cur.getSQLState() != null) {
+      builder.setSqlState(cur.getSQLState());
+    }
+    if (cur.getErrorCode() != 0) {
+      builder.setVendorCode(cur.getErrorCode());
+    }
+    if (cur.getNextWarning() != null) {
+      builder.setNextWarning(buildWarning(cur.getNextWarning()));
+    }
+    return builder.build();
+  }
+  @Override
+  public void getWarnings(GetWarningsReq request, StreamObserver<GetWarningsResp> responseObserver) {
+    GetWarningsResp.Builder builder = GetWarningsResp.newBuilder();
+    try {
+      java.sql.SQLWarning warnings = getConnection().getWarnings();
+
+      if (warnings != null) {
+        builder.setWarnings(buildWarning(getConnection().getWarnings()));
+      }
+      responseObserver.onNext(builder.setStatus(OK).build());
+    } catch (SQLException e) {
+      responseObserver.onNext(builder.setStatus(errorStatus(e)).build());
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void clearWarnings(ClearWarningsReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().clearWarnings();
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+
+  @Override
+  public void setTypeMap(SetTypeMapReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
+      for (Map.Entry<String, String> entry : request.getTypeToClassMap().entrySet()) {
+        classMap.put(entry.getKey(), Class.forName(entry.getValue()));
+      }
+      getConnection().setTypeMap(classMap);
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (Exception e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void getTypeMap(GetTypeMapReq request, StreamObserver<GetTypeMapResp> responseObserver) {
+    GetTypeMapResp.Builder builder = GetTypeMapResp.newBuilder();
+    try {
+      Map<String, Class<?>> typeMap = getConnection().getTypeMap();
+      if (typeMap != null) {
+        Map<String, String> typeToClassMap = new HashMap<String, String>();
+        for (Map.Entry<String, Class<?>> entry : typeMap.entrySet()) {
+          typeToClassMap.put(entry.getKey(), entry.getValue().getName());
+        }
+        builder.putAllTypeToClass(typeToClassMap);
+      }
+      responseObserver.onNext(builder.setStatus(OK).build());
+    } catch (SQLException e) {
+      responseObserver.onNext(builder.setStatus(errorStatus(e)).build());
+    }
+    responseObserver.onCompleted();
+
+  }
+
+  @Override
+  public void setSchema(SetSchemaReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().setSchema(request.getSchema());
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void getSchema(GetSchemaReq request, StreamObserver<GetSchemaResp> responseObserver) {
+    GetSchemaResp.Builder builder = GetSchemaResp.newBuilder();
+    try {
+      String schema = getConnection().getSchema();
+      // schema can be null, can you verify this?
+      GetSchemaResp resp = builder
+        .setStatus(OK)
+        .setSchema(schema)
+        .build();
+      responseObserver.onNext(resp);
+    } catch (SQLException e) {
+      GetSchemaResp resp = builder
+        .setStatus(errorStatus(e))
+        .build();
+      responseObserver.onNext(resp);
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void setNetworkTimeout(SetNetworkTimeoutReq request, StreamObserver<DirectStatusResp> responseObserver) {
+     try {
+        getConnection().setNetworkTimeout(executor, request.getMilliseconds());
+        responseObserver.onNext(ok(request.getConnectionId()));
+      } catch (SQLException e) {
+        responseObserver.onNext(error(e));
+      }
+      responseObserver.onCompleted();
+  }
+
+  @Override
+  public void getNetworkTimeout(GetNetworkTimeoutReq request, StreamObserver<GetNetworkTimeoutResp> responseObserver) {
+    GetNetworkTimeoutResp.Builder builder = GetNetworkTimeoutResp.newBuilder();
+    try {
+      int timeout = getConnection().getNetworkTimeout();
+      GetNetworkTimeoutResp resp = builder
+        .setStatus(OK)
+        .setMilliseconds(timeout)
+        .build();
+      responseObserver.onNext(resp);
+    } catch (SQLException e) {
+      GetNetworkTimeoutResp resp = builder
+        .setStatus(errorStatus(e))
+        .build();
+      responseObserver.onNext(resp);
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void isValid(IsValidReq request, StreamObserver<IsValidResp> responseObserver) {
+    IsValidResp.Builder builder = IsValidResp.newBuilder();
+    try {
+      boolean valid = getConnection().isValid(request.getTimeout());
+      IsValidResp resp = builder
+        .setStatus(OK)
+        .setValid(valid)
+        .build();
+      responseObserver.onNext(resp);
+    } catch (SQLException e) {
+      IsValidResp resp = builder
+        .setStatus(errorStatus(e))
+        .build();
+      responseObserver.onNext(resp);
+    }
+  }
+
+  @Override
+  public void abortConnection(AbortConnectionReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      getConnection().abort(executor);
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void setClientInfo(SetClientInfoReq request, StreamObserver<DirectStatusResp> responseObserver) {
+    try {
+      Properties properties = new Properties();
+      properties.putAll(request.getConfigsMap());
+      getConnection().setClientInfo(properties);
+      responseObserver.onNext(ok(request.getConnectionId()));
+    } catch (SQLException e) {
+      responseObserver.onNext(error(e));
+    }
+    responseObserver.onCompleted();
+  }
+
+  @Override
+  public void getClientInfo(GetClientInfoReq request, StreamObserver<GetClientInfoResp> responseObserver) {
+    GetClientInfoResp.Builder builder = GetClientInfoResp.newBuilder();
+    try {
+      Properties properties = getConnection().getClientInfo();
+      // use a loop to put all properties into the builder
+      for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+        builder.putConfigs(entry.getKey().toString(), entry.getValue().toString());
+      }
+      responseObserver.onNext(builder.setStatus(OK).build());
+    } catch (SQLException e) {
+      responseObserver.onNext(builder.setStatus(errorStatus(e)).build());
+    }
+    responseObserver.onCompleted();
+  }
+
+}
diff --git a/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/client/SimpleBlockingJdbcServiceClientTest.java b/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/client/SimpleBlockingJdbcServiceClientTest.java
new file mode 100644
index 0000000..098746a
--- /dev/null
+++ b/jdbc-grpc-client/src/test/java/org/apache/kyuubi/grpc/client/SimpleBlockingJdbcServiceClientTest.java
@@ -0,0 +1,261 @@
+package org.apache.kyuubi.grpc.client;
+
+import io.grpc.*;
+import org.apache.kyuubi.grpc.*;
+import org.apache.kyuubi.grpc.Status;
+import org.apache.kyuubi.grpc.jdbc.DirectStatusResp;
+import org.apache.kyuubi.grpc.jdbc.connection.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+
+public class SimpleBlockingJdbcServiceClientTest {
+
+  private final int port = 0;
+
+  private Server server = null;
+  private SimpleBlockingJdbcClient client = null;
+  DummyJdbcService dummyFrontendService = new DummyJdbcService();
+  TestConnectionService dummyConnectionService = new TestConnectionService();
+
+  public SimpleBlockingJdbcServiceClientTest() throws IOException {
+  }
+
+  @Before
+  public void setUp() throws IOException {
+    ServerCredentials serverCredentials = InsecureServerCredentials.create();
+    ServerBuilder<?> builder = Grpc.newServerBuilderForPort(port, serverCredentials)
+      .addService(dummyConnectionService)
+      .addService(dummyFrontendService);
+    server = builder.build();
+    server.start();
+    client = new SimpleBlockingJdbcClient(server.getPort());
+  }
+
+  @After
+  public void tearDown() {
+    if (server != null) {
+      try {
+        server.awaitTermination(5, TimeUnit.SECONDS);
+      } catch (InterruptedException e) {
+        // do nothing
+      }
+    }
+  }
+
+  @Test
+  public void testServer() {
+    assertNotEquals(port, server.getPort());
+    assertFalse(server.isTerminated());
+  }
+
+  @Test
+  public void testOpenConnection() {
+    Map<String, String> configs = new HashMap<>();
+    DirectStatusResp resp = client.openConnection(configs, Optional.empty());
+    Status status = resp.getStatus();
+    assertEquals("hello, kyuubi", resp.getIdentifier());
+    assertEquals(StatusCode.OK, status.getStatusCode());
+    assertEquals("00000", status.getSqlState());
+    assertEquals("Serverless SQL on Lakehouse", resp.getExtraInfoOrThrow("Kyuubi"));
+
+    configs.put("apache", "kyuubi");
+    configs.put("kent", "yao");
+    DirectStatusResp resp1 = client.openConnection(configs, Optional.of("20181117"));
+    Status status1 = resp1.getStatus();
+    assertEquals("hello, 20181117", resp1.getIdentifier());
+    assertEquals(StatusCode.OK, status1.getStatusCode());
+    assertEquals("kyuubi", resp1.getExtraInfoOrThrow("apache"));
+  }
+
+  @Test
+  public void testCloseConnection() {
+    DirectStatusResp resp1 = client.closeConnection("");
+    Status resp1Status = resp1.getStatus();
+    assertEquals(StatusCode.ERROR, resp1Status.getStatusCode());
+    assertEquals("2E000", resp1Status.getSqlState());
+    assertEquals("invalid connection id", resp1Status.getErrorMessage());
+    DirectStatusResp resp2 = client.closeConnection("apache kyuubi");
+    Status resp2Status = resp2.getStatus();
+    assertEquals(StatusCode.OK, resp2Status.getStatusCode());
+    assertEquals("00000", resp2Status.getSqlState());
+  }
+
+  @Test
+  public void testSetClientInfo() {
+    DirectStatusResp resp = client.setClientInfo("apache kyuubi", Collections.emptyMap());
+    Status status = resp.getStatus();
+    assertEquals(StatusCode.OK, status.getStatusCode());
+    assertEquals("00000", status.getSqlState());
+  }
+
+  @Test
+  public void testGetCatalogs() {
+    DirectStatusResp resp = client.getCatalogs("kyuubi");
+    Status status = resp.getStatus();
+    assertEquals(StatusCode.OK, status.getStatusCode());
+    assertEquals("00000", status.getSqlState());
+    DirectStatusResp resp1 = client.getCatalogs("");
+    assertEquals("", resp1.getIdentifier());
+    assertEquals(StatusCode.ERROR, resp1.getStatus().getStatusCode());
+  }
+
+  @Test
+  public void testNativeSQL() {
+    NativeSQLResp resp = client.nativeSQL("kyuubi", "SELECT {fn NOW()}");
+    Status status = resp.getStatus();
+    assertEquals(StatusCode.OK, status.getStatusCode());
+    String sql = resp.getSql();
+    assertFalse(sql.contains("fn"));
+  }
+
+
+  @Test
+  public void testSetAndGetAutoCommit() {
+    GetAutoCommitResp resp = client.getAutoCommit("kyuubi");
+    assertTrue(resp.getAutoCommit());
+    client.setAutoCommit("kyuubi", false);
+    GetAutoCommitResp resp1 = client.getAutoCommit("kyuubi");
+    assertFalse(resp1.getAutoCommit());
+  }
+
+  @Test
+  public void testCommitAndRollback() {
+    DirectStatusResp resp = client.commit("kyuubi");
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+    DirectStatusResp resp1 = client.rollback("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+    DirectStatusResp resp2 = client.rollback("kyuubi", "kyuubi_savepoint");
+    assertEquals(StatusCode.ERROR, resp2.getStatus().getStatusCode());
+  }
+
+  @Test
+  public void testSetReadOnly() {
+    DirectStatusResp resp = client.setReadOnly("kyuubi", true);
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+    DirectStatusResp resp1 = client.setReadOnly("kyuubi", false);
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+  }
+
+  @Test
+  public void testIsReadOnly() {
+    IsReadOnlyResp resp = client.isReadOnly("kyuubi");
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+    boolean readOnly = resp.getReadOnly();
+    assertFalse(readOnly);
+  }
+
+  @Test
+  public void testSetAndGetCatalog() {
+    // H2 doesn't support switch catalog and the op will be ignored without any exception
+    DirectStatusResp resp = client.setCatalog("kyuubi", "kyuubi");
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+    GetCatalogResp resp1 = client.getCatalog("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+    String catalog = resp1.getCatalog();
+    assertEquals(dummyConnectionService.defaultCatalogName, catalog);
+  }
+
+  @Test
+  public void testSetAndGetTransactionIsolationLevel() {
+    // case 1: set/get transaction isolation level
+    DirectStatusResp resp = client.setTransactionIsolation("kyuubi", 1);
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+    // case 2: get transaction isolation level
+    GetTransactionIsolationResp resp1 = client.getTransactionIsolation("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+    int level = resp1.getLevel();
+    assertEquals(1, level);
+  }
+
+  @Test
+  public void testBuildSQLWarnings() {
+    java.sql.SQLWarning warning1 = new java.sql.SQLWarning("warning1");
+    warning1.setNextWarning(new java.sql.SQLWarning("warning2"));
+    SQLWarning warning = TestConnectionService.buildWarning(warning1);
+    assertEquals("warning1", warning.getReason());
+    assertEquals("warning2", warning.getNextWarning().getReason());
+  }
+
+  @Test
+  public void testGetAndClearWarnings() {
+    GetWarningsResp resp = client.getWarnings("kyuubi");
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+    SQLWarning warning = resp.getWarnings();
+    assertEquals(SQLWarning.getDefaultInstance(), warning);
+    DirectStatusResp resp1 = client.clearWarnings("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+  }
+
+  @Test
+  public void testGetAndSetSchema() {
+    // case 1: set/get schema
+    DirectStatusResp resp = client.setSchema("kyuubi", "kyuubi");
+    assertEquals(StatusCode.ERROR, resp.getStatus().getStatusCode());
+    assertTrue(resp.getStatus().getErrorMessage().contains("not found"));
+    DirectStatusResp resp0 = client.setSchema("kyuubi", "PUBLIC");
+    assertEquals(StatusCode.OK, resp0.getStatus().getStatusCode());
+    GetSchemaResp resp1 = client.getSchema("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+    String schema = resp1.getSchema();
+    assertEquals("PUBLIC", schema);
+  }
+
+  @Test
+  public void testGetAndSetNetworkTimeout() {
+    // case 1: set/get network timeout
+    DirectStatusResp resp = client.setNetworkTimeout("kyuubi", 1000);
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+    GetNetworkTimeoutResp resp1 = client.getNetworkTimeout("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+    int timeout = resp1.getMilliseconds();
+    assertEquals(0, timeout);
+  }
+
+  @Test
+  public void testGetAndSetClientInfo() {
+    HashMap<String, String> configs = new HashMap<>();
+    configs.put("key1", "value1");
+    DirectStatusResp resp = client.setClientInfo("kyuubi", configs);
+
+    assertEquals(StatusCode.ERROR, resp.getStatus().getStatusCode());
+    assertTrue(resp.getStatus().getErrorMessage().contains("not supported"));
+    configs.clear();
+    configs.put("ApplicationName", "value1");
+    DirectStatusResp resp0 = client.setClientInfo("kyuubi", configs);
+    assertEquals(StatusCode.OK, resp0.getStatus().getStatusCode());
+    GetClientInfoResp resp1 = client.getClientInfo("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+    Map<String, String> clientInfo = resp1.getConfigsMap();
+    configs.put("numServers", "0");
+    assertEquals(configs, clientInfo);
+  }
+
+  @Test
+  public void testGetAndSetClientInfoString() {
+    DirectStatusResp resp = client.setClientInfo("kyuubi", "key1", "value1");
+    assertEquals(StatusCode.ERROR, resp.getStatus().getStatusCode());
+    assertTrue(resp.getStatus().getErrorMessage().contains("not supported"));
+    DirectStatusResp resp0 = client.setClientInfo("kyuubi", "ApplicationName", "value1");
+    assertEquals(StatusCode.OK, resp0.getStatus().getStatusCode());
+    GetClientInfoResp resp1 = client.getClientInfo("kyuubi");
+    assertEquals(StatusCode.OK, resp1.getStatus().getStatusCode());
+    Map<String, String> clientInfo = resp1.getConfigsMap();
+    assertEquals("value1", clientInfo.get("ApplicationName"));
+  }
+
+  @Test
+  public void testAbortConnection() {
+    DirectStatusResp resp = client.abortConnection("kyuubi");
+    assertEquals(StatusCode.OK, resp.getStatus().getStatusCode());
+  }
+}
\ No newline at end of file
diff --git a/jdbc-proto/README.md b/jdbc-proto/README.md
new file mode 100644
index 0000000..a32ad9c
--- /dev/null
+++ b/jdbc-proto/README.md
@@ -0,0 +1,26 @@
+<!--
+ - 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.
+ -->
+
+# Kyuubi JDBC Proto Definition
+
+## Introduction
+
+A [Protocol Buffers](https://developers.google.com/protocol-buffers)
+(a.k.a., protobuf) definition for building Java Database Connectivity (JDBC)
+client and services.
+
+
diff --git a/jdbc-proto/pom.xml b/jdbc-proto/pom.xml
new file mode 100644
index 0000000..0d9483d
--- /dev/null
+++ b/jdbc-proto/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.kyuubi</groupId>
+        <artifactId>kyuubi-service-rpc</artifactId>
+        <version>0.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>kyuubi-jdbc-proto</artifactId>
+    <name>Kyuubi JDBC Service Protobuf Definition</name>
+    <url>https://kyuubi.apache.org/</url>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-protobuf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-stub</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.xolstice.maven.plugins</groupId>
+                <artifactId>protobuf-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-protoc-gen</id>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <phase>generate-sources</phase>
+                        <configuration>
+                            <sources>
+                                <source>${project.build.outputDirectory}/generated-sources/protobuf</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/common/errors.proto b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/common/errors.proto
new file mode 100644
index 0000000..def741e
--- /dev/null
+++ b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/common/errors.proto
@@ -0,0 +1,43 @@
+// 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.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "org.apache.kyuubi.grpc";
+
+enum StatusCode {
+  OK = 0;
+  ERROR = 1;
+}
+
+message Status {
+  StatusCode status_code = 1;
+  string sql_state = 2;
+  // for legacy jdbc engines
+  uint32 error_code = 3;
+  string error_message = 4;
+}
+
+message SQLWarning {
+  // a description of the warning
+  string reason = 1;
+  // an XOPEN or SQL:2003 code identifying the warning
+  string sql_state = 2;
+  // a database vendor-specific warning code
+  uint32 vendor_code = 3;
+  SQLWarning next_warning = 4;
+}
\ No newline at end of file
diff --git a/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/connection.proto b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/connection.proto
new file mode 100644
index 0000000..bcde549
--- /dev/null
+++ b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/connection.proto
@@ -0,0 +1,300 @@
+// 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.
+
+syntax = "proto3";
+
+import "org/apache/kyuubi/grpc/common/errors.proto";
+import "org/apache/kyuubi/grpc/jdbc/response.proto";
+
+option java_multiple_files = true;
+option java_package = "org.apache.kyuubi.grpc.jdbc.connection";
+
+/*
+ * APIs need for build a service component against java.sql.Connection class
+ * A connection (session) with a specific database. SQL statements are executed and results are
+ * returned within the context of a connection.
+ *
+ * APIs defined in separate sub-protocol:
+ *  - DatabaseMetaData API:
+ *    - getMetaData
+ *  - Statement API:
+ *    - createStatement
+ *    - prepareStatement
+ *    - prepareCall
+ *
+ * TODO: Other Undefined APIs:
+ *  1. createArrayOf
+ *  2. createBlob
+ *  3. createClob
+ *  4. createNClob
+ *  5. createSQLXML
+ *  6. createStruct
+ */
+service Connection {
+  rpc OpenConnection(OpenConnectionReq) returns (DirectStatusResp);
+  rpc CloseConnection(CloseConnectionReq) returns (DirectStatusResp);
+  rpc AbortConnection(AbortConnectionReq) returns (DirectStatusResp);
+  rpc NativeSQL(NativeSQLReq) returns (NativeSQLResp);
+  rpc SetAutoCommit(SetAutoCommitReq) returns (DirectStatusResp);
+  rpc GetAutoCommit(GetAutoCommitReq) returns (GetAutoCommitResp);
+  rpc Commit(CommitReq) returns (DirectStatusResp);
+  rpc Rollback(RollbackReq) returns (DirectStatusResp);
+  rpc SetReadOnly(SetReadOnlyReq) returns (DirectStatusResp);
+  rpc IsReadOnly(IsReadOnlyReq) returns (IsReadOnlyResp);
+  rpc SetCatalog(SetCatalogReq) returns (DirectStatusResp);
+  rpc GetCatalog(GetCatalogReq) returns (GetCatalogResp);
+  rpc SetTransactionIsolation(SetTransactionIsolationReq) returns (DirectStatusResp);
+  rpc GetTransactionIsolation(GetTransactionIsolationReq) returns (GetTransactionIsolationResp);
+  rpc GetWarnings(GetWarningsReq) returns (GetWarningsResp);
+  rpc ClearWarnings(ClearWarningsReq) returns (DirectStatusResp);
+  rpc SetTypeMap(SetTypeMapReq) returns (DirectStatusResp);
+  rpc GetTypeMap(GetTypeMapReq) returns (GetTypeMapResp);
+  rpc SetSchema(SetSchemaReq) returns (DirectStatusResp);
+  rpc GetSchema(GetSchemaReq) returns (GetSchemaResp);
+  rpc SetNetworkTimeout(SetNetworkTimeoutReq) returns (DirectStatusResp);
+  rpc GetNetworkTimeout(GetNetworkTimeoutReq) returns (GetNetworkTimeoutResp);
+  rpc SetHoldability(SetHoldabilityReq) returns (DirectStatusResp);
+  rpc GetHoldability(GetHoldabilityReq) returns (GetHoldabilityResp);
+  rpc SetSavepoint(SetSavepointReq) returns (SetSavepointResp);
+  rpc ReleaseSavepoint(ReleaseSavepointReq) returns (DirectStatusResp);
+  rpc IsValid(IsValidReq) returns (IsValidResp);
+  rpc SetClientInfo(SetClientInfoReq) returns (DirectStatusResp);
+  rpc GetClientInfo(GetClientInfoReq) returns (GetClientInfoResp);
+}
+
+message OpenConnectionReq {
+  string connection_id = 1;
+  map<string, string> configs = 2;
+}
+
+message CloseConnectionReq {
+  string connection_id = 1;
+  map<string, string> configs = 2;
+}
+
+message AbortConnectionReq {
+  string connection_id = 1;
+  map<string, string> configs = 2;
+}
+
+message NativeSQLReq {
+  string connection_id = 1;
+  string sql = 2;
+}
+
+message NativeSQLResp {
+  Status status = 1;
+  string sql = 2;
+}
+
+message SetAutoCommitReq {
+  string connection_id = 1;
+  bool autoCommit = 2;
+}
+
+message GetAutoCommitReq {
+  string connection_id = 1;
+}
+
+message GetAutoCommitResp {
+  Status status = 1;
+  bool autoCommit = 2;
+}
+
+message CommitReq {
+  string connection_id = 1;
+}
+
+message Savepoint {
+  uint32 savepoint_id = 1;
+  string savepoint_name = 2;
+}
+
+message RollbackReq {
+  string connection_id = 1;
+  Savepoint savepoint = 2;
+}
+
+message SetReadOnlyReq {
+  string connection_id = 1;
+  bool read_only = 2;
+}
+
+message IsReadOnlyReq {
+  string connection_id = 1;
+}
+
+message IsReadOnlyResp {
+  Status status = 1;
+  bool read_only = 2;
+}
+
+message SetClientInfoReq {
+  string connection_id = 1;
+  map<string, string> configs = 2;
+}
+
+message GetClientInfoReq {
+  string connection_id = 1;
+}
+
+message GetClientInfoResp {
+  Status status = 1;
+  map<string, string> configs = 2;
+}
+
+message SetCatalogReq {
+  string connection_id = 1;
+  string catalog = 2;
+}
+
+message GetCatalogReq {
+  string connection_id = 1;
+}
+
+message GetCatalogResp {
+  Status status = 1;
+  string catalog = 2;
+}
+
+/*
+ * Attempts to change the transaction isolation level for this Connection object to the one given.
+ * The constants defined in the interface Connection are the possible transaction isolation levels.
+ * @see java.sql.Connection
+ */
+message SetTransactionIsolationReq {
+  string connection_id = 1;
+  // The possible transaction isolation levels
+  // level one of the following <code>Connection</code> constants:
+  // <code>Connection.TRANSACTION_READ_UNCOMMITTED(1)</code>,
+  // <code>Connection.TRANSACTION_READ_COMMITTED(2)</code>,
+  // <code>Connection.TRANSACTION_REPEATABLE_READ(4)</code>, or
+  // <code>Connection.TRANSACTION_SERIALIZABLE(8)</code>.
+  uint32 level = 2;
+}
+
+message GetTransactionIsolationReq {
+  string connection_id = 1;
+}
+
+message GetTransactionIsolationResp {
+  Status status = 1;
+  uint32 level = 2;
+}
+
+message GetWarningsReq {
+  string connection_id = 1;
+}
+
+message GetWarningsResp {
+  Status status = 1;
+  SQLWarning warnings = 2;
+}
+
+
+message ClearWarningsReq {
+  string connection_id = 1;
+}
+
+message TypeClassMap {
+  string type_name = 1;
+  string class_name = 2;
+}
+
+message SetTypeMapReq {
+  string connection_id = 1;
+  map<string, string> type_to_class = 2;
+}
+
+message GetTypeMapReq {
+  string connection_id = 1;
+}
+
+message GetTypeMapResp {
+  Status status = 1;
+  map<string, string> type_to_class = 2;
+}
+
+message SetSchemaReq {
+  string connection_id = 1;
+  string schema = 2;
+}
+
+message GetSchemaReq {
+  string connection_id = 1;
+}
+
+message GetSchemaResp {
+  Status status = 1;
+  string schema = 2;
+}
+
+message SetHoldabilityReq {
+  string connection_id = 1;
+  // one of the following <code>ResultSet</code> constants:
+  // <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
+  // <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
+  uint32 holdability = 2;
+}
+
+message GetHoldabilityReq {
+  string connection_id = 1;
+}
+
+message GetHoldabilityResp {
+  Status status = 1;
+  uint32 holdability = 2;
+}
+
+message SetNetworkTimeoutReq {
+  string connection_id = 1;
+  // The new network timeout value in milliseconds
+  uint32 milliseconds = 2;
+}
+
+message GetNetworkTimeoutReq {
+  string connection_id = 1;
+}
+
+message GetNetworkTimeoutResp {
+  Status status = 1;
+  uint32 milliseconds = 2;
+}
+
+message ReleaseSavepointReq {
+  string connection_id = 1;
+  Savepoint savepoint = 2;
+}
+
+message SetSavepointReq {
+  string connection_id = 1;
+  string savepoint_name = 2;
+}
+
+message SetSavepointResp {
+  Status status = 1;
+  Savepoint savepoint = 2;
+}
+
+message IsValidReq {
+  string connection_id = 1;
+  uint32 timeout = 2;
+}
+
+message IsValidResp {
+  Status status = 1;
+  bool valid = 2;
+}
diff --git a/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/request.proto b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/request.proto
new file mode 100644
index 0000000..73d56ed
--- /dev/null
+++ b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/request.proto
@@ -0,0 +1,345 @@
+// 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.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "org.apache.kyuubi.grpc.jdbc";
+
+message GetCatalogsReq {
+  string connection_id = 1;
+}
+
+message GetSchemasReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+}
+
+message GetTablesReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string tableNamePattern = 4;
+  repeated string tableTypes = 5;
+}
+
+message GetTableTypesReq {
+  string connection_id = 1;
+}
+
+message GetTablePrivilegesReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string tableNamePattern = 4;
+}
+
+message GetColumnsReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string tablePattern = 4;
+  string columnNamePattern = 5;
+}
+
+message GetColumnPrivilegesReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schema = 3;
+  string table = 4;
+  string columnNamePattern = 5;
+}
+
+message GetVersionColumnsReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schema = 3;
+  string table = 4;
+}
+
+message GetPseudoColumnsReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string tableNamePattern = 4;
+  string columnNamePattern = 5;
+}
+
+message GetFunctionsReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string functionNamePattern = 4;
+}
+
+message GetFunctionColumnsReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string functionNamePattern = 4;
+  string columnNamePattern = 5;
+}
+
+message GetPrimaryKeysReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schema = 3;
+  string table = 4;
+}
+
+message GetImportedKeysReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schema = 3;
+  string table = 4;
+}
+
+message GetExportedKeysReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schema = 3;
+  string table = 4;
+}
+
+message GetCrossReferenceReq {
+  string connection_id = 1;
+  string primaryCatalog = 2;
+  string primarySchema = 3;
+  string primaryTable = 4;
+  string foreignCatalog = 5;
+  string foreignSchema = 6;
+  string foreignTable = 7;
+}
+
+message GetAttributesReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string typeNamePattern = 4;
+  string attributeNamePattern = 5;
+}
+
+message GetBestRowIdentifierReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schema = 3;
+  string table = 4;
+  uint32 scope = 5;
+  bool nullable = 6;
+}
+
+message GetTypeInfoReq {
+  string connection_id = 1;
+}
+
+message GetIndexInfoReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schema = 3;
+  string table = 4;
+  bool unique = 5;
+  bool approximate = 6;
+}
+
+message GetUDTsReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string typeNamePattern = 4;
+}
+
+message GetSuperTypesReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string typeNamePattern = 4;
+}
+
+message GetSuperTablesReq {
+  string connection_id = 1;
+  string catalog = 2;
+  string schemaPattern = 3;
+  string tableNamePattern = 4;
+}
+
+
+message ExecuteSqlReq {
+  string connection_id = 1;
+  // any SQL statement
+  string sql = 2;
+  // java.sql.Statement.getQueryTimeout
+  // the current query timeout limit in seconds; zero means there is
+  // no limit.
+  uint32 queryTimeout = 3;
+  // java.sql.Statement.getMaxRows
+  // Retrieves the maximum number of rows that a ResultSet could have.
+  // If this limit is exceeded, the excess rows are silently dropped.
+  // zero means there is no limit
+  uint32 maxRows = 4;
+}
+
+message GetResultSetSchemaReq {
+  string operation_id = 1;
+}
+
+message GetNextColumnSet {
+  string operation_id = 1;
+  uint32 fetchSize = 2;
+}
+
+/* java.sql.DatabaseMetaData APIs except for those
+ * - Return java.sql.ResultSet
+ *   - java.sql.DatabaseMetaData.getImportedKeys
+ * - JDBC Driver Info
+ *   - java.sql.DatabaseMetaData.getDriverName
+ *   - java.sql.DatabaseMetaData.getDriverVersion
+ *   - java.sql.DatabaseMetaData.getDriverMajorVersion
+ *   - java.sql.DatabaseMetaData.getDriverMinorVersion
+ */
+enum DatabaseMetaData {
+  // java.sql.DatabaseMetaData.allProceduresAreCallable
+  ALL_PROCEDURES_ARE_CALLABLE = 0;
+  // java.sql.DatabaseMetaData.allTablesAreSelectable
+  ALL_TABLES_ARE_SELECTABLE = 1;
+  // java.sql.DatabaseMetaData.getURL
+  URL = 2;
+  // java.sql.DatabaseMetaData.getUserName
+  USERNAME = 3;
+  // java.sql.DatabaseMetaData.isReadOnly
+  IS_READ_ONLY = 4;
+  // java.sql.DatabaseMetaData.nullsAreSortedHigh
+  NULLS_ARE_SORTED_HIGH = 5;
+  // java.sql.DatabaseMetaData.nullsAreSortedLow
+  NULLS_ARE_SORTED_LOW = 6;
+  // java.sql.DatabaseMetaData.nullsAreSortedAtStart
+  NULLS_ARE_SORTED_AT_START = 7;
+  // java.sql.DatabaseMetaData.nullsAreSortedAtEnd
+  NULLS_ARE_SORTED_AT_END = 8;
+  // java.sql.DatabaseMetaData.getDatabaseProductName
+  DATABASE_PRODUCT_NAME = 9;
+  // java.sql.DatabaseMetaData.getDatabaseProductVersion
+  DATABASE_PRODUCT_VERSION = 10;
+  // java.sql.DatabaseMetaData.usesLocalFiles
+  USES_LOCAL_FILES = 11;
+  // java.sql.DatabaseMetaData.usesLocalFilePerTable
+  USES_LOCAL_FILE_PER_TABLE = 12;
+  // java.sql.DatabaseMetaData.supportsMixedCaseIdentifiers
+  SUPPORTS_MIXED_CASE_IDENTIFIERS = 13;
+  // java.sql.DatabaseMetaData.storesUpperCaseIdentifiers
+  STORES_UPPER_CASE_IDENTIFIERS = 14;
+  // java.sql.DatabaseMetaData.storesLowerCaseIdentifiers
+  STORES_LOWER_CASE_IDENTIFIERS = 15;
+  // java.sql.DatabaseMetaData.storesMixedCaseIdentifiers
+  STORES_MIXED_CASE_IDENTIFIERS = 16;
+  // java.sql.DatabaseMetaData.supportsMixedCaseQuotedIdentifiers
+  SUPPORTS_MIXED_CASE_QUOTED_IDENTIFIERS = 17;
+  // java.sql.DatabaseMetaData.storesUpperCaseQuotedIdentifiers
+  STORES_UPPER_CASE_QUOTED_IDENTIFIERS = 18;
+  // java.sql.DatabaseMetaData.storesLowerCaseQuotedIdentifiers
+  STORES_LOWER_CASE_QUOTED_IDENTIFIERS = 19;
+  // java.sql.DatabaseMetaData.storesMixedCaseQuotedIdentifiers
+  STORES_MIXED_CASE_QUOTED_IDENTIFIERS = 20;
+  // java.sql.DatabaseMetaData.getIdentifierQuoteString
+  IDENTIFIER_QUOTE_STRING = 21;
+  // java.sql.DatabaseMetaData.getSQLKeywords
+  SQL_KEYWORDS = 22;
+  // java.sql.DatabaseMetaData.getNumericFunctions
+  NUMERIC_FUNCTIONS = 23;
+  // java.sql.DatabaseMetaData.getStringFunctions
+  STRING_FUNCTIONS = 24;
+  // java.sql.DatabaseMetaData.getSystemFunctions
+  SYSTEM_FUNCTIONS = 25;
+  // java.sql.DatabaseMetaData.getTimeDateFunctions
+  TIME_DATE_FUNCTIONS = 26;
+  // java.sql.DatabaseMetaData.getSearchStringEscape
+  SEARCH_STRING_ESCAPE = 27;
+  // java.sql.DatabaseMetaData.getExtraNameCharacters
+  EXTRA_NAME_CHARACTERS = 28;
+  // java.sql.DatabaseMetaData.supportsAlterTableWithAddColumn
+  SUPPORTS_ALTER_TABLE_WITH_ADD_COLUMN = 29;
+  // java.sql.DatabaseMetaData.supportsAlterTableWithDropColumn
+  SUPPORTS_ALTER_TABLE_WITH_DROP_COLUMN = 30;
+  // java.sql.DatabaseMetaData.supportsColumnAliasing
+  SUPPORTS_COLUMN_ALIASING = 31;
+  // java.sql.DatabaseMetaData.nullPlusNonNullIsNull
+  NULL_PLUS_NON_NULL_IS_NULL = 32;
+  // java.sql.DatabaseMetaData.supportsConvert()
+  // java.sql.DatabaseMetaData.supportsConvert(int, int)
+  SUPPORTS_CONVERT = 33;
+  // java.sql.DatabaseMetaData.supportsTableCorrelationNames
+  SUPPORTS_TABLE_CORRELATION_NAMES = 34;
+  // java.sql.DatabaseMetaData.supportsDifferentTableCorrelationNames
+  SUPPORTS_DIFFERENT_TABLE_CORRELATION_NAMES = 35;
+  // java.sql.DatabaseMetaData.supportsExpressionsInOrderBy
+  SUPPORTS_EXPRESSIONS_IN_ORDER_BY = 36;
+  // java.sql.DatabaseMetaData.supportsOrderByUnrelated
+  SUPPORTS_ORDER_BY_UNRELATED = 37;
+  // java.sql.DatabaseMetaData.supportsGroupBy
+  SUPPORTS_GROUP_BY = 38;
+  // java.sql.DatabaseMetaData.supportsGroupByUnrelated
+  SUPPORTS_GROUP_BY_UNRELATED = 39;
+  // java.sql.DatabaseMetaData.supportsGroupByBeyondSelect
+  SUPPORTS_GROUP_BY_BEYOND_SELECT = 40;
+  // java.sql.DatabaseMetaData.supportsLikeEscapeClause
+  SUPPORTS_LIKE_ESCAPE_CLAUSE = 41;
+  // java.sql.DatabaseMetaData.supportsMultipleResultSets
+  SUPPORTS_MULTIPLE_RESULT_SETS = 42;
+  // java.sql.DatabaseMetaData.supportsMultipleTransactions
+  SUPPORTS_MULTIPLE_TRANSACTIONS = 43;
+  // java.sql.DatabaseMetaData.supportsNonNullableColumns
+  SUPPORTS_NON_NULLABLE_COLUMNS = 44;
+  // java.sql.DatabaseMetaData.supportsMinimumSQLGrammar
+  // java.sql.DatabaseMetaData.supportsCoreSQLGrammar
+  // java.sql.DatabaseMetaData.supportsExtendedSQLGrammar
+  // java.sql.DatabaseMetaData.supportsANSI92IntermediateSQL
+  // java.sql.DatabaseMetaData.supportsANSI92FullSQL
+  // java.sql.DatabaseMetaData.supportsIntegrityEnhancementFacility
+  // java.sql.DatabaseMetaData.supportsOuterJoins
+  // java.sql.DatabaseMetaData.supportsFullOuterJoins
+  // java.sql.DatabaseMetaData.supportsLimitedOuterJoins
+  // java.sql.DatabaseMetaData.getSchemaTerm
+  // java.sql.DatabaseMetaData.getProcedureTerm
+  // java.sql.DatabaseMetaData.getCatalogTerm
+
+}
+
+message NewEngineReq {
+  string connection_id = 1;
+  map<string, string> configs = 2;
+}
+
+message NewEngineResp {
+  string operation_id = 1;
+  map<string, string> configs = 2;
+}
+
+
+
+enum OperationState {
+  INITIALIZED = 0; // operation created
+  INITIALIZE_FAILED = 1; // operation failed to be created
+  SUBMITTED = 2; // operation submitted
+  SUBMIT_FAILED = 3; // operation failed to be submitted
+  RUNNING = 4; // operation running
+  RUNNING_COMPILED = 5;
+  RUNNING_FAILED = 6;
+  SUCCEEDED = 7;
+  CANCELED = 8;
+  CLOSED = 9;
+  TIMEOUT = 10;
+}
+
diff --git a/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/response.proto b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/response.proto
new file mode 100644
index 0000000..c0c94bb
--- /dev/null
+++ b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/response.proto
@@ -0,0 +1,46 @@
+// 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.
+
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+import "org/apache/kyuubi/grpc/common/errors.proto";
+import "org/apache/kyuubi/grpc/jdbc/schema.proto";
+
+option java_multiple_files = true;
+option java_package = "org.apache.kyuubi.grpc.jdbc";
+
+message DirectStatusResp {
+  string identifier = 1;
+  Status status = 2;
+  map<string, string> extraInfo = 3;
+}
+
+message DirectValueResp {
+  Status status = 2;
+  google.protobuf.Any value = 1;
+}
+
+message GetResultSetSchemaResp {
+  Status status = 1;
+  ResultSetSchema schema = 2;
+}
+
+message GetNextColumnSetResp {
+  Status status = 1;
+  ColumnDataSet data = 2;
+}
+
diff --git a/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/schema.proto b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/schema.proto
new file mode 100644
index 0000000..bb31dff
--- /dev/null
+++ b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/schema.proto
@@ -0,0 +1,103 @@
+// 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.
+
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+
+option java_multiple_files = true;
+option java_package = "org.apache.kyuubi.grpc.jdbc";
+
+enum TypeId {
+  BOOLEAN = 0;
+
+  TINYINT = 1;
+  SMALLINT = 2;
+  INT = 3;
+  LONG = 4;
+
+  FLOAT = 5;
+  DOUBLE = 6;
+  DECIMAL = 7;
+
+  DATE = 8;
+  TIME = 9;
+  TIMESTAMP = 10;
+  TIMESTAMP_WITH_TIMEZONE = 11;
+  TIMESTAMP_LOCAL_TIMEZONE = 12;
+  YEAR_MONTH_INTERVAL = 13;
+  DAY_TIME_INTERVAL = 14;
+
+  CHAR = 15;
+  VARCHAR = 16;
+  STRING = 17;
+  BINARY = 18;
+
+  ARRAY = 19;
+  MAP = 20;
+  STRUCT = 21;
+  UNION = 22;
+  USER_DEFINED_TYPE = 23;
+
+  NULL = 24;
+}
+
+message DataType {
+  TypeId type_id = 1;
+  string type_name = 2;
+  uint32 precision = 3;
+  uint32 scale = 4;
+  uint32 display_size = 5; // indicates the normal maximum width in characters
+  map<string, string> metadata = 6;
+  repeated ColumnInfo children = 7;
+}
+
+message ColumnInfo {
+  uint32 column_index = 1;
+  bool auto_increment = 2;
+  bool case_sensitive = 3;
+  bool searchable = 4;
+  bool currency = 5;
+  // 0 - no
+  // 1 - yes
+  // 2 - unknown
+  uint32 nullable = 6;
+  bool signed = 7;
+  // holds the suggested column title for this column, to be used in printing and displays.
+  string column_label = 9;
+  // holds the name of this column
+  string column_name = 10;
+  string schema_name = 11;
+  string table_name = 14;
+  string catalog_name = 15;
+  bool read_only = 16;
+  bool writable = 17;
+  bool definitely_writable = 18;
+  string class_name = 19;
+  DataType type = 20;
+}
+
+message ResultSetSchema {
+  repeated ColumnInfo columns = 1;
+}
+
+message ColumnData {
+  repeated google.protobuf.Any values = 1;
+}
+
+message ColumnDataSet {
+  repeated ColumnData columns = 1;
+}
\ No newline at end of file
diff --git a/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/service.proto b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/service.proto
new file mode 100644
index 0000000..a2b17d1
--- /dev/null
+++ b/jdbc-proto/src/main/protobuf/org/apache/kyuubi/grpc/jdbc/service.proto
@@ -0,0 +1,54 @@
+// 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.
+
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+import "org/apache/kyuubi/grpc/common/errors.proto";
+import "org/apache/kyuubi/grpc/jdbc/request.proto";
+import "org/apache/kyuubi/grpc/jdbc/response.proto";
+
+option java_multiple_files = true;
+option java_package = "org.apache.kyuubi.grpc.jdbc";
+
+/* Frontend Endpoint for building JDBC-compatible service through Grpc.
+ */
+service Jdbc {
+  rpc GetCatalogs(GetCatalogsReq) returns (DirectStatusResp);
+  rpc GetSchemas(GetSchemasReq) returns (DirectStatusResp);
+  rpc GetTables(GetTablesReq) returns (DirectStatusResp);
+  rpc GetTableTypes(GetTableTypesReq) returns (DirectStatusResp);
+  rpc GetTablePrivileges(GetTablePrivilegesReq) returns (DirectStatusResp);
+  rpc GetColumns(GetColumnsReq) returns (DirectStatusResp);
+  rpc GetColumnPrivileges(GetColumnPrivilegesReq) returns (DirectStatusResp);
+  rpc GetVersionColumns(GetVersionColumnsReq) returns (DirectStatusResp);
+  rpc GetPseudoColumns(GetPseudoColumnsReq) returns (DirectStatusResp);
+  rpc GetFunctions(GetFunctionsReq) returns (DirectStatusResp);
+  rpc GetFunctionColumns(GetFunctionColumnsReq) returns (GetFunctionColumnsReq);
+  rpc GetPrimaryKeys(GetPrimaryKeysReq) returns (DirectStatusResp);
+  rpc GetImportedKeys(GetImportedKeysReq) returns (DirectStatusResp);
+  rpc GetExportedKeys(GetImportedKeysReq) returns (DirectStatusResp);
+  rpc GetCrossReference(GetCrossReferenceReq) returns (DirectStatusResp);
+  rpc GetAttributes(GetAttributesReq) returns (DirectStatusResp);
+  rpc GetBestRowIdentifier(GetBestRowIdentifierReq) returns (DirectStatusResp);
+  rpc GetTypeInfo(GetTypeInfoReq) returns (DirectStatusResp);
+  rpc GetIndexInfo(GetIndexInfoReq) returns (DirectStatusResp);
+  rpc GetUDTs(GetUDTsReq) returns (DirectStatusResp);
+  rpc GetSuperTypes(GetSuperTypesReq) returns (DirectStatusResp);
+  rpc ExecuteSql(ExecuteSqlReq) returns (DirectStatusResp);
+  rpc GetResultSetSchema(GetResultSetSchemaReq) returns (GetResultSetSchemaResp);
+  rpc GetNextResultSet(GetNextColumnSet) returns (GetNextColumnSetResp);
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d2f3797
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>29</version>
+    </parent>
+
+    <groupId>org.apache.kyuubi</groupId>
+    <artifactId>kyuubi-service-rpc</artifactId>
+    <version>0.1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>Kyuubi Service RPC Layer</name>
+
+    <modules>
+        <module>health-proto</module>
+        <module>jdbc-proto</module>
+        <module>jdbc-grpc-client</module>
+    </modules>
+
+    <properties>
+        <io.grpc.version>1.54.1</io.grpc.version>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <os-maven-plugin.version>1.7.1</os-maven-plugin.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <protobuf-java.version>3.22.3</protobuf-java.version>
+        <protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>
+        <tomcat.annotations-api.version>6.0.53</tomcat.annotations-api.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.google.protobuf</groupId>
+                <artifactId>protobuf-java</artifactId>
+                <version>${protobuf-java.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.grpc</groupId>
+                <artifactId>grpc-netty</artifactId>
+                <version>${io.grpc.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.grpc</groupId>
+                <artifactId>grpc-protobuf</artifactId>
+                <version>${io.grpc.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.grpc</groupId>
+                <artifactId>grpc-services</artifactId>
+                <version>${io.grpc.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.grpc</groupId>
+                <artifactId>grpc-stub</artifactId>
+                <version>${io.grpc.version}</version>
+            </dependency>
+            <dependency> <!-- necessary for Java 9+ -->
+                <groupId>org.apache.tomcat</groupId>
+                <artifactId>annotations-api</artifactId>
+                <version>${tomcat.annotations-api.version}</version>
+                <scope>provided</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <extensions>
+            <extension>
+                <groupId>kr.motd.maven</groupId>
+                <artifactId>os-maven-plugin</artifactId>
+                <version>${os-maven-plugin.version}</version>
+            </extension>
+        </extensions>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.xolstice.maven.plugins</groupId>
+                    <artifactId>protobuf-maven-plugin</artifactId>
+                    <version>${protobuf-maven-plugin.version}</version>
+                    <configuration>
+                        <checkStaleness>true</checkStaleness>
+                        <pluginId>grpc-java</pluginId>
+                        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.47.0:exe:${os.detected.classifier}</pluginArtifact>
+                        <protocArtifact>com.google.protobuf:protoc:3.21.1:exe:${os.detected.classifier}</protocArtifact>
+                        <protoSourceRoot>src/main/protobuf/</protoSourceRoot>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <goals>
+                                <goal>compile</goal>
+                                <goal>compile-custom</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>build-helper-maven-plugin</artifactId>
+                    <version>3.3.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>3.0.0-M9</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file


[kyuubi-service-rpc] 02/02: nit

Posted by ya...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

yao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/kyuubi-service-rpc.git

commit 658e9ef60416e95def7bca6c6be8a7ff7c71dec7
Author: Kent Yao <ya...@apache.org>
AuthorDate: Mon May 29 12:09:16 2023 +0800

    nit
---
 .gitignore | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index f4bef8b..08df579 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,4 @@
-.idea/
-.idea/codeStyles/*
-.idea/**/
+.idea
 *.iml
 *.ipr
 *.iws