sql语句解析实现

来源:互联网 发布:阿里云ecs linux教程 编辑:程序博客网 时间:2024/05/12 20:04

第一步:先对sql语句进行预处理;

对于用户,我们应该接受各种形式的查询语句书写,单行或者多行,语句中单个空格或者多个空格的间隔等等。但是我们要解析sql语句,就首先要让对它们做标准化,这样才能进行我们下一步处理。系统中的处理要求:

1)消除SQL语句前后的空白,将其中的连续空白字符(包括空格,TAB和回车换行)替换成单个空格;

2)将sql语句全变成小写形式(或大写形式);

3)在SQL语句的尾后加上结束符号“ENDOFSQL”(原因后面解释)

例如:用户输入:“select c1,c2,c3 from  t1,t2, t3 where condi1=5 and condi6=6 or condi7=7 order 

by g1,g2

通过预处理应该为:“select c1,c2,c3 from t1,t2,t3 where condi1=5 and condi6=6 or condi7=7 order by g1,g2”

第二步:将查询语句切分为语句块;

以查询语句为例(本文中主要是以查询语句作为例子讲解,其它删除,插入等语句原理于此类似,因为查询语句相对复杂,所以用来i讲解),正如上面我们标准化后的语句一样,我们要进行下一步的,对表中数据的处理,首先要知道是对那些表处理,要满足那些条件,输出那些属性,以什么顺序输出等。所以查询语句就可以分割为以下几个块:

1)select c1,c2,c3 from:属性输出块;块头(start)select,块尾(end)from,这个块我们关心的信息(body):c1,c2,c3;以下块类似分析

2)from....where; 涉及数据表块。

3)where.....order by; 查询条件块。

4)order by.....ENDOFSQL; 属性输出顺序。这里也就看出了我们为什么要在查询语句末尾加上结束符,是为了最后一个块的限定需要。

知道了如何分块,接下来要做的就是在我们已经标准化的sql语句上进行块的切割,这里我们用到了正则表达式,以第二个块from....where的查询为例,我们做个分析

"(from)(.+)( where | on | having | group by | order by | ENDOFSQL)“

以上就是第二个块的正则匹配式(其它块的匹配式下面也会给出),可以看出,在一个sql查询语句中,from块中跟from搭配出现的不只是where,还可以是on,having,group by等,那么通过这个正则式我们可以得到如下的块:

      from .... where

  from .... on

  from .... having

  from .... group by

  from .... order by

  from .... ENDOFSQL

这里我们要注意一点,就是在通过正则式对sql语句进行匹配时,我们不能对整个sql语句进行一次匹配操作,因为正则匹配是贪心匹配,它总是尽可能的向后查找,匹配到最大的语句段。就拿上述语句为例,如果通过对整个sql语句进行一次匹配,得到的就不是from....where这个语句段而是from .... where .... order by。显然这不是我们想要的。所以我们只能牺牲效率,通过对整个sql语句进行逐次递增的查询方式来查找相应的语句块。给出一个查询过程,加强理解,以上述sql语句为例,对第一个语句块的查找过程是

s
se
sel
.............
select c1,c2,c3 from

这样就找到第一个块,以此类推,找第二个块时候又从头逐次递增查找。

第三步:找到了各个块,我们还要把我们最关心的信息提取出来,就是夹在语句块头和尾之间的body部分,这个就好实现了,一般的sql语句中都会用逗号来做分割,我们提取出各个块的body信息。

步骤介绍完了,下面就上代码!

