28
28
import java .io .EOFException ;
29
29
import java .io .File ;
30
30
import java .io .FileInputStream ;
31
+ import java .io .IOError ;
31
32
import java .io .IOException ;
32
33
import java .io .InputStream ;
33
34
import java .io .InputStreamReader ;
100
101
import org .apache .hive .beeline .hs2connection .HS2ConnectionFileUtils ;
101
102
import org .apache .hive .beeline .hs2connection .HiveSiteHS2ConnectionFileParser ;
102
103
import org .apache .hive .beeline .hs2connection .UserHS2ConnectionFileParser ;
104
+ import org .apache .hive .common .util .MatchingStringsCompleter ;
103
105
import org .apache .hive .common .util .ShutdownHookManager ;
104
106
import org .apache .hive .common .util .HiveStringUtils ;
105
107
import org .apache .hive .jdbc .HiveConnection ;
110
112
111
113
import com .google .common .annotations .VisibleForTesting ;
112
114
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
+
118
127
119
128
/**
120
129
* A console SQL shell with command completion.
@@ -151,14 +160,14 @@ public class BeeLine implements Closeable {
151
160
private OutputFile recordOutputFile = null ;
152
161
private PrintStream outputStream = new PrintStream (System .out , true );
153
162
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 ;
156
165
private List <String > batch = null ;
157
166
private final Reflector reflector = new Reflector (this );
158
167
private String dbName = null ;
159
168
private String currentDatabase = null ;
160
169
161
- private FileHistory history ;
170
+ private History history ;
162
171
// Indicates if this instance of beeline is running in compatibility mode, or beeline mode
163
172
private boolean isBeeLine = true ;
164
173
@@ -204,7 +213,7 @@ public class BeeLine implements Closeable {
204
213
new ReflectiveCommandHandler (this , new String [] {"quit" , "done" , "exit" },
205
214
null ),
206
215
new ReflectiveCommandHandler (this , new String [] {"connect" , "open" },
207
- new Completer [] {new StringsCompleter (getConnectionURLExamples ())}),
216
+ new Completer [] {new MatchingStringsCompleter (getConnectionURLExamples ())}),
208
217
new ReflectiveCommandHandler (this , new String [] {"describe" },
209
218
new Completer [] {new TableNameCompletor (this )}),
210
219
new ReflectiveCommandHandler (this , new String [] {"indexes" },
@@ -233,7 +242,7 @@ public class BeeLine implements Closeable {
233
242
null ),
234
243
new ReflectiveCommandHandler (this , new String [] {"metadata" },
235
244
new Completer [] {
236
- new StringsCompleter (getMetadataMethodNames ())}),
245
+ new MatchingStringsCompleter (getMetadataMethodNames ())}),
237
246
new ReflectiveCommandHandler (this , new String [] {"nativesql" },
238
247
null ),
239
248
new ReflectiveCommandHandler (this , new String [] {"dbinfo" },
@@ -263,9 +272,9 @@ public class BeeLine implements Closeable {
263
272
new ReflectiveCommandHandler (this , new String [] {"closeall" },
264
273
null ),
265
274
new ReflectiveCommandHandler (this , new String [] {"isolation" },
266
- new Completer [] {new StringsCompleter (getIsolationLevels ())}),
275
+ new Completer [] {new MatchingStringsCompleter (getIsolationLevels ())}),
267
276
new ReflectiveCommandHandler (this , new String [] {"outputformat" },
268
- new Completer [] {new StringsCompleter (
277
+ new Completer [] {new MatchingStringsCompleter (
269
278
formats .keySet ().toArray (new String [0 ]))}),
270
279
new ReflectiveCommandHandler (this , new String [] {"autocommit" },
271
280
null ),
@@ -309,9 +318,9 @@ public class BeeLine implements Closeable {
309
318
310
319
static {
311
320
try {
312
- Class .forName ("jline.console.ConsoleReader " );
321
+ Class .forName ("org. jline.reader.LineReader " );
313
322
} catch (Throwable t ) {
314
- throw new ExceptionInInitializerError ("jline -missing" );
323
+ throw new ExceptionInInitializerError ("jline3 -missing" );
315
324
}
316
325
}
317
326
@@ -400,7 +409,7 @@ public class BeeLine implements Closeable {
400
409
.withLongOpt ("help" )
401
410
.withDescription ("Display this message" )
402
411
.create ('h' ));
403
-
412
+
404
413
// -getUrlsFromBeelineSite
405
414
options .addOption (OptionBuilder
406
415
.withLongOpt ("getUrlsFromBeelineSite" )
@@ -433,7 +442,6 @@ public class BeeLine implements Closeable {
433
442
.create ());
434
443
}
435
444
436
-
437
445
static Manifest getManifest () throws IOException {
438
446
URL base = BeeLine .class .getResource ("/META-INF/MANIFEST.MF" );
439
447
URLConnection c = base .openConnection ();
@@ -569,19 +577,15 @@ public BeeLine() {
569
577
public BeeLine (boolean isBeeLine ) {
570
578
this .isBeeLine = isBeeLine ;
571
579
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 ();
584
584
}
585
+ } catch (IOException e ) {
586
+ error (e );
587
+ } finally {
588
+ close ();
585
589
}
586
590
};
587
591
}
@@ -863,7 +867,7 @@ private boolean connectUsingArgs(BeelineParser beelineParser, CommandLine cl) {
863
867
getOpts ().setHelpAsked (true );
864
868
return true ;
865
869
}
866
-
870
+
867
871
if (cl .hasOption ("getUrlsFromBeelineSite" )) {
868
872
printBeelineSiteUrls ();
869
873
getOpts ().setBeelineSiteUrlsAsked (true );
@@ -937,8 +941,8 @@ private boolean connectUsingArgs(BeelineParser beelineParser, CommandLine cl) {
937
941
String propertyFile = cl .getOptionValue ("property-file" );
938
942
if (propertyFile != null ) {
939
943
try {
940
- this .consoleReader = new ConsoleReader ();
941
- } catch (IOException e ) {
944
+ this .lineReader = LineReaderBuilder . builder (). build ();
945
+ } catch (IOError e ) {
942
946
handleException (e );
943
947
}
944
948
if (!dispatch ("!properties " + propertyFile )) {
@@ -980,7 +984,7 @@ private void printBeelineSiteUrls() {
980
984
}
981
985
}
982
986
}
983
-
987
+
984
988
private boolean isZkBasedUrl (String urlFromBeelineSite ) {
985
989
String zkJdbcUriParam = ("serviceDiscoveryMode=zooKeeper" ).toLowerCase ();
986
990
if (urlFromBeelineSite .toLowerCase ().contains (zkJdbcUriParam )) {
@@ -1116,9 +1120,9 @@ public int begin(String[] args, InputStream inputStream, boolean keepHistory) th
1116
1120
//add shutdown hook to cleanup the beeline for smooth exit
1117
1121
addBeelineShutdownHook ();
1118
1122
1119
- //this method also initializes the consoleReader which is
1123
+ //this method also initializes the lineReader which is
1120
1124
//needed by initArgs for certain execution paths
1121
- ConsoleReader reader = initializeConsoleReader (inputStream );
1125
+ initializeLineReader (inputStream );
1122
1126
if (isBeeLine ) {
1123
1127
int code = initArgs (args );
1124
1128
if (code != 0 ) {
@@ -1146,7 +1150,7 @@ public int begin(String[] args, InputStream inputStream, boolean keepHistory) th
1146
1150
} catch (Exception e ) {
1147
1151
// ignore
1148
1152
}
1149
- return execute (reader , false );
1153
+ return execute (lineReader , false );
1150
1154
}
1151
1155
1152
1156
/*
@@ -1350,7 +1354,7 @@ private int executeFile(String fileName) {
1350
1354
}
1351
1355
fileStream = fs .open (path );
1352
1356
}
1353
- return execute (initializeConsoleReader (fileStream ), !getOpts ().getForce ());
1357
+ return execute (initializeLineReader (fileStream ), !getOpts ().getForce ());
1354
1358
} catch (Throwable t ) {
1355
1359
handleException (t );
1356
1360
return ERRNO_OTHER ;
@@ -1359,16 +1363,17 @@ private int executeFile(String fileName) {
1359
1363
}
1360
1364
}
1361
1365
1362
- private int execute (ConsoleReader reader , boolean exitOnError ) {
1366
+ private int execute (LineReader reader , boolean exitOnError ) {
1363
1367
int lastExecutionResult = ERRNO_OK ;
1364
1368
Character mask = (System .getProperty ("jline.terminal" , "" ).equals ("jline.UnsupportedTerminal" )) ? null
1365
- : ConsoleReader .NULL_MASK ;
1369
+ : LineReaderImpl .NULL_MASK ;
1366
1370
1371
+ String line ;
1367
1372
while (!exit ) {
1368
1373
try {
1369
1374
// Execute one instruction; terminate on executing a script if there is an error
1370
1375
// 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
1372
1377
.readLine (null , mask ) : reader .readLine (getPrompt ());
1373
1378
1374
1379
// trim line
@@ -1384,7 +1389,17 @@ private int execute(ConsoleReader reader, boolean exitOnError) {
1384
1389
} else if (line != null ) {
1385
1390
lastExecutionResult = ERRNO_OK ;
1386
1391
}
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 ;
1388
1403
} catch (Throwable t ) {
1389
1404
handleException (t );
1390
1405
return ERRNO_OTHER ;
@@ -1403,48 +1418,64 @@ private void setupHistory() throws IOException {
1403
1418
return ;
1404
1419
}
1405
1420
1406
- this .history = new FileHistory ( new File ( getOpts (). getHistoryFile ()) );
1421
+ this .history = new DefaultHistory ( );
1407
1422
}
1408
1423
1409
1424
private void addBeelineShutdownHook () throws IOException {
1410
1425
// add shutdown hook to flush the history to history file and it also close all open connections
1411
1426
ShutdownHookManager .addShutdownHook (getShutdownHook ());
1412
1427
}
1413
1428
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 );
1429
1433
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
+ }
1430
1439
try {
1431
1440
// now set the output for the history
1432
1441
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 ()));
1436
1447
}
1437
1448
} catch (Exception e ) {
1438
1449
handleException (e );
1439
1450
}
1440
1451
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 ()));
1444
1468
}
1469
+ return inputStream ;
1470
+ }
1445
1471
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
+ }
1448
1479
}
1449
1480
1450
1481
void usage () {
@@ -1495,13 +1526,12 @@ boolean dispatch(String line) {
1495
1526
}
1496
1527
1497
1528
line = HiveStringUtils .removeComments (line );
1529
+ line = line .trim ();
1498
1530
1499
- if (line .trim (). length () == 0 ) {
1531
+ if (line .length () == 0 ) {
1500
1532
return true ;
1501
1533
}
1502
1534
1503
- line = line .trim ();
1504
-
1505
1535
// save it to the current script, if any
1506
1536
if (scriptOutputFile != null ) {
1507
1537
scriptOutputFile .addLine (line );
@@ -2493,16 +2523,8 @@ PrintStream getErrorStream() {
2493
2523
return errorStream ;
2494
2524
}
2495
2525
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 ;
2506
2528
}
2507
2529
2508
2530
List <String > getBatch () {
0 commit comments