Skip to content

Commit 0288aab

Browse files
authored
Optimize InsertTabletStatement tablet conversion (#17805) (#17842)
(cherry picked from commit dacbf92)
1 parent 251125a commit 0288aab

1 file changed

Lines changed: 163 additions & 0 deletions

File tree

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertTabletStatement.java

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,169 @@ protected long calculateBytesUsed() {
492492
+ InsertNodeMemoryEstimator.sizeOfColumns(columns, measurementSchemas);
493493
}
494494

495+
/**
496+
* Convert this InsertTabletStatement to Tablet. This method constructs a Tablet object from this
497+
* statement, converting all necessary fields. All arrays are copied to rowSize length to ensure
498+
* immutability.
499+
*
500+
* @return Tablet object
501+
* @throws MetadataException if conversion fails
502+
*/
503+
public Tablet convertToTablet() throws MetadataException {
504+
try {
505+
// Get deviceId/tableName from devicePath
506+
final String deviceIdOrTableName =
507+
this.getDevicePath() != null ? this.getDevicePath().getFullPath() : "";
508+
509+
// Get schemas from measurementSchemas
510+
final MeasurementSchema[] measurementSchemas = this.getMeasurementSchemas();
511+
final String[] measurements = this.getMeasurements();
512+
final TSDataType[] dataTypes = this.getDataTypes();
513+
// If measurements and dataTypes are not null, use measurements.length as the standard length
514+
final int originalSchemaSize = measurements != null ? measurements.length : 0;
515+
516+
// Build schemas and track valid column indices (skip null columns)
517+
// measurements and dataTypes being null is standard - skip those columns
518+
final List<MeasurementSchema> schemas = new ArrayList<>(originalSchemaSize);
519+
final int[] validColumnIndices = new int[originalSchemaSize];
520+
int validColumnCount = 0;
521+
if (dataTypes != null) {
522+
final int dataTypeSize = Math.min(originalSchemaSize, dataTypes.length);
523+
for (int i = 0; i < dataTypeSize; i++) {
524+
if (measurements[i] != null && dataTypes[i] != null) {
525+
final MeasurementSchema measurementSchema =
526+
measurementSchemas != null && i < measurementSchemas.length
527+
? measurementSchemas[i]
528+
: null;
529+
schemas.add(
530+
measurementSchema != null
531+
&& Objects.equals(measurementSchema.getMeasurementId(), measurements[i])
532+
&& measurementSchema.getType() == dataTypes[i]
533+
? measurementSchema
534+
: new MeasurementSchema(measurements[i], dataTypes[i]));
535+
validColumnIndices[validColumnCount++] = i;
536+
}
537+
}
538+
}
539+
540+
final int schemaSize = validColumnCount;
541+
542+
// Get timestamps - always copy to ensure immutability
543+
final long[] times = this.getTimes();
544+
final int rowSize = this.getRowCount();
545+
final long[] timestamps;
546+
if (rowSize == 0) {
547+
timestamps = new long[0];
548+
} else if (times != null && times.length >= rowSize) {
549+
timestamps = Arrays.copyOf(times, rowSize);
550+
} else {
551+
LOGGER.warn(
552+
"Times array is null or too small. times.length={}, rowSize={}, deviceId={}",
553+
times != null ? times.length : 0,
554+
rowSize,
555+
deviceIdOrTableName);
556+
timestamps = new long[0];
557+
}
558+
559+
// Get values - convert Statement columns to Tablet format, only for valid columns
560+
// All arrays are copied to rowSize length
561+
final Object[] statementColumns = this.getColumns();
562+
final Object[] tabletValues = new Object[schemaSize];
563+
if (statementColumns != null && statementColumns.length > 0) {
564+
for (int i = 0; i < schemaSize; i++) {
565+
final int originalIndex = validColumnIndices[i];
566+
if (originalIndex < statementColumns.length
567+
&& statementColumns[originalIndex] != null
568+
&& dataTypes[originalIndex] != null) {
569+
tabletValues[i] =
570+
convertColumnToTablet(
571+
statementColumns[originalIndex], dataTypes[originalIndex], rowSize);
572+
} else {
573+
tabletValues[i] = null;
574+
}
575+
}
576+
}
577+
578+
// Get bitMaps - copy and truncate to rowSize, only for valid columns
579+
final BitMap[] originalBitMaps = this.getBitMaps();
580+
BitMap[] bitMaps = null;
581+
if (originalBitMaps != null && originalBitMaps.length > 0) {
582+
final BitMap[] copiedBitMaps = new BitMap[schemaSize];
583+
for (int i = 0; i < schemaSize; i++) {
584+
final int originalIndex = validColumnIndices[i];
585+
if (originalIndex < originalBitMaps.length && originalBitMaps[originalIndex] != null) {
586+
final BitMap originalBitMap = originalBitMaps[originalIndex];
587+
if (!originalBitMap.isAllUnmarked()) {
588+
copiedBitMaps[i] =
589+
originalBitMap.getRegion(0, Math.min(rowSize, originalBitMap.getSize()));
590+
}
591+
} else {
592+
copiedBitMaps[i] = null;
593+
}
594+
}
595+
bitMaps = BitMapUtils.compactBitMaps(copiedBitMaps, rowSize);
596+
}
597+
598+
return new Tablet(deviceIdOrTableName, schemas, timestamps, tabletValues, bitMaps, rowSize);
599+
} catch (final Exception e) {
600+
throw new MetadataException("Failed to convert InsertTabletStatement to Tablet: ", e);
601+
}
602+
}
603+
604+
/**
605+
* Convert a single column value from Statement format to Tablet format. Statement uses primitive
606+
* arrays (e.g., int[], long[], float[]), while Tablet may need different format. All arrays are
607+
* copied to rowSize length to ensure immutability - even if the original array is modified, the
608+
* converted array remains unchanged.
609+
*
610+
* @param columnValue column value from Statement (primitive array)
611+
* @param dataType data type of the column
612+
* @param rowSize number of rows to copy
613+
* @return column value in Tablet format (copied to rowSize)
614+
*/
615+
private Object convertColumnToTablet(
616+
final Object columnValue, final TSDataType dataType, final int rowSize) {
617+
618+
if (columnValue == null) {
619+
return null;
620+
}
621+
622+
if (TSDataType.DATE.equals(dataType)) {
623+
final int[] values = (int[]) columnValue;
624+
final LocalDate[] localDateValue = new LocalDate[rowSize];
625+
final int size = Math.min(values.length, rowSize);
626+
for (int i = 0; i < size; i++) {
627+
localDateValue[i] = DateUtils.parseIntToLocalDate(values[i]);
628+
}
629+
return localDateValue;
630+
}
631+
632+
// For primitive arrays, copy to rowSize
633+
if (columnValue instanceof boolean[]) {
634+
final boolean[] original = (boolean[]) columnValue;
635+
return Arrays.copyOf(original, rowSize);
636+
} else if (columnValue instanceof int[]) {
637+
final int[] original = (int[]) columnValue;
638+
return Arrays.copyOf(original, rowSize);
639+
} else if (columnValue instanceof long[]) {
640+
final long[] original = (long[]) columnValue;
641+
return Arrays.copyOf(original, rowSize);
642+
} else if (columnValue instanceof float[]) {
643+
final float[] original = (float[]) columnValue;
644+
return Arrays.copyOf(original, rowSize);
645+
} else if (columnValue instanceof double[]) {
646+
final double[] original = (double[]) columnValue;
647+
return Arrays.copyOf(original, rowSize);
648+
} else if (columnValue instanceof Binary[]) {
649+
// For Binary arrays, create a new array and copy references to rowSize
650+
final Binary[] original = (Binary[]) columnValue;
651+
return Arrays.copyOf(original, rowSize);
652+
}
653+
654+
// For other types, return as-is (should not happen for standard types)
655+
return columnValue;
656+
}
657+
495658
@Override
496659
public String toString() {
497660
final int size = CommonDescriptor.getInstance().getConfig().getPathLogMaxSize();

0 commit comments

Comments
 (0)