package jef.database.routing.sql;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import jef.common.log.LogUtil;
import jef.common.wrapper.IntRange;
import jef.database.ORMConfig;
import jef.database.jdbc.GenerateKeyReturnOper;
import jef.database.jdbc.JDBCTarget;
import jef.database.jsqlparser.statement.select.Limit;
import jef.database.jsqlparser.statement.select.Select;
import jef.database.jsqlparser.statement.select.Union;
import jef.database.jsqlparser.visitor.SelectBody;
import jef.database.jsqlparser.visitor.Statement;
import jef.database.routing.PartitionResult;
import jef.database.routing.jdbc.UpdateReturn;
import jef.database.wrapper.clause.BindSql;
import jef.database.wrapper.populator.AbstractResultSetTransformer;
import jef.database.wrapper.populator.ResultSetExtractor;
import jef.tools.PageLimit;
import jef.tools.StringUtils;
public class SimpleExecutionPlan implements ExecuteablePlan, QueryablePlan {
private Statement sql;
private List<Object> params;
private JDBCTarget db;
private String changeDataSource;
public String isChangeDatasource() {
return changeDataSource;
}
public SimpleExecutionPlan(Statement sql, List<Object> params, String bindDsName, JDBCTarget db) {
this.changeDataSource = bindDsName;
this.sql = sql;
this.params = params;
this.db = db;
}
@Override
public String getSql(String table) {
return null;
}
@Override
public UpdateReturn processUpdate(GenerateKeyReturnOper generateKeys) throws SQLException {
JDBCTarget db = this.db;
if (changeDataSource != null) {
db = db.getTarget(changeDataSource);
}
return db.innerExecuteUpdate(sql.toString(), params, generateKeys);
}
@Override
public boolean isMultiDatabase() {
return false;
}
@Override
public boolean isSimple() {
return true;
}
@Override
public PartitionResult[] getSites() {
throw new UnsupportedOperationException();
}
@Override
public ResultSet getResultSet(SqlAndParameter parse, int maxRows, int fetchSize) throws SQLException {
JDBCTarget db = this.db;
if (changeDataSource != null) {
// Scenario 2: 普通查询 (变更数据源,垂直拆分场景)
db = db.getTarget(changeDataSource);
}
String s = processPage(parse, sql, sql.toString());
return db.innerSelectBySql(s, AbstractResultSetTransformer.getRaw(fetchSize, maxRows), parse.params, parse);
}
@Override
public long getCount(SqlAndParameter paramHolder, int maxSize, int fetchSize) throws SQLException {
boolean debug = ORMConfig.getInstance().isDebugMode();
JDBCTarget db = this.db;
if (changeDataSource != null) {
db = db.getTarget(changeDataSource);
}
String sql = paramHolder.statement.toString();
long start = System.currentTimeMillis();
long total = db.innerSelectBySql(sql, ResultSetExtractor.COUNT_EXTRACTER, paramHolder.params, paramHolder);
total = (maxSize > 0 && maxSize < total) ? maxSize : total;
if (debug) {
long dbAccess = System.currentTimeMillis();
LogUtil.show(StringUtils.concat("Count:", String.valueOf(total), "\t ([DbAccess]:", String.valueOf(dbAccess - start), "ms) |", db.getTransactionId()));
}
return total;
}
public boolean mustGetAllResultsToCount() {
return false;
}
/*
* @param parse
*
* @param sql 如果传入null,则一定是union查询
*
* @param rawSQL
*
* @return
*/
private String processPage(SqlAndParameter parse, Statement sql, String rawSQL) {
if (parse.getLimit() != null) {
Limit limit = parse.getLimit();
int offset = 0;
int rowcount = 0;
if (limit.getOffsetJdbcParameter() != null) {
Object obj = parse.getParamsMap().get(limit.getOffsetJdbcParameter());
if (obj instanceof Number) {
offset = ((Number) obj).intValue();
}
} else {
offset = (int) limit.getOffset();
}
if (limit.getRowCountJdbcParameter() != null) {
Object obj = parse.getParamsMap().get(limit.getRowCountJdbcParameter());
if (obj instanceof Number) {
rowcount = ((Number) obj).intValue();
}
} else {
rowcount = (int) limit.getRowCount();
}
if (offset > 0 || rowcount > 0) {
parse.setNewLimit(null);
boolean isUnion = sql == null ? true : (((Select) sql).getSelectBody() instanceof Union);
BindSql bs = db.getProfile().getLimitHandler().toPageSQL(rawSQL, new int[] { offset, rowcount }, isUnion);
parse.setReverseResultSet(bs.getRsLaterProcessor());
return bs.getSql();
}
}
return rawSQL;
}
private String toPageSql(SqlAndParameter context, String rawSQL, PageLimit range) {
if (range == null && context.getLimit() == null) {
return rawSQL;
}
Statement sql = context.statement;
if (!(sql instanceof Select))
return rawSQL;
if (context.getLimit() != null) {
Limit limit = context.getLimit();
int offset = 0;
int rowcount = 0;
if (limit.getOffsetJdbcParameter() != null) {
Object obj = context.getParamsMap().get(limit.getOffsetJdbcParameter());
if (obj instanceof Number) {
offset = ((Number) obj).intValue();
}
} else {
offset = (int) limit.getOffset();
}
if (limit.getRowCountJdbcParameter() != null) {
Object obj = context.getParamsMap().get(limit.getRowCountJdbcParameter());
if (obj instanceof Number) {
rowcount = ((Number) obj).intValue();
}
} else {
rowcount = (int) limit.getRowCount();
}
if (offset > 0 || rowcount > 0) {
// 优先解析并执行SQL中指定的分页上的操作
// Range如果null,相当于清空limit,Range如果非null,相当于变为后过滤
context.setNewLimit(range);
IntRange range1 = new IntRange(offset + 1, offset + rowcount);
boolean isUnion = ((Select) sql).getSelectBody() instanceof Union;
BindSql bs = this.db.getProfile().getLimitHandler().toPageSQL(rawSQL, range1.toStartLimitSpan(), isUnion);
context.setReverseResultSet(bs.getRsLaterProcessor());
return bs.getSql();
}
}
if (range != null) {// 再执行后设置的结果限制操作
SelectBody sb = ((Select) sql).getSelectBody();
if (sb.getLimit() != null) {// 如果SQL中已经有限制的情况下,Range变为后过滤。
context.setNewLimit(range);
} else {
boolean isUnion = sb instanceof Union;
BindSql bs = this.db.getProfile().getLimitHandler().toPageSQL(rawSQL, range.toArray(), isUnion);
context.setReverseResultSet(bs.getRsLaterProcessor());
return bs.getSql();
}
}
return rawSQL;
}
@Override
public <T> T doQuery(SqlAndParameter sqlContext, ResultSetExtractor<T> extractor, boolean forCount, PageLimit range) throws SQLException {
JDBCTarget db = this.db;
if (changeDataSource != null) {
db = db.getTarget(changeDataSource);
}
String rawSQL = sqlContext.statement.toString();
rawSQL = toPageSql(sqlContext, rawSQL, range);
return db.innerSelectBySql(rawSQL, extractor, sqlContext.params, sqlContext);
}
}