├── .idea ├── .name ├── encodings.xml ├── copyright │ ├── profiles_settings.xml │ └── .xml ├── compiler.xml └── misc.xml ├── .gitignore └── src ├── main └── java │ └── alchemystar │ └── freedom │ ├── index │ ├── CompareType.java │ ├── Index.java │ └── bp │ │ └── Position.java │ ├── transaction │ ├── OpType.java │ ├── log │ │ ├── LogType.java │ │ ├── LSNFactory.java │ │ └── Log.java │ ├── TrxState.java │ ├── TrxManager.java │ ├── redo │ │ └── RedoManager.java │ ├── undo │ │ └── UndoManager.java │ └── Trx.java │ ├── test │ ├── bptest │ │ ├── FStoreTest.java │ │ ├── BasicGenTable.java │ │ └── PageTest.java │ ├── sqltest │ │ ├── CreateTest.java │ │ ├── DeleteTest.java │ │ ├── InsertTest.java │ │ └── SelectTest.java │ ├── BasicSelectTest.java │ ├── LogTest.java │ └── TableMetaTest.java │ ├── constant │ └── ItemConst.java │ ├── access │ ├── Cursor.java │ ├── ClusterIndexCursor.java │ ├── SecondIndexCursor.java │ └── BaseIndexCursor.java │ ├── engine │ ├── net │ │ ├── handler │ │ │ ├── frontend │ │ │ │ ├── FrontendQueryHandler.java │ │ │ │ ├── BeginHandler.java │ │ │ │ ├── UseHandler.java │ │ │ │ ├── SavepointHandler.java │ │ │ │ ├── StartHandler.java │ │ │ │ ├── ShowHandler.java │ │ │ │ ├── FrontendTailHandler.java │ │ │ │ ├── FrontendGroupHandler.java │ │ │ │ ├── SelectHandler.java │ │ │ │ ├── KillHandler.java │ │ │ │ ├── ServerQueryHandler.java │ │ │ │ ├── FrontendCommandHandler.java │ │ │ │ └── SetHandler.java │ │ │ └── factory │ │ │ │ ├── FrontConnectionFactory.java │ │ │ │ └── FrontHandlerFactory.java │ │ ├── proto │ │ │ ├── util │ │ │ │ ├── Versions.java │ │ │ │ ├── Isolations.java │ │ │ │ ├── ArrayUtil.java │ │ │ │ ├── Fields.java │ │ │ │ ├── RandomUtil.java │ │ │ │ ├── LongUtil.java │ │ │ │ ├── Capabilities.java │ │ │ │ ├── PacketUtil.java │ │ │ │ ├── SecurityUtil.java │ │ │ │ ├── ByteUtil.java │ │ │ │ └── IntegerUtil.java │ │ │ └── mysql │ │ │ │ ├── BinaryPacket.java │ │ │ │ ├── ResultSetHeaderPacket.java │ │ │ │ ├── CommandPacket.java │ │ │ │ ├── EOFPacket.java │ │ │ │ ├── OkPacket.java │ │ │ │ ├── ErrorPacket.java │ │ │ │ ├── HandshakePacket.java │ │ │ │ ├── RowDataPacket.java │ │ │ │ ├── FieldPacket.java │ │ │ │ └── AuthPacket.java │ │ ├── exception │ │ │ ├── RetryConnectFailException.java │ │ │ ├── ErrorPacketException.java │ │ │ ├── HeartbeatException.java │ │ │ ├── UnknownDataNodeException.java │ │ │ ├── UnknownPacketException.java │ │ │ ├── UnknownCharsetException.java │ │ │ └── UnknownTxIsolationException.java │ │ ├── response │ │ │ ├── ErrResponse.java │ │ │ ├── OkResponse.java │ │ │ ├── jdbc │ │ │ │ └── SelectIncrementResponse.java │ │ │ ├── SelectTxResponse.java │ │ │ ├── SelectVersion.java │ │ │ ├── SelectDatabase.java │ │ │ ├── SelectVersionComment.java │ │ │ ├── ShowTables.java │ │ │ └── ShowDatabases.java │ │ └── codec │ │ │ └── MySqlPacketDecoder.java │ ├── session │ │ ├── SessionFactory.java │ │ └── Session.java │ ├── parser │ │ ├── util │ │ │ └── CharTypes.java │ │ └── ServerParseStart.java │ ├── Database.java │ └── server │ │ └── FreedomServer.java │ ├── sql │ ├── select │ │ └── ColumnResolver.java │ ├── CreateExecutor.java │ ├── InsertExecutor.java │ ├── parser │ │ ├── DeleteVisitor.java │ │ └── InsertVisitor.java │ ├── SqlExecutor.java │ └── DeleteExecutor.java │ ├── meta │ ├── NotLeafEntry.java │ ├── factory │ │ └── RelFactory.java │ ├── ClusterIndexEntry.java │ ├── IndexDesc.java │ ├── value │ │ ├── ValueBoolean.java │ │ ├── ValueInt.java │ │ ├── ValueLong.java │ │ ├── ValueString.java │ │ └── Value.java │ ├── Attribute.java │ └── TableManager.java │ ├── config │ ├── SocketConfig.java │ └── SystemConfig.java │ ├── store │ ├── page │ │ ├── PageFactory.java │ │ ├── PageNoAllocator.java │ │ ├── PagePool.java │ │ ├── PageLoader.java │ │ └── PageHeaderData.java │ ├── item │ │ ├── ItemPointer.java │ │ ├── Item.java │ │ └── ItemData.java │ ├── fs │ │ ├── FileUtils.java │ │ └── FStore.java │ └── log │ │ └── LogStore.java │ ├── optimizer │ └── Optimizer.java │ ├── util │ └── ValueConvertUtil.java │ └── recovery │ └── RecoverManager.java └── test └── java └── alchemystar └── AppTest.java /.idea/.name: -------------------------------------------------------------------------------- 1 | freedom -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | svn/ 2 | target/ 3 | .idea/ 4 | *.iml 5 | *.class 6 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/index/CompareType.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.index; 2 | 3 | /** 4 | * @Author lizhuyang 5 | */ 6 | public interface CompareType { 7 | 8 | int EQUAL = 0; 9 | 10 | int LOW = 1; 11 | 12 | int UP = 2; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/OpType.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction; 2 | 3 | /** 4 | * @Author lizhuyang 5 | */ 6 | public interface OpType { 7 | 8 | int insert = 0; 9 | 10 | int update = 1; 11 | 12 | int delete = 2; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/bptest/FStoreTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test.bptest; 2 | 3 | /** 4 | * FStoreTest 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public class FStoreTest { 9 | 10 | public static void main(String args[]) { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/constant/ItemConst.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.constant; 2 | 3 | /** 4 | * ItemConst 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public interface ItemConst { 9 | 10 | int INT_LEANGTH = 8 + 5; 11 | 12 | int LONG_LENGTH = 8 + 9; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/access/Cursor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.access; 2 | 3 | import alchemystar.freedom.meta.IndexEntry; 4 | 5 | /** 6 | * 扫描 7 | * 8 | * @Author lizhuyang 9 | */ 10 | public interface Cursor { 11 | 12 | IndexEntry next(); 13 | 14 | void reset(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/FrontendQueryHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | /** 4 | * Com_Query Handler 5 | * @Author lizhuyang 6 | */ 7 | public interface FrontendQueryHandler { 8 | 9 | void query(String sql); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/log/LogType.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction.log; 2 | 3 | /** 4 | * @Author lizhuyang 5 | */ 6 | public interface LogType { 7 | 8 | int TRX_START = 0; 9 | 10 | int ROLL_BACK = 1 ; 11 | 12 | int COMMIT = 2; 13 | 14 | int ROW = 3; 15 | } 16 | -------------------------------------------------------------------------------- /.idea/copyright/.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/Versions.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * @Author lizhuyang 5 | */ 6 | public interface Versions { 7 | /** 协议版本 */ 8 | byte PROTOCOL_VERSION = 10; 9 | 10 | /** 服务器版本 */ 11 | byte[] SERVER_VERSION = "5.1.1-freedom".getBytes(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/Isolations.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * 事务隔离级别定义 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public interface Isolations { 9 | 10 | int READ_UNCOMMITTED = 1; 11 | int READ_COMMITTED = 2; 12 | int REPEATED_READ = 3; 13 | int SERIALIZABLE = 4; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/BeginHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | /** 4 | * BeginHandler 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public final class BeginHandler { 9 | 10 | public static void handle(String stmt, FrontendConnection c) { 11 | c.commit(); 12 | c.writeOk(); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/UseHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | /** 4 | * UseHandler 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public final class UseHandler { 9 | 10 | public static void handle(String sql, FrontendConnection c, int offset) { 11 | // todo actual use 12 | c.writeOk(); 13 | 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/log/LSNFactory.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction.log; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | /** 6 | * @Author lizhuyang 7 | */ 8 | public class LSNFactory { 9 | 10 | private static AtomicLong lsnAllocator = new AtomicLong(0); 11 | 12 | public static long nextLSN() { 13 | return lsnAllocator.getAndIncrement(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/TrxState.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction; 2 | 3 | /** 4 | * TrxState 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public interface TrxState { 9 | // 事务未开始 10 | int TRX_STATE_NOT_STARTED = 0; 11 | // 事务进行中 12 | int TRX_STATE_ACTIVE = 1; 13 | // 暂时不用 for 2PC/XA 14 | int TRX_STATE_PREPARED = 2; 15 | // 事务已提交 16 | int TRX_COMMITTED = 3; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/session/SessionFactory.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.session; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | 5 | /** 6 | * @Author lizhuyang 7 | */ 8 | public class SessionFactory { 9 | 10 | public static Session newSession(FrontendConnection connection) { 11 | Session session = new Session(connection); 12 | return session; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/access/ClusterIndexCursor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.access; 2 | 3 | import alchemystar.freedom.index.bp.Position; 4 | 5 | /** 6 | * ClusterIndexCursor 7 | * 索引扫描 8 | * 9 | * @Author lizhuyang 10 | */ 11 | public class ClusterIndexCursor extends BaseIndexCursor { 12 | 13 | public ClusterIndexCursor(Position startPos, Position endPos, boolean isEqual) { 14 | super(startPos, endPos, isEqual); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/sql/select/ColumnResolver.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.sql.select; 2 | 3 | import alchemystar.freedom.meta.Attribute; 4 | import alchemystar.freedom.meta.value.Value; 5 | 6 | /** 7 | * @Author lizhuyang 8 | */ 9 | public interface ColumnResolver { 10 | 11 | Attribute[] getAttributes(); 12 | 13 | Value getValue(String columnName); 14 | 15 | TableFilter getTableFilter(); 16 | 17 | String getTableAlias(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/NotLeafEntry.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta; 2 | 3 | import alchemystar.freedom.meta.value.Value; 4 | 5 | /** 6 | * @Author lizhuyang 7 | */ 8 | public class NotLeafEntry extends IndexEntry { 9 | 10 | public NotLeafEntry(Value[] values) { 11 | super(values); 12 | } 13 | 14 | // cluster的非叶子节点,其本身就是compare key 15 | public IndexEntry getCompareEntry() { 16 | return this; 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/exception/RetryConnectFailException.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.exception; 2 | 3 | /** 4 | * @Author lizhuyang 5 | */ 6 | public class RetryConnectFailException extends RuntimeException { 7 | 8 | public RetryConnectFailException(String message, Throwable cause) { 9 | super(message, cause); 10 | } 11 | 12 | public RetryConnectFailException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/SavepointHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import alchemystar.freedom.engine.net.proto.util.ErrorCode; 4 | 5 | /** 6 | * SavePointHandler 7 | * 8 | * @Author lizhuyang 9 | */ 10 | public final class SavepointHandler { 11 | 12 | public static void handle(String stmt, FrontendConnection c) { 13 | c.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unsupported statement"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/config/SocketConfig.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.config; 2 | 3 | /** 4 | * SocketConfig 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public interface SocketConfig { 9 | 10 | int Frontend_Socket_Recv_Buf = 4 * 1024 * 1024; 11 | int Frontend_Socket_Send_Buf = 1024 * 1024; 12 | int Backend_Socket_Recv_Buf = 4 * 1024 * 1024; 13 | int Backend_Socket_Send_Buf = 1024 * 1024;// mysql 5.6 14 | int CONNECT_TIMEOUT_MILLIS = 5000; 15 | int SO_TIMEOUT = 10 * 60; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/TrxManager.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | /** 6 | * @Author lizhuyang 7 | */ 8 | public class TrxManager { 9 | 10 | private static AtomicInteger trxIdCount = new AtomicInteger(1); 11 | 12 | public static Trx newTrx() { 13 | Trx trx = new Trx(); 14 | trx.setTrxId(trxIdCount.getAndIncrement()); 15 | return trx; 16 | } 17 | 18 | public static Trx newEmptyTrx() { 19 | Trx trx = new Trx(); 20 | return trx; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/factory/RelFactory.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta.factory; 2 | 3 | import alchemystar.freedom.config.SystemConfig; 4 | import alchemystar.freedom.meta.Table; 5 | 6 | /** 7 | * RelFactory 8 | * 9 | * @Author lizhuyang 10 | */ 11 | public class RelFactory { 12 | 13 | private static RelFactory relFactory; 14 | 15 | static { 16 | relFactory = new RelFactory(); 17 | } 18 | 19 | public static RelFactory getInstance() { 20 | return relFactory; 21 | } 22 | 23 | public Table newRelation(String tableName) { 24 | Table table = new Table(); 25 | return table; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/sqltest/CreateTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test.sqltest; 2 | 3 | import org.junit.Test; 4 | 5 | import alchemystar.freedom.sql.SqlExecutor; 6 | 7 | /** 8 | * @Author lizhuyang 9 | */ 10 | public class CreateTest { 11 | 12 | public static String CREATE_SQL = "create table test (id bigint,name varchar(256),comment varchar(256), PRIMARY " 13 | + "KEY " 14 | + "('id')," 15 | + "KEY name ('name'));"; 16 | 17 | @Test 18 | public void createTable() { 19 | SqlExecutor sqlExecutor = new SqlExecutor(); 20 | sqlExecutor.execute(CREATE_SQL, null, null); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/exception/ErrorPacketException.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.exception; 2 | 3 | /** 4 | * ErrorPacketException 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public class ErrorPacketException extends RuntimeException { 9 | 10 | public ErrorPacketException() { 11 | super(); 12 | } 13 | 14 | public ErrorPacketException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public ErrorPacketException(String message) { 19 | super(message); 20 | } 21 | 22 | public ErrorPacketException(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/exception/HeartbeatException.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.exception; 2 | 3 | /** 4 | * HeartbeatException 5 | * 6 | * @author lizhuyang 7 | */ 8 | public class HeartbeatException extends RuntimeException { 9 | private static final long serialVersionUID = 7639414445868741580L; 10 | 11 | public HeartbeatException() { 12 | super(); 13 | } 14 | 15 | public HeartbeatException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public HeartbeatException(String message) { 20 | super(message); 21 | } 22 | 23 | public HeartbeatException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/exception/UnknownDataNodeException.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.exception; 2 | 3 | /** 4 | * @author lizhuyang 5 | */ 6 | public class UnknownDataNodeException extends RuntimeException { 7 | private static final long serialVersionUID = -3752985849571697432L; 8 | 9 | public UnknownDataNodeException() { 10 | super(); 11 | } 12 | 13 | public UnknownDataNodeException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public UnknownDataNodeException(String message) { 18 | super(message); 19 | } 20 | 21 | public UnknownDataNodeException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/exception/UnknownPacketException.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.exception; 2 | 3 | /** 4 | * 未知数据包异常 5 | * 6 | * @author lizhuyang 7 | */ 8 | public class UnknownPacketException extends RuntimeException { 9 | private static final long serialVersionUID = 3152986441780514147L; 10 | 11 | public UnknownPacketException() { 12 | super(); 13 | } 14 | 15 | public UnknownPacketException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public UnknownPacketException(String message) { 20 | super(message); 21 | } 22 | 23 | public UnknownPacketException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/exception/UnknownCharsetException.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.exception; 2 | 3 | /** 4 | * 未知字符集异常 5 | * 6 | * @author lizhuyang 7 | */ 8 | public class UnknownCharsetException extends RuntimeException { 9 | private static final long serialVersionUID = 552833416065882969L; 10 | 11 | public UnknownCharsetException() { 12 | super(); 13 | } 14 | 15 | public UnknownCharsetException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public UnknownCharsetException(String message) { 20 | super(message); 21 | } 22 | 23 | public UnknownCharsetException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/ErrResponse.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | 5 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 6 | import alchemystar.freedom.engine.net.proto.mysql.ErrorPacket; 7 | 8 | /** 9 | * ErrResponse 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class ErrResponse { 14 | 15 | public static void response(FrontendConnection connection, String errMsg) { 16 | if (StringUtils.isNotEmpty(errMsg)) { 17 | ErrorPacket errorPacket = new ErrorPacket(); 18 | errorPacket.message = errMsg.getBytes(); 19 | errorPacket.write(connection.getCtx()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/exception/UnknownTxIsolationException.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.exception; 2 | 3 | /** 4 | * 未知事物隔离级别异常 5 | * 6 | * @author lizhuyang 7 | */ 8 | public class UnknownTxIsolationException extends RuntimeException { 9 | private static final long serialVersionUID = -3911059999308980358L; 10 | 11 | public UnknownTxIsolationException() { 12 | super(); 13 | } 14 | 15 | public UnknownTxIsolationException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public UnknownTxIsolationException(String message) { 20 | super(message); 21 | } 22 | 23 | public UnknownTxIsolationException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/page/PageFactory.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.page; 2 | 3 | import alchemystar.freedom.config.SystemConfig; 4 | import alchemystar.freedom.index.bp.BPNode; 5 | import alchemystar.freedom.index.bp.BpPage; 6 | 7 | /** 8 | * BPFactory 9 | * 10 | * @Author lizhuyang 11 | */ 12 | public class PageFactory { 13 | 14 | private static PageFactory factory = new PageFactory(); 15 | 16 | public static PageFactory getInstance() { 17 | return factory; 18 | } 19 | 20 | private PageFactory() { 21 | } 22 | 23 | public Page newPage() { 24 | return new Page(SystemConfig.DEFAULT_PAGE_SIZE); 25 | } 26 | 27 | public BpPage newBpPage(BPNode bpNode) { 28 | return new BpPage(bpNode); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/alchemystar/AppTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/sql/CreateExecutor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLStatement; 4 | 5 | import alchemystar.freedom.meta.TableManager; 6 | import alchemystar.freedom.sql.parser.CreateVisitor; 7 | 8 | /** 9 | * @Author lizhuyang 10 | */ 11 | public class CreateExecutor { 12 | 13 | private SQLStatement sqlStatement; 14 | 15 | private CreateVisitor createVisitor; 16 | 17 | public CreateExecutor(SQLStatement sqlStatement) { 18 | this.sqlStatement = sqlStatement; 19 | } 20 | 21 | public void execute() { 22 | init(); 23 | TableManager.addTable(createVisitor.getTable(), true); 24 | } 25 | 26 | public void init() { 27 | CreateVisitor createVisitor = new CreateVisitor(); 28 | sqlStatement.accept(createVisitor); 29 | this.createVisitor = createVisitor; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/OkResponse.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | import alchemystar.freedom.engine.net.proto.mysql.OkPacket; 5 | import io.netty.channel.ChannelHandlerContext; 6 | 7 | /** 8 | * OkResponse 9 | * 10 | * @Author lizhuyang 11 | */ 12 | public class OkResponse { 13 | public static void response(FrontendConnection c) { 14 | OkPacket okPacket = new OkPacket(); 15 | ChannelHandlerContext ctx = c.getCtx(); 16 | okPacket.write(ctx); 17 | } 18 | 19 | public static void responseWithAffectedRows(FrontendConnection c, long affectedRows) { 20 | OkPacket okPacket = new OkPacket(); 21 | okPacket.affectedRows = affectedRows; 22 | ChannelHandlerContext ctx = c.getCtx(); 23 | okPacket.write(ctx); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/StartHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import alchemystar.freedom.engine.net.proto.util.ErrorCode; 4 | import alchemystar.freedom.engine.parser.ServerParse; 5 | import alchemystar.freedom.engine.parser.ServerParseStart; 6 | 7 | /** 8 | * StartHandler 9 | * 10 | * @Author lizhuyang 11 | */ 12 | public final class StartHandler { 13 | 14 | public static void handle(String stmt, FrontendConnection c, int offset) { 15 | switch (ServerParseStart.parse(stmt, offset)) { 16 | case ServerParseStart.TRANSACTION: 17 | c.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unsupported statement"); 18 | break; 19 | default: 20 | // todo data source 21 | c.execute(stmt, ServerParse.START); 22 | break; 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/index/Index.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.index; 2 | 3 | import java.util.List; 4 | 5 | import alchemystar.freedom.access.Cursor; 6 | import alchemystar.freedom.index.bp.Position; 7 | import alchemystar.freedom.meta.IndexEntry; 8 | 9 | /** 10 | * Index 11 | * 12 | * @Author lizhuyang 13 | */ 14 | public interface Index { 15 | 16 | Cursor searchEqual(IndexEntry key); 17 | 18 | Cursor searchRange(IndexEntry lowKey, IndexEntry upKey); 19 | 20 | Position getFirst(IndexEntry entry, int compareType); // 查询第一个符合的key 21 | 22 | Position getLast(IndexEntry entry, int compareType); // 查询最后一个符合的key 23 | 24 | List getAll(IndexEntry entry); // 查询所有符合条件的key 25 | 26 | int remove(IndexEntry entry); // 移除所有符合key的数据 27 | 28 | boolean removeOne(IndexEntry entry); // 删掉一个key 29 | 30 | void insert(IndexEntry entry, boolean isUnique); // 插入 31 | 32 | void delete(IndexEntry entry); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/ShowHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import alchemystar.freedom.engine.net.response.ErrResponse; 4 | import alchemystar.freedom.engine.net.response.ShowDatabases; 5 | import alchemystar.freedom.engine.net.response.ShowTables; 6 | import alchemystar.freedom.engine.parser.ServerParseShow; 7 | 8 | /** 9 | * ShowHandler 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public final class ShowHandler { 14 | 15 | public static void handle(String stmt, FrontendConnection c, int offset) { 16 | switch (ServerParseShow.parse(stmt, offset)) { 17 | case ServerParseShow.DATABASES: 18 | ShowDatabases.response(c); 19 | break; 20 | case ServerParseShow.SHOWTABLES: 21 | ShowTables.response(c); 22 | break; 23 | default: 24 | ErrResponse.response(c, "not support this set param"); 25 | break; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/ClusterIndexEntry.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta; 2 | 3 | import alchemystar.freedom.meta.value.Value; 4 | import alchemystar.freedom.meta.value.ValueLong; 5 | 6 | /** 7 | * 聚簇索引项 8 | * 9 | * @Author lizhuyang 10 | */ 11 | public class ClusterIndexEntry extends IndexEntry { 12 | 13 | public ClusterIndexEntry() { 14 | } 15 | 16 | public ClusterIndexEntry(Value[] values) { 17 | super(values); 18 | } 19 | 20 | // cluster 的 compareEntry key 就是主键 21 | public IndexEntry getCompareEntry() { 22 | if (compareEntry == null) { 23 | compareEntry = new NotLeafEntry(new Value[] {getRowId()}); 24 | compareEntry.setIndexDesc(new IndexDesc(new Attribute[] {indexDesc.getPrimaryAttr()})); 25 | } 26 | return compareEntry; 27 | } 28 | 29 | public IndexEntry getDeleteCompareEntry() { 30 | return getCompareEntry(); 31 | } 32 | 33 | public ValueLong getRowId() { 34 | return (ValueLong) values[indexDesc.getPrimaryAttr().getIndex()]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/page/PageNoAllocator.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.page; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | /** 8 | * PageNoAllocator 9 | * 10 | * @Author lizhuyang 11 | */ 12 | public class PageNoAllocator { 13 | 14 | private AtomicInteger count; 15 | 16 | // todo thread-safe 17 | private List freePageNoList; 18 | 19 | public PageNoAllocator() { 20 | // 0 for meta page 21 | count = new AtomicInteger(1); 22 | freePageNoList = new LinkedList(); 23 | } 24 | 25 | public int getNextPageNo() { 26 | if (freePageNoList.size() == 0) { 27 | return count.getAndAdd(1); 28 | } 29 | return freePageNoList.remove(0); 30 | } 31 | 32 | public void recycleCount(int pageNo) { 33 | freePageNoList.add(pageNo); 34 | } 35 | 36 | // 从磁盘中,重新构造page的时候,需要重新设置其pageNo 37 | public void setCount(int lastPageNo) { 38 | count.set(lastPageNo + 1); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/optimizer/Optimizer.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.optimizer; 2 | 3 | import alchemystar.freedom.index.Index; 4 | import alchemystar.freedom.meta.IndexDesc; 5 | import alchemystar.freedom.meta.IndexEntry; 6 | import alchemystar.freedom.meta.Table; 7 | 8 | /** 9 | * @Author lizhuyang 10 | */ 11 | public class Optimizer { 12 | 13 | private Table table; 14 | 15 | public Optimizer(Table table) { 16 | this.table = table; 17 | } 18 | 19 | public Index chooseIndex(IndexEntry entry) { 20 | if(entry != null && !entry.isAllNull()) { 21 | IndexDesc indexDesc = entry.getIndexDesc(); 22 | // 如果包含主键id,则直接用主键id进行查询 23 | if (indexDesc.getPrimaryAttr() != null && entry.getValues()[indexDesc.getPrimaryAttr().getIndex()] != 24 | null) { 25 | return table.getClusterIndex(); 26 | } 27 | // 二级索引选择器优化留待后续优化 28 | return table.getSecondIndexes().get(0); 29 | }else { 30 | return table.getClusterIndex(); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/config/SystemConfig.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.config; 2 | 3 | import alchemystar.freedom.engine.net.proto.util.Isolations; 4 | 5 | /** 6 | * SystemConfig 7 | * 8 | * @Author lizhuyang 9 | */ 10 | public interface SystemConfig { 11 | 12 | // todo 不同表,不同文件夹路径 13 | 14 | int DEFAULT_PAGE_SIZE = 4096; 15 | 16 | int DEFAULT_SPECIAL_POINT_LENGTH = 64; 17 | 18 | String RELATION_FILE_PRE_FIX = "/Users/alchemystar/var/freedom/"; 19 | 20 | String FREEDOM_REL_DATA_PATH = RELATION_FILE_PRE_FIX + "/data"; 21 | 22 | String FREEDOM_REL_META_PATH = RELATION_FILE_PRE_FIX + "/meta"; 23 | 24 | String FREEDOM_LOG_FILE_NAME = RELATION_FILE_PRE_FIX + "/log/log"; 25 | 26 | String Database = ""; 27 | // 36小时内连接不发起请求就干掉 秒为单位 28 | // long IDLE_TIME_OUT = 36 * 3600 * 1000; 29 | long IDLE_TIME_OUT = 36 * 3600; 30 | 31 | // 1小时做一次idle check 秒为单位 32 | //int IDLE_CHECK_INTERVAL = 3600 * 1000; 33 | int IDLE_CHECK_INTERVAL = 3600; 34 | 35 | String DEFAULT_CHARSET = "gbk"; 36 | 37 | int DEFAULT_TX_ISOLATION = Isolations.REPEATED_READ; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/FrontendTailHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import alchemystar.freedom.engine.net.proto.util.ErrorCode; 7 | import io.netty.channel.ChannelHandlerAdapter; 8 | import io.netty.channel.ChannelHandlerContext; 9 | 10 | /** 11 | * TailHandler 做exception的操作 12 | * 13 | * @Author lizhuyang 14 | */ 15 | public class FrontendTailHandler extends ChannelHandlerAdapter { 16 | 17 | private static final Logger logger = LoggerFactory.getLogger(FrontendTailHandler.class); 18 | 19 | protected FrontendConnection source; 20 | 21 | public FrontendTailHandler(FrontendConnection source) { 22 | this.source = source; 23 | } 24 | 25 | @Override 26 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 27 | logger.error("Exception caught", cause); 28 | FrontendGroupHandler.frontendGroup.remove(source.getId()); 29 | source.writeErrMessage(ErrorCode.ERR_EXCEPTION_CAUGHT, cause.getMessage()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/sqltest/DeleteTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test.sqltest; 2 | 3 | import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; 4 | import com.alibaba.druid.sql.parser.SQLStatementParser; 5 | import org.junit.Test; 6 | 7 | import alchemystar.freedom.sql.SqlExecutor; 8 | import alchemystar.freedom.test.BasicSelectTest; 9 | 10 | /** 11 | * @Author lizhuyang 12 | */ 13 | public class DeleteTest extends BasicSelectTest { 14 | 15 | public static final String deleteSql = "delete from test where id>=1"; 16 | 17 | @Test 18 | public void test() { 19 | SqlExecutor executor = new SqlExecutor(); 20 | executor.execute(deleteSql, null, null); 21 | } 22 | 23 | @Test 24 | public void test2(){ 25 | System.out.println("+==============================+"); 26 | String s = "insert into INTO `android_message_all` (`msg_payload`,`appkey`,`live_time`,`createtime`) VALUES (\"\\\\'\",\"\\'\",\"\\'\",\"\\'\");"; 27 | System.out.println(s); 28 | SQLStatementParser insertStatement = new SQLStatementParser(s); 29 | System.out.println(insertStatement); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/index/bp/Position.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.index.bp; 2 | 3 | import alchemystar.freedom.meta.IndexEntry; 4 | 5 | /** 6 | * Get请求的返回值 7 | * 8 | * @Author lizhuyang 9 | */ 10 | public class Position { 11 | 12 | private BPNode bpNode; 13 | 14 | private int position; 15 | 16 | private IndexEntry searchEntry; 17 | 18 | public Position(BPNode bpNode, int position) { 19 | this.bpNode = bpNode; 20 | this.position = position; 21 | } 22 | 23 | public BPNode getBpNode() { 24 | return bpNode; 25 | } 26 | 27 | public Position setBpNode(BPNode bpNode) { 28 | this.bpNode = bpNode; 29 | return this; 30 | } 31 | 32 | public IndexEntry getSearchEntry() { 33 | return searchEntry; 34 | } 35 | 36 | public void setSearchEntry(IndexEntry searchEntry) { 37 | this.searchEntry = searchEntry; 38 | } 39 | 40 | public int getPosition() { 41 | return position; 42 | } 43 | 44 | public void setPosition(int position) { 45 | this.position = position; 46 | } 47 | 48 | public void incrPosition() { 49 | position++; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/item/ItemPointer.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.item; 2 | 3 | import alchemystar.freedom.store.page.Page; 4 | 5 | /** 6 | * ItemPointer 7 | * Tuple的pointer 8 | * 9 | * @Author lizhuyang 10 | */ 11 | public class ItemPointer { 12 | 13 | // Tuple的偏移 14 | private int offset; 15 | // Tuple的长度 16 | private int tupleLength; 17 | 18 | public ItemPointer(int offset, int length) { 19 | this.offset = offset; 20 | this.tupleLength = length; 21 | } 22 | 23 | void write(Page page) { 24 | page.writeInt(offset); 25 | page.writeInt(tupleLength); 26 | // 修改freespace的lowerOffset 27 | int lowerOffset = page.getLowerOffset(); 28 | lowerOffset += getPtrLength(); 29 | page.modifyLowerOffer(lowerOffset); 30 | } 31 | 32 | public static int getPtrLength() { 33 | return 8; 34 | } 35 | 36 | public int getTupleLength() { 37 | return tupleLength; 38 | } 39 | 40 | public int getOffset() { 41 | return offset; 42 | } 43 | 44 | public ItemPointer setOffset(int offset) { 45 | this.offset = offset; 46 | return this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/redo/RedoManager.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction.redo; 2 | 3 | import alchemystar.freedom.meta.ClusterIndexEntry; 4 | import alchemystar.freedom.meta.IndexDesc; 5 | import alchemystar.freedom.meta.IndexEntry; 6 | import alchemystar.freedom.meta.Table; 7 | import alchemystar.freedom.meta.TableManager; 8 | import alchemystar.freedom.transaction.OpType; 9 | import alchemystar.freedom.transaction.log.Log; 10 | 11 | /** 12 | * @Author lizhuyang 13 | */ 14 | public class RedoManager { 15 | 16 | public static void redo(Log log) { 17 | Table table = TableManager.getTable(log.getTableName()); 18 | switch (log.getOpType()) { 19 | case OpType.insert: 20 | IndexEntry indexEntry = new ClusterIndexEntry(log.getAfter().getValues()); 21 | indexEntry.setIndexDesc(new IndexDesc(table.getAttributes())); 22 | table.insert(indexEntry); 23 | break; 24 | case OpType.delete: 25 | table.delete(log.getBefore()); 26 | break; 27 | case OpType.update: 28 | // todo 29 | break; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/item/Item.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.item; 2 | 3 | import alchemystar.freedom.meta.IndexEntry; 4 | import alchemystar.freedom.store.page.Page; 5 | 6 | /** 7 | * Item 8 | * 9 | * @Author lizhuyang 10 | */ 11 | public class Item { 12 | 13 | private ItemPointer ptr; 14 | private ItemData data; 15 | 16 | public Item(IndexEntry indexEntry) { 17 | data = new ItemData(indexEntry); 18 | ptr = new ItemPointer(0, data.getLength()); 19 | } 20 | 21 | // 写入item,如果空间不够,返回false 22 | public boolean writeItem(Page page) { 23 | int freeSpace = page.remainFreeSpace(); 24 | if (freeSpace < getLength()) { 25 | return false; 26 | } 27 | // 顺序必须如此,只有写入data之后 28 | // 才能知道ptr中的offset 29 | data.write(page); 30 | // 修正ptr的offset 31 | ptr.setOffset(data.getOffset()); 32 | ptr.write(page); 33 | page.addTupleCount(page); 34 | return true; 35 | } 36 | 37 | public int getLength() { 38 | return data.getLength() + ptr.getPtrLength(); 39 | } 40 | 41 | public static int getItemLength(IndexEntry key) { 42 | return key.getLength() + 8; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/sqltest/InsertTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test.sqltest; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import alchemystar.freedom.meta.Table; 7 | import alchemystar.freedom.meta.TableManager; 8 | import alchemystar.freedom.sql.SqlExecutor; 9 | import alchemystar.freedom.test.bptest.BasicGenTable; 10 | 11 | /** 12 | * @Author lizhuyang 13 | */ 14 | public class InsertTest extends BasicGenTable { 15 | 16 | public static final String insertSqlTemplate = "insert into test (id,name,comment) values (?,?,?)"; 17 | 18 | @Before 19 | public void init() { 20 | Table table = genTable(); 21 | TableManager.addTable(table, false); 22 | } 23 | 24 | @Test 25 | public void test() { 26 | for (int i = 0; i < 1000; i++) { 27 | String insertSql = insertSqlTemplate.replace("?", String.valueOf(i)).replace("?", "alchemystar" + String 28 | .valueOf(i) 29 | + "comment" + String.valueOf(i)); 30 | SqlExecutor sqlExecutor = new SqlExecutor(); 31 | sqlExecutor.execute(insertSql, null, null); 32 | } 33 | 34 | System.out.println("insert okay"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/ArrayUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * ArrayUtil 5 | * @Author lizhuyang 6 | */ 7 | public class ArrayUtil { 8 | public static boolean equals(String str1, String str2) { 9 | if (str1 == null) { 10 | return str2 == null; 11 | } 12 | return str1.equals(str2); 13 | } 14 | 15 | public static boolean contains(String[] list, String str) { 16 | if (list == null) 17 | return false; 18 | for (String string : list) { 19 | if (equals(str, string)) { 20 | return true; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | /** 27 | * 28 | * 29 | * @param obj 30 | * @param seperator 31 | * @return 32 | */ 33 | public static String join(Object[] obj, String seperator) { 34 | StringBuilder sb = new StringBuilder(); 35 | for (int i = 0, len = obj.length; i < len; i++) { 36 | sb.append(obj[i] == null ? "" : obj[i].toString()); 37 | if (i < obj.length - 1) { 38 | sb.append(seperator); 39 | } 40 | } 41 | return sb.toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/sql/InsertExecutor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLStatement; 4 | 5 | import alchemystar.freedom.engine.session.Session; 6 | import alchemystar.freedom.meta.IndexEntry; 7 | import alchemystar.freedom.sql.parser.InsertVisitor; 8 | import alchemystar.freedom.transaction.OpType; 9 | 10 | /** 11 | * @Author lizhuyang 12 | */ 13 | public class InsertExecutor { 14 | 15 | private SQLStatement sqlStatement; 16 | 17 | private InsertVisitor insertVisitor; 18 | 19 | public InsertExecutor(SQLStatement sqlStatement) { 20 | this.sqlStatement = sqlStatement; 21 | } 22 | 23 | public void execute(Session session) { 24 | init(); 25 | // 必须支持带主键 26 | IndexEntry indexEntry = insertVisitor.buildInsertEntry(); 27 | if (session != null) { 28 | session.addLog(insertVisitor.getTable(), OpType.insert, null, indexEntry); 29 | } 30 | // 先落盘再对索引做操作 31 | insertVisitor.getTable().insert(indexEntry); 32 | } 33 | 34 | public void init() { 35 | InsertVisitor insertVisitor = new InsertVisitor(); 36 | sqlStatement.accept(insertVisitor); 37 | this.insertVisitor = insertVisitor; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/page/PagePool.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.page; 2 | 3 | import java.util.AbstractQueue; 4 | import java.util.concurrent.ConcurrentLinkedQueue; 5 | 6 | /** 7 | * PagePool 8 | * 页池 9 | * 10 | * @Author lizhuyang 11 | */ 12 | public class PagePool { 13 | 14 | private static PagePool pagePool; 15 | 16 | // 默认页数 17 | private static int defaultPageNum = 8; 18 | // 可用page 19 | private AbstractQueue frees = new ConcurrentLinkedQueue(); 20 | // page工厂 21 | private PageFactory factory = PageFactory.getInstance(); 22 | 23 | static { 24 | pagePool = new PagePool(); 25 | pagePool.init(); 26 | } 27 | 28 | public void init() { 29 | // 初始化8页的数据 30 | for (int i = 0; i < defaultPageNum; i++) { 31 | frees.add(factory.newPage()); 32 | } 33 | } 34 | 35 | public static PagePool getIntance() { 36 | return pagePool; 37 | } 38 | 39 | public Page getFreePage() { 40 | // Page page = frees.poll(); 41 | // if (page == null) { 42 | return factory.newPage(); 43 | // } 44 | // return page; 45 | } 46 | 47 | public void recycle(Page page) { 48 | page.clean(); 49 | frees.add(page); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/BasicSelectTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test; 2 | 3 | import org.junit.Before; 4 | 5 | import alchemystar.freedom.sql.SqlExecutor; 6 | import alchemystar.freedom.test.bptest.BasicGenTable; 7 | import alchemystar.freedom.test.sqltest.CreateTest; 8 | 9 | /** 10 | * @Author lizhuyang 11 | */ 12 | public class BasicSelectTest extends BasicGenTable { 13 | 14 | public static final String insertSqlTemplate = "insert into test (id,name) values (?,?)"; 15 | 16 | @Before 17 | public void init() { 18 | SqlExecutor executor = new SqlExecutor(); 19 | executor.execute(CreateTest.CREATE_SQL, null, null); 20 | insertSome(); 21 | } 22 | 23 | private void insertSome() { 24 | for (int i = 0; i < 50; i++) { 25 | String insertSql = 26 | insertSqlTemplate.replaceFirst("\\?", String.valueOf(i)).replaceFirst("\\?", "'alchemystar" + 27 | String 28 | .valueOf(i) + "'").replaceFirst("\\?", "'comment" + String.valueOf(i) + "'"); 29 | System.out.println(insertSql); 30 | SqlExecutor sqlExecutor = new SqlExecutor(); 31 | sqlExecutor.execute(insertSql, null, null); 32 | } 33 | 34 | System.out.println("insert okay"); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/util/ValueConvertUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import alchemystar.freedom.meta.Attribute; 7 | import alchemystar.freedom.meta.value.Value; 8 | import alchemystar.freedom.meta.value.ValueInt; 9 | import alchemystar.freedom.meta.value.ValueString; 10 | 11 | /** 12 | * ValueConvertUtil 13 | * 14 | * @Author lizhuyang 15 | */ 16 | public class ValueConvertUtil { 17 | 18 | public static Value[] convertAttr(Attribute attr) { 19 | List list = new ArrayList(); 20 | list.add(new ValueString(attr.getName())); 21 | list.add(new ValueInt(attr.getType())); 22 | list.add(new ValueInt(attr.getIndex())); 23 | list.add(new ValueString(attr.getComment())); 24 | list.add(new ValueInt(attr.isPrimaryKey() ? 1 : 0)); 25 | return list.toArray(new Value[list.size()]); 26 | } 27 | 28 | public static Attribute convertValue(Value[] values) { 29 | Attribute attr = new Attribute(); 30 | attr.setName(values[0].getString()); 31 | attr.setType(values[1].getInt()); 32 | attr.setIndex(values[2].getInt()); 33 | attr.setComment(values[3].getString()); 34 | attr.setPrimaryKey(values[4].getInt() > 0 ? true : false); 35 | return attr; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/BinaryPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * MySql包 外层结构 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class BinaryPacket extends MySQLPacket { 14 | public static final byte OK = 1; 15 | public static final byte ERROR = 2; 16 | public static final byte HEADER = 3; 17 | public static final byte FIELD = 4; 18 | public static final byte FIELD_EOF = 5; 19 | public static final byte ROW = 6; 20 | public static final byte PACKET_EOF = 7; 21 | 22 | public byte[] data; 23 | 24 | @Override 25 | public int calcPacketSize() { 26 | return data == null ? 0 : data.length; 27 | } 28 | 29 | @Override 30 | public void write(ChannelHandlerContext ctx) { 31 | ByteBuf byteBuf = ctx.alloc().buffer(); 32 | BufferUtil.writeUB3(byteBuf, packetLength); 33 | byteBuf.writeByte(packetId); 34 | byteBuf.writeBytes(data); 35 | ctx.writeAndFlush(byteBuf); 36 | } 37 | 38 | @Override 39 | protected String getPacketInfo() { 40 | return "MySQL Binary Packet"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/FrontendGroupHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import io.netty.channel.ChannelHandlerAdapter; 9 | import io.netty.channel.ChannelHandlerContext; 10 | 11 | /** 12 | * 前端连接收集器 13 | * 14 | * @Author lizhuyang 15 | */ 16 | public class FrontendGroupHandler extends ChannelHandlerAdapter { 17 | 18 | private static final Logger logger = LoggerFactory.getLogger(FrontendGroupHandler.class); 19 | 20 | public static ConcurrentHashMap frontendGroup = new ConcurrentHashMap(); 22 | 23 | protected FrontendConnection source; 24 | 25 | public FrontendGroupHandler(FrontendConnection source) { 26 | this.source = source; 27 | } 28 | 29 | @Override 30 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 31 | frontendGroup.put(source.getId(), source); 32 | ctx.fireChannelActive(); 33 | } 34 | 35 | @Override 36 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 37 | frontendGroup.remove(source.getId()); 38 | source.close(); 39 | ctx.fireChannelActive(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/IndexDesc.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 元组的属性描述 8 | * 9 | * @Author lizhuyang 10 | */ 11 | public class IndexDesc { 12 | // 元组的属性数组 13 | private Attribute[] attrs; 14 | // 主键属性 15 | private Attribute primaryAttr; 16 | 17 | private Map attrsMap = new HashMap(); 18 | 19 | public IndexDesc(Attribute[] attrs) { 20 | this.attrs = attrs; 21 | attrsMap = new HashMap(); 22 | for (Attribute attr : attrs) { 23 | attrsMap.put(attr.getName(), attr); 24 | if (attr.isPrimaryKey()) { 25 | primaryAttr = attr; 26 | } 27 | } 28 | } 29 | 30 | public Attribute getPrimaryAttr() { 31 | return primaryAttr; 32 | } 33 | 34 | public void setPrimaryAttr(Attribute primaryAttr) { 35 | this.primaryAttr = primaryAttr; 36 | } 37 | 38 | public Attribute[] getAttrs() { 39 | return attrs; 40 | } 41 | 42 | public void setAttrs(Attribute[] attrs) { 43 | this.attrs = attrs; 44 | } 45 | 46 | public Map getAttrsMap() { 47 | return attrsMap; 48 | } 49 | 50 | public void setAttrsMap(Map attrsMap) { 51 | this.attrsMap = attrsMap; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/undo/UndoManager.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction.undo; 2 | 3 | import alchemystar.freedom.meta.Table; 4 | import alchemystar.freedom.meta.TableManager; 5 | import alchemystar.freedom.transaction.OpType; 6 | import alchemystar.freedom.transaction.log.Log; 7 | import alchemystar.freedom.transaction.log.LogType; 8 | 9 | /** 10 | * @Author lizhuyang 11 | */ 12 | public class UndoManager { 13 | 14 | public static void undo(Log log) { 15 | Table table = TableManager.getTable(log.getTableName()); 16 | if (log.getLogType() == LogType.ROW) { 17 | switch (log.getOpType()) { 18 | case OpType.insert: 19 | undoInsert(table, log); 20 | break; 21 | case OpType.update: 22 | undoUpdate(table, log); 23 | break; 24 | case OpType.delete: 25 | undoDelete(table, log); 26 | break; 27 | } 28 | } else { 29 | // do nothing; 30 | } 31 | } 32 | 33 | public static void undoInsert(Table table, Log log) { 34 | // insert undo = > delete 35 | table.delete(log.getAfter()); 36 | } 37 | 38 | public static void undoUpdate(Table table, Log log) { 39 | // todo 40 | } 41 | 42 | public static void undoDelete(Table table, Log log) { 43 | table.insert(log.getBefore()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/session/Session.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.session; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | import alchemystar.freedom.meta.IndexEntry; 5 | import alchemystar.freedom.meta.Table; 6 | import alchemystar.freedom.sql.SqlExecutor; 7 | import alchemystar.freedom.transaction.Trx; 8 | import alchemystar.freedom.transaction.TrxManager; 9 | 10 | /** 11 | * @Author lizhuyang 12 | */ 13 | public class Session { 14 | // session 对应的连接 15 | private FrontendConnection conn; 16 | // 是否自动提交 17 | private boolean isAutoCommit; 18 | // 当前session下的事务 19 | private Trx trx; 20 | 21 | private SqlExecutor sqlExecutor = new SqlExecutor(); 22 | 23 | public Session(FrontendConnection conn) { 24 | this.conn = conn; 25 | trx = TrxManager.newTrx(); 26 | } 27 | 28 | public void begin() { 29 | trx.begin(); 30 | } 31 | 32 | public void commit() { 33 | trx.commit(); 34 | } 35 | 36 | public void rollback() { 37 | trx.rollback(); 38 | } 39 | 40 | public void addLog(Table table, int opType, IndexEntry before, IndexEntry after) { 41 | trx.addLog(table, opType, before, after); 42 | } 43 | 44 | public void execute(String sql, FrontendConnection connection) { 45 | if (trx.trxIsNotStart()) { 46 | trx.begin(); 47 | } 48 | sqlExecutor.execute(sql, connection, this); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/item/ItemData.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.item; 2 | 3 | import alchemystar.freedom.meta.IndexEntry; 4 | import alchemystar.freedom.store.page.Page; 5 | 6 | /** 7 | * ItemData 8 | * 包装tuple,从而能够和page进行交互 9 | * 10 | * @Author lizhuyang 11 | */ 12 | public class ItemData { 13 | 14 | // 帧结构 15 | // [length]([type][length][data])* 16 | private IndexEntry indexEntry; 17 | // Item实际存储的offset 18 | private int offset; 19 | // Item实际的长度 20 | private int length; 21 | 22 | public ItemData(IndexEntry indexEntry) { 23 | this.indexEntry = indexEntry; 24 | length = indexEntry.getLength(); 25 | } 26 | 27 | public void write(Page page) { 28 | // 获取总长度 29 | int tupleLength = length; 30 | // 找到写入位置 31 | int writePosition = page.getUpperOffset() - tupleLength; 32 | // 写入数据 33 | page.writeBytes(indexEntry.getBytes(), writePosition); 34 | // 更新upperOffset 35 | page.modifyUpperOffset(writePosition); 36 | // 更新ItemData的offset,length 37 | offset = writePosition; 38 | } 39 | 40 | public int getOffset() { 41 | return offset; 42 | } 43 | 44 | public ItemData setOffset(int offset) { 45 | this.offset = offset; 46 | return this; 47 | } 48 | 49 | public int getLength() { 50 | return length; 51 | } 52 | 53 | public ItemData setLength(int length) { 54 | this.length = length; 55 | return this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/factory/FrontConnectionFactory.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.factory; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import alchemystar.freedom.config.SystemConfig; 9 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 10 | import alchemystar.freedom.engine.net.handler.frontend.ServerQueryHandler; 11 | import alchemystar.freedom.engine.session.SessionFactory; 12 | 13 | /** 14 | * FrontendConnection 工厂类 15 | * 16 | * @Author lizhuyang 17 | */ 18 | public class FrontConnectionFactory { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(FrontConnectionFactory.class); 21 | 22 | /** 23 | * MySql ThreadId Generator 24 | */ 25 | private static final AtomicInteger ACCEPT_SEQ = new AtomicInteger(0); 26 | 27 | public FrontendConnection getConnection() { 28 | FrontendConnection connection = new FrontendConnection(); 29 | connection.setQueryHandler(new ServerQueryHandler(connection)); 30 | connection.setId(ACCEPT_SEQ.getAndIncrement()); 31 | logger.info("connection Id=" + connection.getId()); 32 | connection.setCharset(SystemConfig.DEFAULT_CHARSET); 33 | connection.setTxIsolation(SystemConfig.DEFAULT_TX_ISOLATION); 34 | connection.setLastActiveTime(); 35 | connection.setSession(SessionFactory.newSession(connection)); 36 | return connection; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/SelectHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import alchemystar.freedom.engine.net.response.SelectDatabase; 4 | import alchemystar.freedom.engine.net.response.SelectVersion; 5 | import alchemystar.freedom.engine.net.response.SelectVersionComment; 6 | import alchemystar.freedom.engine.net.response.jdbc.SelectIncrementResponse; 7 | import alchemystar.freedom.engine.parser.ServerParse; 8 | import alchemystar.freedom.engine.parser.ServerParseSelect; 9 | 10 | /** 11 | * SelectHandler 12 | * 13 | * @Author lizhuyang 14 | */ 15 | public final class SelectHandler { 16 | 17 | private static String selectIncrement = "SELECT @@session.auto_increment_increment"; 18 | 19 | public static void handle(String stmt, FrontendConnection c, int offs) { 20 | int offset = offs; 21 | switch (ServerParseSelect.parse(stmt, offs)) { 22 | case ServerParseSelect.DATABASE: 23 | SelectDatabase.response(c); 24 | break; 25 | case ServerParseSelect.VERSION_COMMENT: 26 | SelectVersionComment.response(c); 27 | break; 28 | case ServerParseSelect.VERSION: 29 | SelectVersion.response(c); 30 | break; 31 | default: 32 | if (selectIncrement.equals(stmt)) { 33 | SelectIncrementResponse.response(c); 34 | } else { 35 | c.execute(stmt, ServerParse.SELECT); 36 | } 37 | break; 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/ResultSetHeaderPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * 结果集头部Packet 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class ResultSetHeaderPacket extends MySQLPacket { 14 | 15 | public int fieldCount; 16 | public long extra; 17 | 18 | public void read(byte[] data) { 19 | MySQLMessage mm = new MySQLMessage(data); 20 | this.packetLength = mm.readUB3(); 21 | this.packetId = mm.read(); 22 | this.fieldCount = (int) mm.readLength(); 23 | if (mm.hasRemaining()) { 24 | this.extra = mm.readLength(); 25 | } 26 | } 27 | 28 | @Override 29 | public ByteBuf writeBuf(ByteBuf buffer, ChannelHandlerContext ctx) { 30 | int size = calcPacketSize(); 31 | BufferUtil.writeUB3(buffer, size); 32 | buffer.writeByte(packetId); 33 | BufferUtil.writeLength(buffer, fieldCount); 34 | if (extra > 0) { 35 | BufferUtil.writeLength(buffer, extra); 36 | } 37 | return buffer; 38 | } 39 | 40 | @Override 41 | public int calcPacketSize() { 42 | int size = BufferUtil.getLength(fieldCount); 43 | if (extra > 0) { 44 | size += BufferUtil.getLength(extra); 45 | } 46 | return size; 47 | } 48 | 49 | @Override 50 | protected String getPacketInfo() { 51 | return "MySQL ResultSetHeader Packet"; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/Fields.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * 字段类型及标识定义 5 | * 6 | * @author lizhuyang 7 | */ 8 | public interface Fields { 9 | 10 | /** field data type */ 11 | int FIELD_TYPE_DECIMAL = 0; 12 | int FIELD_TYPE_TINY = 1; 13 | int FIELD_TYPE_SHORT = 2; 14 | int FIELD_TYPE_LONG = 3; 15 | int FIELD_TYPE_FLOAT = 4; 16 | int FIELD_TYPE_DOUBLE = 5; 17 | int FIELD_TYPE_NULL = 6; 18 | int FIELD_TYPE_TIMESTAMP = 7; 19 | int FIELD_TYPE_LONGLONG = 8; 20 | int FIELD_TYPE_INT24 = 9; 21 | int FIELD_TYPE_DATE = 10; 22 | int FIELD_TYPE_TIME = 11; 23 | int FIELD_TYPE_DATETIME = 12; 24 | int FIELD_TYPE_YEAR = 13; 25 | int FIELD_TYPE_NEWDATE = 14; 26 | int FIELD_TYPE_VARCHAR = 15; 27 | int FIELD_TYPE_BIT = 16; 28 | int FIELD_TYPE_NEW_DECIMAL = 246; 29 | int FIELD_TYPE_ENUM = 247; 30 | int FIELD_TYPE_SET = 248; 31 | int FIELD_TYPE_TINY_BLOB = 249; 32 | int FIELD_TYPE_MEDIUM_BLOB = 250; 33 | int FIELD_TYPE_LONG_BLOB = 251; 34 | int FIELD_TYPE_BLOB = 252; 35 | int FIELD_TYPE_VAR_STRING = 253; 36 | int FIELD_TYPE_STRING = 254; 37 | int FIELD_TYPE_GEOMETRY = 255; 38 | 39 | /** field flag */ 40 | int NOT_NULL_FLAG = 0x0001; 41 | int PRI_KEY_FLAG = 0x0002; 42 | int UNIQUE_KEY_FLAG = 0x0004; 43 | int MULTIPLE_KEY_FLAG = 0x0008; 44 | int BLOB_FLAG = 0x0010; 45 | int UNSIGNED_FLAG = 0x0020; 46 | int ZEROFILL_FLAG = 0x0040; 47 | int BINARY_FLAG = 0x0080; 48 | int ENUM_FLAG = 0x0100; 49 | int AUTO_INCREMENT_FLAG = 0x0200; 50 | int TIMESTAMP_FLAG = 0x0400; 51 | int SET_FLAG = 0x0800; 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/LogTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test; 2 | 3 | import java.util.List; 4 | 5 | import org.junit.Test; 6 | 7 | import alchemystar.freedom.engine.session.Session; 8 | import alchemystar.freedom.engine.session.SessionFactory; 9 | import alchemystar.freedom.sql.SqlExecutor; 10 | import alchemystar.freedom.store.log.LogStore; 11 | import alchemystar.freedom.test.sqltest.CreateTest; 12 | import alchemystar.freedom.transaction.log.Log; 13 | 14 | /** 15 | * @Author lizhuyang 16 | */ 17 | public class LogTest { 18 | 19 | public static final String insertSqlTemplate = "insert into test (id,name) values (?,?)"; 20 | 21 | @Test 22 | public void testWrite() { 23 | SqlExecutor executor = new SqlExecutor(); 24 | executor.execute(CreateTest.CREATE_SQL, null, null); 25 | insertSome(); 26 | } 27 | 28 | @Test 29 | public void testRead() { 30 | LogStore logStore = new LogStore(); 31 | List list = logStore.loadLog(); 32 | System.out.println(list); 33 | } 34 | 35 | private void insertSome() { 36 | Session session = SessionFactory.newSession(null); 37 | for (int i = 0; i < 50; i++) { 38 | String insertSql = 39 | insertSqlTemplate.replaceFirst("\\?", String.valueOf(i)).replaceFirst("\\?", "'alchemystar" + 40 | String 41 | .valueOf(i) + "'").replaceFirst("\\?", "'comment" + String.valueOf(i) + "'"); 42 | System.out.println(insertSql); 43 | SqlExecutor sqlExecutor = new SqlExecutor(); 44 | sqlExecutor.execute(insertSql, null, session); 45 | } 46 | session.commit(); 47 | 48 | System.out.println("insert okay"); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/CommandPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * CommandPacket 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class CommandPacket extends MySQLPacket { 14 | 15 | public byte command; 16 | public byte[] arg; 17 | 18 | public CommandPacket(String query,byte type) { 19 | packetId = 0; 20 | command = type; 21 | arg = query.getBytes(); 22 | } 23 | 24 | public CommandPacket(String query) { 25 | packetId = 0; 26 | command = MySQLPacket.COM_QUERY; 27 | arg = query.getBytes(); 28 | } 29 | 30 | public void read(byte[] data) { 31 | MySQLMessage mm = new MySQLMessage(data); 32 | packetLength = mm.readUB3(); 33 | packetId = mm.read(); 34 | command = mm.read(); 35 | arg = mm.readBytes(); 36 | } 37 | 38 | 39 | public ByteBuf getByteBuf(ChannelHandlerContext ctx){ 40 | ByteBuf buffer = ctx.alloc().buffer(); 41 | BufferUtil.writeUB3(buffer, calcPacketSize()); 42 | buffer.writeByte(packetId); 43 | buffer.writeByte(command); 44 | buffer.writeBytes(arg); 45 | return buffer; 46 | } 47 | 48 | public void write(ChannelHandlerContext ctx) { 49 | ctx.writeAndFlush(getByteBuf(ctx)); 50 | } 51 | 52 | @Override 53 | public int calcPacketSize() { 54 | return 1 + arg.length; 55 | } 56 | 57 | @Override 58 | protected String getPacketInfo() { 59 | return "MySQL Command Packet"; 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Abstraction issues 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 1.8 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * 随机工具类 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public class RandomUtil { 9 | private static final byte[] bytes = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'q', 'w', 'e', 'r', 't', 10 | 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm', 11 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 12 | 'C', 'V', 'B', 'N', 'M'}; 13 | private static final long multiplier = 0x5DEECE66DL; 14 | private static final long addend = 0xBL; 15 | private static final long mask = (1L << 48) - 1; 16 | private static final long integerMask = (1L << 33) - 1; 17 | private static final long seedUniquifier = 8682522807148012L; 18 | 19 | private static long seed; 20 | 21 | static { 22 | long s = seedUniquifier + System.nanoTime(); 23 | s = (s ^ multiplier) & mask; 24 | seed = s; 25 | } 26 | 27 | public static final byte[] randomBytes(int size) { 28 | byte[] bb = bytes; 29 | byte[] ab = new byte[size]; 30 | for (int i = 0; i < size; i++) { 31 | ab[i] = randomByte(bb); 32 | } 33 | return ab; 34 | } 35 | 36 | private static byte randomByte(byte[] b) { 37 | int ran = (int) ((next() & integerMask) >>> 16); 38 | return b[ran % b.length]; 39 | } 40 | 41 | private static long next() { 42 | long oldSeed = seed; 43 | long nextSeed = 0L; 44 | do { 45 | nextSeed = (oldSeed * multiplier + addend) & mask; 46 | } while (oldSeed == nextSeed); 47 | seed = nextSeed; 48 | return nextSeed; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/page/PageLoader.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.page; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import alchemystar.freedom.meta.IndexEntry; 7 | import alchemystar.freedom.store.item.ItemPointer; 8 | 9 | /** 10 | * PageLoader 11 | * 存储了一页page中所有的tuple 12 | * 13 | * @Author lizhuyang 14 | */ 15 | public class PageLoader { 16 | 17 | Page page; 18 | private IndexEntry[] indexEntries; 19 | private int tupleCount; 20 | 21 | public PageLoader(Page page) { 22 | this.page = page; 23 | } 24 | 25 | public void load() { 26 | PageHeaderData pageHeaderData = PageHeaderData.read(page); 27 | tupleCount = pageHeaderData.getTupleCount(); 28 | int ptrStartOff = pageHeaderData.getLength(); 29 | // 首先建立存储tuple的数组 30 | List temp = new ArrayList(); 31 | // 循环读取 32 | for (int i = 0; i < tupleCount; i++) { 33 | // 重新从page读取tuple 34 | ItemPointer ptr = new ItemPointer(page.readInt(), page.readInt()); 35 | if (ptr.getTupleLength() == -1) { 36 | continue; 37 | } 38 | byte[] bb = page.readBytes(ptr.getOffset(), ptr.getTupleLength()); 39 | IndexEntry indexEntry = new IndexEntry(); 40 | indexEntry.read(bb); 41 | temp.add(indexEntry); 42 | // 进入到下一个元组位置 43 | ptrStartOff = ptrStartOff + ptr.getTupleLength(); 44 | } 45 | // 由于可能由于被删除,置为-1,所以以temp为准 46 | indexEntries = temp.toArray(new IndexEntry[temp.size()]); 47 | tupleCount = temp.size(); 48 | } 49 | 50 | public IndexEntry[] getIndexEntries() { 51 | return indexEntries; 52 | } 53 | 54 | public int getTuplCount() { 55 | return tupleCount; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/bptest/BasicGenTable.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test.bptest; 2 | 3 | import alchemystar.freedom.index.bp.BPTree; 4 | import alchemystar.freedom.meta.Attribute; 5 | import alchemystar.freedom.meta.Table; 6 | import alchemystar.freedom.meta.value.Value; 7 | 8 | /** 9 | * @Author lizhuyang 10 | */ 11 | public class BasicGenTable { 12 | 13 | public Table genTable() { 14 | Table table = new Table(); 15 | table.setName("test"); 16 | table.setAttributes(getTableAttributes()); 17 | BPTree clusterIndex = new BPTree(table, "clusterIndex", getClusterAttributes()); 18 | clusterIndex.setPrimaryKey(true); 19 | table.setClusterIndex(clusterIndex); 20 | BPTree secondIndex = new BPTree(table, "secondIndex", getSecondAttributes()); 21 | table.getSecondIndexes().add(secondIndex); 22 | return table; 23 | } 24 | 25 | public Attribute[] getTableAttributes() { 26 | Attribute[] attributes = new Attribute[3]; 27 | // id 作为主键 28 | attributes[0] = new Attribute("id", Value.LONG, 0, "id"); 29 | attributes[0].setPrimaryKey(true); 30 | attributes[1] = new Attribute("name", Value.STRING, 1, "name"); 31 | attributes[2] = new Attribute("comment", Value.STRING, 2, "comment"); 32 | return attributes; 33 | } 34 | 35 | public Attribute[] getClusterAttributes() { 36 | Attribute[] attributes = new Attribute[1]; 37 | attributes[0] = new Attribute("id", Value.LONG, 0, "id"); 38 | return attributes; 39 | } 40 | 41 | public Attribute[] getSecondAttributes() { 42 | Attribute[] attributes = new Attribute[2]; 43 | attributes[0] = new Attribute("name", Value.STRING, 0, "name"); 44 | attributes[1] = new Attribute("id", Value.LONG, 1, "id"); 45 | return attributes; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/sql/parser/DeleteVisitor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.sql.parser; 2 | 3 | import com.alibaba.druid.sql.ast.SQLExpr; 4 | import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; 5 | import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; 6 | import com.alibaba.druid.sql.ast.statement.SQLTableSource; 7 | import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; 8 | 9 | import alchemystar.freedom.meta.Table; 10 | import alchemystar.freedom.meta.TableManager; 11 | import alchemystar.freedom.sql.select.TableFilter; 12 | 13 | /** 14 | * @Author lizhuyang 15 | */ 16 | public class DeleteVisitor extends SQLASTVisitorAdapter { 17 | 18 | private TableFilter tableFilter; 19 | 20 | private SQLExpr where; 21 | 22 | private WhereVisitor whereVisitor; 23 | 24 | protected Table table; 25 | 26 | private SQLTableSource tableSource; 27 | 28 | public boolean visit(SQLDeleteStatement x) { 29 | tableSource = x.getTableSource(); 30 | if (!(tableSource instanceof SQLExprTableSource)) { 31 | throw new RuntimeException("not support this table source type :" + tableSource); 32 | } 33 | table = TableManager.getTable(tableSource.toString()); 34 | if (x.getWhere() == null) { 35 | throw new RuntimeException("delete must have where"); 36 | } 37 | this.where = x.getWhere(); 38 | whereVisitor = new WhereVisitor(); 39 | x.getWhere().accept(whereVisitor); 40 | tableFilter = TableManager.newTableFilter((SQLExprTableSource) tableSource, x.getWhere()); 41 | return true; 42 | } 43 | 44 | public TableFilter getTableFilter() { 45 | return tableFilter; 46 | } 47 | 48 | public SQLExpr getWhere() { 49 | return where; 50 | } 51 | 52 | public WhereVisitor getWhereVisitor() { 53 | return whereVisitor; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/factory/FrontHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.factory; 2 | 3 | import alchemystar.freedom.config.SystemConfig; 4 | import alchemystar.freedom.engine.net.codec.MySqlPacketDecoder; 5 | import alchemystar.freedom.engine.net.handler.frontend.FrontendAuthenticator; 6 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 7 | import alchemystar.freedom.engine.net.handler.frontend.FrontendGroupHandler; 8 | import alchemystar.freedom.engine.net.handler.frontend.FrontendTailHandler; 9 | import io.netty.channel.ChannelInitializer; 10 | import io.netty.channel.socket.SocketChannel; 11 | import io.netty.handler.timeout.IdleStateHandler; 12 | 13 | /** 14 | * 前端handler工厂 15 | * 16 | * @Author lizhuyang 17 | */ 18 | public class FrontHandlerFactory extends ChannelInitializer { 19 | 20 | private FrontConnectionFactory factory; 21 | 22 | public FrontHandlerFactory() { 23 | factory = new FrontConnectionFactory(); 24 | } 25 | 26 | @Override 27 | protected void initChannel(SocketChannel ch) throws Exception { 28 | 29 | FrontendConnection source = factory.getConnection(); 30 | FrontendGroupHandler groupHandler = new FrontendGroupHandler(source); 31 | FrontendAuthenticator authHandler = new FrontendAuthenticator(source); 32 | FrontendTailHandler tailHandler = new FrontendTailHandler(source); 33 | // 心跳handler 34 | ch.pipeline().addLast(new IdleStateHandler(SystemConfig.IDLE_CHECK_INTERVAL, SystemConfig.IDLE_CHECK_INTERVAL, SystemConfig.IDLE_CHECK_INTERVAL)); 35 | // decode mysql packet depend on it's length 36 | ch.pipeline().addLast(new MySqlPacketDecoder()); 37 | ch.pipeline().addLast(groupHandler); 38 | ch.pipeline().addLast(authHandler); 39 | ch.pipeline().addLast(tailHandler); 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/KillHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import alchemystar.freedom.engine.net.proto.mysql.OkPacket; 4 | import alchemystar.freedom.engine.net.proto.util.ErrorCode; 5 | import alchemystar.freedom.engine.net.proto.util.StringUtil; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * KillHandler 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class KillHandler { 14 | 15 | public static void handle(String stmt, int offset, FrontendConnection c) { 16 | ChannelHandlerContext ctx = c.getCtx(); 17 | String id = stmt.substring(offset).trim(); 18 | if (StringUtil.isEmpty(id)) { 19 | c.writeErrMessage(ErrorCode.ER_NO_SUCH_THREAD, "NULL connection id"); 20 | } else { 21 | // get value 22 | long value = 0; 23 | try { 24 | value = Long.parseLong(id); 25 | } catch (NumberFormatException e) { 26 | c.writeErrMessage(ErrorCode.ER_NO_SUCH_THREAD, "Invalid connection id:" + id); 27 | return; 28 | } 29 | 30 | // kill myself 31 | if (value == c.getId()) { 32 | getOkPacket().write(ctx); 33 | return; 34 | } 35 | 36 | // get connection and close it 37 | FrontendConnection fc = FrontendGroupHandler.frontendGroup.get(value); 38 | 39 | if (fc != null) { 40 | fc.close(); 41 | getOkPacket().write(ctx); 42 | } else { 43 | c.writeErrMessage(ErrorCode.ER_NO_SUCH_THREAD, "Unknown connection id:" + id); 44 | } 45 | } 46 | } 47 | 48 | private static OkPacket getOkPacket() { 49 | OkPacket packet = new OkPacket(); 50 | packet.packetId = 1; 51 | packet.affectedRows = 0; 52 | packet.serverStatus = 2; 53 | return packet; 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/EOFPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * EOFPacket 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class EOFPacket extends MySQLPacket { 14 | public static final byte FIELD_COUNT = (byte) 0xfe; 15 | 16 | public byte fieldCount = FIELD_COUNT; 17 | public int warningCount; 18 | public int status = 2; 19 | 20 | public void read(byte[] data) { 21 | MySQLMessage mm = new MySQLMessage(data); 22 | packetLength = mm.readUB3(); 23 | packetId = mm.read(); 24 | fieldCount = mm.read(); 25 | warningCount = mm.readUB2(); 26 | status = mm.readUB2(); 27 | } 28 | 29 | public void read(BinaryPacket bin) { 30 | packetLength = bin.packetLength; 31 | packetId = bin.packetId; 32 | MySQLMessage mm = new MySQLMessage(bin.data); 33 | fieldCount = mm.read(); 34 | warningCount = mm.readUB2(); 35 | status = mm.readUB2(); 36 | } 37 | 38 | @Override 39 | public ByteBuf writeBuf(ByteBuf buffer, ChannelHandlerContext ctx) { 40 | int size = calcPacketSize(); 41 | BufferUtil.writeUB3(buffer, size); 42 | buffer.writeByte(packetId); 43 | buffer.writeByte(fieldCount); 44 | BufferUtil.writeUB2(buffer, warningCount); 45 | BufferUtil.writeUB2(buffer, status); 46 | return buffer; 47 | } 48 | 49 | public boolean hasStatusFlag(long flag) { 50 | return ((this.status & flag) == flag); 51 | } 52 | 53 | @Override 54 | public int calcPacketSize() { 55 | return 5;// 1+2+2; 56 | } 57 | 58 | @Override 59 | protected String getPacketInfo() { 60 | return "MySQL EOF Packet"; 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/TableMetaTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test; 2 | 3 | import org.junit.Test; 4 | 5 | import alchemystar.freedom.meta.Table; 6 | import alchemystar.freedom.meta.TableLoader; 7 | import alchemystar.freedom.meta.TableManager; 8 | import alchemystar.freedom.sql.SqlExecutor; 9 | import alchemystar.freedom.test.bptest.BasicGenTable; 10 | import alchemystar.freedom.test.sqltest.CreateTest; 11 | import alchemystar.freedom.test.sqltest.SelectTest; 12 | 13 | /** 14 | * @Author lizhuyang 15 | */ 16 | public class TableMetaTest extends BasicGenTable { 17 | 18 | @Test 19 | public void metaWriteTest() { 20 | init(); 21 | TableLoader tableLoader = new TableLoader(); 22 | for (Table table : TableManager.getTableMap().values()) { 23 | tableLoader.writeTableMeta(table); 24 | } 25 | } 26 | 27 | @Test 28 | public void metaReadTest() { 29 | TableLoader tableLoader = new TableLoader(); 30 | tableLoader.readAllTable(); 31 | insertSome(); 32 | SqlExecutor sqlExecutor = new SqlExecutor(); 33 | sqlExecutor.execute(SelectTest.joinSql, null, null); 34 | } 35 | 36 | private void insertSome() { 37 | for (int i = 0; i < 50; i++) { 38 | String insertSql = 39 | BasicSelectTest.insertSqlTemplate.replaceFirst("\\?", String.valueOf(i)).replaceFirst("\\?", 40 | "'alchemystar" + 41 | String 42 | .valueOf(i) + "'").replaceFirst("\\?", "'comment" + String.valueOf(i) + "'"); 43 | System.out.println(insertSql); 44 | SqlExecutor sqlExecutor = new SqlExecutor(); 45 | sqlExecutor.execute(insertSql, null, null); 46 | } 47 | 48 | System.out.println("insert okay"); 49 | } 50 | 51 | public void init() { 52 | SqlExecutor executor = new SqlExecutor(); 53 | executor.execute(CreateTest.CREATE_SQL, null, null); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/value/ValueBoolean.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta.value; 2 | 3 | /** 4 | * ValueBoolean 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public class ValueBoolean extends Value { 9 | 10 | private boolean b; 11 | 12 | public ValueBoolean() { 13 | } 14 | 15 | public ValueBoolean(boolean b) { 16 | this.b = b; 17 | } 18 | 19 | @Override 20 | public int getLength() { 21 | // 1 for type 22 | return 1 + 1; 23 | } 24 | 25 | @Override 26 | public byte getType() { 27 | return BOOLEAN; 28 | } 29 | 30 | // [type][data] 31 | @Override 32 | public byte[] getBytes() { 33 | byte[] result = new byte[2]; 34 | result[0] = BOOLEAN; 35 | if (b) { 36 | result[1] = 1; 37 | } else { 38 | result[1] = 0; 39 | } 40 | return result; 41 | } 42 | 43 | @Override 44 | public void read(byte[] bytes) { 45 | if (bytes[0] == 0) { 46 | b = false; 47 | } else { 48 | b = true; 49 | } 50 | } 51 | 52 | @Override 53 | public String getString() { 54 | return toString(); 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | if (b) { 60 | return "true"; 61 | } else { 62 | return "false"; 63 | } 64 | } 65 | 66 | public boolean getBoolean() { 67 | return b; 68 | } 69 | 70 | public ValueBoolean setBoolean(boolean b) { 71 | this.b = b; 72 | return this; 73 | } 74 | 75 | @Override 76 | public int compare(Value value) { 77 | boolean toCompare = ((ValueBoolean) value).getBoolean(); 78 | if (b) { 79 | if (toCompare) { 80 | return 0; 81 | } else { 82 | return 1; 83 | } 84 | } else { 85 | if (toCompare) { 86 | return -1; 87 | } else { 88 | return 0; 89 | } 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/recovery/RecoverManager.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.recovery; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import alchemystar.freedom.store.log.LogStore; 9 | import alchemystar.freedom.transaction.Trx; 10 | import alchemystar.freedom.transaction.TrxManager; 11 | import alchemystar.freedom.transaction.log.Log; 12 | import alchemystar.freedom.transaction.log.LogType; 13 | 14 | /** 15 | * @Author lizhuyang 16 | */ 17 | public class RecoverManager { 18 | 19 | private LogStore logStore; 20 | 21 | // 先简单的使用redo log 进行redo 22 | public void recover() { 23 | // 首先由logStore加载出所有的log 24 | List list = logStore.loadLog(); 25 | // 找出所有已经提交的事务 26 | List trxList = getAllCommittedTrx(list); 27 | // 然后进行redo操作 28 | for (Trx trx : trxList) { 29 | trx.redo(); 30 | } 31 | } 32 | 33 | public List getAllCommittedTrx(List list) { 34 | List trxList = new ArrayList(); 35 | Map trxMap = new HashMap(); 36 | // 找出所有已提交的trx 37 | for (Log log : list) { 38 | if (log.getLogType() == LogType.TRX_START) { 39 | Trx trx = TrxManager.newEmptyTrx(); 40 | trx.setTrxId(log.getTrxId()); 41 | trx.addLog(log); 42 | trxMap.put(trx.getTrxId(), trx); 43 | } else if (log.getLogType() == LogType.ROW) { 44 | trxMap.get(log.getTrxId()).addLog(log); 45 | } else if (log.getLogType() == LogType.COMMIT) { 46 | trxMap.get(log.getTrxId()).addLog(log); 47 | // 按照先commit的顺序来 48 | trxList.add(trxMap.get(log.getTrxId())); 49 | } 50 | } 51 | 52 | return trxList; 53 | } 54 | 55 | public LogStore getLogStore() { 56 | return logStore; 57 | } 58 | 59 | public void setLogStore(LogStore logStore) { 60 | this.logStore = logStore; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/access/SecondIndexCursor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.access; 2 | 3 | import alchemystar.freedom.index.BaseIndex; 4 | import alchemystar.freedom.index.Index; 5 | import alchemystar.freedom.index.bp.Position; 6 | import alchemystar.freedom.meta.Attribute; 7 | import alchemystar.freedom.meta.IndexDesc; 8 | import alchemystar.freedom.meta.IndexEntry; 9 | import alchemystar.freedom.meta.value.Value; 10 | import alchemystar.freedom.meta.value.ValueLong; 11 | 12 | /** 13 | * @Author lizhuyang 14 | */ 15 | public class SecondIndexCursor extends BaseIndexCursor { 16 | 17 | private Index clusterIndex; 18 | 19 | public SecondIndexCursor(Position startPos, Position endPos, 20 | boolean isEqual) { 21 | super(startPos, endPos, isEqual); 22 | } 23 | 24 | @Override 25 | public IndexEntry next() { 26 | IndexEntry secondIndexEntry = super.next(); 27 | if (secondIndexEntry == null) { 28 | return null; 29 | } 30 | IndexEntry searchEntry = getSearchEntry(secondIndexEntry); 31 | // 直接找到其主键即可 32 | Cursor cursor = clusterIndex.searchEqual(searchEntry); 33 | if (cursor == null) { 34 | return null; 35 | } else { 36 | // 主键肯定唯一 37 | return cursor.next(); 38 | } 39 | } 40 | 41 | private IndexEntry getSearchEntry(IndexEntry indexEntry) { 42 | // rowId肯定是二级索引的最后一个字段 43 | ValueLong rowId = (ValueLong) indexEntry.getValues()[indexEntry.getValues().length - 1]; 44 | IndexEntry result = new IndexEntry(new Value[] {rowId}); 45 | // 由于是cluster计算,所以设置为cluster desc 46 | Attribute primaryAttr = ((BaseIndex) clusterIndex).getIndexDesc().getPrimaryAttr(); 47 | result.setIndexDesc(new IndexDesc(new Attribute[] {primaryAttr})); 48 | return result; 49 | } 50 | 51 | public Index getClusterIndex() { 52 | return clusterIndex; 53 | } 54 | 55 | public void setClusterIndex(Index clusterIndex) { 56 | this.clusterIndex = clusterIndex; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/parser/util/CharTypes.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.parser.util; 2 | 3 | /** 4 | * CharTypes 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public class CharTypes { 9 | private final static boolean[] hexFlags = new boolean[256]; 10 | static { 11 | for (char c = 0; c < hexFlags.length; ++c) { 12 | if (c >= 'A' && c <= 'F') { 13 | hexFlags[c] = true; 14 | } else if (c >= 'a' && c <= 'f') { 15 | hexFlags[c] = true; 16 | } else if (c >= '0' && c <= '9') { 17 | hexFlags[c] = true; 18 | } 19 | } 20 | } 21 | 22 | public static boolean isHex(char c) { 23 | return c < 256 && hexFlags[c]; 24 | } 25 | 26 | public static boolean isDigit(char c) { 27 | return c >= '0' && c <= '9'; 28 | } 29 | 30 | private final static boolean[] identifierFlags = new boolean[256]; 31 | static { 32 | for (char c = 0; c < identifierFlags.length; ++c) { 33 | if (c >= 'A' && c <= 'Z') { 34 | identifierFlags[c] = true; 35 | } else if (c >= 'a' && c <= 'z') { 36 | identifierFlags[c] = true; 37 | } else if (c >= '0' && c <= '9') { 38 | identifierFlags[c] = true; 39 | } 40 | } 41 | // identifierFlags['`'] = true; 42 | identifierFlags['_'] = true; 43 | identifierFlags['$'] = true; 44 | } 45 | 46 | public static boolean isIdentifierChar(char c) { 47 | return c > identifierFlags.length || identifierFlags[c]; 48 | } 49 | 50 | private final static boolean[] whitespaceFlags = new boolean[256]; 51 | static { 52 | whitespaceFlags[' '] = true; 53 | whitespaceFlags['\n'] = true; 54 | whitespaceFlags['\r'] = true; 55 | whitespaceFlags['\t'] = true; 56 | whitespaceFlags['\f'] = true; 57 | whitespaceFlags['\b'] = true; 58 | } 59 | 60 | public static boolean isWhitespace(char c) { 61 | return c <= whitespaceFlags.length && whitespaceFlags[c]; 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/Database.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine; 2 | 3 | import alchemystar.freedom.meta.TableLoader; 4 | import alchemystar.freedom.recovery.RecoverManager; 5 | import alchemystar.freedom.store.log.LogStore; 6 | 7 | /** 8 | * @Author lizhuyang 9 | */ 10 | public class Database { 11 | 12 | private static Database database = null; 13 | // 默认端口号是8090 14 | private int serverPort = 8090; 15 | // 默认用户名密码是pay|miracle 16 | private String userName = "pay"; 17 | private String passWd = "MiraCle"; 18 | private TableLoader tableLoader; 19 | private LogStore logStore; 20 | 21 | // 单例模式 22 | static { 23 | database = new Database(); 24 | // 加载数据 25 | TableLoader tableLoader = new TableLoader(); 26 | tableLoader.readAllTable(); 27 | database.setTableLoader(tableLoader); 28 | LogStore logStore = new LogStore(); 29 | database.setLogStore(logStore); 30 | RecoverManager recoverManager = new RecoverManager(); 31 | recoverManager.setLogStore(logStore); 32 | recoverManager.recover(); 33 | } 34 | 35 | public static Database getInstance() { 36 | return database; 37 | } 38 | 39 | public int getServerPort() { 40 | return serverPort; 41 | } 42 | 43 | public void setServerPort(int serverPort) { 44 | this.serverPort = serverPort; 45 | } 46 | 47 | public String getUserName() { 48 | return userName; 49 | } 50 | 51 | public void setUserName(String userName) { 52 | this.userName = userName; 53 | } 54 | 55 | public String getPassWd() { 56 | return passWd; 57 | } 58 | 59 | public void setPassWd(String passWd) { 60 | this.passWd = passWd; 61 | } 62 | 63 | public TableLoader getTableLoader() { 64 | return tableLoader; 65 | } 66 | 67 | public void setTableLoader(TableLoader tableLoader) { 68 | this.tableLoader = tableLoader; 69 | } 70 | 71 | public LogStore getLogStore() { 72 | return logStore; 73 | } 74 | 75 | public void setLogStore(LogStore logStore) { 76 | this.logStore = logStore; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/bptest/PageTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test.bptest; 2 | 3 | import org.junit.Test; 4 | 5 | import alchemystar.freedom.config.SystemConfig; 6 | import alchemystar.freedom.meta.IndexEntry; 7 | import alchemystar.freedom.meta.value.Value; 8 | import alchemystar.freedom.meta.value.ValueBoolean; 9 | import alchemystar.freedom.meta.value.ValueInt; 10 | import alchemystar.freedom.meta.value.ValueLong; 11 | import alchemystar.freedom.meta.value.ValueString; 12 | import alchemystar.freedom.store.fs.FStore; 13 | import alchemystar.freedom.store.item.Item; 14 | import alchemystar.freedom.store.page.Page; 15 | import alchemystar.freedom.store.page.PageLoader; 16 | import alchemystar.freedom.store.page.PagePool; 17 | 18 | /** 19 | * PageTest 20 | * 21 | * @Author lizhuyang 22 | */ 23 | public class PageTest { 24 | 25 | @Test 26 | public void pageTest() { 27 | Value[] values = new Value[5]; 28 | values[0] = new ValueString("this is freedom db"); 29 | values[1] = new ValueString("just enjoy it"); 30 | values[2] = new ValueBoolean(true); 31 | values[3] = new ValueInt(5); 32 | values[4] = new ValueLong(6L); 33 | IndexEntry indexEntry = new IndexEntry(values); 34 | Item item = new Item(indexEntry); 35 | System.out.println(item.getLength()); 36 | PagePool pagePool = PagePool.getIntance(); 37 | Page page = pagePool.getFreePage(); 38 | for (int i = 0; i < 1000; i++) { 39 | if (page.writeItem(item)) { 40 | continue; 41 | } else { 42 | System.out.println("btee=" + i + ",page size exhaust"); 43 | break; 44 | } 45 | } 46 | FStore fStore = new FStore(SystemConfig.FREEDOM_REL_DATA_PATH); 47 | fStore.open(); 48 | fStore.writePageToFile(page, 0); 49 | fStore.writePageToFile(page, 10); 50 | 51 | PageLoader loader = fStore.readPageLoaderFromFile(0); 52 | IndexEntry[] indexEntries = loader.getIndexEntries(); 53 | for (int i = 0; i < indexEntries.length; i++) { 54 | System.out.println(indexEntries[i]); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/jdbc/SelectIncrementResponse.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response.jdbc; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | import alchemystar.freedom.engine.net.proto.mysql.EOFPacket; 5 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 6 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 7 | import alchemystar.freedom.engine.net.proto.mysql.RowDataPacket; 8 | import alchemystar.freedom.engine.net.proto.util.Fields; 9 | import alchemystar.freedom.engine.net.proto.util.PacketUtil; 10 | import io.netty.buffer.ByteBuf; 11 | import io.netty.channel.ChannelHandlerContext; 12 | 13 | /** 14 | * @Author lizhuyang 15 | */ 16 | public class SelectIncrementResponse { 17 | 18 | private static final int FIELD_COUNT = 1; 19 | private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT); 20 | private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT]; 21 | private static final EOFPacket eof = new EOFPacket(); 22 | 23 | static { 24 | int i = 0; 25 | byte packetId = 0; 26 | header.packetId = ++packetId; 27 | fields[i] = PacketUtil.getField("@@session.auto_increment_increment", Fields.FIELD_TYPE_VAR_STRING); 28 | fields[i++].packetId = ++packetId; 29 | eof.packetId = ++packetId; 30 | } 31 | 32 | public static void response(FrontendConnection c) { 33 | ChannelHandlerContext ctx = c.getCtx(); 34 | ByteBuf buffer = ctx.alloc().buffer(); 35 | buffer = header.writeBuf(buffer, ctx); 36 | for (FieldPacket field : fields) { 37 | buffer = field.writeBuf(buffer, ctx); 38 | } 39 | buffer = eof.writeBuf(buffer, ctx); 40 | byte packetId = eof.packetId; 41 | RowDataPacket row = new RowDataPacket(FIELD_COUNT); 42 | row.add("1".getBytes()); 43 | row.packetId = ++packetId; 44 | buffer = row.writeBuf(buffer, ctx); 45 | EOFPacket lastEof = new EOFPacket(); 46 | lastEof.packetId = ++packetId; 47 | buffer = lastEof.writeBuf(buffer, ctx); 48 | ctx.writeAndFlush(buffer); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/SelectTxResponse.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | import alchemystar.freedom.engine.net.proto.mysql.EOFPacket; 5 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 6 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 7 | import alchemystar.freedom.engine.net.proto.mysql.RowDataPacket; 8 | import alchemystar.freedom.engine.net.proto.util.Fields; 9 | import alchemystar.freedom.engine.net.proto.util.PacketUtil; 10 | import io.netty.buffer.ByteBuf; 11 | import io.netty.channel.ChannelHandlerContext; 12 | 13 | /** 14 | * SelectTxResponse 15 | * 16 | * @Author lizhuyang 17 | */ 18 | public class SelectTxResponse { 19 | 20 | private static final int FIELD_COUNT = 1; 21 | private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT); 22 | private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT]; 23 | private static final EOFPacket eof = new EOFPacket(); 24 | 25 | static { 26 | int i = 0; 27 | byte packetId = 0; 28 | header.packetId = ++packetId; 29 | fields[i] = PacketUtil.getField("@@session.tx_isolation", Fields.FIELD_TYPE_VAR_STRING); 30 | fields[i++].packetId = ++packetId; 31 | eof.packetId = ++packetId; 32 | } 33 | 34 | public static void response(FrontendConnection c) { 35 | ChannelHandlerContext ctx = c.getCtx(); 36 | ByteBuf buffer = ctx.alloc().buffer(); 37 | buffer = header.writeBuf(buffer, ctx); 38 | for (FieldPacket field : fields) { 39 | buffer = field.writeBuf(buffer, ctx); 40 | } 41 | buffer = eof.writeBuf(buffer, ctx); 42 | byte packetId = eof.packetId; 43 | RowDataPacket row = new RowDataPacket(FIELD_COUNT); 44 | row.add("REPEATABLE-READ".getBytes()); 45 | row.packetId = ++packetId; 46 | buffer = row.writeBuf(buffer, ctx); 47 | EOFPacket lastEof = new EOFPacket(); 48 | lastEof.packetId = ++packetId; 49 | buffer = lastEof.writeBuf(buffer, ctx); 50 | ctx.writeAndFlush(buffer); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/parser/ServerParseStart.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.parser; 2 | 3 | import alchemystar.freedom.engine.parser.util.ParseUtil; 4 | 5 | /** 6 | * ServerParseStart 7 | * author lizhuyang 8 | */ 9 | public final class ServerParseStart { 10 | 11 | public static final int OTHER = -1; 12 | public static final int TRANSACTION = 1; 13 | 14 | public static int parse(String stmt, int offset) { 15 | int i = offset; 16 | for (; i < stmt.length(); i++) { 17 | switch (stmt.charAt(i)) { 18 | case ' ': 19 | continue; 20 | case '/': 21 | case '#': 22 | i = ParseUtil.comment(stmt, i); 23 | continue; 24 | case 'T': 25 | case 't': 26 | return transactionCheck(stmt, i); 27 | default: 28 | return OTHER; 29 | } 30 | } 31 | return OTHER; 32 | } 33 | 34 | // START TRANSACTION 35 | static int transactionCheck(String stmt, int offset) { 36 | if (stmt.length() > offset + "ransaction".length()) { 37 | char c1 = stmt.charAt(++offset); 38 | char c2 = stmt.charAt(++offset); 39 | char c3 = stmt.charAt(++offset); 40 | char c4 = stmt.charAt(++offset); 41 | char c5 = stmt.charAt(++offset); 42 | char c6 = stmt.charAt(++offset); 43 | char c7 = stmt.charAt(++offset); 44 | char c8 = stmt.charAt(++offset); 45 | char c9 = stmt.charAt(++offset); 46 | char c10 = stmt.charAt(++offset); 47 | if ((c1 == 'R' || c1 == 'r') && (c2 == 'A' || c2 == 'a') && (c3 == 'N' || c3 == 'n') 48 | && (c4 == 'S' || c4 == 's') && (c5 == 'A' || c5 == 'a') && (c6 == 'C' || c6 == 'c') 49 | && (c7 == 'T' || c7 == 't') && (c8 == 'I' || c8 == 'i') && (c9 == 'O' || c9 == 'o') 50 | && (c10 == 'N' || c10 == 'n') 51 | && (stmt.length() == ++offset || ParseUtil.isEOF(stmt.charAt(offset)))) { 52 | return TRANSACTION; 53 | } 54 | } 55 | return OTHER; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/codec/MySqlPacketDecoder.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.codec; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import alchemystar.freedom.engine.net.proto.mysql.BinaryPacket; 9 | import alchemystar.freedom.engine.net.proto.util.ByteUtil; 10 | import io.netty.buffer.ByteBuf; 11 | import io.netty.channel.ChannelHandlerContext; 12 | import io.netty.handler.codec.ByteToMessageDecoder; 13 | 14 | /** 15 | * MySqlPacketDecoder 16 | * 17 | * @Author lizhuyang 18 | */ 19 | public class MySqlPacketDecoder extends ByteToMessageDecoder { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(MySqlPacketDecoder.class); 22 | 23 | private final int packetHeaderSize = 4; 24 | private final int maxPacketSize = 16 * 1024 * 1024; 25 | 26 | /** 27 | * MySql外层结构解包 28 | * 29 | * @param ctx 30 | * @param in 31 | * @param out 32 | * 33 | * @throws Exception 34 | */ 35 | @Override 36 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 37 | // 4 bytes:3 length + 1 packetId 38 | if (in.readableBytes() < packetHeaderSize) { 39 | return; 40 | } 41 | in.markReaderIndex(); 42 | int packetLength = ByteUtil.readUB3(in); 43 | // 过载保护 44 | if (packetLength > maxPacketSize) { 45 | throw new IllegalArgumentException("Packet size over the limit " + maxPacketSize); 46 | } 47 | byte packetId = in.readByte(); 48 | if (in.readableBytes() < packetLength) { 49 | // 半包回溯 50 | in.resetReaderIndex(); 51 | return; 52 | } 53 | BinaryPacket packet = new BinaryPacket(); 54 | packet.packetLength = packetLength; 55 | packet.packetId = packetId; 56 | // data will not be accessed any more,so we can use this array safely 57 | packet.data = in.readBytes(packetLength).array(); 58 | if (packet.data == null || packet.data.length == 0) { 59 | logger.error("getDecoder data errorMessage,packetLength=" + packet.packetLength); 60 | } 61 | out.add(packet); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/SelectVersion.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | import alchemystar.freedom.engine.net.proto.mysql.EOFPacket; 5 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 6 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 7 | import alchemystar.freedom.engine.net.proto.mysql.RowDataPacket; 8 | import alchemystar.freedom.engine.net.proto.util.Fields; 9 | import alchemystar.freedom.engine.net.proto.util.PacketUtil; 10 | import alchemystar.freedom.engine.net.proto.util.Versions; 11 | import io.netty.buffer.ByteBuf; 12 | import io.netty.channel.ChannelHandlerContext; 13 | 14 | /** 15 | * SelectVersion 16 | * 17 | * @Author lizhuyang 18 | */ 19 | public class SelectVersion { 20 | 21 | private static final int FIELD_COUNT = 1; 22 | private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT); 23 | private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT]; 24 | private static final EOFPacket eof = new EOFPacket(); 25 | static { 26 | int i = 0; 27 | byte packetId = 0; 28 | header.packetId = ++packetId; 29 | fields[i] = PacketUtil.getField("VERSION()", Fields.FIELD_TYPE_VAR_STRING); 30 | fields[i++].packetId = ++packetId; 31 | eof.packetId = ++packetId; 32 | } 33 | 34 | public static void response(FrontendConnection c) { 35 | ChannelHandlerContext ctx = c.getCtx(); 36 | ByteBuf buffer = ctx.alloc().buffer(); 37 | buffer = header.writeBuf(buffer, ctx); 38 | for (FieldPacket field : fields) { 39 | buffer = field.writeBuf(buffer, ctx); 40 | } 41 | buffer = eof.writeBuf(buffer, ctx); 42 | byte packetId = eof.packetId; 43 | RowDataPacket row = new RowDataPacket(FIELD_COUNT); 44 | row.add(Versions.SERVER_VERSION); 45 | row.packetId = ++packetId; 46 | buffer = row.writeBuf(buffer, ctx); 47 | EOFPacket lastEof = new EOFPacket(); 48 | lastEof.packetId = ++packetId; 49 | buffer = lastEof.writeBuf(buffer, ctx); 50 | ctx.writeAndFlush(buffer); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/sql/SqlExecutor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.sql; 2 | 3 | import com.alibaba.druid.sql.ast.SQLStatement; 4 | import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; 5 | import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; 6 | import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; 7 | import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; 8 | import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; 9 | 10 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 11 | import alchemystar.freedom.engine.net.response.OkResponse; 12 | import alchemystar.freedom.engine.session.Session; 13 | 14 | /** 15 | * @Author lizhuyang 16 | */ 17 | public class SqlExecutor { 18 | 19 | public static final long ONE = 1; 20 | 21 | public void execute(String sql, FrontendConnection con, Session session) { 22 | MySqlStatementParser parser = new MySqlStatementParser(sql); 23 | SQLStatement sqlStatement = parser.parseStatement(); 24 | if (sqlStatement instanceof SQLCreateTableStatement) { 25 | CreateExecutor createExecutor = new CreateExecutor(sqlStatement); 26 | createExecutor.execute(); 27 | if (con != null) { 28 | OkResponse.response(con); 29 | } 30 | return; 31 | } else if (sqlStatement instanceof SQLInsertStatement) { 32 | InsertExecutor insertExecutor = new InsertExecutor(sqlStatement); 33 | insertExecutor.execute(session); 34 | if (con != null) { 35 | OkResponse.responseWithAffectedRows(con, ONE); 36 | } 37 | return; 38 | } else if (sqlStatement instanceof SQLSelectStatement) { 39 | SelectExecutor selectExecutor = new SelectExecutor(sqlStatement, con); 40 | selectExecutor.execute(); 41 | return; 42 | } else if (sqlStatement instanceof SQLDeleteStatement) { 43 | DeleteExecutor deleteExecutor = new DeleteExecutor(sqlStatement, con); 44 | deleteExecutor.execute(session); 45 | return; 46 | } else { 47 | throw new RuntimeException("not support this statement " + sqlStatement); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/SelectDatabase.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | import alchemystar.freedom.engine.net.proto.mysql.EOFPacket; 5 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 6 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 7 | import alchemystar.freedom.engine.net.proto.mysql.RowDataPacket; 8 | import alchemystar.freedom.engine.net.proto.util.Fields; 9 | import alchemystar.freedom.engine.net.proto.util.PacketUtil; 10 | import alchemystar.freedom.engine.net.proto.util.StringUtil; 11 | import io.netty.buffer.ByteBuf; 12 | import io.netty.channel.ChannelHandlerContext; 13 | 14 | /** 15 | * Select DataBase 16 | * 17 | * @Author lizhuyang 18 | */ 19 | public class SelectDatabase { 20 | private static final int FIELD_COUNT = 1; 21 | private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT); 22 | private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT]; 23 | private static final EOFPacket eof = new EOFPacket(); 24 | 25 | static { 26 | int i = 0; 27 | byte packetId = 0; 28 | header.packetId = ++packetId; 29 | fields[i] = PacketUtil.getField("DATABASE()", Fields.FIELD_TYPE_VAR_STRING); 30 | fields[i++].packetId = ++packetId; 31 | eof.packetId = ++packetId; 32 | } 33 | 34 | public static void response(FrontendConnection c) { 35 | ChannelHandlerContext ctx = c.getCtx(); 36 | ByteBuf buffer = ctx.alloc().buffer(); 37 | buffer = header.writeBuf(buffer, ctx); 38 | for (FieldPacket field : fields) { 39 | buffer = field.writeBuf(buffer, ctx); 40 | } 41 | buffer = eof.writeBuf(buffer, ctx); 42 | byte packetId = eof.packetId; 43 | RowDataPacket row = new RowDataPacket(FIELD_COUNT); 44 | row.add(StringUtil.encode("freedom", c.getCharset())); 45 | row.packetId = ++packetId; 46 | buffer = row.writeBuf(buffer, ctx); 47 | EOFPacket lastEof = new EOFPacket(); 48 | lastEof.packetId = ++packetId; 49 | buffer = lastEof.writeBuf(buffer, ctx); 50 | ctx.writeAndFlush(buffer); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/fs/FileUtils.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.fs; 2 | 3 | import java.io.EOFException; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.RandomAccessFile; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.FileChannel; 9 | 10 | /** 11 | * FileUtils 12 | */ 13 | public class FileUtils { 14 | 15 | public static boolean exists(String fileName) { 16 | return new File(fileName).exists(); 17 | } 18 | 19 | public static FileChannel open(String fileName) { 20 | try { 21 | RandomAccessFile file = new RandomAccessFile(fileName, "rw"); 22 | return file.getChannel(); 23 | } catch (Exception e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | 28 | public static void closeFile(FileChannel channel) { 29 | try { 30 | channel.close(); 31 | } catch (Exception e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | public static void readFully(FileChannel channel, ByteBuffer dst, long position) throws IOException { 37 | if (channel.position() != position) { 38 | channel.position(position); 39 | } 40 | do { 41 | int r = channel.read(dst); 42 | if (r < 0) { 43 | throw new EOFException(); 44 | } 45 | } while (dst.remaining() > 0); 46 | } 47 | 48 | public static void writeFully(FileChannel channel, ByteBuffer src, long position) throws IOException { 49 | if (channel.position() != position) { 50 | channel.position(position); 51 | } 52 | do { 53 | channel.write(src); 54 | } while (src.remaining() > 0); 55 | } 56 | 57 | public static void readFully(FileChannel channel, ByteBuffer dst) throws IOException { 58 | do { 59 | int r = channel.read(dst); 60 | if (r < 0) { 61 | throw new EOFException(); 62 | } 63 | } while (dst.remaining() > 0); 64 | } 65 | 66 | public static void append(FileChannel channel, ByteBuffer src) throws IOException { 67 | do { 68 | // 追加数据 69 | channel.write(src); 70 | } while (src.remaining() > 0); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/test/sqltest/SelectTest.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.test.sqltest; 2 | 3 | import org.junit.Test; 4 | 5 | import com.alibaba.druid.sql.ast.SQLStatement; 6 | import com.alibaba.druid.sql.parser.SQLStatementParser; 7 | 8 | import alchemystar.freedom.sql.SqlExecutor; 9 | import alchemystar.freedom.sql.parser.SelectVisitor; 10 | import alchemystar.freedom.test.BasicSelectTest; 11 | 12 | /** 13 | * @Author lizhuyang 14 | */ 15 | public class SelectTest extends BasicSelectTest { 16 | 17 | public static final String singleSql = "select id,name,comment from test where id=1"; 18 | 19 | public static final String wildCardSql = "select * from test "; 20 | 21 | public static final String joinSql = 22 | "select 'a.id='+a.id,'b.id='+b.id,'c.id='+c.id,'a.name='+a.name from test as a join test as b on a.id=b" 23 | + ".id+1 " 24 | + "join " 25 | + "test as" 26 | + " c " 27 | + "where a" 28 | + ".id>=3 and a.id < 10 and " 29 | + " b.id>=2 and b.id < 10 and c.id>=1 and c.id < 10 and a.name < 'alchemystar5'"; 30 | 31 | public static void main(String args[]){ 32 | System.out.println(joinSql); 33 | } 34 | 35 | 36 | @Test 37 | public void selectSingleExecutor() { 38 | SqlExecutor sqlExecutor = new SqlExecutor(); 39 | sqlExecutor.execute(singleSql, null, null); 40 | } 41 | 42 | @Test 43 | public void selectWildCardExecutor() { 44 | SqlExecutor sqlExecutor = new SqlExecutor(); 45 | sqlExecutor.execute(wildCardSql, null, null); 46 | } 47 | 48 | @Test 49 | public void selectJoinExecutor() { 50 | SqlExecutor sqlExecutor = new SqlExecutor(); 51 | sqlExecutor.execute(joinSql, null, null); 52 | } 53 | 54 | @Test 55 | public void selectSingle() { 56 | selectParse(singleSql); 57 | 58 | } 59 | 60 | @Test 61 | public void selectJoin() { 62 | selectParse(joinSql); 63 | } 64 | 65 | public void selectParse(String selectSQL) { 66 | SQLStatementParser parser = new SQLStatementParser(selectSQL); 67 | SQLStatement sqlStatement = parser.parseStatement(); 68 | SelectVisitor selectVisitor = new SelectVisitor(); 69 | sqlStatement.accept(selectVisitor); 70 | System.out.println(selectVisitor.getTableFilter()); 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/LongUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * LongUtil 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public final class LongUtil { 9 | 10 | private static final byte[] minValue = "-9223372036854775808".getBytes(); 11 | 12 | public static byte[] toBytes(long i) { 13 | if (i == Long.MIN_VALUE) 14 | return minValue; 15 | int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); 16 | byte[] buf = new byte[size]; 17 | getBytes(i, size, buf); 18 | return buf; 19 | } 20 | 21 | static int stringSize(long x) { 22 | long p = 10; 23 | for (int i = 1; i < 19; i++) { 24 | if (x < p) 25 | return i; 26 | p = 10 * p; 27 | } 28 | return 19; 29 | } 30 | 31 | static void getBytes(long i, int index, byte[] buf) { 32 | long q; 33 | int r; 34 | int charPos = index; 35 | byte sign = 0; 36 | 37 | if (i < 0) { 38 | sign = '-'; 39 | i = -i; 40 | } 41 | 42 | // Get 2 digits/iteration using longs until quotient fits into an int 43 | while (i > Integer.MAX_VALUE) { 44 | q = i / 100; 45 | // really: r = i - (q * 100); 46 | r = (int) (i - ((q << 6) + (q << 5) + (q << 2))); 47 | i = q; 48 | buf[--charPos] = IntegerUtil.digitOnes[r]; 49 | buf[--charPos] = IntegerUtil.digitTens[r]; 50 | } 51 | 52 | // Get 2 digits/iteration using ints 53 | int q2; 54 | int i2 = (int) i; 55 | while (i2 >= 65536) { 56 | q2 = i2 / 100; 57 | // really: r = i2 - (q * 100); 58 | r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); 59 | i2 = q2; 60 | buf[--charPos] = IntegerUtil.digitOnes[r]; 61 | buf[--charPos] = IntegerUtil.digitTens[r]; 62 | } 63 | 64 | // Fall thru to fast mode for smaller numbers 65 | // assert(i2 <= 65536, i2); 66 | for (;;) { 67 | q2 = (i2 * 52429) >>> (16 + 3); 68 | r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... 69 | buf[--charPos] = IntegerUtil.digits[r]; 70 | i2 = q2; 71 | if (i2 == 0) 72 | break; 73 | } 74 | if (sign != 0) { 75 | buf[--charPos] = sign; 76 | } 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/Attribute.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta; 2 | 3 | import alchemystar.freedom.meta.value.Value; 4 | import alchemystar.freedom.meta.value.ValueBoolean; 5 | import alchemystar.freedom.meta.value.ValueInt; 6 | import alchemystar.freedom.meta.value.ValueLong; 7 | import alchemystar.freedom.meta.value.ValueString; 8 | 9 | /** 10 | * 属性 11 | * 12 | * @Author lizhuyang 13 | */ 14 | public class Attribute { 15 | // 属性名称 16 | private String name; 17 | // 属性类型 18 | private int type; 19 | // 在TupleDesc中的位置 20 | private int index; 21 | // 注释 22 | private String comment; 23 | 24 | private boolean isPrimaryKey; 25 | 26 | public Attribute() { 27 | } 28 | 29 | public Attribute(String name, int type, int index, String comment) { 30 | this.name = name; 31 | this.type = type; 32 | this.index = index; 33 | this.comment = comment; 34 | } 35 | 36 | public Value getDefaultValue() { 37 | 38 | switch (type) { 39 | case Value.STRING: 40 | return new ValueString(""); 41 | case Value.INT: 42 | return new ValueInt(0); 43 | case Value.LONG: 44 | return new ValueLong(0); 45 | case Value.BOOLEAN: 46 | return new ValueBoolean(false); 47 | default: 48 | throw new RuntimeException("not support this type :" + type); 49 | } 50 | } 51 | 52 | public String getName() { 53 | return name; 54 | } 55 | 56 | public Attribute setName(String name) { 57 | this.name = name; 58 | return this; 59 | } 60 | 61 | public int getType() { 62 | return type; 63 | } 64 | 65 | public Attribute setType(int type) { 66 | this.type = type; 67 | return this; 68 | } 69 | 70 | public int getIndex() { 71 | return index; 72 | } 73 | 74 | public Attribute setIndex(int index) { 75 | this.index = index; 76 | return this; 77 | } 78 | 79 | public String getComment() { 80 | return comment; 81 | } 82 | 83 | public Attribute setComment(String comment) { 84 | this.comment = comment; 85 | return this; 86 | } 87 | 88 | public boolean isPrimaryKey() { 89 | return isPrimaryKey; 90 | } 91 | 92 | public void setPrimaryKey(boolean primaryKey) { 93 | isPrimaryKey = primaryKey; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/SelectVersionComment.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 4 | import alchemystar.freedom.engine.net.proto.mysql.EOFPacket; 5 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 6 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 7 | import alchemystar.freedom.engine.net.proto.mysql.RowDataPacket; 8 | import alchemystar.freedom.engine.net.proto.util.Fields; 9 | import alchemystar.freedom.engine.net.proto.util.PacketUtil; 10 | import io.netty.buffer.ByteBuf; 11 | import io.netty.channel.ChannelHandlerContext; 12 | 13 | /** 14 | * SelectVersionComment 15 | * 16 | * @Author lizhuyang 17 | */ 18 | public class SelectVersionComment { 19 | 20 | private static final byte[] VERSION_COMMENT = 21 | "Freedom Server-0.1 author:alchemystar@163.com".getBytes(); 22 | private static final int FIELD_COUNT = 1; 23 | private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT); 24 | private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT]; 25 | private static final EOFPacket eof = new EOFPacket(); 26 | 27 | static { 28 | int i = 0; 29 | byte packetId = 0; 30 | header.packetId = ++packetId; 31 | fields[i] = PacketUtil.getField("@@VERSION_COMMENT", Fields.FIELD_TYPE_VAR_STRING); 32 | fields[i++].packetId = ++packetId; 33 | eof.packetId = ++packetId; 34 | } 35 | 36 | public static void response(FrontendConnection c) { 37 | ChannelHandlerContext ctx = c.getCtx(); 38 | ByteBuf buffer = ctx.alloc().buffer(); 39 | // write header 40 | buffer = header.writeBuf(buffer, ctx); 41 | 42 | // write fields 43 | for (FieldPacket field : fields) { 44 | buffer = field.writeBuf(buffer, ctx); 45 | } 46 | 47 | // write eof 48 | buffer = eof.writeBuf(buffer, ctx); 49 | 50 | // write rows 51 | byte packetId = eof.packetId; 52 | RowDataPacket row = new RowDataPacket(FIELD_COUNT); 53 | row.add(VERSION_COMMENT); 54 | row.packetId = ++packetId; 55 | buffer = row.writeBuf(buffer, ctx); 56 | 57 | // write last eof 58 | EOFPacket lastEof = new EOFPacket(); 59 | lastEof.packetId = ++packetId; 60 | buffer = lastEof.writeBuf(buffer, ctx); 61 | 62 | // post write 63 | ctx.writeAndFlush(buffer); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/OkPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * MySql OkPacket 10 | * @Author lizhuyang 11 | */ 12 | public class OkPacket extends MySQLPacket { 13 | public static final byte FIELD_COUNT = 0x00; 14 | 15 | public static final byte[] OK = new byte[] { 7, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0 }; 16 | public static final byte[] AUTH_OK = new byte[] { 7, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0 }; 17 | public byte fieldCount = FIELD_COUNT; 18 | public long affectedRows; 19 | public long insertId; 20 | public int serverStatus; 21 | public int warningCount; 22 | public byte[] message; 23 | 24 | public void read(BinaryPacket bin) { 25 | packetLength = bin.packetLength; 26 | packetId = bin.packetId; 27 | MySQLMessage mm = new MySQLMessage(bin.data); 28 | fieldCount = mm.read(); 29 | affectedRows = mm.readLength(); 30 | insertId = mm.readLength(); 31 | serverStatus = mm.readUB2(); 32 | warningCount = mm.readUB2(); 33 | if (mm.hasRemaining()) { 34 | this.message = mm.readBytesWithLength(); 35 | } 36 | } 37 | 38 | public void write(ChannelHandlerContext ctx) { 39 | // default init 256,so it can avoid buff extract 40 | ByteBuf buffer = ctx.alloc().buffer(); 41 | BufferUtil.writeUB3(buffer, calcPacketSize()); 42 | buffer.writeByte(packetId); 43 | buffer.writeByte(fieldCount); 44 | BufferUtil.writeLength(buffer, affectedRows); 45 | BufferUtil.writeLength(buffer, insertId); 46 | BufferUtil.writeUB2(buffer, serverStatus); 47 | BufferUtil.writeUB2(buffer, warningCount); 48 | if (message != null) { 49 | BufferUtil.writeWithLength(buffer, message); 50 | } 51 | ctx.writeAndFlush(buffer); 52 | } 53 | 54 | @Override 55 | public int calcPacketSize() { 56 | int i = 1; 57 | i += BufferUtil.getLength(affectedRows); 58 | i += BufferUtil.getLength(insertId); 59 | i += 4; 60 | if (message != null) { 61 | i += BufferUtil.getLength(message); 62 | } 63 | return i; 64 | } 65 | 66 | @Override 67 | protected String getPacketInfo() { 68 | return "MySQL OK Packet"; 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/Capabilities.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * Capablities 5 | * @Author lizhuyang 6 | */ 7 | public interface Capabilities { 8 | /** 9 | * server capabilities 10 | * 11 | * 12 | * server: 11110111 11111111 13 | * client_cmd: 11 10100110 10000101 14 | * client_jdbc:10 10100010 10001111 15 | * 16 | * 17 | */ 18 | // new more secure passwords 19 | int CLIENT_LONG_PASSWORD = 1; 20 | 21 | // Found instead of affected rows 22 | // 返回找到(匹配)的行数,而不是改变了的行数。 23 | int CLIENT_FOUND_ROWS = 2; 24 | 25 | // Get all column flags 26 | int CLIENT_LONG_FLAG = 4; 27 | 28 | // One can specify db on connect 29 | int CLIENT_CONNECT_WITH_DB = 8; 30 | 31 | // Don't allow database.table.column 32 | // 不允许“数据库名.表名.列名”这样的语法。这是对于ODBC的设置。 33 | // 当使用这样的语法时解析器会产生一个错误,这对于一些ODBC的程序限制bug来说是有用的。 34 | int CLIENT_NO_SCHEMA = 16; 35 | 36 | // Can use compression protocol 37 | // 使用压缩协议 38 | int CLIENT_COMPRESS = 32; 39 | 40 | // Odbc client 41 | int CLIENT_ODBC = 64; 42 | 43 | // Can use LOAD DATA LOCAL 44 | int CLIENT_LOCAL_FILES = 128; 45 | 46 | // Ignore spaces before '(' 47 | // 允许在函数名后使用空格。所有函数名可以预留字。 48 | int CLIENT_IGNORE_SPACE = 256; 49 | 50 | // New 4.1 protocol This is an interactive client 51 | int CLIENT_PROTOCOL_41 = 512; 52 | 53 | // This is an interactive client 54 | // 允许使用关闭连接之前的不活动交互超时的描述,而不是等待超时秒数。 55 | // 客户端的会话等待超时变量变为交互超时变量。 56 | int CLIENT_INTERACTIVE = 1024; 57 | 58 | // Switch to SSL after handshake 59 | // 使用SSL。这个设置不应该被应用程序设置,他应该是在客户端库内部是设置的。 60 | // 可以在调用mysql_real_connect()之前调用mysql_ssl_set()来代替设置。 61 | int CLIENT_SSL = 2048; 62 | 63 | // IGNORE sigpipes 64 | // 阻止客户端库安装一个SIGPIPE信号处理器。 65 | // 这个可以用于当应用程序已经安装该处理器的时候避免与其发生冲突。 66 | int CLIENT_IGNORE_SIGPIPE = 4096; 67 | 68 | // Client knows about transactions 69 | int CLIENT_TRANSACTIONS = 8192; 70 | 71 | // Old flag for 4.1 protocol 72 | int CLIENT_RESERVED = 16384; 73 | 74 | // New 4.1 authentication 75 | int CLIENT_SECURE_CONNECTION = 32768; 76 | 77 | // Enable/disable multi-stmt support 78 | // 通知服务器客户端可以发送多条语句(由分号分隔)。如果该标志为没有被设置,多条语句执行。 79 | int CLIENT_MULTI_STATEMENTS = 65536; 80 | 81 | // Enable/disable multi-results 82 | // 通知服务器客户端可以处理由多语句或者存储过程执行生成的多结果集。 83 | // 当打开CLIENT_MULTI_STATEMENTS时,这个标志自动的被打开。 84 | int CLIENT_MULTI_RESULTS = 131072; 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/PacketUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | import alchemystar.freedom.engine.net.proto.mysql.BinaryPacket; 6 | import alchemystar.freedom.engine.net.proto.mysql.ErrorPacket; 7 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 8 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 9 | 10 | /** 11 | * PacketUtil 12 | * 13 | * @Author lizhuyang 14 | */ 15 | public class PacketUtil { 16 | private static final String CODE_PAGE_1252 = "Cp1252"; 17 | 18 | public static final ResultSetHeaderPacket getHeader(int fieldCount) { 19 | ResultSetHeaderPacket packet = new ResultSetHeaderPacket(); 20 | packet.packetId = 1; 21 | packet.fieldCount = fieldCount; 22 | return packet; 23 | } 24 | 25 | public static byte[] encode(String src, String charset) { 26 | if (src == null) { 27 | return null; 28 | } 29 | try { 30 | return src.getBytes(charset); 31 | } catch (UnsupportedEncodingException e) { 32 | return src.getBytes(); 33 | } 34 | } 35 | 36 | public static final FieldPacket getField(String name, String orgName, int type) { 37 | FieldPacket packet = new FieldPacket(); 38 | packet.charsetIndex = CharsetUtil.getIndex(CODE_PAGE_1252); 39 | packet.name = encode(name, CODE_PAGE_1252); 40 | packet.orgName = encode(orgName, CODE_PAGE_1252); 41 | packet.type = (byte) type; 42 | return packet; 43 | } 44 | 45 | public static final FieldPacket getField(String name, int type) { 46 | FieldPacket packet = new FieldPacket(); 47 | packet.charsetIndex = CharsetUtil.getIndex(CODE_PAGE_1252); 48 | packet.name = encode(name, CODE_PAGE_1252); 49 | packet.type = (byte) type; 50 | return packet; 51 | } 52 | 53 | public static final ErrorPacket getShutdown() { 54 | ErrorPacket error = new ErrorPacket(); 55 | error.packetId = 1; 56 | error.errno = ErrorCode.ER_SERVER_SHUTDOWN; 57 | error.message = "The server has been shutdown".getBytes(); 58 | return error; 59 | } 60 | 61 | public static final FieldPacket getField(BinaryPacket src, String fieldName) { 62 | FieldPacket field = new FieldPacket(); 63 | field.read(src); 64 | field.name = encode(fieldName, CODE_PAGE_1252); 65 | field.packetLength = field.calcPacketSize(); 66 | return field; 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/value/ValueInt.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta.value; 2 | 3 | import alchemystar.freedom.util.BufferWrapper; 4 | 5 | /** 6 | * @Author lizhuyang 7 | */ 8 | public class ValueInt extends Value { 9 | 10 | private int i; 11 | 12 | public ValueInt() { 13 | } 14 | 15 | public ValueInt(int i) { 16 | this.i = i; 17 | } 18 | 19 | @Override 20 | public int getLength() { 21 | // 1 for tupe 22 | return 1 + 4; 23 | } 24 | 25 | @Override 26 | public byte getType() { 27 | return INT; 28 | } 29 | 30 | @Override 31 | public byte[] getBytes() { 32 | BufferWrapper wrapper = new BufferWrapper(getLength()); 33 | // int type 34 | wrapper.writeByte(INT); 35 | // for the value string 36 | wrapper.writeInt(i); 37 | return wrapper.getBuffer(); 38 | } 39 | 40 | @Override 41 | public void read(byte[] bytes) { 42 | BufferWrapper wrapper = new BufferWrapper(bytes); 43 | i = wrapper.readInt(); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return String.valueOf(i); 49 | } 50 | 51 | public int getInt() { 52 | return i; 53 | } 54 | 55 | public ValueInt setInt(int i) { 56 | this.i = i; 57 | return this; 58 | } 59 | 60 | @Override 61 | public int compare(Value value) { 62 | int toCompare; 63 | if (value instanceof ValueLong) { 64 | toCompare = ((ValueLong) value).getInt(); 65 | } else { 66 | toCompare = (((ValueInt) value).getInt()); 67 | } 68 | if (i > toCompare) { 69 | return 1; 70 | } 71 | if (i == toCompare) { 72 | return 0; 73 | } 74 | return -1; 75 | } 76 | 77 | @Override 78 | public String getString() { 79 | return String.valueOf(i); 80 | } 81 | 82 | @Override 83 | public Value add(Value v) { 84 | return new ValueInt(i + v.getInt()); 85 | } 86 | 87 | public Value subtract(Value v) { 88 | return new ValueInt(i - v.getInt()); 89 | } 90 | 91 | public Value divide(Value v) { 92 | return new ValueInt(i / v.getInt()); 93 | } 94 | 95 | public Value multiply(Value v) { 96 | return new ValueInt(i * v.getInt()); 97 | } 98 | 99 | public Value concat(Value v) { 100 | return new ValueString(this.toString() + v.toString()); 101 | } 102 | 103 | public long getLong() { 104 | return (long) i; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/value/ValueLong.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta.value; 2 | 3 | import alchemystar.freedom.util.BufferWrapper; 4 | 5 | /** 6 | * @Author lizhuyang 7 | */ 8 | public class ValueLong extends Value { 9 | 10 | private long i; 11 | 12 | public ValueLong() { 13 | } 14 | 15 | public ValueLong(long i) { 16 | this.i = i; 17 | } 18 | 19 | @Override 20 | public int getLength() { 21 | // 1 for tupe 22 | return 1 + 8; 23 | } 24 | 25 | @Override 26 | public byte getType() { 27 | return LONG; 28 | } 29 | 30 | @Override 31 | public byte[] getBytes() { 32 | BufferWrapper wrapper = new BufferWrapper(getLength()); 33 | // int type 34 | wrapper.writeByte(LONG); 35 | // for the value string 36 | wrapper.writeLong(i); 37 | return wrapper.getBuffer(); 38 | } 39 | 40 | @Override 41 | public void read(byte[] bytes) { 42 | BufferWrapper wrapper = new BufferWrapper(bytes); 43 | i = wrapper.readLong(); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return String.valueOf(i); 49 | } 50 | 51 | public long getLong() { 52 | return i; 53 | } 54 | 55 | @Override 56 | public String getString() { 57 | return String.valueOf(i); 58 | } 59 | 60 | 61 | public int getInt() { 62 | return (int)i; 63 | } 64 | 65 | public ValueLong setLong(long i) { 66 | this.i = i; 67 | return this; 68 | } 69 | 70 | @Override 71 | public int compare(Value value) { 72 | long toCompare; 73 | if(value instanceof ValueInt){ 74 | toCompare = ((ValueInt)value).getInt(); 75 | }else { 76 | toCompare = (((ValueLong) value).getLong()); 77 | } 78 | if (i > toCompare) { 79 | return 1; 80 | } 81 | if (i == toCompare) { 82 | return 0; 83 | } 84 | return -1; 85 | } 86 | 87 | @Override 88 | public Value add(Value v) { 89 | return new ValueLong(i + v.getLong()); 90 | } 91 | 92 | public Value subtract(Value v) { 93 | return new ValueLong(i - v.getLong()); 94 | } 95 | 96 | public Value divide(Value v) { 97 | return new ValueLong(i / v.getLong()); 98 | } 99 | 100 | public Value multiply(Value v) { 101 | return new ValueLong(i * v.getLong()); 102 | } 103 | 104 | public Value concat(Value v) { 105 | return new ValueString(this.toString() + v.toString()); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/TableManager.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.alibaba.druid.sql.ast.SQLExpr; 7 | import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; 8 | 9 | import alchemystar.freedom.engine.Database; 10 | import alchemystar.freedom.sql.parser.SelectVisitor; 11 | import alchemystar.freedom.sql.select.TableFilter; 12 | 13 | /** 14 | * @Author lizhuyang 15 | */ 16 | public class TableManager { 17 | 18 | public static Map tableMap = new HashMap(); 19 | 20 | public static TableFilter newTableFilter(SQLExprTableSource sqlExprTableSource, SelectVisitor selectVisitor) { 21 | TableFilter tableFilter = new TableFilter(); 22 | String tableName = sqlExprTableSource.getExpr().toString(); 23 | Table table = tableMap.get(tableName); 24 | tableFilter.setTable(table); 25 | tableFilter.setSelectVisitor(selectVisitor); 26 | tableFilter.setAlias(sqlExprTableSource.getAlias()); 27 | tableFilter.setFilterCondition(selectVisitor.getWhereCondition()); 28 | return tableFilter; 29 | } 30 | 31 | public static TableFilter newTableFilter(SQLExprTableSource sqlExprTableSource, SQLExpr whereExpr) { 32 | TableFilter tableFilter = new TableFilter(); 33 | String tableName = sqlExprTableSource.getExpr().toString(); 34 | Table table = tableMap.get(tableName); 35 | tableFilter.setTable(table); 36 | tableFilter.setAlias(sqlExprTableSource.getAlias()); 37 | tableFilter.setFilterCondition(whereExpr); 38 | return tableFilter; 39 | } 40 | 41 | public static Table getTable(String tableName) { 42 | Table table = tableMap.get(tableName); 43 | if (table == null) { 44 | throw new RuntimeException("not found this table , tableName = " + tableName); 45 | } 46 | return table; 47 | } 48 | 49 | public static Table getTableWithNoException(String tableName) { 50 | return tableMap.get(tableName); 51 | } 52 | 53 | public static void addTable(Table table, boolean isPersist) { 54 | if (tableMap.get(table.getName()) != null) { 55 | throw new RuntimeException("table " + table.getName() + " already exists"); 56 | } 57 | if (isPersist) { 58 | // 先落盘,再写入 59 | Database.getInstance().getTableLoader().writeTableMeta(table); 60 | } 61 | tableMap.put(table.getName(), table); 62 | } 63 | 64 | public static Map getTableMap() { 65 | return tableMap; 66 | } 67 | 68 | public static void setTableMap(Map tableMap) { 69 | TableManager.tableMap = tableMap; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/sql/DeleteExecutor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.sql; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.alibaba.druid.sql.ast.SQLStatement; 7 | 8 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 9 | import alchemystar.freedom.engine.net.response.OkResponse; 10 | import alchemystar.freedom.engine.session.Session; 11 | import alchemystar.freedom.meta.IndexEntry; 12 | import alchemystar.freedom.meta.Table; 13 | import alchemystar.freedom.meta.value.Value; 14 | import alchemystar.freedom.meta.value.ValueBoolean; 15 | import alchemystar.freedom.sql.parser.DeleteVisitor; 16 | import alchemystar.freedom.sql.select.TableFilter; 17 | import alchemystar.freedom.sql.select.item.SelectExprEval; 18 | import alchemystar.freedom.transaction.OpType; 19 | 20 | /** 21 | * @Author lizhuyang 22 | */ 23 | public class DeleteExecutor { 24 | 25 | private SQLStatement sqlStatement; 26 | 27 | private DeleteVisitor deleteVisitor; 28 | 29 | private Table table; 30 | 31 | private FrontendConnection con; 32 | 33 | public DeleteExecutor(SQLStatement sqlStatement, FrontendConnection con) { 34 | this.sqlStatement = sqlStatement; 35 | this.con = con; 36 | } 37 | 38 | public void execute(Session session) { 39 | init(); 40 | TableFilter tableFilter = deleteVisitor.getTableFilter(); 41 | List toDelete = new ArrayList(); 42 | // 必须先拿出来再删除,不然会引起删除的position变化 43 | while (tableFilter.next()) { 44 | if (checkWhereCondition()) { 45 | toDelete.add(tableFilter.getCurrent()); 46 | } 47 | } 48 | for (IndexEntry delItem : toDelete) { 49 | if (session != null) { 50 | session.addLog(table, OpType.delete, delItem, null); 51 | } 52 | // 先落盘,再对table做操作 53 | table.delete(delItem); 54 | } 55 | OkResponse.responseWithAffectedRows(con, toDelete.size()); 56 | } 57 | 58 | private boolean checkWhereCondition() { 59 | SelectExprEval eval = new SelectExprEval(deleteVisitor.getWhere(), null); 60 | eval.setSimpleTableFilter(deleteVisitor.getTableFilter()); 61 | Value value = eval.eval(); 62 | if (value instanceof ValueBoolean) { 63 | return ((ValueBoolean) value).getBoolean(); 64 | } 65 | throw new RuntimeException("where condition eval not boolean , wrong"); 66 | } 67 | 68 | public void init() { 69 | DeleteVisitor deleteVisitor = new DeleteVisitor(); 70 | sqlStatement.accept(deleteVisitor); 71 | this.deleteVisitor = deleteVisitor; 72 | this.table = deleteVisitor.getTableFilter().getTable(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/ErrorPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * Error Packet 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class ErrorPacket extends MySQLPacket { 14 | public static final byte FIELD_COUNT = (byte) 0xff; 15 | private static final byte SQLSTATE_MARKER = (byte) '#'; 16 | private static final byte[] DEFAULT_SQLSTATE = "HY000".getBytes(); 17 | 18 | public byte fieldCount = FIELD_COUNT; 19 | public int errno; 20 | public byte mark = SQLSTATE_MARKER; 21 | public byte[] sqlState = DEFAULT_SQLSTATE; 22 | public byte[] message; 23 | 24 | public void read(BinaryPacket bin) { 25 | packetLength = bin.packetLength; 26 | packetId = bin.packetId; 27 | MySQLMessage mm = new MySQLMessage(bin.data); 28 | fieldCount = mm.read(); 29 | errno = mm.readUB2(); 30 | if (mm.hasRemaining() && (mm.read(mm.position()) == SQLSTATE_MARKER)) { 31 | mm.read(); 32 | sqlState = mm.readBytes(5); 33 | } 34 | message = mm.readBytes(); 35 | } 36 | 37 | public void read(byte[] data) { 38 | MySQLMessage mm = new MySQLMessage(data); 39 | packetLength = mm.readUB3(); 40 | packetId = mm.read(); 41 | fieldCount = mm.read(); 42 | errno = mm.readUB2(); 43 | if (mm.hasRemaining() && (mm.read(mm.position()) == SQLSTATE_MARKER)) { 44 | mm.read(); 45 | sqlState = mm.readBytes(5); 46 | } 47 | message = mm.readBytes(); 48 | } 49 | 50 | @Override 51 | public void write(ChannelHandlerContext ctx) { 52 | int size = calcPacketSize(); 53 | // default 256 , no need to check and auto expand 54 | ByteBuf buffer = ctx.alloc().buffer(); 55 | BufferUtil.writeUB3(buffer, size); 56 | buffer.writeByte(packetId); 57 | buffer.writeByte(fieldCount); 58 | BufferUtil.writeUB2(buffer, errno); 59 | buffer.writeByte(mark); 60 | buffer.writeBytes(sqlState); 61 | if (message != null) { 62 | buffer.writeBytes(message); 63 | } 64 | ctx.writeAndFlush(buffer); 65 | } 66 | 67 | @Override 68 | public int calcPacketSize() { 69 | int size = 9;// 1 + 2 + 1 + 5 70 | if (message != null) { 71 | size += message.length; 72 | } 73 | return size; 74 | } 75 | 76 | @Override 77 | protected String getPacketInfo() { 78 | return "MySQL Error Packet"; 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/SecurityUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | /** 7 | * 加解密 校验密码相关 8 | * @Author lizhuyang 9 | */ 10 | public class SecurityUtil { 11 | 12 | public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException { 13 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 14 | byte[] pass1 = md.digest(pass); 15 | md.reset(); 16 | byte[] pass2 = md.digest(pass1); 17 | md.reset(); 18 | md.update(seed); 19 | byte[] pass3 = md.digest(pass2); 20 | for (int i = 0; i < pass3.length; i++) { 21 | pass3[i] = (byte) (pass3[i] ^ pass1[i]); 22 | } 23 | return pass3; 24 | } 25 | 26 | public static final String scramble323(String pass, String seed) { 27 | if ((pass == null) || (pass.length() == 0)) { 28 | return pass; 29 | } 30 | byte b; 31 | double d; 32 | long[] pw = hash(seed); 33 | long[] msg = hash(pass); 34 | long max = 0x3fffffffL; 35 | long seed1 = (pw[0] ^ msg[0]) % max; 36 | long seed2 = (pw[1] ^ msg[1]) % max; 37 | char[] chars = new char[seed.length()]; 38 | for (int i = 0; i < seed.length(); i++) { 39 | seed1 = ((seed1 * 3) + seed2) % max; 40 | seed2 = (seed1 + seed2 + 33) % max; 41 | d = (double) seed1 / (double) max; 42 | b = (byte) Math.floor((d * 31) + 64); 43 | chars[i] = (char) b; 44 | } 45 | seed1 = ((seed1 * 3) + seed2) % max; 46 | seed2 = (seed1 + seed2 + 33) % max; 47 | d = (double) seed1 / (double) max; 48 | b = (byte) Math.floor(d * 31); 49 | for (int i = 0; i < seed.length(); i++) { 50 | chars[i] ^= (char) b; 51 | } 52 | return new String(chars); 53 | } 54 | 55 | private static long[] hash(String src) { 56 | long nr = 1345345333L; 57 | long add = 7; 58 | long nr2 = 0x12345671L; 59 | long tmp; 60 | for (int i = 0; i < src.length(); ++i) { 61 | switch (src.charAt(i)) { 62 | case ' ': 63 | case '\t': 64 | continue; 65 | default: 66 | tmp = (0xff & src.charAt(i)); 67 | nr ^= ((((nr & 63) + add) * tmp) + (nr << 8)); 68 | nr2 += ((nr2 << 8) ^ nr); 69 | add += tmp; 70 | } 71 | } 72 | long[] result = new long[2]; 73 | result[0] = nr & 0x7fffffffL; 74 | result[1] = nr2 & 0x7fffffffL; 75 | return result; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/HandshakePacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * MySql握手包 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class HandshakePacket extends MySQLPacket { 14 | 15 | private static final byte[] FILLER_13 = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 16 | 17 | public byte protocolVersion; 18 | public byte[] serverVersion; 19 | public long threadId; 20 | public byte[] seed; 21 | public int serverCapabilities; 22 | public byte serverCharsetIndex; 23 | public int serverStatus; 24 | public byte[] restOfScrambleBuff; 25 | 26 | public void read(BinaryPacket bin) { 27 | packetLength = bin.packetLength; 28 | packetId = bin.packetId; 29 | MySQLMessage mm = new MySQLMessage(bin.data); 30 | protocolVersion = mm.read(); 31 | serverVersion = mm.readBytesWithNull(); 32 | threadId = mm.readUB4(); 33 | seed = mm.readBytesWithNull(); 34 | serverCapabilities = mm.readUB2(); 35 | serverCharsetIndex = mm.read(); 36 | serverStatus = mm.readUB2(); 37 | mm.move(13); 38 | restOfScrambleBuff = mm.readBytesWithNull(); 39 | } 40 | 41 | public void write(final ChannelHandlerContext ctx) { 42 | // default init 256,so it can avoid buff extract 43 | final ByteBuf buffer = ctx.alloc().buffer(); 44 | BufferUtil.writeUB3(buffer, calcPacketSize()); 45 | buffer.writeByte(packetId); 46 | buffer.writeByte(protocolVersion); 47 | BufferUtil.writeWithNull(buffer, serverVersion); 48 | BufferUtil.writeUB4(buffer, threadId); 49 | BufferUtil.writeWithNull(buffer, seed); 50 | BufferUtil.writeUB2(buffer, serverCapabilities); 51 | buffer.writeByte(serverCharsetIndex); 52 | BufferUtil.writeUB2(buffer, serverStatus); 53 | buffer.writeBytes(FILLER_13); 54 | // buffer.position(buffer.position() + 13); 55 | BufferUtil.writeWithNull(buffer, restOfScrambleBuff); 56 | // just io , so we don't use thread pool 57 | ctx.writeAndFlush(buffer); 58 | 59 | } 60 | 61 | @Override 62 | public int calcPacketSize() { 63 | int size = 1; 64 | size += serverVersion.length;// n 65 | size += 5;// 1+4 66 | size += seed.length;// 8 67 | size += 19;// 1+2+1+2+13 68 | size += restOfScrambleBuff.length;// 12 69 | size += 1;// 1 70 | return size; 71 | } 72 | 73 | @Override 74 | protected String getPacketInfo() { 75 | return "MySQL Handshake Packet"; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/ByteUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | import alchemystar.freedom.engine.net.proto.mysql.MySQLMessage; 4 | import io.netty.buffer.ByteBuf; 5 | 6 | /** 7 | * @author lizhuyang 8 | */ 9 | public class ByteUtil { 10 | public static int readUB2(ByteBuf data) { 11 | int i = data.readByte() & 0xff; 12 | i |= (data.readByte() & 0xff) << 8; 13 | return i; 14 | } 15 | 16 | public static int readUB3(ByteBuf data) { 17 | int i = data.readByte() & 0xff; 18 | i |= (data.readByte() & 0xff) << 8; 19 | i |= (data.readByte() & 0xff) << 16; 20 | return i; 21 | } 22 | 23 | public static long readUB4(ByteBuf data) { 24 | long l = data.readByte() & 0xff; 25 | l |= (data.readByte() & 0xff) << 8; 26 | l |= (data.readByte() & 0xff) << 16; 27 | l |= (data.readByte() & 0xff) << 24; 28 | return l; 29 | } 30 | 31 | public static long readLong(ByteBuf data) { 32 | long l = (long) (data.readByte() & 0xff); 33 | l |= (long) (data.readByte() & 0xff) << 8; 34 | l |= (long) (data.readByte() & 0xff) << 16; 35 | l |= (long) (data.readByte() & 0xff) << 24; 36 | l |= (long) (data.readByte() & 0xff) << 32; 37 | l |= (long) (data.readByte() & 0xff) << 40; 38 | l |= (long) (data.readByte() & 0xff) << 48; 39 | l |= (long) (data.readByte() & 0xff) << 56; 40 | return l; 41 | } 42 | 43 | /** 44 | * this is for the String 45 | * @param data 46 | * @return 47 | */ 48 | public static long readLength(ByteBuf data) { 49 | int length = data.readByte() & 0xff; 50 | switch (length) { 51 | case 251: 52 | return MySQLMessage.NULL_LENGTH; 53 | case 252: 54 | return readUB2(data); 55 | case 253: 56 | return readUB3(data); 57 | case 254: 58 | return readLong(data); 59 | default: 60 | return length; 61 | } 62 | } 63 | 64 | 65 | public static int decodeLength(byte[] src) { 66 | int length = src.length; 67 | if (length < 251) { 68 | return 1 + length; 69 | } else if (length < 0x10000L) { 70 | return 3 + length; 71 | } else if (length < 0x1000000L) { 72 | return 4 + length; 73 | } else { 74 | return 9 + length; 75 | } 76 | } 77 | 78 | public static int decodeLength(long length) { 79 | if (length < 251) { 80 | return 1; 81 | } else if (length < 0x10000L) { 82 | return 3; 83 | } else if (length < 0x1000000L) { 84 | return 4; 85 | } else { 86 | return 9; 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/server/FreedomServer.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.server; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import alchemystar.freedom.config.SocketConfig; 7 | import alchemystar.freedom.engine.Database; 8 | import alchemystar.freedom.engine.net.handler.factory.FrontHandlerFactory; 9 | import io.netty.bootstrap.ServerBootstrap; 10 | import io.netty.buffer.PooledByteBufAllocator; 11 | import io.netty.channel.ChannelFuture; 12 | import io.netty.channel.ChannelOption; 13 | import io.netty.channel.EventLoopGroup; 14 | import io.netty.channel.nio.NioEventLoopGroup; 15 | import io.netty.channel.socket.nio.NioServerSocketChannel; 16 | 17 | /** 18 | * 无毁的湖光 启动器 19 | * 20 | * @Author lizhuyang 21 | */ 22 | public class FreedomServer extends Thread { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(FreedomServer.class); 25 | public static final int BOSS_THREAD_COUNT = 1; 26 | 27 | public static void main(String[] args) { 28 | FreedomServer server = new FreedomServer(); 29 | try { 30 | server.start(); 31 | while (true) { 32 | try { 33 | Thread.sleep(1000 * 300); 34 | }catch (Exception e){ 35 | // just ignore it 36 | } 37 | } 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | @Override 44 | public void run() { 45 | logger.info("Start Freedom"); 46 | startServer(); 47 | } 48 | 49 | public void startServer() { 50 | // acceptor , one port => one thread 51 | EventLoopGroup bossGroup = new NioEventLoopGroup(BOSS_THREAD_COUNT); 52 | // worker 53 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 54 | 55 | try { 56 | // Freedom Server 57 | Database database = Database.getInstance(); 58 | ServerBootstrap b = new ServerBootstrap(); 59 | // 这边的childHandler是用来管理accept的 60 | // 由于线程间传递的是byte[],所以内存池okay 61 | // 只需要保证分配ByteBuf和write在同一个线程(函数)就行了 62 | b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) 63 | .option(ChannelOption.SO_BACKLOG, 1024) 64 | .childHandler(new FrontHandlerFactory()).option(ChannelOption.ALLOCATOR, 65 | PooledByteBufAllocator.DEFAULT) 66 | .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, SocketConfig.CONNECT_TIMEOUT_MILLIS) 67 | .option(ChannelOption.SO_TIMEOUT, SocketConfig.SO_TIMEOUT); 68 | ChannelFuture f = b.bind(database.getServerPort()).sync(); 69 | f.channel().closeFuture().sync(); 70 | 71 | } catch (InterruptedException e) { 72 | logger.error("监听失败" + e); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/ShowTables.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 7 | import alchemystar.freedom.engine.net.proto.mysql.EOFPacket; 8 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 9 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 10 | import alchemystar.freedom.engine.net.proto.mysql.RowDataPacket; 11 | import alchemystar.freedom.engine.net.proto.util.Fields; 12 | import alchemystar.freedom.engine.net.proto.util.PacketUtil; 13 | import alchemystar.freedom.engine.net.proto.util.StringUtil; 14 | import alchemystar.freedom.meta.TableManager; 15 | import io.netty.buffer.ByteBuf; 16 | import io.netty.channel.ChannelHandlerContext; 17 | 18 | /** 19 | * ShowDatabases 20 | * 21 | * @Author lizhuyang 22 | */ 23 | public final class ShowTables { 24 | 25 | private static final int FIELD_COUNT = 1; 26 | private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT); 27 | private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT]; 28 | private static final EOFPacket eof = new EOFPacket(); 29 | 30 | static { 31 | int i = 0; 32 | byte packetId = 0; 33 | header.packetId = ++packetId; 34 | 35 | fields[i] = PacketUtil.getField("TABLES", Fields.FIELD_TYPE_VAR_STRING); 36 | fields[i++].packetId = ++packetId; 37 | 38 | eof.packetId = ++packetId; 39 | } 40 | 41 | public static void response(FrontendConnection c) { 42 | ChannelHandlerContext ctx = c.getCtx(); 43 | ByteBuf buffer = ctx.alloc().buffer(); 44 | 45 | // write header 46 | buffer = header.writeBuf(buffer, ctx); 47 | 48 | // write fields 49 | for (FieldPacket field : fields) { 50 | buffer = field.writeBuf(buffer, ctx); 51 | } 52 | 53 | // write eof 54 | buffer = eof.writeBuf(buffer, ctx); 55 | 56 | // write rows 57 | byte packetId = eof.packetId; 58 | 59 | for (String name : getTables()) { 60 | RowDataPacket row = new RowDataPacket(FIELD_COUNT); 61 | row.add(StringUtil.encode(name, c.getCharset())); 62 | row.packetId = ++packetId; 63 | buffer = row.writeBuf(buffer, ctx); 64 | } 65 | 66 | // write lastEof 67 | EOFPacket lastEof = new EOFPacket(); 68 | lastEof.packetId = ++packetId; 69 | buffer = lastEof.writeBuf(buffer, ctx); 70 | 71 | // write buffer 72 | ctx.writeAndFlush(buffer); 73 | } 74 | 75 | private static List getTables() { 76 | ArrayList list = new ArrayList(); 77 | for (String tableName : TableManager.tableMap.keySet()) { 78 | list.add(tableName); 79 | } 80 | return list; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/response/ShowDatabases.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.response; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import alchemystar.freedom.engine.Database; 7 | import alchemystar.freedom.engine.net.handler.frontend.FrontendConnection; 8 | import alchemystar.freedom.engine.net.proto.mysql.EOFPacket; 9 | import alchemystar.freedom.engine.net.proto.mysql.FieldPacket; 10 | import alchemystar.freedom.engine.net.proto.mysql.ResultSetHeaderPacket; 11 | import alchemystar.freedom.engine.net.proto.mysql.RowDataPacket; 12 | import alchemystar.freedom.engine.net.proto.util.Fields; 13 | import alchemystar.freedom.engine.net.proto.util.PacketUtil; 14 | import alchemystar.freedom.engine.net.proto.util.StringUtil; 15 | import io.netty.buffer.ByteBuf; 16 | import io.netty.channel.ChannelHandlerContext; 17 | 18 | /** 19 | * ShowDatabases 20 | * 21 | * @Author lizhuyang 22 | */ 23 | public final class ShowDatabases { 24 | 25 | private static final int FIELD_COUNT = 1; 26 | private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT); 27 | private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT]; 28 | private static final EOFPacket eof = new EOFPacket(); 29 | 30 | static { 31 | int i = 0; 32 | byte packetId = 0; 33 | header.packetId = ++packetId; 34 | 35 | fields[i] = PacketUtil.getField("DATABASE", Fields.FIELD_TYPE_VAR_STRING); 36 | fields[i++].packetId = ++packetId; 37 | 38 | eof.packetId = ++packetId; 39 | } 40 | 41 | public static void response(FrontendConnection c) { 42 | ChannelHandlerContext ctx = c.getCtx(); 43 | ByteBuf buffer = ctx.alloc().buffer(); 44 | 45 | // write header 46 | buffer = header.writeBuf(buffer, ctx); 47 | 48 | // write fields 49 | for (FieldPacket field : fields) { 50 | buffer = field.writeBuf(buffer, ctx); 51 | } 52 | 53 | // write eof 54 | buffer = eof.writeBuf(buffer, ctx); 55 | 56 | // write rows 57 | byte packetId = eof.packetId; 58 | 59 | for (String name : getSchemas()) { 60 | RowDataPacket row = new RowDataPacket(FIELD_COUNT); 61 | row.add(StringUtil.encode(name, c.getCharset())); 62 | row.packetId = ++packetId; 63 | buffer = row.writeBuf(buffer, ctx); 64 | } 65 | 66 | // write lastEof 67 | EOFPacket lastEof = new EOFPacket(); 68 | lastEof.packetId = ++packetId; 69 | buffer = lastEof.writeBuf(buffer, ctx); 70 | 71 | // write buffer 72 | ctx.writeAndFlush(buffer); 73 | } 74 | 75 | private static List getSchemas() { 76 | Database database = Database.getInstance(); 77 | ArrayList list = new ArrayList(); 78 | // 当前没有schema概念 79 | list.add("freedom"); 80 | return list; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/value/ValueString.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta.value; 2 | 3 | import alchemystar.freedom.util.BufferWrapper; 4 | 5 | /** 6 | * ValueString 7 | * 8 | * @Author lizhuyang 9 | */ 10 | public class ValueString extends Value { 11 | 12 | private String s; 13 | 14 | public ValueString() { 15 | } 16 | 17 | public ValueString(String s) { 18 | this.s = s; 19 | } 20 | 21 | // [type][length][data] 22 | @Override 23 | public int getLength() { 24 | return 1 + 4 + s.length(); 25 | } 26 | 27 | @Override 28 | public byte getType() { 29 | return STRING; 30 | } 31 | 32 | @Override 33 | public byte[] getBytes() { 34 | BufferWrapper wrapper = new BufferWrapper(getLength()); 35 | wrapper.writeByte(getType()); 36 | // 此处写入的是string的长度 37 | wrapper.writeStringLength(s); 38 | return wrapper.getBuffer(); 39 | } 40 | 41 | public void read(byte[] bytes) { 42 | s = new String(bytes); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return s; 48 | } 49 | 50 | public String getString() { 51 | return s; 52 | } 53 | 54 | public ValueString setString(String s) { 55 | this.s = s; 56 | return this; 57 | } 58 | 59 | @Override 60 | public int compare(Value value) { 61 | return s.compareTo(((ValueString) value).getString()); 62 | } 63 | 64 | @Override 65 | public Value add(Value v) { 66 | if (v instanceof ValueString) { 67 | return new ValueString(s + ((ValueString) v).getString()); 68 | } else if (v instanceof ValueInt) { 69 | return new ValueString(s + String.valueOf(((ValueInt) v).getInt())); 70 | } else if (v instanceof ValueLong) { 71 | return new ValueString(s + String.valueOf(((ValueLong) v).getLong())); 72 | } else if (v instanceof ValueBoolean) { 73 | return new ValueString(s + String.valueOf(((ValueBoolean) v).getBoolean())); 74 | } 75 | throw new RuntimeException("not support this type , valueType=" + v.getType()); 76 | } 77 | 78 | @Override 79 | public Value concat(Value v) { 80 | if (v instanceof ValueString) { 81 | return new ValueString(s + ((ValueString) v).getString()); 82 | } else if (v instanceof ValueInt) { 83 | return new ValueString(s + String.valueOf(((ValueInt) v).getInt())); 84 | } else if (v instanceof ValueLong) { 85 | return new ValueString(s + String.valueOf(((ValueLong) v).getLong())); 86 | } else if (v instanceof ValueBoolean) { 87 | return new ValueString(s + String.valueOf(((ValueBoolean) v).getBoolean())); 88 | } 89 | throw new RuntimeException("not support this type , valueType=" + v.getType()); 90 | } 91 | 92 | public static void main(String args[]) { 93 | System.out.println("alchemystar1".compareTo("alchemystar10")); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/access/BaseIndexCursor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.access; 2 | 3 | import alchemystar.freedom.index.bp.BPNode; 4 | import alchemystar.freedom.index.bp.Position; 5 | import alchemystar.freedom.meta.IndexEntry; 6 | 7 | /** 8 | * @Author lizhuyang 9 | */ 10 | public class BaseIndexCursor implements Cursor{ 11 | 12 | // 开始位置 13 | private Position startPos; 14 | // 结束位置 15 | private Position endPos; 16 | // 当前位置 17 | private Position currentPos; 18 | 19 | private boolean isEqual; 20 | 21 | public BaseIndexCursor(Position startPos, Position endPos, boolean isEqual) { 22 | this.startPos = startPos; 23 | this.endPos = endPos; 24 | this.isEqual = isEqual; 25 | resetCurrentPos(startPos); 26 | } 27 | 28 | // 用开始的pos初始化startPos 29 | private void resetCurrentPos(Position startPos) { 30 | currentPos = new Position(startPos.getBpNode(), startPos.getPosition()); 31 | } 32 | 33 | @Override 34 | public IndexEntry next() { 35 | IndexEntry resultEntry = innerNext(); 36 | if (isEqual) { 37 | if (startPos.getSearchEntry().compareIndex(resultEntry) != 0) { 38 | return null; 39 | } 40 | } 41 | return resultEntry; 42 | } 43 | 44 | public IndexEntry innerNext() { 45 | BPNode bpNode = currentPos.getBpNode(); 46 | int currentPosition = currentPos.getPosition(); 47 | // 超过了endPos,则返回null 48 | if (endPos != null) { 49 | if (bpNode.equals(endPos.getBpNode())) { 50 | if (currentPos.getPosition() > endPos.getPosition()) { 51 | return null; 52 | } 53 | } 54 | } 55 | if (currentPosition < bpNode.getEntries().size()) { 56 | IndexEntry indexEntry = bpNode.getEntries().get(currentPosition); 57 | currentPos.incrPosition(); 58 | return indexEntry; 59 | } else { 60 | // 跳入下一个bpNode 61 | bpNode = bpNode.getNext(); 62 | if (bpNode == null) { 63 | return null; 64 | } else { 65 | if (endPos != null) { 66 | // 如果超过了endPos的下一个块,则直接返回null 67 | if (bpNode.equals(endPos.getBpNode().getNext())) { 68 | return null; 69 | } 70 | } 71 | currentPos = new Position(bpNode, 1); 72 | return bpNode.getEntries().get(0); 73 | } 74 | } 75 | } 76 | 77 | public void reset() { 78 | resetCurrentPos(startPos); 79 | } 80 | 81 | public Position getStartPos() { 82 | return startPos; 83 | } 84 | 85 | public void setStartPos(Position startPos) { 86 | this.startPos = startPos; 87 | } 88 | 89 | public Position getEndPos() { 90 | return endPos; 91 | } 92 | 93 | public void setEndPos(Position endPos) { 94 | this.endPos = endPos; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/fs/FStore.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.fs; 2 | 3 | import java.io.EOFException; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.FileChannel; 7 | 8 | import alchemystar.freedom.config.SystemConfig; 9 | import alchemystar.freedom.index.bp.BpPage; 10 | import alchemystar.freedom.store.page.Page; 11 | import alchemystar.freedom.store.page.PageLoader; 12 | import alchemystar.freedom.store.page.PagePool; 13 | import alchemystar.freedom.transaction.log.Log; 14 | 15 | /** 16 | * FStore 17 | * 18 | * @Author lizhuyang 19 | */ 20 | public class FStore { 21 | 22 | // 文件路径 23 | private String filePath; 24 | // 文件channel 25 | private FileChannel fileChannel; 26 | // 当前filePosition 27 | private long currentFilePosition; 28 | 29 | public FStore(String filePath) { 30 | this.filePath = filePath; 31 | currentFilePosition = 0; 32 | open(); 33 | } 34 | 35 | public void open() { 36 | fileChannel = FileUtils.open(filePath); 37 | } 38 | 39 | public Page readPageFromFile(int pageIndex) { 40 | return readPageFromFile(pageIndex, false); 41 | } 42 | 43 | public Page readPageFromFile(int pageIndex, boolean isIndex) { 44 | int readPos = pageIndex * SystemConfig.DEFAULT_PAGE_SIZE; 45 | ByteBuffer buffer = ByteBuffer.allocate(SystemConfig.DEFAULT_PAGE_SIZE); 46 | try { 47 | FileUtils.readFully(fileChannel, buffer, readPos); 48 | } catch (EOFException e) { 49 | return null; 50 | } catch (IOException e) { 51 | throw new RuntimeException(e); 52 | } 53 | // byteBuffer 转 buffer 54 | byte[] b = new byte[SystemConfig.DEFAULT_PAGE_SIZE]; 55 | // position跳回原始位置 56 | buffer.flip(); 57 | buffer.get(b); 58 | if (!isIndex) { 59 | // 从池中拿取空页 60 | Page page = PagePool.getIntance().getFreePage(); 61 | // 初始化page 62 | page.read(b); 63 | return page; 64 | } else { 65 | BpPage bpPage = new BpPage(SystemConfig.DEFAULT_PAGE_SIZE); 66 | bpPage.read(b); 67 | return bpPage; 68 | } 69 | } 70 | 71 | public PageLoader readPageLoaderFromFile(int pageIndex) { 72 | Page page = readPageFromFile(pageIndex); 73 | PageLoader loader = new PageLoader(page); 74 | // 装载byte 75 | loader.load(); 76 | return loader; 77 | } 78 | 79 | public void writePageToFile(Page page, int pageIndex) { 80 | try { 81 | int writePos = pageIndex * SystemConfig.DEFAULT_PAGE_SIZE; 82 | ByteBuffer byteBuffer = ByteBuffer.wrap(page.getBuffer()); 83 | FileUtils.writeFully(fileChannel, byteBuffer, writePos); 84 | } catch (Exception e) { 85 | throw new RuntimeException(e); 86 | } 87 | } 88 | 89 | public void append(Log log) { 90 | 91 | } 92 | 93 | public void close() { 94 | FileUtils.closeFile(fileChannel); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/ServerQueryHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import alchemystar.freedom.engine.net.proto.util.ErrorCode; 7 | import alchemystar.freedom.engine.parser.ServerParse; 8 | 9 | /** 10 | * ServerQueryHandler 11 | * 12 | * @Author lizhuyang 13 | */ 14 | public class ServerQueryHandler implements FrontendQueryHandler { 15 | 16 | private static final Logger logger = LoggerFactory.getLogger("sql-digest"); 17 | 18 | private FrontendConnection source; 19 | 20 | public ServerQueryHandler(FrontendConnection source) { 21 | this.source = source; 22 | } 23 | 24 | public void query(String origin) { 25 | 26 | logger.info("sql = " + origin); 27 | String sql = removeFirstAnnotation(origin); 28 | int rs = ServerParse.parse(sql); 29 | switch (rs & 0xff) { 30 | case ServerParse.SET: 31 | SetHandler.handle(sql, source, rs >>> 8); 32 | break; 33 | case ServerParse.SHOW: 34 | ShowHandler.handle(sql, source, rs >>> 8); 35 | break; 36 | case ServerParse.SELECT: 37 | SelectHandler.handle(sql, source, rs >>> 8); 38 | break; 39 | case ServerParse.START: 40 | StartHandler.handle(sql, source, rs >>> 8); 41 | break; 42 | case ServerParse.BEGIN: 43 | source.begin(); 44 | break; 45 | case ServerParse.SAVEPOINT: 46 | SavepointHandler.handle(sql, source); 47 | break; 48 | case ServerParse.KILL: 49 | KillHandler.handle(sql, rs >>> 8, source); 50 | break; 51 | case ServerParse.KILL_QUERY: 52 | source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unsupported command"); 53 | break; 54 | case ServerParse.EXPLAIN: 55 | source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unsupported command"); 56 | break; 57 | case ServerParse.CREATE_DATABASE: 58 | // source.createShema(sql); 59 | break; 60 | case ServerParse.COMMIT: 61 | source.commit(); 62 | break; 63 | case ServerParse.ROLLBACK: 64 | source.rollBack(); 65 | break; 66 | case ServerParse.USE: 67 | UseHandler.handle(sql, source, rs >>> 8); 68 | break; 69 | default: 70 | // todo add no modify exception 71 | source.execute(sql, rs); 72 | } 73 | } 74 | 75 | public static String removeFirstAnnotation(String sql) { 76 | String result = null; 77 | sql = sql.trim(); 78 | if (sql.startsWith("/*")) { 79 | int index = sql.indexOf("*/") + 2; 80 | return sql.substring(index); 81 | } else { 82 | return sql; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/RowDataPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 7 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 8 | import io.netty.buffer.ByteBuf; 9 | import io.netty.channel.ChannelHandlerContext; 10 | 11 | /** 12 | * RowDataPacket 13 | * 14 | * @Author lizhuyang 15 | */ 16 | public class RowDataPacket extends MySQLPacket { 17 | private static final byte NULL_MARK = (byte) 251; 18 | 19 | public final int fieldCount; 20 | public final List fieldValues; 21 | public final List fieldStrings; 22 | 23 | public RowDataPacket(int fieldCount) { 24 | this.fieldCount = fieldCount; 25 | this.fieldValues = new ArrayList(fieldCount); 26 | this.fieldStrings = new ArrayList(fieldCount); 27 | } 28 | 29 | public void add(byte[] value) { 30 | fieldValues.add(value); 31 | } 32 | 33 | public void read(BinaryPacket bin) { 34 | packetLength = bin.packetLength; 35 | packetId = bin.packetId; 36 | MySQLMessage mm = new MySQLMessage(bin.data); 37 | for (int i = 0; i < fieldCount; i++) { 38 | byte[] bytes = mm.readBytesWithLength(); 39 | fieldValues.add(bytes); 40 | fieldStrings.add(new String(bytes)); 41 | } 42 | } 43 | 44 | @Override 45 | public void write(ChannelHandlerContext ctx) { 46 | ByteBuf buffer = ctx.alloc().buffer(); 47 | BufferUtil.writeUB3(buffer, calcPacketSize()); 48 | buffer.writeByte(packetId); 49 | for (int i = 0; i < fieldCount; i++) { 50 | byte[] fv = fieldValues.get(i); 51 | if (fv == null || fv.length == 0) { 52 | buffer.writeByte(RowDataPacket.NULL_MARK); 53 | } else { 54 | BufferUtil.writeLength(buffer, fv.length); 55 | buffer.writeBytes(fv); 56 | } 57 | } 58 | ctx.writeAndFlush(buffer); 59 | } 60 | 61 | @Override 62 | public ByteBuf writeBuf(ByteBuf buffer, ChannelHandlerContext ctx) { 63 | BufferUtil.writeUB3(buffer, calcPacketSize()); 64 | buffer.writeByte(packetId); 65 | for (int i = 0; i < fieldCount; i++) { 66 | byte[] fv = fieldValues.get(i); 67 | if (fv == null || fv.length == 0) { 68 | buffer.writeByte(RowDataPacket.NULL_MARK); 69 | } else { 70 | BufferUtil.writeLength(buffer, fv.length); 71 | buffer.writeBytes(fv); 72 | } 73 | } 74 | return buffer; 75 | } 76 | 77 | @Override 78 | public int calcPacketSize() { 79 | int size = 0; 80 | for (int i = 0; i < fieldCount; i++) { 81 | byte[] v = fieldValues.get(i); 82 | size += (v == null || v.length == 0) ? 1 : BufferUtil.getLength(v); 83 | } 84 | return size; 85 | } 86 | 87 | @Override 88 | protected String getPacketInfo() { 89 | return "MySQL RowData Packet"; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/FrontendCommandHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import java.util.Date; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import alchemystar.freedom.config.SystemConfig; 9 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 10 | import alchemystar.freedom.engine.net.proto.mysql.BinaryPacket; 11 | import alchemystar.freedom.engine.net.proto.util.ErrorCode; 12 | import io.netty.channel.ChannelHandlerAdapter; 13 | import io.netty.channel.ChannelHandlerContext; 14 | import io.netty.handler.timeout.IdleState; 15 | import io.netty.handler.timeout.IdleStateEvent; 16 | 17 | /** 18 | * 命令Handler 19 | * 20 | * @Author lizhuyang 21 | */ 22 | public class FrontendCommandHandler extends ChannelHandlerAdapter { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(ChannelHandlerAdapter.class); 25 | 26 | protected FrontendConnection source; 27 | 28 | public FrontendCommandHandler(FrontendConnection source) { 29 | this.source = source; 30 | } 31 | 32 | @Override 33 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 34 | // 重置最后active时间 35 | source.setLastActiveTime(); 36 | BinaryPacket bin = (BinaryPacket) msg; 37 | byte type = bin.data[0]; 38 | switch (type) { 39 | case MySQLPacket.COM_INIT_DB: 40 | // just init the frontend 41 | source.initDB(bin); 42 | break; 43 | case MySQLPacket.COM_QUERY: 44 | source.query(bin); 45 | break; 46 | case MySQLPacket.COM_PING: 47 | // todo ping , last access time update 48 | source.ping(); 49 | break; 50 | case MySQLPacket.COM_QUIT: 51 | source.close(); 52 | break; 53 | case MySQLPacket.COM_PROCESS_KILL: 54 | source.kill(bin.data); 55 | break; 56 | case MySQLPacket.COM_STMT_PREPARE: 57 | // todo prepare支持,参考MyCat 58 | source.stmtPrepare(bin.data); 59 | break; 60 | case MySQLPacket.COM_STMT_EXECUTE: 61 | source.stmtExecute(bin.data); 62 | break; 63 | case MySQLPacket.COM_STMT_CLOSE: 64 | source.stmtClose(bin.data); 65 | break; 66 | case MySQLPacket.COM_HEARTBEAT: 67 | source.heartbeat(bin.data); 68 | break; 69 | default: 70 | source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unknown command"); 71 | break; 72 | } 73 | } 74 | 75 | @Override 76 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 77 | // 如果心跳检查>最大值,则close掉此连接 78 | if (evt instanceof IdleStateEvent) { 79 | if (((IdleStateEvent) evt).state().equals(IdleState.ALL_IDLE)) { 80 | Long now = (new Date()).getTime(); 81 | System.out.println("hahaha"); 82 | if (now - source.getLastActiveTime() > (SystemConfig.IDLE_TIME_OUT * 1000)) { 83 | source.close(); 84 | } 85 | } 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/util/IntegerUtil.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.util; 2 | 3 | /** 4 | * IntegerUtil 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public final class IntegerUtil { 9 | 10 | static final byte[] minValue = "-2147483648".getBytes(); 11 | static final int[] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE }; 12 | static final byte[] digitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', 13 | '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', 14 | '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', 15 | '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', 16 | '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', }; 17 | static final byte[] digitOnes = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', 18 | '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', 19 | '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', 20 | '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', 21 | '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', }; 22 | static final byte[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 23 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; 24 | 25 | public static byte[] toBytes(int i) { 26 | if (i == Integer.MIN_VALUE) 27 | return minValue; 28 | int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); 29 | byte[] buf = new byte[size]; 30 | getBytes(i, size, buf); 31 | return buf; 32 | } 33 | 34 | static int stringSize(int x) { 35 | for (int i = 0;; i++) { 36 | if (x <= sizeTable[i]) 37 | return i + 1; 38 | } 39 | } 40 | 41 | static void getBytes(int i, int index, byte[] buf) { 42 | int q, r; 43 | int charPos = index; 44 | byte sign = 0; 45 | 46 | if (i < 0) { 47 | sign = '-'; 48 | i = -i; 49 | } 50 | 51 | // Generate two digits per iteration 52 | while (i >= 65536) { 53 | q = i / 100; 54 | // really: r = i - (q * 100); 55 | r = i - ((q << 6) + (q << 5) + (q << 2)); 56 | i = q; 57 | buf[--charPos] = digitOnes[r]; 58 | buf[--charPos] = digitTens[r]; 59 | } 60 | 61 | // Fall thru to fast mode for smaller numbers 62 | // assert(i <= 65536, i); 63 | for (;;) { 64 | q = (i * 52429) >>> (16 + 3); 65 | r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... 66 | buf[--charPos] = digits[r]; 67 | i = q; 68 | if (i == 0) 69 | break; 70 | } 71 | if (sign != 0) { 72 | buf[--charPos] = sign; 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/handler/frontend/SetHandler.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.handler.frontend; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import alchemystar.freedom.engine.net.proto.mysql.OkPacket; 7 | import alchemystar.freedom.engine.net.proto.util.ErrorCode; 8 | import alchemystar.freedom.engine.net.proto.util.Isolations; 9 | import alchemystar.freedom.engine.net.response.CharacterSet; 10 | import alchemystar.freedom.engine.parser.ServerParseSet; 11 | import io.netty.channel.ChannelHandlerContext; 12 | 13 | /** 14 | * SetHandler 15 | * 16 | * @Author lizhuyang 17 | */ 18 | public final class SetHandler { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(SetHandler.class); 21 | private static final byte[] AC_OFF = new byte[] {7, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}; 22 | private static final String TABLE_PATH_SYNTAX_ERROR = "must set like this [table_path=\"tableName:filePath\""; 23 | 24 | public static void handle(String stmt, FrontendConnection c, int offset) { 25 | ChannelHandlerContext ctx = c.getCtx(); 26 | int rs = ServerParseSet.parse(stmt, offset); 27 | switch (rs & 0xff) { 28 | case ServerParseSet.AUTOCOMMIT_ON: 29 | if (c.isAutocommit()) { 30 | c.writeBuf(OkPacket.OK); 31 | } else { 32 | //c.commit(); 33 | c.setAutocommit(true); 34 | } 35 | break; 36 | case ServerParseSet.AUTOCOMMIT_OFF: { 37 | if (c.isAutocommit()) { 38 | c.setAutocommit(false); 39 | } 40 | c.writeOk(); 41 | break; 42 | } 43 | case ServerParseSet.TX_READ_UNCOMMITTED: { 44 | c.setTxIsolation(Isolations.READ_UNCOMMITTED); 45 | c.writeOk(); 46 | break; 47 | } 48 | case ServerParseSet.TX_READ_COMMITTED: { 49 | c.setTxIsolation(Isolations.READ_COMMITTED); 50 | c.writeOk(); 51 | break; 52 | } 53 | case ServerParseSet.TX_REPEATED_READ: { 54 | c.setTxIsolation(Isolations.REPEATED_READ); 55 | c.writeOk(); 56 | break; 57 | } 58 | case ServerParseSet.TX_SERIALIZABLE: { 59 | c.setTxIsolation(Isolations.SERIALIZABLE); 60 | c.writeOk(); 61 | break; 62 | } 63 | case ServerParseSet.NAMES: 64 | String charset = stmt.substring(rs >>> 8).trim(); 65 | if (c.setCharset(charset)) { 66 | c.writeOk(); 67 | } else { 68 | c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, "Unknown charset '" + charset + "'"); 69 | } 70 | break; 71 | case ServerParseSet.CHARACTER_SET_CLIENT: 72 | case ServerParseSet.CHARACTER_SET_CONNECTION: 73 | case ServerParseSet.CHARACTER_SET_RESULTS: 74 | CharacterSet.response(stmt, c, rs); 75 | break; 76 | default: 77 | StringBuilder s = new StringBuilder(); 78 | logger.warn(s.append(c).append(stmt).append(" is not executed").toString()); 79 | c.writeOk(); 80 | } 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/meta/value/Value.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.meta.value; 2 | 3 | /** 4 | * Value 5 | * 6 | * @Author lizhuyang 7 | */ 8 | public abstract class Value { 9 | 10 | public static final byte UNKNOWN = 100; 11 | public static final byte STRING = 1; 12 | public static final byte INT = 2; 13 | public static final byte LONG = 3; 14 | public static final byte BOOLEAN = 4; 15 | 16 | public abstract int getLength(); 17 | 18 | public abstract byte getType(); 19 | 20 | public abstract byte[] getBytes(); 21 | 22 | public abstract void read(byte[] bytes); 23 | 24 | public abstract int compare(Value value); 25 | 26 | public ValueBoolean and(Value value) { 27 | if (!(this instanceof ValueBoolean)) { 28 | throw new RuntimeException("left value must be boolean"); 29 | } 30 | if (!(value instanceof ValueBoolean)) { 31 | throw new RuntimeException("right value must be boolean"); 32 | } 33 | 34 | boolean result = ((ValueBoolean) this).getBoolean() && ((ValueBoolean) value).getBoolean(); 35 | return new ValueBoolean(result); 36 | } 37 | 38 | public ValueBoolean or(Value value) { 39 | if (!(this instanceof ValueBoolean)) { 40 | throw new RuntimeException("left value must be boolean"); 41 | } 42 | if (!(value instanceof ValueBoolean)) { 43 | throw new RuntimeException("right value must be boolean"); 44 | } 45 | 46 | boolean result = ((ValueBoolean) this).getBoolean() || ((ValueBoolean) value).getBoolean(); 47 | return new ValueBoolean(result); 48 | } 49 | 50 | public ValueBoolean equality(Value value) { 51 | if (compare(value) == 0) { 52 | return new ValueBoolean(true); 53 | } else { 54 | return new ValueBoolean(false); 55 | } 56 | } 57 | 58 | public ValueBoolean greaterThanOrEqual(Value value) { 59 | if (compare(value) >= 0) { 60 | return new ValueBoolean(true); 61 | } else { 62 | return new ValueBoolean(false); 63 | } 64 | } 65 | 66 | public ValueBoolean greaterThan(Value value) { 67 | if (compare(value) > 0) { 68 | return new ValueBoolean(true); 69 | } else { 70 | return new ValueBoolean(false); 71 | } 72 | } 73 | 74 | public ValueBoolean lessThanOrEqual(Value value) { 75 | if (compare(value) <= 0) { 76 | return new ValueBoolean(true); 77 | } else { 78 | return new ValueBoolean(false); 79 | } 80 | } 81 | 82 | public ValueBoolean lessThan(Value value) { 83 | if (compare(value) < 0) { 84 | return new ValueBoolean(true); 85 | } else { 86 | return new ValueBoolean(false); 87 | } 88 | } 89 | 90 | public Value add(Value v) { 91 | throw new RuntimeException("UnSupport Plus Function"); 92 | } 93 | 94 | public int getInt() { 95 | throw new RuntimeException("UnSupport get int"); 96 | } 97 | 98 | public long getLong() { 99 | throw new RuntimeException("UnSupport get long"); 100 | } 101 | 102 | public abstract String getString(); 103 | 104 | public Value subtract(Value v) { 105 | throw new RuntimeException("UnSupport Minus Function"); 106 | } 107 | 108 | public Value divide(Value v) { 109 | throw new RuntimeException("UnSupport divide Function"); 110 | } 111 | 112 | public Value multiply(Value v) { 113 | throw new RuntimeException("UnSupport multiply Function"); 114 | } 115 | 116 | public Value concat(Value v) { 117 | throw new RuntimeException("UnSupport concat Function"); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/Trx.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import alchemystar.freedom.engine.Database; 7 | import alchemystar.freedom.meta.ClusterIndexEntry; 8 | import alchemystar.freedom.meta.IndexEntry; 9 | import alchemystar.freedom.meta.Table; 10 | import alchemystar.freedom.transaction.log.LSNFactory; 11 | import alchemystar.freedom.transaction.log.Log; 12 | import alchemystar.freedom.transaction.log.LogType; 13 | import alchemystar.freedom.transaction.redo.RedoManager; 14 | import alchemystar.freedom.transaction.undo.UndoManager; 15 | 16 | /** 17 | * 事务 18 | * 19 | * @Author lizhuyang 20 | */ 21 | public class Trx { 22 | // 事务状态,初始化为 事务未开始 23 | private int state = TrxState.TRX_STATE_NOT_STARTED; 24 | // 事务id 25 | private int trxId; 26 | 27 | private List logs = new ArrayList(); 28 | 29 | public void begin() { 30 | // 事务开启日志 31 | Log startLog = new Log(); 32 | startLog.setLsn(LSNFactory.nextLSN()); 33 | startLog.setTrxId(trxId); 34 | startLog.setLogType(LogType.TRX_START); 35 | Database.getInstance().getLogStore().appendLog(startLog); 36 | Database.getInstance().getLogStore().appendLog(startLog); 37 | state = TrxState.TRX_STATE_ACTIVE; 38 | } 39 | 40 | public void addLog(Log log) { 41 | logs.add(log); 42 | } 43 | 44 | // 都是Row模式下的add 45 | public void addLog(Table table, int opType, IndexEntry before, IndexEntry after) { 46 | if (!(before == null || before instanceof ClusterIndexEntry) || !(after == null || after instanceof 47 | ClusterIndexEntry)) { 48 | throw new RuntimeException("log before and after must be clusterIndexEntry"); 49 | } 50 | Log log = new Log(); 51 | log.setLsn(LSNFactory.nextLSN()); 52 | log.setLogType(LogType.ROW); 53 | log.setTrxId(trxId); 54 | log.setOpType(opType); 55 | log.setTableName(table.getName()); 56 | log.setBefore(before); 57 | log.setAfter(after); 58 | // log 落盘,不然在宕机的时候无法找到对应信息 59 | Database.getInstance().getLogStore().appendLog(log); 60 | // 这边的logs是为了在内存上加速undo 61 | logs.add(log); 62 | 63 | } 64 | 65 | public void commit() { 66 | // 加上commit日志 67 | Log commitLog = new Log(); 68 | commitLog.setLsn(LSNFactory.nextLSN()); 69 | commitLog.setTrxId(trxId); 70 | commitLog.setLogType(LogType.COMMIT); 71 | Database.getInstance().getLogStore().appendLog(commitLog); 72 | state = TrxState.TRX_COMMITTED; 73 | // commit 之后无法使用undoLog 74 | logs.clear(); 75 | } 76 | 77 | // recovery 78 | public void redo() { 79 | for (Log log : logs) { 80 | if (log.getLogType() == LogType.ROW) { 81 | RedoManager.redo(log); 82 | } 83 | } 84 | } 85 | 86 | public void rollback() { 87 | undo(); 88 | state = TrxState.TRX_STATE_NOT_STARTED; 89 | // rollback之后无法使用undoLog 90 | logs.clear(); 91 | } 92 | 93 | public int getTrxId() { 94 | return trxId; 95 | } 96 | 97 | public void setTrxId(int trxId) { 98 | this.trxId = trxId; 99 | } 100 | 101 | private void undo() { 102 | // 反序undo 103 | for (int i = logs.size() - 1; i >= 0; i--) { 104 | UndoManager.undo(logs.get(i)); 105 | } 106 | } 107 | 108 | public int getState() { 109 | return state; 110 | } 111 | 112 | public void setState(int state) { 113 | this.state = state; 114 | } 115 | 116 | public boolean trxIsNotStart() { 117 | return state == TrxState.TRX_STATE_NOT_STARTED; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/sql/parser/InsertVisitor.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.sql.parser; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import com.alibaba.druid.sql.ast.SQLExpr; 8 | import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr; 9 | import com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr; 10 | import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; 11 | import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; 12 | import com.alibaba.druid.sql.ast.statement.SQLTableSource; 13 | import com.alibaba.druid.sql.visitor.SchemaStatVisitor; 14 | 15 | import alchemystar.freedom.meta.ClusterIndexEntry; 16 | import alchemystar.freedom.meta.IndexDesc; 17 | import alchemystar.freedom.meta.IndexEntry; 18 | import alchemystar.freedom.meta.Table; 19 | import alchemystar.freedom.meta.TableManager; 20 | import alchemystar.freedom.meta.value.Value; 21 | import alchemystar.freedom.meta.value.ValueLong; 22 | import alchemystar.freedom.meta.value.ValueString; 23 | 24 | /** 25 | * @Author lizhuyang 26 | */ 27 | public class InsertVisitor extends SchemaStatVisitor { 28 | 29 | // table表达式 30 | protected SQLTableSource tableSource; 31 | 32 | private Map attributeIndexMap = new HashMap(); 33 | 34 | private Map indexAttributeMap = new HashMap(); 35 | 36 | private List valueExpr; 37 | 38 | private Table table; 39 | 40 | public boolean visit(SQLInsertStatement x) { 41 | tableSource = x.getTableSource(); 42 | if (!(tableSource instanceof SQLExprTableSource)) { 43 | throw new RuntimeException("not support this table source type :" + tableSource); 44 | } 45 | table = TableManager.getTable(tableSource.toString()); 46 | mapColumnIndex(x); 47 | SQLInsertStatement.ValuesClause valuesClause = x.getValues(); 48 | // 只支持单条insert 49 | valueExpr = valuesClause.getValues(); 50 | return true; 51 | } 52 | 53 | public IndexEntry buildInsertEntry() { 54 | Value[] values = new Value[table.getAttributes().length]; 55 | ClusterIndexEntry indexEntry = new ClusterIndexEntry(values); 56 | indexEntry.setIndexDesc(new IndexDesc(table.getAttributes())); 57 | for (int i = 0; i < values.length; i++) { 58 | String attributeName = table.getAttributes()[i].getName(); 59 | Integer index = attributeIndexMap.get(attributeName); 60 | if (index != null) { 61 | values[i] = getValue(valueExpr.get(index)); 62 | } else { 63 | // 采用默认值 64 | values[i] = table.getAttributes()[i].getDefaultValue(); 65 | } 66 | } 67 | return indexEntry; 68 | } 69 | 70 | public Value getValue(SQLExpr sqlExpr) { 71 | if (sqlExpr instanceof SQLNumericLiteralExpr) { 72 | return new ValueLong(((SQLNumericLiteralExpr) sqlExpr).getNumber().longValue()); 73 | } else if (sqlExpr instanceof SQLTextLiteralExpr) { 74 | return new ValueString(((SQLTextLiteralExpr) sqlExpr).getText()); 75 | } 76 | throw new RuntimeException("can't support not literal expr in insert"); 77 | } 78 | 79 | // 组装column和index的映射 80 | public void mapColumnIndex(SQLInsertStatement x) { 81 | List list = x.getColumns(); 82 | for (int i = 0; i < list.size(); i++) { 83 | SQLExpr expr = list.get(i); 84 | String column = getColumn(expr).getName(); 85 | 86 | attributeIndexMap.put(column, i); 87 | // 记录反向映射 88 | indexAttributeMap.put(i, column); 89 | } 90 | } 91 | 92 | public Table getTable() { 93 | return table; 94 | } 95 | 96 | public void setTable(Table table) { 97 | this.table = table; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/log/LogStore.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.log; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.FileChannel; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import alchemystar.freedom.config.SystemConfig; 10 | import alchemystar.freedom.meta.IndexEntry; 11 | import alchemystar.freedom.store.fs.FileUtils; 12 | import alchemystar.freedom.transaction.OpType; 13 | import alchemystar.freedom.transaction.log.Log; 14 | import alchemystar.freedom.transaction.log.LogType; 15 | import io.netty.buffer.ByteBuf; 16 | import io.netty.buffer.ByteBufAllocator; 17 | import io.netty.buffer.UnpooledByteBufAllocator; 18 | 19 | 20 | /** 21 | * 文件路径 22 | * 23 | * @Author lizhuyang 24 | */ 25 | public class LogStore { 26 | 27 | // 文件路径 28 | private String logPath; 29 | // 文件channel 30 | private FileChannel fileChannel; 31 | 32 | private ByteBufAllocator byteBufAllocator; 33 | 34 | public LogStore() { 35 | this.logPath = SystemConfig.FREEDOM_LOG_FILE_NAME; 36 | byteBufAllocator = new UnpooledByteBufAllocator(false); 37 | open(); 38 | } 39 | 40 | public void open() { 41 | fileChannel = FileUtils.open(logPath); 42 | } 43 | 44 | public void close() { 45 | FileUtils.closeFile(fileChannel); 46 | } 47 | 48 | public void appendLog(Log log) { 49 | ByteBuf byteBuf = byteBufAllocator.buffer(1024); 50 | log.writeBytes(byteBuf); 51 | append(byteBuf.nioBuffer()); 52 | } 53 | 54 | // for 重新启动时候使用 55 | public List loadLog() { 56 | // 从文件开始load 57 | try { 58 | fileChannel.position(0); 59 | long length = fileChannel.size(); 60 | ByteBuffer byteBuffer = ByteBuffer.allocate((int) length); 61 | FileUtils.readFully(fileChannel, byteBuffer); 62 | return readAllLog(byteBuffer); 63 | } catch (IOException e) { 64 | throw new RuntimeException(e); 65 | } 66 | } 67 | 68 | public List readAllLog(ByteBuffer byteBuffer) { 69 | List logs = new ArrayList(); 70 | ByteBuf byteBuf = byteBufAllocator.buffer(byteBuffer.capacity()); 71 | byteBuf.writeBytes(byteBuffer.array()); 72 | 73 | while (byteBuf.readableBytes() > 0) { 74 | Log log = new Log(); 75 | // for lsn 76 | log.setLsn(byteBuf.readLong()); 77 | // logType 78 | log.setLogType(byteBuf.readInt()); 79 | // trxId 80 | log.setTrxId(byteBuf.readInt()); 81 | // 只有row的日志才有记录,其它只是记录了其标识 82 | if (log.getLogType() == LogType.ROW) { 83 | int tableNameLength = byteBuf.readInt(); 84 | byte[] byteName = new byte[tableNameLength]; 85 | byteBuf.readBytes(byteName); 86 | String tableName = new String(byteName); 87 | log.setTableName(tableName); 88 | log.setOpType(byteBuf.readInt()); 89 | int length = byteBuf.readInt(); 90 | byte[] entryByte = new byte[length]; 91 | byteBuf.readBytes(entryByte); 92 | IndexEntry entry = new IndexEntry(); 93 | entry.read(entryByte); 94 | if (log.getOpType() == OpType.insert) { 95 | log.setAfter(entry); 96 | } else if (log.getOpType() == OpType.delete) { 97 | log.setAfter(entry); 98 | } 99 | } 100 | logs.add(log); 101 | } 102 | return logs; 103 | } 104 | 105 | // 添加日志 106 | public void append(ByteBuffer dst) { 107 | try { 108 | FileUtils.append(fileChannel, dst); 109 | } catch (Exception e) { 110 | throw new RuntimeException(e); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/store/page/PageHeaderData.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.store.page; 2 | 3 | import alchemystar.freedom.config.SystemConfig; 4 | 5 | /** 6 | * PageHeaderData 7 | * 8 | * @Author lizhuyang 9 | */ 10 | public class PageHeaderData { 11 | // todo 变的时候 需要同步更新这里 12 | public static final Integer PAGE_HEADER_SIZE = 24; 13 | // Page开头的magicWorld 14 | private String magicWord = "Freedom"; 15 | // free space的起始偏移 16 | private int lowerOffset; 17 | // 指向pageHeader中的lowerOffset起始位置 18 | public static final int LOWER_POINTER = 8; 19 | // free space的最终偏移 20 | private int upperOffset; 21 | // 指向pageHeader中的upperOffset起始位置 22 | public static final int UPPER_POINTER = 12; 23 | // special space的起始偏移 24 | private int special; 25 | // 指向pageHeader中的special起始位置 26 | public static final int SPECIAL_POINTER = 16; 27 | // 记录元组的数量 28 | private int tupleCount; 29 | public static final int TUPLE_COUNT_POINTER = 20; 30 | 31 | // 记录header长度 32 | private int headerLength; 33 | 34 | public PageHeaderData(int size) { 35 | int magicWorldLength = magicWord.getBytes().length + 1; 36 | lowerOffset = magicWorldLength + 4 + 4 + 4 + 4; 37 | // 给special 64字节的空间 38 | upperOffset = size - SystemConfig.DEFAULT_SPECIAL_POINT_LENGTH; 39 | special = upperOffset; 40 | headerLength = lowerOffset; 41 | } 42 | 43 | public void modifyLowerOffset(int i, Page page) { 44 | lowerOffset = i; 45 | // 修改byte数组位置 46 | page.writeIntPos(i, LOWER_POINTER); 47 | } 48 | 49 | public void modifyUpperOffset(int i, Page page) { 50 | upperOffset = i; 51 | // 修改byte数组位置 52 | page.writeIntPos(i, UPPER_POINTER); 53 | } 54 | 55 | public void modifySpecial(int i, Page page) { 56 | upperOffset = i; 57 | // 修改byte数组位置 58 | page.writeIntPos(i, SPECIAL_POINTER); 59 | } 60 | 61 | public void addTupleCount(Page page) { 62 | int count = page.readIntPos(TUPLE_COUNT_POINTER); 63 | count++; 64 | // 修改tuple的数量 65 | page.writeIntPos(count, TUPLE_COUNT_POINTER); 66 | tupleCount = count; 67 | } 68 | 69 | public void decTupleCount(Page page) { 70 | int count = page.readIntPos(TUPLE_COUNT_POINTER); 71 | count--; 72 | // 修改tupe的数量 73 | page.writeIntPos(count, TUPLE_COUNT_POINTER); 74 | tupleCount = count; 75 | } 76 | 77 | void write(Page page) { 78 | page.writeStringWithNull(magicWord); 79 | page.writeInt(lowerOffset); 80 | page.writeInt(upperOffset); 81 | page.writeInt(special); 82 | page.writeInt(tupleCount); 83 | } 84 | 85 | public static PageHeaderData read(Page page) { 86 | PageHeaderData pageHeaderData = new PageHeaderData(page.getLength()); 87 | pageHeaderData.magicWord = page.readStringWithNull(); 88 | pageHeaderData.lowerOffset = page.readInt(); 89 | pageHeaderData.upperOffset = page.readInt(); 90 | pageHeaderData.special = page.readInt(); 91 | pageHeaderData.tupleCount = page.readInt(); 92 | return pageHeaderData; 93 | } 94 | 95 | public int getUpperOffset() { 96 | return upperOffset; 97 | } 98 | 99 | public PageHeaderData setUpperOffset(int upperOffset) { 100 | this.upperOffset = upperOffset; 101 | return this; 102 | } 103 | 104 | public int getLowerOffset() { 105 | return lowerOffset; 106 | } 107 | 108 | public PageHeaderData setLowerOffset(int lowerOffset) { 109 | this.lowerOffset = lowerOffset; 110 | return this; 111 | } 112 | 113 | public int getSpecial() { 114 | return special; 115 | } 116 | 117 | public PageHeaderData setSpecial(int special) { 118 | this.special = special; 119 | return this; 120 | } 121 | 122 | public int getTupleCount() { 123 | return tupleCount; 124 | } 125 | 126 | public int getLength() { 127 | return headerLength; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/transaction/log/Log.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.transaction.log; 2 | 3 | import alchemystar.freedom.meta.IndexEntry; 4 | import io.netty.buffer.ByteBuf; 5 | 6 | /** 7 | * @Author lizhuyang 8 | */ 9 | public class Log { 10 | 11 | // log的序列号,for 幂等 12 | private long lsn; 13 | 14 | private int logType; 15 | 16 | // 当前日志对应的trxId 17 | private int trxId; 18 | 19 | private String tableName; 20 | 21 | private int opType; 22 | 23 | private IndexEntry before; 24 | 25 | private IndexEntry after; 26 | 27 | public String getTableName() { 28 | return tableName; 29 | } 30 | 31 | public void setTableName(String tableName) { 32 | this.tableName = tableName; 33 | } 34 | 35 | public int getTrxId() { 36 | return trxId; 37 | } 38 | 39 | public void setTrxId(int trxId) { 40 | this.trxId = trxId; 41 | } 42 | 43 | public IndexEntry getBefore() { 44 | return before; 45 | } 46 | 47 | public void setBefore(IndexEntry before) { 48 | this.before = before; 49 | } 50 | 51 | public IndexEntry getAfter() { 52 | return after; 53 | } 54 | 55 | public void setAfter(IndexEntry after) { 56 | this.after = after; 57 | } 58 | 59 | public int getOpType() { 60 | return opType; 61 | } 62 | 63 | public void setOpType(int opType) { 64 | this.opType = opType; 65 | } 66 | 67 | public int getLogType() { 68 | return logType; 69 | } 70 | 71 | public void setLogType(int logType) { 72 | this.logType = logType; 73 | } 74 | 75 | public long getLsn() { 76 | return lsn; 77 | } 78 | 79 | public void setLsn(long lsn) { 80 | this.lsn = lsn; 81 | } 82 | 83 | public void writeBytes(ByteBuf byteBuf) { 84 | // for lsn 85 | byteBuf.writeLong(lsn); 86 | // for logType 87 | byteBuf.writeInt(logType); 88 | // for trxId 89 | byteBuf.writeInt(trxId); 90 | if (logType == LogType.ROW) { 91 | // for table name 92 | byteBuf.writeInt(tableName.getBytes().length); 93 | byteBuf.writeBytes(tableName.getBytes()); 94 | // for opType 95 | byteBuf.writeInt(opType); 96 | // for before 97 | if (before != null) { 98 | byteBuf.writeInt(before.getLength()); 99 | byteBuf.writeBytes(before.getBytes()); 100 | } 101 | if (after != null) { 102 | byteBuf.writeInt(after.getLength()); 103 | byteBuf.writeBytes(after.getBytes()); 104 | } 105 | } 106 | } 107 | 108 | @Override 109 | public String toString() { 110 | return "Log{" + 111 | "lsn=" + lsn + 112 | ", logType=" + logType + 113 | ", trxId=" + trxId + 114 | ", tableName='" + tableName + '\'' + 115 | ", opType=" + opType + 116 | ", before=" + before + 117 | ", after=" + after + 118 | '}'; 119 | } 120 | 121 | // 122 | // private int getEntryCount() { 123 | // int count = 0; 124 | // if (before != null) { 125 | // count++; 126 | // } 127 | // if (after != null) { 128 | // count++; 129 | // } 130 | // return count; 131 | // } 132 | 133 | // private int getLength() { 134 | // // 4 for int trxId 135 | // // 4 + length for table string' 136 | // // 4 for int opType 137 | // // 4 for int entry count 1 or 2? 138 | // int length = (4) + (4 + tableName.getBytes().length) + (4) + (4); 139 | // if (before != null) { 140 | // // 4 + indexEntry length 141 | // length += (4) + before.getBytes().length; 142 | // } 143 | // if (after != null) { 144 | // // 4 + indexEntry length 145 | // length += (4) + after.getBytes().length; 146 | // } 147 | // return length; 148 | // } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/FieldPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | 8 | /** 9 | * FieldPacket 10 | * 11 | * @Author lizhuyang 12 | */ 13 | public class FieldPacket extends MySQLPacket { 14 | private static final byte[] DEFAULT_CATALOG = "def".getBytes(); 15 | private static final byte[] FILLER = new byte[2]; 16 | 17 | public byte[] catalog = DEFAULT_CATALOG; 18 | public byte[] db; 19 | public byte[] table; 20 | public byte[] orgTable; 21 | public byte[] name; 22 | public byte[] orgName; 23 | public int charsetIndex; 24 | public long length; 25 | public int type; 26 | public int flags; 27 | public byte decimals; 28 | public byte[] definition; 29 | 30 | /** 31 | * 把字节数组转变成FieldPacket 32 | */ 33 | public void read(byte[] data) { 34 | MySQLMessage mm = new MySQLMessage(data); 35 | this.packetLength = mm.readUB3(); 36 | this.packetId = mm.read(); 37 | readBody(mm); 38 | } 39 | 40 | /** 41 | * 把BinaryPacket转变成FieldPacket 42 | */ 43 | public void read(BinaryPacket bin) { 44 | this.packetLength = bin.packetLength; 45 | this.packetId = bin.packetId; 46 | readBody(new MySQLMessage(bin.data)); 47 | } 48 | 49 | @Override 50 | public ByteBuf writeBuf(ByteBuf buffer, ChannelHandlerContext ctx) { 51 | int size = calcPacketSize(); 52 | BufferUtil.writeUB3(buffer, size); 53 | buffer.writeByte(packetId); 54 | writeBody(buffer); 55 | return buffer; 56 | } 57 | 58 | @Override 59 | public int calcPacketSize() { 60 | int size = (catalog == null ? 1 : BufferUtil.getLength(catalog)); 61 | size += (db == null ? 1 : BufferUtil.getLength(db)); 62 | size += (table == null ? 1 : BufferUtil.getLength(table)); 63 | size += (orgTable == null ? 1 : BufferUtil.getLength(orgTable)); 64 | size += (name == null ? 1 : BufferUtil.getLength(name)); 65 | size += (orgName == null ? 1 : BufferUtil.getLength(orgName)); 66 | size += 13;// 1+2+4+1+2+1+2 67 | if (definition != null) { 68 | size += BufferUtil.getLength(definition); 69 | } 70 | return size; 71 | } 72 | 73 | @Override 74 | protected String getPacketInfo() { 75 | return "MySQL Field Packet"; 76 | } 77 | 78 | private void readBody(MySQLMessage mm) { 79 | this.catalog = mm.readBytesWithLength(); 80 | this.db = mm.readBytesWithLength(); 81 | this.table = mm.readBytesWithLength(); 82 | this.orgTable = mm.readBytesWithLength(); 83 | this.name = mm.readBytesWithLength(); 84 | this.orgName = mm.readBytesWithLength(); 85 | mm.move(1); 86 | this.charsetIndex = mm.readUB2(); 87 | this.length = mm.readUB4(); 88 | this.type = mm.read() & 0xff; 89 | this.flags = mm.readUB2(); 90 | this.decimals = mm.read(); 91 | mm.move(FILLER.length); 92 | if (mm.hasRemaining()) { 93 | this.definition = mm.readBytesWithLength(); 94 | } 95 | } 96 | 97 | private void writeBody(ByteBuf buffer) { 98 | byte nullVal = 0; 99 | BufferUtil.writeWithLength(buffer, catalog, nullVal); 100 | BufferUtil.writeWithLength(buffer, db, nullVal); 101 | BufferUtil.writeWithLength(buffer, table, nullVal); 102 | BufferUtil.writeWithLength(buffer, orgTable, nullVal); 103 | BufferUtil.writeWithLength(buffer, name, nullVal); 104 | BufferUtil.writeWithLength(buffer, orgName, nullVal); 105 | buffer.writeByte((byte) 0x0C); 106 | BufferUtil.writeUB2(buffer, charsetIndex); 107 | BufferUtil.writeUB4(buffer, length); 108 | buffer.writeByte((byte) (type & 0xff)); 109 | BufferUtil.writeUB2(buffer, flags); 110 | buffer.writeByte(decimals); 111 | buffer.writeBytes(FILLER); 112 | if (definition != null) { 113 | BufferUtil.writeWithLength(buffer, definition); 114 | } 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /src/main/java/alchemystar/freedom/engine/net/proto/mysql/AuthPacket.java: -------------------------------------------------------------------------------- 1 | package alchemystar.freedom.engine.net.proto.mysql; 2 | 3 | import alchemystar.freedom.engine.net.proto.MySQLPacket; 4 | import alchemystar.freedom.engine.net.proto.util.BufferUtil; 5 | import alchemystar.freedom.engine.net.proto.util.Capabilities; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelHandlerContext; 9 | 10 | /** 11 | * @Author lizhuyang 12 | */ 13 | public class AuthPacket extends MySQLPacket{ 14 | private static final byte[] FILLER = new byte[23]; 15 | 16 | public long clientFlags; 17 | public long maxPacketSize; 18 | public int charsetIndex; 19 | public byte[] extra;// from FILLER(23) 20 | public String user; 21 | public byte[] password; 22 | public String database; 23 | 24 | public void read(BinaryPacket bin) { 25 | packetLength = bin.packetLength; 26 | packetId = bin.packetId; 27 | MySQLMessage mm = new MySQLMessage(bin.data); 28 | clientFlags = mm.readUB4(); 29 | maxPacketSize = mm.readUB4(); 30 | charsetIndex = (mm.read() & 0xff); 31 | int current = mm.position(); 32 | int len = (int) mm.readLength(); 33 | if (len > 0 && len < FILLER.length) { 34 | byte[] ab = new byte[len]; 35 | System.arraycopy(mm.bytes(), mm.position(), ab, 0, len); 36 | this.extra = ab; 37 | } 38 | mm.position(current + FILLER.length); 39 | user = mm.readStringWithNull(); 40 | password = mm.readBytesWithLength(); 41 | if (((clientFlags & Capabilities.CLIENT_CONNECT_WITH_DB) != 0) && mm.hasRemaining()) { 42 | database = mm.readStringWithNull(); 43 | } 44 | } 45 | 46 | 47 | 48 | public void write(Channel c) { 49 | // default init 256,so it can avoid buff extract 50 | ByteBuf buffer = c.alloc().buffer(); 51 | BufferUtil.writeUB3(buffer, calcPacketSize()); 52 | buffer.writeByte(packetId); 53 | BufferUtil.writeUB4(buffer, clientFlags); 54 | BufferUtil.writeUB4(buffer, maxPacketSize); 55 | buffer.writeByte((byte) charsetIndex); 56 | buffer.writeBytes(FILLER); 57 | if (user == null) { 58 | buffer.writeByte((byte) 0); 59 | } else { 60 | byte[] userData = user.getBytes(); 61 | BufferUtil.writeWithNull(buffer, userData); 62 | } 63 | if (password == null) { 64 | buffer.writeByte((byte) 0); 65 | } else { 66 | BufferUtil.writeWithLength(buffer, password); 67 | } 68 | if (database == null) { 69 | buffer.writeByte((byte) 0); 70 | } else { 71 | byte[] databaseData = database.getBytes(); 72 | BufferUtil.writeWithNull(buffer, databaseData); 73 | } 74 | c.writeAndFlush(buffer); 75 | } 76 | 77 | public void write(ChannelHandlerContext ctx) { 78 | // default init 256,so it can avoid buff extract 79 | ByteBuf buffer = ctx.alloc().buffer(); 80 | BufferUtil.writeUB3(buffer, calcPacketSize()); 81 | buffer.writeByte(packetId); 82 | BufferUtil.writeUB4(buffer, clientFlags); 83 | BufferUtil.writeUB4(buffer, maxPacketSize); 84 | buffer.writeByte((byte) charsetIndex); 85 | buffer.writeBytes(FILLER); 86 | if (user == null) { 87 | buffer.writeByte((byte) 0); 88 | } else { 89 | byte[] userData = user.getBytes(); 90 | BufferUtil.writeWithNull(buffer, userData); 91 | } 92 | if (password == null) { 93 | buffer.writeByte((byte) 0); 94 | } else { 95 | BufferUtil.writeWithLength(buffer, password); 96 | } 97 | if (database == null) { 98 | buffer.writeByte((byte) 0); 99 | } else { 100 | byte[] databaseData = database.getBytes(); 101 | BufferUtil.writeWithNull(buffer, databaseData); 102 | } 103 | ctx.writeAndFlush(buffer); 104 | } 105 | 106 | @Override 107 | public int calcPacketSize() { 108 | int size = 32;// 4+4+1+23; 109 | size += (user == null) ? 1 : user.length() + 1; 110 | size += (password == null) ? 1 : BufferUtil.getLength(password); 111 | size += (database == null) ? 1 : database.length() + 1; 112 | return size; 113 | } 114 | 115 | @Override 116 | protected String getPacketInfo() { 117 | return "MySQL Authentication Packet"; 118 | } 119 | } 120 | --------------------------------------------------------------------------------
12 | * server: 11110111 11111111 13 | * client_cmd: 11 10100110 10000101 14 | * client_jdbc:10 10100010 10001111 15 | * 16 | *