This is an automated email from the ASF dual-hosted git repository.
miao 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 8a6e31ffe5a add import-schema.sh/bat and export-schema.sh/bat in
cli/tools (#12623)
8a6e31ffe5a is described below
commit 8a6e31ffe5ab85b42a5d1b6e1be8823fef0ac025
Author: Summer <[email protected]>
AuthorDate: Sat Jun 1 00:14:26 2024 +0800
add import-schema.sh/bat and export-schema.sh/bat in cli/tools (#12623)
* add import-schema.sh/bat and export-schema.sh/bat in cli/tools
* update it
* update it
---------
Co-authored-by: 2b3c511 <[email protected]>
---
.../apache/iotdb/tools/it/ExportSchemaTestIT.java | 149 +++++
.../apache/iotdb/tools/it/ImportSchemaTestIT.java | 126 ++++
.../src/assembly/resources/tools/export-schema.bat | 62 ++
.../src/assembly/resources/tools/export-schema.sh | 57 ++
.../src/assembly/resources/tools/import-schema.bat | 63 ++
.../src/assembly/resources/tools/import-schema.sh | 57 ++
.../org/apache/iotdb/tool/AbstractSchemaTool.java | 242 ++++++++
.../java/org/apache/iotdb/tool/ExportSchema.java | 339 +++++++++++
.../java/org/apache/iotdb/tool/ImportSchema.java | 653 +++++++++++++++++++++
9 files changed, 1748 insertions(+)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/tools/it/ExportSchemaTestIT.java
b/integration-test/src/test/java/org/apache/iotdb/tools/it/ExportSchemaTestIT.java
new file mode 100644
index 00000000000..f8a4cdae171
--- /dev/null
+++
b/integration-test/src/test/java/org/apache/iotdb/tools/it/ExportSchemaTestIT.java
@@ -0,0 +1,149 @@
+/*
+ * 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.tools.it;
+
+import org.apache.iotdb.cli.it.AbstractScript;
+import org.apache.iotdb.isession.ISession;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.StatementExecutionException;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.file.metadata.enums.CompressionType;
+import org.apache.tsfile.file.metadata.enums.TSEncoding;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class ExportSchemaTestIT extends AbstractScript {
+ private static String ip;
+
+ private static String port;
+
+ private static String toolsPath;
+
+ private static String libPath;
+
+ private static String homePath;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ EnvFactory.getEnv().initClusterEnvironment();
+ ip = EnvFactory.getEnv().getIP();
+ port = EnvFactory.getEnv().getPort();
+ toolsPath = EnvFactory.getEnv().getToolsPath();
+ libPath = EnvFactory.getEnv().getLibPath();
+ homePath =
+ libPath.substring(0, libPath.lastIndexOf(File.separator + "lib" +
File.separator + "*"));
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void test() throws IOException {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.startsWith("windows")) {
+ testOnWindows();
+ } else {
+ testOnUnix();
+ }
+ }
+
+ @Override
+ protected void testOnWindows() throws IOException {
+ prepareSchema();
+ final String[] output = {"Export completely!"};
+ ProcessBuilder builder =
+ new ProcessBuilder(
+ "cmd.exe",
+ "/c",
+ toolsPath + File.separator + "export-schema.bat",
+ "-h",
+ ip,
+ "-p",
+ port,
+ "-u",
+ "root",
+ "-pw",
+ "root",
+ "-t",
+ "target",
+ "-path",
+ "root.**",
+ "&",
+ "exit",
+ "%^errorlevel%");
+ builder.environment().put("IOTDB_HOME", homePath);
+ testOutput(builder, output, 0);
+ }
+
+ @Override
+ protected void testOnUnix() throws IOException {
+ prepareSchema();
+ final String[] output = {"Export completely!"};
+ ProcessBuilder builder =
+ new ProcessBuilder(
+ "bash",
+ toolsPath + File.separator + "export-schema.sh",
+ "-h",
+ ip,
+ "-p",
+ port,
+ "-u",
+ "root",
+ "-pw",
+ "root",
+ "-t",
+ "target",
+ "-path",
+ "root.**");
+ builder.environment().put("IOTDB_HOME", homePath);
+ testOutput(builder, output, 0);
+ }
+
+ public void prepareSchema() {
+ try (ISession session = EnvFactory.getEnv().getSessionConnection()) {
+ session.open();
+ session.createTimeseries(
+ "root.schema.t2.c1",
+ TSDataType.DOUBLE,
+ TSEncoding.GORILLA,
+ CompressionType.LZ4,
+ null,
+ null,
+ null,
+ null);
+ } catch (IoTDBConnectionException | StatementExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git
a/integration-test/src/test/java/org/apache/iotdb/tools/it/ImportSchemaTestIT.java
b/integration-test/src/test/java/org/apache/iotdb/tools/it/ImportSchemaTestIT.java
new file mode 100644
index 00000000000..ea7e1ba07f1
--- /dev/null
+++
b/integration-test/src/test/java/org/apache/iotdb/tools/it/ImportSchemaTestIT.java
@@ -0,0 +1,126 @@
+/*
+ * 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.tools.it;
+
+import org.apache.iotdb.cli.it.AbstractScript;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class ImportSchemaTestIT extends AbstractScript {
+
+ private static String ip;
+
+ private static String port;
+
+ private static String toolsPath;
+
+ private static String libPath;
+
+ private static String homePath;
+
+ @BeforeClass
+ public static void setUp() {
+ EnvFactory.getEnv().initClusterEnvironment();
+ ip = EnvFactory.getEnv().getIP();
+ port = EnvFactory.getEnv().getPort();
+ toolsPath = EnvFactory.getEnv().getToolsPath();
+ libPath = EnvFactory.getEnv().getLibPath();
+ homePath =
+ libPath.substring(0, libPath.lastIndexOf(File.separator + "lib" +
File.separator + "*"));
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void test() throws IOException {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.startsWith("windows")) {
+ testOnWindows();
+ } else {
+ testOnUnix();
+ }
+ }
+
+ @Override
+ protected void testOnWindows() throws IOException {
+ final String[] output = {
+ "The file name must end with \"csv\"!",
+ };
+ ProcessBuilder builder =
+ new ProcessBuilder(
+ "cmd.exe",
+ "/c",
+ toolsPath + File.separator + "import-schema.bat",
+ "-h",
+ ip,
+ "-p",
+ port,
+ "-u",
+ "root",
+ "-pw",
+ "root",
+ "-s",
+ "./",
+ "&",
+ "exit",
+ "%^errorlevel%");
+ builder.environment().put("IOTDB_HOME", homePath);
+ testOutput(builder, output, 0);
+ }
+
+ @Override
+ protected void testOnUnix() throws IOException {
+ final String[] output = {
+ "The file name must end with \"csv\"!",
+ };
+ ProcessBuilder builder =
+ new ProcessBuilder(
+ "bash",
+ toolsPath + File.separator + "import-schema.sh",
+ "-h",
+ ip,
+ "-p",
+ port,
+ "-u",
+ "root",
+ "-pw",
+ "root",
+ "-s",
+ "./");
+ builder.environment().put("IOTDB_HOME", homePath);
+ testOutput(builder, output, 0);
+ }
+}
diff --git a/iotdb-client/cli/src/assembly/resources/tools/export-schema.bat
b/iotdb-client/cli/src/assembly/resources/tools/export-schema.bat
new file mode 100644
index 00000000000..8ed2b81f5c7
--- /dev/null
+++ b/iotdb-client/cli/src/assembly/resources/tools/export-schema.bat
@@ -0,0 +1,62 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+
+title IoTDB Export
+
+echo ````````````````````````````````````````````````
+echo Starting IoTDB Client Export Schema Script
+echo ````````````````````````````````````````````````
+
+if "%OS%" == "Windows_NT" setlocal
+
+pushd %~dp0..
+if NOT DEFINED IOTDB_HOME set IOTDB_HOME=%CD%
+popd
+
+if NOT DEFINED MAIN_CLASS set MAIN_CLASS=org.apache.iotdb.tool.ExportSchema
+if NOT DEFINED JAVA_HOME goto :err
+
+@REM
-----------------------------------------------------------------------------
+@REM JVM Opts we'll use in legacy run or installation
+set JAVA_OPTS=-ea^
+ -DIOTDB_HOME="%IOTDB_HOME%"
+
+@REM ***** CLASSPATH library setting *****
+if EXIST "%IOTDB_HOME%\lib" (set CLASSPATH="%IOTDB_HOME%\lib\*") else set
CLASSPATH="%IOTDB_HOME%\..\lib\*"
+
+REM
-----------------------------------------------------------------------------
+
+"%JAVA_HOME%\bin\java" -DIOTDB_HOME="%IOTDB_HOME%" %JAVA_OPTS% -cp %CLASSPATH%
%MAIN_CLASS% %*
+set ret_code=%ERRORLEVEL%
+goto finally
+
+
+:err
+echo JAVA_HOME environment variable must be set!
+set ret_code=1
+pause
+
+@REM
-----------------------------------------------------------------------------
+:finally
+
+ENDLOCAL
+
+EXIT /B %ret_code%
diff --git a/iotdb-client/cli/src/assembly/resources/tools/export-schema.sh
b/iotdb-client/cli/src/assembly/resources/tools/export-schema.sh
new file mode 100644
index 00000000000..b65a7d3950a
--- /dev/null
+++ b/iotdb-client/cli/src/assembly/resources/tools/export-schema.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# 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.
+#
+
+echo ------------------------------------------
+echo Starting IoTDB Client Export Schema Script
+echo ------------------------------------------
+
+if [ -z "${IOTDB_INCLUDE}" ]; then
+ #do nothing
+ :
+elif [ -r "$IOTDB_INCLUDE" ]; then
+ . "$IOTDB_INCLUDE"
+fi
+
+if [ -z "${IOTDB_HOME}" ]; then
+ export IOTDB_HOME="$(cd "`dirname "$0"`"/..; pwd)"
+fi
+
+if [ -n "$JAVA_HOME" ]; then
+ for java in "$JAVA_HOME"/bin/amd64/java "$JAVA_HOME"/bin/java; do
+ if [ -x "$java" ]; then
+ JAVA="$java"
+ break
+ fi
+ done
+else
+ JAVA=java
+fi
+
+if [ -z $JAVA ] ; then
+ echo Unable to find java executable. Check JAVA_HOME and PATH environment
variables. > /dev/stderr
+ exit 1;
+fi
+
+CLASSPATH=${IOTDB_HOME}/lib/*
+
+MAIN_CLASS=org.apache.iotdb.tool.ExportSchema
+
+"$JAVA" -DIOTDB_HOME=${IOTDB_HOME} -cp "$CLASSPATH" "$MAIN_CLASS" "$@"
+exit $?
\ No newline at end of file
diff --git a/iotdb-client/cli/src/assembly/resources/tools/import-schema.bat
b/iotdb-client/cli/src/assembly/resources/tools/import-schema.bat
new file mode 100644
index 00000000000..46a8d5abf6e
--- /dev/null
+++ b/iotdb-client/cli/src/assembly/resources/tools/import-schema.bat
@@ -0,0 +1,63 @@
+@REM
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM
+
+@echo off
+
+title IoTDB Import
+
+echo ````````````````````````````````````````````````
+echo Starting IoTDB Client Import Schema Script
+echo ````````````````````````````````````````````````
+
+if "%OS%" == "Windows_NT" setlocal
+
+pushd %~dp0..
+if NOT DEFINED IOTDB_HOME set IOTDB_HOME=%CD%
+popd
+
+if NOT DEFINED MAIN_CLASS set MAIN_CLASS=org.apache.iotdb.tool.ImportSchema
+if NOT DEFINED JAVA_HOME goto :err
+
+@REM
-----------------------------------------------------------------------------
+@REM JVM Opts we'll use in legacy run or installation
+set JAVA_OPTS=-ea^
+ -DIOTDB_HOME="%IOTDB_HOME%"
+
+@REM ***** CLASSPATH library setting *****
+if EXIST "%IOTDB_HOME%\lib" (set CLASSPATH="%IOTDB_HOME%\lib\*") else set
CLASSPATH="%IOTDB_HOME%\..\lib\*"
+
+REM
-----------------------------------------------------------------------------
+
+"%JAVA_HOME%\bin\java" -DIOTDB_HOME="%IOTDB_HOME%" %JAVA_OPTS% -cp %CLASSPATH%
%MAIN_CLASS% %*
+set ret_code=%ERRORLEVEL%
+goto finally
+
+
+:err
+echo JAVA_HOME environment variable must be set!
+set ret_code=1
+pause
+
+
+@REM
-----------------------------------------------------------------------------
+:finally
+
+ENDLOCAL
+
+EXIT /B %ret_code%
diff --git a/iotdb-client/cli/src/assembly/resources/tools/import-schema.sh
b/iotdb-client/cli/src/assembly/resources/tools/import-schema.sh
new file mode 100644
index 00000000000..ca91293e235
--- /dev/null
+++ b/iotdb-client/cli/src/assembly/resources/tools/import-schema.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# 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.
+#
+
+echo ------------------------------------------
+echo Starting IoTDB Client Import Schema Script
+echo ------------------------------------------
+
+if [ -z "${IOTDB_INCLUDE}" ]; then
+ #do nothing
+ :
+elif [ -r "$IOTDB_INCLUDE" ]; then
+ . "$IOTDB_INCLUDE"
+fi
+
+if [ -z "${IOTDB_HOME}" ]; then
+ export IOTDB_HOME="$(cd "`dirname "$0"`"/..; pwd)"
+fi
+
+if [ -n "$JAVA_HOME" ]; then
+ for java in "$JAVA_HOME"/bin/amd64/java "$JAVA_HOME"/bin/java; do
+ if [ -x "$java" ]; then
+ JAVA="$java"
+ break
+ fi
+ done
+else
+ JAVA=java
+fi
+
+if [ -z $JAVA ] ; then
+ echo Unable to find java executable. Check JAVA_HOME and PATH environment
variables. > /dev/stderr
+ exit 1;
+fi
+
+CLASSPATH=${IOTDB_HOME}/lib/*
+
+MAIN_CLASS=org.apache.iotdb.tool.ImportSchema
+
+"$JAVA" -DIOTDB_HOME=${IOTDB_HOME} -cp "$CLASSPATH" "$MAIN_CLASS" "$@"
+exit $?
\ No newline at end of file
diff --git
a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/AbstractSchemaTool.java
b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/AbstractSchemaTool.java
new file mode 100644
index 00000000000..9e91d404d12
--- /dev/null
+++
b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/AbstractSchemaTool.java
@@ -0,0 +1,242 @@
+/*
+ * 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.tool;
+
+import org.apache.iotdb.cli.utils.IoTPrinter;
+import org.apache.iotdb.exception.ArgsErrorException;
+import org.apache.iotdb.session.Session;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+import org.apache.commons.csv.QuoteMode;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class AbstractSchemaTool {
+
+ protected static final String HOST_ARGS = "h";
+ protected static final String HOST_NAME = "host";
+ protected static final String HOST_DEFAULT_VALUE = "127.0.0.1";
+
+ protected static final String HELP_ARGS = "help";
+
+ protected static final String PORT_ARGS = "p";
+ protected static final String PORT_NAME = "port";
+ protected static final String PORT_DEFAULT_VALUE = "6667";
+
+ protected static final String PW_ARGS = "pw";
+ protected static final String PW_NAME = "password";
+ protected static final String PW_DEFAULT_VALUE = "root";
+
+ protected static final String USERNAME_ARGS = "u";
+ protected static final String USERNAME_NAME = "username";
+ protected static final String USERNAME_DEFAULT_VALUE = "root";
+
+ protected static final String TIMEOUT_ARGS = "timeout";
+ protected static final String TIMEOUT_ARGS_NAME = "queryTimeout";
+
+ protected static final int MAX_HELP_CONSOLE_WIDTH = 92;
+
+ protected static final int CODE_OK = 0;
+ protected static final int CODE_ERROR = 1;
+
+ protected static String host;
+ protected static String port;
+ protected static String username;
+ protected static String password;
+
+ protected static String aligned;
+ protected static Session session;
+
+ protected static final List<String> HEAD_COLUMNS =
+ Arrays.asList("Timeseries", "Alias", "DataType", "Encoding",
"Compression");
+ private static final IoTPrinter ioTPrinter = new IoTPrinter(System.out);
+ private static final Logger LOGGER =
LoggerFactory.getLogger(AbstractSchemaTool.class);
+
+ protected AbstractSchemaTool() {}
+
+ protected static String checkRequiredArg(
+ String arg, String name, CommandLine commandLine, String defaultValue)
+ throws ArgsErrorException {
+ String str = commandLine.getOptionValue(arg);
+ if (str == null) {
+ if (StringUtils.isNotBlank(defaultValue)) {
+ return defaultValue;
+ }
+ String msg = String.format("Required values for option '%s' not
provided", name);
+ LOGGER.info(msg);
+ LOGGER.info("Use -help for more information");
+ throw new ArgsErrorException(msg);
+ }
+ return str;
+ }
+
+ protected static void parseBasicParams(CommandLine commandLine) throws
ArgsErrorException {
+ host = checkRequiredArg(HOST_ARGS, HOST_NAME, commandLine,
HOST_DEFAULT_VALUE);
+ port = checkRequiredArg(PORT_ARGS, PORT_NAME, commandLine,
PORT_DEFAULT_VALUE);
+ username = checkRequiredArg(USERNAME_ARGS, USERNAME_NAME, commandLine,
USERNAME_DEFAULT_VALUE);
+ password = checkRequiredArg(PW_ARGS, PW_NAME, commandLine,
PW_DEFAULT_VALUE);
+ }
+
+ protected static Options createNewOptions() {
+ Options options = new Options();
+
+ Option opHost =
+ Option.builder(HOST_ARGS)
+ .longOpt(HOST_NAME)
+ .optionalArg(true)
+ .argName(HOST_NAME)
+ .hasArg()
+ .desc("Host Name (optional)")
+ .build();
+ options.addOption(opHost);
+
+ Option opPort =
+ Option.builder(PORT_ARGS)
+ .longOpt(PORT_NAME)
+ .optionalArg(true)
+ .argName(PORT_NAME)
+ .hasArg()
+ .desc("Port (optional)")
+ .build();
+ options.addOption(opPort);
+
+ Option opUsername =
+ Option.builder(USERNAME_ARGS)
+ .longOpt(USERNAME_NAME)
+ .optionalArg(true)
+ .argName(USERNAME_NAME)
+ .hasArg()
+ .desc("Username (optional)")
+ .build();
+ options.addOption(opUsername);
+
+ Option opPassword =
+ Option.builder(PW_ARGS)
+ .longOpt(PW_NAME)
+ .optionalArg(true)
+ .argName(PW_NAME)
+ .hasArg()
+ .desc("Password (optional)")
+ .build();
+ options.addOption(opPassword);
+ return options;
+ }
+
+ /**
+ * write data to CSV file.
+ *
+ * @param records the records of CSV file
+ * @param filePath the directory to save the file
+ */
+ public static Boolean writeCsvFile(List<List<Object>> records, String
filePath) {
+ try {
+ final CSVPrinterWrapper csvPrinterWrapper = new
CSVPrinterWrapper(filePath);
+ for (List<Object> CsvRecord : records) {
+ csvPrinterWrapper.printRecordln(CsvRecord);
+ }
+ csvPrinterWrapper.flush();
+ csvPrinterWrapper.close();
+ return true;
+ } catch (IOException e) {
+ ioTPrinter.printException(e);
+ return false;
+ }
+ }
+
+ static class CSVPrinterWrapper {
+ private final String filePath;
+ private final CSVFormat csvFormat;
+ private CSVPrinter csvPrinter;
+
+ public CSVPrinterWrapper(String filePath) {
+ this.filePath = filePath;
+ this.csvFormat =
+ CSVFormat.Builder.create(CSVFormat.DEFAULT)
+ .setHeader()
+ .setSkipHeaderRecord(true)
+ .setEscape('\\')
+ .setQuoteMode(QuoteMode.NONE)
+ .build();
+ }
+
+ public void printRecord(final Iterable<?> values) throws IOException {
+ if (csvPrinter == null) {
+ csvPrinter = csvFormat.print(new PrintWriter(filePath));
+ }
+ csvPrinter.printRecord(values);
+ }
+
+ public void printRecordln(final Iterable<?> values) throws IOException {
+ if (csvPrinter == null) {
+ csvPrinter = csvFormat.print(new PrintWriter(filePath));
+ }
+ Iterator var2 = values.iterator();
+
+ while (var2.hasNext()) {
+ Object value = var2.next();
+ csvPrinter.print(value);
+ }
+ csvPrinter.println();
+ }
+
+ public void print(Object value) {
+ if (csvPrinter == null) {
+ try {
+ csvPrinter = csvFormat.print(new PrintWriter(filePath));
+ } catch (IOException e) {
+ ioTPrinter.printException(e);
+ return;
+ }
+ }
+ try {
+ csvPrinter.print(value);
+ } catch (IOException e) {
+ ioTPrinter.printException(e);
+ }
+ }
+
+ public void println() throws IOException {
+ csvPrinter.println();
+ }
+
+ public void close() throws IOException {
+ if (csvPrinter != null) {
+ csvPrinter.close();
+ }
+ }
+
+ public void flush() throws IOException {
+ if (csvPrinter != null) {
+ csvPrinter.flush();
+ }
+ }
+ }
+}
diff --git
a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/ExportSchema.java
b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/ExportSchema.java
new file mode 100644
index 00000000000..6693336e1f9
--- /dev/null
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/ExportSchema.java
@@ -0,0 +1,339 @@
+/*
+ * 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.tool;
+
+import org.apache.iotdb.cli.type.ExitType;
+import org.apache.iotdb.cli.utils.CliContext;
+import org.apache.iotdb.cli.utils.IoTPrinter;
+import org.apache.iotdb.cli.utils.JlineUtils;
+import org.apache.iotdb.exception.ArgsErrorException;
+import org.apache.iotdb.isession.SessionDataSet;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.StatementExecutionException;
+import org.apache.iotdb.session.Session;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.tsfile.read.common.Field;
+import org.apache.tsfile.read.common.RowRecord;
+import org.jline.reader.LineReader;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.List;
+
+import static org.apache.iotdb.commons.schema.SchemaConstant.SYSTEM_DATABASE;
+
+/** Export Schema CSV file. */
+public class ExportSchema extends AbstractSchemaTool {
+
+ private static final String TARGET_DIR_ARGS = "t";
+ private static final String TARGET_DIR_ARGS_NAME = "target";
+ private static final String TARGET_DIR_NAME = "targetDir";
+
+ private static final String TARGET_PATH_ARGS = "path";
+ private static final String TARGET_PATH_ARGS_NAME = "path_pattern";
+ private static final String TARGET_PATH_NAME = "exportPathPattern";
+ private static String queryPath;
+
+ private static final String TARGET_FILE_ARGS = "pf";
+ private static final String TARGET_FILE_ARGS_NAME = "path_pattern_file";
+ private static final String TARGET_FILE_NAME = "exportPathPatternFile";
+
+ private static final String LINES_PER_FILE_ARGS = "lpf";
+ private static final String LINES_PER_FILE_ARGS_NAME = "lines_per_file";
+ private static final String LINES_PER_FILE_NAME = "linesPerFile";
+ private static int linesPerFile = 10000;
+
+ private static final String EXPORT_SCHEMA_CLI_PREFIX = "ExportSchema";
+
+ private static final String DUMP_FILE_NAME_DEFAULT = "dump";
+ private static String targetFile = DUMP_FILE_NAME_DEFAULT;
+
+ private static String targetDirectory;
+
+ private static long timeout = 60000;
+
+ private static final IoTPrinter ioTPrinter = new IoTPrinter(System.out);
+
+ private static final String BASE_VIEW_TYPE = "BASE";
+ private static final String HEADER_VIEW_TYPE = "ViewType";
+ private static final String HEADER_TIMESERIES = "Timeseries";
+
+ @SuppressWarnings({
+ "squid:S3776",
+ "squid:S2093"
+ }) // Suppress high Cognitive Complexity warning, ignore try-with-resources
+ /* main function of export csv tool. */
+ public static void main(String[] args) {
+ Options options = createOptions();
+ HelpFormatter hf = new HelpFormatter();
+ CommandLine commandLine = null;
+ CommandLineParser parser = new DefaultParser();
+ hf.setOptionComparator(null); // avoid reordering
+ hf.setWidth(MAX_HELP_CONSOLE_WIDTH);
+
+ if (args == null || args.length == 0) {
+ ioTPrinter.println("Too few params input, please check the following
hint.");
+ hf.printHelp(EXPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ }
+ try {
+ commandLine = parser.parse(options, args);
+ } catch (ParseException e) {
+ ioTPrinter.println(e.getMessage());
+ hf.printHelp(EXPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ }
+ if (commandLine.hasOption(HELP_ARGS)) {
+ hf.printHelp(EXPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ }
+ int exitCode = CODE_OK;
+ try {
+ parseBasicParams(commandLine);
+ parseSpecialParams(commandLine);
+ session = new Session(host, Integer.parseInt(port), username, password);
+ session.open(false);
+ if (queryPath == null) {
+ String pathFile = commandLine.getOptionValue(TARGET_FILE_ARGS);
+ String path;
+ if (pathFile == null) {
+ LineReader lineReader =
+ JlineUtils.getLineReader(
+ new CliContext(System.in, System.out, System.err,
ExitType.EXCEPTION),
+ username,
+ host,
+ port);
+ path = lineReader.readLine(EXPORT_SCHEMA_CLI_PREFIX + "> please
input path pattern: ");
+ ioTPrinter.println(path);
+ String[] values = path.trim().split(";");
+ for (int i = 0; i < values.length; i++) {
+ if (StringUtils.isBlank(values[i])) {
+ continue;
+ } else {
+ dumpResult(values[i], i);
+ }
+ }
+ } else if (!pathFile.endsWith(".txt")) {
+ ioTPrinter.println("The file name must end with \"txt\"!");
+ hf.printHelp(EXPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ } else {
+ dumpFromPathFile(pathFile);
+ }
+ } else {
+ dumpResult(queryPath, 0);
+ }
+ } catch (IOException e) {
+ ioTPrinter.println("Failed to operate on file, because " +
e.getMessage());
+ exitCode = CODE_ERROR;
+ } catch (ArgsErrorException e) {
+ ioTPrinter.println("Invalid args: " + e.getMessage());
+ exitCode = CODE_ERROR;
+ } catch (IoTDBConnectionException e) {
+ ioTPrinter.println("Connect failed because " + e.getMessage());
+ exitCode = CODE_ERROR;
+ } finally {
+ if (session != null) {
+ try {
+ session.close();
+ } catch (IoTDBConnectionException e) {
+ exitCode = CODE_ERROR;
+ ioTPrinter.println(
+ "Encounter an error when closing session, error is: " +
e.getMessage());
+ }
+ }
+ }
+ System.exit(exitCode);
+ }
+
+ private static void parseSpecialParams(CommandLine commandLine) throws
ArgsErrorException {
+ targetDirectory = checkRequiredArg(TARGET_DIR_ARGS, TARGET_DIR_ARGS_NAME,
commandLine, null);
+ queryPath = commandLine.getOptionValue(TARGET_PATH_ARGS);
+ String timeoutString = commandLine.getOptionValue(TIMEOUT_ARGS);
+ if (timeoutString != null) {
+ timeout = Long.parseLong(timeoutString);
+ }
+ if (targetFile == null) {
+ targetFile = DUMP_FILE_NAME_DEFAULT;
+ }
+ if (!targetDirectory.endsWith("/") && !targetDirectory.endsWith("\\")) {
+ targetDirectory += File.separator;
+ }
+ if (commandLine.getOptionValue(LINES_PER_FILE_ARGS) != null) {
+ linesPerFile =
Integer.parseInt(commandLine.getOptionValue(LINES_PER_FILE_ARGS));
+ }
+ }
+
+ /**
+ * commandline option create.
+ *
+ * @return object Options
+ */
+ private static Options createOptions() {
+ Options options = createNewOptions();
+
+ Option opTargetFile =
+ Option.builder(TARGET_DIR_ARGS)
+ .required()
+ .longOpt(TARGET_DIR_ARGS_NAME)
+ .hasArg()
+ .argName(TARGET_DIR_NAME)
+ .desc("Target File Directory (required)")
+ .build();
+ options.addOption(opTargetFile);
+
+ Option targetPathPattern =
+ Option.builder(TARGET_PATH_ARGS)
+ .longOpt(TARGET_PATH_ARGS_NAME)
+ .hasArg()
+ .argName(TARGET_PATH_NAME)
+ .desc("Export Path Pattern (optional)")
+ .build();
+ options.addOption(targetPathPattern);
+
+ Option targetFileName =
+ Option.builder(TARGET_FILE_ARGS)
+ .longOpt(TARGET_FILE_ARGS_NAME)
+ .hasArg()
+ .argName(TARGET_FILE_NAME)
+ .desc("Export File Name (optional)")
+ .build();
+ options.addOption(targetFileName);
+
+ Option opLinesPerFile =
+ Option.builder(LINES_PER_FILE_ARGS)
+ .longOpt(LINES_PER_FILE_ARGS_NAME)
+ .hasArg()
+ .argName(LINES_PER_FILE_NAME)
+ .desc("Lines per dump file.")
+ .build();
+ options.addOption(opLinesPerFile);
+
+ Option opTimeout =
+ Option.builder(TIMEOUT_ARGS)
+ .longOpt(TIMEOUT_ARGS_NAME)
+ .hasArg()
+ .argName(TIMEOUT_ARGS)
+ .desc(timeout + " Timeout for session query")
+ .build();
+ options.addOption(opTimeout);
+
+ Option opHelp =
+ Option.builder(HELP_ARGS).longOpt(HELP_ARGS).desc("Display help
information").build();
+ options.addOption(opHelp);
+ return options;
+ }
+
+ /**
+ * This method will be called, if the query commands are written in a sql
file.
+ *
+ * @param pathFile sql file path
+ * @throws IOException exception
+ */
+ private static void dumpFromPathFile(String pathFile) throws IOException {
+ try (BufferedReader reader = new BufferedReader(new FileReader(pathFile)))
{
+ String path;
+ int index = 0;
+ while ((path = reader.readLine()) != null) {
+ dumpResult(path, index);
+ index++;
+ }
+ }
+ }
+
+ /**
+ * Dump files from database to CSV file.
+ *
+ * @param pattern used to be export schema
+ * @param index used to create dump file name
+ */
+ private static void dumpResult(String pattern, int index) {
+ File file = new File(targetDirectory);
+ if (!file.isDirectory()) {
+ file.mkdir();
+ }
+ final String path = targetDirectory + targetFile + index;
+ try {
+ SessionDataSet sessionDataSet =
+ session.executeQueryStatement("show timeseries " + pattern, timeout);
+ writeCsvFile(sessionDataSet, path, sessionDataSet.getColumnNames(),
linesPerFile);
+ sessionDataSet.closeOperationHandle();
+ ioTPrinter.println("Export completely!");
+ } catch (StatementExecutionException | IoTDBConnectionException |
IOException e) {
+ ioTPrinter.println("Cannot dump result because: " + e.getMessage());
+ }
+ }
+
+ @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity
warning
+ public static void writeCsvFile(
+ SessionDataSet sessionDataSet, String filePath, List<String> headers,
int linesPerFile)
+ throws IOException, IoTDBConnectionException,
StatementExecutionException {
+ int viewTypeIndex = headers.indexOf(HEADER_VIEW_TYPE);
+ int timeseriesIndex = headers.indexOf(HEADER_TIMESERIES);
+
+ int fileIndex = 0;
+ boolean hasNext = true;
+ while (hasNext) {
+ int i = 0;
+ final String finalFilePath = filePath + "_" + fileIndex + ".csv";
+ final CSVPrinterWrapper csvPrinterWrapper = new
CSVPrinterWrapper(finalFilePath);
+ while (i++ < linesPerFile) {
+ if (sessionDataSet.hasNext()) {
+ if (i == 1) {
+ csvPrinterWrapper.printRecord(HEAD_COLUMNS);
+ }
+ RowRecord rowRecord = sessionDataSet.next();
+ List<Field> fields = rowRecord.getFields();
+ if
(fields.get(timeseriesIndex).getStringValue().startsWith(SYSTEM_DATABASE)
+ ||
!fields.get(viewTypeIndex).getStringValue().equals(BASE_VIEW_TYPE)) {
+ continue;
+ }
+ HEAD_COLUMNS.forEach(
+ column -> {
+ Field field = fields.get(headers.indexOf(column));
+ String fieldStringValue = field.getStringValue();
+ if (!"null".equals(field.getStringValue())) {
+ csvPrinterWrapper.print(fieldStringValue);
+ } else {
+ csvPrinterWrapper.print("");
+ }
+ });
+ csvPrinterWrapper.println();
+ } else {
+ hasNext = false;
+ break;
+ }
+ }
+ fileIndex++;
+ csvPrinterWrapper.flush();
+ csvPrinterWrapper.close();
+ }
+ }
+}
diff --git
a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/ImportSchema.java
b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/ImportSchema.java
new file mode 100644
index 00000000000..acd55b3eb42
--- /dev/null
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/ImportSchema.java
@@ -0,0 +1,653 @@
+/*
+ * 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.tool;
+
+import org.apache.iotdb.cli.utils.IoTPrinter;
+import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.exception.ArgsErrorException;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.StatementExecutionException;
+import org.apache.iotdb.session.Session;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.file.metadata.enums.CompressionType;
+import org.apache.tsfile.file.metadata.enums.TSEncoding;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.apache.iotdb.commons.schema.SchemaConstant.SYSTEM_DATABASE;
+
+/** Import Schema CSV file. */
+public class ImportSchema extends AbstractSchemaTool {
+
+ private static final String FILE_ARGS = "s";
+ private static final String FILE_NAME = "source";
+ private static final String FILE_ARGS_NAME = "sourceDir/sourceFile";
+
+ private static final String FAILED_FILE_ARGS = "fd";
+ private static final String FAILED_FILE_NAME = "fail_dir";
+ private static final String FAILED_FILE_ARGS_NAME = "failDir";
+
+ private static final String ALIGNED_ARGS = "aligned";
+ private static Boolean aligned = false;
+
+ private static final String BATCH_POINT_SIZE_ARGS = "batch";
+ private static final String BATCH_POINT_SIZE_NAME = "batch_size";
+ private static final String BATCH_POINT_SIZE_ARGS_NAME = "batchSize";
+ private static int batchPointSize = 10_000;
+
+ private static final String CSV_SUFFIXS = "csv";
+
+ private static final String LINES_PER_FAILED_FILE_ARGS = "lpf";
+ private static final String LINES_PER_FAILED_FILE_NAME = "lines_per_file";
+ private static final String LINES_PER_FAILED_FILE_ARGS_NAME = "linesPerFile";
+ private static final String IMPORT_SCHEMA_CLI_PREFIX = "ImportSchema";
+ private static int linesPerFailedFile = 10000;
+
+ private static String targetPath;
+ private static String failedFileDirectory = null;
+
+ private static final String INSERT_CSV_MEET_ERROR_MSG = "Meet error when
insert csv because ";
+
+ private static final IoTPrinter ioTPrinter = new IoTPrinter(System.out);
+
+ /**
+ * create the commandline options.
+ *
+ * @return object Options
+ */
+ private static Options createOptions() {
+ Options options = createNewOptions();
+
+ Option opFile =
+ Option.builder(FILE_ARGS)
+ .required()
+ .longOpt(FILE_NAME)
+ .hasArg()
+ .argName(FILE_ARGS_NAME)
+ .desc(
+ "If input a file path, load a csv file, "
+ + "otherwise load all csv file under this directory
(required)")
+ .build();
+ options.addOption(opFile);
+
+ Option opFailedFile =
+ Option.builder(FAILED_FILE_ARGS)
+ .required(false)
+ .longOpt(FAILED_FILE_NAME)
+ .hasArg()
+ .argName(FAILED_FILE_ARGS_NAME)
+ .desc(
+ "Specifying a directory to save failed file, default
YOUR_CSV_FILE_PATH (optional)")
+ .build();
+ options.addOption(opFailedFile);
+
+ Option opAligned =
+ Option.builder(ALIGNED_ARGS)
+ .longOpt(ALIGNED_ARGS)
+ .desc("Whether import schema as aligned timeseries(optional)")
+ .build();
+ options.addOption(opAligned);
+
+ Option opBatchPointSize =
+ Option.builder(BATCH_POINT_SIZE_ARGS)
+ .longOpt(BATCH_POINT_SIZE_NAME)
+ .hasArg()
+ .argName(BATCH_POINT_SIZE_ARGS_NAME)
+ .desc("10000 (only not aligned optional)")
+ .build();
+ options.addOption(opBatchPointSize);
+
+ Option opFailedLinesPerFile =
+ Option.builder(LINES_PER_FAILED_FILE_ARGS)
+ .longOpt(LINES_PER_FAILED_FILE_NAME)
+ .hasArg()
+ .argName(LINES_PER_FAILED_FILE_ARGS_NAME)
+ .desc("Lines per failed file")
+ .build();
+ options.addOption(opFailedLinesPerFile);
+
+ Option opHelp =
+ Option.builder(HELP_ARGS).longOpt(HELP_ARGS).desc("Display help
information").build();
+ options.addOption(opHelp);
+ return options;
+ }
+
+ /**
+ * parse optional params
+ *
+ * @param commandLine
+ */
+ private static void parseSpecialParams(CommandLine commandLine) throws
ArgsErrorException {
+ targetPath = commandLine.getOptionValue(FILE_ARGS);
+ if (commandLine.getOptionValue(BATCH_POINT_SIZE_ARGS) != null) {
+ batchPointSize =
Integer.parseInt(commandLine.getOptionValue(BATCH_POINT_SIZE_ARGS));
+ }
+ if (commandLine.getOptionValue(FAILED_FILE_ARGS) != null) {
+ failedFileDirectory = commandLine.getOptionValue(FAILED_FILE_ARGS);
+ File file = new File(failedFileDirectory);
+ if (!file.isDirectory()) {
+ file.mkdir();
+ failedFileDirectory = file.getAbsolutePath() + File.separator;
+ }
+ }
+ if (commandLine.getOptionValue(ALIGNED_ARGS) != null) {
+ aligned = Boolean.valueOf(commandLine.getOptionValue(ALIGNED_ARGS));
+ }
+ if (commandLine.getOptionValue(LINES_PER_FAILED_FILE_ARGS) != null) {
+ linesPerFailedFile =
Integer.parseInt(commandLine.getOptionValue(LINES_PER_FAILED_FILE_ARGS));
+ }
+ }
+
+ public static void main(String[] args) throws IoTDBConnectionException {
+ Options options = createOptions();
+ HelpFormatter hf = new HelpFormatter();
+ hf.setOptionComparator(null);
+ hf.setWidth(MAX_HELP_CONSOLE_WIDTH);
+ CommandLine commandLine = null;
+ CommandLineParser parser = new DefaultParser();
+
+ if (args == null || args.length == 0) {
+ ioTPrinter.println("Too few params input, please check the following
hint.");
+ hf.printHelp(IMPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ }
+ try {
+ commandLine = parser.parse(options, args);
+ } catch (org.apache.commons.cli.ParseException e) {
+ ioTPrinter.println("Parse error: " + e.getMessage());
+ hf.printHelp(IMPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ }
+ if (commandLine.hasOption(HELP_ARGS)) {
+ hf.printHelp(IMPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ }
+ try {
+ parseBasicParams(commandLine);
+ String filename = commandLine.getOptionValue(FILE_ARGS);
+ if (filename == null) {
+ hf.printHelp(IMPORT_SCHEMA_CLI_PREFIX, options, true);
+ System.exit(CODE_ERROR);
+ }
+ parseSpecialParams(commandLine);
+ } catch (ArgsErrorException e) {
+ ioTPrinter.println("Args error: " + e.getMessage());
+ System.exit(CODE_ERROR);
+ } catch (Exception e) {
+ ioTPrinter.println("Encounter an error, because: " + e.getMessage());
+ System.exit(CODE_ERROR);
+ }
+ System.exit(importFromTargetPath(host, Integer.parseInt(port), username,
password, targetPath));
+ }
+
+ /**
+ * Specifying a CSV file or a directory including CSV files that you want to
import. This method
+ * can be offered to console cli to implement importing CSV file by command.
+ *
+ * @param host
+ * @param port
+ * @param username
+ * @param password
+ * @param targetPath a CSV file or a directory including CSV files
+ * @return the status code
+ * @throws IoTDBConnectionException
+ */
+ @SuppressWarnings({"squid:S2093"}) // ignore try-with-resources
+ public static int importFromTargetPath(
+ String host, int port, String username, String password, String
targetPath) {
+ try {
+ session = new Session(host, port, username, password, false);
+ session.open(false);
+ File file = new File(targetPath);
+ if (file.isFile()) {
+ importFromSingleFile(file);
+ } else if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ if (files == null) {
+ return CODE_OK;
+ }
+ // 按文件名排序
+ Arrays.sort(files, (f1, f2) -> f1.getName().compareTo(f2.getName()));
+ for (File subFile : files) {
+ if (subFile.isFile()) {
+ importFromSingleFile(subFile);
+ }
+ }
+ } else {
+ ioTPrinter.println("File not found!");
+ return CODE_ERROR;
+ }
+ } catch (IoTDBConnectionException e) {
+ ioTPrinter.println("Encounter an error when connecting to server,
because " + e.getMessage());
+ return CODE_ERROR;
+ } finally {
+ if (session != null) {
+ try {
+ session.close();
+ } catch (IoTDBConnectionException e) {
+ ;
+ }
+ }
+ }
+ return CODE_OK;
+ }
+
+ /**
+ * import the CSV file and load headers and records.
+ *
+ * @param file the File object of the CSV file that you want to import.
+ */
+ private static void importFromSingleFile(File file) {
+ if (file.getName().endsWith(CSV_SUFFIXS)) {
+ try {
+ CSVParser csvRecords = readCsvFile(file.getAbsolutePath());
+ List<String> headerNames = csvRecords.getHeaderNames();
+ Stream<CSVRecord> records = csvRecords.stream();
+ if (headerNames.isEmpty()) {
+ ioTPrinter.println(file.getName() + " : Empty file!");
+ return;
+ }
+ if (!checkHeader(headerNames)) {
+ return;
+ }
+ String failedFilePath = null;
+ if (failedFileDirectory == null) {
+ failedFilePath = file.getAbsolutePath() + ".failed";
+ } else {
+ failedFilePath = failedFileDirectory + file.getName() + ".failed";
+ }
+ writeScheme(file.getName(), headerNames, records, failedFilePath);
+ } catch (IOException | IllegalPathException e) {
+ ioTPrinter.println(
+ file.getName() + " : CSV file read exception because: " +
e.getMessage());
+ }
+ } else {
+ ioTPrinter.println(file.getName() + " : The file name must end with
\"csv\"!");
+ }
+ }
+
+ /**
+ * if the data is aligned by time, the data will be written by this method.
+ *
+ * @param headerNames the header names of CSV file
+ * @param records the records of CSV file
+ * @param failedFilePath the directory to save the failed files
+ */
+ @SuppressWarnings("squid:S3776")
+ private static void writeScheme(
+ String fileName, List<String> headerNames, Stream<CSVRecord> records,
String failedFilePath)
+ throws IllegalPathException {
+ List<String> paths = new ArrayList<>();
+ List<TSDataType> dataTypes = new ArrayList<>();
+ List<TSEncoding> encodings = new ArrayList<>();
+ List<CompressionType> compressors = new ArrayList<>();
+ List<String> pathsWithAlias = new ArrayList<>();
+ List<TSDataType> dataTypesWithAlias = new ArrayList<>();
+ List<TSEncoding> encodingsWithAlias = new ArrayList<>();
+ List<CompressionType> compressorsWithAlias = new ArrayList<>();
+ List<String> measurementAlias = new ArrayList<>();
+
+ AtomicReference<Boolean> hasStarted = new AtomicReference<>(false);
+ AtomicInteger pointSize = new AtomicInteger(0);
+ ArrayList<List<Object>> failedRecords = new ArrayList<>();
+ records.forEach(
+ recordObj -> {
+ boolean failed = false;
+ if (!aligned) {
+ if (Boolean.FALSE.equals(hasStarted.get())) {
+ hasStarted.set(true);
+ } else if (pointSize.get() >= batchPointSize) {
+ try {
+ writeAndEmptyDataSet(
+ paths,
+ dataTypes,
+ encodings,
+ compressors,
+ null,
+ null,
+ null,
+ measurementAlias,
+ 3);
+ writeAndEmptyDataSet(
+ pathsWithAlias,
+ dataTypesWithAlias,
+ encodingsWithAlias,
+ compressorsWithAlias,
+ null,
+ null,
+ null,
+ null,
+ 3);
+ paths.clear();
+ dataTypes.clear();
+ encodings.clear();
+ compressors.clear();
+ measurementAlias.clear();
+ pointSize.set(0);
+ } catch (Exception e) {
+ failedRecords.add((List<Object>) (List<?>) paths);
+ }
+ }
+ } else {
+ paths.clear();
+ dataTypes.clear();
+ encodings.clear();
+ compressors.clear();
+ measurementAlias.clear();
+ }
+ String path =
recordObj.get(headerNames.indexOf(HEAD_COLUMNS.get(0)));
+ String alias =
recordObj.get(headerNames.indexOf(HEAD_COLUMNS.get(1)));
+ String dataTypeRaw =
recordObj.get(headerNames.indexOf(HEAD_COLUMNS.get(2)));
+ TSDataType dataType = typeInfer(dataTypeRaw);
+ String encodingTypeRaw =
recordObj.get(headerNames.indexOf(HEAD_COLUMNS.get(3)));
+ TSEncoding encodingType = encodingInfer(encodingTypeRaw);
+ String compressionTypeRaw =
recordObj.get(headerNames.indexOf(HEAD_COLUMNS.get(4)));
+ CompressionType compressionType = compressInfer(compressionTypeRaw);
+ if (StringUtils.isBlank(path) ||
path.trim().startsWith(SYSTEM_DATABASE)) {
+ ioTPrinter.println(
+ String.format(
+ "Line '%s', column '%s': illegal path %s",
+ recordObj.getRecordNumber(), headerNames, path));
+ failedRecords.add(recordObj.stream().collect(Collectors.toList()));
+ failed = true;
+ } else if (ObjectUtils.isEmpty(dataType)) {
+ ioTPrinter.println(
+ String.format(
+ "Line '%s', column '%s': '%s' unknown dataType %n",
+ recordObj.getRecordNumber(), path, dataTypeRaw));
+ failedRecords.add(recordObj.stream().collect(Collectors.toList()));
+ failed = true;
+ } else if (ObjectUtils.isEmpty(encodingType)) {
+ ioTPrinter.println(
+ String.format(
+ "Line '%s', column '%s': '%s' unknown encodingType %n",
+ recordObj.getRecordNumber(), path, encodingTypeRaw));
+ failedRecords.add(recordObj.stream().collect(Collectors.toList()));
+ failed = true;
+ } else if (ObjectUtils.isEmpty(compressionType)) {
+ ioTPrinter.println(
+ String.format(
+ "Line '%s', column '%s': '%s' unknown compressionType %n",
+ recordObj.getRecordNumber(), path, compressionTypeRaw));
+ failedRecords.add(recordObj.stream().collect(Collectors.toList()));
+ failed = true;
+ } else {
+ if (StringUtils.isBlank(alias)) {
+ paths.add(path);
+ dataTypes.add(dataType);
+ encodings.add(encodingType);
+ compressors.add(compressionType);
+ } else {
+ pathsWithAlias.add(path);
+ dataTypesWithAlias.add(dataType);
+ encodingsWithAlias.add(encodingType);
+ compressorsWithAlias.add(compressionType);
+ measurementAlias.add(alias);
+ }
+ pointSize.getAndIncrement();
+ }
+ if (!failed && aligned) {
+ String deviceId = path.substring(0, path.lastIndexOf("."));
+ paths.add(0, path.substring(deviceId.length() + 1));
+ writeAndEmptyDataSetAligned(
+ deviceId, paths, dataTypes, encodings, compressors,
measurementAlias, 3);
+ }
+ });
+ try {
+ if (CollectionUtils.isNotEmpty(paths)) {
+ writeAndEmptyDataSet(paths, dataTypes, encodings, compressors, null,
null, null, null, 3);
+ }
+ } catch (Exception e) {
+ paths.forEach(t -> failedRecords.add(Collections.singletonList(t)));
+ }
+ try {
+ if (CollectionUtils.isNotEmpty(pathsWithAlias)) {
+ writeAndEmptyDataSet(
+ pathsWithAlias,
+ dataTypesWithAlias,
+ encodingsWithAlias,
+ compressorsWithAlias,
+ null,
+ null,
+ null,
+ measurementAlias,
+ 3);
+ }
+ } catch (Exception e) {
+ pathsWithAlias.forEach(t ->
failedRecords.add(Collections.singletonList(t)));
+ }
+ pointSize.set(0);
+ if (!failedRecords.isEmpty()) {
+ writeFailedLinesFile(failedFilePath, failedRecords);
+ }
+ if (Boolean.TRUE.equals(hasStarted.get())) {
+ if (!failedRecords.isEmpty()) {
+ ioTPrinter.println(fileName + " : Import completely fail!");
+ } else {
+ ioTPrinter.println(fileName + " : Import completely successful!");
+ }
+ } else {
+ ioTPrinter.println(fileName + " : No records!");
+ }
+ }
+
+ private static boolean checkHeader(List<String> headerNames) {
+ if (CollectionUtils.isNotEmpty(headerNames)
+ && new HashSet<>(headerNames).size() == HEAD_COLUMNS.size()) {
+ List<String> strangers =
+ headerNames.stream().filter(t ->
!HEAD_COLUMNS.contains(t)).collect(Collectors.toList());
+ if (CollectionUtils.isNotEmpty(strangers)) {
+ ioTPrinter.println(
+ "The header of the CSV file to be imported is illegal. The correct
format is \"Timeseries, Alibaba, DataType, Encoding, Compression\"!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static void writeFailedLinesFile(
+ String failedFilePath, ArrayList<List<Object>> failedRecords) {
+ int fileIndex = 0;
+ int from = 0;
+ int failedRecordsSize = failedRecords.size();
+ int restFailedRecords = failedRecordsSize;
+ while (from < failedRecordsSize) {
+ int step = Math.min(restFailedRecords, linesPerFailedFile);
+ writeCsvFile(failedRecords.subList(from, from + step), failedFilePath +
"_" + fileIndex++);
+ from += step;
+ restFailedRecords -= step;
+ }
+ }
+
+ private static void writeAndEmptyDataSet(
+ List<String> paths,
+ List<TSDataType> dataTypes,
+ List<TSEncoding> encodings,
+ List<CompressionType> compressors,
+ List<Map<String, String>> propsList,
+ List<Map<String, String>> tagsList,
+ List<Map<String, String>> attributesList,
+ List<String> measurementAliasList,
+ int retryTime)
+ throws StatementExecutionException {
+ try {
+ session.createMultiTimeseries(
+ paths,
+ dataTypes,
+ encodings,
+ compressors,
+ propsList,
+ tagsList,
+ attributesList,
+ measurementAliasList);
+ } catch (IoTDBConnectionException e) {
+ if (retryTime > 0) {
+ try {
+ session.open();
+ } catch (IoTDBConnectionException ex) {
+ ioTPrinter.println(INSERT_CSV_MEET_ERROR_MSG + e.getMessage());
+ }
+ writeAndEmptyDataSet(
+ paths,
+ dataTypes,
+ encodings,
+ compressors,
+ propsList,
+ tagsList,
+ attributesList,
+ measurementAliasList,
+ --retryTime);
+ }
+ } catch (StatementExecutionException e) {
+ try {
+ session.close();
+ } catch (IoTDBConnectionException ex) {
+ // do nothing
+ }
+ throw e;
+ }
+ }
+
+ private static void writeAndEmptyDataSetAligned(
+ String deviceId,
+ List<String> measurements,
+ List<TSDataType> dataTypes,
+ List<TSEncoding> encodings,
+ List<CompressionType> compressors,
+ List<String> measurementAliasList,
+ int retryTime) {
+ try {
+ session.createAlignedTimeseries(
+ deviceId, measurements, dataTypes, encodings, compressors,
measurementAliasList);
+ } catch (IoTDBConnectionException e) {
+ if (retryTime > 0) {
+ try {
+ session.open();
+ } catch (IoTDBConnectionException ex) {
+ ioTPrinter.println(INSERT_CSV_MEET_ERROR_MSG + e.getMessage());
+ }
+ writeAndEmptyDataSetAligned(
+ deviceId,
+ measurements,
+ dataTypes,
+ encodings,
+ compressors,
+ measurementAliasList,
+ --retryTime);
+ }
+ } catch (StatementExecutionException e) {
+ ioTPrinter.println(INSERT_CSV_MEET_ERROR_MSG + e.getMessage());
+ try {
+ session.close();
+ } catch (IoTDBConnectionException ex) {
+ // do nothing
+ }
+ System.exit(1);
+ } finally {
+ deviceId = null;
+ measurements.clear();
+ dataTypes.clear();
+ encodings.clear();
+ compressors.clear();
+ measurementAliasList.clear();
+ }
+ }
+
+ /**
+ * read data from the CSV file
+ *
+ * @param path
+ * @return CSVParser csv parser
+ * @throws IOException when reading the csv file failed.
+ */
+ private static CSVParser readCsvFile(String path) throws IOException {
+ return CSVFormat.Builder.create(CSVFormat.DEFAULT)
+ .setHeader()
+ .setSkipHeaderRecord(true)
+ .setQuote('`')
+ .setEscape('\\')
+ .setIgnoreEmptyLines(true)
+ .build()
+ .parse(new InputStreamReader(new FileInputStream(path)));
+ }
+
+ /**
+ * @param typeStr
+ * @return
+ */
+ private static TSDataType typeInfer(String typeStr) {
+ try {
+ if (StringUtils.isNotBlank(typeStr)) {
+ return TSDataType.valueOf(typeStr);
+ }
+ } catch (IllegalArgumentException e) {
+ ;
+ }
+ return null;
+ }
+
+ private static CompressionType compressInfer(String compressionType) {
+ try {
+ if (StringUtils.isNotBlank(compressionType)) {
+ return CompressionType.valueOf(compressionType);
+ }
+ } catch (IllegalArgumentException e) {
+ ;
+ }
+ return null;
+ }
+
+ private static TSEncoding encodingInfer(String encodingType) {
+ try {
+ if (StringUtils.isNotBlank(encodingType)) {
+ return TSEncoding.valueOf(encodingType);
+ }
+ } catch (IllegalArgumentException e) {
+ ;
+ }
+ return null;
+ }
+}