Skip to content

Commit b545d19

Browse files
LA-Tothabstractdog
authored andcommitted
HIVE-25495: Upgrade JLine to version 3
1 parent 89e7d4a commit b545d19

File tree

33 files changed

+495
-379
lines changed

33 files changed

+495
-379
lines changed

beeline/pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
<artifactId>jackson-core</artifactId>
8181
</dependency>
8282
<dependency>
83-
<groupId>jline</groupId>
83+
<groupId>org.jline</groupId>
8484
<artifactId>jline</artifactId>
8585
</dependency>
8686
<dependency>
@@ -114,6 +114,11 @@
114114
</exclusion>
115115
</exclusions>
116116
</dependency>
117+
<dependency>
118+
<groupId>org.apache.hadoop</groupId>
119+
<artifactId>hadoop-hdfs</artifactId>
120+
<scope>test</scope>
121+
</dependency>
117122
<dependency>
118123
<groupId>org.apache.thrift</groupId>
119124
<artifactId>libthrift</artifactId>

beeline/src/java/org/apache/hive/beeline/AbstractCommandHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
import java.util.LinkedList;
2727
import java.util.List;
2828

29-
import jline.console.completer.Completer;
30-
import jline.console.completer.NullCompleter;
29+
import org.jline.reader.Completer;
30+
import org.jline.reader.impl.completer.NullCompleter;
3131

