This is an automated email from the ASF dual-hosted git repository. morrysnow pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 08a71236a9 [feature](statistics) Internal-query, execute SQL query statement internally in FE (#9983) 08a71236a9 is described below commit 08a71236a962ccd463b664c48fa06d25332eb1ea Author: ElvinWei <zhengte....@outlook.com> AuthorDate: Mon Sep 19 16:26:54 2022 +0800 [feature](statistics) Internal-query, execute SQL query statement internally in FE (#9983) 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. This is a tool module as statistics, it will not affect the original code, also will not affect the use of users. The simple usage process is as follows(the following code does no exception handling): ``` String dbName = "test"; String sql = "SELECT * FROM table0"; InternalQuery query = new InternalQuery(dbName, sql); InternalQueryResult result = query.query(); List<ResultRow> resultRows = result.getResultRows(); for (ResultRow resultRow : resultRows) { List<String> columns = resultRow.getColumns(); for (int i = 0; i < resultRow.getColumns().size(); i++) { resultRow.getColumnIndex(columns.get(i)); resultRow.getColumnName(i); resultRow.getColumnType(columns.get(i)); resultRow.getColumnType(i); resultRow.getColumnValue(columns.get(i)); resultRow.getColumnValue(i); } } ``` --- .../doris/statistics/util/InternalQuery.java | 217 +++++++++++++++++ .../doris/statistics/util/InternalQueryBuffer.java | 158 +++++++++++++ .../doris/statistics/util/InternalQueryResult.java | 256 +++++++++++++++++++++ .../statistics/util/InternalQueryBufferTest.java | 120 ++++++++++ .../statistics/util/InternalQueryResultTest.java | 130 +++++++++++ 5 files changed, 881 insertions(+) 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<TResultBatch> 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<String> columns = stmt.getColLabels(); + List<PrimitiveType> types = stmt.getResultExprs().stream() + .map(e -> e.getType().getPrimitiveType()) + .collect(Collectors.toList()); + + InternalQueryResult result = new InternalQueryResult(columns, types); + List<ResultRow> resultRows = result.getResultRows(); + + for (TResultBatch batch : resultBatches) { + List<ByteBuffer> rows = batch.getRows(); + for (ByteBuffer buffer : rows) { + List<String> 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<String> mateOfColumns; + private static List<PrimitiveType> mateOfTypes; + + private final List<ResultRow> resultRows = Lists.newArrayList(); + + public InternalQueryResult(List<String> columns, List<PrimitiveType> types) { + mateOfColumns = columns; + mateOfTypes = types; + } + + public List<ResultRow> getResultRows() { + return resultRows; + } + + public static List<String> getMateOfColumns() throws DdlException { + if (mateOfColumns == null) { + throw new DdlException("Failed to get the column names."); + } + return mateOfColumns; + } + + public static List<PrimitiveType> getMateOfTypes() throws DdlException { + if (mateOfTypes == null) { + throw new DdlException("Failed to get the column types."); + } + return mateOfTypes; + } + + public static class ResultRow { + private final List<String> values; + + private final Map<String, Integer> columnNameMap = Maps.newHashMap(); + private final Map<Integer, String> columnIndexMap = Maps.newHashMap(); + + public ResultRow(List<String> values) throws DdlException { + this.values = values; + buildColumnNameMap(); + buildColumnIndexMap(); + } + + public List<String> getColumns() throws DdlException { + return getMateOfColumns(); + } + + public List<PrimitiveType> getTypes() throws DdlException { + return getMateOfTypes(); + } + + public List<String> getValues() { + return values != null ? values : Lists.newArrayList(); + } + + private void buildColumnNameMap() throws DdlException { + List<String> columns = getColumns(); + for (int i = 0; i < columns.size(); i++) { + columnNameMap.put(columns.get(i), i); + } + } + + private void buildColumnIndexMap() throws DdlException { + List<String> 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<String> 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<PrimitiveType> 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<PrimitiveType> 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<String> 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<String> 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<PrimitiveType> 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<PrimitiveType> 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<PrimitiveType> 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<PrimitiveType> 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<String> 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<String> columns = Arrays.asList("c1", "c2", "c3", "c4", "c5"); + List<PrimitiveType> 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)); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org