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

shaofengshi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kylin.git


The following commit(s) were added to refs/heads/master by this push:
     new 715f819  KYLIN-3497 Make JDBC Module more testable
715f819 is described below

commit 715f819f7f8013578cbe392c7f3e2ace1bf97b38
Author: hujixu <huj...@youzan.com>
AuthorDate: Tue Aug 14 17:13:55 2018 +0800

    KYLIN-3497 Make JDBC Module more testable
    
    De-coupling IRemoteClient with KylinConnection.
    Add dependencies mocking to improve test coverage.
---
 jdbc/pom.xml                                       |   7 +
 .../java/org/apache/kylin/jdbc/IRemoteClient.java  |   9 +-
 .../java/org/apache/kylin/jdbc/JdbcFactory.java}   |  20 +-
 .../java/org/apache/kylin/jdbc/KylinClient.java    |  36 +-
 .../org/apache/kylin/jdbc/KylinConnection.java     |  15 +-
 .../apache/kylin/jdbc/KylinConnectionInfo.java}    |  23 +-
 .../org/apache/kylin/jdbc/KylinJdbcFactory.java    |   4 +-
 .../java/org/apache/kylin/jdbc/KylinResultSet.java |   2 +-
 .../apache/kylin/jdbc/RemoteClientFactory.java}    |  21 +-
 .../java/org/apache/kylin/jdbc/DriverTest.java     |  24 +
 .../java/org/apache/kylin/jdbc/DummyClient.java    |   5 +-
 .../org/apache/kylin/jdbc/DummyJdbcFactory.java    |   2 +-
 .../org/apache/kylin/jdbc/KylinClientTest.java     | 114 ++++
 .../org/apache/kylin/jdbc/KylinConnectionTest.java | 144 ++++
 .../test/java/org/apache/kylin/jdbc/TestUtil.java  |  68 ++
 jdbc/src/test/resources/query.json                 |  42 ++
 jdbc/src/test/resources/tables_and_columns.json    | 747 +++++++++++++++++++++
 17 files changed, 1202 insertions(+), 81 deletions(-)

diff --git a/jdbc/pom.xml b/jdbc/pom.xml
index 5b4a3b2..4f81feb 100644
--- a/jdbc/pom.xml
+++ b/jdbc/pom.xml
@@ -52,6 +52,13 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 
     <build>