package com.hz.util.sql;import java.util.List;/** * 单句sql解析器制造工厂 *  * @author Blossom * @time 2016年9月9日 */public class SqlParserUtil {/** * 方法的主要入口 *  * @author Blossom * @time 2016年9月9日 * @param sql *            要解析的sql语句 * @return 返回解析结果 * @return_type String * */public String getParsedSql(String sql) {sql = sql.trim();sql = sql.toLowerCase();sql = sql.replace("\\s{1,}", " ");sql = "" + sql + " ENDOFSQL";return SingleSqlParserFactory.generateParser(sql).getParsedSql();}/** * SQL语句解析的接口 *  * @author Blossom * @time 2016年9月9日 * @param sql *            要解析的sql语句 * @return 返回解析结果 * @return_type List<SqlSegment> * */public List<SqlSegment> getParsedSqlList(String sql) {sql = sql.trim();sql = sql.trim();sql = sql.toLowerCase();sql = sql.replace("\\s{1,}", " ");sql = "" + sql + " ENDOFSQL";return SingleSqlParserFactory.generateParser(sql).returnSqlSegments();}}
<pre name="code" class="java">package com.hz.util.sql;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * sql语句片段 *  * @author Blossom * @time 2016年9月9日 */public class SqlSegment {private static final String Crlf = "@";private static final String FourSpace = "  ";// sql语句片段开头private String start;// sql语句片段中间部分private String body;// sql语句片段结束部分private String end;// 表示片段的正则表达式private String segmentRegExp;// 分割后的body小片段private List<String> bodyPieces;// 用于分割中间部分的正则表达式private String bodySplitPattern;/** * @author Blossom * @time 2016年9月9日 */public SqlSegment(String segmentRegExp, String bodySplitPattern) {start = "";body = "";end = "";this.segmentRegExp = segmentRegExp;this.bodySplitPattern = bodySplitPattern;this.bodyPieces = new ArrayList<>();}/** * 从sql中查找符合segmentRegExp的部分,并赋值到start,body,end三个属性中 *  * @author Blossom * @time 2016年9月9日 * @param sql * @return_type void * */public void parse(String sql) {Pattern pattern = Pattern.compile(segmentRegExp, Pattern.CASE_INSENSITIVE);for (int i = 0; i <= sql.length(); i++) {String shortSql = sql.substring(0, i);// 测试输出的子句是否正确 System.out.println(shortSql);Matcher matcher = pattern.matcher(shortSql);while (matcher.find()) {start = matcher.group(1);body = matcher.group(2);end = matcher.group(3);return;}}}/** * 解析body片段 *  * @author Blossom * @time 2016年9月9日 * @return_type void * */@SuppressWarnings("unused")private void parseBody() {List<String> ls = new ArrayList<String>();Pattern p = Pattern.compile(bodySplitPattern, Pattern.CASE_INSENSITIVE);// 先清除掉前后空格body = body.trim();Matcher m = p.matcher(body);StringBuffer sb = new StringBuffer();boolean result = m.find();while (result) {m.appendReplacement(sb, m.group(0) + Crlf);result = m.find();}m.appendTail(sb);// 再按空格断行String[] arr = sb.toString().split(" ");int arrLength = arr.length;for (int i = 0; i < arrLength; i++) {String temp = FourSpace + arr[i];if (i != arrLength - 1) {// temp=temp+Crlf; }ls.add(temp);}bodyPieces = ls;}}/** * 取得解析好的sql片段 *  * @author Blossom * @time 2016年9月9日 * @return * @return_type String * */public String getParsedSqlSegment() {StringBuffer sb = new StringBuffer();sb.append(start + Crlf);for (String piece : bodyPieces) {sb.append(piece + Crlf);}return sb.toString();}/** * @return the start */public String getStart() {return start;}/** * @param start *            the start to set */public void setStart(String start) {this.start = start;}/** * @return the body */public String getBody() {return body;}/** * @param body *            the body to set */public void setBody(String body) {this.body = body;}/** * @return the end */public String getEnd() {return end;}/** * @param end *            the end to set */public void setEnd(String end) {this.end = end;}/** * @return the segmentRegExp */public String getSegmentRegExp() {return segmentRegExp;}/** * @param segmentRegExp *            the segmentRegExp to set */public void setSegmentRegExp(String segmentRegExp) {this.segmentRegExp = segmentRegExp;}/** * @return the bodyPieces */public List<String> getBodyPieces() {return bodyPieces;}/** * @param bodyPieces *            the bodyPieces to set */public void setBodyPieces(List<String> bodyPieces) {this.bodyPieces = bodyPieces;}/** * @return the bodySplitPattern */public String getBodySplitPattern() {return bodySplitPattern;}/** * @param bodySplitPattern *            the bodySplitPattern to set */public void setBodySplitPattern(String bodySplitPattern) {this.bodySplitPattern = bodySplitPattern;}}
package com.hz.util.sql;import java.util.ArrayList;import java.util.List;/** * 单句sql解析器,单句即非嵌套的意思 *  * @author Blossom * @time 2016年9月9日 */public abstract class BaseSingleSqlParser {// 原始sql语句protected String originalSql;// sql语句片段protected List<SqlSegment> segments;/** * @author Blossom * @time 2016年9月9日 */public BaseSingleSqlParser(String originalSql) {this.originalSql = originalSql;segments = new ArrayList<>();initializeSegments();splitSqlToSegment();}/** * @author Blossom * @time 2016年9月9日 * @return_type void * */protected void splitSqlToSegment() {for (SqlSegment sqlSegment : segments) {sqlSegment.parse(originalSql);}}/** * @author Blossom * @time 2016年9月9日 * @return_type void * */protected abstract void initializeSegments();/** * 得到解析完毕的Sql语句 *  * @author Blossom * @time 2016年9月9日 * @return * @return_type String * */public String getParsedSql() {StringBuffer stringBuffer = new StringBuffer();for (SqlSegment sqlSegment : segments) {stringBuffer.append(sqlSegment.getParsedSqlSegment());}String result = stringBuffer.toString().replaceAll("@+", "\n");return result;}/** * 得到解析的sql片段 *  * @author Blossom * @time 2016年9月9日 * @return * @return_type List<SqlSegment> * */public List<SqlSegment> returnSqlSegments() {int segmentLength = this.segments.size();if (segmentLength != 0) {List<SqlSegment> result = this.segments;return result;}return null;}}

package com.hz.util.sql;/** * 单句删除语句解析 *  * @author Blossom * @time 2016年9月9日 */public class DeleteSqlParser extends BaseSingleSqlParser {/** * @author Blossom * @time 2016年9月9日 * @param originalSql */public DeleteSqlParser(String originalSql) {super(originalSql);}/** * @author Blossom * @time 2016年9月9日 * @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments() */@Overrideprotected void initializeSegments() {segments.add(new SqlSegment("(delete from)(.+)(where | ENDOFSQL)", "[,]"));segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)", "(and|or)"));}}

package com.hz.util.sql;/** * 单句查询插入语句解析器 *  * @author Blossom * @time 2016年9月9日 */public class InsertSelectSqlParser extends BaseSingleSqlParser {/** * @author Blossom * @time 2016年9月9日 * @param originalSql */public InsertSelectSqlParser(String originalSql) {super(originalSql);}/** * @author Blossom * @time 2016年9月9日 * @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments() */@Overrideprotected void initializeSegments() {segments.add(new SqlSegment("(insert into)(.+)( select )", "[,]"));segments.add(new SqlSegment("(select)(.+)(from)", "[,]"));segments.add(new SqlSegment("(from)(.+)( where | on | having | groups+by | orders+by | ENDOFSQL)","(,|s+lefts+joins+|s+rights+joins+|s+inners+joins+)"));segments.add(new SqlSegment("(where|on|having)(.+)( groups+by | orders+by | ENDOFSQL)", "(and|or)"));segments.add(new SqlSegment("(groups+by)(.+)( orders+by| ENDOFSQL)", "[,]"));segments.add(new SqlSegment("(orders+by)(.+)( ENDOFSQL)", "[,]"));}}
package com.hz.util.sql;/** * 单句插入语句解析 *  * @author Blossom * @time 2016年9月9日 */public class InsertSqlParser extends BaseSingleSqlParser {/** * @author Blossom * @time 2016年9月9日 * @param originalSql */public InsertSqlParser(String originalSql) {super(originalSql);}/** * @author Blossom * @time 2016年9月9日 * @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments() */@Overrideprotected void initializeSegments() {segments.add(new SqlSegment("(insert into)(.+)([(])", "[,]"));segments.add(new SqlSegment("([(])(.+)( [)] values )", "[,]"));segments.add(new SqlSegment("([)] values [(])(.+)( [)])", "[,]"));}/** * @author Blossom * @time 2016年9月9日 * @return * @see com.hz.util.sql.BaseSingleSqlParser#getParsedSql() */@Overridepublic String getParsedSql() {String result = super.getParsedSql();result += ")";return result;}}
package com.hz.util.sql;/** * 单句查询语句解析器 *  * @author Blossom * @time 2016年9月9日 */public class SelectSqlParser extends BaseSingleSqlParser {/** * @author Blossom * @time 2016年9月9日 * @param originalSql */public SelectSqlParser(String originalSql) {super(originalSql);}/** * @author Blossom * @time 2016年9月9日 * @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments() */@Overrideprotected void initializeSegments() {segments.add(new SqlSegment("(select)(.+)(from)", "[,]"));segments.add(new SqlSegment("(from)(.+)( where | on | having | group by | order by | ENDOFSQL)","(,| left join | right join | inner join )"));segments.add(new SqlSegment("(where|on|having)(.+)( group by | order by | ENDOFSQL)", "(and|or)"));segments.add(new SqlSegment("(group by)(.+)( order by| ENDOFSQL)", "[,]"));segments.add(new SqlSegment("(order by)(.+)( ENDOFSQL)", "[,]"));}}
package com.hz.util.sql;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * @author Blossom * @time 2016年9月9日 */public class SingleSqlParserFactory {public static BaseSingleSqlParser generateParser(String sql) {if (contains(sql, "(insert into)(.+)(select)(.+)(from)(.+)")) {return new InsertSelectSqlParser(sql);} else if (contains(sql, "(select)(.+)(from)(.+)")) {return new SelectSqlParser(sql);} else if (contains(sql, "(delete from)(.+)")) {return new DeleteSqlParser(sql);} else if (contains(sql, "(update)(.+)(set)(.+)")) {return new UpdateSqlParser(sql);} else if (contains(sql, "(insert into)(.+)(values)(.+)")) {return new InsertSqlParser(sql);}return new InsertSqlParser(sql);}/** * 看word是否在lineText中存在,支持正则表达式 *  * @author Blossom * @time 2016年9月9日 * @param sql * @param regExp * @return * @return_type boolean * */private static boolean contains(String sql, String regExp) {Pattern pattern = Pattern.compile(regExp, Pattern.CASE_INSENSITIVE);Matcher matcher = pattern.matcher(sql);return matcher.find();}}

package com.hz.util.sql;/** * 单句更新sql语句 *  * @author Blossom * @time 2016年9月9日 */public class UpdateSqlParser extends BaseSingleSqlParser {/** * @author Blossom * @time 2016年9月9日 * @param originalSql */public UpdateSqlParser(String originalSql) {super(originalSql);}/** * @author Blossom * @time 2016年9月9日 * @see com.hz.util.sql.BaseSingleSqlParser#initializeSegments() */@Overrideprotected void initializeSegments() {segments.add(new SqlSegment("(update)(.+)(set)", "[,]"));segments.add(new SqlSegment("(set)(.+)( where | ENDOFSQL)", "[,]"));segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)", "(and|or)"));}}







0 0
原创粉丝点击