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 + } +]