This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/dev/1.3 by this push:
new b2950a3192e [Load] Preserve invalid device path failures during tablet
conversion (#18614) (#18618)
b2950a3192e is described below
commit b2950a3192e93bdcc6badf555f4c48bcc2c832c7
Author: Caideyipi <[email protected]>
AuthorDate: Fri Sep 11 09:30:21 2026 +0800
[Load] Preserve invalid device path failures during tablet conversion
(#18614) (#18618)
(cherry picked from commit 6a3c8d667f0552966f4ccf7e660c5ea356e8985d)
---
.../exception/LoadAnalyzeInvalidPathException.java | 28 +++
.../plan/analyze/load/LoadTsFileAnalyzer.java | 28 +--
.../db/storageengine/load/LoadTsFilePathUtils.java | 47 +++++
.../converter/LoadTreeTsFileTabletIterator.java | 26 ++-
.../plan/analyze/load/LoadTsFileAnalyzerTest.java | 11 +-
.../load/converter/LoadTsFileInvalidPathTest.java | 205 +++++++++++++++++++++
6 files changed, 328 insertions(+), 17 deletions(-)
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/LoadAnalyzeInvalidPathException.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/LoadAnalyzeInvalidPathException.java
new file mode 100644
index 00000000000..ed818200870
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/LoadAnalyzeInvalidPathException.java
@@ -0,0 +1,28 @@
+/*
+ * 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.exception;
+
+/** An invalid tree path cannot be repaired by converting the TsFile to
tablets. */
+public class LoadAnalyzeInvalidPathException extends LoadAnalyzeException {
+
+ public LoadAnalyzeInvalidPathException(String message) {
+ super(message);
+ }
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
index 1715fe4bb27..3e25e033626 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzer.java
@@ -37,6 +37,7 @@ import org.apache.iotdb.db.auth.AuthorityChecker;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.exception.LoadAnalyzeException;
+import org.apache.iotdb.db.exception.LoadAnalyzeInvalidPathException;
import org.apache.iotdb.db.exception.LoadAnalyzeMissingSchemaException;
import org.apache.iotdb.db.exception.LoadAnalyzeTypeMismatchException;
import org.apache.iotdb.db.exception.load.LoadEmptyFileException;
@@ -111,6 +112,7 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import static
org.apache.iotdb.db.storageengine.load.LoadTsFilePathUtils.getValidatedDevicePath;
import static
org.apache.iotdb.db.storageengine.load.metrics.LoadTsFileCostMetricsSet.ANALYSIS;
import static
org.apache.iotdb.db.storageengine.load.metrics.LoadTsFileCostMetricsSet.ANALYSIS_ASYNC_MOVE;
@@ -611,8 +613,9 @@ public class LoadTsFileAnalyzer implements AutoCloseable {
}
private boolean shouldSkipConversion(LoadAnalyzeException e) {
- return (e instanceof LoadAnalyzeTypeMismatchException)
- && !loadTsFileStatement.isConvertOnTypeMismatch();
+ return e instanceof LoadAnalyzeInvalidPathException
+ || (e instanceof LoadAnalyzeTypeMismatchException)
+ && !loadTsFileStatement.isConvertOnTypeMismatch();
}
@Override
@@ -643,6 +646,8 @@ public class LoadTsFileAnalyzer implements AutoCloseable {
device2TimeSeriesMetadataList.entrySet()) {
final IDeviceID device = entry.getKey();
+ getValidatedDevicePath(device);
+
try {
if (schemaCache.isDeviceDeletedByMods(device)) {
continue;
@@ -706,11 +711,13 @@ public class LoadTsFileAnalyzer implements AutoCloseable {
public void checkWritePermission(
Map<IDeviceID, List<TimeseriesMetadata>> device2TimeseriesMetadataList)
- throws AuthException {
+ throws AuthException, LoadAnalyzeInvalidPathException {
for (final Map.Entry<IDeviceID, List<TimeseriesMetadata>> entry :
device2TimeseriesMetadataList.entrySet()) {
final IDeviceID device = entry.getKey();
+ getValidatedDevicePath(device);
+
try {
if (schemaCache.isDeviceDeletedByMods(device)) {
continue;
@@ -813,7 +820,9 @@ public class LoadTsFileAnalyzer implements AutoCloseable {
if (isVerifySchema) {
verifySchema(schemaTree);
}
- } catch (AuthException | LoadAnalyzeTypeMismatchException e) {
+ } catch (AuthException
+ | LoadAnalyzeInvalidPathException
+ | LoadAnalyzeTypeMismatchException e) {
throw e;
} catch (LoadAnalyzeMissingSchemaException e) {
if (isTemporaryUnavailableDueToPipeSchemaNotReady(e)) {
@@ -858,18 +867,9 @@ public class LoadTsFileAnalyzer implements AutoCloseable {
final Set<PartialPath> databasesNeededToBeSet = new HashSet<>();
for (final IDeviceID device :
schemaCache.getDevice2TimeSeries().keySet()) {
- final PartialPath devicePath;
- try {
- devicePath = new PartialPath(device);
- } catch (final IllegalPathException e) {
- throw new LoadAnalyzeException(e.getMessage());
- }
+ final PartialPath devicePath = getValidatedDevicePath(device);
final String[] devicePrefixNodes = devicePath.getNodes();
- if (hasEmptyPathNode(devicePath)) {
- throw new LoadAnalyzeException(
- new IllegalPathException(devicePath.getFullPath()).getMessage());
- }
if (devicePrefixNodes.length < databasePrefixNodesLength) {
throw new LoadAnalyzeException(
String.format(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePathUtils.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePathUtils.java
new file mode 100644
index 00000000000..8d83746ce67
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePathUtils.java
@@ -0,0 +1,47 @@
+/*
+ * 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.storageengine.load;
+
+import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.exception.LoadAnalyzeInvalidPathException;
+
+import org.apache.tsfile.file.metadata.IDeviceID;
+
+public class LoadTsFilePathUtils {
+
+ private LoadTsFilePathUtils() {}
+
+ public static PartialPath getValidatedDevicePath(final IDeviceID device)
+ throws LoadAnalyzeInvalidPathException {
+ try {
+ final PartialPath devicePath = new PartialPath(device);
+ // Validate the original nodes before converting to a string, which
loses null nodes.
+ for (final String node : devicePath.getNodes()) {
+ if (node == null || node.isEmpty()) {
+ throw new IllegalPathException(devicePath.getFullPath());
+ }
+ }
+ return devicePath;
+ } catch (final IllegalPathException e) {
+ throw new LoadAnalyzeInvalidPathException(e.getMessage());
+ }
+ }
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/converter/LoadTreeTsFileTabletIterator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/converter/LoadTreeTsFileTabletIterator.java
index 41c88ce4752..8c7e260aa68 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/converter/LoadTreeTsFileTabletIterator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/converter/LoadTreeTsFileTabletIterator.java
@@ -19,9 +19,11 @@
package org.apache.iotdb.db.storageengine.load.converter;
+import org.apache.iotdb.commons.exception.IllegalPathException;
import
org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
import org.apache.iotdb.commons.pipe.datastructure.pattern.IoTDBPipePattern;
import org.apache.iotdb.commons.pipe.datastructure.pattern.PipePattern;
+import org.apache.iotdb.db.exception.LoadAnalyzeInvalidPathException;
import org.apache.iotdb.db.exception.load.LoadRuntimeOutOfMemoryException;
import
org.apache.iotdb.db.pipe.event.common.tablet.PipeRawTabletInsertionEvent;
import
org.apache.iotdb.db.pipe.event.common.tsfile.container.query.TsFileInsertionQueryDataContainer;
@@ -29,6 +31,7 @@ import
org.apache.iotdb.db.pipe.event.common.tsfile.container.scan.TsFileInserti
import
org.apache.iotdb.db.storageengine.load.memory.LoadTsFileParserMemoryManager;
import org.apache.iotdb.pipe.api.event.dml.insertion.TabletInsertionEvent;
+import org.apache.tsfile.exception.PathParseException;
import org.apache.tsfile.file.metadata.IDeviceID;
import org.apache.tsfile.file.metadata.PlainDeviceID;
import org.apache.tsfile.file.metadata.TimeseriesMetadata;
@@ -54,6 +57,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
+import static
org.apache.iotdb.db.storageengine.load.LoadTsFilePathUtils.getValidatedDevicePath;
+
/**
* Load uses scan parsing first for throughput. If scan parsing hits
corruption, fall back to query
* parsing for the remaining measurements and devices so later data can still
be loaded.
@@ -63,7 +68,18 @@ class LoadTreeTsFileTabletIterator
private static final Logger LOGGER =
LoggerFactory.getLogger(LoadTreeTsFileTabletIterator.class);
- private static final PipePattern LOAD_TREE_PATTERN = new
IoTDBPipePattern(null);
+ private static final PipePattern LOAD_TREE_PATTERN =
+ new IoTDBPipePattern(null) {
+ @Override
+ public boolean mayOverlapWithDevice(final String device) {
+ try {
+ getValidatedDevicePath(new PlainDeviceID(device));
+ } catch (final LoadAnalyzeInvalidPathException e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ }
+ return super.mayOverlapWithDevice(device);
+ }
+ };
private final File file;
private final boolean isWithMod;
@@ -333,6 +349,7 @@ class LoadTreeTsFileTabletIterator
while (!pendingQueryTasks.isEmpty()) {
activeQueryTask = pendingQueryTasks.removeFirst();
try {
+ getValidatedDevicePath(activeQueryTask.device);
activeQueryParser =
new TsFileInsertionQueryDataContainer(
file,
@@ -412,6 +429,10 @@ class LoadTreeTsFileTabletIterator
Throwable current = e;
while (Objects.nonNull(current)) {
if (current instanceof InterruptedException
+ // Invalid paths cannot be recovered by query parsing or splitting
measurements.
+ || current instanceof PathParseException
+ || current instanceof IllegalPathException
+ || current instanceof LoadAnalyzeInvalidPathException
|| current instanceof PipeRuntimeOutOfMemoryCriticalException
|| current instanceof LoadRuntimeOutOfMemoryException) {
return true;
@@ -422,6 +443,9 @@ class LoadTreeTsFileTabletIterator
}
private RuntimeException toRuntimeException(final Exception e) {
+ if (e instanceof LoadAnalyzeInvalidPathException) {
+ return new IllegalArgumentException(e.getMessage(), e);
+ }
return e instanceof RuntimeException
? (RuntimeException) e
: new IllegalStateException("Failed to iterate tablets while loading
TsFile.", e);
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
index 589542c8023..0f3e2b19134 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
@@ -21,7 +21,7 @@ package org.apache.iotdb.db.queryengine.plan.analyze.load;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.exception.LoadAnalyzeException;
+import org.apache.iotdb.db.exception.LoadAnalyzeInvalidPathException;
import org.apache.iotdb.db.exception.LoadAnalyzeMissingSchemaException;
import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
import org.apache.iotdb.db.queryengine.common.QueryId;
@@ -65,7 +65,7 @@ public class LoadTsFileAnalyzerTest {
() -> getAutoCreateDatabaseMethod(verifier).invoke(verifier));
Assert.assertTrue(
String.valueOf(exception.getCause()),
- exception.getCause() instanceof LoadAnalyzeException);
+ exception.getCause() instanceof LoadAnalyzeInvalidPathException);
} finally {
Assert.assertTrue(tsFile.delete());
}
@@ -97,6 +97,13 @@ public class LoadTsFileAnalyzerTest {
Collections.singleton(databaseWithSameStringPrefix),
databasesNeededToBeSet);
Assert.assertEquals(
Collections.singleton(database),
getAlreadySetDatabases(getSchemaCache(verifier)));
+
+ addTimeSeries(
+ getSchemaCache(verifier),
+ new PlainDeviceID("root.sg.d1"),
+ new MeasurementSchema("s1", TSDataType.INT32));
+ // A valid device still uses its existing database despite the legacy
root. entry.
+ getAutoCreateDatabaseMethod(verifier).invoke(verifier);
} finally {
Assert.assertTrue(tsFile.delete());
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/converter/LoadTsFileInvalidPathTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/converter/LoadTsFileInvalidPathTest.java
new file mode 100644
index 00000000000..2775a432ba6
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/converter/LoadTsFileInvalidPathTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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.storageengine.load.converter;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
+import org.apache.iotdb.db.queryengine.common.QueryId;
+import org.apache.iotdb.db.queryengine.common.SessionInfo;
+import org.apache.iotdb.db.queryengine.plan.analyze.Analysis;
+import org.apache.iotdb.db.queryengine.plan.analyze.IPartitionFetcher;
+import org.apache.iotdb.db.queryengine.plan.analyze.load.LoadTsFileAnalyzer;
+import org.apache.iotdb.db.queryengine.plan.analyze.schema.ISchemaFetcher;
+import org.apache.iotdb.db.queryengine.plan.statement.crud.LoadTsFileStatement;
+import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
+import org.apache.iotdb.db.storageengine.load.memory.LoadTsFileMemoryManager;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.file.metadata.IDeviceID;
+import org.apache.tsfile.file.metadata.PlainDeviceID;
+import org.apache.tsfile.write.chunk.AlignedChunkWriterImpl;
+import org.apache.tsfile.write.schema.MeasurementSchema;
+import org.apache.tsfile.write.writer.TsFileIOWriter;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Optional;
+
+import static org.mockito.Mockito.mock;
+
+public class LoadTsFileInvalidPathTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private int dataNodeId;
+
+ @Before
+ public void setUp() {
+ dataNodeId = IoTDBDescriptor.getInstance().getConfig().getDataNodeId();
+ IoTDBDescriptor.getInstance().getConfig().setDataNodeId(0);
+ }
+
+ @After
+ public void tearDown() {
+ IoTDBDescriptor.getInstance().getConfig().setDataNodeId(dataNodeId);
+ }
+
+ @Test
+ public void testAnalysisRejectsEmptyDeviceNodeWithAndWithoutResource()
throws Exception {
+ final IDeviceID device = new PlainDeviceID("root.");
+ final File file = writeTsFile(device);
+ assertAnalysisRejectsInvalidPath(file, device, true, true, -1);
+
+ final TsFileResource resource = new TsFileResource(file);
+ resource.updateStartTime(device, 1);
+ resource.updateEndTime(device, 1);
+ resource.serialize();
+ Assert.assertTrue(resource.resourceFileExists());
+ assertAnalysisRejectsInvalidPath(file, device, true, true, -1);
+ }
+
+ @Test
+ public void testAnalysisRejectsEmptyInnerDeviceNode() throws Exception {
+ final IDeviceID device = new PlainDeviceID("root.sg..d1");
+ assertAnalysisRejectsInvalidPath(writeTsFile(device), device, true, true,
-1);
+ }
+
+ @Test
+ public void testAnalysisRejectsInvalidDeviceWithoutAutoCreateDatabase()
throws Exception {
+ final IDeviceID device = new PlainDeviceID("root.");
+ assertAnalysisRejectsInvalidPath(writeTsFile(device), device, false, true,
-1);
+ }
+
+ @Test
+ public void testAnalysisRejectsInvalidDeviceWithoutSchemaChecks() throws
Exception {
+ final IDeviceID device = new PlainDeviceID("root.");
+ assertAnalysisRejectsInvalidPath(writeTsFile(device), device, false,
false, -1);
+ }
+
+ @Test
+ public void testMiniFileAnalysisRejectsInvalidDevice() throws Exception {
+ final IDeviceID device = new PlainDeviceID("root.");
+ assertAnalysisRejectsInvalidPath(writeTsFile(device), device, true, true,
Long.MAX_VALUE);
+ }
+
+ @Test
+ public void testConversionReportsInvalidPathAndReleasesMemory() throws
Exception {
+ assertConversionRejectsInvalidPath(new PlainDeviceID("root."));
+ }
+
+ @Test
+ public void testConversionRejectsEmptyInnerDeviceNode() throws Exception {
+ assertConversionRejectsInvalidPath(new PlainDeviceID("root.sg..d1"));
+ }
+
+ private void assertConversionRejectsInvalidPath(final IDeviceID device)
throws Exception {
+ final File file = writeTsFile(device);
+ final long memoryBefore =
LoadTsFileMemoryManager.getInstance().getUsedMemorySizeInBytes();
+ final LoadTreeStatementDataTypeConvertExecutionVisitor visitor =
+ new LoadTreeStatementDataTypeConvertExecutionVisitor(
+ statement -> {
+ Assert.fail("An invalid device must not be inserted.");
+ return null;
+ });
+
+ final Optional<TSStatus> status =
+
visitor.visitLoadFile(LoadTsFileStatement.createUnchecked(file.getAbsolutePath()),
null);
+
+ Assert.assertTrue(status.isPresent());
+ Assert.assertEquals(TSStatusCode.LOAD_FILE_ERROR.getStatusCode(),
status.get().getCode());
+ Assert.assertNotNull(status.get().getMessage());
+ Assert.assertEquals(
+ new IllegalPathException(((PlainDeviceID)
device).toStringID()).getMessage(),
+ status.get().getMessage());
+ Assert.assertEquals(
+ memoryBefore,
LoadTsFileMemoryManager.getInstance().getUsedMemorySizeInBytes());
+ }
+
+ private void assertAnalysisRejectsInvalidPath(
+ final File file,
+ final IDeviceID device,
+ final boolean autoCreateDatabase,
+ final boolean checkSchema,
+ final long conversionThreshold)
+ throws Exception {
+ final LoadTsFileStatement statement =
+ LoadTsFileStatement.createUnchecked(file.getAbsolutePath());
+ statement.setAutoCreateDatabase(autoCreateDatabase);
+ statement.setConvertOnTypeMismatch(true);
+ statement.setAutoCreateSchema(checkSchema);
+ statement.setVerifySchema(checkSchema);
+ statement.setTabletConversionThresholdBytes(conversionThreshold);
+ final MPPQueryContext context =
+ new MPPQueryContext(
+ "",
+ new QueryId("load_invalid_path_test"),
+ new SessionInfo(0, "root", ZoneId.systemDefault(), ""),
+ null,
+ null);
+
+ try (final LoadTsFileAnalyzer analyzer =
+ new LoadTsFileAnalyzer(
+ statement, context, mock(IPartitionFetcher.class),
mock(ISchemaFetcher.class))) {
+ final Analysis analysis = new Analysis();
+ analyzer.analyzeFileByFile(analysis);
+ Assert.assertTrue(analysis.isFinishQueryAfterAnalyze());
+ Assert.assertNotNull(analysis.getFailStatus());
+ Assert.assertEquals(
+ TSStatusCode.LOAD_FILE_ERROR.getStatusCode(),
analysis.getFailStatus().getCode());
+ Assert.assertEquals(
+ String.format(
+ "Loading file %s failed. Detail: %s",
+ file.getAbsolutePath(),
+ new IllegalPathException(((PlainDeviceID)
device).toStringID()).getMessage()),
+ analysis.getFailStatus().getMessage());
+ Assert.assertTrue(file.exists());
+ }
+ }
+
+ private File writeTsFile(final IDeviceID device) throws Exception {
+ final File file = new File(temporaryFolder.getRoot(), "1-1-0-0.tsfile");
+ // Use the low-level writer to preserve legacy empty device nodes in the
file.
+ try (final TsFileIOWriter writer = new TsFileIOWriter(file)) {
+ writer.startChunkGroup(device);
+ final AlignedChunkWriterImpl chunkWriter =
+ new AlignedChunkWriterImpl(
+ Arrays.asList(
+ new MeasurementSchema("quality", TSDataType.INT32),
+ new MeasurementSchema("value", TSDataType.DOUBLE)));
+ chunkWriter.getTimeChunkWriter().write(1);
+ chunkWriter.getValueChunkWriterByIndex(0).write(1, 1, false);
+ chunkWriter.getValueChunkWriterByIndex(1).write(1, 1.0, false);
+ chunkWriter.writeToFileWriter(writer);
+ writer.endChunkGroup();
+ writer.endFile();
+ }
+ return file;
+ }
+}