diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQuery.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQuery.java new file mode 100644 index 0000000000..8bbd1958d7 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQuery.java @@ -0,0 +1,217 @@ +// 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.doris.statistics.util; + +import org.apache.doris.analysis.Analyzer; +import org.apache.doris.analysis.QueryStmt; +import org.apache.doris.analysis.SqlParser; +import org.apache.doris.analysis.SqlScanner; +import org.apache.doris.analysis.StatementBase; +import org.apache.doris.analysis.UserIdentity; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.cluster.ClusterNamespace; +import org.apache.doris.common.Config; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.common.util.SqlParserUtils; +import org.apache.doris.planner.OriginalPlanner; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.Coordinator; +import org.apache.doris.qe.OriginStatement; +import org.apache.doris.qe.QeProcessorImpl; +import org.apache.doris.qe.RowBatch; +import org.apache.doris.statistics.util.InternalQueryResult.ResultRow; +import org.apache.doris.system.SystemInfoService; +import org.apache.doris.thrift.TQueryOptions; +import org.apache.doris.thrift.TResultBatch; +import org.apache.doris.thrift.TUniqueId; + +import com.google.common.collect.Lists; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.StringReader; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * Execute SQL query statements internally(in FE). Internal-query mainly used for statistics module, + * FE obtains statistics by SQL from BE, such as column maximum value, minimum value, etc. + * TODO(wzt): For statistics it should be better to implement a statistics sink. + **/ +public class InternalQuery { + private static final Logger LOG = LogManager.getLogger(InternalQuery.class); + + private int timeout = 0; + private final String sql; + private final String database; + + private ConnectContext context; + private Coordinator coord; + + private StatementBase stmt; + private final List resultBatches = Lists.newArrayList(); + + public InternalQuery(String database, String sql) { + this.database = database; + this.sql = sql; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + /** + * Execute the query internally and return the query result. + * + * @return Result of the query statement + * @throws Exception Errors in parsing or execution + */ + public InternalQueryResult query() throws Exception { + // step1: mock connectContext + buildContext(); + + // step2: parse sql + parseSql(); + + // step3: generate plan + prepare(); + + // step4: execute and get result + execute(); + + // step5: parse result data and return + return fetchResult(); + } + + private void buildContext() { + context = new ConnectContext(); + context.setEnv(Env.getCurrentEnv()); + context.setCluster(SystemInfoService.DEFAULT_CLUSTER); + context.setCurrentUserIdentity(UserIdentity.ROOT); + context.setQualifiedUser(UserIdentity.ROOT.getQualifiedUser()); + + String fullDbName = ClusterNamespace + .getFullName(SystemInfoService.DEFAULT_CLUSTER, database); + context.setDatabase(fullDbName); + + UUID uuid = UUID.randomUUID(); + TUniqueId newQueryId = new TUniqueId(uuid.getMostSignificantBits(), + uuid.getLeastSignificantBits()); + context.setQueryId(newQueryId); + + context.setThreadLocalInfo(); + context.setStartTime(); + + // If user does not set the timeout, then use max_cbo_statistics_task_timeout_sec + timeout = timeout > 0 ? timeout : Config.max_cbo_statistics_task_timeout_sec; + context.getSessionVariable().setQueryTimeoutS(timeout); + } + + private void parseSql() throws DdlException { + SqlScanner input = new SqlScanner(new StringReader(sql), + context.getSessionVariable().getSqlMode()); + SqlParser parser = new SqlParser(input); + + try { + stmt = SqlParserUtils.getFirstStmt(parser); + stmt.setOrigStmt(new OriginStatement(sql, 0)); + } catch (Exception e) { + LOG.warn("Failed to parse the statement: {}. {}", sql, e); + throw new DdlException("Failed to parse the statement:" + sql); + } + + if (! (stmt instanceof QueryStmt)) { + throw new DdlException("Only query statements are supported:" + sql); + } + } + + private void prepare() throws UserException { + Analyzer analyzer = new Analyzer(context.getEnv(), context); + stmt.analyze(analyzer); + + OriginalPlanner originalPlanner = new OriginalPlanner(stmt.getAnalyzer()); + TQueryOptions queryOptions = new TQueryOptions(); + originalPlanner.plan(stmt, queryOptions); + + coord = new Coordinator(context, analyzer, originalPlanner); + } + + private void execute() throws Exception { + TUniqueId tUniqueId = context.queryId(); + try { + QeProcessorImpl.INSTANCE.registerQuery(tUniqueId, coord); + coord.exec(); + if (coord.getExecStatus().ok()) { + RowBatch batch; + do { + batch = coord.getNext(); + if (batch.getBatch() != null) { + resultBatches.add(batch.getBatch()); + } + } while (!batch.isEos()); + } else { + coord.cancel(); + String errMsg = coord.getExecStatus().getErrorMsg(); + ErrorReport.reportDdlException(errMsg, ErrorCode.ERR_QUERY_INTERRUPTED); + } + } finally { + QeProcessorImpl.INSTANCE.unregisterQuery(tUniqueId); + } + } + + private InternalQueryResult fetchResult() throws DdlException { + List columns = stmt.getColLabels(); + List types = stmt.getResultExprs().stream() + .map(e -> e.getType().getPrimitiveType()) + .collect(Collectors.toList()); + + InternalQueryResult result = new InternalQueryResult(columns, types); + List resultRows = result.getResultRows(); + + for (TResultBatch batch : resultBatches) { + List rows = batch.getRows(); + for (ByteBuffer buffer : rows) { + List values = Lists.newArrayList(); + InternalQueryBuffer queryBuffer = new InternalQueryBuffer(buffer.slice()); + + for (int i = 0; i < columns.size(); i++) { + String value = queryBuffer.readStringWithLength(); + values.add(value); + } + + ResultRow resultRow = new ResultRow(values); + resultRows.add(resultRow); + } + } + + return result; + } + + public void cancel() { + if (!coord.isDone()) { + coord.cancel(); + LOG.info("Internal query has been cancelled: {}", sql); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryBuffer.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryBuffer.java new file mode 100644 index 0000000000..257698e2de --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryBuffer.java @@ -0,0 +1,158 @@ +// 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.doris.statistics.util; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; + +/** + * Parse the MySQL protocol result data returned by BE, + * only simple parsing operations are performed here (parsed as String). + * For more, @see `be/src/runtime/mysql_result_writer.cpp`. + */ +public class InternalQueryBuffer { + private static final long NULL_LENGTH = -1; + private static final byte[] EMPTY_BYTES = new byte[0]; + + private final ByteBuffer buffer; + + public InternalQueryBuffer(ByteBuffer buffer) { + this.buffer = buffer; + } + + public byte[] data() { + return buffer.array(); + } + + public int length() { + return buffer.capacity(); + } + + public int position() { + return buffer.position(); + } + + public void clear() { + buffer.clear(); + } + + private byte read() { + return buffer.get(); + } + + private int readUB2() { + int i = read() & 0xff; + i |= (read() & 0xff) << 8; + return i; + } + + private int readUB3() { + int i = read() & 0xff; + i |= (read() & 0xff) << 8; + i |= (read() & 0xff) << 16; + return i; + } + + private int readUB4() { + int i = read() & 0xff; + i |= (read() & 0xff) << 8; + i |= (read() & 0xff) << 16; + i |= (read() & 0xff) << 24; + return i; + } + + private long readLong() { + long i = read() & 0xff; + i |= (long) (read() & 0xff) << 8; + i |= (long) (read() & 0xff) << 16; + i |= (long) (read() & 0xff) << 24; + i |= (long) (read() & 0xff) << 32; + i |= (long) (read() & 0xff) << 40; + i |= (long) (read() & 0xff) << 48; + i |= (long) (read() & 0xff) << 56; + return i; + } + + /** + * The length of the data is not fixed, the length value is determined by the 1-9 bytes + * before the data, and the number of bytes occupied by the length value is not fixed, + * and the number of bytes is determined by the first byte. (@see `be/src/runtime/mysql_row_buffer.cpp`) + * + * @return Length coded binary + */ + private long readLength() { + int length = read() & 0xff; + switch (length) { + case 251: + return NULL_LENGTH; + case 252: + return readUB2(); + case 253: + return readUB3(); + case 254: + return readLong(); + default: + return length; + } + } + + public byte[] readBytesWithLength() { + int length = (int) readLength(); + if (length == NULL_LENGTH) { + return null; + } + if (length <= 0) { + return EMPTY_BYTES; + } + byte[] bytes = new byte[length]; + buffer.get(bytes); + return bytes; + } + + public String readStringWithLength() { + byte[] bytes = readBytesWithLength(); + if (bytes != null) { + return new String(bytes); + } + return null; + } + + public String readStringWithLength(String charset) + throws UnsupportedEncodingException { + byte[] bytes = readBytesWithLength(); + if (bytes != null) { + return new String(bytes, charset); + } + return null; + } + + public Integer readInt() { + String src = readStringWithLength(); + return src == null ? null : new Integer(src); + } + + public Float readFloat() { + String src = readStringWithLength(); + return src == null ? null : new Float(src); + } + + public Double readDouble() { + String src = readStringWithLength(); + return src == null ? null : new Double(src); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryResult.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryResult.java new file mode 100644 index 0000000000..59a3e568ff --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryResult.java @@ -0,0 +1,256 @@ +// 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.doris.statistics.util; + +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.common.DdlException; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import java.util.List; +import java.util.Map; + +/** + * Readable results of internal SQL execution, + * providing some read operations. + */ +public class InternalQueryResult { + private static List mateOfColumns; + private static List mateOfTypes; + + private final List resultRows = Lists.newArrayList(); + + public InternalQueryResult(List columns, List types) { + mateOfColumns = columns; + mateOfTypes = types; + } + + public List getResultRows() { + return resultRows; + } + + public static List getMateOfColumns() throws DdlException { + if (mateOfColumns == null) { + throw new DdlException("Failed to get the column names."); + } + return mateOfColumns; + } + + public static List getMateOfTypes() throws DdlException { + if (mateOfTypes == null) { + throw new DdlException("Failed to get the column types."); + } + return mateOfTypes; + } + + public static class ResultRow { + private final List values; + + private final Map columnNameMap = Maps.newHashMap(); + private final Map columnIndexMap = Maps.newHashMap(); + + public ResultRow(List values) throws DdlException { + this.values = values; + buildColumnNameMap(); + buildColumnIndexMap(); + } + + public List getColumns() throws DdlException { + return getMateOfColumns(); + } + + public List getTypes() throws DdlException { + return getMateOfTypes(); + } + + public List getValues() { + return values != null ? values : Lists.newArrayList(); + } + + private void buildColumnNameMap() throws DdlException { + List columns = getColumns(); + for (int i = 0; i < columns.size(); i++) { + columnNameMap.put(columns.get(i), i); + } + } + + private void buildColumnIndexMap() throws DdlException { + List columns = getColumns(); + for (int i = 0; i < columns.size(); i++) { + columnIndexMap.put(i, columns.get(i)); + } + } + + public int getColumnIndex(String columnName) { + return columnNameMap.getOrDefault(columnName, -1); + } + + public String getColumnName(int index) throws DdlException { + List columns = getColumns(); + if (columnIndexMap.containsKey(index)) { + return columnIndexMap.get(index); + } else { + throw new DdlException("Index should be between 0 and " + columns.size()); + } + } + + public PrimitiveType getColumnType(String columnName) throws DdlException { + List types = getTypes(); + int index = getColumnIndex(columnName); + if (index == -1) { + throw new DdlException("The column name does not exist."); + } + return types.get(index); + } + + public PrimitiveType getColumnType(int index) throws DdlException { + List types = getTypes(); + if (index >= 0 && index < types.size()) { + return types.get(index); + } else { + throw new DdlException("Index should be between 0 and " + types.size()); + } + } + + public Object getColumnValue(String columnName) throws DdlException { + int index = getColumnIndex(columnName); + if (index == -1) { + throw new DdlException("The column name does not exist."); + } + return values.get(index); + } + + public Object getColumnValue(int index) throws DdlException { + List columns = getColumns(); + if (index >= 0 && index < columns.size()) { + return values.get(index); + } else { + throw new DdlException("Index should be between 0 and " + columns.size()); + } + } + + public String getString(int index) throws DdlException { + List columns = getColumns(); + if (index >= 0 && index < columns.size()) { + return values.get(index); + } + throw new DdlException("Index should be between 0 and " + columns.size()); + } + + public int getInt(int index) throws DdlException { + List types = getTypes(); + if (index >= 0 && index < types.size()) { + String value = values.get(index); + PrimitiveType type = types.get(index); + switch (type) { + case BOOLEAN: + case TINYINT: + case SMALLINT: + case INT: + case BIGINT: + return new Integer(value); + default: + throw new DdlException("Unable to convert field to int: " + value); + } + } + throw new DdlException("Index should be between 0 and " + types.size()); + } + + public long getLong(int index) throws DdlException { + List types = getTypes(); + if (index >= 0 && index < types.size()) { + String value = values.get(index); + PrimitiveType type = types.get(index); + switch (type) { + case TINYINT: + case SMALLINT: + case INT: + case BIGINT: + return Long.parseLong(value); + default: + throw new DdlException("Unable to convert field to long: " + value); + } + } + throw new DdlException("Index should be between 0 and " + types.size()); + } + + public float getFloat(int index) throws DdlException { + List types = getTypes(); + if (index >= 0 && index < types.size()) { + String value = values.get(index); + PrimitiveType type = types.get(index); + if (type == PrimitiveType.FLOAT) { + return Float.parseFloat(value); + } + throw new DdlException("Unable to convert field to float: " + value); + } + throw new DdlException("Index should be between 0 and " + types.size()); + } + + public double getDouble(int index) throws DdlException { + List types = getTypes(); + if (index >= 0 && index < types.size()) { + String value = values.get(index); + PrimitiveType type = types.get(index); + if (type == PrimitiveType.DOUBLE) { + return Double.parseDouble(value); + } + throw new DdlException("Unable to convert field to long: " + value); + } + throw new DdlException("Index should be between 0 and " + types.size()); + } + + @Override + public String toString() { + try { + StringBuilder sb = new StringBuilder(); + sb.append("ResultRow{ "); + if (values != null && values.size() > 0) { + List columns = getColumns(); + for (int i = 0; i < values.size(); i++) { + sb.append(columns.get(i)); + sb.append(":"); + sb.append(values.get(i)); + sb.append(" "); + } + } + sb.append("}"); + return sb.toString(); + } catch (DdlException ignored) { + return "ResultRow{" + "values=" + values + ", columnNameMap=" + + columnNameMap + ", columnIndexMap=" + columnIndexMap + '}'; + } + } + } + + @Override + public String toString() { + if (resultRows.size() > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("InternalQueryResult:\n"); + for (ResultRow resultRow : resultRows) { + sb.append(" - "); + sb.append(resultRow.toString()); + sb.append("\n"); + } + return sb.toString(); + } + return "InternalQueryResult{" + "resultRows=" + resultRows + '}'; + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/statistics/util/InternalQueryBufferTest.java b/fe/fe-core/src/test/java/org/apache/doris/statistics/util/InternalQueryBufferTest.java new file mode 100644 index 0000000000..c1e1d889b4 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/statistics/util/InternalQueryBufferTest.java @@ -0,0 +1,120 @@ +// 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.doris.statistics.util; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.nio.ByteBuffer; + +public class InternalQueryBufferTest { + private InternalQueryBuffer internalQueryBuffer; + + @Before + public void setUp() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(1024); + buffer.put((byte) 6); + buffer.put("field1".getBytes()); + + buffer.put((byte) 3); + buffer.put("123".getBytes()); + + buffer.put((byte) 3); + buffer.put("0.1".getBytes()); + + buffer.put((byte) 7); + buffer.put("18.2322".getBytes()); + + internalQueryBuffer = new InternalQueryBuffer(buffer); + } + + @Test + public void testData() { + byte[] result = internalQueryBuffer.data(); + Assert.assertEquals(1024, result.length); + } + + @Test + public void testLength() { + int result = internalQueryBuffer.length(); + Assert.assertEquals(1024, result); + } + + @Test + public void testPosition() { + int result = internalQueryBuffer.position(); + // (1 + 6) + (1 + 3) + (1 + 3) + (1 + 7) + Assert.assertEquals(23, result); + } + + @Test + public void testReadBytesWithLength() { + internalQueryBuffer.clear(); + byte[] result1 = internalQueryBuffer.readBytesWithLength(); + Assert.assertArrayEquals("field1".getBytes(), result1); + + byte[] result2 = internalQueryBuffer.readBytesWithLength(); + Assert.assertArrayEquals("123".getBytes(), result2); + + byte[] result3 = internalQueryBuffer.readBytesWithLength(); + Assert.assertArrayEquals("0.1".getBytes(), result3); + } + + @Test + public void testReadStringWithLength() { + internalQueryBuffer.clear(); + String result1 = internalQueryBuffer.readStringWithLength(); + Assert.assertEquals("field1", result1); + + String result2 = internalQueryBuffer.readStringWithLength(); + Assert.assertEquals("123", result2); + + String result3 = internalQueryBuffer.readStringWithLength(); + Assert.assertEquals("0.1", result3); + } + + @Test + public void testReadStringWithLengthByCharset() throws Exception { + internalQueryBuffer.clear(); + String result1 = internalQueryBuffer.readStringWithLength("UTF-8"); + Assert.assertEquals("field1", result1); + + String result2 = internalQueryBuffer.readStringWithLength("UTF-8"); + Assert.assertEquals("123", result2); + + String result3 = internalQueryBuffer.readStringWithLength("UTF-8"); + Assert.assertEquals("0.1", result3); + } + + @Test + public void testReadIntAndFloatAndDouble() { + internalQueryBuffer.clear(); + String result1 = internalQueryBuffer.readStringWithLength(); + Assert.assertEquals("field1", result1); + + int result2 = internalQueryBuffer.readInt(); + Assert.assertEquals(123, result2); + + float result3 = internalQueryBuffer.readFloat(); + Assert.assertEquals(0.1, result3, 1); + + double result4 = internalQueryBuffer.readDouble(); + Assert.assertEquals(18.2322, result4, 4); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/statistics/util/InternalQueryResultTest.java b/fe/fe-core/src/test/java/org/apache/doris/statistics/util/InternalQueryResultTest.java new file mode 100644 index 0000000000..47532fc0fd --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/statistics/util/InternalQueryResultTest.java @@ -0,0 +1,130 @@ +// 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.doris.statistics.util; + +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.common.DdlException; +import org.apache.doris.statistics.util.InternalQueryResult.ResultRow; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + + +public class InternalQueryResultTest { + private InternalQueryResult queryResult; + private InternalQueryResult.ResultRow resultRow; + + @Before + public void setUp() throws Exception { + List columns = Arrays.asList("c1", "c2", "c3", "c4", "c5"); + List types = Arrays.asList(PrimitiveType.STRING, + PrimitiveType.INT, PrimitiveType.FLOAT, + PrimitiveType.DOUBLE, PrimitiveType.BIGINT); + queryResult = new InternalQueryResult(columns, types); + resultRow = new ResultRow(Arrays.asList("s1", "1000", "0.1", "0.0001", "1000000")); + } + + @Test + public void testGetMateOfColumns() throws Exception { + Assert.assertEquals(Arrays.asList("c1", "c2", "c3", "c4", "c5"), + InternalQueryResult.getMateOfColumns()); + } + + @Test + public void testGetMateOfTypes() throws Exception { + Assert.assertEquals(Arrays.asList(PrimitiveType.STRING, PrimitiveType.INT, PrimitiveType.FLOAT, + PrimitiveType.DOUBLE, PrimitiveType.BIGINT), InternalQueryResult.getMateOfTypes()); + } + + @Test + public void testGetColumnIndex() { + Assert.assertEquals(0, resultRow.getColumnIndex("c1")); + Assert.assertEquals(1, resultRow.getColumnIndex("c2")); + Assert.assertEquals(2, resultRow.getColumnIndex("c3")); + Assert.assertEquals(3, resultRow.getColumnIndex("c4")); + Assert.assertEquals(4, resultRow.getColumnIndex("c5")); + } + + @Test + public void testGetColumnName() throws Exception { + Assert.assertEquals("c1", resultRow.getColumnName(0)); + Assert.assertEquals("c2", resultRow.getColumnName(1)); + Assert.assertEquals("c3", resultRow.getColumnName(2)); + Assert.assertEquals("c4", resultRow.getColumnName(3)); + Assert.assertEquals("c5", resultRow.getColumnName(4)); + } + + @Test + public void testGetColumnTypeWithIndex() { + try { + Assert.assertEquals(PrimitiveType.STRING, resultRow.getColumnType(0)); + Assert.assertEquals(PrimitiveType.INT, resultRow.getColumnType(1)); + Assert.assertEquals(PrimitiveType.FLOAT, resultRow.getColumnType(2)); + Assert.assertEquals(PrimitiveType.DOUBLE, resultRow.getColumnType(3)); + Assert.assertEquals(PrimitiveType.BIGINT, resultRow.getColumnType(4)); + } catch (DdlException e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Test + public void testGetColumnTypeWithName() { + try { + Assert.assertEquals(PrimitiveType.STRING, resultRow.getColumnType("c1")); + Assert.assertEquals(PrimitiveType.INT, resultRow.getColumnType("c2")); + Assert.assertEquals(PrimitiveType.FLOAT, resultRow.getColumnType("c3")); + Assert.assertEquals(PrimitiveType.DOUBLE, resultRow.getColumnType("c4")); + Assert.assertEquals(PrimitiveType.BIGINT, resultRow.getColumnType("c5")); + } catch (DdlException e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Test + public void testGetColumnValueWithIndex() throws Exception { + Assert.assertEquals("s1", resultRow.getColumnValue(0).toString()); + Assert.assertEquals(1000, Integer.parseInt((String) resultRow.getColumnValue(1))); + Assert.assertEquals(0.1f, Float.parseFloat((String) resultRow.getColumnValue(2)), 1); + Assert.assertEquals(0.0001, Double.parseDouble((String) resultRow.getColumnValue(3)), 4); + Assert.assertEquals(1000000, Long.parseLong((String) resultRow.getColumnValue(4))); + } + + @Test + public void testGetColumnValueWithName() throws Exception { + Assert.assertEquals("s1", resultRow.getColumnValue(0).toString()); + Assert.assertEquals(1000, Integer.parseInt((String) resultRow.getColumnValue(1))); + Assert.assertEquals(0.1f, Float.parseFloat((String) resultRow.getColumnValue(2)), 1); + Assert.assertEquals(0.0001, Double.parseDouble((String) resultRow.getColumnValue(3)), 4); + Assert.assertEquals(1000000, Long.parseLong((String) resultRow.getColumnValue(4))); + } + + @Test + public void testGetTypeValue() throws Exception { + Assert.assertEquals("s1", resultRow.getString(0)); + Assert.assertEquals(1000, resultRow.getInt(1)); + Assert.assertEquals(0.1f, resultRow.getFloat(2), 1); + Assert.assertEquals(0.0001, resultRow.getDouble(3), 4); + Assert.assertEquals(1000000, resultRow.getLong(4)); + } +}