diff --git "a/MYDB\346\267\273\345\212\240\345\212\237\350\203\275.md" "b/MYDB\346\267\273\345\212\240\345\212\237\350\203\275.md" new file mode 100644 index 0000000..d357c40 --- /dev/null +++ "b/MYDB\346\267\273\345\212\240\345\212\237\350\203\275.md" @@ -0,0 +1,219 @@ +# MYDB添加功能 + +## 增加Where查找不等于的功能 + +在当前项目中, where只能查找等于,小于,大于,不能查找不等于,我在此基础上添加了不等于的功能。 + +希望达到的目标是:select * from table where not id = 10;这条语句能够成立。 + +核心思路是 + +1. 先检查where语句中是否有not,没有的话就按原样执行,有的话执行2 +2. where语句的思路是求出where语句中的范围,比如b>10,最后得出的范围就是11-Long.Max_value;如果where语句有not的话,就对这个范围取反。也就是求出id所有的值放入集合当中,然后把集合中对应的范围值取掉,最后返回这个集合 + +### 具体操作 + +在Where类当中添加一个flag,用于判断Where是否进行查找不等于的字段 + +``` +public class Where { + public SingleExpression singleExp1; + public String logicOp; + public SingleExpression singleExp2; + public boolean Notflag=false; +} +``` + +在parser类当中的parser.Where方法中,添加判断是否有NOT + +``` + int notPos = tokenizer.getPos(); + //保存tokenizer里的pos,方便回溯 + System.out.println(notPos); + String not = tokenizer.peek(); + if (not.equals("not")) + { + //如果有not,就更新where的flag为true + //并且将tokenizer刷新 + where.Notflag=true; + tokenizer.pop(); + } + else + { + //没有的话,将将pos设置回原来的值 + //刷新tokenizer + //如果不刷新的话,tokenizer就永远不变了 + tokenizer.setPos(notPos); + tokenizer.pop(); + } +``` + +接着在table类的parseWhere方法里面 + +在查找完字段的范围后,判断where.notFlag是否为true,如果是的话,就要对该范围进行取反。 + +``` +//判断是否有notfLAG + if(where != null && where.Notflag) + { + List alluids = fd.search(0,Long.MAX_VALUE); + for(Long uid:uids) + { + alluids.remove(uid); + } + return alluids; + } +``` + +### 遇到的问题 + +忘记对tokenizer进行pop的操作,导致tokenizer不能刷新,一直是一个值。 + +还有就是更改代码的时候,忘记先停止运行了... + +## 修改Select功能 + +目前Select语句存在缺陷,体现在 + +``` +Select * from table +Select id from table +``` + +这两个Sql语句是没有区别的,最后得到的结果都是第一个Sql语句的结果 + +​ 核心思路: + +1. 先弄清楚字段插入文件时,是以什么样的形式插入的 +2. 在Table类的insert方法中可以看到,它是把值转化成map的形式,以key-10,value-10的方式进行插入,再将这个map转化成byte数组的格式插入文件中 +3. 在Table类的insert方法中可以看到,它的读取也是这样的,先把byte数组转化成map,再把里面的数据读出来 +4. 我们只需要按照提供的field,来读取对应的数据就可以了。 + +### 具体操作 + +在Table类的read方法里,我们在获取了范围内的UID后就可以从数据文件中根据UID把数据读出来了。 + +我们在这里添加一个判断逻辑,如果Select.field里面是*的话,就按照原来的逻辑就行,即输出列的全部字段信息。 + +如果不是列的话,就根据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"); + } +``` + +### 遇到的问题 + +忘记了String字符串判断是否相等的时候,不是用==,而是使用equals + +## 全表扫描 + +在使用Select语句查询字段的时候呢,目前只支持基于索引去查询,如果使用不是索引的字段去查询的话呢,就会报错,现在我要增加一个全表扫描的功能 + +### 核心思路 + +1. 通过该表的索引,获取对应B+树上的所有数据 +2. 然后再去这些数据里进行查询即可。 + +### 具体操作 + +1. 修改了Field类里的 + + ``` + public FieldCalRes calExp(SingleExpression exp) throws Exception { + Object v = null; + FieldCalRes res = new FieldCalRes(); + res.field = exp.field; + ``` + + 目的是为了获取单个表达式里属性的值,即id>10里的id。 + +2.修改FieldCalRes类 + +``` +public class FieldCalRes { + public long left; + public long right; + public String field; +} +``` + +增加了一个filed属性,用来记录单个表达式的值 + +3.修改Table中的parseWhere方法 + +1. 首先获取当前表所有的数据,通过索引树获取 + + ``` + alluids = fd.search(0, Long.MAX_VALUE); + flag = new boolean[alluids.size()]; + right_flag=new boolean[alluids.size()]; + list = new ArrayList<>(); + for (Long uid : alluids) { + byte[] read = ((TableManagerImpl) tbm).vm.read(xid, uid); + //因为删除数据时,只是把dataitem的标志位设置为0 + //它本身的数据还是会存在索引树、文件当中,所以在读取数据的时候就要注意树否为空 + //否则会出现空指针的情况 + if(read==null) + { + list.add(new HashMap<>()); + continue; + } + Map map = parseEntry(read); + list.add(map); + } + ``` + +2. 接着开始解析Where,如果表达式的属性有索引的话,就正常处理 + +3. 如果不是的话,就进行全表扫描,实现方式为 + + ``` + { + for (int i = 0; i < list.size(); i++) { + Map map = list.get(i); + if (map.size() == 0) continue; + int temp = (int) map.get(fieldLeft); + if (temp >= l0 && temp <= r0) { + flag[i] = true; + } + } + left_res = new ArrayList<>(); + for (int j = 0; j < alluids.size(); j++) { + if (flag[j]) left_res.add(alluids.get(j)); + } + uids = left_res; + } + ``` + + 在前面获取的该表所有数据里,一个个查找符合条件的数据。 + +4. 判断是否是单一表达式,如果是的话,直接返回uid就行了 + +5. 不是的话,就和前面一样,根据表达式的属性来求范围,求完之后要处理左右两边范围的合并,这里需要判断where.logicop是什么,如果是and就取交集,如果是or的话就取交集。 + + ``` + if(where.logicOp.equals("and"))uids.retainAll(right_res); + else + { + Set mergedSet = new HashSet<>(uids); + mergedSet.addAll(right_res); + // 将 Set 转换回列表得到最终结果 + uids = new ArrayList<>(mergedSet); + } + ``` + +### 遇到的问题 + +1. 第一个遇到的问题是:获取到当前表的所有数据的alluid后,要读取当前这些uid将其具体数据放入list,在后面的全表扫描中,我们需要在这个list当中查找符合条件的数据,并返回对应的uid;在当前使用的数据结构,没办法把uid和list放在一起,所以我选择创建一个flag数组,用来记录对应的位置是否满足条件,最后根据flag的值来返回对应的uid。 +2. 第二个问题:由于在本项目中,删除数据是设置dataitem的flag,而不是将其从数据文件中清除,所以对应数据的uid其实还是存在索引树当中,我们获取当前表所有数据时,就会获得已删除的数据的uid,只不过对应的具体数据是读取不到的;这样情况下,后面进行全表扫描时,会进入map来查找数据,有可能会触发空指针异常,所以我在这里添加了一个判断,判断map.size是否为空来规避。 \ No newline at end of file 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..186cb42 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("/Users/nica/Documents/MYDB/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..9fb992b 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,10 +208,11 @@ public String toString() { .append(")") .toString(); } - + //将表达式a>b这种类型变成? 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 05a7a70..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; /** @@ -35,10 +33,11 @@ public class Table { 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); } @@ -47,18 +46,21 @@ public static Table loadTable(TableManager tbm, long 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; } } + //创建字段 tb.fields.add(Field.createField(tb, xid, fieldName, fieldType, indexed)); } @@ -76,105 +78,141 @@ public Table(TableManager tbm, String tableName, long nextUid) { 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)); + nextUid = Parser.parseLong(Arrays.copyOfRange(raw, position, position + 8)); position += 8; - - while(position < raw.length) { - long uid = Parser.parseLong(Arrays.copyOfRange(raw, position, position+8)); + //先读取字段的uid + //然后根据uid读取对应的字段值 + 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 { - List uids = parseWhere(update.where); + //获得where对应的数据uid + 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转化为字段对应的字段类型 Object value = fd.string2Value(update.value); int count = 0; + //读取数据 for (Long uid : uids) { - byte[] raw = ((TableManagerImpl)tbm).vm.read(xid, uid); - if(raw == null) continue; - - ((TableManagerImpl)tbm).vm.delete(xid, uid); - + //读取对应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 ++; + //添加数据 + 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); } } } + //修改的数据条数 return count; } + //读取对应字段 public String read(long xid, Select read) throws Exception { - List uids = parseWhere(read.where); + //先获得范围内的uid + 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); - 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); + //把数据插入数据文件中 + 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<>(); @@ -185,14 +223,25 @@ private Map string2Entry(String[] values) throws Exception { } return entry; } - - private List parseWhere(Where where) throws Exception { - long l0=0, r0=0, l1=0, r1=0; + //根据给定的条件where,在字段中fields进行查找 + 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; - if(where == null) { + List alluids=null; + boolean[] flag=null; + boolean[] right_flag=null; + ArrayList> list=null; + List left_res = null; + List right_res=null; + List uids=null; + //如果where为空 + //则在所有的字段查找是否有索引字段 + //选择第一个索引字段作为查询字段条件 + if (where == null) { for (Field field : fields) { - if(field.isIndexed()) { + if (field.isIndexed()) { fd = field; break; } @@ -201,27 +250,134 @@ 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; + if (field.isIndexed()) + { + fd = field; + break; } - fd = field; - break; - } } - if(fd == null) { + if (fd == null) { throw Error.FieldNotFoundException; } + alluids = fd.search(0, Long.MAX_VALUE); + flag = new boolean[alluids.size()]; + right_flag=new boolean[alluids.size()]; + list = new ArrayList<>(); + for (Long uid : alluids) { + byte[] read = ((TableManagerImpl) tbm).vm.read(xid, uid); + //因为删除数据时,只是把dataitem的标志位设置为0 + //它本身的数据还是会存在索引树、文件当中,所以在读取数据的时候就要注意树否为空 + //否则会出现空指针的情况 + if(read==null) + { + list.add(new HashMap<>()); + continue; + } + Map map = parseEntry(read); + list.add(map); + } CalWhereRes res = calWhere(fd, where); - l0 = res.l0; r0 = res.r0; - l1 = res.l1; r1 = res.r1; + l0 = res.l0; + r0 = res.r0; + l1 = res.l1; + r1 = res.r1; + fieldLeft = res.fieldLeft; + fieldRight = res.fieldRight; single = res.single; } - List uids = fd.search(l0, r0); - if(!single) { + //判断字段值是否是索引,不是的话就用全表扫描 + if(where!=null) + { + if(fieldLeft.equals(fd.fieldName)) + { + uids = fd.search(l0, r0); + } + //在全部数据,list当中,找出符合条件的数据,并且将对应的flag设置为true + //然后根据flag和alluid,给left-res赋值,代表where左边的范围 + else + { + for (int i = 0; i < list.size(); i++) { + Map map = list.get(i); + if (map.size() == 0) continue; + int temp = (int) map.get(fieldLeft); + if (temp >= l0 && temp <= r0) { + flag[i] = true; + } + } + left_res = new ArrayList<>(); + for (int j = 0; j < alluids.size(); j++) { + if (flag[j]) left_res.add(alluids.get(j)); + } + uids = left_res; + } + if(!single) + { + //右边的表达式是索引的情况 + if(fieldRight.equals(fd.fieldName)) + { + List tmp = fd.search(l1, r1); + //根据where的符号来决定是取交集还是并集 + if(where.logicOp.equals("and")) uids.retainAll(tmp); + else + { + Set mergedSet = new HashSet<>(uids); + mergedSet.addAll(tmp); + // 将 Set 转换回列表得到最终结果 + uids = new ArrayList<>(mergedSet); + } + } + //右边的表达式不是索引的情况 + else + { + for(int i=0;i map = list.get(i); + if(map.size()==0) continue; + int temp = (int) map.get(fieldRight); + if (temp >= l1 && temp <= r1) { + right_flag[i]=true; + } + } + right_res = new ArrayList<>(); + for(int j=0;j mergedSet = new HashSet<>(uids); + mergedSet.addAll(right_res); + // 将 Set 转换回列表得到最终结果 + uids = new ArrayList<>(mergedSet); + } + + } + } + } + else + { + //根据where获得的字段值范围,来查找字段的uid + uids = fd.search(l0, r0); + if (!single) + { List tmp = fd.search(l1, r1); uids.addAll(tmp); + } + } + + + //判断是否有notfLAG + if (where != null && where.Notflag) { + List all_uids = fd.search(0, Long.MAX_VALUE); + for (Long uid : uids) { + all_uids.remove(uid); + } + return all_uids; } return uids; } @@ -229,31 +385,44 @@ private List parseWhere(Where where) throws Exception { class CalWhereRes { long l0, r0, l1, r1; boolean single; + String fieldLeft, fieldRight; } + //字段名 private CalWhereRes calWhere(Field fd, Where where) throws Exception { CalWhereRes res = new CalWhereRes(); - switch(where.logicOp) { + switch (where.logicOp) { case "": res.single = true; FieldCalRes r = fd.calExp(where.singleExp1); - res.l0 = r.left; res.r0 = r.right; + res.l0 = r.left; + res.r0 = r.right; + res.fieldLeft = r.field; break; case "or": res.single = false; r = fd.calExp(where.singleExp1); - res.l0 = r.left; res.r0 = r.right; + res.l0 = r.left; + res.r0 = r.right; + res.fieldLeft = r.field; r = fd.calExp(where.singleExp2); - res.l1 = r.left; res.r1 = r.right; + res.l1 = r.left; + res.r1 = r.right; + res.fieldRight = r.field; break; case "and": - res.single = true; + res.single = false; r = fd.calExp(where.singleExp1); - res.l0 = r.left; res.r0 = r.right; + res.l0 = r.left; + res.r0 = r.right; + res.fieldLeft = r.field; 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; + res.l1 = r.left; + res.r1 = r.right; + res.fieldRight = r.field; + //合并范围 + if (res.l1 > res.l0) res.l0 = res.l1; + if (res.r1 < res.r0) res.r0 = res.r1; break; default: throw Error.InvalidLogOpException; @@ -261,12 +430,14 @@ 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++) { Field field = fields.get(i); sb.append(field.printValue(entry.get(field.fieldName))); - if(i == fields.size()-1) { + if (i == fields.size() - 1) { sb.append("]"); } else { sb.append(", "); @@ -275,6 +446,8 @@ private String printEntry(Map entry) { return sb.toString(); } + //把要更改的数据传进来 + // private Map parseEntry(byte[] raw) { int pos = 0; Map entry = new HashMap<>(); @@ -286,6 +459,8 @@ private Map parseEntry(byte[] raw) { return entry; } + //将map转化为二进制数组 + //只把里面的值存进字节数组 private byte[] entry2Raw(Map entry) { byte[] raw = new byte[0]; for (Field field : fields) { @@ -294,13 +469,15 @@ private byte[] entry2Raw(Map entry) { return raw; } + //把表的基本信息转化成string + //为了打印出来 @Override public String toString() { StringBuilder sb = new StringBuilder("{"); sb.append(name).append(": "); - for(Field field : fields) { + for (Field field : fields) { sb.append(field.toString()); - if(field == fields.get(fields.size()-1)) { + if (field == fields.get(fields.size() - 1)) { sb.append("}"); } else { sb.append(", "); 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); }