You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@kylin.apache.org by "ASF GitHub Bot (JIRA)" <ji...@apache.org> on 2018/09/21 09:48:00 UTC

[jira] [Commented] (KYLIN-3497) Make JDBC Module more testable

    [ https://issues.apache.org/jira/browse/KYLIN-3497?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16623341#comment-16623341 ] 

ASF GitHub Bot commented on KYLIN-3497:
---------------------------------------

shaofengshi closed pull request #199: KYLIN-3497 Make JDBC Module more testable
URL: https://github.com/apache/kylin/pull/199
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/jdbc/pom.xml b/jdbc/pom.xml
index 661356c412..a04d81b4c5 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 dfd8d76719..630eedd1fc 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.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 QueryResult(List<ColumnMetaData> columnMeta, Iterable<Object> iterable) {
     /**
      * 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/main/java/org/apache/kylin/jdbc/JdbcFactory.java b/jdbc/src/main/java/org/apache/kylin/jdbc/JdbcFactory.java
new file mode 100644
index 0000000000..f95a30125a
--- /dev/null
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/JdbcFactory.java
@@ -0,0 +1,24 @@
+/*
+ * 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 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 9f8c7dd24e..b98b2d108c 100644
--- a/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java
@@ -34,11 +34,11 @@
 
 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;
@@ -64,19 +64,20 @@
 
 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();
 
@@ -97,6 +98,11 @@ public boolean isTrusted(final X509Certificate[] chain, String authType)
         }
     }
 
+    @VisibleForTesting
+    void setHttpClient(HttpClient httpClient) {
+        this.httpClient = httpClient;
+    }
+
     @SuppressWarnings("rawtypes")
     public static Class convertType(int sqlType) {
         Class result = Object.class;
@@ -207,7 +213,7 @@ private boolean isSSL() {
     }
 
     private String baseUrl() {
-        return (isSSL() ? "https://" : "http://") + conn.getBaseUrl();
+        return (isSSL() ? "https://" : "http://") + connInfo.getBaseUrl();
     }
 
     private void addHttpHeaders(HttpRequestBase method) {
@@ -241,7 +247,7 @@ public void connect() throws IOException {
 
     @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);
@@ -330,10 +336,10 @@ private KMetaColumn convertMetaColumn(ColumnMetaStub columnStub) {
     }
 
     @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());
 
@@ -343,12 +349,10 @@ public QueryResult executeQuery(String sql, List<AvaticaParameter> params, List<
         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)));
@@ -359,7 +363,7 @@ public QueryResult executeQuery(String sql, List<AvaticaParameter> params, List<
     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 991eca353c..b2b3315225 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.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.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 @@
     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 @@ protected KylinConnection(UnregisteredDriver driver, KylinJdbcFactory factory, S
         }
     }
 
-    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 @@ Signature mockPreparedSignature(String sql) {
         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/main/java/org/apache/kylin/jdbc/KylinConnectionInfo.java b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnectionInfo.java
new file mode 100644
index 0000000000..436d65944c
--- /dev/null
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/KylinConnectionInfo.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kylin.jdbc;
+
+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 6aae983f95..0f3462517e 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 @@
 /**
  * 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 ResultSetMetaData newResultSetMetaData(AvaticaStatement statement, Signat
         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 1c1157a072..731b75a9f4 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 @@ protected AvaticaResultSet execute() throws SQLException {
 
         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/main/java/org/apache/kylin/jdbc/RemoteClientFactory.java b/jdbc/src/main/java/org/apache/kylin/jdbc/RemoteClientFactory.java
new file mode 100644
index 0000000000..d33623c685
--- /dev/null
+++ b/jdbc/src/main/java/org/apache/kylin/jdbc/RemoteClientFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+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 9b66f4d2f5..11dbcde45d 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 void testSSLFromURL() throws SQLException {
         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 6578825659..7039cc022a 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.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 @@
  */
 public class DummyClient implements IRemoteClient {
 
-    public DummyClient(KylinConnection conn) {
+    public DummyClient(KylinConnectionInfo conn) {
     }
 
     @Override
@@ -64,7 +63,7 @@ public KMetaProject retrieveMetaData(String project) throws IOException {
     }
 
     @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 e5f24b8ecc..91a222f4c5 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 DummyJdbcFactory() {
     }
 
     @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 0000000000..2e2e560650
--- /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 0000000000..ae15472328
--- /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 0000000000..8be6b8ad59
--- /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 0000000000..eb5fa40c7d
--- /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 0000000000..03d9b03175
--- /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
+  }
+]


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


> Make JDBC Module more testable
> ------------------------------
>
>                 Key: KYLIN-3497
>                 URL: https://issues.apache.org/jira/browse/KYLIN-3497
>             Project: Kylin
>          Issue Type: Improvement
>          Components: Driver - JDBC
>            Reporter: Ian Hu
>            Assignee: Ian Hu
>            Priority: Minor
>             Fix For: v2.6.0
>
>
> While I am trying my work about KYLIN-3496, I found it is difficult to test. I would offer a work to make it more testable.



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)