Index: ql/src/java/org/apache/hadoop/hive/ql/exec/Utilities.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/exec/Utilities.java (revision 1101220) +++ ql/src/java/org/apache/hadoop/hive/ql/exec/Utilities.java (working copy) @@ -2014,12 +2014,12 @@ * all input formats, and choose the ones that extend ReworkMapredInputFormat * to a set. And finally go through the ReworkMapredInputFormat set, and call * rework for each one. - * + * * Technically all these can be avoided if all Hive's input formats can share * a same interface. As in today's hive and Hadoop, it is not possible because * a lot of Hive's input formats are in Hadoop's code. And most of Hadoop's * input formats just extend InputFormat interface. - * + * * @param task * @param reworkMapredWork * @param conf @@ -2203,4 +2203,28 @@ baseWindow * failures + // grace period for the last round of attempt baseWindow * (failures + 1) * r.nextDouble()); // expanding time window for each failure } + + /** + * Escape the '_', '%', as well as the escape characters inside the string key. + * @param key the string that will be used for the SQL LIKE operator. + * @param escape the escape character + * @return a string with escaped '_' and '%'. + */ + public static final char sqlEscapeChar = '\\'; + public static String escapeSqlLike(String key) { + StringBuffer sb = new StringBuffer(key.length()); + for (char c: key.toCharArray()) { + switch(c) { + case '_': + case '%': + case sqlEscapeChar: + sb.append(sqlEscapeChar); + // fall through + default: + sb.append(c); + break; + } + } + return sb.toString(); + } } Index: ql/src/java/org/apache/hadoop/hive/ql/stats/jdbc/JDBCStatsAggregator.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/stats/jdbc/JDBCStatsAggregator.java (revision 1101220) +++ ql/src/java/org/apache/hadoop/hive/ql/stats/jdbc/JDBCStatsAggregator.java (working copy) @@ -72,7 +72,8 @@ "SELECT /* " + comment + " */ " + " SUM(" + JDBCStatsSetupConstants.PART_STAT_ROW_COUNT_COLUMN_NAME + ")" + " FROM " + JDBCStatsSetupConstants.PART_STAT_TABLE_NAME + - " WHERE " + JDBCStatsSetupConstants.PART_STAT_ID_COLUMN_NAME + " LIKE ?"; + " WHERE " + JDBCStatsSetupConstants.PART_STAT_ID_COLUMN_NAME + + " LIKE ? ESCAPE ?"; /* Automatic Cleaning: IMPORTANT: Since we publish and aggregate only 1 value (1 column) which is the row count, it @@ -85,7 +86,8 @@ String delete = "DELETE /* " + comment + " */ " + " FROM " + JDBCStatsSetupConstants.PART_STAT_TABLE_NAME + - " WHERE " + JDBCStatsSetupConstants.PART_STAT_ID_COLUMN_NAME + " LIKE ?"; + " WHERE " + JDBCStatsSetupConstants.PART_STAT_ID_COLUMN_NAME + + " LIKE ? ESCAPE ?"; // stats is non-blocking -- throw an exception when timeout DriverManager.setLoginTimeout(timeout); @@ -156,12 +158,13 @@ } }; + String keyPrefix = Utilities.escapeSqlLike(fileID) + "%"; for (int failures = 0; ; failures++) { try { long retval = 0; - String keyPrefix = fileID + "%"; selStmt.setString(1, keyPrefix); + selStmt.setString(2, Character.toString(Utilities.sqlEscapeChar)); ResultSet result = Utilities.executeWithRetry(execQuery, selStmt, waitWindow, maxRetries); if (result.next()) { retval = result.getLong(1); @@ -179,6 +182,7 @@ through a separate method which the developer has to call it manually in the code. */ delStmt.setString(1, keyPrefix); + delStmt.setString(2, Character.toString(Utilities.sqlEscapeChar)); Utilities.executeWithRetry(execUpdate, delStmt, waitWindow, maxRetries); LOG.info("Stats aggregator got " + retval);