You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by qi...@apache.org on 2022/01/29 09:37:39 UTC

[iotdb] branch master updated: [IOTDB-2258] restructure the csv import tool (#4954)

This is an automated email from the ASF dual-hosted git repository.

qiaojialin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 18dea61  [IOTDB-2258] restructure the csv import tool (#4954)
18dea61 is described below

commit 18dea61ee9b7771b98ade5f8369d04b490548899
Author: Xuan Ronaldo <xu...@qq.com>
AuthorDate: Sat Jan 29 17:36:59 2022 +0800

    [IOTDB-2258] restructure the csv import tool (#4954)
---
 .../main/java/org/apache/iotdb/tool/ImportCsv.java | 478 +++++++++++----------
 .../tests/tools/importCsv/ImportCsvTestIT.java     |   1 -
 docs/UserGuide/Write-And-Delete-Data/CSV-Tool.md   |   3 +-
 .../zh/UserGuide/Write-And-Delete-Data/CSV-Tool.md |   3 +-
 4 files changed, 260 insertions(+), 225 deletions(-)

diff --git a/cli/src/main/java/org/apache/iotdb/tool/ImportCsv.java b/cli/src/main/java/org/apache/iotdb/tool/ImportCsv.java
index 9755463..e4eef04 100644
--- a/cli/src/main/java/org/apache/iotdb/tool/ImportCsv.java
+++ b/cli/src/main/java/org/apache/iotdb/tool/ImportCsv.java
@@ -44,17 +44,12 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.apache.iotdb.tsfile.file.metadata.enums.TSDataType.BOOLEAN;
 import static org.apache.iotdb.tsfile.file.metadata.enums.TSDataType.DOUBLE;
@@ -86,6 +81,8 @@ public class ImportCsv extends AbstractCsvTool {
   private static String timeColumn = "Time";
   private static String deviceColumn = "Device";
 
+  private static final int BATCH_SIZE = 10000;
+
   /**
    * create the commandline options.
    *
@@ -261,7 +258,7 @@ public class ImportCsv extends AbstractCsvTool {
       try {
         CSVParser csvRecords = readCsvFile(file.getAbsolutePath());
         List<String> headerNames = csvRecords.getHeaderNames();
-        List<CSVRecord> records = csvRecords.getRecords();
+        Stream<CSVRecord> records = csvRecords.stream();
         if (headerNames.isEmpty()) {
           System.out.println("Empty file!");
           return;
@@ -270,10 +267,6 @@ public class ImportCsv extends AbstractCsvTool {
           System.out.println("No headers!");
           return;
         }
-        if (records.isEmpty()) {
-          System.out.println("No records!");
-          return;
-        }
         String failedFilePath = null;
         if (failedFileDirectory == null) {
           failedFilePath = file.getAbsolutePath() + ".failed";
@@ -301,7 +294,7 @@ public class ImportCsv extends AbstractCsvTool {
    * @param failedFilePath the directory to save the failed files
    */
   private static void writeDataAlignedByTime(
-      List<String> headerNames, List<CSVRecord> records, String failedFilePath) {
+      List<String> headerNames, Stream<CSVRecord> records, String failedFilePath) {
     HashMap<String, List<String>> deviceAndMeasurementNames = new HashMap<>();
     HashMap<String, TSDataType> headerTypeMap = new HashMap<>();
     HashMap<String, String> headerNameMap = new HashMap<>();
@@ -315,100 +308,102 @@ public class ImportCsv extends AbstractCsvTool {
       e.printStackTrace();
     }
 
-    SimpleDateFormat timeFormatter = formatterInit(records.get(0).get(timeColumn));
+    List<String> deviceIds = new ArrayList<>();
+    List<Long> times = new ArrayList<>();
+    List<List<String>> measurementsList = new ArrayList<>();
+    List<List<TSDataType>> typesList = new ArrayList<>();
+    List<List<Object>> valuesList = new ArrayList<>();
+
+    AtomicReference<SimpleDateFormat> timeFormatter = new AtomicReference<>(null);
+    AtomicReference<Boolean> hasStarted = new AtomicReference<>(false);
 
     ArrayList<List<Object>> failedRecords = new ArrayList<>();
 
-    for (Map.Entry<String, List<String>> entry : deviceAndMeasurementNames.entrySet()) {
-      String deviceId = entry.getKey();
-      List<Long> times = new ArrayList<>();
-      List<String> measurementNames = entry.getValue();
-      List<List<TSDataType>> typesList = new ArrayList<>();
-      List<List<Object>> valuesList = new ArrayList<>();
-      List<List<String>> measurementsList = new ArrayList<>();
-
-      final int BATCH_SIZE = 1000;
-      AtomicInteger recordSize = new AtomicInteger();
-      records.stream()
-          .forEach(
-              record -> {
-                ArrayList<TSDataType> types = new ArrayList<>();
-                ArrayList<Object> values = new ArrayList<>();
-                ArrayList<String> measurements = new ArrayList<>();
-                AtomicReference<Boolean> isFail = new AtomicReference<>(false);
-                measurementNames.stream()
-                    .forEach(
-                        measurementName -> {
-                          String header = deviceId + "." + measurementName;
-                          String value = record.get(header);
-                          if (!"".equals(value)) {
-                            TSDataType type;
-                            if (!headerTypeMap.containsKey(headerNameMap.get(header))) {
-                              type = typeInfer(value);
-                              if (type != null) {
-                                headerTypeMap.put(header, type);
-                              } else {
-                                System.out.println(
-                                    String.format(
-                                        "Line '%s', column '%s': '%s' unknown type",
-                                        (records.indexOf(record) + 1), header, value));
-                                isFail.set(true);
-                              }
-                            }
-                            type = headerTypeMap.get(headerNameMap.get(header));
-                            if (type != null) {
-                              Object valueTransed = typeTrans(value, type);
-                              if (valueTransed == null) {
-                                isFail.set(true);
-                                System.out.println(
-                                    String.format(
-                                        "Line '%s', column '%s': '%s' can't convert to '%s'",
-                                        (records.indexOf(record) + 1), header, value, type));
-                              } else {
-                                measurements.add(
-                                    headerNameMap.get(header).replace(deviceId + '.', ""));
-                                types.add(type);
-                                values.add(valueTransed);
-                              }
-                            }
-                          }
-                        });
-                if (isFail.get()) {
-                  failedRecords.add(record.stream().collect(Collectors.toList()));
-                }
-                if (!measurements.isEmpty()) {
-                  try {
-                    if (timeFormatter == null) {
-                      try {
-                        times.add(Long.valueOf(record.get(timeColumn)));
-                      } catch (Exception e) {
-                        System.out.println(
-                            "Meet error when insert csv because the format of time is not supported");
-                        System.exit(0);
-                      }
-                    } else {
-                      times.add(timeFormatter.parse(record.get(timeColumn)).getTime());
-                    }
-                  } catch (ParseException e) {
-                    e.printStackTrace();
+    records.forEach(
+        record -> {
+          if (!hasStarted.get()) {
+            hasStarted.set(true);
+            timeFormatter.set(formatterInit(record.get(0)));
+          } else if ((record.getRecordNumber() - 1) % BATCH_SIZE == 0) {
+            writeAndEmptyDataSet(deviceIds, times, typesList, valuesList, measurementsList, 3);
+          }
+
+          boolean isFail = false;
+
+          for (String deviceId : deviceAndMeasurementNames.keySet()) {
+            ArrayList<TSDataType> types = new ArrayList<>();
+            ArrayList<Object> values = new ArrayList<>();
+            ArrayList<String> measurements = new ArrayList<>();
+
+            List<String> measurementNames = deviceAndMeasurementNames.get(deviceId);
+            for (String measurement : measurementNames) {
+              String header = deviceId + "." + measurement;
+              String value = record.get(header);
+              if (!"".equals(value)) {
+                TSDataType type;
+                if (!headerTypeMap.containsKey(headerNameMap.get(header))) {
+                  type = typeInfer(value);
+                  if (type != null) {
+                    headerTypeMap.put(header, type);
+                  } else {
+                    System.out.println(
+                        String.format(
+                            "Line '%s', column '%s': '%s' unknown type",
+                            record.getRecordNumber(), header, value));
+                    isFail = true;
                   }
-                  typesList.add(types);
-                  valuesList.add(values);
-                  measurementsList.add(measurements);
-                  recordSize.getAndIncrement();
-                  if (recordSize.get() >= BATCH_SIZE) {
-                    writeAndEmptyDataSet(
-                        deviceId, times, typesList, valuesList, measurementsList, 3);
-                    recordSize.set(0);
+                }
+                type = headerTypeMap.get(headerNameMap.get(header));
+                if (type != null) {
+                  Object valueTrans = typeTrans(value, type);
+                  if (valueTrans == null) {
+                    isFail = true;
+                    System.out.println(
+                        String.format(
+                            "Line '%s', column '%s': '%s' can't convert to '%s'",
+                            record.getRecordNumber(), header, value, type));
+                  } else {
+                    measurements.add(headerNameMap.get(header).replace(deviceId + '.', ""));
+                    types.add(type);
+                    values.add(valueTrans);
                   }
                 }
-              });
-      writeAndEmptyDataSet(deviceId, times, typesList, valuesList, measurementsList, 3);
+              }
+            }
+            if (!measurements.isEmpty()) {
+              if (timeFormatter.get() == null) {
+                times.add(Long.valueOf(record.get(timeColumn)));
+              } else {
+                try {
+                  times.add(timeFormatter.get().parse(record.get(timeColumn)).getTime());
+                } catch (ParseException e) {
+                  System.out.println(
+                      "Meet error when insert csv because the format of time is not supported");
+                  System.exit(0);
+                }
+              }
+              deviceIds.add(deviceId);
+              typesList.add(types);
+              valuesList.add(values);
+              measurementsList.add(measurements);
+            }
+          }
+          if (isFail) {
+            failedRecords.add(record.stream().collect(Collectors.toList()));
+          }
+        });
+    if (!deviceIds.isEmpty()) {
+      writeAndEmptyDataSet(deviceIds, times, typesList, valuesList, measurementsList, 3);
     }
+
     if (!failedRecords.isEmpty()) {
       writeCsvFile(headerNames, failedRecords, failedFilePath);
     }
-    System.out.println("Import completely!");
+    if (hasStarted.get()) {
+      System.out.println("Import completely!");
+    } else {
+      System.out.println("No records!");
+    }
   }
 
   /**
@@ -419,115 +414,117 @@ public class ImportCsv extends AbstractCsvTool {
    * @param failedFilePath the directory to save the failed files
    */
   private static void writeDataAlignedByDevice(
-      List<String> headerNames, List<CSVRecord> records, String failedFilePath) {
+      List<String> headerNames, Stream<CSVRecord> records, String failedFilePath) {
     HashMap<String, TSDataType> headerTypeMap = new HashMap<>();
     HashMap<String, String> headerNameMap = new HashMap<>();
     parseHeaders(headerNames, null, headerTypeMap, headerNameMap);
-    Set<String> devices =
-        records.stream().map(record -> record.get(deviceColumn)).collect(Collectors.toSet());
-    String devicesStr = StringUtils.join(devices, ",");
-    try {
-      queryType(devicesStr, headerTypeMap, "Device");
-    } catch (IoTDBConnectionException e) {
-      e.printStackTrace();
-    }
 
-    SimpleDateFormat timeFormatter = formatterInit(records.get(0).get(timeColumn));
-    Set<String> measurementNames = headerNameMap.keySet();
+    AtomicReference<SimpleDateFormat> timeFormatter = new AtomicReference<>(null);
+    AtomicReference<String> deviceName = new AtomicReference<>(null);
+
+    HashSet<String> typeQueriedDevice = new HashSet<>();
+
+    // the data that interface need
+    List<Long> times = new ArrayList<>();
+    List<List<TSDataType>> typesList = new ArrayList<>();
+    List<List<Object>> valuesList = new ArrayList<>();
+    List<List<String>> measurementsList = new ArrayList<>();
+
     ArrayList<List<Object>> failedRecords = new ArrayList<>();
 
-    devices.stream()
-        .forEach(
-            device -> {
-              List<Long> times = new ArrayList<>();
-
-              List<List<TSDataType>> typesList = new ArrayList<>();
-              List<List<Object>> valuesList = new ArrayList<>();
-              List<List<String>> measurementsList = new ArrayList<>();
-
-              final int BATCH_SIZE = 1000;
-              AtomicInteger recordSize = new AtomicInteger();
-              records.stream()
-                  .filter(record -> record.get(deviceColumn).equals(device))
-                  .forEach(
-                      record -> {
-                        ArrayList<TSDataType> types = new ArrayList<>();
-                        ArrayList<Object> values = new ArrayList<>();
-                        ArrayList<String> measurements = new ArrayList<>();
-
-                        AtomicReference<Boolean> isFail = new AtomicReference<>(false);
-
-                        measurementNames.stream()
-                            .forEach(
-                                measurement -> {
-                                  String value = record.get(measurement);
-                                  if (!"".equals(value)) {
-                                    TSDataType type;
-                                    if (!headerTypeMap.containsKey(
-                                        headerNameMap.get(measurement))) {
-                                      type = typeInfer(value);
-                                      if (type != null) {
-                                        headerTypeMap.put(measurement, type);
-                                      } else {
-                                        System.out.println(
-                                            String.format(
-                                                "Line '%s', column '%s': '%s' unknown type",
-                                                (records.indexOf(record) + 1), measurement, value));
-                                        isFail.set(true);
-                                      }
-                                    }
-                                    type = headerTypeMap.get(headerNameMap.get(measurement));
-                                    if (type != null) {
-                                      Object valueTransed = typeTrans(value, type);
-                                      if (valueTransed == null) {
-                                        isFail.set(true);
-                                        System.out.println(
-                                            String.format(
-                                                "Line '%s', column '%s': '%s' can't convert to '%s'",
-                                                (records.indexOf(record) + 1),
-                                                measurement,
-                                                value,
-                                                type));
-                                      } else {
-                                        values.add(valueTransed);
-                                        measurements.add(headerNameMap.get(measurement));
-                                        types.add(type);
-                                      }
-                                    }
-                                  }
-                                });
-                        if (isFail.get()) {
-                          failedRecords.add(record.stream().collect(Collectors.toList()));
-                        }
-                        if (!measurements.isEmpty()) {
-                          try {
-                            if (timeFormatter == null) {
-                              try {
-                                times.add(Long.valueOf(record.get(timeColumn)));
-                              } catch (Exception e) {
-                                System.out.println(
-                                    "Meet error when insert csv because the format of time is not supported");
-                                System.exit(0);
-                              }
-                            } else {
-                              times.add(timeFormatter.parse(record.get(timeColumn)).getTime());
-                            }
-                          } catch (ParseException e) {
-                            e.printStackTrace();
-                          }
-                          typesList.add(types);
-                          valuesList.add(values);
-                          measurementsList.add(measurements);
-                          recordSize.getAndIncrement();
-                          if (recordSize.get() >= BATCH_SIZE) {
-                            writeAndEmptyDataSet(
-                                device, times, typesList, valuesList, measurementsList, 3);
-                            recordSize.set(0);
-                          }
-                        }
-                      });
-              writeAndEmptyDataSet(device, times, typesList, valuesList, measurementsList, 3);
-            });
+    records.forEach(
+        record -> {
+          // only run in first record
+          if (deviceName.get() == null) {
+            deviceName.set(record.get(1));
+            timeFormatter.set(formatterInit(record.get(0)));
+          } else if (deviceName.get() != record.get(1)) {
+            // if device changed
+            writeAndEmptyDataSet(
+                deviceName.get(), times, typesList, valuesList, measurementsList, 3);
+            deviceName.set(record.get(1));
+          } else if (record.getRecordNumber() - 1 % BATCH_SIZE == 0 && times.size() != 0) {
+            // insert a batch
+            writeAndEmptyDataSet(
+                deviceName.get(), times, typesList, valuesList, measurementsList, 3);
+          }
+
+          // the data of the record
+          ArrayList<TSDataType> types = new ArrayList<>();
+          ArrayList<Object> values = new ArrayList<>();
+          ArrayList<String> measurements = new ArrayList<>();
+
+          AtomicReference<Boolean> isFail = new AtomicReference<>(false);
+
+          // read data from record
+          for (String measurement : headerNameMap.keySet()) {
+            String value = record.get(measurement);
+            if (!"".equals(value)) {
+              TSDataType type;
+              if (!headerTypeMap.containsKey(headerNameMap.get(measurement))) {
+                boolean hasResult = false;
+                // query the data type in iotdb
+                if (!typeQueriedDevice.contains(deviceName.get())) {
+                  try {
+                    hasResult = queryType(deviceName.get(), headerTypeMap, "Device");
+                    typeQueriedDevice.add(deviceName.get());
+                  } catch (IoTDBConnectionException e) {
+                    e.printStackTrace();
+                  }
+                }
+                if (!hasResult) {
+                  type = typeInfer(value);
+                  if (type != null) {
+                    headerTypeMap.put(measurement, type);
+                  } else {
+                    System.out.println(
+                        String.format(
+                            "Line '%s', column '%s': '%s' unknown type",
+                            record.getRecordNumber(), measurement, value));
+                    isFail.set(true);
+                  }
+                }
+              }
+              type = headerTypeMap.get(headerNameMap.get(measurement));
+              if (type != null) {
+                Object valueTrans = typeTrans(value, type);
+                if (valueTrans == null) {
+                  isFail.set(true);
+                  System.out.println(
+                      String.format(
+                          "Line '%s', column '%s': '%s' can't convert to '%s'",
+                          record.getRecordNumber(), headerNameMap.get(measurement), value, type));
+                } else {
+                  values.add(valueTrans);
+                  measurements.add(headerNameMap.get(measurement));
+                  types.add(type);
+                }
+              }
+            }
+          }
+          if (isFail.get()) {
+            failedRecords.add(record.stream().collect(Collectors.toList()));
+          }
+          if (!measurements.isEmpty()) {
+            if (timeFormatter.get() == null) {
+              times.add(Long.valueOf(record.get(timeColumn)));
+            } else {
+              try {
+                times.add(timeFormatter.get().parse(record.get(timeColumn)).getTime());
+              } catch (ParseException e) {
+                System.out.println(
+                    "Meet error when insert csv because the format of time is not supported");
+                System.exit(0);
+              }
+            }
+            typesList.add(types);
+            valuesList.add(values);
+            measurementsList.add(measurements);
+          }
+        });
+    if (times.size() != 0) {
+      writeAndEmptyDataSet(deviceName.get(), times, typesList, valuesList, measurementsList, 3);
+    }
     if (!failedRecords.isEmpty()) {
       writeCsvFile(headerNames, failedRecords, failedFilePath);
     }
@@ -549,12 +546,12 @@ public class ImportCsv extends AbstractCsvTool {
             device, times, measurementsList, typesList, valuesList);
       }
     } catch (IoTDBConnectionException e) {
-      try {
-        session.open();
-      } catch (IoTDBConnectionException ex) {
-        System.out.println("Meet error when insert csv because " + e.getMessage());
-      }
       if (retryTime > 0) {
+        try {
+          session.open();
+        } catch (IoTDBConnectionException ex) {
+          System.out.println("Meet error when insert csv because " + e.getMessage());
+        }
         writeAndEmptyDataSet(device, times, typesList, valuesList, measurementsList, --retryTime);
       } else {
         return;
@@ -569,6 +566,38 @@ public class ImportCsv extends AbstractCsvTool {
     }
   }
 
+  private static void writeAndEmptyDataSet(
+      List<String> deviceIds,
+      List<Long> times,
+      List<List<TSDataType>> typesList,
+      List<List<Object>> valuesList,
+      List<List<String>> measurementsList,
+      int retryTime) {
+    try {
+      session.insertAlignedRecords(deviceIds, times, measurementsList, typesList, valuesList);
+    } catch (IoTDBConnectionException e) {
+      if (retryTime > 0) {
+        try {
+          session.open();
+        } catch (IoTDBConnectionException ex) {
+          System.out.println("Meet error when insert csv because " + e.getMessage());
+        }
+        writeAndEmptyDataSet(
+            deviceIds, times, typesList, valuesList, measurementsList, --retryTime);
+      } else {
+        return;
+      }
+    } catch (StatementExecutionException e) {
+      System.out.println("Meet error when insert csv because " + e.getMessage());
+    } finally {
+      deviceIds.clear();
+      times.clear();
+      typesList.clear();
+      valuesList.clear();
+      measurementsList.clear();
+    }
+  }
+
   /**
    * read data from the CSV file
    *
@@ -640,7 +669,7 @@ public class ImportCsv extends AbstractCsvTool {
    * @throws IoTDBConnectionException
    * @throws StatementExecutionException
    */
-  private static void queryType(
+  private static boolean queryType(
       String deviceNames, HashMap<String, TSDataType> headerTypeMap, String alignedType)
       throws IoTDBConnectionException {
     String sql = "select * from " + deviceNames + " limit 1";
@@ -650,18 +679,23 @@ public class ImportCsv extends AbstractCsvTool {
     } catch (StatementExecutionException e) {
       System.out.println(
           "Meet error when query the type of timeseries because the IoTDB v0.13 don't support that the path contains any purely digital path.");
-      return;
+      return false;
     }
     List<String> columnNames = sessionDataSet.getColumnNames();
     List<String> columnTypes = sessionDataSet.getColumnTypes();
-    for (int i = 1; i < columnNames.size(); i++) {
-      if (alignedType == "Time") {
-        headerTypeMap.put(columnNames.get(i), getType(columnTypes.get(i)));
-      } else if (alignedType == "Device") {
-        String[] split = columnNames.get(i).split("\\.");
-        String measurement = split[split.length - 1];
-        headerTypeMap.put(measurement, getType(columnTypes.get(i)));
+    if (columnNames.size() == 1) {
+      return false;
+    } else {
+      for (int i = 1; i < columnNames.size(); i++) {
+        if (alignedType == "Time") {
+          headerTypeMap.put(columnNames.get(i), getType(columnTypes.get(i)));
+        } else if (alignedType == "Device") {
+          String[] split = columnNames.get(i).split("\\.");
+          String measurement = split[split.length - 1];
+          headerTypeMap.put(measurement, getType(columnTypes.get(i)));
+        }
       }
+      return true;
     }
   }
 
@@ -724,10 +758,13 @@ public class ImportCsv extends AbstractCsvTool {
    * @return
    */
   private static TSDataType typeInfer(String value) {
-    if (value.contains("\"")) return TEXT;
-    else if (value.equals("true") || value.equals("false")) return BOOLEAN;
-    else if (value.equals("NaN")) return DOUBLE;
-    else if (!value.contains(".")) {
+    if (value.contains("\"")) {
+      return TEXT;
+    } else if (value.equals("true") || value.equals("false")) {
+      return BOOLEAN;
+    } else if (value.equals("NaN")) {
+      return DOUBLE;
+    } else if (!value.contains(".")) {
       try {
         Integer.valueOf(value);
         return INT32;
@@ -753,9 +790,10 @@ public class ImportCsv extends AbstractCsvTool {
     try {
       switch (type) {
         case TEXT:
-          if (value.startsWith("\"") && value.endsWith("\""))
+          if (value.startsWith("\"") && value.endsWith("\"")) {
             return value.substring(1, value.length() - 1);
-          else return null;
+          }
+          return null;
         case BOOLEAN:
           if (!"true".equals(value) && !"false".equals(value)) {
             return null;
diff --git a/cross-tests/src/test/java/org/apache/iotdb/cross/tests/tools/importCsv/ImportCsvTestIT.java b/cross-tests/src/test/java/org/apache/iotdb/cross/tests/tools/importCsv/ImportCsvTestIT.java
index 1e199d5..749ff8c 100644
--- a/cross-tests/src/test/java/org/apache/iotdb/cross/tests/tools/importCsv/ImportCsvTestIT.java
+++ b/cross-tests/src/test/java/org/apache/iotdb/cross/tests/tools/importCsv/ImportCsvTestIT.java
@@ -168,7 +168,6 @@ public class ImportCsvTestIT extends AbstractScript {
    */
   @Test
   public void test() throws IOException, ClassNotFoundException {
-    createSchema();
     assertTrue(generateTestCSV(false, false, false, false, false));
     String[] params = {"-f", CSV_FILE};
     testMethod(params, null);
diff --git a/docs/UserGuide/Write-And-Delete-Data/CSV-Tool.md b/docs/UserGuide/Write-And-Delete-Data/CSV-Tool.md
index 2e227ae..08bad7d 100644
--- a/docs/UserGuide/Write-And-Delete-Data/CSV-Tool.md
+++ b/docs/UserGuide/Write-And-Delete-Data/CSV-Tool.md
@@ -210,5 +210,4 @@ Note that the following special characters in fields need to be checked before i
 
 1. `,` : fields containing `,` should be escaped by `\`.
 2. you can input time format like `yyyy-MM-dd'T'HH:mm:ss`, `yyy-MM-dd HH:mm:ss`, or `yyyy-MM-dd'T'HH:mm:ss.SSSZ`.
-3. a single CSV or TXT file should not exceed 2GB. If the size is larger than 2GB, you can divide the file into small files and import them in batches using parameter `-f` to specify the folder of small files.
-4. the `Time` column must be the first one.
\ No newline at end of file
+3. the `Time` column must be the first one.
\ No newline at end of file
diff --git a/docs/zh/UserGuide/Write-And-Delete-Data/CSV-Tool.md b/docs/zh/UserGuide/Write-And-Delete-Data/CSV-Tool.md
index 3126951..a34437c 100644
--- a/docs/zh/UserGuide/Write-And-Delete-Data/CSV-Tool.md
+++ b/docs/zh/UserGuide/Write-And-Delete-Data/CSV-Tool.md
@@ -210,5 +210,4 @@ Time,Device,str(TEXT),int(INT32)
 
 1. `,` :如果text类型的字段中包含`,`那么需要用`\`来进行转义。
 2. 你可以导入像`yyyy-MM-dd'T'HH:mm:ss`, `yyy-MM-dd HH:mm:ss`, 或者 `yyyy-MM-dd'T'HH:mm:ss.SSSZ`格式的时间。
-3. 单个CSV或者txt文件最好不要超过2GB。如果超过2GB可以将文件分割成小文件,然后用`-f`参数来指定存放小文件的文件夹的方式进行批量导入。
-4. `Time`这一列应该放在第一列。
\ No newline at end of file
+3. `Time`这一列应该放在第一列。
\ No newline at end of file