3232
/**
3333
* An abstract implementation of CommandHandler.

beeline/src/java/org/apache/hive/beeline/BeeLine.java

Lines changed: 99 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.io.EOFException;
2929
import java.io.File;
3030
import java.io.FileInputStream;
31+
import java.io.IOError;
3132
import java.io.IOException;
3233
import java.io.InputStream;
3334
import java.io.InputStreamReader;
@@ -100,6 +101,7 @@
100101
import org.apache.hive.beeline.hs2connection.HS2ConnectionFileUtils;
101102
import org.apache.hive.beeline.hs2connection.HiveSiteHS2ConnectionFileParser;
102103
import org.apache.hive.beeline.hs2connection.UserHS2ConnectionFileParser;
104+
import org.apache.hive.common.util.MatchingStringsCompleter;
103105
import org.apache.hive.common.util.ShutdownHookManager;
104106
import org.apache.hive.common.util.HiveStringUtils;
105107
import org.apache.hive.jdbc.HiveConnection;
@@ -110,11 +112,18 @@
110112

111113
import com.google.common.annotations.VisibleForTesting;
112114

113-
import jline.console.ConsoleReader;
114-
import jline.console.completer.Completer;
115-
import jline.console.completer.FileNameCompleter;
116-
import jline.console.completer.StringsCompleter;
117-
import jline.console.history.FileHistory;
115+
import org.jline.reader.Completer;
116+
import org.jline.reader.EndOfFileException;
117+
import org.jline.reader.History;
118+
import org.jline.reader.LineReader;
119+
import org.jline.reader.LineReaderBuilder;
120+
import org.jline.reader.impl.LineReaderImpl;
121+
import org.jline.reader.impl.history.DefaultHistory;
122+
import org.jline.terminal.Terminal;
123+
import org.jline.terminal.TerminalBuilder;
124+
125+
import static org.jline.builtins.Completers.FileNameCompleter;
126+
118127

119128
/**
120129
* A console SQL shell with command completion.
@@ -151,14 +160,14 @@ public class BeeLine implements Closeable {
151160
private OutputFile recordOutputFile = null;
152161
private PrintStream outputStream = new PrintStream(System.out, true);
153162
private PrintStream errorStream = new PrintStream(System.err, true);
154-
private InputStream inputStream = System.in;
155-
private ConsoleReader consoleReader;
163+
private Terminal terminal;
164+
private LineReader lineReader;
156165
private List<String> batch = null;
157166
private final Reflector reflector = new Reflector(this);
158167
private String dbName = null;
159168
private String currentDatabase = null;
160169

161-
private FileHistory history;
170+
private History history;
162171
// Indicates if this instance of beeline is running in compatibility mode, or beeline mode
163172
private boolean isBeeLine = true;
164173

@@ -204,7 +213,7 @@ public class BeeLine implements Closeable {
204213
new ReflectiveCommandHandler(this, new String[] {"quit", "done", "exit"},
205214
null),
206215
new ReflectiveCommandHandler(this, new String[] {"connect", "open"},
207-
new Completer[] {new StringsCompleter(getConnectionURLExamples())}),
216+
new Completer[] {new MatchingStringsCompleter(getConnectionURLExamples())}),
208217
new ReflectiveCommandHandler(this, new String[] {"describe"},
209218
new Completer[] {new TableNameCompletor(this)}),
210219
new ReflectiveCommandHandler(this, new String[] {"indexes"},
@@ -233,7 +242,7 @@ public class BeeLine implements Closeable {
233242
null),
234243
new ReflectiveCommandHandler(this, new String[] {"metadata"},
235244
new Completer[] {
236-
new StringsCompleter(getMetadataMethodNames())}),
245+
new MatchingStringsCompleter(getMetadataMethodNames())}),
237246
new ReflectiveCommandHandler(this, new String[] {"nativesql"},
238247
null),
239248
new ReflectiveCommandHandler(this, new String[] {"dbinfo"},
@@ -263,9 +272,9 @@ public class BeeLine implements Closeable {
263272
new ReflectiveCommandHandler(this, new String[] {"closeall"},
264273
null),
265274
new ReflectiveCommandHandler(this, new String[] {"isolation"},
266-
new Completer[] {new StringsCompleter(getIsolationLevels())}),
275+
new Completer[] {new MatchingStringsCompleter(getIsolationLevels())}),
267276
new ReflectiveCommandHandler(this, new String[] {"outputformat"},
268-
new Completer[] {new StringsCompleter(
277+
new Completer[] {new MatchingStringsCompleter(
269278
formats.keySet().toArray(new String[0]))}),
270279
new ReflectiveCommandHandler(this, new String[] {"autocommit"},
271280
null),
@@ -309,9 +318,9 @@ public class BeeLine implements Closeable {
309318

310319
static {
311320
try {
312-
Class.forName("jline.console.ConsoleReader");
321+
Class.forName("org.jline.reader.LineReader");
313322
} catch (Throwable t) {
314-
throw new ExceptionInInitializerError("jline-missing");
323+
throw new ExceptionInInitializerError("jline3-missing");
315324
}
316325
}
317326

@@ -400,7 +409,7 @@ public class BeeLine implements Closeable {
400409
.withLongOpt("help")
401410
.withDescription("Display this message")
402411
.create('h'));
403-
412+
404413
// -getUrlsFromBeelineSite
405414
options.addOption(OptionBuilder
406415
.withLongOpt("getUrlsFromBeelineSite")
@@ -433,7 +442,6 @@ public class BeeLine implements Closeable {
433442
.create());
434443
}
435444

436-
437445
static Manifest getManifest() throws IOException {
438446
URL base = BeeLine.class.getResource("/META-INF/MANIFEST.MF");
439447
URLConnection c = base.openConnection();
@@ -569,19 +577,15 @@ public BeeLine() {
569577
public BeeLine(boolean isBeeLine) {
570578
this.isBeeLine = isBeeLine;
571579
this.signalHandler = new SunSignalHandler(this);
572-
this.shutdownHook = new Runnable() {
573-
@Override
574-
public void run() {
575-
try {
576-
if (history != null) {
577-
history.setMaxSize(getOpts().getMaxHistoryRows());
578-
history.flush();
579-
}
580-
} catch (IOException e) {
581-
error(e);
582-
} finally {
583-
close();
580+
this.shutdownHook = () -> {
581+
try {
582+
if (history != null) {
583+
history.save();
584584
}
585+
} catch (IOException e) {
586+
error(e);
587+
} finally {
588+
close();
585589
}
586590
};
587591
}
@@ -863,7 +867,7 @@ private boolean connectUsingArgs(BeelineParser beelineParser, CommandLine cl) {
863867
getOpts().setHelpAsked(true);
864868
return true;
865869
}
866-
870+
867871
if (cl.hasOption("getUrlsFromBeelineSite")) {
868872
printBeelineSiteUrls();
869873
getOpts().setBeelineSiteUrlsAsked(true);
@@ -937,8 +941,8 @@ private boolean connectUsingArgs(BeelineParser beelineParser, CommandLine cl) {
937941
String propertyFile = cl.getOptionValue("property-file");
938942
if (propertyFile != null) {
939943
try {
940-
this.consoleReader = new ConsoleReader();
941-
} catch (IOException e) {
944+
this.lineReader = LineReaderBuilder.builder().build();
945+
} catch (IOError e) {
942946
handleException(e);
943947
}
944948
if (!dispatch("!properties " + propertyFile)) {
@@ -980,7 +984,7 @@ private void printBeelineSiteUrls() {
980984
}
981985
}
982986
}
983-
987+
984988
private boolean isZkBasedUrl(String urlFromBeelineSite) {
985989
String zkJdbcUriParam = ("serviceDiscoveryMode=zooKeeper").toLowerCase();
986990
if (urlFromBeelineSite.toLowerCase().contains(zkJdbcUriParam)) {
@@ -1116,9 +1120,9 @@ public int begin(String[] args, InputStream inputStream, boolean keepHistory) th
11161120
//add shutdown hook to cleanup the beeline for smooth exit
11171121
addBeelineShutdownHook();
11181122

1119-
//this method also initializes the consoleReader which is
1123+
//this method also initializes the lineReader which is
11201124
//needed by initArgs for certain execution paths
1121-
ConsoleReader reader = initializeConsoleReader(inputStream);
1125+
initializeLineReader(inputStream);
11221126
if (isBeeLine) {
11231127
int code = initArgs(args);
11241128
if (code != 0) {
@@ -1146,7 +1150,7 @@ public int begin(String[] args, InputStream inputStream, boolean keepHistory) th
11461150
} catch (Exception e) {
11471151
// ignore
11481152
}
1149-
return execute(reader, false);
1153+
return execute(lineReader, false);
11501154
}
11511155

11521156
/*
@@ -1350,7 +1354,7 @@ private int executeFile(String fileName) {
13501354
}
13511355
fileStream = fs.open(path);
13521356
}
1353-
return execute(initializeConsoleReader(fileStream), !getOpts().getForce());
1357+
return execute(initializeLineReader(fileStream), !getOpts().getForce());
13541358
} catch (Throwable t) {
13551359
handleException(t);
13561360
return ERRNO_OTHER;
@@ -1359,16 +1363,17 @@ private int executeFile(String fileName) {
13591363
}
13601364
}
13611365

1362-
private int execute(ConsoleReader reader, boolean exitOnError) {
1366+
private int execute(LineReader reader, boolean exitOnError) {
13631367
int lastExecutionResult = ERRNO_OK;
13641368
Character mask = (System.getProperty("jline.terminal", "").equals("jline.UnsupportedTerminal")) ? null
1365-
: ConsoleReader.NULL_MASK;
1369+
: LineReaderImpl.NULL_MASK;
13661370

1371+
String line;
13671372
while (!exit) {
13681373
try {
13691374
// Execute one instruction; terminate on executing a script if there is an error
13701375
// in silent mode, prevent the query and prompt being echoed back to terminal
1371-
String line = (getOpts().isSilent() && getOpts().getScriptFile() != null) ? reader
1376+
line = (getOpts().isSilent() && getOpts().getScriptFile() != null) ? reader
13721377
.readLine(null, mask) : reader.readLine(getPrompt());
13731378

13741379
// trim line
@@ -1384,7 +1389,17 @@ private int execute(ConsoleReader reader, boolean exitOnError) {
13841389
} else if (line != null) {
13851390
lastExecutionResult = ERRNO_OK;
13861391
}
1387-
1392+
} catch (EndOfFileException t) {
1393+
/*
1394+
* If you're reading from a normal file (not from standard input or a terminal), JLine might raise an
1395+
* EndOfFileException when it reaches the end of the file. JLine uses readLine() for reading input, and it
1396+
* expects the input source to provide data interactively. When reading from a file, it might misinterpret
1397+
* the EOF condition.
1398+
* It's unlikely that this catch-and-return-OK block masks a real issue.
1399+
* In interactive usage, EndOfFileException is not triggered, and in script file mode (-f),
1400+
* any potential corruption would typically surface as a failing command.
1401+
*/
1402+
return lastExecutionResult;
13881403
} catch (Throwable t) {
13891404
handleException(t);
13901405
return ERRNO_OTHER;
@@ -1403,48 +1418,64 @@ private void setupHistory() throws IOException {
14031418
return;
14041419
}
14051420

1406-
this.history = new FileHistory(new File(getOpts().getHistoryFile()));
1421+
this.history = new DefaultHistory();
14071422
}
14081423

