You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by sp...@apache.org on 2016/07/25 15:18:11 UTC

hive git commit: HIVE-12646: beeline and HIVE CLI do not parse ; in quote properly (Sahil Takiar, reviewed by Sergio Pena)

Repository: hive
Updated Branches:
  refs/heads/master cd5147c7e -> 9938d45da


HIVE-12646: beeline and HIVE CLI do not parse ; in quote properly (Sahil Takiar, reviewed by Sergio Pena)


Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/9938d45d
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/9938d45d
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/9938d45d

Branch: refs/heads/master
Commit: 9938d45da0d6bbd74e0adf72ca3e19e58a03c9dd
Parents: cd5147c
Author: Sahil Takiar <ta...@gmail.com>
Authored: Mon Jul 25 10:17:16 2016 -0500
Committer: Sergio Pena <se...@cloudera.com>
Committed: Mon Jul 25 10:17:16 2016 -0500

----------------------------------------------------------------------
 .../java/org/apache/hive/beeline/Commands.java  | 90 ++++++++++++++++----
 .../hive/beeline/TestBeeLineWithArgs.java       | 21 +++++
 .../org/apache/hadoop/hive/ql/exec/DDLTask.java |  2 +-
 3 files changed, 96 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hive/blob/9938d45d/beeline/src/java/org/apache/hive/beeline/Commands.java
----------------------------------------------------------------------
diff --git a/beeline/src/java/org/apache/hive/beeline/Commands.java b/beeline/src/java/org/apache/hive/beeline/Commands.java
index 387861b..ffd1775 100644
--- a/beeline/src/java/org/apache/hive/beeline/Commands.java
+++ b/beeline/src/java/org/apache/hive/beeline/Commands.java
@@ -1128,22 +1128,7 @@ public class Commands {
     }
 
     line = line.trim();
-    List<String> cmdList = new ArrayList<String>();
-    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<String> 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,79 @@ public class Commands {
     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. It iterates through each character in the line and checks to see if it is a ;, ',
+   * or "
+   */
+  private List<String> getCmdList(String line, boolean entireLineAsCommand) {
+    List<String> cmdList = new ArrayList<String>();
+    if (entireLineAsCommand) {
+      cmdList.add(line);
+    } else {
+      StringBuffer command = new StringBuffer();
+
+      boolean hasUnterminatedDoubleQuote = false;
+      boolean hasUntermindatedSingleQuote = false;
+
+      int lastSemiColonIndex = 0;
+      char[] lineChars = line.toCharArray();
+
+      boolean wasPrevEscape = false;
+      int index = 0;
+      for (; index < lineChars.length; index++) {
+        switch (lineChars[index]) {
+          case '\'':
+            if (!hasUnterminatedDoubleQuote && !wasPrevEscape) {
+              hasUntermindatedSingleQuote = !hasUntermindatedSingleQuote;
+            }
+            wasPrevEscape = false;
+            break;
+          case '\"':
+            if (!hasUntermindatedSingleQuote && !wasPrevEscape) {
+              hasUnterminatedDoubleQuote = !hasUnterminatedDoubleQuote;
+            }
+            wasPrevEscape = false;
+            break;
+          case ';':
+            if (!hasUnterminatedDoubleQuote && !hasUntermindatedSingleQuote) {
+              addCmdPart(cmdList, command, line.substring(lastSemiColonIndex, index));
+              lastSemiColonIndex = index + 1;
+            }
+            wasPrevEscape = false;
+            break;
+          case '\\':
+            wasPrevEscape = true;
+            break;
+          default:
+            wasPrevEscape = false;
+            break;
+        }
+      }
+      // 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));
+      }
+    }
+    return cmdList;
+  }
+
+  /**
+   * 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) {
+    if (cmdpart.endsWith("\\")) {
+      command.append(cmdpart.substring(0, cmdpart.length() - 1)).append(";");
+      return;
+    } else {
+      command.append(cmdpart);
+    }
+    cmdList.add(command.toString());
+    command.setLength(0);
+  }
+
   private Runnable createLogRunnable(Statement statement) {
     if (statement instanceof HiveStatement) {
       final HiveStatement hiveStatement = (HiveStatement) statement;

http://git-wip-us.apache.org/repos/asf/hive/blob/9938d45d/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java
----------------------------------------------------------------------
diff --git a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java
index ecfeddb..3b40d71 100644
--- a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java
+++ b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java
@@ -863,4 +863,25 @@ public class TestBeeLineWithArgs {
 
     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<String> argList = getBaseArgs(miniHS2.getBaseJdbcURL());
+    testScriptFile(SCRIPT_TEXT, EXPECTED_PATTERN, true, argList);
+  }
+
+  @Test
+  public void testSelectQueryWithNonEscapedSemiColon() throws Throwable {
+    String SCRIPT_TEXT = "select ';', \"';'\", '\";\"', '\\';', ';\\'', '\\\";', ';\\\"' from " + tableName + ";";
+    final String EXPECTED_PATTERN = ";\t';'\t\";\"\t';\t;'\t\";\t;\"";
+    List<String> argList = getBaseArgs(miniHS2.getBaseJdbcURL());
+    argList.add("--outputformat=tsv2");
+    testScriptFile(SCRIPT_TEXT, EXPECTED_PATTERN, true, argList);
+  }
 }

http://git-wip-us.apache.org/repos/asf/hive/blob/9938d45d/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java b/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java
index 2b8d6a7..39ac7d6 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java
@@ -4346,7 +4346,7 @@ public class DDLTask extends Task<DDLWork> implements Serializable {
     StringBuilder sb = new StringBuilder();
     for (int i = 0; i < str.length(); i ++) {
       char c = str.charAt(i);
-      if (c == '\'' || c == ';') {
+      if (c == '\'') {
         sb.append('\\');
       }
       sb.append(c);