package cn.org.rapid_framework.generator.util.sqlparse; import java.io.StringReader; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.LinkedHashSet; import java.util.Random; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import cn.org.rapid_framework.generator.provider.db.table.TableFactory.DatabaseMetaDataUtils; import cn.org.rapid_framework.generator.util.GLogger; import cn.org.rapid_framework.generator.util.IOHelper; import cn.org.rapid_framework.generator.util.StringHelper; public class SqlParseHelper { public static class NameWithAlias { private String name; private String alias; public NameWithAlias(String name, String alias) { if(name == null) throw new IllegalArgumentException("name must be not null"); if(name.trim().indexOf(' ') >= 0) throw new IllegalArgumentException("error name:"+name); if(alias != null && alias.trim().indexOf(' ') >= 0) throw new IllegalArgumentException("error alias:"+alias); this.name = name.trim(); this.alias = alias == null ? null : alias.trim(); } public String getName() { return name; } public String getAlias() { return StringHelper.isBlank(alias) ? getName() : alias; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; NameWithAlias other = (NameWithAlias) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public String toString() { return StringHelper.isBlank(alias) ? name : name +" as " + alias; } } static Pattern fromRegex = Pattern.compile("(\\sfrom\\s+)([,\\w]+)", Pattern.CASE_INSENSITIVE); static Pattern join = Pattern.compile("(join\\s+)(\\w+)(as)?(\\w*)", Pattern.CASE_INSENSITIVE); static Pattern update = Pattern.compile("(\\s*update\\s+)(\\w+)", Pattern.CASE_INSENSITIVE); static Pattern insert = Pattern.compile("(\\s*insert\\s+into\\s+)(\\w+)", Pattern.CASE_INSENSITIVE); /** 从一条sql中解释中包含的表 */ public static Set<NameWithAlias> getTableNamesByQuery(String sql) { sql = removeSqlComments(StringHelper.removeXMLCdataTag(sql)).trim(); Set<NameWithAlias> result = new LinkedHashSet<NameWithAlias>(); Matcher m = fromRegex.matcher(sql); if (m.find()) { String from = getFromClauses(sql); if(from.matches("(?ims).*\\sfrom\\s.*")) { return getTableNamesByQuery(from); } if(from.indexOf(',') >= 0) { //逗号分隔的多表 String[] array = StringHelper.tokenizeToStringArray(from, ","); for(String s : array) { result.add(parseTableSqlAlias(s)); } }else if(StringHelper.indexOfByRegex(from.toLowerCase(), "\\sjoin\\s") >= 0) { //join的多表 String removedFrom = StringHelper.removeMany(from.toLowerCase(),"inner","full","left","right","outer"); String[] joins = removedFrom.split("join"); for(String s : joins) { result.add(parseTableSqlAlias(s)); } }else { //单表 result.add(parseTableSqlAlias(from)); } } if(SqlTypeChecker.isUpdateSql(sql)) { m = update.matcher(sql); if (m.find()) { result.add(new NameWithAlias(m.group(2),null)); } } if(SqlTypeChecker.isInsertSql(sql)) { m = insert.matcher(sql); if (m.find()) { result.add(new NameWithAlias(m.group(2),null)); } } return result; } /** 解析sql的别名,如 user as u,将返回 user及u */ public static NameWithAlias parseTableSqlAlias(String str) { try { str = str.trim(); String[] array = str.split("\\sas\\s"); if(array.length >= 2 && str.matches("^[\\w_]+\\s+as\\s+[_\\w]+.*")) { return new NameWithAlias(array[0],StringHelper.tokenizeToStringArray(array[1], " \n\t")[0]); } array = StringHelper.tokenizeToStringArray(str, " \n\t"); if(array.length >= 2 && str.matches("^[\\w_]+\\s+[_\\w]+.*")) { return new NameWithAlias(array[0], array[1]); } return new NameWithAlias(StringHelper.getByRegex(str.trim(),"^[\\w_]+"),StringHelper.getByRegex(str.trim(),"^[\\w_]+\\s+([\\w_]+)",1)); }catch(Exception e) { throw new IllegalArgumentException("parseTableSqlAlias error,str:"+str,e); } } // static Pattern p = Pattern.compile("(:)(\\w+)(\\|?)([\\w.]+)"); public static String getParameterClassName(String sql, String paramName) { Pattern p = Pattern.compile("(:)(" + paramName + ")(\\|?)([\\w.]+)"); Matcher m = p.matcher(sql); if (m.find()) { return m.group(4); } return null; } public static String getColumnNameByRightCondition(String sql,String column) { String operator = "[=<>!]{1,2}"; String result = getColumnNameByRightCondition(sql, column, operator); if(result == null) { result = getColumnNameByRightCondition(sql, column, "\\s+like\\s+"); } if(result == null) { result = getColumnNameByRightCondition(sql, column, "\\s+between\\s+"); } if(result == null) { result = getColumnNameByRightCondition(sql, column, "\\s+between\\s.+\\sand\\s+"); } if(result == null) { result = getColumnNameByRightCondition(sql, column, "\\s+not\\s+in\\s*\\("); } if(result == null) { result = getColumnNameByRightCondition(sql, column, "\\s+in\\s*\\("); } if(result == null) { result = getColumnNameByRightConditionWithFunction(sql, column, operator); } return result; } //有函数的表达式提取 private static String getColumnNameByRightConditionWithFunction(String sql,String column, String operator) { Pattern p = Pattern.compile("(\\w+)\\s*"+operator+"\\s*\\w+\\([,\\w]*[:#$&]\\{?"+column+"[\\}#$]?[,\\w]*\\)",Pattern.DOTALL|Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(sql); if(m.find()) { return m.group(1); } return null; } private static String getColumnNameByRightCondition(String sql, String column, String operator) { Pattern p = Pattern.compile("(\\w+)\\s*"+operator+"\\s*[:#$&]\\{?"+column+"[\\}#$]?",Pattern.DOTALL|Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(sql); if(m.find()) { return m.group(1); } return null; } public static String convert2NamedParametersSql(String sql,String prefix,String suffix) { return new NamedSqlConverter(prefix,suffix).convert2NamedParametersSql(sql); } /** * 将sql从占位符转换为命名参数,如 select * from user where id =? ,将返回: select * from user where id = #id# * @param includeSqls * @param prefix 命名参数的前缀 * @param suffix 命名参数的后缀 * @return */ public static class NamedSqlConverter { private String prefix; private String suffix; public NamedSqlConverter(String prefix, String suffix) { if(prefix == null) throw new NullPointerException("'prefix' must be not null"); if(suffix == null) throw new NullPointerException("'suffix' must be not null"); this.prefix = prefix; this.suffix = suffix; } public String convert2NamedParametersSql(String sql) { if(sql.trim().toLowerCase().matches("(?is)\\s*insert\\s+into\\s+.*")) { return replace2NamedParameters(replaceInsertSql2NamedParameters(sql)); }else { return replace2NamedParameters(sql); } } private String replace2NamedParameters(String sql) { String replacedSql = replace2NamedParametersByOperator(sql,"[=<>!]{1,2}"); //缺少oracle的^=运算符: !=,<>,^=:不等于 replacedSql = replace2NamedParametersByOperator(replacedSql,"\\s+like\\s+"); // like // replacedSql = replace2NamedParametersByOperator(replacedSql,"\\s+not\\s+in\\s+\\("); // not in // replacedSql = replace2NamedParametersByOperator(replacedSql,"\\s+in\\s+\\("); // in return replacedSql; } private String replaceInsertSql2NamedParameters(String sql) { if(sql.matches("(?is)\\s*insert\\s+into\\s+\\w+\\s+values\\s*\\(.*\\).*")) { if(sql.indexOf("?") >= 0) { throw new IllegalArgumentException("无法解析的insert sql:"+sql+",values()段没有包含疑问号?"); } else { return sql; } } //FIXME: insert into user_info(user,pwd) values (length(?),?); 将没有办法解析,因为正则表达式由于length()函数匹配错误. //需要处理 <selectKey>问题 String selectKeyPattern = "<selectKey.*"; String insertPattern = "\\s*insert\\s+into.*\\((.*?)\\).*values.*?\\((.*)\\).*"; Matcher m = matcher(sql,Pattern.DOTALL|Pattern.CASE_INSENSITIVE,insertPattern+selectKeyPattern,insertPattern); if(m != null) { String[] columns = StringHelper.tokenizeToStringArray(m.group(1),", \t\n\r\f"); String[] values = StringHelper.tokenizeToStringArray(m.group(2),", \t\n\r\f"); if(columns.length != values.length) { throw new IllegalArgumentException("insert 语句的插入列与值列数目不相等,sql:"+sql+" \ncolumns:"+StringHelper.join(columns,",")+" \nvalues:"+StringHelper.join(values,",")); } for(int i = 0; i < columns.length; i++) { String column = columns[i]; String paranName = StringHelper.uncapitalize(StringHelper.makeAllWordFirstLetterUpperCase(column)); values[i] = values[i].replace("?", prefix + paranName + suffix);; } return StringHelper.replace(m.start(2), m.end(2), sql, StringHelper.join(values,",")); } throw new IllegalArgumentException("无法解析的sql:"+sql+",不匹配正则表达式:"+insertPattern); } private static Matcher matcher(String input,int flags,String... regexArray) { for(String regex : regexArray) { Pattern p = Pattern.compile(regex,flags); Matcher m = p.matcher(input); if(m.find()) { return m; } } return null; } private String replace2NamedParametersByOperator(String sql,String operator) { Pattern p = Pattern.compile("(\\w+)\\s*"+operator+"\\s*\\?",Pattern.DOTALL|Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(sql); StringBuffer sb = new StringBuffer(); while(m.find()) { String segment = m.group(0); String columnSqlName = m.group(1); String paramName = StringHelper.uncapitalize(StringHelper.makeAllWordFirstLetterUpperCase(columnSqlName)); String replacedSegment = segment.replace("?", prefix + paramName + suffix); m.appendReplacement(sb, replacedSegment); } m.appendTail(sb); return sb.toString(); } } /** * 美化sql * * @param sql * @return */ public static String getPrettySql(String sql) { try { if (IOHelper.readLines(new StringReader(sql)).size() > 1) { return sql; } else { return StringHelper.replace(StringHelper.replace(sql, "from", "\n\tfrom"), "where", "\n\twhere"); } } catch (Exception e) { throw new RuntimeException(e); } } /** * 去除select 子句,未考虑union的情况 * * @param sql * @return */ public static String removeSelect(String sql) { if(StringHelper.isBlank(sql)) throw new IllegalArgumentException("sql must be not empty"); int beginPos = StringHelper.indexOfByRegex(sql.toLowerCase(), "\\sfrom\\s"); if(beginPos == -1) throw new IllegalArgumentException(" sql : " + sql + " must has a keyword 'from'"); return sql.substring(beginPos); } public static String toCountSqlForPaging(String sql,String countQueryPrefix) { if(StringHelper.isBlank(sql)) throw new IllegalArgumentException("sql must be not empty"); if(StringHelper.indexOfByRegex(sql.toLowerCase(), "\\sgroup\\s+by\\s") >= 0) { return countQueryPrefix +" from (" + sql + " ) forGroupByCountTable"; }else { int selectBeginOps = StringHelper.indexOfByRegex(sql.toLowerCase(), "select\\s"); int fromBeingOps = StringHelper.indexOfByRegex(sql.toLowerCase(), "\\sfrom\\s"); if(fromBeingOps == -1) throw new IllegalArgumentException(" sql : " + sql + " must has a keyword 'from'"); return sql.substring(0,selectBeginOps) + countQueryPrefix + sql.substring(fromBeingOps); } } public static String getSelect(String sql) { if(StringHelper.isBlank(sql)) throw new IllegalArgumentException("sql must be not empty"); int beginPos = StringHelper.indexOfByRegex(sql.toLowerCase(), "\\sfrom\\s"); if(beginPos == -1) throw new IllegalArgumentException(" sql : " + sql + " must has a keyword 'from'"); return sql.substring(0,beginPos); } /** 得到sql的from子句 */ public static String getFromClauses(String sql) { String lowerSql = sql.toLowerCase(); int fromBegin = StringHelper.indexOfByRegex(lowerSql, "\\sfrom\\s"); if(fromBegin <= 0) throw new IllegalArgumentException("error from begin:"+fromBegin); int fromEnd = lowerSql.indexOf("where"); if(fromEnd == -1) { fromEnd = StringHelper.indexOfByRegex(lowerSql,"\\sgroup\\s+by\\s"); } if(fromEnd == -1) { fromEnd = StringHelper.indexOfByRegex(lowerSql, "\\shaving\\s"); } if(fromEnd == -1) { fromEnd = StringHelper.indexOfByRegex(lowerSql,"\\sorder\\s+by\\s"); } if(fromEnd == -1) { fromEnd = StringHelper.indexOfByRegex(lowerSql, "\\sunion\\s");; } if(fromEnd == -1) { //SELECT Date FROM Store_Information //INTERSECT //SELECT Date FROM Internet_Sales fromEnd = StringHelper.indexOfByRegex(lowerSql, "\\sintersect\\s"); } if(fromEnd == -1) { //SELECT Date FROM Store_Information //MINUS //SELECT Date FROM Internet_Sales fromEnd = StringHelper.indexOfByRegex(lowerSql, "\\sminus\\s"); } if(fromEnd == -1) { //SELECT Date FROM Store_Information //EXCEPT //SELECT Date FROM Internet_Sales fromEnd = StringHelper.indexOfByRegex(lowerSql, "\\sexcept\\s"); } if(fromEnd == -1) { fromEnd = sql.length(); } return sql.substring(fromBegin+" from ".length(),fromEnd); } public static String removeSqlComments(String sql) { if(sql == null) return null; return sql.replaceAll("(?s)/\\*.*?\\*/", "").replaceAll("--.*", ""); } /** * 去除orderby 子句 * @param sql * @return */ public static String removeOrders(String sql) { return sql.replaceAll("(?is)order\\s+by[\\w|\\W|\\s|\\S]*", ""); } public static String replaceWhere(String sql) { return sql.toString().replaceAll("(?i)\\swhere\\s+(and|or)\\s", " WHERE "); } public static long startTimes = System.currentTimeMillis(); public static void setRandomParamsValueForPreparedStatement(String sql, PreparedStatement ps) throws SQLException { int count = StringHelper.containsCount(sql, "?"); if(DatabaseMetaDataUtils.isOracleDataBase(ps.getConnection().getMetaData())) { if(SqlTypeChecker.isSelectSql(sql)) { for (int i = 1; i <= count; i++) { ps.setObject(i, null); } return; } } for (int i = 1; i <= count; i++) { long random = new Random(System.currentTimeMillis()+startTimes++).nextInt() * 30 + System.currentTimeMillis() + startTimes; try { ps.setLong(i, random); } catch (SQLException e) { try { ps.setInt(i, (int) random % Integer.MAX_VALUE); } catch (SQLException e1) { try { ps.setString(i, "" + random); } catch (SQLException e2) { try { ps.setTimestamp(i, new java.sql.Timestamp(random)); } catch (SQLException e3) { try { ps.setDate(i, new java.sql.Date(random)); } catch (SQLException e6) { try { ps.setString(i, "" + (int) random); } catch (SQLException e4) { try { ps.setString(i, "" + (short) random); } catch (SQLException e82) { try { ps.setString(i, "" + (byte) random); } catch (SQLException e32) { try { ps.setNull(i, java.sql.Types.OTHER); }catch(SQLException error){ warn(sql, i, error); } } } } } } } } } } } private static void warn(String sql, int i, SQLException error) { GLogger.warn("error on set parametet index:" + i + " cause:" + error + " sql:" + sql); } }