This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 58ec38a371d Enhance approx_percentile with strict weight validation,
improved error handling and IT coverage (#17368)
58ec38a371d is described below
commit 58ec38a371dcb313455e4daabf5d9e546a25af99
Author: FearfulTomcat27 <[email protected]>
AuthorDate: Thu Mar 26 20:50:50 2026 +0800
Enhance approx_percentile with strict weight validation, improved error
handling and IT coverage (#17368)
---
.../it/query/recent/IoTDBTableAggregationIT.java | 24 +++++++++++
.../ApproxPercentileWithWeightAccumulator.java | 50 ++++++++++++++++++++++
...oupedApproxPercentileWithWeightAccumulator.java | 49 +++++++++++++++++++++
.../relational/metadata/TableMetadataImpl.java | 21 +++++----
4 files changed, 135 insertions(+), 9 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBTableAggregationIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBTableAggregationIT.java
index df6ed279ea0..460fe83a397 100644
---
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBTableAggregationIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBTableAggregationIT.java
@@ -4352,6 +4352,18 @@ public class IoTDBTableAggregationIT {
"2024-09-24T06:15:55.000Z,shanghai,55,null,",
},
DATABASE_NAME);
+
+ tableResultSetEqualTest(
+ "select approx_percentile(s1,null,0.5) from table1",
+ new String[] {"_col0"},
+ new String[] {"null,"},
+ DATABASE_NAME);
+
+ tableResultSetEqualTest(
+ "select 1 as g, approx_percentile(s1,null,0.5) from table1 group by 1",
+ new String[] {"g", "_col1"},
+ new String[] {"1,null,"},
+ DATABASE_NAME);
}
@Test
@@ -4432,6 +4444,18 @@ public class IoTDBTableAggregationIT {
"select approx_percentile(s5,0.5) from table1",
"701: Aggregation functions [approx_percentile] should have value
column as numeric type [INT32, INT64, FLOAT, DOUBLE, TIMESTAMP]",
DATABASE_NAME);
+ tableAssertTestFail(
+ "select approx_percentile(s1,-1,0.5) from table1",
+ "701: weight must be >= 1, was -1",
+ DATABASE_NAME);
+ tableAssertTestFail(
+ "select approx_percentile(s1,s2,0.5) from table1",
+ "701: Aggregation functions [approx_percentile] do not support weight
as INT64 type",
+ DATABASE_NAME);
+ tableAssertTestFail(
+ "select 1 as g, approx_percentile(s1,s2,0.5) from table1 group by 1",
+ "701: Aggregation functions [approx_percentile] do not support weight
as INT64 type",
+ DATABASE_NAME);
}
// ==================================================================
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/ApproxPercentileWithWeightAccumulator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/ApproxPercentileWithWeightAccumulator.java
index 0e7de6c612b..f52165472f7 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/ApproxPercentileWithWeightAccumulator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/ApproxPercentileWithWeightAccumulator.java
@@ -14,6 +14,8 @@
package
org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation;
+import org.apache.iotdb.db.exception.sql.SemanticException;
+
import org.apache.tsfile.block.column.Column;
import org.apache.tsfile.enums.TSDataType;
@@ -32,6 +34,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
if (mask.isSelectAll()) {
for (int i = 0; i < valueColumn.getPositionCount(); i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
if (!valueColumn.isNull(i)) {
tDigest.add(valueColumn.getInt(i), weightColumn.getInt(i));
}
@@ -41,6 +49,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
int position;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
if (!valueColumn.isNull(position)) {
tDigest.add(valueColumn.getInt(position),
weightColumn.getInt(position));
}
@@ -57,6 +71,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
if (mask.isSelectAll()) {
for (int i = 0; i < valueColumn.getPositionCount(); i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
if (!valueColumn.isNull(i)) {
tDigest.add(toDoubleExact(valueColumn.getLong(i)),
weightColumn.getInt(i));
}
@@ -66,6 +86,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
int position;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
if (!valueColumn.isNull(position)) {
tDigest.add(toDoubleExact(valueColumn.getLong(position)),
weightColumn.getInt(position));
}
@@ -82,6 +108,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
if (mask.isSelectAll()) {
for (int i = 0; i < valueColumn.getPositionCount(); i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
if (!valueColumn.isNull(i)) {
tDigest.add(valueColumn.getFloat(i), weightColumn.getInt(i));
}
@@ -91,6 +123,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
int position;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
if (!valueColumn.isNull(position)) {
tDigest.add(valueColumn.getFloat(position),
weightColumn.getInt(position));
}
@@ -107,6 +145,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
if (mask.isSelectAll()) {
for (int i = 0; i < valueColumn.getPositionCount(); i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
if (!valueColumn.isNull(i)) {
tDigest.add(valueColumn.getDouble(i), weightColumn.getInt(i));
}
@@ -116,6 +160,12 @@ public class ApproxPercentileWithWeightAccumulator extends
AbstractApproxPercent
int position;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
if (!valueColumn.isNull(position)) {
tDigest.add(valueColumn.getDouble(position),
weightColumn.getInt(position));
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/grouped/GroupedApproxPercentileWithWeightAccumulator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/grouped/GroupedApproxPercentileWithWeightAccumulator.java
index abde1dde650..b7c587df0d0 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/grouped/GroupedApproxPercentileWithWeightAccumulator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/grouped/GroupedApproxPercentileWithWeightAccumulator.java
@@ -14,6 +14,7 @@
package
org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped;
+import org.apache.iotdb.db.exception.sql.SemanticException;
import
org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.AggregationMask;
import
org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.approximate.TDigest;
@@ -36,6 +37,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
if (mask.isSelectAll()) {
for (int i = 0; i < positionCount; i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
int groupId = groupIds[i];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(i)) {
@@ -48,6 +55,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
int groupId;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
groupId = groupIds[position];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(position)) {
@@ -66,6 +79,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
if (mask.isSelectAll()) {
for (int i = 0; i < positionCount; i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
int groupId = groupIds[i];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(i)) {
@@ -78,6 +97,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
int groupId;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
groupId = groupIds[position];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(position)) {
@@ -96,6 +121,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
if (mask.isSelectAll()) {
for (int i = 0; i < positionCount; i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
int groupId = groupIds[i];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(i)) {
@@ -108,6 +139,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
int groupId;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
groupId = groupIds[position];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(position)) {
@@ -126,6 +163,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
if (mask.isSelectAll()) {
for (int i = 0; i < positionCount; i++) {
+ if (weightColumn.isNull(i)) {
+ continue;
+ }
+ if (weightColumn.getInt(i) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(i));
+ }
int groupId = groupIds[i];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(i)) {
@@ -138,6 +181,12 @@ public class GroupedApproxPercentileWithWeightAccumulator
int groupId;
for (int i = 0; i < positionCount; i++) {
position = selectedPositions[i];
+ if (weightColumn.isNull(position)) {
+ continue;
+ }
+ if (weightColumn.getInt(position) < 1) {
+ throw new SemanticException("weight must be >= 1, was " +
weightColumn.getInt(position));
+ }
groupId = groupIds[position];
TDigest tDigest = array.get(groupId);
if (!valueColumn.isNull(position)) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
index ed0be5b10d0..000d9cddf80 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java
@@ -1194,19 +1194,22 @@ public class TableMetadataImpl implements Metadata {
functionName));
}
- // Validate percentage and weight parameters
- boolean hasInvalidTypes =
- (argumentSize == 2 && !isDecimalType(argumentTypes.get(1)))
- || (argumentSize == 3
- && (!isIntegerNumber(argumentTypes.get(1))
- || !isDecimalType(argumentTypes.get(2))));
-
- if (hasInvalidTypes) {
+ Type percentageType = argumentTypes.get(argumentSize - 1);
+ if (!isDecimalType(percentageType)) {
throw new SemanticException(
String.format(
- "Aggregation functions [%s] should have weight as integer
type and percentage as decimal type",
+ "Aggregation functions [%s] should have percentage as
decimal type",
functionName));
}
+ if (argumentSize == 3) {
+ Type weightType = argumentTypes.get(1);
+ if (!INT32.equals(weightType) && !isUnknownType(weightType)) {
+ throw new SemanticException(
+ String.format(
+ "Aggregation functions [%s] do not support weight as %s
type",
+ functionName, weightType.getDisplayName()));
+ }
+ }
break;
case SqlConstant.COUNT: