Skip to content

Commit

Permalink
HIVE-14606: Beeline fails if quoted string ends with \\ (Sahil Takiar…
Browse files Browse the repository at this point in the history
…, reviewed by Sergio Pena)
  • Loading branch information
sahilTakiar authored and Sergio Pena committed Mar 23, 2017
1 parent 6079550 commit 4812486
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 8 deletions.
31 changes: 23 additions & 8 deletions beeline/src/java/org/apache/hive/beeline/Commands.java
Expand Up @@ -1221,46 +1221,61 @@ private List<String> getCmdList(String line, boolean entireLineAsCommand) {
if (entireLineAsCommand) {
cmdList.add(line);
} else {
StringBuffer command = new StringBuffer();
StringBuilder command = new StringBuilder();

// Marker to track if there is starting double quote without an ending double quote
boolean hasUnterminatedDoubleQuote = false;
boolean hasUntermindatedSingleQuote = false;

// Marker to track if there is starting single quote without an ending double quote
boolean hasUnterminatedSingleQuote = false;

// Index of the last seen semicolon in the given line
int lastSemiColonIndex = 0;
char[] lineChars = line.toCharArray();

// Marker to track if the previous character was an escape character
boolean wasPrevEscape = false;

int index = 0;

// Iterate through the line and invoke the addCmdPart method whenever a semicolon is seen that is not inside a
// quoted string
for (; index < lineChars.length; index++) {
switch (lineChars[index]) {
case '\'':
// If a single quote is seen and the index is not inside a double quoted string and the previous character
// was not an escape, then update the hasUnterminatedSingleQuote flag
if (!hasUnterminatedDoubleQuote && !wasPrevEscape) {
hasUntermindatedSingleQuote = !hasUntermindatedSingleQuote;
hasUnterminatedSingleQuote = !hasUnterminatedSingleQuote;
}
wasPrevEscape = false;
break;
case '\"':
if (!hasUntermindatedSingleQuote && !wasPrevEscape) {
// If a double quote is seen and the index is not inside a single quoted string and the previous character
// was not an escape, then update the hasUnterminatedDoubleQuote flag
if (!hasUnterminatedSingleQuote && !wasPrevEscape) {
hasUnterminatedDoubleQuote = !hasUnterminatedDoubleQuote;
}
wasPrevEscape = false;
break;
case ';':
if (!hasUnterminatedDoubleQuote && !hasUntermindatedSingleQuote) {
// If a semicolon is seen, and the line isn't inside a quoted string, then treat
// line[lastSemiColonIndex] to line[index] as a single command
if (!hasUnterminatedDoubleQuote && !hasUnterminatedSingleQuote) {
addCmdPart(cmdList, command, line.substring(lastSemiColonIndex, index));
lastSemiColonIndex = index + 1;
}
wasPrevEscape = false;
break;
case '\\':
wasPrevEscape = true;
wasPrevEscape = !wasPrevEscape;
break;
default:
wasPrevEscape = false;
break;
}
}
// if the line doesn't end with a ; or if the line is empty, add the cmd part
// If the line doesn't end with a ; or if the line is empty, add the cmd part
if (lastSemiColonIndex != index || lineChars.length == 0) {
addCmdPart(cmdList, command, line.substring(lastSemiColonIndex, index));
}
Expand All @@ -1272,7 +1287,7 @@ private List<String> getCmdList(String line, boolean entireLineAsCommand) {
* Given a cmdpart (e.g. if a command spans multiple lines), add to the current command, and if
* applicable add that command to the {@link List} of commands
*/
private void addCmdPart(List<String> cmdList, StringBuffer command, String cmdpart) {
private void addCmdPart(List<String> cmdList, StringBuilder command, String cmdpart) {
if (cmdpart.endsWith("\\")) {
command.append(cmdpart.substring(0, cmdpart.length() - 1)).append(";");
return;
Expand Down
Expand Up @@ -1069,4 +1069,17 @@ private static class Tuple<K> {
this.shouldMatch = shouldMatch;
}
}

/**
* Test that Beeline can handle \\ characters within a string literal. Either at the beginning, middle, or end of the
* literal.
*/
@Test
public void testBackslashInLiteral() throws Throwable {
String SCRIPT_TEXT = "select 'hello\\\\', '\\\\hello', 'hel\\\\lo', '\\\\' as literal;";
final String EXPECTED_PATTERN = "hello\\\\\t\\\\hello\thel\\\\lo\t\\\\";
List<String> argList = getBaseArgs(miniHS2.getBaseJdbcURL());
argList.add("--outputformat=tsv2");
testScriptFile(SCRIPT_TEXT, argList, EXPECTED_PATTERN, true);
}
}

0 comments on commit 4812486

Please sign in to comment.