mysql sql语法解析器_利用 druid 解析器解析SQL

最近参与一个开源项目,一个功能的实现,用到了 druid 解析器来解析SQL,记录下如果使用 druid 来解析SQL,实现对SQL的拦截改写。

1. 对 insert 语句进行解析:

private static String convertInsertSQL(String sql){

try{

MySqlStatementParser parser = new MySqlStatementParser(sql);

SQLStatement statement = parser.parseStatement();

MySqlInsertStatement insert = (MySqlInsertStatement)statement;

String tableName = StringUtil.removeBackquote(insert.getTableName().getSimpleName());

if(!isGlobalTable(tableName))

return sql;

if(!isInnerColExist(tableName))

return sql;

List columns = insert.getColumns();

if(columns == null || columns.size() <= 0)

return sql;

if(insert.getQuery() != null)// insert into tab select

return sql;

StringBuilder sb = new StringBuilder(200)// 指定初始容量可以提高性能

.append("insert into ")

.append(tableName).append("(");

int idx = -1;

for(int i = 0; i < columns.size(); i++) {

if(i < columns.size() - 1)

sb.append(columns.get(i).toString()).append(",");

else

sb.append(columns.get(i).toString());

String column = StringUtil.removeBackquote(insert.getColumns().get(i).toString());

if(column.equalsIgnoreCase(GLOBAL_TABLE_MYCAT_COLUMN))

idx = i;

}

if(idx <= -1)

sb.append(",").append(GLOBAL_TABLE_MYCAT_COLUMN);

sb.append(")");

sb.append(" values");

List vcl = insert.getValuesList();

if(vcl != null && vcl.size() > 1){// 批量insert

for(int j=0; j

if(j != vcl.size() - 1)

appendValues(vcl.get(j).getValues(), sb, idx).append(",");

else

appendValues(vcl.get(j).getValues(), sb, idx);

}

}else{// 非批量 insert

List valuse = insert.getValues().getValues();

appendValues(valuse, sb, idx);

}

List dku = insert.getDuplicateKeyUpdate();

if(dku != null && dku.size() > 0){

sb.append(" on duplicate key update ");

for(int i=0; i

SQLExpr exp = dku.get(i);

if(exp != null){

if(i < dku.size() - 1)

sb.append(exp.toString()).append(",");

else

sb.append(exp.toString());

}

}

}

return sb.toString();

}catch(Exception e){ // 发生异常,则返回原始 sql

LOGGER.warn(e.getMessage());

return sql;

}

}

三行代码就可以解析一条insert语句:

MySqlStatementParser parser = new MySqlStatementParser(sql);

SQLStatement statement = parser.parseStatement();

MySqlInsertStatement insert = (MySqlInsertStatement)statement;

然后使用解析得到的 insert ,就可以获得原始insert语句的各个部分:

List columns = insert.getColumns();  // 获得所有列名

insert.getQuery(); // 如果是 insert into select 语句,则可以获取 select查询

如果是批量插入的insert:insert into tab(id,name) values(1,'a'),(2,'b'),(3,'c');

则可以使用:

List vcl = insert.getValuesList();

获得素有的 values 子句部分。

非批量插入,则可以使用:

List valuse = insert.getValues().getValues();

获得 values 子句。

on duplicate 部分可以使用下面的语句获取:

List dku = insert.getDuplicateKeyUpdate();

获得了这些,就而已重组得到原始SQL语句,并且对其进行各种改写。

mysql 中的insert语法如下:

mysql> ? insert

Name: 'INSERT'

Description:

Syntax:

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]

[INTO] tbl_name

[PARTITION (partition_name,...)]

[(col_name,...)]

{VALUES | VALUE} ({expr | DEFAULT},...),(...),...

[ ON DUPLICATE KEY UPDATE

col_name=expr

[, col_name=expr] ... ]

Or:

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]

[INTO] tbl_name

[PARTITION (partition_name,...)]

SET col_name={expr | DEFAULT}, ...

[ ON DUPLICATE KEY UPDATE

col_name=expr

[, col_name=expr] ... ]

Or:

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]

[INTO] tbl_name

[PARTITION (partition_name,...)]

[(col_name,...)]

SELECT ...

[ ON DUPLICATE KEY UPDATE

col_name=expr

[, col_name=expr] ... ]

2. 解析 update 语句:

