use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.
the class DruidUpdateParser method shardColCanBeUpdated.
/*
* 遍历where子句的AST,寻找是否有与update子句中更新分片字段相同的条件,
* o 如果发现有or或者xor,然后分片字段的条件在or或者xor中的,这种情况update也无法执行,比如
* update mytab set ptn_col = val, col1 = val1 where col1 = val11 or ptn_col = val;
* 但是下面的这种update是可以执行的
* update mytab set ptn_col = val, col1 = val1 where ptn_col = val and (col1 = val11 or col2 = val2);
* o 如果没有发现与update子句中更新分片字段相同的条件,则update也无法执行,比如
* update mytab set ptn_col = val, col1 = val1 where col1 = val11 and col2 = val22;
* o 如果条件之间都是and,且有与更新分片字段相同的条件,这种情况是允许执行的。比如
* update mytab set ptn_col = val, col1 = val1 where ptn_col = val and col1 = val11 and col2 = val2;
* o 对于一些特殊的运算符,比如between,not,或者子查询,遇到这些子句现在不会去检查分片字段是否在此类子句中,
* 即使分片字段在此类子句中,现在也认为对应的update语句无法执行。
*
* @param whereClauseExpr where子句的语法树AST
* @param column 分片字段的名字
* @param value 分片字段要被更新成的值
* @hasOR 遍历到whereClauseExpr这个节点的时候,其上层路径中是否有OR/XOR关系运算
*
* @return true,表示update不能执行,false表示可以执行
*/
private boolean shardColCanBeUpdated(SQLExpr whereClauseExpr, String column, SQLExpr value, boolean hasOR) throws SQLNonTransientException {
boolean canUpdate = false;
boolean parentHasOR = false;
if (whereClauseExpr == null)
return false;
if (whereClauseExpr instanceof SQLBinaryOpExpr) {
SQLBinaryOpExpr nodeOpExpr = (SQLBinaryOpExpr) whereClauseExpr;
/*
* 条件中有or或者xor的,如果分片字段出现在or/xor的一个子句中,则此update
* 语句无法执行
*/
if ((nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanOr) || (nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanXor)) {
parentHasOR = true;
}
// 发现类似 col = value 的子句
if (nodeOpExpr.getOperator() == SQLBinaryOperator.Equality) {
boolean foundCol;
SQLExpr leftExpr = nodeOpExpr.getLeft();
SQLExpr rightExpr = nodeOpExpr.getRight();
foundCol = columnInExpr(leftExpr, column);
// 发现col = value子句,col刚好是分片字段,比较value与update要更新的值是否一样,并且是否在or/xor子句中
if (foundCol) {
if (rightExpr.getClass() != value.getClass()) {
throw new SQLNonTransientException("SQL AST nodes type mismatch!");
}
canUpdate = rightExpr.toString().equals(value.toString()) && (!hasOR) && (!parentHasOR);
}
} else if (nodeOpExpr.getOperator().isLogical()) {
if (nodeOpExpr.getLeft() != null) {
if (nodeOpExpr.getLeft() instanceof SQLBinaryOpExpr) {
canUpdate = shardColCanBeUpdated(nodeOpExpr.getLeft(), column, value, parentHasOR);
}
// else
// 此子语句不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y
// 或者 NOT,或者单独的子查询,这些情况,我们不做处理
}
if ((!canUpdate) && nodeOpExpr.getRight() != null) {
if (nodeOpExpr.getRight() instanceof SQLBinaryOpExpr) {
canUpdate = shardColCanBeUpdated(nodeOpExpr.getRight(), column, value, parentHasOR);
}
// else
// 此子语句不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y
// 或者 NOT,或者单独的子查询,这些情况,我们不做处理
}
} else if (isSubQueryClause(nodeOpExpr)) {
// 对于子查询的检查有点复杂,这里暂时不支持
return false;
}
// else
// 其他类型的子句,忽略, 如果分片字段在这类子句中,此类情况目前不做处理,将返回false
}
return canUpdate;
}
use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.
the class FunctionParser method parseFunction.
public static Function parseFunction(String function) throws SQLNonTransientException {
StringBuilder buffer = new StringBuilder();
Stack<Function> functions = new Stack<>();
int flag = 0;
for (int i = 0; i < function.length(); i++) {
char current = function.charAt(i);
switch(current) {
case Commons.LEFT_BRACKET:
if (flag == 0) {
String currentIdentifier = buffer.toString().trim();
buffer = new StringBuilder();
if (!StringUtil.isEmpty(currentIdentifier)) {
Function function1 = new Function(currentIdentifier);
if (!functions.empty() && functions.peek() != null) {
functions.peek().getArguments().add(function1);
}
functions.push(function1);
}
break;
}
buffer.append(current);
break;
case Commons.ARGUMENT_SEPARATOR:
if (flag == 0 || flag == 3) {
String currentIdentifier = buffer.toString().trim();
buffer = new StringBuilder();
if (!StringUtil.isEmpty(currentIdentifier)) {
if (flag == 3) {
flag = 0;
Identifier identifier = new Identifier(currentIdentifier);
functions.peek().getArguments().add(identifier);
} else {
Field field = new Field(currentIdentifier);
functions.peek().getArguments().add(field);
}
}
break;
}
buffer.append(current);
break;
case Commons.RIGHT_BRACKET:
if (flag != 1 && flag != 2) {
String currentIdentifier = buffer.toString().trim();
buffer = new StringBuilder();
if (!StringUtil.isEmpty(currentIdentifier)) {
if (flag == 3) {
flag = 0;
Identifier identifier = new Identifier(currentIdentifier);
functions.peek().getArguments().add(identifier);
} else {
Field field = new Field(currentIdentifier);
functions.peek().getArguments().add(field);
}
}
if (flag == 0) {
if (functions.size() == 1) {
return functions.pop();
} else {
functions.pop();
}
}
break;
}
buffer.append(current);
break;
case Commons.QUOTE:
if (flag == 0) {
flag = 1;
} else if (flag == 1) {
flag = 3;
}
case Commons.DOUBLE_QUOTE:
if (flag == 0) {
flag = 2;
} else if (flag == 2) {
flag = 3;
}
default:
buffer.append(current);
}
}
throw new SQLNonTransientException("Function is not in right format!");
}
use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.
the class DruidMycatRouteStrategy method routeNormalSqlWithAST.
@Override
public RouteResultset routeNormalSqlWithAST(SchemaConfig schema, String stmt, RouteResultset rrs, String charset, LayerCachePool cachePool) throws SQLNonTransientException {
/**
* 只有mysql时只支持mysql语法
*/
SQLStatementParser parser = null;
if (schema.isNeedSupportMultiDBType()) {
parser = new MycatStatementParser(stmt);
} else {
parser = new MySqlStatementParser(stmt);
}
MycatSchemaStatVisitor visitor = null;
SQLStatement statement;
/**
* 解析出现问题统一抛SQL语法错误
*/
try {
statement = parser.parseStatement();
visitor = new MycatSchemaStatVisitor();
} catch (Exception t) {
LOGGER.error("DruidMycatRouteStrategyError", t);
throw new SQLSyntaxErrorException(t);
}
/**
* 检验unsupported statement
*/
checkUnSupportedStatement(statement);
DruidParser druidParser = DruidParserFactory.create(schema, statement, visitor);
druidParser.parser(schema, rrs, statement, stmt, cachePool, visitor);
DruidShardingParseInfo ctx = druidParser.getCtx();
rrs.setTables(ctx.getTables());
/**
* DruidParser 解析过程中已完成了路由的直接返回
*/
if (rrs.isFinishedRoute()) {
return rrs;
}
/**
* 没有from的select语句或其他
*/
if ((ctx.getTables() == null || ctx.getTables().size() == 0) && (ctx.getTableAliasMap() == null || ctx.getTableAliasMap().isEmpty())) {
return RouterUtil.routeToSingleNode(rrs, schema.getRandomDataNode(), druidParser.getCtx().getSql());
}
if (druidParser.getCtx().getRouteCalculateUnits().size() == 0) {
RouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();
druidParser.getCtx().addRouteCalculateUnit(routeCalculateUnit);
}
SortedSet<RouteResultsetNode> nodeSet = new TreeSet<RouteResultsetNode>();
for (RouteCalculateUnit unit : druidParser.getCtx().getRouteCalculateUnits()) {
RouteResultset rrsTmp = RouterUtil.tryRouteForTables(schema, druidParser.getCtx(), unit, rrs, isSelect(statement), cachePool);
if (rrsTmp != null) {
for (RouteResultsetNode node : rrsTmp.getNodes()) {
nodeSet.add(node);
}
}
}
RouteResultsetNode[] nodes = new RouteResultsetNode[nodeSet.size()];
int i = 0;
for (RouteResultsetNode aNodeSet : nodeSet) {
nodes[i] = aNodeSet;
if (statement instanceof MySqlInsertStatement && ctx.getTables().size() == 1 && schema.getTables().containsKey(ctx.getTables().get(0))) {
RuleConfig rule = schema.getTables().get(ctx.getTables().get(0)).getRule();
if (rule != null && rule.getRuleAlgorithm() instanceof SlotFunction) {
aNodeSet.setStatement(ParseUtil.changeInsertAddSlot(aNodeSet.getStatement(), aNodeSet.getSlot()));
}
}
i++;
}
rrs.setNodes(nodes);
/**
* subTables="t_order$1-2,t_order3"
*目前分表 1.6 开始支持 幵丏 dataNode 在分表条件下只能配置一个,分表条件下不支持join。
*/
if (rrs.isDistTable()) {
return this.routeDisTable(statement, rrs);
}
return rrs;
}
use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.
the class RouterUtil method ruleByJoinValueCalculate.
/**
* @return dataNodeIndex -> [partitionKeysValueTuple+]
*/
public static Set<String> ruleByJoinValueCalculate(RouteResultset rrs, TableConfig tc, Set<ColumnRoutePair> colRoutePairSet) throws SQLNonTransientException {
String joinValue = "";
if (colRoutePairSet.size() > 1) {
LOGGER.warn("joinKey can't have multi Value");
} else {
Iterator<ColumnRoutePair> it = colRoutePairSet.iterator();
ColumnRoutePair joinCol = it.next();
joinValue = joinCol.colValue;
}
Set<String> retNodeSet = new LinkedHashSet<String>();
Set<String> nodeSet;
if (tc.isSecondLevel() && tc.getParentTC().getPartitionColumn().equals(tc.getParentKey())) {
// using
// parent
// rule to
// find
// datanode
nodeSet = ruleCalculate(tc.getParentTC(), colRoutePairSet, rrs.getDataNodeSlotMap());
if (nodeSet.isEmpty()) {
throw new SQLNonTransientException("parent key can't find valid datanode ,expect 1 but found: " + nodeSet.size());
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("found partion node (using parent partion rule directly) for child table to insert " + nodeSet + " sql :" + rrs.getStatement());
}
retNodeSet.addAll(nodeSet);
// }
return retNodeSet;
} else {
retNodeSet.addAll(tc.getParentTC().getDataNodes());
}
return retNodeSet;
}
use of java.sql.SQLNonTransientException in project Mycat-Server by MyCATApache.
the class RouterUtil method processERChildTable.
public static boolean processERChildTable(final SchemaConfig schema, final String origSQL, final ServerConnection sc) throws SQLNonTransientException {
String tableName = StringUtil.getTableName(origSQL).toUpperCase();
final TableConfig tc = schema.getTables().get(tableName);
//判断是否为子表,如果不是,只会返回false
if (null != tc && tc.isChildTable()) {
final RouteResultset rrs = new RouteResultset(origSQL, ServerParse.INSERT);
String joinKey = tc.getJoinKey();
//因为是Insert语句,用MySqlInsertStatement进行parse
MySqlInsertStatement insertStmt = (MySqlInsertStatement) (new MySqlStatementParser(origSQL)).parseInsert();
//判断条件完整性,取得解析后语句列中的joinkey列的index
int joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);
if (joinKeyIndex == -1) {
String inf = "joinKey not provided :" + tc.getJoinKey() + "," + insertStmt;
LOGGER.warn(inf);
throw new SQLNonTransientException(inf);
}
//子表不支持批量插入
if (isMultiInsert(insertStmt)) {
String msg = "ChildTable multi insert not provided";
LOGGER.warn(msg);
throw new SQLNonTransientException(msg);
}
//取得joinkey的值
String joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();
//解决bug #938,当关联字段的值为char类型时,去掉前后"'"
String realVal = joinKeyVal;
if (joinKeyVal.startsWith("'") && joinKeyVal.endsWith("'") && joinKeyVal.length() > 2) {
realVal = joinKeyVal.substring(1, joinKeyVal.length() - 1);
}
String sql = insertStmt.toString();
// try to route by ER parent partion key
//如果是二级子表(父表不再有父表),并且分片字段正好是joinkey字段,调用routeByERParentKey
RouteResultset theRrs = RouterUtil.routeByERParentKey(sc, schema, ServerParse.INSERT, sql, rrs, tc, realVal);
if (theRrs != null) {
boolean processedInsert = false;
//判断是否需要全局序列号
if (sc != null && tc.isAutoIncrement()) {
String primaryKey = tc.getPrimaryKey();
processedInsert = processInsert(sc, schema, ServerParse.INSERT, sql, tc.getName(), primaryKey);
}
if (processedInsert == false) {
rrs.setFinishedRoute(true);
sc.getSession2().execute(rrs, ServerParse.INSERT);
}
return true;
}
// route by sql query root parent's datanode
//如果不是二级子表或者分片字段不是joinKey字段结果为空,则启动异步线程去后台分片查询出datanode
//只要查询出上一级表的parentkey字段的对应值在哪个分片即可
final String findRootTBSql = tc.getLocateRTableKeySql().toLowerCase() + joinKeyVal;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find root parent's node sql " + findRootTBSql);
}
ListenableFuture<String> listenableFuture = MycatServer.getInstance().getListeningExecutorService().submit(new Callable<String>() {
@Override
public String call() throws Exception {
FetchStoreNodeOfChildTableHandler fetchHandler = new FetchStoreNodeOfChildTableHandler();
// return fetchHandler.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes());
return fetchHandler.execute(schema.getName(), findRootTBSql, tc.getRootParent().getDataNodes(), sc);
}
});
Futures.addCallback(listenableFuture, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
//结果为空,证明上一级表中不存在那条记录,失败
if (Strings.isNullOrEmpty(result)) {
StringBuilder s = new StringBuilder();
LOGGER.warn(s.append(sc.getSession2()).append(origSQL).toString() + " err:" + "can't find (root) parent sharding node for sql:" + origSQL);
if (!sc.isAutocommit()) {
// 处于事务下失败, 必须回滚
sc.setTxInterrupt("can't find (root) parent sharding node for sql:" + origSQL);
}
sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, "can't find (root) parent sharding node for sql:" + origSQL);
return;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("found partion node for child table to insert " + result + " sql :" + origSQL);
}
//找到分片,进行插入(和其他的一样,需要判断是否需要全局自增ID)
boolean processedInsert = false;
if (sc != null && tc.isAutoIncrement()) {
try {
String primaryKey = tc.getPrimaryKey();
processedInsert = processInsert(sc, schema, ServerParse.INSERT, origSQL, tc.getName(), primaryKey);
} catch (SQLNonTransientException e) {
LOGGER.warn("sequence processInsert error,", e);
sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, "sequence processInsert error," + e.getMessage());
}
}
if (processedInsert == false) {
RouteResultset executeRrs = RouterUtil.routeToSingleNode(rrs, result, origSQL);
sc.getSession2().execute(executeRrs, ServerParse.INSERT);
}
}
@Override
public void onFailure(Throwable t) {
StringBuilder s = new StringBuilder();
LOGGER.warn(s.append(sc.getSession2()).append(origSQL).toString() + " err:" + t.getMessage());
sc.writeErrMessage(ErrorCode.ER_PARSE_ERROR, t.getMessage() + " " + s.toString());
}
}, MycatServer.getInstance().getListeningExecutorService());
return true;
}
return false;
}
Aggregations