Skip to content

Commit 2fca2e6

Browse files
committed
Merge master and resolve conflicts
2 parents e3fc705 + 37cf3ca commit 2fca2e6

541 files changed

Lines changed: 31018 additions & 3818 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎RELEASE_NOTES.md‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,44 @@
1919
2020
-->
2121

22+
# Apache IoTDB 2.0.10
23+
24+
## Features & Improvements
25+
26+
- Data Query: Support schema-level and table-level storage space statistics
27+
- Data Query: Add DEBUG SQL capability and optimize the result set of Explain Analyze
28+
- Data Query: Support set operations (UNION/INTERSECT/EXCEPT) and Common Table Expressions (CTE) in the table model
29+
- Data Query: Add IF scalar function, binary functions and APPROX_PERCENTILE aggregate function for the table model
30+
- Data Query: Optimize display fields of the show queries command, add three new columns: client IP that initiates the query, timeout threshold, and total request waiting latency
31+
- Storage Management: Provide C-language driver SDK interfaces, including parameter binding, automatic failover for multi-node switching and other fault-tolerant capabilities
32+
- System Management: Add the capability to manually trigger node load balancing
33+
- System Management: Add progress query capability for the remove datanode operation
34+
- System Management: Introduce the show configuration statement to view cluster configuration information
35+
- System Management: Add statistics for the number of slow write requests
36+
- AI Management: Add two built-in models: Moirai2 and Toto
37+
- AI Management: Enable AINode to manage multiple models under the same model_type
38+
- AI Management: Support loading built-in models for CPU inference with isolated inference processes
39+
40+
## Bugs
41+
42+
- Fix the issue that partition tables get lost when leader switch happens on single-node ConfigNode
43+
- Fix the bug where configurations delivered via set configuration to a single DataNode take effect on all other DataNodes unexpectedly
44+
- Fix the occasional continuous accumulation of reservedSize for timeout-only queries across multiple devices in tree model, which triggers query errors after hitting the max capacity limit
45+
- Fix the parsing error in tree-model-to-table conversion logic when tree paths consist entirely of numeric characters
46+
- Correct abnormal output of show devices root.db.** when root.db itself is a device, making it display root.db properly
47+
- Fix the failure of kill query and query timeout mechanism when the client holds the connection open without fully consuming all result sets
48+
- Fix array index out-of-bounds error caused by null values in attribute fields on the receiver side, which blocks the data synchronization pipeline
49+
- Fix connection pool leakage exceptions in the C# client
50+
- Fix exceptions occurring in the C++ client when write redirection is enabled while dn_rpc_address is configured to 0.0.0.0 on DataNode server
51+
- Fix abnormal repeated broadcasting of deletion markers (Deletions) during the load operation
52+
- Fix the issue that rows with null inserted fields cannot be synchronized to the receiver in real-time data synchronization scenarios
53+
- Fix incorrect idempotency logic for out-of-order TTL-expired data on the synchronization receiver, which leads to repeated synchronization of expired data
54+
- Fix the problem that enable-send-tsfile-limit parameter cannot be manually configured for historical Pipes split from full-data Pipe tasks
55+
- Fix Pipe temporary stoppage triggered by insufficient memory of a single subtask within synchronization jobs
56+
- Fix the issue that modifying username/password of source or sink in write-back-sink model generates error logs and prevents data from being received on the sink side
57+
- Fix permission issue preventing non-root users from stopping the AINode process
58+
- Mitigate memory leaks that tend to occur during long-running continuous inference of models running on CPU
59+
2260
# Apache IoTDB 2.0.8
2361

2462
## Features & Improvements

