Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* #%L
* ddal-jsqlparser
* %%
* Copyright (C) 2016 - 2017 the original author or authors.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.hellojavaer.ddal.jsqlparser;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Database;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.hellojavaer.ddal.ddr.datasource.exception.CrossPreparedStatementException;
import org.hellojavaer.ddal.ddr.shard.RangeShardValue;
import org.hellojavaer.ddal.ddr.shard.ShardRouteConfig;
import org.hellojavaer.ddal.ddr.shard.ShardRouteInfo;
import org.hellojavaer.ddal.ddr.shard.ShardRouter;
import org.hellojavaer.ddal.ddr.shard.rule.SpelShardRouteRule;
import org.hellojavaer.ddal.ddr.shard.simple.SimpleShardParser;
import org.hellojavaer.ddal.ddr.shard.simple.SimpleShardRouteRuleBinding;
import org.hellojavaer.ddal.ddr.shard.simple.SimpleShardRouter;
import org.hellojavaer.ddal.ddr.sqlparse.SQLParsedResult;
import org.hellojavaer.ddal.ddr.sqlparse.SQLParsedState;
import org.hellojavaer.ddal.ddr.sqlparse.exception.*;
import org.hellojavaer.ddal.ddr.utils.DDRJSONUtils;
import org.hellojavaer.ddal.ddr.utils.DDRStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.StringReader;
import java.util.*;
/**
*
* JSQLParserAdapter sql解析适配器
* 1.支持解析insert,update,delete,select 4种类型的sql解析,支持子语句嵌套和union all语法;
* 2.如果sql匹配到分表信息
* 2.1 分表select会被自动添加别名;
* 2.2 分表非select操作不会被自动添加别名,如果column有表名前缀,前缀会根据路由信息动态被替换;
* 2.3 分表路由值的获取
* 2.3.1 如果路由规则中配置了分片字段,则检查sql参数及jdbc参数是否命中路由
* 2.3.1.1 如果分表字段是不含'not'修饰符的'=','between'或'in()'操作,则命中分表路由;
* 2.3.1.2 between和in操作允许混合使用sql参数和jdbc参数(eg:'id between(1 and ?)' 或者 'id in(1,2,?)');
* 2.3.2 如果路由规则中未配置分片字段或(2.3.1)未命中路由值则通过ShardRoute注解或ShardRouteContext设置的路由信息进行路由,
* 若ShardRouteContext中也未获取到匹配的路由信息则抛异常;
* (注:在指定分表字段后支持ShardRouteContext方式是为了能够提供'扫表'功能)
* 3.如果解析过程如果没有匹配到分表配置,sql语句中的关键字格式化后返回(关键字大写);
*
* @author Kaiming Zou,created on 12/11/2016.
*/
public class JSQLParserAdapter extends JSQLBaseVisitor {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private String sql;
private ShardRouter shardRouter;
private Statement statement;
// the schemas which used in current sql
private Set schemas = new HashSet<>();
private boolean enableLimitCheck = false;
private List toBeConvertedTables = new ArrayList<>();
static {
try {
checkJSqlParserFeature();
checkCompatibilityWithJSqlParser();
} catch (Exception e) {
throw new RuntimeException("JSqlParser feature check failed", e);
}
}
/**
* To make ddal-jsqlparser work well, JSqlParser should include the feature of 'support getting jdbc parameter index'.
* And this feature is provided on the version of {@link 0.9.7}.
* This method is designed to check the necessary feature.
*/
public static void checkJSqlParserFeature() throws JSQLParserException {
CCJSqlParserManager parserManager = new CCJSqlParserManager();
String sql = "SELECT * FROM tab_1 WHERE tab_1.col_1 = ? AND col_2 IN (SELECT DISTINCT col_2 FROM tab_2 WHERE col_3 LIKE ? AND col_4 > ?) LIMIT ?, ?";
Select select = (Select) parserManager.parse(new StringReader(sql));
PlainSelect selectBody = (PlainSelect) select.getSelectBody();
//
AndExpression andExpression = (AndExpression) selectBody.getWhere();
EqualsTo equalsTo = (EqualsTo) andExpression.getLeftExpression();
JdbcParameter jdbcParameter = (JdbcParameter) equalsTo.getRightExpression();
Integer index1 = jdbcParameter.getIndex();
if (index1 != 1) {
throw new IllegalStateException("Current version of JSQLParser doesn't support the feature of 'support "
+ "get jdbc parameter index'");
}
//
InExpression inExpression = (InExpression) andExpression.getRightExpression();
SubSelect subSelect = (SubSelect) inExpression.getRightItemsList();
PlainSelect subSelectBody = (PlainSelect) subSelect.getSelectBody();
AndExpression subAndExpression = (AndExpression) subSelectBody.getWhere();
LikeExpression likeExpression = (LikeExpression) subAndExpression.getLeftExpression();
if (((JdbcParameter) likeExpression.getRightExpression()).getIndex() != 2) {
throw new IllegalStateException(
"Current version of JSQLParser doesn't support the feature of 'support get jdbc parameter index'");
}
//
GreaterThan greaterThan = (GreaterThan) subAndExpression.getRightExpression();
if (((JdbcParameter) greaterThan.getRightExpression()).getIndex() != 3) {
throw new IllegalStateException(
"Current version of JSQLParser doesn't support the feature of 'support get jdbc parameter index'");
}
//
Expression offset = selectBody.getLimit().getOffset();
Expression rowCount = selectBody.getLimit().getRowCount();
if (((JdbcParameter) offset).getIndex() != 4 || ((JdbcParameter) rowCount).getIndex() != 5) {
throw new IllegalStateException(
"Current version of JSQLParser doesn't support the feature of 'support get jdbc parameter index'");
}
}
public static void checkCompatibilityWithJSqlParser() {
List bindings = new ArrayList<>();
SpelShardRouteRule numRule = new SpelShardRouteRule("{scName}_{format('%02d', sdValue % 8)}",
"{tbName}_{format('%04d', sdValue % 128)}");
SimpleShardRouteRuleBinding user = new SimpleShardRouteRuleBinding();
user.setScName("db");
user.setTbName("user");
user.setSdKey("id");
user.setSdValues("[1..128]");
user.setRule(numRule);
bindings.add(user);
SimpleShardRouter shardRouter = new SimpleShardRouter(bindings);
SimpleShardParser parser = new SimpleShardParser(new JSQLParser(), shardRouter);
// insert
SQLParsedResult parsedResult = parser.parse("insert into db.user(id,`desc`) values(506,'desc')", null);
if (!parsedResult.getSql().equals("INSERT INTO db_02.user_0122 (id, `desc`) VALUES (506, 'desc')")) {
throw new IllegalStateException("ddal-jsqlparser is not compatibility with current version of JSqlParser");
}
// delete
parsedResult = parser.parse("delete from db.user where id = 506 and `desc` = 'desc'", null);
if (!parsedResult.getSql().equals("DELETE FROM db_02.user_0122 WHERE id = 506 AND `desc` = 'desc'")) {
throw new IllegalStateException("ddal-jsqlparser is not compatibility with current version of JSqlParser");
}
// update
parsedResult = parser.parse("update db.user set `desc` = 'desc' where id = 506", null);
if (!parsedResult.getSql().equals("UPDATE db_02.user_0122 SET `desc` = 'desc' WHERE id = 506")) {
throw new IllegalStateException("ddal-jsqlparser is not compatibility with current version of JSqlParser");
}
// select
parsedResult = parser.parse("select * from db.user where id = 506 and `desc` = 'desc'", null);
if (!parsedResult.getSql().equals("SELECT * FROM db_02.user_0122 AS user WHERE id = 506 AND `desc` = 'desc'")) {
throw new IllegalStateException("ddal-jsqlparser is not compatibility with current version of JSqlParser");
}
}
public JSQLParserAdapter(String sql, ShardRouter shardRouter, boolean enableLimitCheck) {
this.sql = sql;
this.shardRouter = shardRouter;
this.enableLimitCheck = enableLimitCheck;
try {
this.statement = CCJSqlParserUtil.parse(sql);
} catch (Throwable e) {
throw new SQLSyntaxErrorException("sql is [" + sql + "]", e);
}
if (statement instanceof Select //
|| statement instanceof Update//
|| statement instanceof Insert//
|| statement instanceof Delete) {
// ok
} else {
throw new UnsupportedSQLExpressionException(
"Sql ["
+ sql
+ "] is not supported in shard sql. Only support 'select' 'insert' 'update' and 'delete' sql statement");
}
}
private String generateSplitString(String str) {
Random random = new Random(System.currentTimeMillis());
while (true) {
long l = random.nextLong();
if (l < 0) {
l = -l;
}
String tar = "__" + l;
if (str.indexOf(tar) < 0) {
return tar;
}
}
}
public SQLParsedState parse() {
try {
statement.accept(this);
String targetSql = statement.toString();
//
String splitString = generateSplitString(targetSql);
for (int i = 0; i < toBeConvertedTables.size(); i++) {
TableWrapper tab = toBeConvertedTables.get(i);
// sql param的解析结果已经存储在routedFullTableName中,因此可以覆盖schemaName和name中的值;
tab.setSchemaName(null);
tab.setName("_" + i + splitString);
}
//
targetSql = statement.toString();
//
final List