package com.taobao.tddl.repo.mysql.spi;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import com.taobao.tddl.common.exception.TddlRuntimeException;
import com.taobao.tddl.common.jdbc.ParameterContext;
import com.taobao.tddl.common.jdbc.ParameterMethod;
import com.taobao.tddl.executor.common.ExecutionContext;
import com.taobao.tddl.executor.cursor.ICursorMeta;
import com.taobao.tddl.executor.cursor.ISchematicCursor;
import com.taobao.tddl.executor.cursor.impl.AffectRowCursor;
import com.taobao.tddl.executor.cursor.impl.ResultSetCursor;
import com.taobao.tddl.executor.record.CloneableRecord;
import com.taobao.tddl.executor.rowset.IRowSet;
import com.taobao.tddl.executor.spi.ITable;
import com.taobao.tddl.optimizer.config.table.IndexMeta;
import com.taobao.tddl.optimizer.core.plan.IDataNodeExecutor;
import com.taobao.tddl.optimizer.core.plan.IPut;
import com.taobao.tddl.optimizer.core.plan.IQueryTree;
import com.taobao.tddl.optimizer.core.plan.query.IQuery;
import com.taobao.tddl.repo.mysql.common.ResultSetAutoCloseConnection;
import com.taobao.tddl.repo.mysql.common.ResultSetRemeberIfClosed;
import com.taobao.tddl.repo.mysql.sqlconvertor.MysqlPlanVisitorImpl;
import com.taobao.tddl.repo.mysql.sqlconvertor.SqlAndParam;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* jdbc 方法执行相关的数据封装. 每个需要执行的cursor都可以持有这个对象进行数据库操作。 ps .. fuxk java
* ,没有多继承。。只能用组合。。你懂的。。 类不是线程安全的哦亲
*
* @author whisper
*/
public class My_JdbcHandler implements GeneralQueryHandler {
private static final Logger logger = LoggerFactory.getLogger(My_JdbcHandler.class);
protected My_Transaction myTransaction = null;
protected Connection connection = null;
protected ResultSet resultSet = null;
protected PreparedStatement ps = null;
protected ExecutionType executionType = null;
protected IRowSet current = null;
protected IRowSet prev_kv = null;
protected ICursorMeta cursorMeta;
protected boolean isStreaming = false;
protected String groupName = null;
protected DataSource ds = null;
protected ExecutionContext executionContext = null;
protected boolean initPrev = false;
protected IDataNodeExecutor plan;
public enum ExecutionType {
PUT, GET
}
public My_JdbcHandler(ExecutionContext executionContext){
this.executionContext = executionContext;
}
@Override
public void executeQuery(ICursorMeta meta, boolean isStreaming) throws SQLException {
setContext(meta, isStreaming);
SqlAndParam sqlAndParam = null;
boolean isControlSql = false;
sqlAndParam = new SqlAndParam();
if (plan instanceof IQuery && ((IQuery) plan).getSql() != null) {
sqlAndParam.sql = ((IQuery) plan).getSql();
if (executionContext.getParams() != null) {
sqlAndParam.param = executionContext.getParams();
} else {
sqlAndParam.param = new HashMap<Integer, ParameterContext>();
}
isControlSql = true;
} else {
cursorMeta.setIsSureLogicalIndexEqualActualIndex(true);
if (plan instanceof IQueryTree) {
((IQueryTree) plan).setTopQuery(true);
MysqlPlanVisitorImpl visitor = new MysqlPlanVisitorImpl(plan, null, null, true);
plan.accept(visitor);
sqlAndParam.sql = visitor.getString();
sqlAndParam.param = visitor.getParamMap();
}
}
try {
executionType = ExecutionType.GET;
connection = myTransaction.getConnection(groupName, ds);
if (logger.isDebugEnabled()) {
logger.warn("sqlAndParam:\n" + sqlAndParam);
}
if (executionContext.getGroupHint() != null) {
// 如果有group hint,传递一下hint
ps = connection.prepareStatement(executionContext.getGroupHint() + sqlAndParam.sql);
} else {
ps = connection.prepareStatement(sqlAndParam.sql);
}
if (isStreaming) {
// 当prev的时候 不能设置
setStreamingForStatement(ps);
}
Map<Integer, ParameterContext> map = sqlAndParam.param;
ParameterMethod.setParameters(ps, map);
ResultSet rs = new ResultSetRemeberIfClosed(ps.executeQuery());
if (isControlSql) {
rs = new ResultSetAutoCloseConnection(rs, connection, ps);
}
this.resultSet = rs;
} catch (Throwable e) {
if (myTransaction.isAutoCommit()) {
// 关闭自提交的链接
close();
}
if (e.getMessage()
.contains("only select, insert, update, delete,replace,truncate,create,drop,load,merge sql is supported")) {
// 返回一个空结果
ps = connection.prepareStatement("select 1");
this.resultSet = new ResultSetRemeberIfClosed(new ResultSetAutoCloseConnection(ps.executeQuery(),
connection,
ps));
} else {
throw new TddlRuntimeException("sql generated is :\n" + sqlAndParam.toString(), e);
}
}
}
@Override
public void executeUpdate(ExecutionContext executionContext, IPut put, ITable table, IndexMeta meta)
throws SQLException {
SqlAndParam sqlAndParam = new SqlAndParam();
if (put.getSql() != null) {
sqlAndParam.sql = put.getSql();
if (executionContext.getParams() != null) {
sqlAndParam.param = executionContext.getParams();
} else {
sqlAndParam.param = new HashMap<Integer, ParameterContext>();
}
} else {
MysqlPlanVisitorImpl visitor = new MysqlPlanVisitorImpl(put, null, null, true);
put.accept(visitor);
sqlAndParam.sql = visitor.getString();
sqlAndParam.param = visitor.getParamMap();
}
try {
// 可能执行过程有失败,需要释放链接
connection = myTransaction.getConnection(groupName, ds);
if (executionContext.getGroupHint() != null) {
// 如果有group hint,传递一下hint
ps = prepareStatement(executionContext.getGroupHint() + sqlAndParam.sql, connection);
} else {
ps = prepareStatement(sqlAndParam.sql, connection);
}
ParameterMethod.setParameters(ps, sqlAndParam.param);
if (logger.isDebugEnabled()) {
logger.warn("sqlAndParam:\n" + sqlAndParam);
}
int tmpNum = ps.executeUpdate();
UpdateResultWrapper urw = new UpdateResultWrapper(tmpNum, this);
executionType = ExecutionType.PUT;
this.resultSet = urw;
} catch (Throwable e) {
if (myTransaction.isAutoCommit()) {
close();
}
throw new SQLException(e);
}
}
@Override
public ISchematicCursor getResultCursor() {
try {
if (executionType == ExecutionType.PUT) {
int i = resultSet.getInt(UpdateResultWrapper.AFFECT_ROW);
ISchematicCursor isc = new AffectRowCursor(i);
close();
return isc;
} else if (executionType == ExecutionType.GET) {
// get的时候只会有一个结果集
ResultSet rs = resultSet;
return new ResultSetCursor(rs);
} else {
return null;
}
} catch (SQLException e) {
throw new TddlRuntimeException(e);
}
}
protected PreparedStatement getPs() {
return ps;
}
@Override
public void setDs(Object ds) {
this.ds = (DataSource) ds;
}
protected void setStreamingForStatement(Statement stat) throws SQLException {
stat.setFetchSize(Integer.MIN_VALUE);
if (logger.isDebugEnabled()) {
logger.warn("fetchSize:\n" + stat.getFetchSize());
}
}
protected void setContext(ICursorMeta meta, boolean isStreaming) {
if (cursorMeta == null) {
cursorMeta = meta;
}
if (isStreaming != this.isStreaming) {
this.isStreaming = isStreaming;
}
}
@Override
public boolean isAutoCommit() throws SQLException {
return myTransaction.isAutoCommit();
}
public DataSource getDs() {
return ds;
}
@Override
public void close() throws SQLException {
try {
/*
* 非流计算的时候,用普通的close方法,而如果是streaming的情况下 将按以下模式关闭:
* http://jira.taobao.ali.com/browse/ANDOR-149
* http://gitlab.alibaba-inc.com/andor/issues/1835
*/
if (!isStreaming) {
if (resultSet != null && !resultSet.isClosed()) {
resultSet.close();
resultSet = null;
}
} else {
try {
if (resultSet != null && !resultSet.isClosed()) {
My_Transaction.closeStreaming(myTransaction, groupName, ds);
}
} catch (Throwable e) {
if (resultSet != null && !resultSet.isClosed()) {
resultSet.close();
resultSet = null;
}
throw new SQLException(e);
}
}
} finally {
try {
if (ps != null) {
ps.setFetchSize(0);
ps.close();
ps = null;
}
} finally {
// 针对自动提交的事务,链接不会进行重用,各自关闭
if (connection != null && myTransaction.isAutoCommit()) {
if (!isStreaming) {
connection.close();
} else {
My_Transaction.closeStreaming(connection);
}
}
}
}
executionType = null;
}
@Override
public boolean skipTo(CloneableRecord key, ICursorMeta indexMeta) throws SQLException {
checkInitedInRsNext();
throw new RuntimeException("暂时不支持skip to");
}
@Override
public IRowSet next() throws SQLException {
if (ds == null) {
throw new TddlRuntimeException("数据源为空");
}
checkInitedInRsNext();
prev_kv = current;
try {
if (resultSet.isClosed()) {
return null;
}
} catch (Exception ex) {
return null;
}
if (resultSet.next()) {
current = My_Convertor.convert(resultSet, cursorMeta);
} else {
current = null;
}
return current;
}
protected PreparedStatement prepareStatement(String sql, Connection myJdbcHandler) throws SQLException {
if (this.ps != null) {
throw new IllegalStateException("上一个请求还未执行完毕");
}
if (myJdbcHandler == null) {
throw new IllegalStateException("should not be here");
}
PreparedStatement ps = myJdbcHandler.prepareStatement(sql);
this.ps = ps;
return ps;
}
@Override
public IRowSet first() throws SQLException {
resultSet.beforeFirst();
resultSet.next();
current = My_Convertor.convert(resultSet, cursorMeta);
return current;
}
@Override
public IRowSet last() throws SQLException {
resultSet.afterLast();
resultSet.previous();
current = My_Convertor.convert(resultSet, cursorMeta);
return current;
}
@Override
public IRowSet getCurrent() {
return current;
}
protected void checkInitedInRsNext() {
if (!isInited()) {
throw new IllegalArgumentException("not inited");
}
}
@Override
public boolean isInited() {
return executionType != null;
}
@Override
public void beforeFirst() throws SQLException {
this.close();
this.executeQuery(cursorMeta, isStreaming);
current = null;
}
@Override
public IRowSet prev() throws SQLException {
if (ds == null) {
throw new TddlRuntimeException("数据源为空");
}
if (!initPrev) {
initPrev = true;
return convertRowSet(resultSet.last());
}
checkInitedInRsNext();
return convertRowSet(resultSet.previous());
}
protected IRowSet convertRowSet(boolean isOk) throws SQLException {
prev_kv = current;
if (isOk) {
current = My_Convertor.convert(resultSet, cursorMeta);
} else {
current = null;
}
return current;
}
@Override
public boolean isDone() {
return true;
}
@Override
public void cancel(boolean interruptedIfRunning) {
}
public void setMyTransaction(My_Transaction myTransaction) {
this.myTransaction = myTransaction;
}
@Override
public boolean isCanceled() {
return false;
}
public My_Transaction getMyTransaction() {
return myTransaction;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setDs(DataSource ds) {
this.ds = ds;
}
public Boolean getIsStreaming() {
return isStreaming;
}
public void setIsStreaming(Boolean isStreaming) {
this.isStreaming = isStreaming;
}
public ResultSet getResultSet() {
return this.resultSet;
}
public void setPlan(IDataNodeExecutor plan) {
this.plan = plan;
}
public IDataNodeExecutor getPlan() {
return this.plan;
}
public ExecutionContext getExecutionContext() {
return executionContext;
}
public void setExecutionContext(ExecutionContext executionContext) {
this.executionContext = executionContext;
}
}