‎external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/MPPPublishHandler.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.iotdb.commons.queryengine.common.SqlDialect;
2525
import org.apache.iotdb.commons.queryengine.utils.TimestampPrecisionUtils;
2626
import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
27+
import org.apache.iotdb.commons.utils.PathUtils;
2728
import org.apache.iotdb.db.auth.AuthorityChecker;
2829
import org.apache.iotdb.db.conf.IoTDBConfig;
2930
import org.apache.iotdb.db.conf.IoTDBDescriptor;
@@ -274,7 +275,9 @@ private void insertTree(TreeMessage message, MqttClientSession session) {
274275
DataNodeDevicePathCache.getInstance().getPartialPath(message.getDevice()));
275276
TimestampPrecisionUtils.checkTimestampPrecision(message.getTimestamp());
276277
statement.setTime(message.getTimestamp());
277-
statement.setMeasurements(message.getMeasurements().toArray(new String[0]));
278+
statement.setMeasurements(
279+
PathUtils.checkIsLegalSingleMeasurementsAndUpdate(message.getMeasurements())
280+
.toArray(new String[0]));
278281
if (message.getDataTypes() == null) {
279282
statement.setDataTypes(new TSDataType[message.getMeasurements().size()]);
280283
statement.setValues(message.getValues().toArray(new Object[0]));

‎external-service-impl/rest-openapi/src/main/openapi3/iotdb_rest_table_v1.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ components:
9898
row_limit:
9999
type: integer
100100
format: int32
101+
description: Maximum rows to return. The effective limit is capped by rest_query_default_row_size_limit.
101102

102103
InsertTabletRequest:
103104
title: InsertTabletRequest

‎external-service-impl/rest-openapi/src/main/openapi3/iotdb_rest_v1.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ components:
166166
rowLimit:
167167
type: integer
168168
format: int32
169+
description: Maximum rows to return. The effective limit is capped by rest_query_default_row_size_limit.
169170

170171
InsertTabletRequest:
171172
title: InsertTabletRequest

‎external-service-impl/rest-openapi/src/main/openapi3/iotdb_rest_v2.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ components:
201201
row_limit:
202202
type: integer
203203
format: int32
204+
description: Maximum rows to return. The effective limit is capped by rest_query_default_row_size_limit.
204205

205206
PrefixPathList:
206207
title: PrefixPathList

‎external-service-impl/rest/src/main/i18n/en/org/apache/iotdb/rest/i18n/RestMessages.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public final class RestMessages {
3434
public static final String SQL_SHOULD_NOT_BE_NULL = "sql should not be null";
3535
public static final String ROW_LIMIT_SHOULD_BE_POSITIVE = "row_limit should be positive";
3636
public static final String ROW_LIMIT_CAMEL_SHOULD_BE_POSITIVE = "rowLimit should be positive";
37+
public static final String
38+
MESSAGE_DATASET_ROW_SIZE_EXCEEDED_THE_GIVEN_MAX_ROW_SIZE_ARG_7A3F6452 =
39+
"Dataset row size exceeded the given max row size (%d)";
3740
public static final String PREFIX_PATHS_NOT_NULL = "prefix_paths should not be null";
3841
public static final String TIMESTAMPS_NOT_NULL = "timestamps should not be null";
3942
public static final String IS_ALIGNED_NOT_NULL = "is_aligned should not be null";

‎external-service-impl/rest/src/main/i18n/zh/org/apache/iotdb/rest/i18n/RestMessages.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public final class RestMessages {
3434
public static final String SQL_SHOULD_NOT_BE_NULL = "sql 不能为空";
3535
public static final String ROW_LIMIT_SHOULD_BE_POSITIVE = "row_limit 应为正数";
3636
public static final String ROW_LIMIT_CAMEL_SHOULD_BE_POSITIVE = "rowLimit 应为正数";
37+
public static final String
38+
MESSAGE_DATASET_ROW_SIZE_EXCEEDED_THE_GIVEN_MAX_ROW_SIZE_ARG_7A3F6452 =
39+
"数据集行数超过给定的最大行数(%d)";
3740
public static final String PREFIX_PATHS_NOT_NULL = "prefix_paths 不能为空";
3841
public static final String TIMESTAMPS_NOT_NULL = "timestamps 不能为空";
3942
public static final String IS_ALIGNED_NOT_NULL = "is_aligned 不能为空";
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.rest.protocol.handler;
21+
22+
import org.apache.iotdb.rest.i18n.RestMessages;
23+
import org.apache.iotdb.rest.protocol.model.ExecutionStatus;
24+
import org.apache.iotdb.rpc.TSStatusCode;
25+
26+
import jakarta.ws.rs.core.Response;
27+
28+
public final class QueryRowLimitUtils {
29+
30+
/**
31+
* Fallback used when {@code rest_query_default_row_size_limit} is missing or non-positive.
32+
* Matches the built-in default in {@code IoTDBRestServiceConfig}; a non-positive configured value
33+
* used to mean "unlimited" and is now treated as this cap instead of being clamped down to 1.
34+
*/
35+
private static final int DEFAULT_ROW_SIZE_LIMIT = 10000;
36+
37+
private QueryRowLimitUtils() {}
38+
39+
public static int resolveActualRowSizeLimit(
40+
Integer requestedRowSizeLimit, int configuredRowSizeLimit) {
41+
int hardLimit = normalizeRowSizeLimit(configuredRowSizeLimit);
42+
if (requestedRowSizeLimit == null) {
43+
return hardLimit;
44+
}
45+
return normalizeRowSizeLimit(Math.min(requestedRowSizeLimit, hardLimit));
46+
}
47+
48+
public static int normalizeRowSizeLimit(int rowSizeLimit) {
49+
return rowSizeLimit > 0 ? rowSizeLimit : DEFAULT_ROW_SIZE_LIMIT;
50+
}
51+
52+
public static boolean exceedsLimit(
53+
int fetchedRowCount, int incomingRowCount, int actualRowSizeLimit) {
54+
return incomingRowCount > 0
55+
&& (long) fetchedRowCount + incomingRowCount > normalizeRowSizeLimit(actualRowSizeLimit);
56+
}
57+
58+
public static Response buildRowSizeLimitExceededResponse(int actualRowSizeLimit) {
59+
int rowSizeLimit = normalizeRowSizeLimit(actualRowSizeLimit);
60+
return Response.ok()
61+
.entity(
62+
new ExecutionStatus()
63+
.code(TSStatusCode.QUERY_PROCESS_ERROR.getStatusCode())
64+
.message(
65+
String.format(
66+
RestMessages
67+
.MESSAGE_DATASET_ROW_SIZE_EXCEEDED_THE_GIVEN_MAX_ROW_SIZE_ARG_7A3F6452,
68+
rowSizeLimit)))
69+
.build();
70+
}
71+
}

‎external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/QueryDataSetHandler.java‎

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement;
2323
import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
2424
import org.apache.iotdb.db.queryengine.plan.execution.IQueryExecution;
25-
import org.apache.iotdb.rest.protocol.model.ExecutionStatus;
25+
import org.apache.iotdb.rest.protocol.handler.QueryRowLimitUtils;
2626
import org.apache.iotdb.rest.protocol.table.v1.model.QueryDataSet;
27-
import org.apache.iotdb.rpc.TSStatusCode;
2827

2928
import org.apache.tsfile.block.column.Column;
3029
import org.apache.tsfile.common.conf.TSFileConfig;
@@ -45,7 +44,7 @@ public class QueryDataSetHandler {
4544
private QueryDataSetHandler() {}
4645

4746
/**
48-
* @param actualRowSizeLimit max number of rows to return. no limit when actualRowSizeLimit <= 0.
47+
* @param actualRowSizeLimit max number of rows to return.
4948
*/
5049
public static Response fillQueryDataSet(
5150
IQueryExecution queryExecution, Statement statement, int actualRowSizeLimit)
@@ -61,7 +60,6 @@ public static Response fillQueryDataSet(
6160
IQueryExecution queryExecution, final int actualRowSizeLimit) throws IoTDBException {
6261
QueryDataSet targetDataSet = new QueryDataSet();
6362
int fetched = 0;
64-
int columnNum = queryExecution.getOutputValueColumnCount();
6563

6664
DatasetHeader header = queryExecution.getDatasetHeader();
6765
List<String> resultColumns = header.getRespColumns();
@@ -73,17 +71,6 @@ public static Response fillQueryDataSet(
7371
targetDataSet.addValuesItem(new ArrayList<>());
7472
}
7573
while (true) {
76-
if (0 < actualRowSizeLimit && actualRowSizeLimit <= fetched) {
77-
return Response.ok()
78-
.entity(
79-
new ExecutionStatus()
80-
.code(TSStatusCode.QUERY_PROCESS_ERROR.getStatusCode())
81-
.message(
82-
String.format(
83-
"Dataset row size exceeded the given max row size (%d)",
84-
actualRowSizeLimit)))
85-
.build();
86-
}
8774
Optional<TsBlock> optionalTsBlock = queryExecution.getBatchResult();
8875
if (!optionalTsBlock.isPresent() || optionalTsBlock.get().isEmpty()) {
8976
if (fetched == 0) {
@@ -94,22 +81,22 @@ public static Response fillQueryDataSet(
9481
}
9582
TsBlock tsBlock = optionalTsBlock.get();
9683
int currentCount = tsBlock.getPositionCount();
84+
if (QueryRowLimitUtils.exceedsLimit(fetched, currentCount, actualRowSizeLimit)) {
85+
return QueryRowLimitUtils.buildRowSizeLimitExceededResponse(actualRowSizeLimit);
86+
}
9787

9888
for (int k = 0; k < resultColumns.size(); k++) {
9989
Column column = tsBlock.getColumn(headerMap.get(resultColumns.get(k)));
10090
List<Object> targetDataSetColumn = targetDataSet.getValues().get(k);
10191
for (int i = 0; i < currentCount; i++) {
102-
fetched++;
10392
if (column.isNull(i)) {
10493
targetDataSetColumn.add(null);
10594
} else {
10695
addTypedValueToTarget(targetDataSet.getDataTypes(), k, i, targetDataSetColumn, column);
10796
}
10897
}
109-
if (k != columnNum - 1) {
110-
fetched -= currentCount;
111-
}
11298
}
99+
fetched += currentCount;
113100
}
114101
targetDataSet.setValues(convertColumnToRow(targetDataSet.getValues()));
115102
return Response.ok().entity(targetDataSet).build();
@@ -174,17 +161,6 @@ private static Response fillOtherQueryDataSet(
174161
int fetched = 0;
175162
int columnNum = queryExecution.getOutputValueColumnCount();
176163
while (true) {
177-
if (0 < actualRowSizeLimit && actualRowSizeLimit <= fetched) {
178-
return Response.ok()
179-
.entity(
180-
new ExecutionStatus()
181-
.code(TSStatusCode.QUERY_PROCESS_ERROR.getStatusCode())
182-
.message(
183-
String.format(
184-
"Dataset row size exceeded the given max row size (%d)",
185-
actualRowSizeLimit)))
186-
.build();
187-
}
188164
Optional<TsBlock> optionalTsBlock = queryExecution.getBatchResult();
189165
if (!optionalTsBlock.isPresent()) {
190166
if (fetched == 0) {
@@ -199,21 +175,21 @@ private static Response fillOtherQueryDataSet(
199175
targetDataSet.setValues(new ArrayList<>());
200176
return Response.ok().entity(targetDataSet).build();
201177
}
178+
if (QueryRowLimitUtils.exceedsLimit(fetched, currentCount, actualRowSizeLimit)) {
179+
return QueryRowLimitUtils.buildRowSizeLimitExceededResponse(actualRowSizeLimit);
180+
}
202181
for (int k = 0; k < columnNum; k++) {
203182
Column column = tsBlock.getColumn(targetDataSetIndexToSourceDataSetIndex[k]);
204183
List<Object> targetDataSetColumn = targetDataSet.getValues().get(k);
205184
for (int i = 0; i < currentCount; i++) {
206-
fetched++;
207185
if (column.isNull(i)) {
208186
targetDataSetColumn.add(null);
209187
} else {
210188
addTypedValueToTarget(targetDataSet.getDataTypes(), k, i, targetDataSetColumn, column);
211189
}
212190
}
213-
if (k != columnNum - 1) {
214-
fetched -= currentCount;
215-
}
216191
}
192+
fetched += currentCount;
217193
}
218194
targetDataSet.setValues(convertColumnToRow(targetDataSet.getValues()));
219195
return Response.ok().entity(targetDataSet).build();

‎external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/impl/RestApiServiceImpl.java‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement;
3939
import org.apache.iotdb.db.utils.CommonUtils;
4040
import org.apache.iotdb.db.utils.SetThreadName;
41+
import org.apache.iotdb.rest.protocol.handler.QueryRowLimitUtils;
4142
import org.apache.iotdb.rest.protocol.table.v1.NotFoundException;
4243
import org.apache.iotdb.rest.protocol.table.v1.RestApiService;
4344
import org.apache.iotdb.rest.protocol.table.v1.handler.ExceptionHandler;
@@ -63,11 +64,12 @@ public class RestApiServiceImpl extends RestApiService {
6364

6465
private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
6566

66-
private final Integer defaultQueryRowLimit;
67+
private final int defaultQueryRowLimit;
6768

6869
public RestApiServiceImpl() {
6970
defaultQueryRowLimit =
70-
IoTDBRestServiceDescriptor.getInstance().getConfig().getRestQueryDefaultRowSizeLimit();
71+
QueryRowLimitUtils.normalizeRowSizeLimit(
72+
IoTDBRestServiceDescriptor.getInstance().getConfig().getRestQueryDefaultRowSizeLimit());
7173
}
7274

7375
public Response executeQueryInternal(
@@ -103,7 +105,8 @@ public Response executeQueryInternal(
103105
QueryDataSetHandler.fillQueryDataSet(
104106
queryExecution,
105107
statement,
106-
sql.getRowLimit() == null ? defaultQueryRowLimit : sql.getRowLimit());
108+
QueryRowLimitUtils.resolveActualRowSizeLimit(
109+
sql.getRowLimit(), defaultQueryRowLimit));
107110
if (queryExecution.getQueryType() == QueryType.READ_WRITE) {
108111
return responseGenerateHelper(result);
109112
}

0 commit comments

Comments
 (0)