/*
* Copyright 1999-2012 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package fm.liu.timo.server;
import java.nio.channels.SocketChannel;
import java.sql.SQLSyntaxErrorException;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicLong;
import fm.liu.timo.TimoServer;
import fm.liu.timo.config.ErrorCode;
import fm.liu.timo.config.model.Database;
import fm.liu.timo.mysql.PreparedStatement;
import fm.liu.timo.mysql.packet.OkPacket;
import fm.liu.timo.net.NIOProcessor;
import fm.liu.timo.net.connection.FrontendConnection;
import fm.liu.timo.parser.ast.expression.Expression;
import fm.liu.timo.parser.ast.stmt.extension.PrepareStatement;
import fm.liu.timo.route.Outlets;
import fm.liu.timo.route.Router;
import fm.liu.timo.server.parser.ServerParse;
import fm.liu.timo.server.response.Heartbeat;
import fm.liu.timo.server.response.Ping;
import fm.liu.timo.server.session.AutoCommitSession;
import fm.liu.timo.server.session.AutoTransactionSession;
import fm.liu.timo.server.session.Session;
import fm.liu.timo.server.session.TransactionSession;
import fm.liu.timo.server.session.XATransactionSession;
import fm.liu.timo.server.session.handler.ResultHandler;
import fm.liu.timo.server.session.handler.VirtualHandler;
import fm.liu.timo.util.TimeUtil;
/**
* @author xianmao.hexm 2011-4-21 上午11:22:57
*/
public class ServerConnection extends FrontendConnection {
private static final long AUTH_TIMEOUT = 15 * 1000L;
private volatile Session session;
private volatile Session nextSession;
private final Session autocommitSession;
private final Session transactionSession;
private final Session autoTransactionSession;
private long lastInsertID;
private HashMap<String, Expression> userVariables;
private static final AtomicLong PREPARED_ID = new AtomicLong();
private HashMap<String, PreparedStatement> preparedStatements;
public ServerConnection(SocketChannel channel, NIOProcessor processor) {
super(channel, processor);
autocommitSession = new AutoCommitSession(this);
if (TimoServer.getInstance().getConfig().getSystem().isEnableXA()) {
transactionSession = new XATransactionSession(this);
} else {
transactionSession = new TransactionSession(this);
}
autoTransactionSession = new AutoTransactionSession(this);
session = autocommitSession;
}
@Override
public boolean isIdleTimeout() {
if (isAuthenticated) {
return super.isIdleTimeout();
} else {
return TimeUtil.currentTimeMillis() > variables.getLastActiveTime() + AUTH_TIMEOUT;
}
}
@Override
public void ping() {
Ping.response(this);
}
@Override
public void heartbeat(byte[] data) {
Heartbeat.response(this, data);
}
public void execute(String sql, int type) {
if (!TimoServer.getInstance().isOnline()) {
writeErrMessage(ErrorCode.ER_ACCESS_DENIED_ERROR, "Timo-server is offline");
return;
}
Database database = checkDB();
if (database == null) {
return;
}
Outlets out = null;
try {
out = Router.route(database, sql, this.getCharset(), type);
} catch (SQLSyntaxErrorException e) {
String msg = e.getMessage();
writeErrMessage(ErrorCode.ER_PARSE_ERROR,
msg == null ? e.getClass().getSimpleName() : msg);
return;
}
chooseSession(out, type);
session.execute(out, type);
}
private Database checkDB() {
String db = this.db;
if (db == null) {
writeErrMessage(ErrorCode.ER_NO_DB_ERROR, "No database selected");
return null;
}
Database database =
TimoServer.getInstance().getConfig().getDatabases().get(db.toUpperCase());
if (database == null) {
writeErrMessage(ErrorCode.ER_BAD_DB_ERROR, "Unknown database '" + db + "'");
}
return database;
}
private void chooseSession(Outlets out, int type) {
if (session instanceof TransactionSession) {
return;
}
if (variables.isAutocommit() && out.size() > 1) {
switch (type) {
case ServerParse.INSERT:
case ServerParse.DELETE:
case ServerParse.UPDATE:
case ServerParse.REPLACE:
session = autoTransactionSession;
}
}
}
/**
* 撤销执行中的语句
*
* @param sponsor 发起者为null表示是自己
*/
public void cancel(final FrontendConnection sponsor) {
processor.getExecutor().execute(() -> {
ResultHandler handler = new VirtualHandler();
session.availableConnections().parallelStream().filter(con -> con.isRunning()).forEach(
con -> con.getDatasource().query("kill query " + con.getThreadID(), handler));
;
});
}
@Override
public void close(String reason) {
if (super.closed.compareAndSet(false, true)) {
this.processor.remove(this);
if (session != null) {
Session tmp = session;
session = null;
tmp.clear();
}
super.cleanup();
}
}
public void setLastInsertId(long lastInsertID) {
this.lastInsertID = lastInsertID;
}
public long getLastInsertId() {
return lastInsertID;
}
public Session getSession() {
return session;
}
public boolean setCharset(String charset) {
boolean result = variables.setCharset(charset);
if (result) {
int index = variables.getCharsetIndex();
autocommitSession.getVariables().setCharsetIndex(index);
transactionSession.getVariables().setCharsetIndex(index);
autoTransactionSession.getVariables().setCharsetIndex(index);
}
return result;
}
public void setIsolationLevel(int level) {
variables.setIsolationLevel(level);
autocommitSession.getVariables().setIsolationLevel(level);
if (!(transactionSession instanceof XATransactionSession)) {
transactionSession.getVariables().setIsolationLevel(level);
}
autoTransactionSession.getVariables().setIsolationLevel(level);
}
public void setAutocommit(boolean autocommit) {
Session tmp = session;
variables.setAutocommit(autocommit);
if (autocommit) {
tmp.commit();
} else {
if (!(tmp instanceof TransactionSession)) {
session = transactionSession;
}
write(OkPacket.OK);
}
}
public void startTransaction() {
if (session instanceof TransactionSession && !session.getConnections().isEmpty()) {
nextSession = transactionSession;
if (session instanceof XATransactionSession) {
((XATransactionSession) session).start(checkDB());
session.commit();
} else {
session.commit();
}
} else {
session = transactionSession;
if (session instanceof XATransactionSession) {
((XATransactionSession) session).start(checkDB());
} else {
write(OkPacket.OK);
}
}
}
public void commit() {
session.commit();
}
public void rollback() {
session.rollback(true);
}
public void reset() {
if (nextSession != null) {
session = nextSession;
nextSession = null;
return;
}
if (this.getVariables().isAutocommit()) {
session = autocommitSession;
} else {
session = transactionSession;
}
}
public void setUserVariable(String name, Expression value) {
this.userVariables.put(name, value);
}
public Expression getUserVariable(String name) {
return this.userVariables.get(name);
}
public void prepare(PrepareStatement stmt) {
this.preparedStatements.put(stmt.getName(),
new PreparedStatement(PREPARED_ID.incrementAndGet(), stmt.getStatement()));
}
public PreparedStatement getPreparedStatement(String name) {
return this.preparedStatements.get(name);
}
public void dropPreparedStatement(String name) {
this.preparedStatements.remove(name);
}
}