public static String convertUpdateSQL(String sql){

try{

MySqlStatementParser parser = new MySqlStatementParser(sql);

SQLStatement stmt = parser.parseStatement();

MySqlUpdateStatement update = (MySqlUpdateStatement)stmt;

SQLTableSource ts = update.getTableSource();

if(ts != null && ts.toString().contains(",")){

System.out.println(ts.toString());

LOGGER.warn("Do not support Multiple-table udpate syntax...");

return sql;

}

String tableName = StringUtil.removeBackquote(update.getTableName().getSimpleName());

if(!isGlobalTable(tableName))

return sql;

if(!isInnerColExist(tableName))

return sql;// 没有内部列

StringBuilder sb = new StringBuilder(150);

SQLExpr se = update.getWhere();

// where中有子查询: update company set name='com' where id in (select id from xxx where ...)

if(se instanceof SQLInSubQueryExpr){

// return sql;

int idx = sql.toUpperCase().indexOf(" SET ") + 5;

sb.append(sql.substring(0, idx)).append(GLOBAL_TABLE_MYCAT_COLUMN)

.append("=").append(operationTimestamp)

.append(",").append(sql.substring(idx));

return sb.toString();

}

String where = null;

if(update.getWhere() != null)

where = update.getWhere().toString();

SQLOrderBy orderBy = update.getOrderBy();

Limit limit = update.getLimit();

sb.append("update ").append(tableName).append(" set ");

List items = update.getItems();

boolean flag = false;

for(int i=0; i

SQLUpdateSetItem item = items.get(i);

String col = item.getColumn().toString();

String val = item.getValue().toString();

if(StringUtil.removeBackquote(col)

.equalsIgnoreCase(GLOBAL_TABLE_MYCAT_COLUMN)){

flag = true;

sb.append(col).append("=");

if(i != items.size() - 1)

sb.append(operationTimestamp).append(",");

else

sb.append(operationTimestamp);

}else{

sb.append(col).append("=");

if(i != items.size() -1 )

sb.append(val).append(",");

else

sb.append(val);

}

}

if(!flag){

sb.append(",").append(GLOBAL_TABLE_MYCAT_COLUMN)

.append("=").append(operationTimestamp);

}

sb.append(" where ").append(where);

if(orderBy != null && orderBy.getItems()!=null

&& orderBy.getItems().size() > 0){

sb.append(" order by ");

for(int i=0; i

SQLSelectOrderByItem item = orderBy.getItems().get(i);

SQLOrderingSpecification os = item.getType();

sb.append(item.getExpr().toString());

if(i < orderBy.getItems().size() - 1){

if(os != null)

sb.append(" ").append(os.toString());

sb.append(",");

}else{

if(os != null)

sb.append(" ").append(os.toString());

}

}

}

if(limit != null){// 分为两种情况: limit 10; limit 10,10;

sb.append(" limit ");

if(limit.getOffset() != null)

sb.append(limit.getOffset().toString()).append(",");

sb.append(limit.getRowCount().toString());

}

return sb.toString();

}catch(Exception e){

LOGGER.warn(e.getMessage());

return sql;

}

}

同样三行,解析update语句:

MySqlStatementParser parser = new MySqlStatementParser(sql);

SQLStatement stmt = parser.parseStatement();

MySqlUpdateStatement update = (MySqlUpdateStatement)stmt;

如果是 多表 udpate 语句,可以使用下面的语句进行判断:

SQLTableSource ts = update.getTableSource();

if(ts != null && ts.toString().contains(",")){

System.out.println(ts.toString());

LOGGER.warn("Do not support Multiple-table udpate syntax...");

return sql;

}

如果是单表update语句:

获得 update 语句的 where 部分:

SQLExpr se = update.getWhere();

// where中有子查询: update company set name='com' where id in (select id from xxx where ...)

if(se instanceof SQLInSubQueryExpr){

// return sql;

int idx = sql.toUpperCase().indexOf(" SET ") + 5;

sb.append(sql.substring(0, idx)).append(GLOBAL_TABLE_MYCAT_COLUMN)

.append("=").append(operationTimestamp)

.append(",").append(sql.substring(idx));

return sb.toString();

}

String where = null;

if(update.getWhere() != null)

where = update.getWhere().toString();

如果where 部分由 select 语句,由:se instanceof SQLInSubQueryExpr 来判断。

order by 和 limit 部分分别由:

SQLOrderBy orderBy = update.getOrderBy();

Limit limit = update.getLimit();

获得。

update 对应的 列和值,有下面的代码获得:

boolean flag = false;

for(int i=0; i

SQLUpdateSetItem item = items.get(i);

String col = item.getColumn().toString();

String val = item.getValue().toString();

解析得到了这些部分,就可以重组出原始的 update 语句,并且按照自己的需求进行SQL改写。

3. 解析 alter 语句:

String sql = "alter table t add colomn name varchar(30)";

MySqlStatementParser parser = new MySqlStatementParser(sql);

SQLStatement statement = parser.parseStatement();

MySqlAlterTableStatement alter = (MySqlAlterTableStatement)statement;

SQLExprTableSource source = alter.getTableSource();

String tableName = source.toString();

解析器:

com.alibaba

druid

1.0.14

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值