Skip to content

Commit fda8031

Browse files
authored
[To dev/1.3] Fix partial insert handling for null measurements (#17879) (#17922)
* Fix partial insert handling for null measurements (#17879) * Fix partial insert handling for null measurements * Fix partial insert handling for missing columns * Fix table insert schema validation for partial insert * Fix partial insert null column handling * Fix failed measurement WAL serialization tests * Fix all-failed tablet test column reuse (cherry picked from commit feaeaa6) * spotless * Fix ITs * Handle null values in type inference * Share WAL measurement schema serialization
1 parent 0ce224e commit fda8031

33 files changed

Lines changed: 1643 additions & 304 deletions

‎integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBSessionSimpleIT.java‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,13 +1185,14 @@ public void insertRecordsWithDuplicatedMeasurementsTest() {
11851185
public void insertRecordsWithExpiredDataTest()
11861186
throws IoTDBConnectionException, StatementExecutionException {
11871187
try (ISession session = EnvFactory.getEnv().getSessionConnection()) {
1188+
String device = "root.sg.expiredData.d1";
11881189
List<Long> times = new ArrayList<>();
11891190
List<List<String>> measurements = new ArrayList<>();
11901191
List<List<TSDataType>> datatypes = new ArrayList<>();
11911192
List<List<Object>> values = new ArrayList<>();
11921193
List<String> devices = new ArrayList<>();
11931194

1194-
devices.add("root.sg.d1");
1195+
devices.add(device);
11951196
addLine(
11961197
times,
11971198
measurements,
@@ -1204,14 +1205,14 @@ public void insertRecordsWithExpiredDataTest()
12041205
TSDataType.INT32,
12051206
1,
12061207
2);
1207-
session.executeNonQueryStatement("set ttl to root.sg.d1 1");
1208-
try {
1209-
session.insertRecords(devices, times, measurements, datatypes, values);
1210-
} catch (Exception e) {
1211-
assertTrue(e.getMessage().contains("less than ttl time bound"));
1212-
}
1213-
session.executeNonQueryStatement("unset ttl to root.sg.d1");
1214-
SessionDataSet dataSet = session.executeQueryStatement("select * from root.sg.d1");
1208+
session.executeNonQueryStatement("set ttl to " + device + " 1");
1209+
StatementExecutionException exception =
1210+
assertThrows(
1211+
StatementExecutionException.class,
1212+
() -> session.insertRecords(devices, times, measurements, datatypes, values));
1213+
assertTrue(exception.getMessage().contains("less than ttl time bound"));
1214+
session.executeNonQueryStatement("unset ttl to " + device);
1215+
SessionDataSet dataSet = session.executeQueryStatement("select * from " + device);
12151216
assertFalse(dataSet.hasNext());
12161217
}
12171218
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tablet/TabletInsertionDataContainer.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,11 @@ private void generateColumnIndexMapper(
385385
// case 1: for example, pattern is root.a.b or pattern is null and device is root.a.b.c
386386
// in this case, all data can be matched without checking the measurements
387387
if (Objects.isNull(pattern) || pattern.isRoot() || pattern.coversDevice(deviceId)) {
388+
int filteredCount = 0;
388389
for (int i = 0; i < originColumnSize; i++) {
389-
originColumnIndex2FilteredColumnIndexMapperList[i] = i;
390+
if (originMeasurementList[i] != null) {
391+
originColumnIndex2FilteredColumnIndexMapperList[i] = filteredCount++;
392+
}
390393
}
391394
}
392395

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/transform/statement/PipeConvertedInsertRowStatement.java‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,13 @@ protected boolean checkAndCastDataType(int columnIndex, TSDataType dataType) {
100100

101101
@Override
102102
public void transferType(ZoneId zoneId) throws QueryProcessException {
103+
if (measurementSchemas == null) {
104+
return;
105+
}
103106
for (int i = 0; i < measurementSchemas.length; i++) {
107+
if (!isColumnPresent(i) || dataTypes == null || i >= dataTypes.length) {
108+
continue;
109+
}
104110
// null when time series doesn't exist
105111
if (measurementSchemas[i] == null) {
106112
if (!IoTDBDescriptor.getInstance().getConfig().isEnablePartialInsert()) {
@@ -119,6 +125,9 @@ public void transferType(ZoneId zoneId) throws QueryProcessException {
119125

120126
// parse string value to specific type
121127
dataTypes[i] = measurementSchemas[i].getType();
128+
if (values == null || i >= values.length || values[i] == null) {
129+
continue;
130+
}
122131
try {
123132
values[i] = ValueConverter.parse(values[i].toString(), dataTypes[i]);
124133
} catch (Exception e) {

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/InsertNodeMemoryEstimator.java‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@ private static long sizeOfBitMap(final BitMap bitMaps) {
510510

511511
public static long sizeOfColumns(
512512
final Object[] columns, final MeasurementSchema[] measurementSchemas) {
513+
if (Objects.isNull(columns)) {
514+
return 0L;
515+
}
513516
// Directly calculate if measurementSchemas are absent
514517
if (Objects.isNull(measurementSchemas)) {
515518
return RamUsageEstimator.shallowSizeOf(columns)
@@ -521,7 +524,10 @@ public static long sizeOfColumns(
521524
RamUsageEstimator.alignObjectSize(
522525
NUM_BYTES_ARRAY_HEADER + NUM_BYTES_OBJECT_REF * columns.length);
523526
for (int i = 0; i < columns.length; i++) {
524-
if (measurementSchemas[i] == null || measurementSchemas[i].getType() == null) {
527+
if (columns[i] == null
528+
|| i >= measurementSchemas.length
529+
|| measurementSchemas[i] == null
530+
|| measurementSchemas[i].getType() == null) {
525531
continue;
526532
}
527533
switch (measurementSchemas[i].getType()) {
@@ -585,6 +591,9 @@ private static long getBinarySize(final Binary[] binaries) {
585591

586592
public static long sizeOfValues(
587593
final Object[] values, final MeasurementSchema[] measurementSchemas) {
594+
if (Objects.isNull(values)) {
595+
return 0L;
596+
}
588597
// Directly calculate if measurementSchemas are absent
589598
if (Objects.isNull(measurementSchemas)) {
590599
return RamUsageEstimator.shallowSizeOf(values)
@@ -596,7 +605,9 @@ public static long sizeOfValues(
596605
RamUsageEstimator.alignObjectSize(
597606
NUM_BYTES_ARRAY_HEADER + NUM_BYTES_OBJECT_REF * values.length);
598607
for (int i = 0; i < values.length; i++) {
599-
if (measurementSchemas[i] == null || measurementSchemas[i].getType() == null) {
608+
if (i >= measurementSchemas.length
609+
|| measurementSchemas[i] == null
610+
|| measurementSchemas[i].getType() == null) {
600611
size += NUM_BYTES_OBJECT_HEADER;
601612
continue;
602613
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/DataNodeSchemaCache.java‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,24 @@ public ClusterSchemaTree getMatchedNormalSchema(final PartialPath fullPath) {
166166
public List<Integer> computeWithoutTemplate(final ISchemaComputation schemaComputation) {
167167
final List<Integer> indexOfMissingMeasurements = new ArrayList<>();
168168
final String[] measurements = schemaComputation.getMeasurements();
169+
if (measurements == null) {
170+
return indexOfMissingMeasurements;
171+
}
169172

170173
final IDeviceSchema schema =
171174
deviceSchemaCache.getDeviceSchema(schemaComputation.getDevicePath());
172175
if (!(schema instanceof DeviceNormalSchema)) {
173-
return IntStream.range(0, schemaComputation.getMeasurements().length)
176+
return IntStream.range(0, measurements.length)
177+
.filter(i -> measurements[i] != null)
174178
.boxed()
175179
.collect(Collectors.toList());
176180
}
177181
final DeviceNormalSchema treeSchema = (DeviceNormalSchema) schema;
178182

179-
for (int i = 0; i < schemaComputation.getMeasurements().length; i++) {
183+
for (int i = 0; i < measurements.length; i++) {
184+
if (measurements[i] == null) {
185+
continue;
186+
}
180187
final SchemaCacheEntry value = treeSchema.getSchemaCacheEntry(measurements[i]);
181188
if (value == null) {
182189
indexOfMissingMeasurements.add(i);

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/NormalSchemaFetcher.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,9 @@ void processNormalTimeSeries(
337337
schemaComputationWithAutoCreationList.stream()
338338
.map(
339339
o -> {
340-
TSDataType[] dataTypes = new TSDataType[o.getMeasurements().length];
340+
final String[] measurements = o.getMeasurements();
341+
TSDataType[] dataTypes =
342+
new TSDataType[measurements == null ? 0 : measurements.length];
341343
for (int i = 0, length = dataTypes.length; i < length; i++) {
342344
dataTypes[i] = o.getDataType(i);
343345
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertNode.java‎

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public TSDataType[] getDataTypes() {
168168
}
169169

170170
public TSDataType getDataType(int index) {
171-
return dataTypes[index];
171+
return dataTypes == null || index < 0 || index >= dataTypes.length ? null : dataTypes[index];
172172
}
173173

174174
public void setDataTypes(TSDataType[] dataTypes) {
@@ -318,24 +318,20 @@ private static Map<String, String> deserializeProps(ByteBuffer buffer) {
318318
/** Serialized size of measurement schemas, ignoring failed time series */
319319
protected int serializeMeasurementSchemasSize() {
320320
int byteLen = 0;
321-
for (int i = 0; i < measurements.length; i++) {
322-
// ignore failed partial insert
323-
if (measurements[i] == null) {
324-
continue;
321+
for (int i = 0; measurements != null && i < measurements.length; i++) {
322+
if (shouldSerializeMeasurementToWAL(i)) {
323+
byteLen += WALWriteUtils.sizeToWrite(measurementSchemas[i]);
325324
}
326-
byteLen += WALWriteUtils.sizeToWrite(measurementSchemas[i]);
327325
}
328326
return byteLen;
329327
}
330328

331329
/** Serialize measurement schemas, ignoring failed time series */
332330
protected void serializeMeasurementSchemasToWAL(IWALByteBufferView buffer) {
333-
for (int i = 0; i < measurements.length; i++) {
334-
// ignore failed partial insert
335-
if (measurements[i] == null) {
336-
continue;
331+
for (int i = 0; measurements != null && i < measurements.length; i++) {
332+
if (shouldSerializeMeasurementToWAL(i)) {
333+
WALWriteUtils.write(measurementSchemas[i], buffer);
337334
}
338-
WALWriteUtils.write(measurementSchemas[i], buffer);
339335
}
340336
}
341337

@@ -373,8 +369,8 @@ public void markFailedMeasurement(int index) {
373369
}
374370

375371
public boolean hasValidMeasurements() {
376-
for (Object o : measurements) {
377-
if (o != null) {
372+
for (int i = 0; measurements != null && i < measurements.length; i++) {
373+
if (!isMeasurementFailed(i)) {
378374
return true;
379375
}
380376
}
@@ -389,11 +385,46 @@ public int getFailedMeasurementNumber() {
389385
return failedMeasurementNumber;
390386
}
391387

392-
public boolean allMeasurementFailed() {
393-
if (measurements != null) {
394-
return failedMeasurementNumber >= measurements.length;
388+
protected int getValidMeasurementNumber() {
389+
int validMeasurementNumber = 0;
390+
for (int i = 0; measurements != null && i < measurements.length; i++) {
391+
if (shouldSerializeMeasurement(i)) {
392+
validMeasurementNumber++;
393+
}
395394
}
396-
return true;
395+
return validMeasurementNumber;
396+
}
397+
398+
protected int getValidMeasurementNumberForWAL() {
399+
int validMeasurementNumber = 0;
400+
for (int i = 0; measurements != null && i < measurements.length; i++) {
401+
if (shouldSerializeMeasurementToWAL(i)) {
402+
validMeasurementNumber++;
403+
}
404+
}
405+
return validMeasurementNumber;
406+
}
407+
408+
protected boolean shouldSerializeMeasurement(final int index) {
409+
return !isMeasurementFailed(index);
410+
}
411+
412+
protected boolean shouldSerializeMeasurementToWAL(final int index) {
413+
return shouldSerializeMeasurement(index)
414+
&& measurementSchemas != null
415+
&& index < measurementSchemas.length
416+
&& measurementSchemas[index] != null;
417+
}
418+
419+
public boolean isMeasurementFailed(int index) {
420+
return measurements == null
421+
|| index < 0
422+
|| index >= measurements.length
423+
|| measurements[index] == null;
424+
}
425+
426+
public boolean allMeasurementFailed() {
427+
return measurements == null || !hasValidMeasurements();
397428
}
398429

399430
public String[] getRawMeasurements() {

0 commit comments

Comments
 (0)