14091424
private void addBeelineShutdownHook() throws IOException {
14101425
// add shutdown hook to flush the history to history file and it also close all open connections
14111426
ShutdownHookManager.addShutdownHook(getShutdownHook());
14121427
}
14131428

1414-
public ConsoleReader initializeConsoleReader(InputStream inputStream) throws IOException {
1415-
if (inputStream != null) {
1416-
// ### NOTE: fix for sf.net bug 879425.
1417-
// Working around an issue in jline-2.1.2, see https://github.com/jline/jline/issues/10
1418-
// by appending a newline to the end of inputstream
1419-
InputStream inputStreamAppendedNewline = new SequenceInputStream(inputStream,
1420-
new ByteArrayInputStream((new String("\n")).getBytes()));
1421-
consoleReader = new ConsoleReader(inputStreamAppendedNewline, getErrorStream());
1422-
consoleReader.setCopyPasteDetection(true); // jline will detect if <tab> is regular character
1423-
} else {
1424-
consoleReader = new ConsoleReader(getInputStream(), getErrorStream());
1425-
}
1426-
1427-
//disable the expandEvents for the purpose of backward compatibility
1428-
consoleReader.setExpandEvents(false);
1429+
public LineReader initializeLineReader(InputStream inputStream) throws IOException {
1430+
final LineReaderBuilder builder = LineReaderBuilder.builder();
1431+
terminal = buildTerminal(prepareInputStream(inputStream));
1432+
builder.terminal(terminal);
14291433

1434+
if (inputStream instanceof FileInputStream || inputStream instanceof FSDataInputStream) {
1435+
// from script.. no need to load history and no need of completer, either
1436+
lineReader = builder.build();
1437+
return lineReader;
1438+
}
14301439
try {
14311440
// now set the output for the history
14321441
if (this.history != null) {
1433-
consoleReader.setHistory(this.history);
1434-
} else {
1435-
consoleReader.setHistoryEnabled(false);
1442+
builder.history(this.history);
1443+
builder.variable(LineReader.HISTORY_FILE, new File(getOpts().getHistoryFile()));
1444+
builder.variable(LineReader.HISTORY_FILE_SIZE, getOpts().getMaxHistoryRows());
1445+
// in-memory keep more data, but at least 500 entries
1446+
builder.variable(LineReader.HISTORY_SIZE, Math.max(500, 3 * getOpts().getMaxHistoryRows()));
14361447
}
14371448
} catch (Exception e) {
14381449
handleException(e);
14391450
}
14401451

1441-
if (inputStream instanceof FileInputStream || inputStream instanceof FSDataInputStream) {
1442-
// from script.. no need to load history and no need of completer, either
1443-
return consoleReader;
1452+
builder.completer(new BeeLineCompleter(this));
1453+
lineReader = builder.build();
1454+
lineReader.unsetOpt(LineReader.Option.HISTORY_TIMESTAMPED);
1455+
1456+
if (this.history != null) {
1457+
this.history.attach(lineReader);
1458+
}
1459+
1460+
return lineReader;
1461+
1462+
}
1463+
1464+
private InputStream prepareInputStream(InputStream inputStream) {
1465+
if (inputStream != null) {
1466+
inputStream = new SequenceInputStream(inputStream,
1467+
new ByteArrayInputStream((new String("\n")).getBytes()));
14441468
}
1469+
return inputStream;
1470+
}
14451471

1446-
consoleReader.addCompleter(new BeeLineCompleter(this));
1447-
return consoleReader;
1472+
protected Terminal buildTerminal(InputStream inputStream) throws IOException {
1473+
if (inputStream != null) { // typically when there is a file script to read from
1474+
return TerminalBuilder.builder().streams(inputStream, getErrorStream()).build();
1475+
} else { // no input stream, normal operation: proper behavior needs a system terminal
1476+
// system terminal can only be created with system streams
1477+
return TerminalBuilder.builder().system(true).dumb(false).streams(System.in, System.err).build();
1478+
}
14481479
}
14491480

14501481
void usage() {
@@ -1495,13 +1526,12 @@ boolean dispatch(String line) {
14951526
}
14961527

14971528
line = HiveStringUtils.removeComments(line);
1529+
line = line.trim();
14981530

1499-
if (line.trim().length() == 0) {
1531+
if (line.length() == 0) {
15001532
return true;
15011533
}
15021534

1503-
line = line.trim();
1504-
15051535
// save it to the current script, if any
15061536
if (scriptOutputFile != null) {
15071537
scriptOutputFile.addLine(line);
@@ -2493,16 +2523,8 @@ PrintStream getErrorStream() {
24932523
return errorStream;
24942524
}
24952525

2496-
InputStream getInputStream() {
2497-
return inputStream;
2498-
}
2499-
2500-
ConsoleReader getConsoleReader() {
2501-
return consoleReader;
2502-
}
2503-
2504-
void setConsoleReader(ConsoleReader reader) {
2505-
this.consoleReader = reader;
2526+
LineReader getLineReader() {
2527+
return lineReader;
25062528
}
25072529

25082530
List<String> getBatch() {

0 commit comments

Comments
 (0)