diff --git beeline/src/java/org/apache/hive/beeline/Commands.java beeline/src/java/org/apache/hive/beeline/Commands.java index 3a204c0..8f9e472 100644 --- beeline/src/java/org/apache/hive/beeline/Commands.java +++ beeline/src/java/org/apache/hive/beeline/Commands.java @@ -1128,22 +1128,7 @@ private boolean execute(String line, boolean call, boolean entireLineAsCommand) } line = line.trim(); - List cmdList = new ArrayList(); - if (entireLineAsCommand) { - cmdList.add(line); - } else { - StringBuffer command = new StringBuffer(); - for (String cmdpart: line.split(";")) { - if (cmdpart.endsWith("\\")) { - command.append(cmdpart.substring(0, cmdpart.length() -1)).append(";"); - continue; - } else { - command.append(cmdpart); - } - cmdList.add(command.toString()); - command.setLength(0); - } - } + List cmdList = getCmdList(line, entireLineAsCommand); for (int i = 0; i < cmdList.size(); i++) { String sql = cmdList.get(i).trim(); if (sql.length() != 0) { @@ -1155,6 +1140,59 @@ private boolean execute(String line, boolean call, boolean entireLineAsCommand) return true; } + /** + * Helper method to parse input from Beeline and convert it to a {@link List} of commands that + * can be executed. This method contains logic for handling semicolons that are placed within + * quotations. + */ + private List getCmdList(String line, boolean entireLineAsCommand) { + List cmdList = new ArrayList(); + if (entireLineAsCommand) { + cmdList.add(line); + } else { + StringBuffer command = new StringBuffer(); + + boolean hasUnterminatedDoubleQuote = false; + boolean hasUntermindatedSingleQuote = false; + + int lastSemiColonIndex = 0; + char[] lineChars = line.toCharArray(); + + for (int index = 0; index < lineChars.length; index++) { + switch (lineChars[index]) { + case '\'': + if (!hasUnterminatedDoubleQuote) { + hasUntermindatedSingleQuote = !hasUntermindatedSingleQuote; + } + break; + case '\"': + if (!hasUntermindatedSingleQuote) { + hasUnterminatedDoubleQuote = !hasUnterminatedDoubleQuote; + } + break; + case ';': + if (!hasUnterminatedDoubleQuote && !hasUntermindatedSingleQuote) { + String cmdpart = line.substring(lastSemiColonIndex, index); + lastSemiColonIndex = index + 1; + + if (cmdpart.endsWith("\\")) { + command.append(cmdpart.substring(0, cmdpart.length() - 1)).append(";"); + continue; + } else { + command.append(cmdpart); + } + cmdList.add(command.toString()); + command.setLength(0); + } + break; + default: + break; + } + } + } + return cmdList; + } + private Runnable createLogRunnable(Statement statement) { if (statement instanceof HiveStatement) { final HiveStatement hiveStatement = (HiveStatement) statement; diff --git itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java index ecfeddb..88a66e6 100644 --- itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java +++ itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java @@ -863,4 +863,16 @@ public void testConnectionWithURLParams() throws Throwable { testScriptFile( SCRIPT_TEXT, EXPECTED_PATTERN, true, argList); } + + /** + * Test that Beeline queries don't treat semicolons inside quotations as query-ending characters. + */ + @Test + public void testQueryNonEscapedSemiColon() throws Throwable { + String SCRIPT_TEXT = "drop table if exists nonEscapedSemiColon;create table nonEscapedSemiColon " + + "(key int) ROW FORMAT DELIMITED FIELDS TERMINATED BY ';';show tables;"; + final String EXPECTED_PATTERN = " nonEscapedSemiColon "; + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + testScriptFile(SCRIPT_TEXT, EXPECTED_PATTERN, true, argList); + } }