This is an automated email from the ASF dual-hosted git repository. chenyz pushed a commit to branch tvf_add_checker in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 3ed7261fa1bdf9fda56cf2cdc694ba37f97352dd Author: Chen YZ <[email protected]> AuthorDate: Mon Apr 21 23:55:24 2025 +0800 done --- .../udf/example/relational/MySelectColumn.java | 89 ++++++++++++++++++++++ .../relational/it/db/it/IoTDBWindowTVFIT.java | 23 +++++- .../db/it/udf/IoTDBUserDefinedTableFunctionIT.java | 16 ++++ .../table/argument/ScalarArgumentChecker.java | 32 ++++++++ .../processor/TableFunctionDataProcessor.java | 6 +- .../ScalarParameterSpecification.java | 23 +++++- .../process/function/TableFunctionOperator.java | 18 ++++- .../plan/planner/TableOperatorGenerator.java | 3 + .../relational/analyzer/StatementAnalyzer.java | 8 ++ .../relational/tvf/CumulateTableFunction.java | 13 +++- .../builtin/relational/tvf/HOPTableFunction.java | 13 +++- .../relational/tvf/TumbleTableFunction.java | 7 +- 12 files changed, 237 insertions(+), 14 deletions(-) diff --git a/integration-test/src/main/java/org/apache/iotdb/db/query/udf/example/relational/MySelectColumn.java b/integration-test/src/main/java/org/apache/iotdb/db/query/udf/example/relational/MySelectColumn.java new file mode 100644 index 00000000000..27409032702 --- /dev/null +++ b/integration-test/src/main/java/org/apache/iotdb/db/query/udf/example/relational/MySelectColumn.java @@ -0,0 +1,89 @@ +/* + * 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.iotdb.db.query.udf.example.relational; + +import org.apache.iotdb.udf.api.exception.UDFException; +import org.apache.iotdb.udf.api.relational.TableFunction; +import org.apache.iotdb.udf.api.relational.table.TableFunctionAnalysis; +import org.apache.iotdb.udf.api.relational.table.TableFunctionProcessorProvider; +import org.apache.iotdb.udf.api.relational.table.argument.Argument; +import org.apache.iotdb.udf.api.relational.table.argument.DescribedSchema; +import org.apache.iotdb.udf.api.relational.table.argument.ScalarArgument; +import org.apache.iotdb.udf.api.relational.table.argument.TableArgument; +import org.apache.iotdb.udf.api.relational.table.processor.TableFunctionDataProcessor; +import org.apache.iotdb.udf.api.relational.table.specification.ParameterSpecification; +import org.apache.iotdb.udf.api.relational.table.specification.ScalarParameterSpecification; +import org.apache.iotdb.udf.api.relational.table.specification.TableParameterSpecification; +import org.apache.iotdb.udf.api.type.Type; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class MySelectColumn implements TableFunction { + private final String TBL_PARAM = "DATA"; + private final String COL_PARAM = "SELECT"; + + @Override + public List<ParameterSpecification> getArgumentsSpecifications() { + return Arrays.asList( + TableParameterSpecification.builder().name(TBL_PARAM).build(), + ScalarParameterSpecification.builder().name(COL_PARAM).type(Type.STRING).build()); + } + + @Override + public TableFunctionAnalysis analyze(Map<String, Argument> arguments) throws UDFException { + TableArgument tableArgument = (TableArgument) arguments.get(TBL_PARAM); + String selectColumn = (String) ((ScalarArgument) arguments.get(COL_PARAM)).getValue(); + List<Integer> requiredColumns = new ArrayList<>(); + DescribedSchema.Builder schemaBuilder = DescribedSchema.builder(); + for (int i = 0; i < tableArgument.getFieldNames().size(); i++) { + Optional<String> fieldName = tableArgument.getFieldNames().get(i); + if (fieldName.isPresent() && fieldName.get().equalsIgnoreCase(selectColumn)) { + requiredColumns.add(i); + schemaBuilder.addField(fieldName, tableArgument.getFieldTypes().get(i)); + } + } + return TableFunctionAnalysis.builder() + .properColumnSchema(schemaBuilder.build()) + .requiredColumns(TBL_PARAM, requiredColumns) + .build(); + } + + @Override + public TableFunctionProcessorProvider getProcessorProvider(Map<String, Argument> arguments) { + return new TableFunctionProcessorProvider() { + @Override + public TableFunctionDataProcessor getDataProcessor() { + return (input, properColumnBuilders, passThroughIndexBuilder) -> { + for (int i = 0; i < input.size(); i++) { + if (input.isNull(i)) { + properColumnBuilders.get(i).appendNull(); + } else { + properColumnBuilders.get(i).writeObject(input.getObject(i)); + } + } + }; + } + }; + } +} diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java index 68cd390f094..c947722f26d 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBWindowTVFIT.java @@ -130,6 +130,14 @@ public class IoTDBWindowTVFIT { expectedHeader, retArray, DATABASE_NAME); + tableAssertTestFail( + "SELECT * FROM HOP(DATA => bid, TIMECOL => 'time', SLIDE => -300000, SIZE => 600000) ORDER BY stock_id, time", + "Invalid scalar argument SLIDE, should be a positive value", + DATABASE_NAME); + tableAssertTestFail( + "SELECT * FROM HOP(DATA => bid, TIMECOL => 'time', SLIDE => 300000, SIZE => -600000) ORDER BY stock_id, time", + "Invalid scalar argument SIZE, should be a positive value", + DATABASE_NAME); } @Test @@ -274,6 +282,10 @@ public class IoTDBWindowTVFIT { expectedHeader, retArray, DATABASE_NAME); + tableAssertTestFail( + "SELECT * FROM TUMBLE(DATA => bid, TIMECOL => 'time', SIZE => 0m) ORDER BY stock_id, time", + "Invalid scalar argument SIZE, should be a positive value", + DATABASE_NAME); } @Test @@ -325,10 +337,17 @@ public class IoTDBWindowTVFIT { DATABASE_NAME); // test UDFException - String errMsg = "Cumulative table function requires size must be an integral multiple of step."; tableAssertTestFail( "SELECT window_start, window_end, stock_id, sum(price) as sum FROM CUMULATE(DATA => bid, TIMECOL => 'time', STEP => 4m, SIZE => 10m) GROUP BY window_start, window_end, stock_id ORDER BY stock_id, window_start", - errMsg, + "Cumulative table function requires size must be an integral multiple of step.", + DATABASE_NAME); + tableAssertTestFail( + "SELECT * FROM CUMULATE(DATA => bid, TIMECOL => 'time', STEP => 0m, SIZE => 5m) ORDER BY stock_id, time", + "Invalid scalar argument STEP, should be a positive value", + DATABASE_NAME); + tableAssertTestFail( + "SELECT * FROM CUMULATE(DATA => bid, TIMECOL => 'time', STEP => 1m, SIZE => 0m) ORDER BY stock_id, time", + "Invalid scalar argument SIZE, should be a positive value", DATABASE_NAME); } } diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/udf/IoTDBUserDefinedTableFunctionIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/udf/IoTDBUserDefinedTableFunctionIT.java index 26f229c082f..7fe6648fe70 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/udf/IoTDBUserDefinedTableFunctionIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/udf/IoTDBUserDefinedTableFunctionIT.java @@ -242,6 +242,22 @@ public class IoTDBUserDefinedTableFunctionIT { DATABASE_NAME); } + @Test + public void testPassThroughPartitionColumn() { + SQLFunctionUtils.createUDF( + "MY_SELECT", "org.apache.iotdb.db.query.udf.example.relational.MySelectColumn"); + String[] expectedHeader = new String[] {"s1", "device_id"}; + String[] retArray = + new String[] { + "1,d0,", "null,d0,", "3,d0,", "4,d1,", + }; + tableResultSetEqualTest( + "select * from MY_SELECT(vehicle PARTITION BY device_id, 's1') ORDER BY device_id", + expectedHeader, + retArray, + DATABASE_NAME); + } + @Test public void testIllegalInput() { SQLFunctionUtils.createUDF( diff --git a/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/argument/ScalarArgumentChecker.java b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/argument/ScalarArgumentChecker.java new file mode 100644 index 00000000000..241ce5939bc --- /dev/null +++ b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/argument/ScalarArgumentChecker.java @@ -0,0 +1,32 @@ +/* + * 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.iotdb.udf.api.relational.table.argument; + +import java.util.function.Function; + +public class ScalarArgumentChecker { + public static Function<Object, String> POSITIVE_LONG_CHECKER = + (value) -> { + if (value instanceof Long && (Long) value > 0) { + return null; + } + return "should be a positive value"; + }; +} diff --git a/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/processor/TableFunctionDataProcessor.java b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/processor/TableFunctionDataProcessor.java index c3cd5483b64..d96cc4a98c4 100644 --- a/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/processor/TableFunctionDataProcessor.java +++ b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/processor/TableFunctionDataProcessor.java @@ -43,7 +43,8 @@ public interface TableFunctionDataProcessor { * @param properColumnBuilders A list of {@link ColumnBuilder} for each column in the output * table. * @param passThroughIndexBuilder A {@link ColumnBuilder} for pass through columns. Index is - * started from 0 of the whole partition. + * started from 0 of the whole partition. It will be null if table argument is not declared + * with PASS_THROUGH_COLUMNS. */ void process( Record input, @@ -56,7 +57,8 @@ public interface TableFunctionDataProcessor { * * @param columnBuilders A list of {@link ColumnBuilder} for each column in the output table. * @param passThroughIndexBuilder A {@link ColumnBuilder} for pass through columns. Index is - * started from 0 of the whole partition. + * started from 0 of the whole partition. It will be null if table argument is not declared + * with PASS_THROUGH_COLUMNS. */ default void finish(List<ColumnBuilder> columnBuilders, ColumnBuilder passThroughIndexBuilder) { // do nothing diff --git a/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/specification/ScalarParameterSpecification.java b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/specification/ScalarParameterSpecification.java index 496d923b079..7333bd32601 100644 --- a/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/specification/ScalarParameterSpecification.java +++ b/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/relational/table/specification/ScalarParameterSpecification.java @@ -21,13 +21,21 @@ package org.apache.iotdb.udf.api.relational.table.specification; import org.apache.iotdb.udf.api.type.Type; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.function.Function; public class ScalarParameterSpecification extends ParameterSpecification { private final Type type; + private final List<Function<Object, String>> checkers; private ScalarParameterSpecification( - String name, Type type, boolean required, Object defaultValue) { + String name, + Type type, + boolean required, + Object defaultValue, + List<Function<Object, String>> checkers) { super(name, required, Optional.ofNullable(defaultValue)); this.type = type; if (defaultValue != null && !type.checkObjectType(defaultValue)) { @@ -35,12 +43,17 @@ public class ScalarParameterSpecification extends ParameterSpecification { String.format( "default value %s does not match the declared type: %s", defaultValue, type)); } + this.checkers = checkers; } public Type getType() { return type; } + public List<Function<Object, String>> getCheckers() { + return checkers; + } + public static Builder builder() { return new Builder(); } @@ -50,6 +63,7 @@ public class ScalarParameterSpecification extends ParameterSpecification { private Type type; private boolean required = true; private Object defaultValue; + private List<Function<Object, String>> checkers = new ArrayList<>(); private Builder() {} @@ -63,6 +77,11 @@ public class ScalarParameterSpecification extends ParameterSpecification { return this; } + public Builder addChecker(Function<Object, String> checker) { + this.checkers.add(checker); + return this; + } + public Builder defaultValue(Object defaultValue) { this.required = false; this.defaultValue = defaultValue; @@ -70,7 +89,7 @@ public class ScalarParameterSpecification extends ParameterSpecification { } public ScalarParameterSpecification build() { - return new ScalarParameterSpecification(name, type, required, defaultValue); + return new ScalarParameterSpecification(name, type, required, defaultValue, checkers); } } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/function/TableFunctionOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/function/TableFunctionOperator.java index a0a2073f7cf..4e9f7e436a5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/function/TableFunctionOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/function/TableFunctionOperator.java @@ -38,6 +38,7 @@ import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.LongColumn; import org.apache.tsfile.read.common.block.column.LongColumnBuilder; import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; import org.apache.tsfile.utils.RamUsageEstimator; @@ -48,6 +49,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Queue; import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; @@ -70,6 +72,7 @@ public class TableFunctionOperator implements ProcessOperator { private final boolean needPassThrough; private final PartitionCache partitionCache; private final boolean requireRecordSnapshot; + private final boolean isDeclaredAsPassThrough; private TableFunctionDataProcessor processor; private PartitionState partitionState; @@ -87,6 +90,7 @@ public class TableFunctionOperator implements ProcessOperator { int properChannelCount, List<Integer> requiredChannels, List<Integer> passThroughChannels, + boolean isDeclaredAsPassThrough, List<Integer> partitionChannels, boolean requireRecordSnapshot) { this.operatorContext = operatorContext; @@ -96,6 +100,7 @@ public class TableFunctionOperator implements ProcessOperator { this.partitionRecognizer = new PartitionRecognizer( partitionChannels, requiredChannels, passThroughChannels, inputDataTypes); + this.isDeclaredAsPassThrough = isDeclaredAsPassThrough; this.needPassThrough = properChannelCount != outputDataTypes.size(); this.partitionState = null; this.properBlockBuilder = new TsBlockBuilder(outputDataTypes.subList(0, properChannelCount)); @@ -192,7 +197,7 @@ public class TableFunctionOperator implements ProcessOperator { } private ColumnBuilder getPassThroughIndexBuilder() { - return new LongColumnBuilder(null, 1); + return isDeclaredAsPassThrough ? new LongColumnBuilder(null, 1) : null; } private List<TsBlock> buildTsBlock( @@ -201,7 +206,7 @@ public class TableFunctionOperator implements ProcessOperator { if (properChannelCount > 0) { // if there is proper column, use its position count positionCount = properColumnBuilders.get(0).getPositionCount(); - } else if (needPassThrough) { + } else if (isDeclaredAsPassThrough) { // if there is no proper column, use pass through column's position count positionCount = passThroughIndexBuilder.getPositionCount(); } @@ -215,7 +220,14 @@ public class TableFunctionOperator implements ProcessOperator { if (needPassThrough) { // handle pass through column only if needed int builtCount = 0; - Column passThroughIndex = passThroughIndexBuilder.build(); + Column passThroughIndex; + if (isDeclaredAsPassThrough) { + passThroughIndex = passThroughIndexBuilder.build(); + } else { + passThroughIndex = + new RunLengthEncodedColumn( + new LongColumn(1, Optional.empty(), new long[] {0}), positionCount); + } for (Column[] passThroughColumns : partitionCache.getPassThroughResult(passThroughIndex)) { int subBlockPositionCount = passThroughColumns[0].getPositionCount(); TsBlock subProperBlock = properBlock.getRegion(builtCount, subBlockPositionCount); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java index ee6a13a006c..e98259a353a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java @@ -2934,6 +2934,9 @@ public class TableOperatorGenerator extends PlanVisitor<Operator, LocalExecution properChannelCount, requiredChannels, passThroughChannels, + passThroughSpecification + .map(TableFunctionNode.PassThroughSpecification::isDeclaredAsPassThrough) + .orElse(false), partitionChannels, node.isRequireRecordSnapshot()); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java index ec8279853bd..8c945bd72ab 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java @@ -4417,6 +4417,14 @@ public class StatementAnalyzer { argumentSpecification.getType(), constantValue.getClass().getSimpleName())); } } + for (Function<Object, String> checker : argumentSpecification.getCheckers()) { + String errMsg = checker.apply(constantValue); + if (errMsg != null) { + throw new SemanticException( + String.format( + "Invalid scalar argument %s, %s", argumentSpecification.getName(), errMsg)); + } + } return new ArgumentAnalysis( new ScalarArgument(argumentSpecification.getType(), constantValue), Optional.empty()); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/CumulateTableFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/CumulateTableFunction.java index acb3e588f05..aee0481cd24 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/CumulateTableFunction.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/CumulateTableFunction.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import static org.apache.iotdb.commons.udf.builtin.relational.tvf.WindowTVFUtils.findColumnIndex; +import static org.apache.iotdb.udf.api.relational.table.argument.ScalarArgumentChecker.POSITIVE_LONG_CHECKER; public class CumulateTableFunction implements TableFunction { @@ -64,8 +65,16 @@ public class CumulateTableFunction implements TableFunction { .type(Type.STRING) .defaultValue("time") .build(), - ScalarParameterSpecification.builder().name(SIZE_PARAMETER_NAME).type(Type.INT64).build(), - ScalarParameterSpecification.builder().name(STEP_PARAMETER_NAME).type(Type.INT64).build(), + ScalarParameterSpecification.builder() + .name(SIZE_PARAMETER_NAME) + .type(Type.INT64) + .addChecker(POSITIVE_LONG_CHECKER) + .build(), + ScalarParameterSpecification.builder() + .name(STEP_PARAMETER_NAME) + .type(Type.INT64) + .addChecker(POSITIVE_LONG_CHECKER) + .build(), ScalarParameterSpecification.builder() .name(ORIGIN_PARAMETER_NAME) .type(Type.TIMESTAMP) diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/HOPTableFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/HOPTableFunction.java index b2178c5805a..f621d24e80b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/HOPTableFunction.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/HOPTableFunction.java @@ -41,6 +41,7 @@ import java.util.List; import java.util.Map; import static org.apache.iotdb.commons.udf.builtin.relational.tvf.WindowTVFUtils.findColumnIndex; +import static org.apache.iotdb.udf.api.relational.table.argument.ScalarArgumentChecker.POSITIVE_LONG_CHECKER; public class HOPTableFunction implements TableFunction { @@ -63,8 +64,16 @@ public class HOPTableFunction implements TableFunction { .type(Type.STRING) .defaultValue("time") .build(), - ScalarParameterSpecification.builder().name(SIZE_PARAMETER_NAME).type(Type.INT64).build(), - ScalarParameterSpecification.builder().name(SLIDE_PARAMETER_NAME).type(Type.INT64).build(), + ScalarParameterSpecification.builder() + .name(SIZE_PARAMETER_NAME) + .addChecker(POSITIVE_LONG_CHECKER) + .type(Type.INT64) + .build(), + ScalarParameterSpecification.builder() + .name(SLIDE_PARAMETER_NAME) + .addChecker(POSITIVE_LONG_CHECKER) + .type(Type.INT64) + .build(), ScalarParameterSpecification.builder() .name(ORIGIN_PARAMETER_NAME) .type(Type.TIMESTAMP) diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/TumbleTableFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/TumbleTableFunction.java index a239c694129..3c7ec080cef 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/TumbleTableFunction.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/TumbleTableFunction.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import static org.apache.iotdb.commons.udf.builtin.relational.tvf.WindowTVFUtils.findColumnIndex; +import static org.apache.iotdb.udf.api.relational.table.argument.ScalarArgumentChecker.POSITIVE_LONG_CHECKER; public class TumbleTableFunction implements TableFunction { private static final String DATA_PARAMETER_NAME = "DATA"; @@ -62,7 +63,11 @@ public class TumbleTableFunction implements TableFunction { .type(Type.STRING) .defaultValue("time") .build(), - ScalarParameterSpecification.builder().name(SIZE_PARAMETER_NAME).type(Type.INT64).build(), + ScalarParameterSpecification.builder() + .name(SIZE_PARAMETER_NAME) + .addChecker(POSITIVE_LONG_CHECKER) + .type(Type.INT64) + .build(), ScalarParameterSpecification.builder() .name(ORIGIN_PARAMETER_NAME) .type(Type.TIMESTAMP)
