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 744db80..fe1bb60 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 @@ -55,7 +55,7 @@ public int newPage(byte[] initData) { } public Page getPage(int pgno) throws Exception { - return get((long)pgno); + return get(pgno); } /** 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 2926eb4..ea48d2b 100644 --- a/src/main/java/top/guoziyang/mydb/backend/im/BPlusTree.java +++ b/src/main/java/top/guoziyang/mydb/backend/im/BPlusTree.java @@ -241,4 +241,36 @@ public void close() { static class InsertRes { long newNode, newKey; } + + /** + * 删除键值 + * @param key 键值 + */ + public void delete(long key) throws Exception { + long rootUid = rootUid(); + delete(rootUid, key); + } + + /** + * 递归删除键值 + * @param nodeUid 节点 UID + * @param key 键值 + */ + private void delete(long nodeUid, long key) throws Exception { + Node node = Node.loadNode(this, nodeUid); + boolean isLeaf = node.isLeaf(); + node.release(); + + if (isLeaf) { + Node leaf = Node.loadNode(this, nodeUid); + leaf.remove(key); + leaf.release(); + } else { + long next = searchNext(nodeUid, key); + delete(next, key); + Node internalNode = Node.loadNode(this, nodeUid); + internalNode.remove(key); + internalNode.release(); + } + } } \ No newline at end of file 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 529d948..d010361 100644 --- a/src/main/java/top/guoziyang/mydb/backend/im/Node.java +++ b/src/main/java/top/guoziyang/mydb/backend/im/Node.java @@ -355,4 +355,67 @@ static class SplitRes { long newSon, newKey; } + /** + * 从节点中删除指定的键值 + * @param key 要删除的键值 + * @return 返回是否成功删除 + */ + public boolean remove(long key) throws Exception { + dataItem.before(); + try { + boolean result = removeKey(key); + if (result && needMerge()) { + merge(); + } + return result; + } finally { + dataItem.after(TransactionManagerImpl.SUPER_XID); + } + } + + /** + * 从节点中删除指定的键值,并重新排列节点 + * @param key 要删除的键值 + * @return 返回是否成功删除 + */ + private boolean removeKey(long key) { + int noKeys = getRawNoKeys(raw); + int kth = 0; + while (kth < noKeys) { + long ik = getRawKthKey(raw, kth); + if (ik == key) { + break; + } + kth++; + } + if (kth == noKeys || getRawKthKey(raw, kth) != key) { + return false; + } + + if (getRawIfLeaf(raw)) { + shiftRawKth(raw, kth); + setRawNoKeys(raw, noKeys - 1); + } else { + shiftRawKth(raw, kth); + setRawNoKeys(raw, noKeys - 1); + } + return true; + } + + /** + * 判断是否需要合并节点 + * @return 是否需要合并 + */ + private boolean needMerge() { + return getRawNoKeys(raw) < BALANCE_NUMBER / 2; + } + + /** + * 合并当前节点 + * @throws Exception + */ + private void merge() throws Exception { + // Implement the merge logic here + } + } 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 29eaefa..11ff9f5 100644 --- a/src/main/java/top/guoziyang/mydb/backend/parser/Parser.java +++ b/src/main/java/top/guoziyang/mydb/backend/parser/Parser.java @@ -291,7 +291,12 @@ private static boolean isLogicOp(String op) { return ("and".equals(op) || "or".equals(op)); } - private static Drop parseDrop(Tokenizer tokenizer) throws Exception { + private static Object parseDrop(Tokenizer tokenizer) throws Exception { + if ("all".equals(tokenizer.peek())) { + tokenizer.pop(); + return new DropAll(); + } + if (!"table".equals(tokenizer.peek())) { throw Error.InvalidCommandException; } @@ -313,6 +318,14 @@ private static Drop parseDrop(Tokenizer tokenizer) throws Exception { return drop; } + private static DropAll parseDropAll(Tokenizer tokenizer) throws Exception { + tokenizer.pop(); + if ("all".equals(tokenizer.peek())) { + return new DropAll(); + } + throw Error.InvalidCommandException; + } + private static Create parseCreate(Tokenizer tokenizer) throws Exception { if (!"table".equals(tokenizer.peek())) { throw Error.InvalidCommandException; diff --git a/src/main/java/top/guoziyang/mydb/backend/parser/statement/DropAll.java b/src/main/java/top/guoziyang/mydb/backend/parser/statement/DropAll.java new file mode 100644 index 0000000..2fd2874 --- /dev/null +++ b/src/main/java/top/guoziyang/mydb/backend/parser/statement/DropAll.java @@ -0,0 +1,5 @@ +package top.guoziyang.mydb.backend.parser.statement; + +public class DropAll { + +} 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 e3f5875..b1eda1f 100644 --- a/src/main/java/top/guoziyang/mydb/backend/server/Executor.java +++ b/src/main/java/top/guoziyang/mydb/backend/server/Executor.java @@ -92,6 +92,12 @@ private byte[] execute2(Object stat) throws Exception { res = tbm.showTables(xid); } else if (stat instanceof Create) { res = tbm.create(xid, (Create) stat); + } else if (stat instanceof Drop) { + res = "COMMAND IS UNSUPPORTED".getBytes(); +// res = tbm.drop(xid, (Drop) stat); + } else if (stat instanceof DropAll) { + res = "COMMAND IS UNSUPPORTED".getBytes(); +// res = tbm.dropAll(xid, (DropAll) stat); } else if (stat instanceof Select) { res = tbm.read(xid, (Select) stat); } else if (stat instanceof Insert) { 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 5a917b9..cc6bd33 100644 --- a/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java +++ b/src/main/java/top/guoziyang/mydb/backend/tbm/Field.java @@ -27,7 +27,7 @@ public class Field { String fieldType; private final Table tb; private long index; - private BPlusTree bt; + BPlusTree bt; public Field(long uid, Table tb) { this.uid = uid; 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 c6c465e..f241b91 100644 --- a/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java +++ b/src/main/java/top/guoziyang/mydb/backend/tbm/Table.java @@ -1,6 +1,8 @@ package top.guoziyang.mydb.backend.tbm; import com.google.common.primitives.Bytes; +import top.guoziyang.mydb.backend.dm.page.Page; +import top.guoziyang.mydb.backend.dm.pageCache.PageCache; import top.guoziyang.mydb.backend.parser.statement.*; import top.guoziyang.mydb.backend.tbm.Field.ParseValueRes; import top.guoziyang.mydb.backend.tm.TransactionManagerImpl; @@ -302,4 +304,17 @@ class CalWhereRes { long l0, r0, l1, r1; boolean single; } + + + private PageCache pageCache; // 页面缓存对象 + private int maxPageNumber; // 该表的最大页号 + + + public PageCache getPageCache() { + return pageCache; + } + + public int getMaxPageNumber() { + return maxPageNumber; + } } diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/TableManager.java b/src/main/java/top/guoziyang/mydb/backend/tbm/TableManager.java index 1e393ca..7b1cc55 100644 --- a/src/main/java/top/guoziyang/mydb/backend/tbm/TableManager.java +++ b/src/main/java/top/guoziyang/mydb/backend/tbm/TableManager.java @@ -52,6 +52,12 @@ static TableManager open(String path, VersionManager vm, DataManager dm) { // 创建一个新的表 byte[] create(long xid, Create create) throws Exception; + // 删除一张表 + byte[] drop(long xid, Drop drop) throws Exception; + + // 删除所有表 + byte[] dropAll(long xid, DropAll drop) throws Exception; + // 插入新的记录 byte[] insert(long xid, Insert insert) throws Exception; diff --git a/src/main/java/top/guoziyang/mydb/backend/tbm/TableManagerImpl.java b/src/main/java/top/guoziyang/mydb/backend/tbm/TableManagerImpl.java index 6e38925..45fbb9b 100644 --- a/src/main/java/top/guoziyang/mydb/backend/tbm/TableManagerImpl.java +++ b/src/main/java/top/guoziyang/mydb/backend/tbm/TableManagerImpl.java @@ -1,15 +1,15 @@ package top.guoziyang.mydb.backend.tbm; import top.guoziyang.mydb.backend.dm.DataManager; +import top.guoziyang.mydb.backend.dm.page.Page; +import top.guoziyang.mydb.backend.dm.pageCache.PageCache; +import top.guoziyang.mydb.backend.im.BPlusTree; import top.guoziyang.mydb.backend.parser.statement.*; import top.guoziyang.mydb.backend.utils.Parser; import top.guoziyang.mydb.backend.vm.VersionManager; import top.guoziyang.mydb.common.Error; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -42,10 +42,14 @@ private void loadTables() { while (uid != 0) { Table tb = Table.loadTable(this, uid); uid = tb.nextUid; - tableCache.put(tb.name, tb); + putTableCache(tb); } } + private void putTableCache(Table tb) { + tableCache.put(tb.name, tb); + } + private long firstTableUid() { byte[] raw = booter.load(); return Parser.parseLong(raw); @@ -104,10 +108,15 @@ public byte[] showTables(long xid) throws Exception { try { StringBuilder sb = new StringBuilder(); if (tableCache.isEmpty()) { - return "No tables found.".getBytes(); // 如果没有表,返回提示信息 + throw Error.TableNotFoundException; } - for (String tableName : tableCache.keySet()) { - sb.append(tableName).append("\n"); // 将每个表名追加到字符串 + Iterator iterator = tableCache.keySet().iterator(); + while (iterator.hasNext()) { + String tableName = iterator.next(); + sb.append(tableName); + if (iterator.hasNext()) { + sb.append("\n"); + } } return sb.toString().getBytes(); // 返回字节数组 } finally { @@ -125,7 +134,7 @@ public byte[] create(long xid, Create create) throws Exception { } Table table = Table.createTable(this, firstTableUid(), xid, create); updateFirstTableUid(table.uid); - tableCache.put(create.tableName, table); + putTableCache(table); if (!xidTableCache.containsKey(xid)) { xidTableCache.put(xid, new ArrayList<>()); } @@ -136,6 +145,212 @@ public byte[] create(long xid, Create create) throws Exception { } } + /** + * 有误! + * @param xid + * @param drop + * @return + * @throws Exception + */ + @Override + public byte[] drop(long xid, Drop drop) throws Exception { + lock.lock(); + + // 检查表是否存在 + Table table = tableCache.get(drop.tableName); + if (table == null) { + throw Error.TableNotFoundException; + } + + try { + // 从缓存中移除表 + tableCache.remove(table.name); + + // 从事务缓存中移除表 + List xidTables = xidTableCache.get(xid); + if (xidTables != null) { + xidTables.remove(table); + if (xidTables.isEmpty()) { + xidTableCache.remove(xid); + } + } + + // 删除表的持久化数据 + vm.delete(xid, table.uid); + + // 删除字段的索引(如果有的话) + for (Field field : table.fields) { + if (field.isIndexed()) { + BPlusTree bt = field.bt; + if (bt != null) { + bt.delete(xid); // 清除索引数据 + } + } + } + + // 删除表的所有数据页 + PageCache pageCache = getTablePageCache(table); + if (pageCache != null) { + // 清除所有数据页,将页号回归到 0 + pageCache.truncateByBgno(0); + } + + // 更新 Booter 文件,移除表的 UID + long head = updateBooterOnDrop(table.uid); + updateFirstTableUid(head); + + + return ("drop " + table.name).getBytes(); + } finally { + lock.unlock(); + } + } + + /** + * 找到所有属于表的数据页 + * @param table + * @return + */ + private PageCache getTablePageCache(Table table) { + PageCache pageCache = table.getPageCache(); + if (pageCache == null) { + System.out.println("PageCache is null for table " + table.name); + return null; + } + + int maxPageNumber = table.getMaxPageNumber(); + for (int pgno = 1; pgno <= maxPageNumber; pgno++) { + try { + Page page = pageCache.getPage(pgno); + if (page != null) { + page.release(); // 释放页面资源 + } + } catch (Exception e) { + // 可能有部分页面为空或者无法获取,忽略这些异常 + } + } + return pageCache; + } + + /** + * 移除表的 UID(未删除,只是移到了尾节点0后面,眼不见心不烦) + */ + private long updateBooterOnDrop(long uid) { + long head = firstTableUid(); + + // 如果链表为空或只有一个节点,直接返回头节点 + if (head == 0 || head == uid) { + return head; + } + + long preUid = head; + long curUid = Table.loadTable(this, head).nextUid; + + // 找到要删除的节点 + while (curUid != 0 && curUid != uid) { + preUid = curUid; + curUid = Table.loadTable(this, curUid).nextUid; + } + + // 如果找到了要删除的节点 + if (curUid == uid) { + Table preTable = Table.loadTable(this, preUid); + Table curTable = Table.loadTable(this, curUid); + preTable.nextUid = curTable.nextUid; // 跳过当前节点 + curTable.uid = -1; // 将当前节点标记为无效 + +// // 从链表中移除节点 +// Table preTable = Table.loadTable(this, preUid); +// Table curTable = Table.loadTable(this, curUid); +// preTable.nextUid = curTable.nextUid; // 跳过当前节点 +// +// // 找到链表的尾节点 +// long tailUid = head; +// while (Table.loadTable(this, tailUid).nextUid != 0) { +// tailUid = Table.loadTable(this, tailUid).nextUid; +// } +// +// // 将要删除的节点放到尾节点后面 +// Table tailTable = Table.loadTable(this, tailUid); +// tailTable.nextUid = uid; +// +// // 将被删除的节点放到链表末尾,nextUid设置为0 +// curTable.nextUid = 0; + } + + // 返回头节点 + return head; + } + + /** + * 有误! + * @param xid + * @param dropAll + * @return + * @throws Exception + */ + @Override + public byte[] dropAll(long xid, DropAll dropAll) throws Exception { + lock.lock(); + + + try { + + tableCache.forEach((k, v) -> { + + // 从缓存中移除表 + tableCache.remove(k); + + try { + // 删除字段的索引(如果有的话) + v.fields.forEach(f -> { + if (f.isIndexed()) { + BPlusTree bt = f.bt; + if (bt != null) { + try { + bt.delete(xid); // 清除索引数据 + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // 删除表的所有数据页 + PageCache pageCache = getTablePageCache(v); + if (pageCache != null) { + // 清除所有数据页,将页号回归到 0 + pageCache.truncateByBgno(0); + } + + + // 删除表的持久化数据 + try { + vm.delete(xid, v.uid); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + // 从事务缓存中移除表 + xidTableCache.forEach((k, v) -> { + xidTableCache.remove(k); + }); + + + // 更新 Booter 文件,移除表的 UID + updateFirstTableUid(0L); + + + return ("drop all tables").getBytes(); + } finally { + lock.unlock(); + } + } + @Override public byte[] insert(long xid, Insert insert) throws Exception { Table table = getTable(insert.tableName); 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 4b68be6..4c0f391 100644 --- a/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManager.java +++ b/src/main/java/top/guoziyang/mydb/backend/tm/TransactionManager.java @@ -1,5 +1,8 @@ package top.guoziyang.mydb.backend.tm; +import top.guoziyang.mydb.backend.utils.Panic; +import top.guoziyang.mydb.common.Error; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -7,9 +10,6 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; -import top.guoziyang.mydb.backend.utils.Panic; -import top.guoziyang.mydb.common.Error; - public interface TransactionManager { long begin(); void commit(long xid); diff --git a/src/test/java/top/guoziyang/mydb/backend/parser/ParserTest.java b/src/test/java/top/guoziyang/mydb/backend/parser/ParserTest.java index 9d82241..04ed020 100644 --- a/src/test/java/top/guoziyang/mydb/backend/parser/ParserTest.java +++ b/src/test/java/top/guoziyang/mydb/backend/parser/ParserTest.java @@ -119,4 +119,26 @@ public void testHelp() throws Exception { System.out.println(gson.toJson(help)); System.out.println("======================"); } + + @Test + public void testDrop() throws Exception { + String stat = "drop table test_table"; + Object res = Parser.Parse(stat.getBytes()); + Drop drop = (Drop) res; + Gson gson = new Gson(); + System.out.println("drop table test_table"); + System.out.println(gson.toJson(drop)); + System.out.println("======================"); + } + + @Test + public void testDropAll() throws Exception { + String stat = "drop all"; + Object res = Parser.Parse(stat.getBytes()); + DropAll dropAll = (DropAll) res; + Gson gson = new Gson(); + System.out.println("drop all"); + System.out.println(gson.toJson(dropAll)); + System.out.println("======================"); + } }