diff --git a/LICENSE-binary b/LICENSE-binary
index 14264f0f701d2..6c79ced653d6d 100644
--- a/LICENSE-binary
+++ b/LICENSE-binary
@@ -304,7 +304,7 @@ org.yaml:snakeyaml:2.6
BSD 3-Clause
------------
org.antlr:antlr4-runtime:4.13.2
-org.jline:jline:3.26.2
+org.jline:jline:3.30.17
org.ow2.asm:asm:9.3
diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java
index 8962a5a5526b2..6546e36e62360 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java
@@ -283,7 +283,7 @@ private static String buildJdbcUrl(String host, String port) {
return Config.IOTDB_URL_PREFIX + formatEndpoint(host, port) + "/";
}
- private static boolean readerReadLine(CliContext ctx, IoTDBConnection connection) {
+ static boolean readerReadLine(CliContext ctx, IoTDBConnection connection) {
String s;
try {
s = ctx.getLineReader().readLine(cliPrefix + "> ", null);
@@ -295,8 +295,13 @@ private static boolean readerReadLine(CliContext ctx, IoTDBConnection connection
// Exit on signal INT requires confirmation.
readLine(ctx);
} catch (EndOfFileException e) {
- // Exit on EOF (usually by pressing CTRL+D).
- ctx.exit(CODE_OK);
+ // JLine 3.30.16 and later can report a SIGINT-interrupted PTY read as EOF.
+ if (Thread.interrupted()) {
+ readLine(ctx);
+ } else {
+ // Exit on EOF (usually by pressing CTRL+D).
+ ctx.exit(CODE_OK);
+ }
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("history")) {
return false;
diff --git a/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/CliTest.java b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/CliTest.java
new file mode 100644
index 0000000000000..6681fb7af5a1b
--- /dev/null
+++ b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/CliTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.cli;
+
+import org.apache.iotdb.cli.i18n.CliMessages;
+import org.apache.iotdb.cli.utils.CliContext;
+
+import org.jline.reader.EndOfFileException;
+import org.jline.reader.LineReader;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class CliTest {
+ private final CliContext context = mock(CliContext.class);
+ private final LineReader reader = mock(LineReader.class);
+
+ @Before
+ public void setUp() {
+ when(context.getLineReader()).thenReturn(reader);
+ }
+
+ @Test
+ public void testInterruptedEofRequiresExitConfirmation() {
+ when(reader.readLine(anyString(), isNull()))
+ .thenAnswer(
+ invocation -> {
+ Thread.currentThread().interrupt();
+ throw new EndOfFileException();
+ });
+ when(reader.readLine(anyString(), eq('\0')))
+ .thenAnswer(
+ invocation -> {
+ assertFalse(Thread.currentThread().isInterrupted());
+ return "";
+ });
+
+ try {
+ assertFalse(Cli.readerReadLine(context, null));
+ verify(reader)
+ .readLine(
+ CliMessages.EXCEPTION_PRESS_CTRL_C_AGAIN_TO_EXIT_OR_PRESS_ENTER_TO_CONTINUE_8CA49A92,
+ '\0');
+ verify(context, never()).exit(anyInt());
+ } finally {
+ Thread.interrupted();
+ }
+ }
+
+ @Test
+ public void testEofExitsWithoutConfirmation() {
+ when(reader.readLine(anyString(), isNull())).thenThrow(new EndOfFileException());
+
+ Cli.readerReadLine(context, null);
+
+ verify(context).exit(AbstractCli.CODE_OK);
+ verify(reader, never()).readLine(anyString(), eq('\0'));
+ }
+}
diff --git a/pom.xml b/pom.xml
index 9dfb37abed865..6286660c83c74 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,7 +99,7 @@
1.0-1
3.1.11
12.0.41
- 3.26.2
+ 3.30.17
5.14.0
3.1
4.13.2