Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,142 @@ public void normalFillTest() {
}
}

@Test
public void nextFillTest() {
String[] expectedHeader =
new String[] {
"time", "device_id", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10"
};
String[] retArray =
new String[] {
"1970-01-01T00:00:00.001Z,d1,1,11,1.1,11.1,true,text1,string1,0xcafebabe01,1970-01-01T00:00:00.001Z,2024-10-01,",
"1970-01-01T00:00:00.002Z,d1,2,22,2.2,22.2,false,text3,string3,0xcafebabe03,1970-01-01T00:00:00.003Z,2024-10-03,",
"1970-01-01T00:00:00.003Z,d1,5,55,5.5,55.5,false,text3,string3,0xcafebabe03,1970-01-01T00:00:00.003Z,2024-10-03,",
"1970-01-01T00:00:00.004Z,d1,5,55,5.5,55.5,false,text4,string4,0xcafebabe04,1970-01-01T00:00:00.004Z,2024-10-04,",
"1970-01-01T00:00:00.005Z,d1,5,55,5.5,55.5,false,text8,string8,0xcafebabe08,1970-01-01T00:00:00.008Z,2024-10-08,",
"1970-01-01T00:00:00.007Z,d1,7,77,7.7,77.7,true,text8,string8,0xcafebabe08,1970-01-01T00:00:00.008Z,2024-10-08,",
"1970-01-01T00:00:00.008Z,d1,null,null,null,null,null,text8,string8,0xcafebabe08,1970-01-01T00:00:00.008Z,2024-10-08,",
};
tableResultSetEqualTest(
"select * from table1 FILL METHOD NEXT", expectedHeader, retArray, DATABASE_NAME);

retArray =
new String[] {
"1970-01-01T00:00:00.001Z,d1,1,11,1.1,11.1,true,text1,string1,0xcafebabe01,1970-01-01T00:00:00.001Z,2024-10-01,",
"1970-01-01T00:00:00.002Z,d1,2,22,2.2,22.2,false,text3,string3,0xcafebabe03,1970-01-01T00:00:00.003Z,2024-10-03,",
"1970-01-01T00:00:00.003Z,d1,5,55,5.5,55.5,false,text3,string3,0xcafebabe03,1970-01-01T00:00:00.003Z,2024-10-03,",
"1970-01-01T00:00:00.004Z,d1,5,55,5.5,55.5,false,text4,string4,0xcafebabe04,1970-01-01T00:00:00.004Z,2024-10-04,",
"1970-01-01T00:00:00.005Z,d1,5,55,5.5,55.5,false,null,null,null,null,null,",
"1970-01-01T00:00:00.007Z,d1,7,77,7.7,77.7,true,text8,string8,0xcafebabe08,1970-01-01T00:00:00.008Z,2024-10-08,",
"1970-01-01T00:00:00.008Z,d1,null,null,null,null,null,text8,string8,0xcafebabe08,1970-01-01T00:00:00.008Z,2024-10-08,",
};
tableResultSetEqualTest(
"select * from table1 FILL METHOD NEXT TIME_BOUND 2ms TIME_COLUMN 1",
expectedHeader,
retArray,
DATABASE_NAME);

retArray =
new String[] {
"1970-01-01T00:00:00.001Z,d1,1,11,1.1,11.1,true,text1,string1,0xcafebabe01,1970-01-01T00:00:00.001Z,2024-10-01,",
"1970-01-01T00:00:00.002Z,d1,2,22,2.2,22.2,false,null,null,null,null,null,",
"1970-01-01T00:00:00.003Z,d1,null,null,null,null,null,text3,string3,0xcafebabe03,1970-01-01T00:00:00.003Z,2024-10-03,",
"1970-01-01T00:00:00.004Z,d1,null,null,null,null,null,text4,string4,0xcafebabe04,1970-01-01T00:00:00.004Z,2024-10-04,",
"1970-01-01T00:00:00.005Z,d1,5,55,5.5,55.5,false,null,null,null,null,null,",
"1970-01-01T00:00:00.007Z,d1,7,77,7.7,77.7,true,null,null,null,null,null,",
"1970-01-01T00:00:00.008Z,d1,null,null,null,null,null,text8,string8,0xcafebabe08,1970-01-01T00:00:00.008Z,2024-10-08,",
};
tableResultSetEqualTest(
"select * from table1 FILL METHOD NEXT TIME_BOUND 2ms TIME_COLUMN 11",
expectedHeader,
retArray,
DATABASE_NAME);

expectedHeader = new String[] {"s1", "s6"};
retArray =
new String[] {
"1,text1,", "2,text3,", "5,text3,", "5,text4,", "5,text8,", "7,text8,", "null,text8,",
};
tableResultSetEqualTest(
"select s1,s6 from table1 FILL METHOD NEXT", expectedHeader, retArray, DATABASE_NAME);

expectedHeader = new String[] {"time", "s1", "s6"};
retArray =
new String[] {
"1970-01-01T00:00:00.008Z,null,text8,",
"1970-01-01T00:00:00.007Z,7,text8,",
"1970-01-01T00:00:00.005Z,5,text8,",
"1970-01-01T00:00:00.004Z,5,text4,",
"1970-01-01T00:00:00.003Z,5,text3,",
"1970-01-01T00:00:00.002Z,2,text3,",
"1970-01-01T00:00:00.001Z,1,text1,",
};
tableResultSetEqualTest(
"select time,s1,s6 from table1 FILL METHOD NEXT order by time desc",
expectedHeader,
retArray,
DATABASE_NAME);

retArray =
new String[] {
"1970-01-01T00:00:00.001Z,1,text1,",
"1970-01-01T00:00:00.002Z,2,text1,",
"1970-01-01T00:00:00.003Z,2,text3,",
"1970-01-01T00:00:00.004Z,2,text4,",
"1970-01-01T00:00:00.005Z,5,text4,",
"1970-01-01T00:00:00.007Z,7,text4,",
"1970-01-01T00:00:00.008Z,7,text8,",
};
tableResultSetEqualTest(
"select * from (select time,s1,s6 from table1 order by time desc) FILL METHOD NEXT order by time",
expectedHeader,
retArray,
DATABASE_NAME);

expectedHeader = new String[] {"time", "s1"};
retArray =
new String[] {
"1970-01-01T00:00:00.001Z,1,",
"1970-01-01T00:00:00.002Z,2,",
"1970-01-01T00:00:00.003Z,5,",
};
tableResultSetEqualTest(
"select time,s1 from table1 FILL METHOD NEXT limit 3",
expectedHeader,
retArray,
DATABASE_NAME);

expectedHeader = new String[] {"time", "city", "device_id", "s1", "s2"};
retArray =
new String[] {
"1970-01-01T00:00:00.001Z,beijing,d1,102,1011,",
"1970-01-01T00:00:00.002Z,beijing,d1,102,null,",
"1970-01-01T00:00:00.003Z,beijing,d1,103,null,",
"1970-01-01T00:00:00.004Z,beijing,d1,104,null,",
"1970-01-01T00:00:00.001Z,beijing,d2,101,1022,",
"1970-01-01T00:00:00.002Z,beijing,d2,null,1022,",
"1970-01-01T00:00:00.003Z,beijing,d2,null,1033,",
"1970-01-01T00:00:00.004Z,beijing,d2,null,1044,",
"1970-01-01T00:00:00.001Z,shanghai,d1,212,2111,",
"1970-01-01T00:00:00.002Z,shanghai,d1,212,null,",
};
tableResultSetEqualTest(
"select time,city,device_id,s1,s2 from table2 FILL METHOD NEXT FILL_GROUP 2,3",
expectedHeader,
retArray,
DATABASE_NAME);
tableResultSetEqualTest(
"select time,city,device_id,s1,s2 from table2 FILL METHOD NEXT TIME_COLUMN 1 FILL_GROUP 2,3",
expectedHeader,
retArray,
DATABASE_NAME);
tableResultSetEqualTest(
"select time,city,device_id,s1,s2 from table2 FILL METHOD NEXT TIME_BOUND 2ms TIME_COLUMN 1 FILL_GROUP 2,3",
expectedHeader,
retArray,
DATABASE_NAME);
}

