From 2e702db15dc80ce4e0565f92a95fac87e1a117ba Mon Sep 17 00:00:00 2001
From: lazyhwj <604993149@qq.com>
Date: Fri, 28 Jul 2023 14:19:42 +0800
Subject: [PATCH 1/3] =?UTF-8?q?=E5=8A=A0=E4=BA=86=E6=B3=A8=E9=87=8A?=
=?UTF-8?q?=E3=80=82=20=E5=AF=B9Where=E8=AF=AD=E5=8F=A5=E5=A2=9E=E5=8A=A0?=
=?UTF-8?q?=E4=BA=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E7=8E=B0=E5=9C=A8=E5=8F=AF?=
=?UTF-8?q?=E4=BB=A5=E4=BD=BF=E7=94=A8select=20*=20from=20table=20where=20?=
=?UTF-8?q?not=20id=3D10;=E8=BF=99=E7=A7=8D=E7=B1=BB=E5=9E=8B=E7=9A=84SQl?=
=?UTF-8?q?=E8=AF=AD=E5=8F=A5=E4=BA=86=E3=80=82=20=E5=90=8C=E6=97=B6?=
=?UTF-8?q?=E5=AF=B9Select=E8=AF=AD=E5=8F=A5=E5=8A=9F=E8=83=BD=E8=BF=9B?=
=?UTF-8?q?=E8=A1=8C=E4=BA=86=E4=BC=98=E5=8C=96=EF=BC=8C=E6=B2=A1=E6=94=B9?=
=?UTF-8?q?=E4=B9=8B=E5=89=8DSelect=20*=20=E5=92=8CSelect=20id=20=E6=9C=80?=
=?UTF-8?q?=E7=BB=88=E5=BE=97=E5=87=BA=E7=9A=84=E7=BB=93=E6=9E=9C=E9=83=BD?=
=?UTF-8?q?=E6=98=AFSelect=20*=E7=9A=84=E7=BB=93=E6=9E=9C=EF=BC=8C?=
=?UTF-8?q?=E4=B9=9F=E5=B0=B1=E6=98=AF=E6=B2=A1=E6=9C=89=E5=88=86=E7=B1=BB?=
=?UTF-8?q?=E8=AE=A8=E8=AE=BA=EF=BC=8C=E6=8A=8A=E5=85=A8=E9=83=A8=E5=AD=97?=
=?UTF-8?q?=E6=AE=B5=E7=9A=84=E5=80=BC=E9=83=BD=E8=AF=BB=E5=8F=96=E8=BF=9B?=
=?UTF-8?q?=E5=8E=BB=E4=BA=86=EF=BC=9B=E7=8E=B0=E5=9C=A8=E5=B0=B1=E6=98=AF?=
=?UTF-8?q?=E6=A0=B9=E6=8D=AEselect=E4=B8=ADfield=E7=9A=84=E5=80=BC?=
=?UTF-8?q?=E8=BF=9B=E8=A1=8C=E8=AF=BB=E5=8F=96=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 5 +-
.../top/guoziyang/mydb/backend/Launcher.java | 13 +-
.../mydb/backend/common/AbstractCache.java | 2 +-
.../mydb/backend/dm/DataManager.java | 13 +-
.../mydb/backend/dm/DataManagerImpl.java | 34 +++--
.../guoziyang/mydb/backend/dm/Recover.java | 56 +++++---
.../mydb/backend/dm/dataItem/DataItem.java | 8 +-
.../backend/dm/dataItem/DataItemImpl.java | 14 +-
.../mydb/backend/dm/logger/Logger.java | 13 +-
.../mydb/backend/dm/logger/LoggerImpl.java | 54 +++++---
.../guoziyang/mydb/backend/dm/page/Page.java | 8 +-
.../mydb/backend/dm/page/PageImpl.java | 11 +-
.../mydb/backend/dm/page/PageOne.java | 12 +-
.../guoziyang/mydb/backend/dm/page/PageX.java | 12 +-
.../mydb/backend/dm/pageCache/PageCache.java | 9 +-
.../backend/dm/pageCache/PageCacheImpl.java | 27 ++--
.../mydb/backend/dm/pageIndex/PageIndex.java | 16 ++-
.../mydb/backend/dm/pageIndex/PageInfo.java | 4 +-
.../guoziyang/mydb/backend/im/BPlusTree.java | 28 +++-
.../top/guoziyang/mydb/backend/im/Node.java | 82 +++++++-----
.../guoziyang/mydb/backend/parser/Parser.java | 126 +++++++++++-------
.../mydb/backend/parser/Tokenizer.java | 44 +++---
.../mydb/backend/parser/statement/Create.java | 2 +-
.../mydb/backend/parser/statement/Delete.java | 4 +-
.../mydb/backend/parser/statement/Insert.java | 4 +-
.../parser/statement/SingleExpression.java | 6 +-
.../mydb/backend/parser/statement/Update.java | 8 +-
.../mydb/backend/parser/statement/Where.java | 3 +-
.../mydb/backend/server/Executor.java | 6 +-
.../guoziyang/mydb/backend/server/Server.java | 7 +-
.../guoziyang/mydb/backend/tbm/Booter.java | 17 ++-
.../top/guoziyang/mydb/backend/tbm/Field.java | 29 ++--
.../top/guoziyang/mydb/backend/tbm/Table.java | 93 ++++++++++---
.../mydb/backend/tm/TransactionManager.java | 12 +-
.../backend/tm/TransactionManagerImpl.java | 9 +-
.../guoziyang/mydb/backend/utils/Types.java | 2 +
.../top/guoziyang/mydb/backend/vm/Entry.java | 25 ++--
.../guoziyang/mydb/backend/vm/LockTable.java | 37 ++++-
.../mydb/backend/vm/Transaction.java | 6 +-
.../mydb/backend/vm/VersionManagerImpl.java | 52 +++++---
.../guoziyang/mydb/backend/vm/Visibility.java | 26 +++-
.../top/guoziyang/mydb/client/Client.java | 2 +-
.../top/guoziyang/mydb/client/Launcher.java | 3 +-
.../guoziyang/mydb/client/RoundTripper.java | 4 +-
.../java/top/guoziyang/mydb/client/Shell.java | 2 +-
.../top/guoziyang/mydb/transport/Encoder.java | 9 +-
.../guoziyang/mydb/transport/Packager.java | 2 +-
.../guoziyang/mydb/transport/Transporter.java | 12 +-
48 files changed, 660 insertions(+), 313 deletions(-)
diff --git a/pom.xml b/pom.xml
index f16996e..d61767e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,10 +10,11 @@
UTF-8
- 11
- 11
+ 1.8
+ 1.8
+
junit
diff --git a/src/main/java/top/guoziyang/mydb/backend/Launcher.java b/src/main/java/top/guoziyang/mydb/backend/Launcher.java
index eb79e35..a0a19ad 100644
--- a/src/main/java/top/guoziyang/mydb/backend/Launcher.java
+++ b/src/main/java/top/guoziyang/mydb/backend/Launcher.java
@@ -14,7 +14,7 @@
import top.guoziyang.mydb.backend.vm.VersionManager;
import top.guoziyang.mydb.backend.vm.VersionManagerImpl;
import top.guoziyang.mydb.common.Error;
-
+//服务器的启动入口
public class Launcher {
public static final int port = 9999;
@@ -23,7 +23,8 @@ public class Launcher {
public static final long KB = 1 << 10;
public static final long MB = 1 << 20;
public static final long GB = 1 << 30;
-
+ //调用CommandLine来解析命令行的参数是open还是create
+ //以此来决定是打开数据库文件还是创建数据库文件
public static void main(String[] args) throws ParseException {
Options options = new Options();
options.addOption("open", true, "-open DBPath");
@@ -40,9 +41,10 @@ public static void main(String[] args) throws ParseException {
createDB(cmd.getOptionValue("create"));
return;
}
+ openDB("/tmp/mydb",DEFALUT_MEM);
System.out.println("Usage: launcher (open|create) DBPath");
}
-
+ //创建数据库文件
private static void createDB(String path) {
TransactionManager tm = TransactionManager.create(path);
DataManager dm = DataManager.create(path, DEFALUT_MEM, tm);
@@ -51,7 +53,7 @@ private static void createDB(String path) {
tm.close();
dm.close();
}
-
+ //打开数据库文件
private static void openDB(String path, long mem) {
TransactionManager tm = TransactionManager.open(path);
DataManager dm = DataManager.open(path, mem, tm);
@@ -59,7 +61,8 @@ private static void openDB(String path, long mem) {
TableManager tbm = TableManager.open(path, vm, dm);
new Server(port, tbm).start();
}
-
+ //根据给定的内存大小,创建指定大小的页内存
+ //就是指定数据库文件大小了
private static long parseMem(String memStr) {
if(memStr == null || "".equals(memStr)) {
return DEFALUT_MEM;
diff --git a/src/main/java/top/guoziyang/mydb/backend/common/AbstractCache.java b/src/main/java/top/guoziyang/mydb/backend/common/AbstractCache.java
index 5cd017b..625686e 100644
--- a/src/main/java/top/guoziyang/mydb/backend/common/AbstractCache.java
+++ b/src/main/java/top/guoziyang/mydb/backend/common/AbstractCache.java
@@ -26,7 +26,7 @@ public AbstractCache(int maxResource) {
getting = new HashMap<>();
lock = new ReentrantLock();
}
-
+ //读取资源
protected T get(long key) throws Exception {
while(true) {
lock.lock();
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/DataManager.java b/src/main/java/top/guoziyang/mydb/backend/dm/DataManager.java
index 7812e51..5c84eca 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/DataManager.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/DataManager.java
@@ -10,7 +10,10 @@ public interface DataManager {
DataItem read(long uid) throws Exception;
long insert(long xid, byte[] data) throws Exception;
void close();
-
+ //从空文件创建datamanager
+ //先创建pageCache,logger
+ //再创建dataManager
+ //然后初始化数据库的第一页
public static DataManager create(String path, long mem, TransactionManager tm) {
PageCache pc = PageCache.create(path, mem);
Logger lg = Logger.create(path);
@@ -19,11 +22,17 @@ public static DataManager create(String path, long mem, TransactionManager tm) {
dm.initPageOne();
return dm;
}
-
+ //从已有文件创建DataManager
+ //先检查数据文件的第一页是否正常
+ //不正常的话呢,就执行数据恢复流程
+ //正常的话,就先获取索引,把索引文件都填上
+ //重新设置第一页的随机字节
+ //将页面写进磁盘中
public static DataManager open(String path, long mem, TransactionManager tm) {
PageCache pc = PageCache.open(path, mem);
Logger lg = Logger.open(path);
DataManagerImpl dm = new DataManagerImpl(pc, lg, tm);
+ //在检查的时候,就把pageone传进来了
if(!dm.loadCheckPageOne()) {
Recover.recover(tm, lg, pc);
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/DataManagerImpl.java b/src/main/java/top/guoziyang/mydb/backend/dm/DataManagerImpl.java
index 6ff4509..ae12942 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/DataManagerImpl.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/DataManagerImpl.java
@@ -14,7 +14,7 @@
import top.guoziyang.mydb.backend.utils.Panic;
import top.guoziyang.mydb.backend.utils.Types;
import top.guoziyang.mydb.common.Error;
-
+//直接对外提供方法的类
public class DataManagerImpl extends AbstractCache implements DataManager {
TransactionManager tm;
@@ -30,7 +30,8 @@ public DataManagerImpl(PageCache pc, Logger logger, TransactionManager tm) {
this.tm = tm;
this.pIndex = new PageIndex();
}
-
+ //读取对应uid的数据
+ //检查该数据是否合法,不合法就把它释放掉
@Override
public DataItem read(long uid) throws Exception {
DataItemImpl di = (DataItemImpl)super.get(uid);
@@ -40,14 +41,18 @@ public DataItem read(long uid) throws Exception {
}
return di;
}
-
+ //先判断插入的数据,不能超过一个页面的大小
+ //接着在索引中查找可用页面,如果没有可用页面,那就创建一个新的数据页面
+ //然后在创建对应的日志记录,写进日志
+ //把数据写进文件
+ //最后把拿出的页面放回索引文件当中
@Override
public long insert(long xid, byte[] data) throws Exception {
byte[] raw = DataItem.wrapDataItemRaw(data);
if(raw.length > PageX.MAX_FREE_SPACE) {
throw Error.DataTooLargeException;
}
-
+ //在索引中找到对应的页面
PageInfo pi = null;
for(int i = 0; i < 5; i ++) {
pi = pIndex.select(raw.length);
@@ -61,16 +66,17 @@ public long insert(long xid, byte[] data) throws Exception {
if(pi == null) {
throw Error.DatabaseBusyException;
}
-
+ //创建日志
Page pg = null;
int freeSpace = 0;
try {
pg = pc.getPage(pi.pgno);
byte[] log = Recover.insertLog(xid, pg, raw);
+ //写入日志当中
logger.log(log);
-
+ //把数据写进文件当中
short offset = PageX.insert(pg, raw);
-
+ //释放页面
pg.release();
return Types.addressToUid(pi.pgno, offset);
@@ -83,7 +89,10 @@ public long insert(long xid, byte[] data) throws Exception {
}
}
}
-
+ //正常关闭datamanager时
+ //执行缓存,日志关闭流程
+ //设置第一页的关闭字节
+ //释放page
@Override
public void close() {
super.close();
@@ -99,11 +108,14 @@ public void logDataItem(long xid, DataItem di) {
byte[] log = Recover.updateLog(xid, di);
logger.log(log);
}
-
+ //使用完dataitem后,释放掉dataitem
public void releaseDataItem(DataItem di) {
super.release(di.getUid());
}
+ //这个uid,是由页号和页内偏移组成的一个8字节无符号整数,页号和偏移各占四字节
+ //这里就是把页号和页内偏移分别取出来
+ //有了页号和业内偏移,就能把对应的dataitem取出来了
@Override
protected DataItem getForCache(long uid) throws Exception {
short offset = (short)(uid & ((1L << 16) - 1));
@@ -112,7 +124,7 @@ protected DataItem getForCache(long uid) throws Exception {
Page pg = pc.getPage(pgno);
return DataItem.parseDataItem(pg, offset, this);
}
-
+ //将dataitem所在的页释放
@Override
protected void releaseForCache(DataItem di) {
di.page().release();
@@ -141,6 +153,8 @@ boolean loadCheckPageOne() {
}
// 初始化pageIndex
+ //获取所有页面,加入页面索引当中
+ //释放获取的页面
void fillPageIndex() {
int pageNumber = pc.getPageNumber();
for(int i = 2; i <= pageNumber; i ++) {
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/Recover.java b/src/main/java/top/guoziyang/mydb/backend/dm/Recover.java
index 42fc076..cfbb132 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/Recover.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/Recover.java
@@ -26,14 +26,15 @@ public class Recover {
private static final int REDO = 0;
private static final int UNDO = 1;
-
+ //定义日志的格式
+ //insertlog的格式
static class InsertLogInfo {
long xid;
- int pgno;
- short offset;
+ int pgno;//修改的页数
+ short offset;//修改位置在页面的偏移
byte[] raw;
}
-
+ //updatelog的格式
static class UpdateLogInfo {
long xid;
int pgno;
@@ -41,10 +42,10 @@ static class UpdateLogInfo {
byte[] oldRaw;
byte[] newRaw;
}
-
+ //启动恢复数据
public static void recover(TransactionManager tm, Logger lg, PageCache pc) {
System.out.println("Recovering...");
-
+ //从日志文件头开始读
lg.rewind();
int maxPgno = 0;
while(true) {
@@ -65,23 +66,28 @@ public static void recover(TransactionManager tm, Logger lg, PageCache pc) {
if(maxPgno == 0) {
maxPgno = 1;
}
+ //截断日志文件
pc.truncateByBgno(maxPgno);
System.out.println("Truncate to " + maxPgno + " pages.");
-
+ //对事务进行重做
redoTranscations(tm, lg, pc);
System.out.println("Redo Transactions Over.");
-
+ //对事务进行撤销
undoTranscations(tm, lg, pc);
System.out.println("Undo Transactions Over.");
System.out.println("Recovery Over.");
}
-
+ //对事务进行重做
private static void redoTranscations(TransactionManager tm, Logger lg, PageCache pc) {
+ //从日志的头部开始读起
lg.rewind();
while(true) {
byte[] log = lg.next();
if(log == null) break;
+ //如果日志记录是插入日志
+ //并且所属的事务状态不是活跃
+ //那么就重做该插入日志
if(isInsertLog(log)) {
InsertLogInfo li = parseInsertLog(log);
long xid = li.xid;
@@ -89,6 +95,8 @@ private static void redoTranscations(TransactionManager tm, Logger lg, PageCache
doInsertLog(pc, log, REDO);
}
} else {
+ //如果是update事件,那么同样也去检查其事务所属的状态
+ //不是活跃的状态就可以重新执行
UpdateLogInfo xi = parseUpdateLog(log);
long xid = xi.xid;
if(!tm.isActive(xid)) {
@@ -97,13 +105,14 @@ private static void redoTranscations(TransactionManager tm, Logger lg, PageCache
}
}
}
-
+ //撤销事务
private static void undoTranscations(TransactionManager tm, Logger lg, PageCache pc) {
Map> logCache = new HashMap<>();
lg.rewind();
while(true) {
byte[] log = lg.next();
if(log == null) break;
+ //将读取到的日志,按照时间顺序,以事务为组放进map当中
if(isInsertLog(log)) {
InsertLogInfo li = parseInsertLog(log);
long xid = li.xid;
@@ -126,6 +135,8 @@ private static void undoTranscations(TransactionManager tm, Logger lg, PageCache
}
// 对所有active log进行倒序undo
+ //把每个事务从键值里去取出来
+ //然后把事务里面的日志记录,按照倒序的方式一个一个撤销
for(Entry> entry : logCache.entrySet()) {
List logs = entry.getValue();
for (int i = logs.size()-1; i >= 0; i --) {
@@ -136,10 +147,12 @@ private static void undoTranscations(TransactionManager tm, Logger lg, PageCache
doUpdateLog(pc, log, UNDO);
}
}
+ //撤销完一个事务,就将事务的状态设置为aborted
tm.abort(entry.getKey());
}
}
-
+ //检查日志记录属于什么类型
+ //查看日志记录数据里的标志位是什么
private static boolean isInsertLog(byte[] log) {
return log[0] == LOG_TYPE_INSERT;
}
@@ -149,7 +162,8 @@ private static boolean isInsertLog(byte[] log) {
private static final int OF_XID = OF_TYPE+1;
private static final int OF_UPDATE_UID = OF_XID+8;
private static final int OF_UPDATE_RAW = OF_UPDATE_UID+8;
-
+ //定义updatelog的raw字节数据的结构
+ //也就是实际上写在日志上的日志数据的结构
public static byte[] updateLog(long xid, DataItem di) {
byte[] logType = {LOG_TYPE_UPDATE};
byte[] xidRaw = Parser.long2Byte(xid);
@@ -159,7 +173,9 @@ public static byte[] updateLog(long xid, DataItem di) {
byte[] newRaw = Arrays.copyOfRange(raw.raw, raw.start, raw.end);
return Bytes.concat(logType, xidRaw, uidRaw, oldRaw, newRaw);
}
-
+ //解析updatelog
+ //就是把里面的logtype,xid,pgno,offset给解析出来
+ //根据uid可以吧,pgno,offset给解析出来
private static UpdateLogInfo parseUpdateLog(byte[] log) {
UpdateLogInfo li = new UpdateLogInfo();
li.xid = Parser.parseLong(Arrays.copyOfRange(log, OF_XID, OF_UPDATE_UID));
@@ -167,16 +183,18 @@ private static UpdateLogInfo parseUpdateLog(byte[] log) {
li.offset = (short)(uid & ((1L << 16) - 1));
uid >>>= 32;
li.pgno = (int)(uid & ((1L << 32) - 1));
+ //取出oldraw和newraw
int length = (log.length - OF_UPDATE_RAW) / 2;
li.oldRaw = Arrays.copyOfRange(log, OF_UPDATE_RAW, OF_UPDATE_RAW+length);
li.newRaw = Arrays.copyOfRange(log, OF_UPDATE_RAW+length, OF_UPDATE_RAW+length*2);
return li;
}
-
+ //根据日志记录去更新操作
private static void doUpdateLog(PageCache pc, byte[] log, int flag) {
int pgno;
short offset;
byte[] raw;
+ //根据标志位,对raw值选择赋上old值还是new值
if(flag == REDO) {
UpdateLogInfo xi = parseUpdateLog(log);
pgno = xi.pgno;
@@ -205,7 +223,7 @@ private static void doUpdateLog(PageCache pc, byte[] log, int flag) {
private static final int OF_INSERT_PGNO = OF_XID+8;
private static final int OF_INSERT_OFFSET = OF_INSERT_PGNO+4;
private static final int OF_INSERT_RAW = OF_INSERT_OFFSET+2;
-
+ //真实写在日志当中的insertlog的结构
public static byte[] insertLog(long xid, Page pg, byte[] raw) {
byte[] logTypeRaw = {LOG_TYPE_INSERT};
byte[] xidRaw = Parser.long2Byte(xid);
@@ -213,7 +231,8 @@ public static byte[] insertLog(long xid, Page pg, byte[] raw) {
byte[] offsetRaw = Parser.short2Byte(PageX.getFSO(pg));
return Bytes.concat(logTypeRaw, xidRaw, pgnoRaw, offsetRaw, raw);
}
-
+ //将insertlog解析成insertLogInfo
+ //获取里面的xid,pgnp,offset,raw
private static InsertLogInfo parseInsertLog(byte[] log) {
InsertLogInfo li = new InsertLogInfo();
li.xid = Parser.parseLong(Arrays.copyOfRange(log, OF_XID, OF_INSERT_PGNO));
@@ -222,7 +241,7 @@ private static InsertLogInfo parseInsertLog(byte[] log) {
li.raw = Arrays.copyOfRange(log, OF_INSERT_RAW, log.length);
return li;
}
-
+ //根据日志去执行插入操作
private static void doInsertLog(PageCache pc, byte[] log, int flag) {
InsertLogInfo li = parseInsertLog(log);
Page pg = null;
@@ -233,10 +252,13 @@ private static void doInsertLog(PageCache pc, byte[] log, int flag) {
}
try {
if(flag == UNDO) {
+ //将dataitem的标志位设为无效
DataItem.setDataItemRawInvalid(li.raw);
}
+ //将数据插入进去,指定位置
PageX.recoverInsert(pg, li.raw, li.offset);
} finally {
+ //释放该页面
pg.release();
}
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItem.java b/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItem.java
index ee52ccd..f31d1a9 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItem.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItem.java
@@ -9,7 +9,7 @@
import top.guoziyang.mydb.backend.dm.page.Page;
import top.guoziyang.mydb.backend.utils.Parser;
import top.guoziyang.mydb.backend.utils.Types;
-
+//定义实际数据的结构
public interface DataItem {
SubArray data();
@@ -27,7 +27,7 @@ public interface DataItem {
long getUid();
byte[] getOldRaw();
SubArray getRaw();
-
+ //将数组转变成dataitem的格式
public static byte[] wrapDataItemRaw(byte[] raw) {
byte[] valid = new byte[1];
byte[] size = Parser.short2Byte((short)raw.length);
@@ -35,10 +35,14 @@ public static byte[] wrapDataItemRaw(byte[] raw) {
}
// 从页面的offset处解析处dataitem
+ //
public static DataItem parseDataItem(Page pg, short offset, DataManagerImpl dm) {
byte[] raw = pg.getData();
+ //获得这段数据的大小
short size = Parser.parseShort(Arrays.copyOfRange(raw, offset+DataItemImpl.OF_SIZE, offset+DataItemImpl.OF_DATA));
+ //得到这段数据的长度
short length = (short)(size + DataItemImpl.OF_DATA);
+ //获取这段数据的uid
long uid = Types.addressToUid(pg.getPageNumber(), offset);
return new DataItemImpl(new SubArray(raw, offset, offset+length), new byte[length], pg, uid, dm);
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItemImpl.java b/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItemImpl.java
index 2163148..5ad90c2 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItemImpl.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/dataItem/DataItemImpl.java
@@ -14,6 +14,7 @@
* ValidFlag 1字节,0为合法,1为非法
* DataSize 2字节,标识Data的长度
*/
+//处理dataitem
public class DataItemImpl implements DataItem {
static final int OF_VALID = 0;
@@ -38,29 +39,36 @@ public DataItemImpl(SubArray raw, byte[] oldRaw, Page pg, long uid, DataManagerI
this.uid = uid;
this.pg = pg;
}
-
+ //查看该dataitem是否为合法数据
+ //检查其是否能被访问
public boolean isValid() {
return raw.raw[raw.start+OF_VALID] == (byte)0;
}
+ //上层模块在获得了dataitem后
+ //使用data方法返回的数组是共享的,而非拷贝的实现的
+ //上层模块看到就是dataitem里面的数据
@Override
public SubArray data() {
return new SubArray(raw.raw, raw.start+OF_DATA, raw.end);
}
+ //在修改之前要保存目前的值
+ //把raw数组目前的值复制到oldraw数组里面
@Override
public void before() {
wLock.lock();
pg.setDirty(true);
System.arraycopy(raw.raw, raw.start, oldRaw, 0, oldRaw.length);
}
-
+ //想要撤销修改
+ //把oldraw数组里的值复制到raw数组里面
@Override
public void unBefore() {
System.arraycopy(oldRaw, 0, raw.raw, raw.start, oldRaw.length);
wLock.unlock();
}
-
+ //为该事务生成日志
@Override
public void after(long xid) {
dm.logDataItem(xid, this);
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/logger/Logger.java b/src/main/java/top/guoziyang/mydb/backend/dm/logger/Logger.java
index 48eb7ac..ac6af95 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/logger/Logger.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/logger/Logger.java
@@ -17,7 +17,9 @@ public interface Logger {
byte[] next();
void rewind();
void close();
-
+ //创建一个log文件
+ //生成文件流和文件通道去获取这个文件
+ //
public static Logger create(String path) {
File f = new File(path+LoggerImpl.LOG_SUFFIX);
try {
@@ -39,11 +41,14 @@ public static Logger create(String path) {
} catch (FileNotFoundException e) {
Panic.panic(e);
}
-
+ //把0写进去,是为了标志日志的初始状态
+ //因为日志的格式包括xchecksum,所以初始化的时候
+ //要将xchecksum初始化为0
ByteBuffer buf = ByteBuffer.wrap(Parser.int2Byte(0));
try {
fc.position(0);
fc.write(buf);
+ //metadata代表文件的权限,创建时间,修改时间。
fc.force(false);
} catch (IOException e) {
Panic.panic(e);
@@ -51,7 +56,9 @@ public static Logger create(String path) {
return new LoggerImpl(raf, fc, 0);
}
-
+ //通过打开日志文件的方式,来创建一个logger
+ //这里就不需要给日志文件赋初值了
+ //直接创建文件流,通道读取文件就行了。
public static Logger open(String path) {
File f = new File(path+LoggerImpl.LOG_SUFFIX);
if(!f.exists()) {
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/logger/LoggerImpl.java b/src/main/java/top/guoziyang/mydb/backend/dm/logger/LoggerImpl.java
index 1482834..5243488 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/logger/LoggerImpl.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/logger/LoggerImpl.java
@@ -30,9 +30,9 @@ public class LoggerImpl implements Logger {
private static final int SEED = 13331;
- private static final int OF_SIZE = 0;
- private static final int OF_CHECKSUM = OF_SIZE + 4;
- private static final int OF_DATA = OF_CHECKSUM + 4;
+ private static final int OF_SIZE = 0;//获取这条日志文件的大小
+ private static final int OF_CHECKSUM = OF_SIZE + 4;//获取这条日志文件的校验和
+ private static final int OF_DATA = OF_CHECKSUM + 4;//获取这条日志文件的数据
public static final String LOG_SUFFIX = ".log";
@@ -43,20 +43,25 @@ public class LoggerImpl implements Logger {
private long position; // 当前日志指针的位置
private long fileSize; // 初始化时记录,log操作不更新
private int xChecksum;
-
+ //已经存在日志文件的情况下
+ //只需要传入文件就行了
LoggerImpl(RandomAccessFile raf, FileChannel fc) {
this.file = raf;
this.fc = fc;
lock = new ReentrantLock();
}
-
+ //没有日志文件,需要去创建日志文件
+ //还需要传入checksum,初始化
LoggerImpl(RandomAccessFile raf, FileChannel fc, int xChecksum) {
this.file = raf;
this.fc = fc;
this.xChecksum = xChecksum;
lock = new ReentrantLock();
}
-
+ //初始化logger
+ //检查文件是否是合法的log文件
+ //去读取日志文件的校验和
+ //检查校验和是否正确,并且删除bad-tail(日志记录末尾不完整的log)
void init() {
long size = 0;
try {
@@ -108,14 +113,18 @@ private void checkAndRemoveTail() {
}
rewind();
}
-
+ //计算校验和
+ //对日志的每个字节遍历,并将每个每个字节与当前校验值相乘,并加上当前字节值
+ //可以在当前操作中验证操作完整性
private int calChecksum(int xCheck, byte[] log) {
for (byte b : log) {
xCheck = xCheck * SEED + b;
}
return xCheck;
}
-
+ //写入新的日志
+ //将数据转化为日志格式
+ //将数据写入文件,更新校验和
@Override
public void log(byte[] data) {
byte[] log = wrapLog(data);
@@ -131,7 +140,9 @@ public void log(byte[] data) {
}
updateXChecksum(log);
}
-
+ //更新校验和
+ //计算新的校验值,传入当前的校验值和新的日志
+ //把新的校验和写入文件,刷入磁盘
private void updateXChecksum(byte[] log) {
this.xChecksum = calChecksum(this.xChecksum, log);
try {
@@ -142,13 +153,17 @@ private void updateXChecksum(byte[] log) {
Panic.panic(e);
}
}
-
+ //把字节数组转化为日志格式
+ //根据字节数组,算出当前的校验和
+ //算出数据的size
+ //拼接成一个数组
private byte[] wrapLog(byte[] data) {
byte[] checksum = Parser.int2Byte(calChecksum(0, data));
byte[] size = Parser.int2Byte(data.length);
return Bytes.concat(size, checksum, data);
}
-
+ //截断文件
+ //调用文件流的截断
@Override
public void truncate(long x) throws Exception {
lock.lock();
@@ -158,11 +173,14 @@ public void truncate(long x) throws Exception {
lock.unlock();
}
}
-
+ //读取下一个日志文件
private byte[] internNext() {
+ //前面的判断都是检查日志是否完整,不完整就不要
+ //检查是否还有log,就是看日志校验和+日志大小的记录是否会超过文件大小
if(position + OF_DATA >= fileSize) {
return null;
}
+ //读取日志文件大小
ByteBuffer tmp = ByteBuffer.allocate(4);
try {
fc.position(position);
@@ -170,11 +188,12 @@ private byte[] internNext() {
} catch(IOException e) {
Panic.panic(e);
}
+ //看这个日志记录是否会超过文件大小
int size = Parser.parseInt(tmp.array());
if(position + size + OF_DATA > fileSize) {
return null;
}
-
+ //现在开始读取整个日志记录
ByteBuffer buf = ByteBuffer.allocate(OF_DATA + size);
try {
fc.position(position);
@@ -182,7 +201,7 @@ private byte[] internNext() {
} catch(IOException e) {
Panic.panic(e);
}
-
+ //保存在字节数组中,检查该日志是否被修改
byte[] log = buf.array();
int checkSum1 = calChecksum(0, Arrays.copyOfRange(log, OF_DATA, log.length));
int checkSum2 = Parser.parseInt(Arrays.copyOfRange(log, OF_CHECKSUM, OF_DATA));
@@ -192,7 +211,7 @@ private byte[] internNext() {
position += log.length;
return log;
}
-
+ //读取日志记录
@Override
public byte[] next() {
lock.lock();
@@ -204,12 +223,13 @@ public byte[] next() {
lock.unlock();
}
}
-
+ //让position位置回到开头的位置,在校验和之后
@Override
public void rewind() {
position = 4;
}
-
+ //关闭日志
+ //关闭文件流,关闭文件
@Override
public void close() {
try {
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/page/Page.java b/src/main/java/top/guoziyang/mydb/backend/dm/page/Page.java
index 911f33a..b76da6e 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/page/Page.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/page/Page.java
@@ -1,11 +1,11 @@
package top.guoziyang.mydb.backend.dm.page;
-
+//管理page里面的具体
public interface Page {
void lock();
void unlock();
void release();
void setDirty(boolean dirty);
- boolean isDirty();
- int getPageNumber();
- byte[] getData();
+ boolean isDirty();//定义脏页
+ int getPageNumber();//获得页号
+ byte[] getData();//获得页面数据
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/page/PageImpl.java b/src/main/java/top/guoziyang/mydb/backend/dm/page/PageImpl.java
index c0c968c..a2c51b7 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/page/PageImpl.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/page/PageImpl.java
@@ -4,14 +4,15 @@
import java.util.concurrent.locks.ReentrantLock;
import top.guoziyang.mydb.backend.dm.pageCache.PageCache;
-
+//对页面的数据进行管理
+//页号啊,字节数据,脏页标志位
public class PageImpl implements Page {
- private int pageNumber;
- private byte[] data;
- private boolean dirty;
+ private int pageNumber;//该页面的页号
+ private byte[] data;//页面实际包含的字节数据
+ private boolean dirty;//脏页,需要被写会磁盘当中
private Lock lock;
- private PageCache pc;
+ private PageCache pc;//方便在拿到page引用时,可以快速对页面进行释放操作
public PageImpl(int pageNumber, byte[] data, PageCache pc) {
this.pageNumber = pageNumber;
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/page/PageOne.java b/src/main/java/top/guoziyang/mydb/backend/dm/page/PageOne.java
index e425a8f..f8883df 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/page/PageOne.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/page/PageOne.java
@@ -20,29 +20,29 @@ public static byte[] InitRaw() {
setVcOpen(raw);
return raw;
}
-
+ //设置文件启动时的字节位
public static void setVcOpen(Page pg) {
pg.setDirty(true);
setVcOpen(pg.getData());
}
-
+ //修改文件中的第100-107位的数据
private static void setVcOpen(byte[] raw) {
System.arraycopy(RandomUtil.randomBytes(LEN_VC), 0, raw, OF_VC, LEN_VC);
}
-
+ //设置页面关闭时的字节位
public static void setVcClose(Page pg) {
pg.setDirty(true);
setVcClose(pg.getData());
}
-
+ //修改文件中第108-115的位置
private static void setVcClose(byte[] raw) {
System.arraycopy(raw, OF_VC, raw, OF_VC+LEN_VC, LEN_VC);
}
-
+ //检查数据库是否正确关闭
public static boolean checkVc(Page pg) {
return checkVc(pg.getData());
}
-
+ //检查两处字节是否相同
private static boolean checkVc(byte[] raw) {
return Arrays.equals(Arrays.copyOfRange(raw, OF_VC, OF_VC+LEN_VC), Arrays.copyOfRange(raw, OF_VC+LEN_VC, OF_VC+2*LEN_VC));
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/page/PageX.java b/src/main/java/top/guoziyang/mydb/backend/dm/page/PageX.java
index 99f37c3..cf32ab1 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/page/PageX.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/page/PageX.java
@@ -16,13 +16,14 @@ public class PageX {
private static final short OF_FREE = 0;
private static final short OF_DATA = 2;
public static final int MAX_FREE_SPACE = PageCache.PAGE_SIZE - OF_DATA;
-
+ //初始化一张普通页
public static byte[] initRaw() {
byte[] raw = new byte[PageCache.PAGE_SIZE];
setFSO(raw, OF_DATA);
return raw;
}
-
+ //初始化页面的结构
+ //设置空闲位置偏移
private static void setFSO(byte[] raw, short ofData) {
System.arraycopy(Parser.short2Byte(ofData), 0, raw, OF_FREE, OF_DATA);
}
@@ -31,12 +32,12 @@ private static void setFSO(byte[] raw, short ofData) {
public static short getFSO(Page pg) {
return getFSO(pg.getData());
}
-
+ //获取page的空闲位置偏移
private static short getFSO(byte[] raw) {
return Parser.parseShort(Arrays.copyOfRange(raw, 0, 2));
}
- // 将raw插入pg中,返回插入位置
+ // 将raw插入pg中,返回一开始插入位置
public static short insert(Page pg, byte[] raw) {
pg.setDirty(true);
short offset = getFSO(pg.getData());
@@ -51,10 +52,11 @@ public static int getFreeSpace(Page pg) {
}
// 将raw插入pg中的offset位置,并将pg的offset设置为较大的offset
+ //不知道为什么要去判断offset
public static void recoverInsert(Page pg, byte[] raw, short offset) {
pg.setDirty(true);
System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
-
+ //判断页面空余空间够不够插入一条数据
short rawFSO = getFSO(pg.getData());
if(rawFSO < offset + raw.length) {
setFSO(pg.getData(), (short)(offset+raw.length));
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCache.java b/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCache.java
index 10190a5..619b54b 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCache.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCache.java
@@ -8,7 +8,7 @@
import top.guoziyang.mydb.backend.dm.page.Page;
import top.guoziyang.mydb.backend.utils.Panic;
import top.guoziyang.mydb.common.Error;
-
+//用来管理page
public interface PageCache {
public static final int PAGE_SIZE = 1 << 13;
@@ -21,7 +21,9 @@ public interface PageCache {
void truncateByBgno(int maxPgno);
int getPageNumber();
void flushPage(Page pg);
-
+ //使用创建数据库文件的方式打开pagecache
+ //读取数据库文件
+ //获取对应的文件流,通道和缓存页面的张数
public static PageCacheImpl create(String path, long memory) {
File f = new File(path+PageCacheImpl.DB_SUFFIX);
try {
@@ -43,9 +45,10 @@ public static PageCacheImpl create(String path, long memory) {
} catch (FileNotFoundException e) {
Panic.panic(e);
}
+ //根据传入的可用内存大小,来确定内存中可以缓存几张页面
return new PageCacheImpl(raf, fc, (int)memory/PAGE_SIZE);
}
-
+ //使用打开数据库文件的方式,来创建一个pagecache对象
public static PageCacheImpl open(String path, long memory) {
File f = new File(path+PageCacheImpl.DB_SUFFIX);
if(!f.exists()) {
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCacheImpl.java b/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCacheImpl.java
index 9e8a836..cd3adee 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCacheImpl.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/pageCache/PageCacheImpl.java
@@ -24,7 +24,8 @@ public class PageCacheImpl extends AbstractCache implements PageCache {
private Lock fileLock;
private AtomicInteger pageNumbers;
-
+ //初始化一个PageCacheImpl对象
+ //如果提供的可缓存页面张数小于最低值,就抛出异常
PageCacheImpl(RandomAccessFile file, FileChannel fileChannel, int maxResource) {
super(maxResource);
if(maxResource < MEM_MIN_LIM) {
@@ -39,16 +40,18 @@ public class PageCacheImpl extends AbstractCache implements PageCache {
this.file = file;
this.fc = fileChannel;
this.fileLock = new ReentrantLock();
+ //打开数据库文件时,就会确定数据库文件的张数
this.pageNumbers = new AtomicInteger((int)length / PAGE_SIZE);
}
-
+ //创建一个新的页面,返回他的页面号码
+ //将新页面写进磁盘当中
public int newPage(byte[] initData) {
int pgno = pageNumbers.incrementAndGet();
Page pg = new PageImpl(pgno, initData, null);
flush(pg);
return pgno;
}
-
+ //根据页面号获得对应的页面
public Page getPage(int pgno) throws Exception {
return get((long)pgno);
}
@@ -59,6 +62,7 @@ public Page getPage(int pgno) throws Exception {
@Override
protected Page getForCache(long key) throws Exception {
int pgno = (int)key;
+ //获取页面的偏移
long offset = PageCacheImpl.pageOffset(pgno);
ByteBuffer buf = ByteBuffer.allocate(PAGE_SIZE);
@@ -88,7 +92,10 @@ public void release(Page page) {
public void flushPage(Page pg) {
flush(pg);
}
-
+ //把该页面写进磁盘中
+ //获取页的页面号,该页面在文件中的偏移
+ //把页面的数据写进文件中指定的位置
+ //把缓冲区的数据刷新至硬盘中
private void flush(Page pg) {
int pgno = pg.getPageNumber();
long offset = pageOffset(pgno);
@@ -105,7 +112,9 @@ private void flush(Page pg) {
fileLock.unlock();
}
}
-
+ //截断页面
+ //将数据库里的文件也截断
+ //设置pagenumber
public void truncateByBgno(int maxPgno) {
long size = pageOffset(maxPgno + 1);
try {
@@ -115,7 +124,9 @@ public void truncateByBgno(int maxPgno) {
}
pageNumbers.set(maxPgno);
}
-
+ //关闭pagecacheimpl
+ //关闭缓存,写回全部的资源
+ //关闭文件流,关闭通道
@Override
public void close() {
super.close();
@@ -126,11 +137,11 @@ public void close() {
Panic.panic(e);
}
}
-
+ //获取数据库文件的最大页面
public int getPageNumber() {
return pageNumbers.intValue();
}
-
+ //获取当前页面在文件中的偏移
private static long pageOffset(int pgno) {
return (pgno-1) * PAGE_SIZE;
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageIndex.java b/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageIndex.java
index f62f2ff..3e2eb77 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageIndex.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageIndex.java
@@ -10,12 +10,16 @@
public class PageIndex {
// 将一页划成40个区间
private static final int INTERVALS_NO = 40;
+ //一个区间的大小
private static final int THRESHOLD = PageCache.PAGE_SIZE / INTERVALS_NO;
private Lock lock;
+ //每个区间里面的页面信息
private List[] lists;
@SuppressWarnings("unchecked")
+ //创建一个互斥锁
+ //初始化每个区间里面的页面信息
public PageIndex() {
lock = new ReentrantLock();
lists = new List[INTERVALS_NO+1];
@@ -23,7 +27,10 @@ public PageIndex() {
lists[i] = new ArrayList<>();
}
}
-
+ //将页面分配在索引区间的依据是:页面的空余空间
+ //向页面索引当中添加页面信息
+ //根据freespace,获取要添加到的区间
+ //然后对应的区间添加页面信息
public void add(int pgno, int freeSpace) {
lock.lock();
try {
@@ -33,7 +40,12 @@ public void add(int pgno, int freeSpace) {
lock.unlock();
}
}
-
+ //首先用spacesize/区间大小,这个应该去哪个区间里面找
+ //将number向上取整
+ //然后看对应区间里的列表是否存有数据
+ //没有的话就看下一个区间的,有的话就取出列表里的第一个,并且从列表中移除
+ //被选择的页,会被pageindex移除,同一个页面是不允许并发写的
+ //上层模块在添加完之后,要重新把这一个页面加回来
public PageInfo select(int spaceSize) {
lock.lock();
try {
diff --git a/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageInfo.java b/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageInfo.java
index c53842d..150079e 100644
--- a/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageInfo.java
+++ b/src/main/java/top/guoziyang/mydb/backend/dm/pageIndex/PageInfo.java
@@ -1,5 +1,7 @@
package top.guoziyang.mydb.backend.dm.pageIndex;
-
+//索引里包含的页面信息
+//页面号
+//页面空余空间
public class PageInfo {
public int pgno;
public int freeSpace;
diff --git a/src/main/java/top/guoziyang/mydb/backend/im/BPlusTree.java b/src/main/java/top/guoziyang/mydb/backend/im/BPlusTree.java
index c1354f5..6c5b617 100644
--- a/src/main/java/top/guoziyang/mydb/backend/im/BPlusTree.java
+++ b/src/main/java/top/guoziyang/mydb/backend/im/BPlusTree.java
@@ -20,13 +20,19 @@ public class BPlusTree {
long bootUid;
DataItem bootDataItem;
Lock bootLock;
-
+ //建立根节点
+ //将一个字段设置为索引时会用到的
public static long create(DataManager dm) throws Exception {
+ //先构建一个空节点的原始数据
byte[] rawRoot = Node.newNilRootRaw();
+ //将这个节点插入到文件当中,也就是说,这就是索引文件的数据
+ //返回这个根节点数据的地址
long rootUid = dm.insert(TransactionManagerImpl.SUPER_XID, rawRoot);
+ //把事务id,根节点的地址,插入文件
+ //这才是索引文件
return dm.insert(TransactionManagerImpl.SUPER_XID, Parser.long2Byte(rootUid));
}
-
+ //读取节点的数据值,创建一个b+树对象
public static BPlusTree load(long bootUid, DataManager dm) throws Exception {
DataItem bootDataItem = dm.read(bootUid);
assert bootDataItem != null;
@@ -37,7 +43,7 @@ public static BPlusTree load(long bootUid, DataManager dm) throws Exception {
t.bootLock = new ReentrantLock();
return t;
}
-
+ //获得根节点的uid
private long rootUid() {
bootLock.lock();
try {
@@ -47,11 +53,13 @@ private long rootUid() {
bootLock.unlock();
}
}
-
+ //更新根节点的uid值
private void updateRootUid(long left, long right, long rightKey) throws Exception {
bootLock.lock();
try {
+ //创建一个新的根节点的值
byte[] rootRaw = Node.newRootRaw(left, right, rightKey);
+ //先把这个根节点的值插入文件当中
long newRootUid = dm.insert(TransactionManagerImpl.SUPER_XID, rootRaw);
bootDataItem.before();
SubArray diRaw = bootDataItem.data();
@@ -106,8 +114,9 @@ public List searchRange(long leftKey, long rightKey) throws Exception {
}
return uids;
}
-
+ //将新的key插入到b+树中
public void insert(long key, long uid) throws Exception {
+ //获得该索引树的根节点
long rootUid = rootUid();
InsertRes res = insert(rootUid, uid, key);
assert res != null;
@@ -115,18 +124,23 @@ public void insert(long key, long uid) throws Exception {
updateRootUid(rootUid, res.newNode, res.newKey);
}
}
-
+ //插入新节点时用到的
+ //新的节点和新的key
class InsertRes {
long newNode, newKey;
}
private InsertRes insert(long nodeUid, long uid, long key) throws Exception {
+ //加载该索引树的根节点
Node node = Node.loadNode(this, nodeUid);
+ //判断该节点是不是叶子节点
boolean isLeaf = node.isLeaf();
+ //释放该节点
node.release();
InsertRes res = null;
if(isLeaf) {
+ //如果是叶子结点的话
res = insertAndSplit(nodeUid, uid, key);
} else {
long next = searchNext(nodeUid, key);
@@ -139,7 +153,7 @@ private InsertRes insert(long nodeUid, long uid, long key) throws Exception {
}
return res;
}
-
+ //插入节点并且分裂
private InsertRes insertAndSplit(long nodeUid, long uid, long key) throws Exception {
while(true) {
Node node = Node.loadNode(this, nodeUid);
diff --git a/src/main/java/top/guoziyang/mydb/backend/im/Node.java b/src/main/java/top/guoziyang/mydb/backend/im/Node.java
index 847db4a..7dabc52 100644
--- a/src/main/java/top/guoziyang/mydb/backend/im/Node.java
+++ b/src/main/java/top/guoziyang/mydb/backend/im/Node.java
@@ -15,19 +15,21 @@
* [Son0][Key0][Son1][Key1]...[SonN][KeyN]
*/
public class Node {
- static final int IS_LEAF_OFFSET = 0;
- static final int NO_KEYS_OFFSET = IS_LEAF_OFFSET+1;
- static final int SIBLING_OFFSET = NO_KEYS_OFFSET+2;
- static final int NODE_HEADER_SIZE = SIBLING_OFFSET+8;
+ static final int IS_LEAF_OFFSET = 0;//标记该节点是否是个叶子节点
+ static final int NO_KEYS_OFFSET = IS_LEAF_OFFSET+1;//当前节点key的个数
+ static final int SIBLING_OFFSET = NO_KEYS_OFFSET+2;//兄弟节点的uid
+ static final int NODE_HEADER_SIZE = SIBLING_OFFSET+8;//
static final int BALANCE_NUMBER = 32;
+ //求节点的大小,头部大小加上键值对大小*键值对个数+左右子节点信息
+ //我觉得balance_number不用*2
static final int NODE_SIZE = NODE_HEADER_SIZE + (2*8)*(BALANCE_NUMBER*2+2);
BPlusTree tree;
DataItem dataItem;
SubArray raw;
long uid;
-
+ //设置节点的叶子节点标志位
static void setRawIsLeaf(SubArray raw, boolean isLeaf) {
if(isLeaf) {
raw.raw[raw.start + IS_LEAF_OFFSET] = (byte)1;
@@ -35,52 +37,53 @@ static void setRawIsLeaf(SubArray raw, boolean isLeaf) {
raw.raw[raw.start + IS_LEAF_OFFSET] = (byte)0;
}
}
-
+ //获得节点的标志位
static boolean getRawIfLeaf(SubArray raw) {
return raw.raw[raw.start + IS_LEAF_OFFSET] == (byte)1;
}
-
+ //设置当前节点的key个数
static void setRawNoKeys(SubArray raw, int noKeys) {
System.arraycopy(Parser.short2Byte((short)noKeys), 0, raw.raw, raw.start+NO_KEYS_OFFSET, 2);
}
-
+ //获得当前节点的key个数
static int getRawNoKeys(SubArray raw) {
return (int)Parser.parseShort(Arrays.copyOfRange(raw.raw, raw.start+NO_KEYS_OFFSET, raw.start+NO_KEYS_OFFSET+2));
}
-
+ //设置当前节点的兄弟节点的位置
static void setRawSibling(SubArray raw, long sibling) {
System.arraycopy(Parser.long2Byte(sibling), 0, raw.raw, raw.start+SIBLING_OFFSET, 8);
}
-
+ //获得当前节点的兄弟节点位置
static long getRawSibling(SubArray raw) {
return Parser.parseLong(Arrays.copyOfRange(raw.raw, raw.start+SIBLING_OFFSET, raw.start+SIBLING_OFFSET+8));
}
-
+ //设置对应节点的uid值
static void setRawKthSon(SubArray raw, long uid, int kth) {
int offset = raw.start+NODE_HEADER_SIZE+kth*(8*2);
System.arraycopy(Parser.long2Byte(uid), 0, raw.raw, offset, 8);
}
-
+ //获取对应的节点的uid值
static long getRawKthSon(SubArray raw, int kth) {
int offset = raw.start+NODE_HEADER_SIZE+kth*(8*2);
return Parser.parseLong(Arrays.copyOfRange(raw.raw, offset, offset+8));
}
-
+ //获取对应节点的key值
static void setRawKthKey(SubArray raw, long key, int kth) {
int offset = raw.start+NODE_HEADER_SIZE+kth*(8*2)+8;
System.arraycopy(Parser.long2Byte(key), 0, raw.raw, offset, 8);
}
-
+ //获取对应节点的key值
static long getRawKthKey(SubArray raw, int kth) {
int offset = raw.start+NODE_HEADER_SIZE+kth*(8*2)+8;
return Parser.parseLong(Arrays.copyOfRange(raw.raw, offset, offset+8));
}
-
+ //复制节点的uid值和key值
static void copyRawFromKth(SubArray from, SubArray to, int kth) {
int offset = from.start+NODE_HEADER_SIZE+kth*(8*2);
System.arraycopy(from.raw, offset, to.raw, to.start+NODE_HEADER_SIZE, from.end-offset);
}
-
+ //把kth键值对后面的数据往后移动为插入键值对移动
+ //包括kth往后移
static void shiftRawKth(SubArray raw, int kth) {
int begin = raw.start+NODE_HEADER_SIZE+(kth+1)*(8*2);
int end = raw.start+NODE_SIZE-1;
@@ -88,7 +91,7 @@ static void shiftRawKth(SubArray raw, int kth) {
raw.raw[i] = raw.raw[i-(8*2)];
}
}
-
+ //创建一个新节点
static byte[] newRootRaw(long left, long right, long key) {
SubArray raw = new SubArray(new byte[NODE_SIZE], 0, NODE_SIZE);
@@ -102,7 +105,7 @@ static byte[] newRootRaw(long left, long right, long key) {
return raw.raw;
}
-
+ //创建一个空根节点的原始raw数据
static byte[] newNilRootRaw() {
SubArray raw = new SubArray(new byte[NODE_SIZE], 0, NODE_SIZE);
@@ -112,7 +115,7 @@ static byte[] newNilRootRaw() {
return raw.raw;
}
-
+ //加载指定的uid节点,返回一个node对象
static Node loadNode(BPlusTree bTree, long uid) throws Exception {
DataItem di = bTree.dm.read(uid);
assert di != null;
@@ -123,11 +126,11 @@ static Node loadNode(BPlusTree bTree, long uid) throws Exception {
n.uid = uid;
return n;
}
-
+ //释放dataitem
public void release() {
dataItem.release();
}
-
+ //判断是不是叶子节点
public boolean isLeaf() {
dataItem.rLock();
try {
@@ -136,12 +139,12 @@ public boolean isLeaf() {
dataItem.rUnLock();
}
}
-
+ //抽象一个类,保存uid和兄弟节点的uid
class SearchNextRes {
long uid;
long siblingUid;
}
-
+ //寻找对应key的uid
public SearchNextRes searchNext(long key) {
dataItem.rLock();
try {
@@ -149,12 +152,14 @@ public SearchNextRes searchNext(long key) {
int noKeys = getRawNoKeys(raw);
for(int i = 0; i < noKeys; i ++) {
long ik = getRawKthKey(raw, i);
+ //比该key值小
if(key < ik) {
res.uid = getRawKthSon(raw, i);
res.siblingUid = 0;
return res;
}
}
+ //找完这个节点也没找到,就去兄弟节点找
res.uid = 0;
res.siblingUid = getRawSibling(raw);
return res;
@@ -168,12 +173,14 @@ class LeafSearchRangeRes {
List uids;
long siblingUid;
}
-
+ //在叶子节点中搜索指定键值范围的数据
+ //该叶子节点没有对应值范围的话,就去兄弟节点找
public LeafSearchRangeRes leafSearchRange(long leftKey, long rightKey) {
dataItem.rLock();
try {
int noKeys = getRawNoKeys(raw);
int kth = 0;
+ //找到节点中,指定的键值范围
while(kth < noKeys) {
long ik = getRawKthKey(raw, kth);
if(ik >= leftKey) {
@@ -181,6 +188,7 @@ public LeafSearchRangeRes leafSearchRange(long leftKey, long rightKey) {
}
kth ++;
}
+ //将处在范围内的uid加入进来
List uids = new ArrayList<>();
while(kth < noKeys) {
long ik = getRawKthKey(raw, kth);
@@ -203,23 +211,26 @@ public LeafSearchRangeRes leafSearchRange(long leftKey, long rightKey) {
dataItem.rUnLock();
}
}
-
+ //插入时设置兄弟节点的uid,新儿子,新的key值
class InsertAndSplitRes {
long siblingUid, newSon, newKey;
}
-
+ //插入键值对并进行分裂操作
public InsertAndSplitRes insertAndSplit(long uid, long key) throws Exception {
boolean success = false;
Exception err = null;
InsertAndSplitRes res = new InsertAndSplitRes();
-
+ //先保存原来的值
dataItem.before();
try {
+
success = insert(uid, key);
+ //如果插入不成功,就返回他的兄弟节点
if(!success) {
res.siblingUid = getRawSibling(raw);
return res;
}
+ //然后看这个插入的节点是否需要分裂
if(needSplit()) {
try {
SplitRes r = split();
@@ -241,11 +252,15 @@ public InsertAndSplitRes insertAndSplit(long uid, long key) throws Exception {
}
}
}
-
+ //插入节点的操作
+ //先在节点中找到插入的位置,先插进去再说
private boolean insert(long uid, long key) {
+ //先获取这个节点有多少个key
int noKeys = getRawNoKeys(raw);
int kth = 0;
+ //找到插入的位置
while(kth < noKeys) {
+ //先获取对应位置的key值
long ik = getRawKthKey(raw, kth);
if(ik < key) {
kth ++;
@@ -253,14 +268,18 @@ private boolean insert(long uid, long key) {
break;
}
}
+ //没找到插入的位置并且有兄弟节点
if(kth == noKeys && getRawSibling(raw) != 0) return false;
-
+ //找到了插入的位置,如果是叶子节点的话
if(getRawIfLeaf(raw)) {
+ //如果是叶子节点,直接将uid和key值插入就可以了
shiftRawKth(raw, kth);
setRawKthKey(raw, key, kth);
setRawKthSon(raw, uid, kth);
setRawNoKeys(raw, noKeys+1);
} else {
+ //如果不是叶子节点,
+ //先获取kth位置的key值
long kk = getRawKthKey(raw, kth);
setRawKthKey(raw, key, kth);
shiftRawKth(raw, kth+1);
@@ -270,7 +289,7 @@ private boolean insert(long uid, long key) {
}
return true;
}
-
+ //看这个节点的数量是否超过了平衡节点
private boolean needSplit() {
return BALANCE_NUMBER*2 == getRawNoKeys(raw);
}
@@ -278,7 +297,8 @@ private boolean needSplit() {
class SplitRes {
long newSon, newKey;
}
-
+ //该方法将一个节点分裂成两个节点,新的节点包含一部分键和子节点指针,原始节点保留剩余的键和指针。
+ // 分裂操作涉及将键和指针移动到新节点,并更新原始节点的键的数量和兄弟节点指针。
private SplitRes split() throws Exception {
SubArray nodeRaw = new SubArray(new byte[NODE_SIZE], 0, NODE_SIZE);
setRawIsLeaf(nodeRaw, getRawIfLeaf(raw));
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/Parser.java b/src/main/java/top/guoziyang/mydb/backend/parser/Parser.java
index 1705504..0d82890 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/Parser.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/Parser.java
@@ -20,12 +20,14 @@
public class Parser {
public static Object Parse(byte[] statement) throws Exception {
Tokenizer tokenizer = new Tokenizer(statement);
+ //获取第一个token,并刷新flushtoken
String token = tokenizer.peek();
tokenizer.pop();
Object stat = null;
Exception statErr = null;
try {
+ //根据不同的token来分析剩下的语句
switch(token) {
case "begin":
stat = parseBegin(tokenizer);
@@ -63,6 +65,7 @@ public static Object Parse(byte[] statement) throws Exception {
} catch(Exception e) {
statErr = e;
}
+ //检查语法错误
try {
String next = tokenizer.peek();
if(!"".equals(next)) {
@@ -79,7 +82,7 @@ public static Object Parse(byte[] statement) throws Exception {
}
return stat;
}
-
+ //解析show语句
private static Show parseShow(Tokenizer tokenizer) throws Exception {
String tmp = tokenizer.peek();
if("".equals(tmp)) {
@@ -87,76 +90,76 @@ private static Show parseShow(Tokenizer tokenizer) throws Exception {
}
throw Error.InvalidCommandException;
}
-
+ //解析update语句
private static Update parseUpdate(Tokenizer tokenizer) throws Exception {
Update update = new Update();
update.tableName = tokenizer.peek();
tokenizer.pop();
-
+ //检查set是否存在
if(!"set".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //获取字段名
update.fieldName = tokenizer.peek();
tokenizer.pop();
-
+ //检测=是否存在
if(!"=".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //获取数值
update.value = tokenizer.peek();
tokenizer.pop();
-
+ //检测后面有没有where
String tmp = tokenizer.peek();
if("".equals(tmp)) {
update.where = null;
return update;
}
-
+ //获取where
update.where = parseWhere(tokenizer);
return update;
}
-
+ //解析删除语句
private static Delete parseDelete(Tokenizer tokenizer) throws Exception {
Delete delete = new Delete();
-
+ //检查是否有from
if(!"from".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //获取表名
String tableName = tokenizer.peek();
if(!isName(tableName)) {
throw Error.InvalidCommandException;
}
delete.tableName = tableName;
tokenizer.pop();
-
+ //解析后面的where语句
delete.where = parseWhere(tokenizer);
return delete;
}
-
+ //解析insert语句
private static Insert parseInsert(Tokenizer tokenizer) throws Exception {
Insert insert = new Insert();
-
+ //检查是否有into
if(!"into".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //获取表名
String tableName = tokenizer.peek();
if(!isName(tableName)) {
throw Error.InvalidCommandException;
}
insert.tableName = tableName;
tokenizer.pop();
-
+ //检查是否有values
if(!"values".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
-
+ //获取插入的数值
List values = new ArrayList<>();
while(true) {
tokenizer.pop();
@@ -171,10 +174,10 @@ private static Insert parseInsert(Tokenizer tokenizer) throws Exception {
return insert;
}
-
+ //解析select语句
private static Select parseSelect(Tokenizer tokenizer) throws Exception {
Select read = new Select();
-
+ //获取要读取的属性
List fields = new ArrayList<>();
String asterisk = tokenizer.peek();
if("*".equals(asterisk)) {
@@ -196,40 +199,54 @@ private static Select parseSelect(Tokenizer tokenizer) throws Exception {
}
}
read.fields = fields.toArray(new String[fields.size()]);
-
+ //检查from是否存在
if(!"from".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //获取要读的表名
String tableName = tokenizer.peek();
if(!isName(tableName)) {
throw Error.InvalidCommandException;
}
read.tableName = tableName;
tokenizer.pop();
-
+ //检查是否有where语句
String tmp = tokenizer.peek();
if("".equals(tmp)) {
read.where = null;
return read;
}
-
+ //解析where语句
read.where = parseWhere(tokenizer);
return read;
}
-
+ //解析where语句
private static Where parseWhere(Tokenizer tokenizer) throws Exception {
Where where = new Where();
-
+ //查看是否有where的存在
if(!"where".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //判断是否有Not
+ int notPos = tokenizer.getPos();
+ System.out.println(notPos);
+ String not = tokenizer.peek();
+ if (not.equals("not"))
+ {
+ where.Notflag=true;
+ tokenizer.pop();
+ }
+ else
+ {
+ tokenizer.setPos(notPos);
+ tokenizer.pop();
+ }
+ //获取第一个表达式
SingleExpression exp1 = parseSingleExp(tokenizer);
where.singleExp1 = exp1;
-
+ //获取逻辑运算符
String logicOp = tokenizer.peek();
if("".equals(logicOp)) {
where.logicOp = logicOp;
@@ -240,7 +257,7 @@ private static Where parseWhere(Tokenizer tokenizer) throws Exception {
}
where.logicOp = logicOp;
tokenizer.pop();
-
+ //获取第二个表达式
SingleExpression exp2 = parseSingleExp(tokenizer);
where.singleExp2 = exp2;
@@ -249,49 +266,50 @@ private static Where parseWhere(Tokenizer tokenizer) throws Exception {
}
return where;
}
-
+ //解析大于,小于这类语句
private static SingleExpression parseSingleExp(Tokenizer tokenizer) throws Exception {
SingleExpression exp = new SingleExpression();
-
+ //先获取字段名
String field = tokenizer.peek();
if(!isName(field)) {
throw Error.InvalidCommandException;
}
exp.field = field;
tokenizer.pop();
-
+ //获取操作符
String op = tokenizer.peek();
if(!isCmpOp(op)) {
throw Error.InvalidCommandException;
}
exp.compareOp = op;
tokenizer.pop();
-
+ //获取值
exp.value = tokenizer.peek();
tokenizer.pop();
return exp;
}
-
+ //判断是否是符号
private static boolean isCmpOp(String op) {
- return ("=".equals(op) || ">".equals(op) || "<".equals(op));
+ return ("=".equals(op) || ">".equals(op) || "<".equals(op) || "!=".equals(op));
}
-
+ //判断是否是逻辑运算法
private static boolean isLogicOp(String op) {
return ("and".equals(op) || "or".equals(op));
}
-
+ //解析drop语句
private static Drop parseDrop(Tokenizer tokenizer) throws Exception {
+ //检查table是否存在
if(!"table".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //获取表名
String tableName = tokenizer.peek();
if(!isName(tableName)) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //检查语法错误
if(!"".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
@@ -300,8 +318,9 @@ private static Drop parseDrop(Tokenizer tokenizer) throws Exception {
drop.tableName = tableName;
return drop;
}
-
+ //解析create语句
private static Create parseCreate(Tokenizer tokenizer) throws Exception {
+ //先检查语法
if(!"table".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
@@ -309,6 +328,7 @@ private static Create parseCreate(Tokenizer tokenizer) throws Exception {
Create create = new Create();
String name = tokenizer.peek();
+ //检查table name 是否合法
if(!isName(name)) {
throw Error.InvalidCommandException;
}
@@ -316,13 +336,14 @@ private static Create parseCreate(Tokenizer tokenizer) throws Exception {
List fNames = new ArrayList<>();
List fTypes = new ArrayList<>();
+ //获取属性名和属性类型
while(true) {
tokenizer.pop();
String field = tokenizer.peek();
if("(".equals(field)) {
break;
}
-
+ //检查是否符合命名规则
if(!isName(field)) {
throw Error.InvalidCommandException;
}
@@ -335,7 +356,7 @@ private static Create parseCreate(Tokenizer tokenizer) throws Exception {
fNames.add(field);
fTypes.add(fieldType);
tokenizer.pop();
-
+ //检查语法
String next = tokenizer.peek();
if(",".equals(next)) {
continue;
@@ -349,12 +370,12 @@ private static Create parseCreate(Tokenizer tokenizer) throws Exception {
}
create.fieldName = fNames.toArray(new String[fNames.size()]);
create.fieldType = fTypes.toArray(new String[fTypes.size()]);
-
+ //检查是否有语法错误
tokenizer.pop();
if(!"index".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
-
+ //获取索引
List indexes = new ArrayList<>();
while(true) {
tokenizer.pop();
@@ -370,7 +391,7 @@ private static Create parseCreate(Tokenizer tokenizer) throws Exception {
}
create.index = indexes.toArray(new String[indexes.size()]);
tokenizer.pop();
-
+ //检测语法错误
if(!"".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
@@ -381,22 +402,26 @@ private static boolean isType(String tp) {
return ("int32".equals(tp) || "int64".equals(tp) ||
"string".equals(tp));
}
-
+ //解析abort语句
+ //检查后续语法是否正常
private static Abort parseAbort(Tokenizer tokenizer) throws Exception {
if(!"".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
return new Abort();
}
-
+ //解析commit语句
+ //检查语法是否合格
private static Commit parseCommit(Tokenizer tokenizer) throws Exception {
if(!"".equals(tokenizer.peek())) {
throw Error.InvalidCommandException;
}
return new Commit();
}
-
+ //分析begin语句
private static Begin parseBegin(Tokenizer tokenizer) throws Exception {
+ //判断隔离级别
+ //先检查语法有无错误,跟着的是不是isolation
String isolation = tokenizer.peek();
Begin begin = new Begin();
if("".equals(isolation)) {
@@ -405,14 +430,14 @@ private static Begin parseBegin(Tokenizer tokenizer) throws Exception {
if(!"isolation".equals(isolation)) {
throw Error.InvalidCommandException;
}
-
+ //检查跟着的是不是level
tokenizer.pop();
String level = tokenizer.peek();
if(!"level".equals(level)) {
throw Error.InvalidCommandException;
}
tokenizer.pop();
-
+ //判断是什么隔离级别
String tmp1 = tokenizer.peek();
if("read".equals(tmp1)) {
tokenizer.pop();
@@ -426,6 +451,7 @@ private static Begin parseBegin(Tokenizer tokenizer) throws Exception {
} else {
throw Error.InvalidCommandException;
}
+ //如果是可重复读级别,还有给begin里的属性赋值true
} else if("repeatable".equals(tmp1)) {
tokenizer.pop();
String tmp2 = tokenizer.peek();
@@ -443,7 +469,7 @@ private static Begin parseBegin(Tokenizer tokenizer) throws Exception {
throw Error.InvalidCommandException;
}
}
-
+ //检查这个name是否合法
private static boolean isName(String name) {
return !(name.length() == 1 && !Tokenizer.isAlphaBeta(name.getBytes()[0]));
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/Tokenizer.java b/src/main/java/top/guoziyang/mydb/backend/parser/Tokenizer.java
index 3f05f60..36bb68e 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/Tokenizer.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/Tokenizer.java
@@ -1,9 +1,18 @@
package top.guoziyang.mydb.backend.parser;
import top.guoziyang.mydb.common.Error;
-
+//将一个语句切割成多个tokenizer,来解析
public class Tokenizer {
private byte[] stat;
+
+ public int getPos() {
+ return pos;
+ }
+
+ public void setPos(int pos) {
+ this.pos = pos;
+ }
+
private int pos;
private String currentToken;
private boolean flushToken;
@@ -13,9 +22,9 @@ public Tokenizer(byte[] stat) {
this.stat = stat;
this.pos = 0;
this.currentToken = "";
- this.flushToken = true;
+ this.flushToken = true;//用来控制当前token是否需要刷新
}
-
+ //获取下一个token
public String peek() throws Exception {
if(err != null) {
throw err;
@@ -33,11 +42,12 @@ public String peek() throws Exception {
}
return currentToken;
}
-
+ //刷新标记
public void pop() {
flushToken = true;
}
-
+ //生成一个错误标记数组
+ //错误位置之前的<< 错误位置之后
public byte[] errStat() {
byte[] res = new byte[stat.length+3];
System.arraycopy(stat, 0, res, 0, pos);
@@ -45,28 +55,28 @@ public byte[] errStat() {
System.arraycopy(stat, pos, res, pos+3, stat.length-pos);
return res;
}
-
+ //移动byte数组里面的位置指针
private void popByte() {
pos ++;
if(pos > stat.length) {
pos = stat.length;
}
}
-
+ //返回数组当前位置的字节
private Byte peekByte() {
if(pos == stat.length) {
return null;
}
return stat[pos];
}
-
+ //读取下一个语句的数据
private String next() throws Exception {
if(err != null) {
throw err;
}
return nextMetaState();
}
-
+ //解析下一个元数据状态内容
private String nextMetaState() throws Exception {
while(true) {
Byte b = peekByte();
@@ -79,11 +89,14 @@ private String nextMetaState() throws Exception {
popByte();
}
byte b = peekByte();
+ //如果是符号,则直接返回该符号,字符串表示
if(isSymbol(b)) {
popByte();
return new String(new byte[]{b});
+ //如果是单引号或双引号,继续解析引号内容
} else if(b == '"' || b == '\'') {
return nextQuoteState();
+ //如果是字母或者数字,则去返回完整的字母和数字
} else if(isAlphaBeta(b) || isDigit(b)) {
return nextTokenState();
} else {
@@ -91,11 +104,12 @@ private String nextMetaState() throws Exception {
throw err;
}
}
-
+ //将字母或数字返回
private String nextTokenState() throws Exception {
StringBuilder sb = new StringBuilder();
while(true) {
Byte b = peekByte();
+ //如果b不满足条件了,就跳出循环,返回值
if(b == null || !(isAlphaBeta(b) || isDigit(b) || b == '_')) {
if(b != null && isBlank(b)) {
popByte();
@@ -106,15 +120,15 @@ private String nextTokenState() throws Exception {
popByte();
}
}
-
+ //判断b是不是数字
static boolean isDigit(byte b) {
return (b >= '0' && b <= '9');
}
-
+ //判断b是不是字母
static boolean isAlphaBeta(byte b) {
return ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z'));
}
-
+ //解析括号里的内容
private String nextQuoteState() throws Exception {
byte quote = peekByte();
popByte();
@@ -134,12 +148,12 @@ private String nextQuoteState() throws Exception {
}
return sb.toString();
}
-
+ //检查b是否是符号
static boolean isSymbol(byte b) {
return (b == '>' || b == '<' || b == '=' || b == '*' ||
b == ',' || b == '(' || b == ')');
}
-
+ //检查b是否为空
static boolean isBlank(byte b) {
return (b == '\n' || b == ' ' || b == '\t');
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Create.java b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Create.java
index edbb26a..0f82b21 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Create.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Create.java
@@ -1,5 +1,5 @@
package top.guoziyang.mydb.backend.parser.statement;
-
+//标记create 语句里面的属性
public class Create {
public String tableName;
public String[] fieldName;
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Delete.java b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Delete.java
index 171c17f..4904d17 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Delete.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Delete.java
@@ -1,6 +1,6 @@
package top.guoziyang.mydb.backend.parser.statement;
public class Delete {
- public String tableName;
- public Where where;
+ public String tableName;//表名
+ public Where where;//获取where对象
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Insert.java b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Insert.java
index 13aee63..725f013 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Insert.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Insert.java
@@ -1,6 +1,6 @@
package top.guoziyang.mydb.backend.parser.statement;
public class Insert {
- public String tableName;
- public String[] values;
+ public String tableName;//表名
+ public String[] values;//数值
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/statement/SingleExpression.java b/src/main/java/top/guoziyang/mydb/backend/parser/statement/SingleExpression.java
index 8d0378e..4f006ec 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/statement/SingleExpression.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/statement/SingleExpression.java
@@ -1,7 +1,7 @@
package top.guoziyang.mydb.backend.parser.statement;
public class SingleExpression {
- public String field;
- public String compareOp;
- public String value;
+ public String field;//字段名
+ public String compareOp;//操作符
+ public String value;//具体值
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Update.java b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Update.java
index 722da0d..a75abf0 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Update.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Update.java
@@ -1,8 +1,8 @@
package top.guoziyang.mydb.backend.parser.statement;
public class Update {
- public String tableName;
- public String fieldName;
- public String value;
- public Where where;
+ public String tableName;//表名
+ public String fieldName;//字段名
+ public String value;//数值
+ public Where where;//where类
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Where.java b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Where.java
index ed52353..63905a7 100644
--- a/src/main/java/top/guoziyang/mydb/backend/parser/statement/Where.java
+++ b/src/main/java/top/guoziyang/mydb/backend/parser/statement/Where.java
@@ -1,7 +1,8 @@
package top.guoziyang.mydb.backend.parser.statement;
-
+//where a>b
public class Where {
public SingleExpression singleExp1;
public String logicOp;
public SingleExpression singleExp2;
+ public boolean Notflag=false;
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/server/Executor.java b/src/main/java/top/guoziyang/mydb/backend/server/Executor.java
index df3a28d..233d4e9 100644
--- a/src/main/java/top/guoziyang/mydb/backend/server/Executor.java
+++ b/src/main/java/top/guoziyang/mydb/backend/server/Executor.java
@@ -29,10 +29,12 @@ public void close() {
tbm.abort(xid);
}
}
-
+ //执行sql语句
+ //先检查是否是关于事务的语句
public byte[] execute(byte[] sql) throws Exception {
System.out.println("Execute: " + new String(sql));
Object stat = Parser.Parse(sql);
+ //先检查是不是开启事务
if(Begin.class.isInstance(stat)) {
if(xid != 0) {
throw Error.NestedTransactionException;
@@ -58,7 +60,7 @@ public byte[] execute(byte[] sql) throws Exception {
return execute2(stat);
}
}
-
+ //根据类型来选择不同的执行操作
private byte[] execute2(Object stat) throws Exception {
boolean tmpTransaction = false;
Exception e = null;
diff --git a/src/main/java/top/guoziyang/mydb/backend/server/Server.java b/src/main/java/top/guoziyang/mydb/backend/server/Server.java
index 11d15e5..bff3ae7 100644
--- a/src/main/java/top/guoziyang/mydb/backend/server/Server.java
+++ b/src/main/java/top/guoziyang/mydb/backend/server/Server.java
@@ -22,8 +22,9 @@ public Server(int port, TableManager tbm) {
this.port = port;
this.tbm = tbm;
}
-
+ //启动服务器
public void start() {
+ //启动ServerSocket监听端口
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
@@ -32,6 +33,7 @@ public void start() {
return;
}
System.out.println("Server listen to port: " + port);
+ //建立一个线程池
ThreadPoolExecutor tpe = new ThreadPoolExecutor(10, 20, 1L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());
try {
while(true) {
@@ -63,6 +65,7 @@ public void run() {
InetSocketAddress address = (InetSocketAddress)socket.getRemoteSocketAddress();
System.out.println("Establish connection: " + address.getAddress().getHostAddress()+":"+address.getPort());
Packager packager = null;
+ //先初始化transporter,packager
try {
Transporter t = new Transporter(socket);
Encoder e = new Encoder();
@@ -78,6 +81,7 @@ public void run() {
}
Executor exe = new Executor(tbm);
while(true) {
+ //获取package
Package pkg = null;
try {
pkg = packager.receive();
@@ -88,6 +92,7 @@ public void run() {
byte[] result = null;
Exception e = null;
try {
+ //将执行的结果返回到客户端页面上
result = exe.execute(sql);
} catch (Exception e1) {
e = e1;
diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/Booter.java b/src/main/java/top/guoziyang/mydb/backend/tbm/Booter.java
index 9d45ec5..df819b0 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tbm/Booter.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tbm/Booter.java
@@ -8,7 +8,7 @@
import top.guoziyang.mydb.backend.utils.Panic;
import top.guoziyang.mydb.common.Error;
-
+//创建表的文件
// 记录第一个表的uid
public class Booter {
public static final String BOOTER_SUFFIX = ".bt";
@@ -16,7 +16,7 @@ public class Booter {
String path;
File file;
-
+ //创建一个表
public static Booter create(String path) {
removeBadTmp(path);
File f = new File(path+BOOTER_SUFFIX);
@@ -32,7 +32,8 @@ public static Booter create(String path) {
}
return new Booter(path, f);
}
-
+ //读取一个表
+ //关闭没有正常关闭的临时表
public static Booter open(String path) {
removeBadTmp(path);
File f = new File(path+BOOTER_SUFFIX);
@@ -44,7 +45,7 @@ public static Booter open(String path) {
}
return new Booter(path, f);
}
-
+ //删除临时表
private static void removeBadTmp(String path) {
new File(path+BOOTER_TMP_SUFFIX).delete();
}
@@ -53,7 +54,7 @@ private Booter(String path, File file) {
this.path = path;
this.file = file;
}
-
+ //读取文件的数据放进缓冲区中
public byte[] load() {
byte[] buf = null;
try {
@@ -63,8 +64,9 @@ public byte[] load() {
}
return buf;
}
-
+ //更新数据
public void update(byte[] data) {
+ //先创建一个临时文件
File tmp = new File(path + BOOTER_TMP_SUFFIX);
try {
tmp.createNewFile();
@@ -74,12 +76,15 @@ public void update(byte[] data) {
if(!tmp.canRead() || !tmp.canWrite()) {
Panic.panic(Error.FileCannotRWException);
}
+ //将data写入临时文件
try(FileOutputStream out = new FileOutputStream(tmp)) {
out.write(data);
out.flush();
} catch(IOException e) {
Panic.panic(e);
}
+ //将临时文件重新命名为目标数据库文件
+ //覆盖原有数据库文件
try {
Files.move(tmp.toPath(), new File(path+BOOTER_SUFFIX).toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch(IOException e) {
diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java b/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java
index d08f2e5..e2b5696 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java
@@ -26,7 +26,7 @@ public class Field {
String fieldType;
private long index;
private BPlusTree bt;
-
+ //根据uid来读取字段的值
public static Field loadField(Table tb, long uid) {
byte[] raw = null;
try {
@@ -49,7 +49,9 @@ public Field(Table tb, String fieldName, String fieldType, long index) {
this.fieldType = fieldType;
this.index = index;
}
-
+ //解析数组中的字段信息
+ //从数组中提取字段名,字段类型,索引信息
+ //如果存在索引,指向该b+树
private Field parseSelf(byte[] raw) {
int position = 0;
ParseStringRes res = Parser.parseString(raw);
@@ -68,10 +70,12 @@ private Field parseSelf(byte[] raw) {
}
return this;
}
-
+ //创建一个字段的信息
public static Field createField(Table tb, long xid, String fieldName, String fieldType, boolean indexed) throws Exception {
+ //检查字段是否合法
typeCheck(fieldType);
Field f = new Field(tb, fieldName, fieldType, 0);
+ //是否对该字段创建索引
if(indexed) {
long index = BPlusTree.create(((TableManagerImpl)tb.tbm).dm);
BPlusTree bt = BPlusTree.load(index, ((TableManagerImpl)tb.tbm).dm);
@@ -81,14 +85,14 @@ public static Field createField(Table tb, long xid, String fieldName, String fie
f.persistSelf(xid);
return f;
}
-
+ //将该字段表信息插入数据库文件中
private void persistSelf(long xid) throws Exception {
byte[] nameRaw = Parser.string2Byte(fieldName);
byte[] typeRaw = Parser.string2Byte(fieldType);
byte[] indexRaw = Parser.long2Byte(index);
this.uid = ((TableManagerImpl)tb.tbm).vm.insert(xid, Bytes.concat(nameRaw, typeRaw, indexRaw));
}
-
+ //检查字段类型
private static void typeCheck(String fieldType) throws Exception {
if(!"int32".equals(fieldType) && !"int64".equals(fieldType) && !"string".equals(fieldType)) {
throw Error.InvalidFieldException;
@@ -98,16 +102,17 @@ private static void typeCheck(String fieldType) throws Exception {
public boolean isIndexed() {
return index != 0;
}
-
+ //传入的key是字段对应的值,uid是该字段所在记录的物理位置
+ //将其插入到索引当中
public void insert(Object key, long uid) throws Exception {
long uKey = value2Uid(key);
bt.insert(uKey, uid);
}
-
+ //用范围查找的方式在字段中查找
public List search(long left, long right) throws Exception {
return bt.searchRange(left, right);
}
-
+ //根据字段类型转化字段的值
public Object string2Value(String str) {
switch(fieldType) {
case "int32":
@@ -119,7 +124,7 @@ public Object string2Value(String str) {
}
return null;
}
-
+ //获取该字段对应的uid?
public long value2Uid(Object key) {
long uid = 0;
switch(fieldType) {
@@ -135,7 +140,7 @@ public long value2Uid(Object key) {
}
return uid;
}
-
+ //根据字段类型转化字节数组
public byte[] value2Raw(Object v) {
byte[] raw = null;
switch(fieldType) {
@@ -156,7 +161,7 @@ class ParseValueRes {
Object v;
int shift;
}
-
+ //将原始数据转换为字段信息
public ParseValueRes parserValue(byte[] raw) {
ParseValueRes res = new ParseValueRes();
switch(fieldType) {
@@ -203,7 +208,7 @@ public String toString() {
.append(")")
.toString();
}
-
+ //将表达式a>b这种类型变成?
public FieldCalRes calExp(SingleExpression exp) throws Exception {
Object v = null;
FieldCalRes res = new FieldCalRes();
diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java b/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java
index 05a7a70..dc48cfc 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java
@@ -34,7 +34,7 @@ public class Table {
byte status;
long nextUid;
List fields = new ArrayList<>();
-
+ //读取表信息
public static Table loadTable(TableManager tbm, long uid) {
byte[] raw = null;
try {
@@ -46,19 +46,21 @@ public static Table loadTable(TableManager tbm, long uid) {
Table tb = new Table(tbm, uid);
return tb.parseSelf(raw);
}
-
+ //创建一个表
public static Table createTable(TableManager tbm, long nextUid, long xid, Create create) throws Exception {
Table tb = new Table(tbm, create.tableName, nextUid);
for(int i = 0; i < create.fieldName.length; i ++) {
String fieldName = create.fieldName[i];
String fieldType = create.fieldType[i];
boolean indexed = false;
+ //判断是否要对相应的字段创建索引
for(int j = 0; j < create.index.length; j ++) {
if(fieldName.equals(create.index[j])) {
indexed = true;
break;
}
}
+ //创建字段
tb.fields.add(Field.createField(tb, xid, fieldName, fieldType, indexed));
}
@@ -75,15 +77,17 @@ public Table(TableManager tbm, String tableName, long nextUid) {
this.name = tableName;
this.nextUid = nextUid;
}
-
+ //从字节数组中读取table的信息
private Table parseSelf(byte[] raw) {
+ //先读取table的基本信息,name,nextuid,下张表的uid
int position = 0;
ParseStringRes res = Parser.parseString(raw);
name = res.str;
position += res.next;
nextUid = Parser.parseLong(Arrays.copyOfRange(raw, position, position+8));
position += 8;
-
+ //先读取字段的uid
+ //然后根据uid读取对应的字段值
while(position < raw.length) {
long uid = Parser.parseLong(Arrays.copyOfRange(raw, position, position+8));
position += 8;
@@ -91,7 +95,8 @@ private Table parseSelf(byte[] raw) {
}
return this;
}
-
+ //将表的信息保存到数据库文件当中
+ //都转化成byte数组
private Table persistSelf(long xid) throws Exception {
byte[] nameRaw = Parser.string2Byte(name);
byte[] nextRaw = Parser.long2Byte(nextUid);
@@ -99,10 +104,11 @@ private Table persistSelf(long xid) throws Exception {
for(Field field : fields) {
fieldRaw = Bytes.concat(fieldRaw, Parser.long2Byte(field.uid));
}
+ //插入数据文件中
uid = ((TableManagerImpl)tbm).vm.insert(xid, Bytes.concat(nameRaw, nextRaw, fieldRaw));
return this;
}
-
+ //删除表信息
public int delete(long xid, Delete delete) throws Exception {
List uids = parseWhere(delete.where);
int count = 0;
@@ -113,10 +119,12 @@ public int delete(long xid, Delete delete) throws Exception {
}
return count;
}
-
+ //更新表信息
public int update(long xid, Update update) throws Exception {
+ //获得where对应的数据uid
List uids = parseWhere(update.where);
Field fd = null;
+ //找到要更新的表字段
for (Field f : fields) {
if(f.fieldName.equals(update.fieldName)) {
fd = f;
@@ -126,17 +134,21 @@ public int update(long xid, Update update) throws Exception {
if(fd == null) {
throw Error.FieldNotFoundException;
}
+ //将value转化为字段对应的字段类型
Object value = fd.string2Value(update.value);
int count = 0;
+ //读取数据
for (Long uid : uids) {
+ //读取对应uid的字段值
byte[] raw = ((TableManagerImpl)tbm).vm.read(xid, uid);
if(raw == null) continue;
-
+ //删除原有的数据
((TableManagerImpl)tbm).vm.delete(xid, uid);
-
+ //将要更改的数据放进去
Map entry = parseEntry(raw);
entry.put(fd.fieldName, value);
raw = entry2Raw(entry);
+ //添加数据
long uuid = ((TableManagerImpl)tbm).vm.insert(xid, raw);
count ++;
@@ -147,32 +159,53 @@ public int update(long xid, Update update) throws Exception {
}
}
}
+ //修改的数据条数
return count;
}
-
+ //读取对应字段
public String read(long xid, Select read) throws Exception {
+ //先获得范围内的uid
List uids = parseWhere(read.where);
StringBuilder sb = new StringBuilder();
+ //将范围内的数据读取出来
+ //然后加入到StringBuilder里面
for (Long uid : uids) {
byte[] raw = ((TableManagerImpl)tbm).vm.read(xid, uid);
if(raw == null) continue;
Map entry = parseEntry(raw);
- sb.append(printEntry(entry)).append("\n");
+ //根据Select语句中field的值,来读取对应的数据。
+ if(read.fields[0].equals("*"))sb.append(printEntry(entry)).append("\n");
+ else
+ {
+ sb.append("[");
+ for(String field:read.fields)
+ {
+ sb.append(entry.get(field)).append(",");
+ }
+ sb.deleteCharAt(sb.length()-1);
+ sb.append("]");
+ sb.append("\n");
+ }
+ // sb.append(printEntry(entry)).append("\n");
}
return sb.toString();
}
-
+ //插入字段值
public void insert(long xid, Insert insert) throws Exception {
+ //将insert里面的值,转化为字段名-值这样的map
Map entry = string2Entry(insert.values);
+ //将map转化为二进制数组
byte[] raw = entry2Raw(entry);
+ //把数据插入数据文件中
long uid = ((TableManagerImpl)tbm).vm.insert(xid, raw);
for (Field field : fields) {
if(field.isIndexed()) {
+ //将对应的值插入索引树当中
field.insert(entry.get(field.fieldName), uid);
}
}
}
-
+ //将value放入map当中,key是对应的filedname
private Map string2Entry(String[] values) throws Exception {
if(values.length != fields.size()) {
throw Error.InvalidValuesException;
@@ -185,11 +218,14 @@ private Map string2Entry(String[] values) throws Exception {
}
return entry;
}
-
+ //根据给定的条件where,在字段中fields进行查找
private List parseWhere(Where where) throws Exception {
long l0=0, r0=0, l1=0, r1=0;
boolean single = false;
Field fd = null;
+ //如果where为空
+ //则在所有的字段查找是否有索引字段
+ //选择第一个索引字段作为查询字段条件
if(where == null) {
for (Field field : fields) {
if(field.isIndexed()) {
@@ -201,8 +237,11 @@ private List parseWhere(Where where) throws Exception {
r0 = Long.MAX_VALUE;
single = true;
} else {
+ //查找与查询条件匹配的字段
+ //并检查该字段是否有索引
for (Field field : fields) {
if(field.fieldName.equals(where.singleExp1.field)) {
+ //找到字段中符合表达式的字段
if(!field.isIndexed()) {
throw Error.FieldNotIndexedException;
}
@@ -218,11 +257,22 @@ private List parseWhere(Where where) throws Exception {
l1 = res.l1; r1 = res.r1;
single = res.single;
}
+ //根据where获得的字段值范围,来查找字段的uid
List uids = fd.search(l0, r0);
if(!single) {
List tmp = fd.search(l1, r1);
uids.addAll(tmp);
}
+ //判断是否有notfLAG
+ if(where != null && where.Notflag)
+ {
+ List alluids = fd.search(0,Long.MAX_VALUE);
+ for(Long uid:uids)
+ {
+ alluids.remove(uid);
+ }
+ return alluids;
+ }
return uids;
}
@@ -230,7 +280,7 @@ class CalWhereRes {
long l0, r0, l1, r1;
boolean single;
}
-
+ //字段名
private CalWhereRes calWhere(Field fd, Where where) throws Exception {
CalWhereRes res = new CalWhereRes();
switch(where.logicOp) {
@@ -252,6 +302,7 @@ private CalWhereRes calWhere(Field fd, Where where) throws Exception {
res.l0 = r.left; res.r0 = r.right;
r = fd.calExp(where.singleExp2);
res.l1 = r.left; res.r1 = r.right;
+ //合并范围
if(res.l1 > res.l0) res.l0 = res.l1;
if(res.r1 < res.r0) res.r0 = res.r1;
break;
@@ -260,7 +311,8 @@ private CalWhereRes calWhere(Field fd, Where where) throws Exception {
}
return res;
}
-
+ //将filename-key这一map转化成string,加入到stringbuilder当中
+ //为了打印出来
private String printEntry(Map entry) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < fields.size(); i++) {
@@ -274,7 +326,8 @@ private String printEntry(Map entry) {
}
return sb.toString();
}
-
+ //把要更改的数据传进来
+ //
private Map parseEntry(byte[] raw) {
int pos = 0;
Map entry = new HashMap<>();
@@ -285,7 +338,8 @@ private Map parseEntry(byte[] raw) {
}
return entry;
}
-
+ //将map转化为二进制数组
+ //只把里面的值存进字节数组
private byte[] entry2Raw(Map entry) {
byte[] raw = new byte[0];
for (Field field : fields) {
@@ -293,7 +347,8 @@ private byte[] entry2Raw(Map entry) {
}
return raw;
}
-
+ //把表的基本信息转化成string
+ //为了打印出来
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
diff --git a/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManager.java b/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManager.java
index 0e74fb0..c9c7f41 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManager.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManager.java
@@ -11,12 +11,12 @@
import top.guoziyang.mydb.common.Error;
public interface TransactionManager {
- long begin();
- void commit(long xid);
- void abort(long xid);
- boolean isActive(long xid);
- boolean isCommitted(long xid);
- boolean isAborted(long xid);
+ long begin(); //开启一个新事务
+ void commit(long xid);//提交一个事务
+ void abort(long xid);//取消一个事务
+ boolean isActive(long xid);//查询一个事务的状态是否正在运行
+ boolean isCommitted(long xid);//查询一个事务的状态是否已经提交
+ boolean isAborted(long xid);//查询一个事务的状态是否已经取消
void close();
public static TransactionManagerImpl create(String path) {
diff --git a/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManagerImpl.java b/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManagerImpl.java
index 3efe309..fd3c343 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManagerImpl.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManagerImpl.java
@@ -51,10 +51,13 @@ private void checkXIDCounter() {
} catch (IOException e1) {
Panic.panic(Error.BadXIDFileException);
}
+ //意义在于,比8字节小,就说明这个文件根本没有文件头来记录管理事务的个数,所以文件肯定不合法
if(fileLen < LEN_XID_HEADER_LENGTH) {
Panic.panic(Error.BadXIDFileException);
}
-
+ //建立一个缓冲区,来读取文件的文件头
+ //将前八个字节表示的字节数转化成一个long类型的值
+ //代表当前管理的事务数,相当于事务数的最后一位
ByteBuffer buf = ByteBuffer.allocate(LEN_XID_HEADER_LENGTH);
try {
fc.position(0);
@@ -62,8 +65,10 @@ private void checkXIDCounter() {
} catch (IOException e) {
Panic.panic(e);
}
+ //获取事务数最后一位在文件中的偏移
this.xidCounter = Parser.parseLong(buf.array());
long end = getXidPosition(this.xidCounter + 1);
+ //如果偏移量和长度不相同,就说明文件不合法
if(end != fileLen) {
Panic.panic(Error.BadXIDFileException);
}
@@ -87,6 +92,8 @@ private void updateXID(long xid, byte status) {
Panic.panic(e);
}
try {
+ //虽然数据已经写入文件通道,但还只是存储在内存当中
+ //所以这里强制进行刷新
fc.force(false);
} catch (IOException e) {
Panic.panic(e);
diff --git a/src/main/java/top/guoziyang/mydb/backend/utils/Types.java b/src/main/java/top/guoziyang/mydb/backend/utils/Types.java
index a472160..8f33d60 100644
--- a/src/main/java/top/guoziyang/mydb/backend/utils/Types.java
+++ b/src/main/java/top/guoziyang/mydb/backend/utils/Types.java
@@ -1,6 +1,8 @@
package top.guoziyang.mydb.backend.utils;
public class Types {
+ //通过位运算符,将页号左移32位,就是将页号设为32位的高位值
+ //然后将偏移量的值低32位和左移后的页号进行合并,得到一个标识符
public static long addressToUid(int pgno, short offset) {
long u0 = (long)pgno;
long u1 = (long)offset;
diff --git a/src/main/java/top/guoziyang/mydb/backend/vm/Entry.java b/src/main/java/top/guoziyang/mydb/backend/vm/Entry.java
index 9a4366c..4ce3a74 100644
--- a/src/main/java/top/guoziyang/mydb/backend/vm/Entry.java
+++ b/src/main/java/top/guoziyang/mydb/backend/vm/Entry.java
@@ -15,14 +15,15 @@
*/
public class Entry {
- private static final int OF_XMIN = 0;
- private static final int OF_XMAX = OF_XMIN+8;
- private static final int OF_DATA = OF_XMAX+8;
+ private static final int OF_XMIN = 0;//创建该记录的事务编号
+ private static final int OF_XMAX = OF_XMIN+8;//删除该记录的事务编号
+ private static final int OF_DATA = OF_XMAX+8;//该条记录持有的数据
private long uid;
private DataItem dataItem;
private VersionManager vm;
+ //根据uid,dataitem创建一个entry对象
public static Entry newEntry(VersionManager vm, DataItem dataItem, long uid) {
Entry entry = new Entry();
entry.uid = uid;
@@ -30,22 +31,22 @@ public static Entry newEntry(VersionManager vm, DataItem dataItem, long uid) {
entry.vm = vm;
return entry;
}
-
+ //根据uid,获得dataitem,然后创建一个entry对象
public static Entry loadEntry(VersionManager vm, long uid) throws Exception {
DataItem di = ((VersionManagerImpl)vm).dm.read(uid);
return newEntry(vm, di, uid);
}
-
+ //将数据打包成entry格式
public static byte[] wrapEntryRaw(long xid, byte[] data) {
byte[] xmin = Parser.long2Byte(xid);
byte[] xmax = new byte[8];
return Bytes.concat(xmin, xmax, data);
}
-
+ //从内存中释放该条记录
public void release() {
((VersionManagerImpl)vm).releaseEntry(this);
}
-
+ //释放该条dataitem数据
public void remove() {
dataItem.release();
}
@@ -62,7 +63,7 @@ public byte[] data() {
dataItem.rUnLock();
}
}
-
+ //得到该条记录的创建数据
public long getXmin() {
dataItem.rLock();
try {
@@ -72,7 +73,7 @@ public long getXmin() {
dataItem.rUnLock();
}
}
-
+ //得到该条记录的删除数据
public long getXmax() {
dataItem.rLock();
try {
@@ -82,7 +83,9 @@ public long getXmax() {
dataItem.rUnLock();
}
}
-
+ //设置该条数据的删除
+ //删除之前对dataitm的数据进行保存,把dataitem的raw数据先保存到old-raw数组里面
+ //生成一个更新日志,把更新的内容写进日志里面
public void setXmax(long xid) {
dataItem.before();
try {
@@ -92,7 +95,7 @@ public void setXmax(long xid) {
dataItem.after(xid);
}
}
-
+ //获取当前dataitem的uid
public long getUid() {
return uid;
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/vm/LockTable.java b/src/main/java/top/guoziyang/mydb/backend/vm/LockTable.java
index d2a2243..3dc9551 100644
--- a/src/main/java/top/guoziyang/mydb/backend/vm/LockTable.java
+++ b/src/main/java/top/guoziyang/mydb/backend/vm/LockTable.java
@@ -36,22 +36,35 @@ public LockTable() {
public Lock add(long xid, long uid) throws Exception {
lock.lock();
try {
+ //看xid是否拥有uid这个资源
if(isInList(x2u, xid, uid)) {
return null;
}
+ //如果uid这个资源还没被其他xid持有
+ //把uid这个资源交给xid
+ //加入map中
if(!u2x.containsKey(uid)) {
u2x.put(uid, xid);
putIntoList(x2u, xid, uid);
return null;
}
+ //如果uid被其他xid持有了
+ //将xid加入等待资源队列
waitU.put(xid, uid);
//putIntoList(wait, xid, uid);
+ //加入等待uid的xid列表
putIntoList(wait, uid, xid);
+ //检查是否死锁
if(hasDeadLock()) {
+ //检测到了死锁
+ //将xid从等待资源移除
+ //将xid从等待uid资源队列移除
+ //抛出异常
waitU.remove(xid);
removeFromList(wait, uid, xid);
throw Error.DeadlockException;
}
+ //将该xid加入真正的锁的队列
Lock l = new ReentrantLock();
l.lock();
waitLock.put(xid, l);
@@ -61,10 +74,11 @@ public Lock add(long xid, long uid) throws Exception {
lock.unlock();
}
}
-
+ //在一个事务commit或者aborted的时候,释放锁
public void remove(long xid) {
lock.lock();
try {
+ //获取xid持有的全部uid资源
List l = x2u.get(xid);
if(l != null) {
while(l.size() > 0) {
@@ -84,15 +98,21 @@ public void remove(long xid) {
// 从等待队列中选择一个xid来占用uid
private void selectNewXID(long uid) {
u2x.remove(uid);
+ //获取等待uid资源的xid队列
List l = wait.get(uid);
if(l == null) return;
assert l.size() > 0;
while(l.size() > 0) {
+ //将xid从等待uid队列去除
long xid = l.remove(0);
+ //查看等待锁队列里是否有该事务
if(!waitLock.containsKey(xid)) {
continue;
} else {
+ //如果有的话
+ //将xid放入map,说明xid持有uid
+ //把xid从等待锁中去除
u2x.put(uid, xid);
Lock lo = waitLock.remove(xid);
waitU.remove(xid);
@@ -100,7 +120,7 @@ private void selectNewXID(long uid) {
break;
}
}
-
+ //如果列表里面没有等待的xid了,就去除掉该uid的key
if(l.size() == 0) wait.remove(uid);
}
@@ -110,6 +130,9 @@ private void selectNewXID(long uid) {
private boolean hasDeadLock() {
xidStamp = new HashMap<>();
stamp = 1;
+ //遍历每个节点,为每个节点设置访问戳
+ //不同的图的访问戳是不同的
+ //对每个图进行深度搜索
for(long xid : x2u.keySet()) {
Integer s = xidStamp.get(xid);
if(s != null && s > 0) {
@@ -122,7 +145,9 @@ private boolean hasDeadLock() {
}
return false;
}
-
+ //核心就是检查该事务是否缺少资源uid
+ //然后找持有该uid的资源xid
+ //再进行深度搜索
private boolean dfs(long xid) {
Integer stp = xidStamp.get(xid);
if(stp != null && stp == stamp) {
@@ -139,7 +164,7 @@ private boolean dfs(long xid) {
assert x != null;
return dfs(x);
}
-
+ //将xid从对应的列表中删除
private void removeFromList(Map> listMap, long uid0, long uid1) {
List l = listMap.get(uid0);
if(l == null) return;
@@ -155,14 +180,14 @@ private void removeFromList(Map> listMap, long uid0, long uid1)
listMap.remove(uid0);
}
}
-
+ //将xid加入对应的列表
private void putIntoList(Map> listMap, long uid0, long uid1) {
if(!listMap.containsKey(uid0)) {
listMap.put(uid0, new ArrayList<>());
}
listMap.get(uid0).add(0, uid1);
}
-
+ //查看事务是否拥有uid该资源
private boolean isInList(Map> listMap, long uid0, long uid1) {
List l = listMap.get(uid0);
if(l == null) return false;
diff --git a/src/main/java/top/guoziyang/mydb/backend/vm/Transaction.java b/src/main/java/top/guoziyang/mydb/backend/vm/Transaction.java
index 61abcff..33e4a90 100644
--- a/src/main/java/top/guoziyang/mydb/backend/vm/Transaction.java
+++ b/src/main/java/top/guoziyang/mydb/backend/vm/Transaction.java
@@ -6,13 +6,15 @@
import top.guoziyang.mydb.backend.tm.TransactionManagerImpl;
// vm对一个事务的抽象
+//保存当前系统事务的快照
public class Transaction {
public long xid;
+ //隔离级别
public int level;
public Map snapshot;
public Exception err;
public boolean autoAborted;
-
+ //系统中还处在活跃中的事务
public static Transaction newTransaction(long xid, int level, Map active) {
Transaction t = new Transaction();
t.xid = xid;
@@ -25,7 +27,7 @@ public static Transaction newTransaction(long xid, int level, Map implements VersionManager {
TransactionManager tm;
@@ -25,11 +25,13 @@ public VersionManagerImpl(TransactionManager tm, DataManager dm) {
this.tm = tm;
this.dm = dm;
this.activeTransaction = new HashMap<>();
+ //先把super-xid加进去,它的事务级别就是最低的,权限最高
activeTransaction.put(TransactionManagerImpl.SUPER_XID, Transaction.newTransaction(TransactionManagerImpl.SUPER_XID, 0, null));
this.lock = new ReentrantLock();
this.lt = new LockTable();
}
-
+ //开启读行为
+ //检查该事务开启时,系统中活跃的事务
@Override
public byte[] read(long xid, long uid) throws Exception {
lock.lock();
@@ -39,7 +41,7 @@ public byte[] read(long xid, long uid) throws Exception {
if(t.err != null) {
throw t.err;
}
-
+ //获取对应的entry
Entry entry = null;
try {
entry = super.get(uid);
@@ -50,6 +52,8 @@ public byte[] read(long xid, long uid) throws Exception {
throw e;
}
}
+ //检查该事务是否有读取该entry的资格
+ //读完后就释放该entry
try {
if(Visibility.isVisible(tm, t, entry)) {
return entry.data();
@@ -60,7 +64,8 @@ public byte[] read(long xid, long uid) throws Exception {
entry.release();
}
}
-
+ //插入数据
+ //同样是先获取该事务开启时,系统中活跃事务列表
@Override
public long insert(long xid, byte[] data) throws Exception {
lock.lock();
@@ -70,11 +75,13 @@ public long insert(long xid, byte[] data) throws Exception {
if(t.err != null) {
throw t.err;
}
-
+ //数据包装成entry
+ //调用dataitem的方法插入文件中
byte[] raw = Entry.wrapEntryRaw(xid, data);
return dm.insert(xid, raw);
}
-
+ //删除数据
+ //获取事务开启时,系统中活跃事务列表
@Override
public boolean delete(long xid, long uid) throws Exception {
lock.lock();
@@ -84,6 +91,7 @@ public boolean delete(long xid, long uid) throws Exception {
if(t.err != null) {
throw t.err;
}
+ //获取entry
Entry entry = null;
try {
entry = super.get(uid);
@@ -94,10 +102,14 @@ public boolean delete(long xid, long uid) throws Exception {
throw e;
}
}
+ //检查该事务有没有对该数据修改的权限
try {
if(!Visibility.isVisible(tm, t, entry)) {
return false;
}
+ //去锁管理中,请求资源
+ //有资源就继续
+ //没有资源就进入等待队列,死锁就报错,撤销该事务
Lock l = null;
try {
l = lt.add(xid, uid);
@@ -107,22 +119,24 @@ public boolean delete(long xid, long uid) throws Exception {
t.autoAborted = true;
throw t.err;
}
+ //假如进入了等待队列,就自旋一下,等待锁的释放
if(l != null) {
l.lock();
l.unlock();
}
-
+ //检查该资源是否被自己删除
if(entry.getXmax() == xid) {
return false;
}
-
+ //查看事务t是否跳过了版本
+ //如果跳过了,就撤销该事务
if(Visibility.isVersionSkip(tm, t, entry)) {
t.err = Error.ConcurrentUpdateException;
internAbort(xid, true);
t.autoAborted = true;
throw t.err;
}
-
+ //删除该数据,生成update日志
entry.setXmax(xid);
return true;
@@ -130,20 +144,22 @@ public boolean delete(long xid, long uid) throws Exception {
entry.release();
}
}
-
+ //开启一个事务
@Override
public long begin(int level) {
lock.lock();
try {
long xid = tm.begin();
+ //里面的构造函数根据当前activeTransaction的key生成active set。
Transaction t = Transaction.newTransaction(xid, level, activeTransaction);
+ //将该事务放进活跃列表中
activeTransaction.put(xid, t);
return xid;
} finally {
lock.unlock();
}
}
-
+ //提交一个事务
@Override
public void commit(long xid) throws Exception {
lock.lock();
@@ -159,7 +175,9 @@ public void commit(long xid) throws Exception {
System.out.println(activeTransaction.keySet());
Panic.panic(n);
}
-
+ //将该事务从活跃事务移除
+ //释放该事务所持有的全部资源
+ //修改该事务的状态
lock.lock();
activeTransaction.remove(xid);
lock.unlock();
@@ -167,12 +185,14 @@ public void commit(long xid) throws Exception {
lt.remove(xid);
tm.commit(xid);
}
-
+ //将该事务给撤销
@Override
public void abort(long xid) {
internAbort(xid, false);
}
-
+ //同样的将该事务从活跃列表去除
+ //释放掉该事务持有的资源
+ //将该事务的标志设为撤销
private void internAbort(long xid, boolean autoAborted) {
lock.lock();
Transaction t = activeTransaction.get(xid);
@@ -190,6 +210,8 @@ public void releaseEntry(Entry entry) {
super.release(entry.getUid());
}
+ //实际上是调用dataitem的方法
+ //然后将dataitem包装成entry返回
@Override
protected Entry getForCache(long uid) throws Exception {
Entry entry = Entry.loadEntry(this, uid);
@@ -198,7 +220,7 @@ protected Entry getForCache(long uid) throws Exception {
}
return entry;
}
-
+ //实际上是调用dataitem的release
@Override
protected void releaseForCache(Entry entry) {
entry.remove();
diff --git a/src/main/java/top/guoziyang/mydb/backend/vm/Visibility.java b/src/main/java/top/guoziyang/mydb/backend/vm/Visibility.java
index 08799c7..72a5408 100644
--- a/src/main/java/top/guoziyang/mydb/backend/vm/Visibility.java
+++ b/src/main/java/top/guoziyang/mydb/backend/vm/Visibility.java
@@ -1,9 +1,12 @@
package top.guoziyang.mydb.backend.vm;
import top.guoziyang.mydb.backend.tm.TransactionManager;
-
+//确定事务的可行性
public class Visibility {
-
+ //版本t是否跳过了版本e
+ //就是看版本t什么时候会跳过版本e
+ //数据已经被删除并且xmax大于本事务或者事务在活跃当中。
+ //这时候版本t不会知道数据被删除,就会跳过这个版本。
public static boolean isVersionSkip(TransactionManager tm, Transaction t, Entry e) {
long xmax = e.getXmax();
if(t.level == 0) {
@@ -12,21 +15,26 @@ public static boolean isVersionSkip(TransactionManager tm, Transaction t, Entry
return tm.isCommitted(xmax) && (xmax > t.xid || t.isInSnapshot(xmax));
}
}
-
+ //检查这条记录对本事务是否有可见性
public static boolean isVisible(TransactionManager tm, Transaction t, Entry e) {
+ //读已提交
if(t.level == 0) {
return readCommitted(tm, t, e);
} else {
+ //可重复读
return repeatableRead(tm, t, e);
}
}
-
+ //读已提交
private static boolean readCommitted(TransactionManager tm, Transaction t, Entry e) {
long xid = t.xid;
long xmin = e.getXmin();
long xmax = e.getXmax();
+ //这条记录是当前事务创建,并且没有删除,就可以读
if(xmin == xid && xmax == 0) return true;
-
+ //这条记录不是本事务创建,但是已提交
+ //情况一:没有被删除,可以读
+ //情况二:有删除记录,删除者不是本事务,并且删除的事务没有提交,就可以读
if(tm.isCommitted(xmin)) {
if(xmax == 0) return true;
if(xmax != xid) {
@@ -37,15 +45,19 @@ private static boolean readCommitted(TransactionManager tm, Transaction t, Entry
}
return false;
}
-
+ //可重复读
private static boolean repeatableRead(TransactionManager tm, Transaction t, Entry e) {
long xid = t.xid;
long xmin = e.getXmin();
long xmax = e.getXmax();
+ //记录是本事务创建的,并且没被删除,就可以读
if(xmin == xid && xmax == 0) return true;
-
+ //创建该记录的xin已经提交并且xin<本事务并且在本事务开始之前,xmin不是活跃的
if(tm.isCommitted(xmin) && xmin < xid && !t.isInSnapshot(xmin)) {
+ //没人删除,就可以读
if(xmax == 0) return true;
+ //有人删除,在删除者不是本事务的情况下
+ //xmax没有提交,或者xmax大于xid,或者xmax在本事务开始之前是活跃的
if(xmax != xid) {
if(!tm.isCommitted(xmax) || xmax > xid || t.isInSnapshot(xmax)) {
return true;
diff --git a/src/main/java/top/guoziyang/mydb/client/Client.java b/src/main/java/top/guoziyang/mydb/client/Client.java
index a48a05d..6891a4d 100644
--- a/src/main/java/top/guoziyang/mydb/client/Client.java
+++ b/src/main/java/top/guoziyang/mydb/client/Client.java
@@ -9,7 +9,7 @@ public class Client {
public Client(Packager packager) {
this.rt = new RoundTripper(packager);
}
-
+ //利用RoundTripper来发送数据
public byte[] execute(byte[] stat) throws Exception {
Package pkg = new Package(stat, null);
Package resPkg = rt.roundTrip(pkg);
diff --git a/src/main/java/top/guoziyang/mydb/client/Launcher.java b/src/main/java/top/guoziyang/mydb/client/Launcher.java
index 86e1c97..efc0462 100644
--- a/src/main/java/top/guoziyang/mydb/client/Launcher.java
+++ b/src/main/java/top/guoziyang/mydb/client/Launcher.java
@@ -7,7 +7,8 @@
import top.guoziyang.mydb.transport.Encoder;
import top.guoziyang.mydb.transport.Packager;
import top.guoziyang.mydb.transport.Transporter;
-
+//打开客户端
+//设立端口
public class Launcher {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 9999);
diff --git a/src/main/java/top/guoziyang/mydb/client/RoundTripper.java b/src/main/java/top/guoziyang/mydb/client/RoundTripper.java
index 11d5a52..defb0f8 100644
--- a/src/main/java/top/guoziyang/mydb/client/RoundTripper.java
+++ b/src/main/java/top/guoziyang/mydb/client/RoundTripper.java
@@ -9,7 +9,9 @@ public class RoundTripper {
public RoundTripper(Packager packager) {
this.packager = packager;
}
-
+ //发送数据
+ //返回得到的结果
+ //实现单次收发的功能
public Package roundTrip(Package pkg) throws Exception {
packager.send(pkg);
return packager.receive();
diff --git a/src/main/java/top/guoziyang/mydb/client/Shell.java b/src/main/java/top/guoziyang/mydb/client/Shell.java
index 9e65179..ffaf3ab 100644
--- a/src/main/java/top/guoziyang/mydb/client/Shell.java
+++ b/src/main/java/top/guoziyang/mydb/client/Shell.java
@@ -1,7 +1,7 @@
package top.guoziyang.mydb.client;
import java.util.Scanner;
-
+//读取用户在shell上的输入
public class Shell {
private Client client;
diff --git a/src/main/java/top/guoziyang/mydb/transport/Encoder.java b/src/main/java/top/guoziyang/mydb/transport/Encoder.java
index 5fd1a82..7ffe986 100644
--- a/src/main/java/top/guoziyang/mydb/transport/Encoder.java
+++ b/src/main/java/top/guoziyang/mydb/transport/Encoder.java
@@ -5,9 +5,11 @@
import com.google.common.primitives.Bytes;
import top.guoziyang.mydb.common.Error;
-
+//package在发送之前
+//由encoder编写为特殊的二维数组
public class Encoder {
-
+ //判断该package是否错误
+ //将错误标志存在flag里面
public byte[] encode(Package pkg) {
if(pkg.getErr() != null) {
Exception err = pkg.getErr();
@@ -20,7 +22,8 @@ public byte[] encode(Package pkg) {
return Bytes.concat(new byte[]{0}, pkg.getData());
}
}
-
+ //根据错误状态位
+ //来解码
public Package decode(byte[] data) throws Exception {
if(data.length < 1) {
throw Error.InvalidPkgDataException;
diff --git a/src/main/java/top/guoziyang/mydb/transport/Packager.java b/src/main/java/top/guoziyang/mydb/transport/Packager.java
index 3d855f5..1b720c5 100644
--- a/src/main/java/top/guoziyang/mydb/transport/Packager.java
+++ b/src/main/java/top/guoziyang/mydb/transport/Packager.java
@@ -1,5 +1,5 @@
package top.guoziyang.mydb.transport;
-
+//将package和transpoter集合在一起
public class Packager {
private Transporter transpoter;
private Encoder encoder;
diff --git a/src/main/java/top/guoziyang/mydb/transport/Transporter.java b/src/main/java/top/guoziyang/mydb/transport/Transporter.java
index cc716f4..c0e28db 100644
--- a/src/main/java/top/guoziyang/mydb/transport/Transporter.java
+++ b/src/main/java/top/guoziyang/mydb/transport/Transporter.java
@@ -20,13 +20,14 @@ public Transporter(Socket socket) throws IOException {
this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
}
-
+ //将数据转化为16进制
+ //然后将数据写入输出流
public void send(byte[] data) throws Exception {
String raw = hexEncode(data);
writer.write(raw);
writer.flush();
}
-
+ //接收数据
public byte[] receive() throws Exception {
String line = reader.readLine();
if(line == null) {
@@ -34,17 +35,18 @@ public byte[] receive() throws Exception {
}
return hexDecode(line);
}
-
+ //关闭文件流
public void close() throws IOException {
writer.close();
reader.close();
socket.close();
}
-
+ //将字节数组转化为16进制,并且在信息末尾加上换行符
+ //转化成十六进制是防止特殊字符被篡改的问题
private String hexEncode(byte[] buf) {
return Hex.encodeHexString(buf, true)+"\n";
}
-
+ //将十六进制的数组转化为二进制的
private byte[] hexDecode(String buf) throws DecoderException {
return Hex.decodeHex(buf);
}
From 8f9fa9ea41c8214e9961e2f0092c96ecaa363638 Mon Sep 17 00:00:00 2001
From: lazyhwj <604993149@qq.com>
Date: Sat, 29 Jul 2023 12:35:34 +0800
Subject: [PATCH 2/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=85=A8?=
=?UTF-8?q?=E8=A1=A8=E6=89=AB=E6=8F=8F=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C?=
=?UTF-8?q?=E5=8D=B3=E4=BD=BF=E4=B8=8D=E6=98=AF=E7=B4=A2=E5=BC=95=E7=9A=84?=
=?UTF-8?q?=E5=AD=97=E6=AE=B5=EF=BC=8C=E4=B9=9F=E8=83=BD=E5=A4=9F=E8=BF=9B?=
=?UTF-8?q?=E8=A1=8C=E6=90=9C=E7=B4=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../top/guoziyang/mydb/backend/Launcher.java | 2 +-
.../top/guoziyang/mydb/backend/tbm/Field.java | 1 +
.../mydb/backend/tbm/FieldCalRes.java | 1 +
.../top/guoziyang/mydb/backend/tbm/Table.java | 274 +++++++++++++-----
4 files changed, 201 insertions(+), 77 deletions(-)
diff --git a/src/main/java/top/guoziyang/mydb/backend/Launcher.java b/src/main/java/top/guoziyang/mydb/backend/Launcher.java
index a0a19ad..186cb42 100644
--- a/src/main/java/top/guoziyang/mydb/backend/Launcher.java
+++ b/src/main/java/top/guoziyang/mydb/backend/Launcher.java
@@ -41,7 +41,7 @@ public static void main(String[] args) throws ParseException {
createDB(cmd.getOptionValue("create"));
return;
}
- openDB("/tmp/mydb",DEFALUT_MEM);
+ openDB("/Users/nica/Documents/MYDB/tmp/mydb",DEFALUT_MEM);
System.out.println("Usage: launcher (open|create) DBPath");
}
//创建数据库文件
diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java b/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java
index e2b5696..9fb992b 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java
@@ -212,6 +212,7 @@ public String toString() {
public FieldCalRes calExp(SingleExpression exp) throws Exception {
Object v = null;
FieldCalRes res = new FieldCalRes();
+ res.field = exp.field;
switch(exp.compareOp) {
case "<":
res.left = 0;
diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/FieldCalRes.java b/src/main/java/top/guoziyang/mydb/backend/tbm/FieldCalRes.java
index f258c58..e3c8a17 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tbm/FieldCalRes.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tbm/FieldCalRes.java
@@ -3,4 +3,5 @@
public class FieldCalRes {
public long left;
public long right;
+ public String field;
}
diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java b/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java
index dc48cfc..20d5c32 100644
--- a/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java
+++ b/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java
@@ -1,13 +1,10 @@
package top.guoziyang.mydb.backend.tbm;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import com.google.common.primitives.Bytes;
+import top.guoziyang.mydb.backend.dm.DataManager;
import top.guoziyang.mydb.backend.parser.statement.Create;
import top.guoziyang.mydb.backend.parser.statement.Delete;
import top.guoziyang.mydb.backend.parser.statement.Insert;
@@ -19,6 +16,7 @@
import top.guoziyang.mydb.backend.utils.Panic;
import top.guoziyang.mydb.backend.utils.ParseStringRes;
import top.guoziyang.mydb.backend.utils.Parser;
+import top.guoziyang.mydb.backend.vm.VersionManager;
import top.guoziyang.mydb.common.Error;
/**
@@ -34,11 +32,12 @@ public class Table {
byte status;
long nextUid;
List fields = new ArrayList<>();
+
//读取表信息
public static Table loadTable(TableManager tbm, long uid) {
byte[] raw = null;
try {
- raw = ((TableManagerImpl)tbm).vm.read(TransactionManagerImpl.SUPER_XID, uid);
+ raw = ((TableManagerImpl) tbm).vm.read(TransactionManagerImpl.SUPER_XID, uid);
} catch (Exception e) {
Panic.panic(e);
}
@@ -46,16 +45,17 @@ public static Table loadTable(TableManager tbm, long uid) {
Table tb = new Table(tbm, uid);
return tb.parseSelf(raw);
}
+
//创建一个表
public static Table createTable(TableManager tbm, long nextUid, long xid, Create create) throws Exception {
Table tb = new Table(tbm, create.tableName, nextUid);
- for(int i = 0; i < create.fieldName.length; i ++) {
+ for (int i = 0; i < create.fieldName.length; i++) {
String fieldName = create.fieldName[i];
String fieldType = create.fieldType[i];
boolean indexed = false;
//判断是否要对相应的字段创建索引
- for(int j = 0; j < create.index.length; j ++) {
- if(fieldName.equals(create.index[j])) {
+ for (int j = 0; j < create.index.length; j++) {
+ if (fieldName.equals(create.index[j])) {
indexed = true;
break;
}
@@ -77,6 +77,7 @@ public Table(TableManager tbm, String tableName, long nextUid) {
this.name = tableName;
this.nextUid = nextUid;
}
+
//从字节数组中读取table的信息
private Table parseSelf(byte[] raw) {
//先读取table的基本信息,name,nextuid,下张表的uid
@@ -84,54 +85,57 @@ private Table parseSelf(byte[] raw) {
ParseStringRes res = Parser.parseString(raw);
name = res.str;
position += res.next;
- nextUid = Parser.parseLong(Arrays.copyOfRange(raw, position, position+8));
+ nextUid = Parser.parseLong(Arrays.copyOfRange(raw, position, position + 8));
position += 8;
//先读取字段的uid
//然后根据uid读取对应的字段值
- while(position < raw.length) {
- long uid = Parser.parseLong(Arrays.copyOfRange(raw, position, position+8));
+ while (position < raw.length) {
+ long uid = Parser.parseLong(Arrays.copyOfRange(raw, position, position + 8));
position += 8;
fields.add(Field.loadField(this, uid));
}
return this;
}
+
//将表的信息保存到数据库文件当中
//都转化成byte数组
private Table persistSelf(long xid) throws Exception {
byte[] nameRaw = Parser.string2Byte(name);
byte[] nextRaw = Parser.long2Byte(nextUid);
byte[] fieldRaw = new byte[0];
- for(Field field : fields) {
+ for (Field field : fields) {
fieldRaw = Bytes.concat(fieldRaw, Parser.long2Byte(field.uid));
}
//插入数据文件中
- uid = ((TableManagerImpl)tbm).vm.insert(xid, Bytes.concat(nameRaw, nextRaw, fieldRaw));
+ uid = ((TableManagerImpl) tbm).vm.insert(xid, Bytes.concat(nameRaw, nextRaw, fieldRaw));
return this;
}
+
//删除表信息
public int delete(long xid, Delete delete) throws Exception {
- List uids = parseWhere(delete.where);
+ List uids = parseWhere(delete.where, xid);
int count = 0;
for (Long uid : uids) {
- if(((TableManagerImpl)tbm).vm.delete(xid, uid)) {
- count ++;
+ if (((TableManagerImpl) tbm).vm.delete(xid, uid)) {
+ count++;
}
}
return count;
}
+
//更新表信息
public int update(long xid, Update update) throws Exception {
//获得where对应的数据uid
- List uids = parseWhere(update.where);
+ List uids = parseWhere(update.where, xid);
Field fd = null;
//找到要更新的表字段
for (Field f : fields) {
- if(f.fieldName.equals(update.fieldName)) {
+ if (f.fieldName.equals(update.fieldName)) {
fd = f;
break;
}
}
- if(fd == null) {
+ if (fd == null) {
throw Error.FieldNotFoundException;
}
//将value转化为字段对应的字段类型
@@ -140,21 +144,21 @@ public int update(long xid, Update update) throws Exception {
//读取数据
for (Long uid : uids) {
//读取对应uid的字段值
- byte[] raw = ((TableManagerImpl)tbm).vm.read(xid, uid);
- if(raw == null) continue;
+ byte[] raw = ((TableManagerImpl) tbm).vm.read(xid, uid);
+ if (raw == null) continue;
//删除原有的数据
- ((TableManagerImpl)tbm).vm.delete(xid, uid);
+ ((TableManagerImpl) tbm).vm.delete(xid, uid);
//将要更改的数据放进去
Map entry = parseEntry(raw);
entry.put(fd.fieldName, value);
raw = entry2Raw(entry);
//添加数据
- long uuid = ((TableManagerImpl)tbm).vm.insert(xid, raw);
-
- count ++;
+ long uuid = ((TableManagerImpl) tbm).vm.insert(xid, raw);
+
+ count++;
for (Field field : fields) {
- if(field.isIndexed()) {
+ if (field.isIndexed()) {
field.insert(entry.get(field.fieldName), uuid);
}
}
@@ -162,34 +166,34 @@ public int update(long xid, Update update) throws Exception {
//修改的数据条数
return count;
}
+
//读取对应字段
public String read(long xid, Select read) throws Exception {
//先获得范围内的uid
- List uids = parseWhere(read.where);
+ List uids = parseWhere(read.where, xid);
StringBuilder sb = new StringBuilder();
//将范围内的数据读取出来
//然后加入到StringBuilder里面
for (Long uid : uids) {
- byte[] raw = ((TableManagerImpl)tbm).vm.read(xid, uid);
- if(raw == null) continue;
+ byte[] raw = ((TableManagerImpl) tbm).vm.read(xid, uid);
+ if (raw == null) continue;
Map entry = parseEntry(raw);
//根据Select语句中field的值,来读取对应的数据。
- if(read.fields[0].equals("*"))sb.append(printEntry(entry)).append("\n");
- else
- {
+ if (read.fields[0].equals("*")) sb.append(printEntry(entry)).append("\n");
+ else {
sb.append("[");
- for(String field:read.fields)
- {
+ for (String field : read.fields) {
sb.append(entry.get(field)).append(",");
}
- sb.deleteCharAt(sb.length()-1);
+ sb.deleteCharAt(sb.length() - 1);
sb.append("]");
sb.append("\n");
}
- // sb.append(printEntry(entry)).append("\n");
+ // sb.append(printEntry(entry)).append("\n");
}
return sb.toString();
}
+
//插入字段值
public void insert(long xid, Insert insert) throws Exception {
//将insert里面的值,转化为字段名-值这样的map
@@ -197,17 +201,18 @@ public void insert(long xid, Insert insert) throws Exception {
//将map转化为二进制数组
byte[] raw = entry2Raw(entry);
//把数据插入数据文件中
- long uid = ((TableManagerImpl)tbm).vm.insert(xid, raw);
+ long uid = ((TableManagerImpl) tbm).vm.insert(xid, raw);
for (Field field : fields) {
- if(field.isIndexed()) {
+ if (field.isIndexed()) {
//将对应的值插入索引树当中
field.insert(entry.get(field.fieldName), uid);
}
}
}
+
//将value放入map当中,key是对应的filedname
private Map string2Entry(String[] values) throws Exception {
- if(values.length != fields.size()) {
+ if (values.length != fields.size()) {
throw Error.InvalidValuesException;
}
Map entry = new HashMap<>();
@@ -219,16 +224,24 @@ private Map string2Entry(String[] values) throws Exception {
return entry;
}
//根据给定的条件where,在字段中fields进行查找
- private List parseWhere(Where where) throws Exception {
- long l0=0, r0=0, l1=0, r1=0;
+ private List parseWhere(Where where, long xid) throws Exception {
+ long l0 = 0, r0 = 0, l1 = 0, r1 = 0;
+ String fieldLeft=null, fieldRight=null;
boolean single = false;
Field fd = null;
+ List alluids=null;
+ boolean[] flag=null;
+ boolean[] right_flag=null;
+ ArrayList