diff --git a/jdbc/src/main/java/org/apache/kylin/jdbc/IRemoteClient.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/IRemoteClient.java
index dfd8d76..630eedd 100644
--- a/jdbc/src/main/java/org/apache/kylin/jdbc/IRemoteClient.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/IRemoteClient.java
@@ -23,13 +23,12 @@ import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.calcite.avatica.AvaticaParameter;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.kylin.jdbc.KylinMeta.KMetaProject;
 
 public interface IRemoteClient extends Closeable {
 
-    public static class QueryResult {
+    class QueryResult {
         public final List<ColumnMetaData> columnMeta;
         public final Iterable<Object> iterable;
 
@@ -42,16 +41,16 @@ public interface IRemoteClient extends Closeable {
     /**
      * Connect to Kylin restful service. IOException will be thrown if 
authentication failed.
      */
-    public void connect() throws IOException;
+    void connect() throws IOException;
 
     /**
      * Retrieve meta data of given project.
      */
-    public KMetaProject retrieveMetaData(String project) throws IOException;
+    KMetaProject retrieveMetaData(String project) throws IOException;
 
     /**
      * Execute query remotely and get back result.
      */
-    public QueryResult executeQuery(String sql, List<AvaticaParameter> params, 
List<Object> paramValues, Map<String, String> queryToggles) throws IOException;
+    QueryResult executeQuery(String sql, List<Object> paramValues, Map<String, 
String> queryToggles) throws IOException;
 
 }
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/JdbcFactory.java
similarity index 76%
copy from jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
copy to jdbc/src/main/java/org/apache/kylin/jdbc/JdbcFactory.java
index e5f24b8..f95a301 100644
--- a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/JdbcFactory.java
@@ -6,29 +6,19 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
-*/
-
-package org.apache.kylin.jdbc;
-
-/**
  */
-public class DummyJdbcFactory extends KylinJdbcFactory {
 
-    public DummyJdbcFactory() {
-        super(4, 1);
-    }
+package org.apache.kylin.jdbc;
 
-    @Override
-    public IRemoteClient newRemoteClient(KylinConnection conn) {
-        return new DummyClient(conn);
-    }
+import org.apache.calcite.avatica.AvaticaFactory;
 
+public interface JdbcFactory extends RemoteClientFactory, AvaticaFactory {
 }
diff --git a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java
index 7e3ca05..6814f2d 100644
--- a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java
@@ -35,11 +35,11 @@ import java.util.Properties;
 
 import javax.xml.bind.DatatypeConverter;
 
-import org.apache.calcite.avatica.AvaticaParameter;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.ColumnMetaData.Rep;
 import org.apache.calcite.avatica.ColumnMetaData.ScalarType;
 import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.methods.HttpRequestBase;
@@ -65,19 +65,20 @@ import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.annotations.VisibleForTesting;
 
 public class KylinClient implements IRemoteClient {
 
     private static final Logger logger = 
LoggerFactory.getLogger(KylinClient.class);
 
-    private final KylinConnection conn;
+    private final KylinConnectionInfo connInfo;
     private final Properties connProps;
-    private DefaultHttpClient httpClient;
+    private HttpClient httpClient;
     private final ObjectMapper jsonMapper;
 
-    public KylinClient(KylinConnection conn) {
-        this.conn = conn;
-        this.connProps = conn.getConnectionProperties();
+    public KylinClient(KylinConnectionInfo connInfo) {
+        this.connInfo = connInfo;
+        this.connProps = connInfo.getConnectionProperties();
         this.httpClient = new DefaultHttpClient();
         this.jsonMapper = new ObjectMapper();
 
@@ -98,6 +99,11 @@ public class KylinClient implements IRemoteClient {
         }
     }
 
+    @VisibleForTesting
+    void setHttpClient(HttpClient httpClient) {
+        this.httpClient = httpClient;
+    }
+
     @SuppressWarnings("rawtypes")
     public static Class convertType(int sqlType) {
         Class result = Object.class;
@@ -208,7 +214,7 @@ public class KylinClient implements IRemoteClient {
     }
 
     private String baseUrl() {
-        return (isSSL() ? "https://"; : "http://";) + conn.getBaseUrl();
+        return (isSSL() ? "https://"; : "http://";) + connInfo.getBaseUrl();
     }
 
     private void addHttpHeaders(HttpRequestBase method) {
@@ -243,7 +249,7 @@ public class KylinClient implements IRemoteClient {
 
     @Override
     public KMetaProject retrieveMetaData(String project) throws IOException {
-        assert conn.getProject().equals(project);
+        assert connInfo.getProject().equals(project);
 
         String url = baseUrl() + "/kylin/api/tables_and_columns?project=" + 
project;
         HttpGet get = new HttpGet(url);
@@ -332,10 +338,10 @@ public class KylinClient implements IRemoteClient {
     }
 
     @Override
-    public QueryResult executeQuery(String sql, List<AvaticaParameter> params, 
List<Object> paramValues,
+    public QueryResult executeQuery(String sql, List<Object> paramValues,
             Map<String, String> queryToggles) throws IOException {
 
-        SQLResponseStub queryResp = executeKylinQuery(sql, 
convertParameters(params, paramValues), queryToggles);
+        SQLResponseStub queryResp = executeKylinQuery(sql, 
convertParameters(paramValues), queryToggles);
         if (queryResp.getIsException())
             throw new IOException(queryResp.getExceptionMessage());
 
@@ -345,12 +351,10 @@ public class KylinClient implements IRemoteClient {
         return new QueryResult(metas, data);
     }
 
-    private List<StatementParameter> convertParameters(List<AvaticaParameter> 
params, List<Object> paramValues) {
-        if (params == null || params.isEmpty())
+    private List<StatementParameter> convertParameters(List<Object> 
paramValues) {
+        if (paramValues == null) {
             return null;
-
-        assert params.size() == paramValues.size();
-
+        }
         List<StatementParameter> result = new ArrayList<StatementParameter>();
         for (Object v : paramValues) {
             result.add(new StatementParameter(v.getClass().getCanonicalName(), 
String.valueOf(v)));
@@ -361,7 +365,7 @@ public class KylinClient implements IRemoteClient {
     private SQLResponseStub executeKylinQuery(String sql, 
List<StatementParameter> params,
             Map<String, String> queryToggles) throws IOException {
         String url = baseUrl() + "/kylin/api/query";
-        String project = conn.getProject();
+        String project = connInfo.getProject();
 
         PreparedQueryRequest request = new PreparedQueryRequest();
         if (null != params) {
diff --git a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnection.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnection.java
index 991eca3..b2b3315 100644
--- a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnection.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnection.java
@@ -28,6 +28,7 @@ import java.util.Map;
 import java.util.Properties;
 
 import org.apache.calcite.avatica.AvaticaConnection;
+import org.apache.calcite.avatica.AvaticaFactory;
 import org.apache.calcite.avatica.AvaticaParameter;
 import org.apache.calcite.avatica.AvaticaStatement;
 import org.apache.calcite.avatica.ColumnMetaData;
@@ -39,7 +40,7 @@ import org.apache.calcite.avatica.UnregisteredDriver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class KylinConnection extends AvaticaConnection {
+public class KylinConnection extends AvaticaConnection implements 
KylinConnectionInfo {
 
     private static final Logger logger = 
LoggerFactory.getLogger(KylinConnection.class);
 
@@ -47,7 +48,7 @@ public class KylinConnection extends AvaticaConnection {
     private final String project;
     private final IRemoteClient remoteClient;
 
-    protected KylinConnection(UnregisteredDriver driver, KylinJdbcFactory 
factory, String url, Properties info) throws SQLException {
+    protected KylinConnection(UnregisteredDriver driver, JdbcFactory factory, 
String url, Properties info) throws SQLException {
         super(driver, factory, url, info);
 
         String odbcUrl = url;
@@ -70,15 +71,15 @@ public class KylinConnection extends AvaticaConnection {
         }
     }
 
-    String getBaseUrl() {
+    public String getBaseUrl() {
         return baseUrl;
     }
 
-    String getProject() {
+    public String getProject() {
         return project;
     }
 
-    Properties getConnectionProperties() {
+    public Properties getConnectionProperties() {
         return info;
     }
 
@@ -121,8 +122,8 @@ public class KylinConnection extends AvaticaConnection {
         return new Meta.Signature(columns, sql, params, internalParams, 
CursorFactory.ARRAY, Meta.StatementType.SELECT);
     }
 
-    private KylinJdbcFactory factory() {
-        return (KylinJdbcFactory) factory;
+    private AvaticaFactory factory() {
+        return factory;
     }
 
     public IRemoteClient getRemoteClient() {
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnectionInfo.java
similarity index 76%
copy from jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
copy to jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnectionInfo.java
index e5f24b8..436d659 100644
--- a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnectionInfo.java
@@ -6,29 +6,22 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
-*/
-
-package org.apache.kylin.jdbc;
-
-/**
  */
-public class DummyJdbcFactory extends KylinJdbcFactory {
 
-    public DummyJdbcFactory() {
-        super(4, 1);
-    }
+package org.apache.kylin.jdbc;
 
-    @Override
-    public IRemoteClient newRemoteClient(KylinConnection conn) {
-        return new DummyClient(conn);
-    }
+import java.util.Properties;
 
+public interface KylinConnectionInfo {
+    String getProject();
+    String getBaseUrl();
+    Properties getConnectionProperties();
 }
diff --git a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinJdbcFactory.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinJdbcFactory.java
index 6aae983..0f34625 100644
--- a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinJdbcFactory.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinJdbcFactory.java
@@ -39,7 +39,7 @@ import org.apache.calcite.avatica.UnregisteredDriver;
 /**
  * Kylin JDBC factory.
  */
-public class KylinJdbcFactory implements AvaticaFactory {
+public class KylinJdbcFactory implements JdbcFactory {
 
     public static class Version40 extends KylinJdbcFactory {
         public Version40() {
@@ -104,7 +104,7 @@ public class KylinJdbcFactory implements AvaticaFactory {
         return new AvaticaResultSetMetaData(statement, null, signature);
     }
 
-    public IRemoteClient newRemoteClient(KylinConnection conn) {
+    public IRemoteClient newRemoteClient(KylinConnectionInfo conn) {
         return new KylinClient(conn);
     }
 }
diff --git a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinResultSet.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinResultSet.java
index 1c1157a..731b75a 100644
--- a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinResultSet.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinResultSet.java
@@ -66,7 +66,7 @@ public class KylinResultSet extends AvaticaResultSet {
 
         QueryResult result;
         try {
-            result = client.executeQuery(sql, params, paramValues, 
queryToggles);
+            result = client.executeQuery(sql, paramValues, queryToggles);
         } catch (IOException e) {
             throw new SQLException(e);
         }
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java 
b/jdbc/src/main/java/org/apache/kylin/jdbc/RemoteClientFactory.java
similarity index 76%
copy from jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
copy to jdbc/src/main/java/org/apache/kylin/jdbc/RemoteClientFactory.java
index e5f24b8..d33623c 100644
--- a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/RemoteClientFactory.java
@@ -6,29 +6,18 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
-*/
-
-package org.apache.kylin.jdbc;
-
-/**
  */
-public class DummyJdbcFactory extends KylinJdbcFactory {
 
-    public DummyJdbcFactory() {
-        super(4, 1);
-    }
-
-    @Override
-    public IRemoteClient newRemoteClient(KylinConnection conn) {
-        return new DummyClient(conn);
-    }
+package org.apache.kylin.jdbc;
 
+public interface RemoteClientFactory {
+    IRemoteClient newRemoteClient(KylinConnectionInfo connInfo);
 }
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/DriverTest.java 
b/jdbc/src/test/java/org/apache/kylin/jdbc/DriverTest.java
index 9b66f4d..11dbcde 100644
--- a/jdbc/src/test/java/org/apache/kylin/jdbc/DriverTest.java
+++ b/jdbc/src/test/java/org/apache/kylin/jdbc/DriverTest.java
@@ -223,6 +223,30 @@ public class DriverTest {
         assertTrue(Boolean.parseBoolean((String) ((KylinConnection) 
conn).getConnectionProperties().get("ssl")));
     }
 
+    @Test
+    public void testCalciteProps() throws SQLException {
+        Driver driver = new DummyDriver();
+        Properties props = new Properties();
+        props.setProperty("kylin.query.calcite.extras-props.caseSensitive", 
"true");
+        props.setProperty("kylin.query.calcite.extras-props.unquotedCasing", 
"TO_LOWER");
+        props.setProperty("kylin.query.calcite.extras-props.quoting", 
"BRACKET");
+        KylinConnection conn = (KylinConnection) 
driver.connect("jdbc:kylin:test_url/test_db", props);
+        Properties connProps = conn.getConnectionProperties();
+        assertEquals("true", 
connProps.getProperty("kylin.query.calcite.extras-props.caseSensitive"));
+        assertEquals("TO_LOWER", 
connProps.getProperty("kylin.query.calcite.extras-props.unquotedCasing"));
+        assertEquals("BRACKET", 
connProps.getProperty("kylin.query.calcite.extras-props.quoting"));
+
+        // parameters in url is prior to props parameter
+        KylinConnection conn2 = (KylinConnection) 
driver.connect("jdbc:kylin:kylin.query.calcite.extras-props.caseSensitive=false;"
 +
+                "kylin.query.calcite.extras-props.unquotedCasing=UNCHANGED;" +
+                "kylin.query.calcite.extras-props.quoting=BACK_TICK;" +
+                "test_url/test_db", props);
+        Properties connProps2 = conn2.getConnectionProperties();
+        assertEquals("false", 
connProps2.getProperty("kylin.query.calcite.extras-props.caseSensitive"));
+        assertEquals("UNCHANGED", 
connProps2.getProperty("kylin.query.calcite.extras-props.unquotedCasing"));
+        assertEquals("BACK_TICK", 
connProps2.getProperty("kylin.query.calcite.extras-props.quoting"));
+    }
+
     private void printResultSet(ResultSet rs) throws SQLException {
         ResultSetMetaData meta = rs.getMetaData();
         System.out.println("Data:");
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyClient.java 
b/jdbc/src/test/java/org/apache/kylin/jdbc/DummyClient.java
index 6578825..7039cc0 100644
--- a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyClient.java
+++ b/jdbc/src/test/java/org/apache/kylin/jdbc/DummyClient.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.calcite.avatica.AvaticaParameter;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.ColumnMetaData.Rep;
 import org.apache.kylin.jdbc.KylinMeta.KMetaCatalog;
@@ -37,7 +36,7 @@ import org.apache.kylin.jdbc.KylinMeta.KMetaTable;
  */
 public class DummyClient implements IRemoteClient {
 
-    public DummyClient(KylinConnection conn) {
+    public DummyClient(KylinConnectionInfo conn) {
     }
 
     @Override
@@ -64,7 +63,7 @@ public class DummyClient implements IRemoteClient {
     }
 
     @Override
-    public QueryResult executeQuery(String sql, List<AvaticaParameter> params, 
List<Object> paramValues, Map<String, String> queryToggles) throws IOException {
+    public QueryResult executeQuery(String sql, List<Object> paramValues, 
Map<String, String> queryToggles) throws IOException {
         List<Object> data = new ArrayList<Object>();
         Object[] row = new Object[] { "foo", "bar", "tool" };
         data.add(row);
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java 
b/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
index e5f24b8..91a222f 100644
--- a/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
+++ b/jdbc/src/test/java/org/apache/kylin/jdbc/DummyJdbcFactory.java
@@ -27,7 +27,7 @@ public class DummyJdbcFactory extends KylinJdbcFactory {
     }
 
     @Override
-    public IRemoteClient newRemoteClient(KylinConnection conn) {
+    public IRemoteClient newRemoteClient(KylinConnectionInfo conn) {
         return new DummyClient(conn);
     }
 
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/KylinClientTest.java 
b/jdbc/src/test/java/org/apache/kylin/jdbc/KylinClientTest.java
new file mode 100644
index 0000000..2e2e560
--- /dev/null
+++ b/jdbc/src/test/java/org/apache/kylin/jdbc/KylinClientTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kylin.jdbc;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.message.BasicStatusLine;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+import static org.apache.http.HttpVersion.HTTP_1_1;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class KylinClientTest {
+
+    KylinConnectionInfo connInfo = new KylinConnectionInfo() {
+        @Override
+        public String getProject() {
+            return "default";
+        }
+
+        @Override
+        public String getBaseUrl() {
+            return "http://localhost:7070";;
+        }
+
+        @Override
+        public Properties getConnectionProperties() {
+            Properties props = new Properties();
+            props.setProperty("user", "ADMIN");
+            props.setProperty("password", "KYLIN");
+            return props;
+        }
+    };
+
+    KylinClient client = new KylinClient(connInfo);
+
+    HttpClient httpClient = mock(HttpClient.class);
+
+    @Before
+    public void setUp() throws Exception {
+        client.setHttpClient(httpClient);
+    }
+
+    @Test
+    public void connect() throws IOException {
+        HttpResponse response = mock(HttpResponse.class);
+        
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(response);
+        when(response.getStatusLine()).thenReturn(new 
BasicStatusLine(HTTP_1_1, 200, "OK"));
+        client.connect();
+    }
+
+    @Test
+    public void retrieveMetaData() throws IOException {
+        HttpResponse response = TestUtil.mockHttpResponseWithFile(200, "OK", 
"tables_and_columns.json");
+        
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(response);
+
+        KylinMeta.KMetaProject metaData = 
client.retrieveMetaData(connInfo.getProject());
+
+        assertEquals(connInfo.getProject(), metaData.projectName);
+        assertTrue(!metaData.catalogs.isEmpty());
+        KylinMeta.KMetaCatalog catalog = metaData.catalogs.get(0);
+        assertEquals("defaultCatalog", catalog.getName());
+        assertEquals(1, catalog.schemas.size());
+        KylinMeta.KMetaSchema schema = catalog.schemas.get(0);
+        assertEquals("DEFAULT", schema.getName());
+        assertEquals(5, schema.tables.size());
+    }
+
+    @Test(expected = AssertionError.class)
+    public void retrieveMetaDataWithWrongProject() throws IOException {
+        client.retrieveMetaData("defualt2");
+    }
+
+    @Test
+    public void executeQuery() throws IOException {
+        HttpResponse response = TestUtil.mockHttpResponseWithFile(200, "OK", 
"query.json");
+        
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(response);
+        IRemoteClient.QueryResult queryResult = client.executeQuery("SELECT 1 
as val", Collections.emptyList(), new HashMap<String, String>());
+        assertEquals(1, queryResult.columnMeta.size());
+        Iterable<Object> iterable = queryResult.iterable;
+        ArrayList<Object> list = Lists.newArrayList(iterable);
+        assertEquals(1, list.size());
+    }
+}
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/KylinConnectionTest.java 
b/jdbc/src/test/java/org/apache/kylin/jdbc/KylinConnectionTest.java
new file mode 100644
index 0000000..ae15472
--- /dev/null
+++ b/jdbc/src/test/java/org/apache/kylin/jdbc/KylinConnectionTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kylin.jdbc;
+
+import java.io.IOException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.calcite.avatica.ColumnMetaData;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.message.BasicStatusLine;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import static org.apache.http.HttpVersion.HTTP_1_1;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class KylinConnectionTest {
+
+    private Driver driver = new Driver();
+    private KylinJdbcFactory factory = spy(new KylinJdbcFactory.Version41());
+    private IRemoteClient client = mock(IRemoteClient.class);
+    private HttpClient httpClient = mock(HttpClient.class);
+
+    @Before
+    public void setUp() throws Exception {
+
+    }
+
+    @Test
+    public void testPrepareStatementWithMockKylinClient() throws SQLException, 
IOException {
+        String sql = "select 1 as val";
+        ArrayList<ColumnMetaData> columnMeta = new ArrayList<>();
+        columnMeta.add(new ColumnMetaData(0, false, true, false,
+                false, 1, true, 1,
+                    "VAL", "VAL", null,
+                10, 0, null, null,
+                ColumnMetaData.scalar(Types.INTEGER, "INTEGER", 
ColumnMetaData.Rep.INTEGER),
+                true, false, false, "java.lang.Integer"));
+        ArrayList<Object> list = new ArrayList<>();
+        list.add(new Object[]{1});
+        IRemoteClient.QueryResult result = new 
IRemoteClient.QueryResult(columnMeta, list);
+        // mock client
+        when(client.executeQuery(anyString(), Mockito.<List<Object>>any(), 
Mockito.<Map<String, String>>any())).thenReturn(result);
+
+        PreparedStatement preparedStatement = 
getConnectionWithMockClient().prepareStatement(sql);
+        ResultSet resultSet = preparedStatement.executeQuery();
+
+        verify(client).executeQuery(eq(sql), Mockito.<List<Object>>any(), 
Mockito.<Map<String, String>>any());
+
+        assertTrue(resultSet.next());
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals("VAL", metaData.getColumnName(1));
+        assertEquals(1, resultSet.getInt("VAL"));
+    }
+
+    @Test
+    public void testPrepareStatementWithMockHttp() throws IOException, 
SQLException {
+        String sql = "select 1 as val";
+        KylinConnection connection = getConnectionWithMockHttp();
+
+        // mock http
+        HttpResponse response = TestUtil.mockHttpResponseWithFile(200, "OK", 
"query.json");
+        
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(response);
+
+        ResultSet resultSet = connection.prepareStatement(sql).executeQuery();
+
+        assertTrue(resultSet.next());
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals("VAL", metaData.getColumnName(1));
+        assertEquals(1, resultSet.getInt("VAL"));
+    }
+
+    private KylinConnection getConnectionWithMockClient() throws SQLException {
+        Properties info = new Properties();
+        info.setProperty("user", "ADMIN");
+        info.setProperty("password", "KYLIN");
+
+        
doReturn(client).when(factory).newRemoteClient(any(KylinConnectionInfo.class));
+        return new KylinConnection(driver, factory, 
"jdbc:kylin:test_url/test_db", info);
+    }
+
+    private KylinConnection getConnectionWithMockHttp() throws SQLException, 
IOException {
+        final Properties info = new Properties();
+        info.setProperty("user", "ADMIN");
+        info.setProperty("password", "KYLIN");
+
+        // hack KylinClient
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invo) throws Throwable {
+                KylinConnectionInfo connInfo = invo.getArgument(0);
+                KylinClient client = new KylinClient(connInfo);
+                client.setHttpClient(httpClient);
+                return client;
+            }
+        }).when(factory).newRemoteClient(any(KylinConnectionInfo.class));
+
+        // Workaround IRemoteClient.connect()
+        HttpResponse response = mock(HttpResponse.class);
+        
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(response);
+        when(response.getStatusLine()).thenReturn(new 
BasicStatusLine(HTTP_1_1, 200, "OK"));
+
+        return new KylinConnection(driver, factory, 
"jdbc:kylin:test_url/test_db", info);
+    }
+}
diff --git a/jdbc/src/test/java/org/apache/kylin/jdbc/TestUtil.java 
b/jdbc/src/test/java/org/apache/kylin/jdbc/TestUtil.java
new file mode 100644
index 0000000..8be6b8a
--- /dev/null
+++ b/jdbc/src/test/java/org/apache/kylin/jdbc/TestUtil.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kylin.jdbc;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.message.BasicStatusLine;
+import org.mockito.Mockito;
+
+/**
+ * For test purpose.
+ */
+public final class TestUtil {
+
+    private TestUtil() {
+    }
+
+    public static String getResourceContent(String path) {
+        ClassLoader loader = Thread.currentThread().getContextClassLoader();
+        StringWriter writer = new StringWriter();
+        InputStream is = loader.getResourceAsStream(path);
+        if (is == null) {
+            throw new IllegalArgumentException(new FileNotFoundException(path 
+ " not found in class path"));
+        }
+        try {
+            IOUtils.copy(is, writer, "utf-8");
+            return writer.toString();
+        } catch (IOException e) {
+            IOUtils.closeQuietly(is);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static HttpResponse mockHttpResponse(int statusCode, String 
message, String body) {
+        HttpResponse response = Mockito.mock(HttpResponse.class);
+        Mockito.when(response.getStatusLine()).thenReturn(new 
BasicStatusLine(HttpVersion.HTTP_1_1, statusCode, message));
+        Mockito.when(response.getEntity()).thenReturn(new StringEntity(body, 
StandardCharsets.UTF_8));
+        return response;
+    }
+
+    public static HttpResponse mockHttpResponseWithFile(int statusCode, String 
message, String path) {
+        return mockHttpResponse(statusCode, message, getResourceContent(path));
+    }
+}
diff --git a/jdbc/src/test/resources/query.json 
b/jdbc/src/test/resources/query.json
new file mode 100644
index 0000000..eb5fa40
--- /dev/null
+++ b/jdbc/src/test/resources/query.json
@@ -0,0 +1,42 @@
+{
+  "affectedRowCount": 0,
+  "columnMetas": [
+    {
+      "autoIncrement": false,
+      "caseSensitive": true,
+      "catelogName": null,
+      "columnType": 4,
+      "columnTypeName": "INTEGER",
+      "currency": false,
+      "definitelyWritable": false,
+      "displaySize": 10,
+      "isNullable": 0,
+      "label": "VAL",
+      "name": "VAL",
+      "precision": 10,
+      "readOnly": true,
+      "scale": 0,
+      "schemaName": null,
+      "searchable": false,
+      "signed": true,
+      "tableName": null,
+      "writable": false
+    }
+  ],
+  "cube": "",
+  "duration": 71,
+  "exceptionMessage": null,
+  "hitExceptionCache": false,
+  "isException": false,
+  "partial": false,
+  "pushDown": false,
+  "results": [
+    [
+      "1"
+    ]
+  ],
+  "storageCacheUsed": false,
+  "totalScanBytes": 0,
+  "totalScanCount": 0,
+  "traceUrl": null
+}
diff --git a/jdbc/src/test/resources/tables_and_columns.json 
b/jdbc/src/test/resources/tables_and_columns.json
new file mode 100644
index 0000000..03d9b03
--- /dev/null
+++ b/jdbc/src/test/resources/tables_and_columns.json
@@ -0,0 +1,747 @@
+[
+  {
+    "columns": [
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "ACCOUNT_ID",
+        "column_SIZE": -1,
+        "data_TYPE": -5,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 1,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_ACCOUNT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "BIGINT"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "ACCOUNT_BUYER_LEVEL",
+        "column_SIZE": -1,
+        "data_TYPE": 4,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 2,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_ACCOUNT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "INTEGER"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "ACCOUNT_SELLER_LEVEL",
+        "column_SIZE": -1,
+        "data_TYPE": 4,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 3,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_ACCOUNT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "INTEGER"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "ACCOUNT_COUNTRY",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 4,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_ACCOUNT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      }
+    ],
+    "ref_GENERATION": null,
+    "remarks": null,
+    "self_REFERENCING_COL_NAME": null,
+    "table_CAT": "defaultCatalog",
+    "table_NAME": "KYLIN_ACCOUNT",
+    "table_SCHEM": "DEFAULT",
+    "table_TYPE": "TABLE",
+    "type_CAT": null,
+    "type_NAME": null,
+    "type_SCHEM": null
+  },
+  {
+    "columns": [
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "CAL_DT",
+        "column_SIZE": -1,
+        "data_TYPE": 91,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 1,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CAL_DT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "DATE"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "YEAR_BEG_DT",
+        "column_SIZE": -1,
+        "data_TYPE": 91,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 2,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CAL_DT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "DATE"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "MONTH_BEG_DT",
+        "column_SIZE": -1,
+        "data_TYPE": 91,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 3,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CAL_DT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "DATE"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "WEEK_BEG_DT",
+        "column_SIZE": -1,
+        "data_TYPE": 91,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 4,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CAL_DT",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "DATE"
+      }
+    ],
+    "ref_GENERATION": null,
+    "remarks": null,
+    "self_REFERENCING_COL_NAME": null,
+    "table_CAT": "defaultCatalog",
+    "table_NAME": "KYLIN_CAL_DT",
+    "table_SCHEM": "DEFAULT",
+    "table_TYPE": "TABLE",
+    "type_CAT": null,
+    "type_NAME": null,
+    "type_SCHEM": null
+  },
+  {
+    "columns": [
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "LEAF_CATEG_ID",
+        "column_SIZE": -1,
+        "data_TYPE": -5,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 1,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "BIGINT"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "SITE_ID",
+        "column_SIZE": -1,
+        "data_TYPE": 4,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 2,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "INTEGER"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "USER_DEFINED_FIELD1",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 3,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "USER_DEFINED_FIELD3",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 4,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "META_CATEG_NAME",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 5,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "CATEG_LVL2_NAME",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 6,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "CATEG_LVL3_NAME",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 7,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      }
+    ],
+    "ref_GENERATION": null,
+    "remarks": null,
+    "self_REFERENCING_COL_NAME": null,
+    "table_CAT": "defaultCatalog",
+    "table_NAME": "KYLIN_CATEGORY_GROUPINGS",
+    "table_SCHEM": "DEFAULT",
+    "table_TYPE": "TABLE",
+    "type_CAT": null,
+    "type_NAME": null,
+    "type_SCHEM": null
+  },
+  {
+    "columns": [
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "COUNTRY",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 1,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_COUNTRY",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "NAME",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 2,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_COUNTRY",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      }
+    ],
+    "ref_GENERATION": null,
+    "remarks": null,
+    "self_REFERENCING_COL_NAME": null,
+    "table_CAT": "defaultCatalog",
+    "table_NAME": "KYLIN_COUNTRY",
+    "table_SCHEM": "DEFAULT",
+    "table_TYPE": "TABLE",
+    "type_CAT": null,
+    "type_NAME": null,
+    "type_SCHEM": null
+  },
+  {
+    "columns": [
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "TRANS_ID",
+        "column_SIZE": -1,
+        "data_TYPE": -5,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 1,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "BIGINT"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "PART_DT",
+        "column_SIZE": -1,
+        "data_TYPE": 91,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 2,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "DATE"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "LSTG_FORMAT_NAME",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 3,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "LEAF_CATEG_ID",
+        "column_SIZE": -1,
+        "data_TYPE": -5,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 4,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "BIGINT"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "LSTG_SITE_ID",
+        "column_SIZE": -1,
+        "data_TYPE": 4,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 5,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "INTEGER"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 19,
+        "column_DEF": null,
+        "column_NAME": "PRICE",
+        "column_SIZE": 19,
+        "data_TYPE": 3,
+        "decimal_DIGITS": 4,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 6,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "DECIMAL(19, 4)"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "SELLER_ID",
+        "column_SIZE": -1,
+        "data_TYPE": -5,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 7,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "BIGINT"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": -1,
+        "column_DEF": null,
+        "column_NAME": "BUYER_ID",
+        "column_SIZE": -1,
+        "data_TYPE": -5,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 8,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "BIGINT"
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "OPS_USER_ID",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 9,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      },
+      {
+        "buffer_LENGTH": -1,
+        "char_OCTET_LENGTH": 256,
+        "column_DEF": null,
+        "column_NAME": "OPS_REGION",
+        "column_SIZE": 256,
+        "data_TYPE": 12,
+        "decimal_DIGITS": 0,
+        "is_AUTOINCREMENT": "",
+        "is_NULLABLE": "YES",
+        "nullable": 1,
+        "num_PREC_RADIX": 10,
+        "ordinal_POSITION": 10,
+        "remarks": null,
+        "scope_CATLOG": null,
+        "scope_SCHEMA": null,
+        "scope_TABLE": null,
+        "source_DATA_TYPE": -1,
+        "sql_DATA_TYPE": -1,
+        "sql_DATETIME_SUB": -1,
+        "table_CAT": "defaultCatalog",
+        "table_NAME": "KYLIN_SALES",
+        "table_SCHEM": "DEFAULT",
+        "type_NAME": "VARCHAR(256) CHARACTER SET \"UTF-16LE\" COLLATE 
\"UTF-16LE$en_US$primary\""
+      }
+    ],
+    "ref_GENERATION": null,
+    "remarks": null,
+    "self_REFERENCING_COL_NAME": null,
+    "table_CAT": "defaultCatalog",
+    "table_NAME": "KYLIN_SALES",
+    "table_SCHEM": "DEFAULT",
+    "table_TYPE": "TABLE",
+    "type_CAT": null,
+    "type_NAME": null,
+    "type_SCHEM": null
+  }
+]

Reply via email to