@Test
public void abNormalFillTest() {

Expand Down Expand Up @@ -705,6 +841,25 @@ public void abNormalFillTest() {
+ ": PREVIOUS FILL FILL_GROUP position 3 is not in select list",
DATABASE_NAME);

// --------------------------------- NEXT FILL ---------------------------------
tableAssertTestFail(
"select s1 from table1 FILL METHOD NEXT TIME_COLUMN 1",
TSStatusCode.SEMANTIC_ERROR.getStatusCode()
+ ": Don't need to specify TIME_COLUMN while either TIME_BOUND or FILL_GROUP parameter is not specified",
DATABASE_NAME);

tableAssertTestFail(
"select s1 from table1 FILL METHOD NEXT TIME_BOUND 2ms",
TSStatusCode.SEMANTIC_ERROR.getStatusCode()
+ ": Cannot infer TIME_COLUMN for NEXT FILL, there exists no column whose type is TIMESTAMP",
DATABASE_NAME);

tableAssertTestFail(
"select s1 from table1 FILL METHOD NEXT FILL_GROUP 1",
TSStatusCode.SEMANTIC_ERROR.getStatusCode()
+ ": Cannot infer TIME_COLUMN for NEXT FILL, there exists no column whose type is TIMESTAMP",
DATABASE_NAME);

// --------------------------------- LINEAR FILL ---------------------------------
tableAssertTestFail(
"select s1 from table1 FILL METHOD LINEAR",
Expand Down
101 changes: 101 additions & 0 deletions iotdb-core/calc-commons/src/main/codegen/templates/nextFill.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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.
*/
<@pp.dropOutputFile />

<#list allDataTypes.types as type>

<#assign className = "${type.dataType?cap_first}NextFill">
<@pp.changeOutputFile name="/org/apache/iotdb/calc/execution/operator/process/fill/next/${className}.java" />
package org.apache.iotdb.calc.execution.operator.process.fill.next;

import org.apache.iotdb.calc.execution.operator.process.fill.IFillFilter;
import org.apache.tsfile.block.column.Column;
import org.apache.tsfile.read.common.block.column.${type.column};
import org.apache.tsfile.read.common.block.column.${type.column}Builder;
import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn;
<#if type.dataType == "Binary">
import org.apache.tsfile.utils.Binary;
</#if>

import java.util.Optional;

/*
* This class is generated using freemarker and the ${.template_name} template.
*/
@SuppressWarnings("unused")
public class ${className} extends NextFill {

private ${type.dataType} nextValue;
private ${type.dataType} nextValueInCurrentColumn;

public ${className}(IFillFilter filter) {
super(filter);
}

@Override
void fillValue(Column column, int index, Object array) {
((${type.dataType}[]) array)[index] = column.get${type.dataType?cap_first}(index);
}

@Override
void fillNextValueInCurrentColumn(Object array, int index) {
((${type.dataType}[]) array)[index] = nextValueInCurrentColumn;
}

@Override
Object createValueArray(int size) {
return new ${type.dataType}[size];
}

@Override
Column createNullValueColumn() {
return ${type.column}Builder.NULL_VALUE_BLOCK;
}

@Override
Column createRunLengthEncodedFilledValueColumn(int size) {
return new RunLengthEncodedColumn(
new ${type.column}(1, Optional.empty(), new ${type.dataType}[] {nextValueInCurrentColumn}), size);
}

@Override
Column createFilledValueColumn(Object array, boolean[] isNull, boolean hasNullValue, int size) {
if (hasNullValue) {
return new ${type.column}(size, Optional.of(isNull), (${type.dataType}[]) array);
} else {
return new ${type.column}(size, Optional.empty(), (${type.dataType}[]) array);
}
}

@Override
void updateNextValue(Column nextValueColumn, int index) {
this.nextValue = nextValueColumn.get${type.dataType?cap_first}(index);
}

@Override
void updateNextValueInCurrentColumn(Column nextValueColumn, int index) {
this.nextValueInCurrentColumn = nextValueColumn.get${type.dataType?cap_first}(index);
}

@Override
void updateNextValueInCurrentColumn() {
this.nextValueInCurrentColumn = this.nextValue;
}
}
</#list>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.calc.execution.operator.process;

import org.apache.iotdb.calc.execution.operator.CommonOperatorContext;
import org.apache.iotdb.calc.execution.operator.Operator;
import org.apache.iotdb.calc.execution.operator.process.fill.ILinearFill;

import org.apache.tsfile.block.column.Column;
import org.apache.tsfile.read.common.block.TsBlock;

import static com.google.common.base.Preconditions.checkArgument;

public class TableNextFillOperator extends AbstractLinearFillOperator {

// start from 0; -1 means plain NEXT has no helper column.
private final int helperColumnIndex;

private final boolean hasTimeBound;

public TableNextFillOperator(
CommonOperatorContext operatorContext,
ILinearFill[] fillArray,
Operator child,
int helperColumnIndex,
boolean hasTimeBound) {
super(operatorContext, fillArray, child);
checkArgument(
!hasTimeBound || helperColumnIndex != -1,
"helperColumnIndex should be resolved when timeBound exists");
this.helperColumnIndex = helperColumnIndex;
this.hasTimeBound = hasTimeBound;
}

@Override
protected Column getHelperColumn(TsBlock tsBlock) {
return helperColumnIndex == -1 ? tsBlock.getTimeColumn() : tsBlock.getColumn(helperColumnIndex);
}

@Override
protected Integer getLastRowIndexForNonNullHelperColumn(TsBlock tsBlock) {
Comment thread
JackieTien97 marked this conversation as resolved.
int size = tsBlock.getPositionCount();
if (!hasTimeBound) {
return size - 1;
}
Column helperColumn = getHelperColumn(tsBlock);
if (!helperColumn.mayHaveNull()) {
return size - 1;
} else {
int i = size - 1;
for (; i >= 0; i--) {
if (!helperColumn.isNull(i)) {
break;
}
}
return i;
}
}

@Override
public long ramBytesUsed() {
return super.ramBytesUsed() + Integer.BYTES;
}
}
Loading
Loading