├── part1 ├── ch01.md ├── ch02.md ├── ch03.md └── part1.md ├── myQRcode.png ├── part3 ├── part3-01.png ├── ch12 │ ├── 12-1.png │ ├── 12-10.png │ ├── 12-2.png │ ├── 12-3.png │ ├── 12-4.png │ ├── 12-5.png │ ├── 12-6.png │ ├── 12-8.png │ └── 12-9.png ├── ch13 │ ├── 13-11.png │ ├── 13-2.png │ ├── 13-5.png │ ├── 13-7.png │ ├── simpledb.png │ ├── log-test-vis.png │ └── log-test-result.png ├── ch14 │ ├── 14-21.png │ ├── 14-24.png │ ├── 14-25.png │ ├── test_result_1.png │ └── test_result_2.png ├── ch15 │ ├── 15-1.png │ ├── 15-2.png │ ├── 15-3.png │ ├── 15-4.png │ ├── 15-5.png │ ├── 15-6.png │ ├── 15-7.png │ ├── 15-8.png │ ├── 15-9.png │ └── test-2.png ├── ch16 │ ├── 16-3.png │ └── 16-7.png ├── ch17 │ ├── 17-12.png │ ├── 17-13.png │ ├── 17-16.png │ ├── 17-4.png │ ├── 17-5.png │ ├── 17-7.png │ ├── 17-14-1.png │ └── 17-14-2.png ├── ch18 │ ├── 18-1.png │ └── 18-3.png ├── ch19 │ ├── 19-5.png │ ├── 19-6.png │ └── 19-8.png ├── ch20 │ └── 20-2.png └── part3.md ├── part4 ├── ch21 │ ├── 21-10.png │ ├── 21-11.png │ ├── 21-12.png │ ├── 21-14.png │ ├── 21-15.png │ ├── 21-16.png │ ├── 21-17.png │ ├── 21-18.png │ ├── 21-4.png │ ├── 21-6.png │ ├── 21-8.png │ └── 21-9.png └── part4.md ├── SimpleDB_Chinese_V1_0.pdf ├── .gitignore ├── simpledb └── src │ └── simpledb │ ├── parser │ ├── BadSyntaxException.java │ ├── CreateViewData.java │ ├── DeleteData.java │ ├── CreateTableData.java │ ├── CreateIndexData.java │ ├── InsertData.java │ ├── ModifyData.java │ ├── QueryData.java │ └── Lexer.java │ ├── buffer │ ├── BufferAbortException.java │ ├── PageFormatter.java │ ├── ABCStringFormatter.java │ ├── BufferTest.java │ ├── BufferMgr.java │ ├── Buffer.java │ └── BasicBufferMgr.java │ ├── query │ ├── Constant.java │ ├── Plan.java │ ├── UpdateScan.java │ ├── Expression.java │ ├── Scan.java │ ├── PredicateTest.java │ ├── IntConstant.java │ ├── StringConstant.java │ ├── ConstantExpression.java │ ├── FieldNameExpression.java │ ├── ProjectPlan.java │ ├── TablePlan.java │ ├── ProductPlan.java │ ├── SelectPlan.java │ ├── ProjectScan.java │ ├── ProductScan.java │ ├── TableScan.java │ ├── SelectScan.java │ └── Term.java │ ├── tx │ ├── concurrency │ │ ├── LockAbortException.java │ │ ├── ConcurrencyMgr.java │ │ └── LockTable.java │ ├── recovery │ │ ├── LogRecord.java │ │ ├── CommitRecord.java │ │ ├── CheckpointRecord.java │ │ ├── RollBackRecord.java │ │ ├── StartRecord.java │ │ ├── LogRecordIterator.java │ │ ├── SetIntRecord.java │ │ └── SetStringRecord.java │ ├── BufferList.java │ ├── Transaction.java │ └── TransactionTest.java │ ├── remote │ ├── RemoteStatement.java │ ├── RemoteDriver.java │ ├── RemoteMetaData.java │ ├── RemoteConnection.java │ ├── RemoteResultSet.java │ ├── RemoteDriverImpl.java │ ├── SimpleConnection.java │ ├── SimpleStatement.java │ ├── SimpleDriver.java │ ├── DriverAdapter.java │ ├── RemoteConnectionImpl.java │ ├── SimpleMetaData.java │ ├── ClientTest.java │ ├── SimpleResultSet.java │ ├── RemoteMetaDataImpl.java │ ├── RemoteStatementImpl.java │ ├── RemoteResultSetImpl.java │ └── MetaDataAdapter.java │ ├── planner │ ├── QueryPlanner.java │ ├── UpdatePlanner.java │ ├── BasicQueryPlanner.java │ ├── Planner.java │ ├── PlannerTest.java │ └── BasicUpdatePlanner.java │ ├── record │ ├── SchemaTest.java │ ├── RID.java │ ├── RecordFileTest.java │ ├── RecordFormatter.java │ ├── TableInfo.java │ ├── RecordPageTest.java │ ├── Schema.java │ └── RecordFile.java │ ├── index │ ├── Index.java │ ├── btree │ │ ├── DirEntry.java │ │ ├── BTreePageFormatter.java │ │ ├── BTreeDir.java │ │ └── BTreeLeaf.java │ ├── query │ │ ├── IndexSelectPlan.java │ │ ├── IndexSelectScan.java │ │ ├── IndexJoinPlan.java │ │ └── IndexJoinScan.java │ ├── IndexQueryTest.java │ ├── hash │ │ └── HashIndex.java │ └── IndexUpdateTest.java │ ├── server │ ├── Startup.java │ └── SimpleDB.java │ ├── log │ ├── BasicLogRecord.java │ ├── LogTest.java │ └── LogIterator.java │ ├── metadata │ ├── StatInfo.java │ ├── StatInfoTest.java │ ├── IndexInfoTest.java │ ├── TableTest.java │ ├── MetadataMgr.java │ ├── ViewMgr.java │ ├── IndexMgr.java │ ├── IndexInfo.java │ └── StatMgr.java │ └── file │ ├── Block.java │ ├── FileTest.java │ ├── Page.java │ └── FileMgr.java ├── book.json ├── FAQ.md ├── SUMMARY.md └── README.md /part1/ch01.md: -------------------------------------------------------------------------------- 1 | # 第一章 -------------------------------------------------------------------------------- /part1/ch02.md: -------------------------------------------------------------------------------- 1 | # 第二章 -------------------------------------------------------------------------------- /part1/ch03.md: -------------------------------------------------------------------------------- 1 | # 第三章 -------------------------------------------------------------------------------- /part1/part1.md: -------------------------------------------------------------------------------- 1 | # Part1 -------------------------------------------------------------------------------- /myQRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/myQRcode.png -------------------------------------------------------------------------------- /part3/part3-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/part3-01.png -------------------------------------------------------------------------------- /part3/ch12/12-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-1.png -------------------------------------------------------------------------------- /part3/ch12/12-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-10.png -------------------------------------------------------------------------------- /part3/ch12/12-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-2.png -------------------------------------------------------------------------------- /part3/ch12/12-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-3.png -------------------------------------------------------------------------------- /part3/ch12/12-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-4.png -------------------------------------------------------------------------------- /part3/ch12/12-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-5.png -------------------------------------------------------------------------------- /part3/ch12/12-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-6.png -------------------------------------------------------------------------------- /part3/ch12/12-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-8.png -------------------------------------------------------------------------------- /part3/ch12/12-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch12/12-9.png -------------------------------------------------------------------------------- /part3/ch13/13-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch13/13-11.png -------------------------------------------------------------------------------- /part3/ch13/13-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch13/13-2.png -------------------------------------------------------------------------------- /part3/ch13/13-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch13/13-5.png -------------------------------------------------------------------------------- /part3/ch13/13-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch13/13-7.png -------------------------------------------------------------------------------- /part3/ch14/14-21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch14/14-21.png -------------------------------------------------------------------------------- /part3/ch14/14-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch14/14-24.png -------------------------------------------------------------------------------- /part3/ch14/14-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch14/14-25.png -------------------------------------------------------------------------------- /part3/ch15/15-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-1.png -------------------------------------------------------------------------------- /part3/ch15/15-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-2.png -------------------------------------------------------------------------------- /part3/ch15/15-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-3.png -------------------------------------------------------------------------------- /part3/ch15/15-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-4.png -------------------------------------------------------------------------------- /part3/ch15/15-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-5.png -------------------------------------------------------------------------------- /part3/ch15/15-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-6.png -------------------------------------------------------------------------------- /part3/ch15/15-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-7.png -------------------------------------------------------------------------------- /part3/ch15/15-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-8.png -------------------------------------------------------------------------------- /part3/ch15/15-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/15-9.png -------------------------------------------------------------------------------- /part3/ch15/test-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch15/test-2.png -------------------------------------------------------------------------------- /part3/ch16/16-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch16/16-3.png -------------------------------------------------------------------------------- /part3/ch16/16-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch16/16-7.png -------------------------------------------------------------------------------- /part3/ch17/17-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-12.png -------------------------------------------------------------------------------- /part3/ch17/17-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-13.png -------------------------------------------------------------------------------- /part3/ch17/17-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-16.png -------------------------------------------------------------------------------- /part3/ch17/17-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-4.png -------------------------------------------------------------------------------- /part3/ch17/17-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-5.png -------------------------------------------------------------------------------- /part3/ch17/17-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-7.png -------------------------------------------------------------------------------- /part3/ch18/18-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch18/18-1.png -------------------------------------------------------------------------------- /part3/ch18/18-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch18/18-3.png -------------------------------------------------------------------------------- /part3/ch19/19-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch19/19-5.png -------------------------------------------------------------------------------- /part3/ch19/19-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch19/19-6.png -------------------------------------------------------------------------------- /part3/ch19/19-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch19/19-8.png -------------------------------------------------------------------------------- /part3/ch20/20-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch20/20-2.png -------------------------------------------------------------------------------- /part4/ch21/21-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-10.png -------------------------------------------------------------------------------- /part4/ch21/21-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-11.png -------------------------------------------------------------------------------- /part4/ch21/21-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-12.png -------------------------------------------------------------------------------- /part4/ch21/21-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-14.png -------------------------------------------------------------------------------- /part4/ch21/21-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-15.png -------------------------------------------------------------------------------- /part4/ch21/21-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-16.png -------------------------------------------------------------------------------- /part4/ch21/21-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-17.png -------------------------------------------------------------------------------- /part4/ch21/21-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-18.png -------------------------------------------------------------------------------- /part4/ch21/21-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-4.png -------------------------------------------------------------------------------- /part4/ch21/21-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-6.png -------------------------------------------------------------------------------- /part4/ch21/21-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-8.png -------------------------------------------------------------------------------- /part4/ch21/21-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part4/ch21/21-9.png -------------------------------------------------------------------------------- /part3/ch13/simpledb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch13/simpledb.png -------------------------------------------------------------------------------- /part3/ch17/17-14-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-14-1.png -------------------------------------------------------------------------------- /part3/ch17/17-14-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch17/17-14-2.png -------------------------------------------------------------------------------- /SimpleDB_Chinese_V1_0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/SimpleDB_Chinese_V1_0.pdf -------------------------------------------------------------------------------- /part3/ch13/log-test-vis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch13/log-test-vis.png -------------------------------------------------------------------------------- /part3/ch14/test_result_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch14/test_result_1.png -------------------------------------------------------------------------------- /part3/ch14/test_result_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch14/test_result_2.png -------------------------------------------------------------------------------- /part3/ch13/log-test-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiUzHiAn/simpleDB-gitbook/HEAD/part3/ch13/log-test-result.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | 4 | /_book 5 | 6 | /node_modules 7 | 8 | 9 | /simpledb/.DS_Store 10 | /simpledb/.idea/ 11 | /simpledb/out/ 12 | /simpledb/simpledb.iml 13 | 14 | /materials 15 | 16 | Database_Design_And_Implementation.pdf 17 | 18 | SimpleDB_2.10/ 19 | 20 | package-lock.json 21 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/BadSyntaxException.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | /** 4 | * @ClassName BadSyntaxException 5 | * @Deacription // TODO 6 | * @Author LiuZhian 7 | * @Date 2020-02-19 23:36 8 | * @Version 1.0 9 | **/ 10 | 11 | public class BadSyntaxException extends RuntimeException { 12 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/buffer/BufferAbortException.java: -------------------------------------------------------------------------------- 1 | package simpledb.buffer; 2 | 3 | /** 4 | * @ClassName BufferAbortException 5 | * @Deacription // TODO 6 | * @Author LiuZhian 7 | * @Date 2020-01-17 23:32 8 | * @Version 1.0 9 | **/ 10 | 11 | public class BufferAbortException extends RuntimeException { 12 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/Constant.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | /** 4 | * @ClassName Constant 5 | * @Description TODO 6 | * @Author LiuZhian 7 | * @Date 2020/2/13 7:52 下午 8 | * @Version 1.0 9 | */ 10 | public interface Constant extends Comparable { 11 | public Object asJavaVal(); 12 | } 13 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/concurrency/LockAbortException.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.concurrency; 2 | 3 | /** 4 | * @ClassName LockAbortException 5 | * @Deacription // TODO 6 | * @Author LiuZhian 7 | * @Date 2020-01-27 18:31 8 | * @Version 1.0 9 | **/ 10 | 11 | public class LockAbortException extends RuntimeException { 12 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/buffer/PageFormatter.java: -------------------------------------------------------------------------------- 1 | package simpledb.buffer; 2 | 3 | import simpledb.file.Page; 4 | 5 | /** 6 | * @ClassName PageFormatter 7 | * @Deacription // TODO 8 | * @Author LiuZhian 9 | * @Date 2020-01-17 19:33 10 | * @Version 1.0 11 | **/ 12 | 13 | public interface PageFormatter { 14 | public void format(Page p); 15 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteStatement.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.rmi.Remote; 4 | import java.rmi.RemoteException; 5 | 6 | public interface RemoteStatement extends Remote { 7 | public RemoteResultSet executeQuery(String qry) throws RemoteException; 8 | public int executeUpdateCmd(String cmd) throws RemoteException; 9 | } 10 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteDriver.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | 4 | import java.rmi.Remote; 5 | import java.rmi.RemoteException; 6 | 7 | /** 8 | * @ClassName RemoteDriver 9 | * @Deacription // TODO 10 | * @Author LiuZhian 11 | * @Date 2020-03-09 19:21 12 | * @Version 1.0 13 | **/ 14 | 15 | public interface RemoteDriver extends Remote { 16 | public RemoteConnection connect() throws RemoteException; 17 | } -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "SimpleDB中文版", 3 | "author": "刘知安", 4 | "description": "SimpleDB中文版", 5 | "language": "zh-hans", 6 | "plugins": [ 7 | "katex", 8 | "chapter-fold", 9 | "page-treeview" 10 | ], 11 | "pluginsConfig": { 12 | "page-treview": { 13 | "copyright": "", 14 | "style": "markdown", 15 | "minHeaderCount": "2", 16 | "minHeaderDeep": "2" 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteMetaData.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.rmi.Remote; 4 | import java.rmi.RemoteException; 5 | 6 | public interface RemoteMetaData extends Remote { 7 | public int getColumnCount() throws RemoteException; 8 | public String getColumnName(int column) throws RemoteException; 9 | public int getColumnType(int column) throws RemoteException; 10 | public int getColumnDisplaySize(int column) throws RemoteException; 11 | } 12 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteConnection.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.rmi.Remote; 4 | import java.rmi.RemoteException; 5 | 6 | /** 7 | * @ClassName RemoteConnection 8 | * @Deacription // TODO 9 | * @Author LiuZhian 10 | * @Date 2020-03-09 19:19 11 | * @Version 1.0 12 | **/ 13 | 14 | public interface RemoteConnection extends Remote { 15 | public RemoteStatement createStatement() throws RemoteException; 16 | 17 | public void close() throws RemoteException; 18 | } 19 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/LogRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | /** 4 | * @ClassName LogRecord 5 | * @Deacription // TODO 6 | * @Author LiuZhian 7 | * @Date 2020-01-21 14:20 8 | * @Version 1.0 9 | **/ 10 | 11 | public interface LogRecord { 12 | 13 | static final int CHECKPOINT = 0, START = 1, 14 | COMMIT = 2, ROLLBACK = 3, 15 | SETINT = 4, SETSTRING = 5; 16 | int writeToLog(); 17 | int op(); 18 | int txNumber(); 19 | void undo(); 20 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/planner/QueryPlanner.java: -------------------------------------------------------------------------------- 1 | package simpledb.planner; 2 | 3 | import simpledb.parser.QueryData; 4 | import simpledb.query.Plan; 5 | import simpledb.tx.Transaction; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @ClassName QueryPlanner 11 | * @Deacription // TODO 12 | * @Author LiuZhian 13 | * @Date 2020-02-26 17:26 14 | * @Version 1.0 15 | **/ 16 | 17 | public interface QueryPlanner { 18 | public Plan createPlan(QueryData queryData, Transaction tx) throws IOException; 19 | } -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # 常见问题 2 | 3 | ### 1. 本书的面向的读者? 4 | 本书的面向的读者是那些想要学习或研究数据库原理和底层实现的人,你最好对数据库的基本概念有个大概的了解,例如基本的SQL语句、数据库设计的几种范数、数据库的ACID原则等等,细节不清楚没关系,这本书就是展示细节的。本书假设你对操作系统、编译原理中的一些基本概念略有耳闻,例如磁盘访问、并发、多线程等等。 5 | 6 | ### 2. 关于错误 7 | 本书是译者在阅读英文版原书时翻译的手稿或者说笔记,难免会有很多错误,必然存在我的理解错误、口误、输入错误、翻译不准确众多问题,如果你有任何问题,请先仔细思考或查阅相关资料,如果的确有问题,请通过email联系。 8 | 9 | ### 3. 译本进度 10 | 目前只翻译了第12—16章,由于本人时间有限,因此会不定时地更新本译本,欢迎任何人共同加入到翻译此书的工作中来,如果你有意向,请email联系,也欢迎直接pull request。 11 | 12 | 译者:刘知安 13 | 14 | email:929910266@qq.com 15 | 16 | 最后更新时间:2020/02/26 17 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [介绍](README.md) 4 | * [Part 3 SimpleDB内部实现](part3/part3.md) 5 | * [第12章——磁盘及文件管理](part3/ch12.md) 6 | * [第13章——内存管理](part3/ch13.md) 7 | * [第14章——事务管理](part3/ch14.md) 8 | * [第15章——记录管理](part3/ch15.md) 9 | * [第16章——元数据管理](part3/ch16.md) 10 | * [第17章——查询处理](part3/ch17.md) 11 | * [第18章——SQL语句解析](part3/ch18.md) 12 | * [第19章——SQL Planning](part3/ch19.md) 13 | * [第20章——数据库服务](part3/ch20.md) 14 | * [Part 4 高效的查询处理](part4/part4.md) 15 | * [第21章——索引](part4/ch21.md) -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteResultSet.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.rmi.Remote; 4 | import java.rmi.RemoteException; 5 | 6 | public interface RemoteResultSet extends Remote { 7 | public boolean next() throws RemoteException; 8 | public int getInt(String fieldName) throws RemoteException; 9 | public String getString(String fieldName) throws RemoteException; 10 | public RemoteMetaData getMetaData() throws RemoteException; 11 | public void close() throws RemoteException; 12 | } 13 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/Plan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * @ClassName Plan 9 | * @Description TODO 10 | * @Author LiuZhian 11 | * @Date 2020/2/13 5:52 下午 12 | * @Version 1.0 13 | */ 14 | public interface Plan { 15 | public Scan open() throws IOException; 16 | public int blockAccessed(); 17 | public int recordsOutput(); 18 | public int distinctValues(String fldName); 19 | public Schema schema(); 20 | } 21 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/UpdateScan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.RID; 4 | 5 | import java.io.IOException; 6 | 7 | public interface UpdateScan extends Scan { 8 | public void setVal(String fieldName,Constant newVal); 9 | public void setInt(String fieldName,int newVal); 10 | public void setString(String fieldName,String newVal); 11 | public void insert() throws IOException; 12 | public void delete(); 13 | 14 | public RID getRID(); 15 | public void moveToRId(RID rid); 16 | } 17 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/Expression.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | /** 6 | * @ClassName Expression 7 | * @Description TODO 8 | * @Author LiuZhian 9 | * @Date 2020/2/18 7:43 下午 10 | * @Version 1.0 11 | */ 12 | public interface Expression { 13 | public boolean isConstant(); 14 | public boolean isFieldName(); 15 | public Constant asConstant(); 16 | public String asFieldName(); 17 | public Constant evaluate(Scan scan); 18 | public boolean appliesTo(Schema schema); 19 | } 20 | -------------------------------------------------------------------------------- /part4/part4.md: -------------------------------------------------------------------------------- 1 | # part 4—高效查询处理 2 | 3 | 在第三部分中,我们的关注点主要在于创建一个简单同时也功能完备的数据库系统。不幸的是,正是这种“简单”的实现为我们带来了代价—SimpleDB创建的查询计划(即`plan`)非常不高效。对于一些小的数据库而言,SimpleDB的不高效似乎还可以忍受,但是对于某个大型数据库的某个多表查询,可能需要几年的时间来执行一个查询任务,对于任何一个希望可以实际运行的数据库而言,这显然是不能接受的。在第4部分中,我们将主要关注高效性相关的问题。 4 | 5 | 之所以SimpleDB查询处理不高效的原因主要有两个: 6 | 7 | - 关系代数的实现很不高效; 8 | - 查询计划器(`planner`)采取的策略非常naive。 9 | 10 | 接下来的4章中我们将依次考虑这些问题。第21-23章将介绍3种技术来实现高效的关系代数操作:索引(`indexing`)、固化查询(`materialization`)以及对查询的输出排序(`sorting`)、更好地使用缓冲区(`buffers`)。第24章将考虑查询计划处理相关的问题,我们将学习查询计划器(`planner`)是怎样从多个等价的关系代数操作中得到那个效率最高的查询的,并且学习在关系代数树中不同操作的不同实现。 11 | 12 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/SchemaTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | public class SchemaTest { 4 | public static void main(String[] args) { 5 | Schema sch= new Schema(); 6 | sch.addIntField("cid"); 7 | sch.addStringField("title", 20); 8 | sch.addIntField("deptid"); 9 | TableInfo ti = new TableInfo("course", sch); 10 | 11 | for (String fldname : ti.schema().fields()) { 12 | int offset = ti.offset(fldname); 13 | System.out.println(fldname + " has offset " + offset); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/CreateViewData.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | /** 4 | * @ClassName CreateViewData 5 | * @Deacription // TODO 6 | * @Author LiuZhian 7 | * @Date 2020-02-20 21:25 8 | * @Version 1.0 9 | **/ 10 | 11 | public class CreateViewData { 12 | private String viewName; 13 | private QueryData qd; 14 | 15 | public CreateViewData(String viewName, QueryData qd) { 16 | this.viewName = viewName; 17 | this.qd = qd; 18 | } 19 | 20 | public String getViewName() { 21 | return viewName; 22 | } 23 | 24 | public String getViewDef() { 25 | return qd.toString(); 26 | } 27 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteDriverImpl.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.rmi.RemoteException; 4 | import java.rmi.server.UnicastRemoteObject; 5 | 6 | /** 7 | * @ClassName RemoteDriverImpl 8 | * @Deacription // TODO 9 | * @Author LiuZhian 10 | * @Date 2020-03-14 19:53 11 | * @Version 1.0 12 | **/ 13 | 14 | public class RemoteDriverImpl extends UnicastRemoteObject 15 | implements RemoteDriver { 16 | 17 | public RemoteDriverImpl() throws RemoteException { 18 | 19 | } 20 | 21 | @Override 22 | public RemoteConnection connect() throws RemoteException { 23 | return new RemoteConnectionImpl(); 24 | } 25 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/DeleteData.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | import simpledb.query.Predicate; 4 | 5 | /** 6 | * @ClassName DeleteData 7 | * @Deacription // TODO 8 | * @Author LiuZhian 9 | * @Date 2020-02-20 21:07 10 | * @Version 1.0 11 | **/ 12 | 13 | public class DeleteData { 14 | private String tblName; 15 | private Predicate pred; 16 | 17 | public DeleteData(String tblName, Predicate pred) { 18 | this.tblName = tblName; 19 | this.pred = pred; 20 | } 21 | 22 | public String getTblName() { 23 | return tblName; 24 | } 25 | 26 | public Predicate getPred() { 27 | return pred; 28 | } 29 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/CreateTableData.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | import simpledb.record.Schema; 4 | 5 | /** 6 | * @ClassName CreateTableData 7 | * @Deacription // TODO 8 | * @Author LiuZhian 9 | * @Date 2020-02-20 21:23 10 | * @Version 1.0 11 | **/ 12 | 13 | public class CreateTableData { 14 | private String tblName; 15 | private Schema schema; 16 | public CreateTableData(String tblName, Schema schema) { 17 | this.tblName=tblName; 18 | this.schema=schema; 19 | } 20 | 21 | public String getTblName() { 22 | return tblName; 23 | } 24 | 25 | public Schema getSchema() { 26 | return schema; 27 | } 28 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/buffer/ABCStringFormatter.java: -------------------------------------------------------------------------------- 1 | package simpledb.buffer; 2 | 3 | import simpledb.file.Page; 4 | 5 | import static simpledb.file.Page.BLOCK_SIZE; 6 | import static simpledb.file.Page.STR_SIZE; 7 | 8 | /** 9 | * @ClassName ABCStringFormatter 10 | * @Deacription // TODO 11 | * @Author LiuZhian 12 | * @Date 2020-01-17 20:15 13 | * @Version 1.0 14 | **/ 15 | 16 | public class ABCStringFormatter implements PageFormatter { 17 | @Override 18 | public void format(Page p) { 19 | int recSize = STR_SIZE("abc".length()); 20 | for (int i = 0; i + recSize <= BLOCK_SIZE; i += recSize) { 21 | p.setString(i, "abc"); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/Index.java: -------------------------------------------------------------------------------- 1 | package simpledb.index; 2 | 3 | import simpledb.query.Constant; 4 | import simpledb.record.RID; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * @ClassName Index 10 | * @Description TODO 11 | * @Author LiuZhian 12 | * @Date 2020/9/6 7:36 下午 13 | * @Version 1.0 14 | */ 15 | public interface Index { 16 | public void beforeFirst(Constant searchKey) throws IOException; 17 | public boolean next() throws IOException; 18 | public RID getDataRid(); 19 | public void insert(Constant dataval,RID datarid) throws IOException; 20 | public void delete(Constant dataval,RID datarid) throws IOException; 21 | public void close(); 22 | } 23 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/RID.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | /** 4 | * @ClassName RID 5 | * @Description TODO 6 | * @Author LiuZhian 7 | * @Date 2020/2/3 7:58 下午 8 | * @Version 1.0 9 | */ 10 | public class RID { 11 | private int blkNum; 12 | private int id; 13 | 14 | public RID(int blkNum, int id) { 15 | this.blkNum = blkNum; 16 | this.id = id; 17 | } 18 | 19 | public int blockNumber() { 20 | return blkNum; 21 | } 22 | 23 | public int id() { 24 | return id; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object obj) { 29 | RID rid = (RID) obj; 30 | return blkNum == rid.blkNum && id == rid.id; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/Scan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import java.io.IOException; 4 | 5 | public interface Scan { 6 | // 移动到第一条记录之前 7 | public void beforeFirst() throws IOException; 8 | 9 | // 移动到下一条记录,如果没有下一条记录,返回false 10 | public boolean next() throws IOException; 11 | 12 | // 关闭scan,如果有subScan,也会相应关闭 13 | public void close() throws IOException; 14 | 15 | // 获取当前记录指定字段的值,被抽象成了一个Constant对象 16 | public Constant getVal(String fieldName); 17 | 18 | // 获取当前字段指定int字段的值 19 | public int getInt(String fieldName); 20 | 21 | // 获取当前字段指定string字段的值 22 | public String getString(String fieldName); 23 | 24 | // 判断是否有指定字段 25 | public boolean hasField(String fieldName); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/CreateIndexData.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | /** 4 | * @ClassName CreateIndexData 5 | * @Deacription // TODO 6 | * @Author LiuZhian 7 | * @Date 2020-02-20 21:25 8 | * @Version 1.0 9 | **/ 10 | 11 | public class CreateIndexData { 12 | private String indexName; 13 | private String tblName; 14 | private String fldName; 15 | 16 | public CreateIndexData(String indexName, String tblName, 17 | String fldName) { 18 | this.indexName = indexName; 19 | this.tblName = tblName; 20 | this.fldName = fldName; 21 | } 22 | 23 | public String getIndexName() { 24 | return indexName; 25 | } 26 | 27 | public String getTblName() { 28 | return tblName; 29 | } 30 | 31 | public String getFldName() { 32 | return fldName; 33 | } 34 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/PredicateTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | /** 4 | * @ClassName PredicateTest 5 | * @Description TODO 6 | * @Author LiuZhian 7 | * @Date 2020/2/18 11:07 下午 8 | * @Version 1.0 9 | */ 10 | public class PredicateTest { 11 | public static void main(String[] args) { 12 | Expression lhs1=new FieldNameExpression("SName"); 13 | Expression rhs1=new ConstantExpression(new StringConstant("joe")); 14 | Term term1=new Term(lhs1,rhs1); 15 | 16 | Expression lhs2=new FieldNameExpression("MajorId"); 17 | Expression rhs2=new FieldNameExpression("DId"); 18 | Term term2=new Term(lhs2,rhs2); 19 | 20 | Predicate pred1=new Predicate(term1); 21 | System.out.println(pred1); 22 | pred1.conjoinWith(new Predicate(term2)); 23 | System.out.println(pred1); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/server/Startup.java: -------------------------------------------------------------------------------- 1 | package simpledb.server; 2 | 3 | import simpledb.remote.RemoteDriver; 4 | import simpledb.remote.RemoteDriverImpl; 5 | 6 | import java.rmi.registry.LocateRegistry; 7 | import java.rmi.registry.Registry; 8 | 9 | /** 10 | * @ClassName Startup 11 | * @Deacription // TODO 12 | * @Author LiuZhian 13 | * @Date 2020-03-14 19:10 14 | * @Version 1.0 15 | **/ 16 | 17 | public class Startup { 18 | public static void main(String args[]) throws Exception { 19 | // configure and initialize the database 20 | SimpleDB.init("lzadb"); 21 | 22 | // 在指定端口创建RMI注册表 23 | Registry reg = LocateRegistry.createRegistry(1099); 24 | 25 | // 服务端访问入口 26 | RemoteDriver d = new RemoteDriverImpl(); 27 | reg.rebind("simpledb", d); 28 | 29 | System.out.println("database server ready"); 30 | } 31 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/SimpleConnection.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | 4 | import java.sql.SQLException; 5 | import java.sql.Statement; 6 | 7 | public class SimpleConnection extends ConnectionAdapter { 8 | RemoteConnection rconn; 9 | 10 | public SimpleConnection(RemoteConnection rconn) { 11 | this.rconn = rconn; 12 | } 13 | 14 | @Override 15 | public Statement createStatement() throws SQLException { 16 | try { 17 | RemoteStatement rstmt = rconn.createStatement(); 18 | return new SimpleStatement(rstmt); 19 | } catch (Exception e) { 20 | throw new SQLException(e); 21 | } 22 | } 23 | 24 | @Override 25 | public void close() throws SQLException { 26 | try { 27 | rconn.close(); 28 | } catch (Exception e) { 29 | throw new SQLException(e); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/planner/UpdatePlanner.java: -------------------------------------------------------------------------------- 1 | package simpledb.planner; 2 | 3 | import simpledb.parser.*; 4 | import simpledb.tx.Transaction; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * @ClassName UpdatePlanner 10 | * @Deacription // TODO 11 | * @Author LiuZhian 12 | * @Date 2020-02-26 18:09 13 | * @Version 1.0 14 | **/ 15 | 16 | public interface UpdatePlanner { 17 | public int executeInsert(InsertData insertData, Transaction tx) throws IOException; 18 | public int executeDelete(DeleteData deleteData,Transaction tx) throws IOException; 19 | public int executeModify(ModifyData modifyData,Transaction tx) throws IOException; 20 | 21 | public int executeCreateTable(CreateTableData data,Transaction tx) throws IOException; 22 | public int executeCreateView(CreateViewData data, Transaction tx) throws IOException; 23 | public int executeCreateIndex(CreateIndexData data,Transaction tx) throws IOException; 24 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/SimpleStatement.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | 6 | public class SimpleStatement extends StatementAdapter { 7 | RemoteStatement rstmt; 8 | 9 | public SimpleStatement(RemoteStatement rstmt) { 10 | this.rstmt = rstmt; 11 | } 12 | 13 | @Override 14 | public ResultSet executeQuery(String sql) throws SQLException { 15 | try { 16 | RemoteResultSet rrs = rstmt.executeQuery(sql); 17 | return new SimpleResultSet(rrs); 18 | } catch (Exception e) { 19 | throw new SQLException(e); 20 | } 21 | } 22 | 23 | @Override 24 | public int executeUpdate(String sql) throws SQLException { 25 | try { 26 | return rstmt.executeUpdateCmd(sql); 27 | } catch (Exception e) { 28 | throw new SQLException(e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/log/BasicLogRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.log; 2 | 3 | import simpledb.file.Page; 4 | 5 | import static simpledb.file.Page.INT_SIZE; 6 | import static simpledb.file.Page.STR_SIZE; 7 | 8 | /** 9 | * @ClassName BasicLogRecord 10 | * @Deacription // TODO 11 | * @Author LiuZhian 12 | * @Date 2020-01-15 19:54 13 | * @Version 1.0 14 | **/ 15 | 16 | public class BasicLogRecord { 17 | 18 | private Page logPage; 19 | private int pos; 20 | 21 | 22 | public BasicLogRecord(Page logPage, int pos) { 23 | this.logPage = logPage; 24 | this.pos = pos; // 日志记录起始的位置 25 | } 26 | 27 | public int nextInt() { 28 | int intVal = logPage.getInt(pos); 29 | pos += INT_SIZE; 30 | return intVal; 31 | } 32 | 33 | public String nextString() { 34 | String stringVal = logPage.getString(pos); 35 | pos += STR_SIZE(stringVal.length()); 36 | return stringVal; 37 | } 38 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/StatInfo.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | /** 4 | * @ClassName StatInfo 5 | * @Description TODO 6 | * @Author LiuZhian 7 | * @Date 2020/2/5 8:52 下午 8 | * @Version 1.0 9 | */ 10 | public class StatInfo { 11 | private int numblocks; 12 | private int numRecords; 13 | 14 | public StatInfo(int numblocks, int numRecords) { 15 | this.numblocks = numblocks; 16 | this.numRecords = numRecords; 17 | } 18 | 19 | public int blocksAccessed() { 20 | return numblocks; 21 | } 22 | 23 | public int recordsOutput() { 24 | return numRecords; 25 | } 26 | 27 | /** 28 | * 统计某个字段的所有取值的数量。 29 | * 30 | * 当前仅用( 记录数/3 + 1 )来模拟。 31 | * @param filedName 32 | * @return 33 | */ 34 | public int distinctValues(String filedName) { 35 | // TODO 36 | // TODO 实际统计每个字段的取值情况 37 | return 1 + (numRecords / 3); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/btree/DirEntry.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.btree; 2 | 3 | import simpledb.query.Constant; 4 | 5 | /** 6 | * @program simpledb 7 | * @description: TODO 8 | * @author: liuzhian 9 | * @create: 2020/09/27 11:30 10 | */ 11 | public class DirEntry { 12 | // 目录记录的dataval 13 | private Constant dataval; 14 | // 如果当前目录记录的level为0,那么blockNum为索引块的块号 15 | // 如果当前目录记录的level大于0,那么blockNum为level-1级目录块的块号 16 | private int blockNum; 17 | 18 | public DirEntry(Constant dataval, int blockNum) { 19 | this.dataval = dataval; 20 | this.blockNum = blockNum; 21 | } 22 | 23 | public Constant getDataval() { 24 | return dataval; 25 | } 26 | 27 | public void setDataval(Constant dataval) { 28 | this.dataval = dataval; 29 | } 30 | 31 | public int getBlockNum() { 32 | return blockNum; 33 | } 34 | 35 | public void setBlockNum(int blockNum) { 36 | this.blockNum = blockNum; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/StatInfoTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.record.TableInfo; 4 | import simpledb.server.SimpleDB; 5 | import simpledb.tx.Transaction; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @ClassName StatInfoTest 11 | * @Description TODO 12 | * @Author LiuZhian 13 | * @Date 2020/2/5 9:01 下午 14 | * @Version 1.0 15 | */ 16 | public class StatInfoTest { 17 | public static void main(String[] args) throws IOException { 18 | SimpleDB.init("lzadb"); 19 | MetadataMgr metadataMgr = SimpleDB.metadataMgr(); 20 | 21 | Transaction tx = new Transaction(); 22 | TableInfo tableInfo=metadataMgr.getTableInfo("dept",tx); 23 | StatInfo statInfo=metadataMgr.getStatInfo("dept",tx); 24 | 25 | System.out.println(statInfo.blocksAccessed() + " " + 26 | statInfo.recordsOutput() + " " + 27 | statInfo.distinctValues("DName")); 28 | tx.commit(); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/InsertData.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | import simpledb.query.Constant; 4 | 5 | import java.util.Collection; 6 | 7 | /** 8 | * @ClassName InsertData 9 | * @Deacription // TODO 10 | * @Author LiuZhian 11 | * @Date 2020-02-20 21:02 12 | * @Version 1.0 13 | **/ 14 | 15 | public class InsertData { 16 | 17 | private String tblName; 18 | private Collection fields; 19 | private Collection vals; 20 | 21 | public InsertData(String tblName, 22 | Collection fields, 23 | Collection vals) { 24 | this.tblName = tblName; 25 | this.fields = fields; 26 | this.vals = vals; 27 | } 28 | 29 | public String getTblName() { 30 | return tblName; 31 | } 32 | 33 | public Collection getFields() { 34 | return fields; 35 | } 36 | 37 | public Collection getVals() { 38 | return vals; 39 | } 40 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/IntConstant.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | /** 4 | * @ClassName IntConstant 5 | * @Description TODO 6 | * @Author LiuZhian 7 | * @Date 2020/2/18 7:34 下午 8 | * @Version 1.0 9 | */ 10 | public class IntConstant implements Constant { 11 | private Integer val; 12 | 13 | public IntConstant(Integer val) { 14 | this.val = val; 15 | } 16 | 17 | @Override 18 | public int hashCode() { 19 | return val.hashCode(); 20 | } 21 | 22 | @Override 23 | public boolean equals(Object obj) { 24 | IntConstant ic = (IntConstant) obj; 25 | return ic != null && val.equals(ic.val); 26 | } 27 | 28 | 29 | @Override 30 | public String toString() { 31 | return val.toString(); 32 | } 33 | 34 | 35 | @Override 36 | public Object asJavaVal() { 37 | return val; 38 | } 39 | 40 | @Override 41 | public int compareTo(Constant o) { 42 | IntConstant ic = (IntConstant) o; 43 | return val.compareTo(ic.val); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/file/Block.java: -------------------------------------------------------------------------------- 1 | package simpledb.file; 2 | 3 | /** 4 | * @ClassName simpledb.file.Block 5 | * @Deacription // TODO 6 | * @Author LiuZhian 7 | * @Date 2020-01-14 14:45 8 | * @Version 1.0 9 | **/ 10 | 11 | public class Block { 12 | private String fileName; 13 | private int blkNum; 14 | 15 | public Block(String fileName, int blkNum) { 16 | this.fileName = fileName; 17 | this.blkNum = blkNum; 18 | } 19 | 20 | public String filename() { 21 | return fileName; 22 | } 23 | 24 | public int number() { 25 | return blkNum; 26 | } 27 | 28 | @Override 29 | public int hashCode() { 30 | return toString().hashCode(); 31 | } 32 | 33 | @Override 34 | public boolean equals(Object obj) { 35 | Block blk = (Block) obj; 36 | return fileName.equals(blk.fileName) && blkNum == blk.blkNum; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "[File: " + fileName + ", block number: " + blkNum + "]"; 42 | } 43 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/ModifyData.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | import simpledb.query.Expression; 4 | import simpledb.query.Predicate; 5 | 6 | /** 7 | * @ClassName ModifyData 8 | * @Deacription // TODO 9 | * @Author LiuZhian 10 | * @Date 2020-02-20 21:10 11 | * @Version 1.0 12 | **/ 13 | 14 | public class ModifyData { 15 | private String tblName; 16 | private String fldName; 17 | private Expression newVal; 18 | private Predicate pred; 19 | 20 | public ModifyData(String tblName, String fldName, 21 | Expression newVal, Predicate pred) { 22 | this.tblName = tblName; 23 | this.fldName = fldName; 24 | this.newVal = newVal; 25 | this.pred = pred; 26 | } 27 | 28 | public String getTblName() { 29 | return tblName; 30 | } 31 | 32 | public String getFldName() { 33 | return fldName; 34 | } 35 | 36 | public Expression getNewVal() { 37 | return newVal; 38 | } 39 | 40 | public Predicate getPred() { 41 | return pred; 42 | } 43 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/StringConstant.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | /** 4 | * @ClassName StringConstan 5 | * @Description TODO 6 | * @Author LiuZhian 7 | * @Date 2020/2/18 7:40 下午 8 | * @Version 1.0 9 | */ 10 | public class StringConstant implements Constant { 11 | private String val; 12 | 13 | public StringConstant(String val) { 14 | this.val = val; 15 | } 16 | 17 | @Override 18 | public int hashCode() { 19 | return val.hashCode(); 20 | } 21 | 22 | @Override 23 | public boolean equals(Object obj) { 24 | StringConstant sc = (StringConstant) obj; 25 | return sc != null && val.equals(sc.val); 26 | } 27 | 28 | 29 | @Override 30 | public String toString() { 31 | return val; 32 | } 33 | 34 | 35 | @Override 36 | public Object asJavaVal() { 37 | return val; 38 | } 39 | 40 | @Override 41 | public int compareTo(Constant o) { 42 | StringConstant sc = (StringConstant) o; 43 | return val.compareTo(sc.val); 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/SimpleDriver.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.rmi.Naming; 4 | import java.sql.Connection; 5 | import java.sql.Driver; 6 | import java.sql.SQLException; 7 | import java.util.Properties; 8 | 9 | public class SimpleDriver extends DriverAdapter { 10 | 11 | /** 12 | * 客户端根据指定的url访问数据库,得到一个连接。 13 | * 首先,我们需要将JDBC格式的url换成RMI格式; 14 | * 然后,我们通过Naming绑定机制,在RMI注册表中得到RemoteDriver对象的存根; 15 | * 最后,通过调用RemoteDriver存根对象的相关方法来获取RemoteConnection对象的存根。 16 | * 17 | * @param url 18 | * @param info 19 | * @return 20 | * @throws SQLException 21 | */ 22 | @Override 23 | public Connection connect(String url, Properties info) throws SQLException { 24 | try { 25 | // 把JDBC格式的url换成RMI格式 26 | String newURL = url.replace("jdbc:simpledb", "rmi") + "/simpledb"; 27 | RemoteDriver rdvr = (RemoteDriver) Naming.lookup(newURL); 28 | RemoteConnection rconn = rdvr.connect(); 29 | 30 | return new SimpleConnection(rconn); 31 | } catch (Exception e) { 32 | throw new SQLException(e); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/ConstantExpression.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | /** 6 | * @ClassName ConstanExpression 7 | * @Description TODO 8 | * @Author LiuZhian 9 | * @Date 2020/2/18 8:00 下午 10 | * @Version 1.0 11 | */ 12 | public class ConstantExpression implements Expression { 13 | private Constant val; 14 | 15 | public ConstantExpression(Constant val) { 16 | this.val = val; 17 | } 18 | 19 | @Override 20 | public boolean isConstant() { 21 | return true; 22 | } 23 | 24 | @Override 25 | public boolean isFieldName() { 26 | return false; 27 | } 28 | 29 | @Override 30 | public Constant asConstant() { 31 | return val; 32 | } 33 | 34 | @Override 35 | public String asFieldName() { 36 | throw new ClassCastException(); 37 | } 38 | 39 | @Override 40 | public Constant evaluate(Scan scan) { 41 | return val; 42 | } 43 | 44 | @Override 45 | public boolean appliesTo(Schema schema) { 46 | return true; 47 | } 48 | 49 | public String toString() 50 | { 51 | return val.toString(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/IndexInfoTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.index.Index; 4 | import simpledb.server.SimpleDB; 5 | import simpledb.tx.Transaction; 6 | 7 | import java.io.IOException; 8 | import java.util.Map; 9 | 10 | /** 11 | * @ClassName IndexInfoTest 12 | * @Description TODO 13 | * @Author LiuZhian 14 | * @Date 2020/2/6 12:59 下午 15 | * @Version 1.0 16 | */ 17 | public class IndexInfoTest { 18 | public static void main(String[] args) throws IOException { 19 | SimpleDB.init("lzadb"); 20 | // Transaction tx = new Transaction(); 21 | // MetadataMgr metadataMgr = SimpleDB.metadataMgr(); 22 | // Map indexes = metadataMgr.getIndexInfo("student", tx); 23 | 24 | // Part 1: Print the name and cost of each index on STUDENT 25 | // for (String fieldName : indexes.keySet()) { 26 | // IndexInfo indexInfo = indexes.get(fieldName); 27 | // System.out.println(fieldName + "\t" + indexInfo.blocksAccessed()); 28 | // } 29 | // 30 | // // Part 2: Open the index on MajorId 31 | // IndexInfo ii = indexes.get("majorid"); 32 | // Index idx = ii.open(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/DriverAdapter.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.sql.*; 4 | import java.util.Properties; 5 | import java.util.logging.Logger; 6 | 7 | public abstract class DriverAdapter implements Driver { 8 | @Override 9 | public Connection connect(String url, Properties info) throws SQLException { 10 | throw new SQLException("operation not implemented"); 11 | } 12 | 13 | @Override 14 | public boolean acceptsURL(String url) throws SQLException { 15 | throw new SQLException("operation not implemented"); 16 | } 17 | 18 | @Override 19 | public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { 20 | return null; 21 | } 22 | 23 | @Override 24 | public int getMajorVersion() { 25 | return 0; 26 | } 27 | 28 | @Override 29 | public int getMinorVersion() { 30 | return 0; 31 | } 32 | 33 | @Override 34 | public boolean jdbcCompliant() { 35 | return false; 36 | } 37 | 38 | @Override 39 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { 40 | throw new SQLFeatureNotSupportedException("operation not implemented"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/FieldNameExpression.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | /** 6 | * @ClassName FieldNameExpression 7 | * @Description TODO 8 | * @Author LiuZhian 9 | * @Date 2020/2/18 8:04 下午 10 | * @Version 1.0 11 | */ 12 | public class FieldNameExpression implements Expression { 13 | private String fldName; 14 | 15 | public FieldNameExpression(String fldName) { 16 | this.fldName = fldName; 17 | } 18 | 19 | @Override 20 | public boolean isConstant() { 21 | return false; 22 | } 23 | 24 | @Override 25 | public boolean isFieldName() { 26 | return true; 27 | } 28 | 29 | @Override 30 | public Constant asConstant() { 31 | throw new ClassCastException(); 32 | } 33 | 34 | @Override 35 | public String asFieldName() { 36 | return fldName; 37 | } 38 | 39 | @Override 40 | public Constant evaluate(Scan scan) { 41 | return scan.getVal(fldName); 42 | } 43 | 44 | @Override 45 | public boolean appliesTo(Schema schema) { 46 | return schema.hasFiled(fldName); 47 | } 48 | 49 | public String toString() 50 | { 51 | return fldName; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteConnectionImpl.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import simpledb.tx.Transaction; 4 | 5 | import java.rmi.RemoteException; 6 | import java.rmi.server.UnicastRemoteObject; 7 | 8 | /** 9 | * @ClassName RemoteConnectionImpl 10 | * @Deacription // TODO 11 | * @Author LiuZhian 12 | * @Date 2020-03-09 19:29 13 | * @Version 1.0 14 | **/ 15 | 16 | public class RemoteConnectionImpl extends UnicastRemoteObject 17 | implements RemoteConnection { 18 | private Transaction tx; 19 | 20 | public RemoteConnectionImpl() throws RemoteException { 21 | this.tx = new Transaction(); 22 | } 23 | 24 | @Override 25 | public RemoteStatement createStatement() throws RemoteException { 26 | return new RemoteStatementImpl(this); 27 | } 28 | 29 | @Override 30 | public void close() throws RemoteException{ 31 | tx.commit(); 32 | } 33 | 34 | //=============以下方法都是在服务端中调用的================ 35 | Transaction getTransaction() 36 | { 37 | return tx; 38 | } 39 | void commit() 40 | { 41 | tx.commit(); 42 | tx=new Transaction(); 43 | } 44 | void rollback() 45 | { 46 | tx.rollback(); 47 | tx=new Transaction(); 48 | } 49 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/ProjectPlan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | import java.io.IOException; 6 | import java.util.Collection; 7 | 8 | /** 9 | * @ClassName ProjectPlan 10 | * @Description TODO 11 | * @Author LiuZhian 12 | * @Date 2020/2/13 7:16 下午 13 | * @Version 1.0 14 | */ 15 | public class ProjectPlan implements Plan { 16 | private Plan plan; 17 | private Schema schema = new Schema(); 18 | 19 | public ProjectPlan(Plan plan, Collection fieldList) { 20 | this.plan=plan; 21 | for (String fieldName : fieldList) 22 | schema.add(fieldName,plan.schema()); 23 | } 24 | 25 | @Override 26 | public Scan open() throws IOException { 27 | Scan scan=plan.open(); 28 | return new ProjectScan(scan,schema.fields()); 29 | } 30 | 31 | @Override 32 | public int blockAccessed() { 33 | return plan.blockAccessed(); 34 | } 35 | 36 | @Override 37 | public int recordsOutput() { 38 | return plan.recordsOutput(); 39 | } 40 | 41 | @Override 42 | public int distinctValues(String fldName) { 43 | return plan.distinctValues(fldName); 44 | } 45 | 46 | @Override 47 | public Schema schema() { 48 | return schema; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/TableTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.record.RecordFile; 4 | import simpledb.record.Schema; 5 | import simpledb.record.TableInfo; 6 | import simpledb.server.SimpleDB; 7 | import simpledb.tx.Transaction; 8 | 9 | import java.io.IOException; 10 | 11 | public class TableTest { 12 | public static void main(String[] args) throws IOException { 13 | SimpleDB.init("lzadb"); 14 | MetadataMgr mtdMgr = SimpleDB.metadataMgr(); 15 | 16 | Transaction tx = new Transaction(); 17 | Schema schema = new Schema(); 18 | schema.addStringField("DName", 10); 19 | schema.addIntField("DId"); 20 | mtdMgr.createTable("dept", schema, tx); 21 | tx.commit(); 22 | 23 | Transaction tx2 = new Transaction(); 24 | TableInfo tableInfo = mtdMgr.getTableInfo("dept", tx2); 25 | RecordFile recordFile = new RecordFile(tableInfo, tx2); 26 | recordFile.insert(); 27 | recordFile.setString("DName","C.S."); 28 | recordFile.setInt("DId",12); 29 | 30 | // 开始遍历 31 | recordFile.beforeFirst(); 32 | while (recordFile.next()) 33 | System.out.println(recordFile.getInt("DId") + ", " + recordFile.getString("DName")); 34 | recordFile.close(); 35 | tx2.commit(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/RecordFileTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | import simpledb.server.SimpleDB; 4 | import simpledb.tx.Transaction; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * @ClassName RecordFileTest 10 | * @Description TODO 11 | * @Author LiuZhian 12 | * @Date 2020/2/3 9:19 下午 13 | * @Version 1.0 14 | */ 15 | public class RecordFileTest { 16 | public static void main(String[] args) throws IOException { 17 | SimpleDB.init("lza"); 18 | Transaction tx = new Transaction(); 19 | Schema schema = new Schema(); 20 | schema.addIntField("A"); 21 | TableInfo tableInfo = new TableInfo("junk", schema); 22 | 23 | RecordFile recordFile = new RecordFile(tableInfo, tx); 24 | for (int i = 0; i < 100; i++) { 25 | recordFile.insert(); 26 | int n = (int) Math.round(Math.random() * 200); 27 | recordFile.setInt("A", n); 28 | System.out.print(n+" "); 29 | } 30 | 31 | int cnt = 0; 32 | recordFile.beforeFirst(); 33 | while (recordFile.next()) { 34 | if (recordFile.getInt("A") < 100) { 35 | recordFile.delete(); 36 | cnt++; 37 | } 38 | } 39 | System.out.println("删除的记录数:" + cnt); 40 | recordFile.close(); 41 | tx.commit(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/SimpleMetaData.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.sql.SQLException; 4 | 5 | public class SimpleMetaData extends MetaDataAdapter { 6 | 7 | RemoteMetaData rmd; 8 | 9 | public SimpleMetaData(RemoteMetaData rmd) { 10 | this.rmd = rmd; 11 | } 12 | 13 | @Override 14 | public int getColumnCount() throws SQLException { 15 | try { 16 | return rmd.getColumnCount(); 17 | } catch (Exception e) { 18 | throw new SQLException(e); 19 | } 20 | } 21 | 22 | @Override 23 | public String getColumnName(int column) throws SQLException { 24 | try { 25 | return rmd.getColumnName(column); 26 | } catch (Exception e) { 27 | throw new SQLException(e); 28 | } 29 | } 30 | 31 | @Override 32 | public int getColumnType(int column) throws SQLException { 33 | try { 34 | return rmd.getColumnType(column); 35 | } catch (Exception e) { 36 | throw new SQLException(e); 37 | } 38 | } 39 | 40 | @Override 41 | public int getColumnDisplaySize(int column) throws SQLException { 42 | try { 43 | return rmd.getColumnDisplaySize(column); 44 | } catch (Exception e) { 45 | throw new SQLException(e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/CommitRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | import simpledb.log.BasicLogRecord; 4 | import simpledb.log.LogMgr; 5 | import simpledb.server.SimpleDB; 6 | 7 | /** 8 | * @ClassName CommitRecord 9 | * @Deacription // TODO 10 | * @Author LiuZhian 11 | * @Date 2020-01-21 15:27 12 | * @Version 1.0 13 | **/ 14 | 15 | public class CommitRecord implements LogRecord { 16 | 17 | private int myTxNum; 18 | 19 | public CommitRecord(int myTxNum) { 20 | this.myTxNum = myTxNum; 21 | } 22 | 23 | /** 24 | * 根据一条BasicLogRecord来构造一条CommitRecord。 25 | * 该构造函数是为了给 恢复/回滚 算法调用 26 | *

27 | * 注意,一条提交日志记录的格式为: 28 | *

29 | * 30 | * 31 | * @param blr 32 | */ 33 | public CommitRecord(BasicLogRecord blr) { 34 | myTxNum = blr.nextInt(); 35 | } 36 | 37 | @Override 38 | public int writeToLog() { 39 | Object[] rec = new Object[]{COMMIT, myTxNum}; 40 | LogMgr logMgr = SimpleDB.logMgr(); 41 | return logMgr.append(rec); 42 | } 43 | 44 | @Override 45 | public int op() { 46 | return COMMIT; 47 | } 48 | 49 | @Override 50 | public int txNumber() { 51 | return myTxNum; 52 | } 53 | 54 | @Override 55 | public void undo() { 56 | // empty is OK 57 | } 58 | 59 | public String toString() { 60 | return ""; 61 | } 62 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/CheckpointRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | import simpledb.log.BasicLogRecord; 4 | import simpledb.log.LogMgr; 5 | import simpledb.server.SimpleDB; 6 | 7 | /** 8 | * @ClassName CheckpointRecord 9 | * @Deacription // TODO 10 | * @Author LiuZhian 11 | * @Date 2020-01-21 15:32 12 | * @Version 1.0 13 | **/ 14 | 15 | public class CheckpointRecord implements LogRecord { 16 | 17 | 18 | public CheckpointRecord() { 19 | 20 | } 21 | 22 | /** 23 | * 根据一条BasicLogRecord来构造一条CheckpointRecord。 24 | * 该构造函数是为了给 恢复/回滚 算法调用 25 | *

26 | * 注意,一条提交日志记录的格式为: 27 | *

28 | * 29 | * 30 | * @param blr 31 | */ 32 | public CheckpointRecord(BasicLogRecord blr) { 33 | 34 | } 35 | 36 | @Override 37 | public int writeToLog() { 38 | Object[] rec = new Object[]{CHECKPOINT}; 39 | LogMgr logMgr = SimpleDB.logMgr(); 40 | return logMgr.append(rec); 41 | } 42 | 43 | @Override 44 | public int op() { 45 | return CHECKPOINT; 46 | } 47 | 48 | /** 49 | * Checkpoint 日志记录没有对应的事务ID 50 | * 51 | * @return -1,a dummy value 52 | */ 53 | @Override 54 | public int txNumber() { 55 | return -1; 56 | } 57 | 58 | @Override 59 | public void undo() { 60 | // empty is Okay 61 | } 62 | 63 | public String toString() { 64 | return ""; 65 | } 66 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/RollBackRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | import simpledb.log.BasicLogRecord; 4 | import simpledb.log.LogMgr; 5 | import simpledb.server.SimpleDB; 6 | 7 | /** 8 | * @ClassName RollBackRecord 9 | * @Deacription // TODO 10 | * @Author LiuZhian 11 | * @Date 2020-01-21 15:30 12 | * @Version 1.0 13 | **/ 14 | 15 | public class RollBackRecord implements LogRecord { 16 | private int myTxNum; 17 | 18 | public RollBackRecord(int myTxNum) { 19 | this.myTxNum = myTxNum; 20 | } 21 | 22 | /** 23 | * 根据一条BasicLogRecord来构造一条RollBackRecord。 24 | * 该构造函数是为了给 恢复/回滚 算法调用 25 | *

26 | * 注意,一条提交日志记录的格式为: 27 | *

28 | * 29 | * 30 | * @param blr 31 | */ 32 | public RollBackRecord(BasicLogRecord blr) { 33 | myTxNum = blr.nextInt(); 34 | } 35 | 36 | @Override 37 | public int writeToLog() { 38 | Object[] rec = new Object[]{ROLLBACK, myTxNum}; 39 | LogMgr logMgr = SimpleDB.logMgr(); 40 | return logMgr.append(rec); 41 | } 42 | 43 | @Override 44 | public int op() { 45 | return ROLLBACK; 46 | } 47 | 48 | @Override 49 | public int txNumber() { 50 | return myTxNum; 51 | } 52 | 53 | @Override 54 | public void undo() { 55 | // empty is OKay 56 | } 57 | 58 | public String toString() { 59 | return ""; 60 | } 61 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/TablePlan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.metadata.StatInfo; 4 | import simpledb.record.Schema; 5 | import simpledb.record.TableInfo; 6 | import simpledb.server.SimpleDB; 7 | import simpledb.tx.Transaction; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * @ClassName TablePlan 13 | * @Description TODO 14 | * @Author LiuZhian 15 | * @Date 2020/2/13 6:04 下午 16 | * @Version 1.0 17 | */ 18 | public class TablePlan implements Plan { 19 | private Transaction tx; 20 | private TableInfo tableInfo; 21 | private StatInfo statInfo; 22 | 23 | public TablePlan(String tblName,Transaction tx) throws IOException { 24 | this.tx = tx; 25 | this.tableInfo = SimpleDB.metadataMgr().getTableInfo(tblName,tx); 26 | this.statInfo = SimpleDB.metadataMgr().getStatInfo(tblName,tx); 27 | } 28 | 29 | @Override 30 | public Scan open() throws IOException { 31 | return new TableScan(tableInfo,tx); 32 | } 33 | 34 | @Override 35 | public int blockAccessed() { 36 | return statInfo.blocksAccessed(); 37 | } 38 | 39 | @Override 40 | public int recordsOutput() { 41 | return statInfo.recordsOutput(); 42 | } 43 | 44 | @Override 45 | public int distinctValues(String fldName) { 46 | return statInfo.distinctValues(fldName); 47 | } 48 | 49 | @Override 50 | public Schema schema() { 51 | return tableInfo.schema(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/planner/BasicQueryPlanner.java: -------------------------------------------------------------------------------- 1 | package simpledb.planner; 2 | 3 | import simpledb.parser.QueryData; 4 | import simpledb.query.*; 5 | import simpledb.server.SimpleDB; 6 | import simpledb.tx.Transaction; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @ClassName BasicQueryPlanner 14 | * @Deacription // TODO 15 | * @Author LiuZhian 16 | * @Date 2020-02-26 17:24 17 | * @Version 1.0 18 | **/ 19 | 20 | public class BasicQueryPlanner implements QueryPlanner { 21 | 22 | @Override 23 | public Plan createPlan(QueryData queryData, Transaction tx) throws IOException { 24 | // 步骤1: 对每个表或视图,创建一个plan 25 | List plans = new ArrayList<>(); 26 | for (String tblName : queryData.getTables()) { 27 | String viewDef = SimpleDB.metadataMgr().getViewDef(tblName, tx); 28 | if (null != viewDef) 29 | plans.add(SimpleDB.planner().createQueryPlan(viewDef, tx)); 30 | else 31 | plans.add(new TablePlan(tblName, tx)); 32 | } 33 | 34 | // 步骤2: 依次做product操作 35 | Plan p = plans.remove(0); 36 | for (Plan nextPlan : plans) { 37 | p = new ProductPlan(p, nextPlan); 38 | } 39 | 40 | // 步骤3: 根据where部分的谓词筛选 41 | p=new SelectPlan(p,queryData.getPred()); 42 | 43 | // 步骤4: 根据select部分做project操作 44 | p=new ProjectPlan(p,queryData.getFields()); 45 | 46 | return p; 47 | } 48 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/StartRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | import simpledb.log.BasicLogRecord; 4 | import simpledb.log.LogMgr; 5 | import simpledb.server.SimpleDB; 6 | 7 | /** 8 | * @ClassName StartRecord 9 | * @Deacription // TODO 10 | * @Author LiuZhian 11 | * @Date 2020-01-21 15:22 12 | * @Version 1.0 13 | **/ 14 | 15 | public class StartRecord implements LogRecord { 16 | private int myTxNum; 17 | 18 | public StartRecord(int myTxNum) { 19 | this.myTxNum = myTxNum; 20 | } 21 | 22 | /** 23 | * 根据一条BasicLogRecord来构造一条StartRecord。 24 | * 该构造函数是为了给 恢复/回滚 算法调用 25 | *

26 | * 注意,一条开始日志记录的格式为: 27 | *

28 | * 29 | * 30 | * @param blr 31 | */ 32 | public StartRecord(BasicLogRecord blr) { 33 | myTxNum = blr.nextInt(); 34 | } 35 | 36 | /** 37 | * 将一条日志记录写到日志文件,返回对应的LSN 38 | * 39 | * @return 日志记录的 LSN 40 | */ 41 | @Override 42 | public int writeToLog() { 43 | Object[] rec = new Object[]{START, myTxNum}; 44 | LogMgr logMgr = SimpleDB.logMgr(); 45 | return logMgr.append(rec); 46 | } 47 | 48 | @Override 49 | public int op() { 50 | return START; 51 | } 52 | 53 | @Override 54 | public int txNumber() { 55 | return myTxNum; 56 | } 57 | 58 | @Override 59 | public void undo() { 60 | // 空方法即可 61 | } 62 | 63 | public String toString() { 64 | return ""; 65 | } 66 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/log/LogTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.log; 2 | 3 | import simpledb.server.SimpleDB; 4 | import simpledb.tx.recovery.LogRecord; 5 | import simpledb.tx.recovery.LogRecordIterator; 6 | 7 | import java.io.IOException; 8 | import java.util.Iterator; 9 | 10 | /** 11 | * @ClassName LogTest 12 | * @Deacription // TODO 13 | * @Author LiuZhian 14 | * @Date 2020-01-16 12:28 15 | * @Version 1.0 16 | **/ 17 | 18 | public class LogTest { 19 | 20 | public static void main(String[] args) throws IOException { 21 | // SimpleDB.init("lzadb"); 22 | // LogMgr logMgr = SimpleDB.logMgr(); 23 | // int lsn1 = logMgr.append(new Object[]{"a", "b"}); 24 | // int lsn2 = logMgr.append(new Object[]{"c", "d"}); 25 | // int lsn3 = logMgr.append(new Object[]{"e", "f"}); 26 | // logMgr.flush(lsn3); 27 | // 28 | // Iterator iter = logMgr.iterator(); 29 | // while (iter.hasNext()) { 30 | // BasicLogRecord rec = iter.next(); 31 | // String v1 = rec.nextString(); 32 | // String v2 = rec.nextString(); 33 | // System.out.println("[" + v1 + ", " + v2 + "]"); 34 | // } 35 | 36 | SimpleDB.init("lzadb"); 37 | int logRecordNum = 0; 38 | Iterator iter = new LogRecordIterator(); 39 | while (iter.hasNext()) { 40 | LogRecord record = iter.next(); 41 | logRecordNum++; 42 | System.out.println(record); 43 | 44 | } 45 | System.out.println(logRecordNum); 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/ClientTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.rmi.RemoteException; 4 | import java.rmi.registry.LocateRegistry; 5 | import java.rmi.registry.Registry; 6 | import java.sql.Connection; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | 11 | public class ClientTest { 12 | public static void main(String[] args) throws RemoteException { 13 | // ============查看本机的rmi registry================ 14 | // String host = "localhost"; 15 | // int port = 1099; 16 | // Registry registry = LocateRegistry.getRegistry(host, port); 17 | // for (String name : registry.list()) { 18 | // System.out.println(name); 19 | // } 20 | // ============查看本机的rmi registry================ 21 | 22 | SimpleDriver driver = new SimpleDriver(); 23 | try { 24 | Connection conn = driver.connect("jdbc:simpledb://localhost:1099", null); 25 | 26 | Statement stmt = conn.createStatement(); 27 | 28 | String qryStr = "select sid,sname,age from student"; 29 | ResultSet rs = stmt.executeQuery(qryStr); 30 | 31 | while (rs.next()) { 32 | System.out.println(rs.getInt("sid") + " " + 33 | rs.getString("sname") + " " + 34 | rs.getInt("age")); 35 | } 36 | rs.close(); 37 | 38 | } catch (SQLException throwables) { 39 | throwables.printStackTrace(); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/RecordFormatter.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | import simpledb.buffer.PageFormatter; 4 | import simpledb.file.Page; 5 | 6 | import static simpledb.file.Page.BLOCK_SIZE; 7 | import static simpledb.file.Page.INT_SIZE; 8 | import static simpledb.record.RecordPage.EMPTY; 9 | 10 | /** 11 | * @ClassName PageFormatter 12 | * @Description TODO 13 | * @Author LiuZhian 14 | * @Date 2020/2/3 5:07 下午 15 | * @Version 1.0 16 | */ 17 | public class RecordFormatter implements PageFormatter { 18 | 19 | private TableInfo tableInfo; 20 | 21 | public RecordFormatter(TableInfo tableInfo) { 22 | this.tableInfo = tableInfo; 23 | } 24 | 25 | @Override 26 | public void format(Page p) { 27 | int recordSize = tableInfo.recordLength() + INT_SIZE; 28 | for (int pos = 0; pos + recordSize <= BLOCK_SIZE; pos += recordSize) { 29 | // 1. 先设置标志位为空 30 | p.setInt(pos, EMPTY); 31 | // 2. 再添加默认的记录值 32 | makeDefaultRecord(p, pos); 33 | } 34 | } 35 | 36 | private void makeDefaultRecord(Page page, int position) { 37 | for (String fldName : tableInfo.schema().fields()) { 38 | // 每个字段在一条记录内的offset 39 | int offset = tableInfo.offset(fldName); 40 | // int的默认值为0 41 | if (tableInfo.schema().type(fldName) == Schema.INTEGER) 42 | page.setInt(position + INT_SIZE + offset, 0); 43 | // string的默认值为"",即空串 44 | else 45 | page.setString(position + INT_SIZE + offset, ""); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/LogRecordIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | import simpledb.log.BasicLogRecord; 4 | import simpledb.server.SimpleDB; 5 | 6 | import java.util.Iterator; 7 | 8 | import static simpledb.tx.recovery.LogRecord.*; 9 | 10 | /** 11 | * @ClassName LogRecordIterator 12 | * @Deacription // TODO 13 | * @Author LiuZhian 14 | * @Date 2020-01-21 15:37 15 | * @Version 1.0 16 | **/ 17 | 18 | public class LogRecordIterator implements Iterator { 19 | // 先获得一个BasicLogRecord迭代器 20 | // 此迭代器迭代得到结果是一条条raw的日志记录 21 | private Iterator iter = SimpleDB.logMgr().iterator(); 22 | 23 | 24 | @Override 25 | public boolean hasNext() { 26 | return iter.hasNext(); 27 | } 28 | 29 | @Override 30 | public LogRecord next() { 31 | BasicLogRecord blr = iter.next(); 32 | int op = blr.nextInt(); 33 | switch (op) { 34 | case CHECKPOINT: 35 | return new CheckpointRecord(blr); 36 | case START: 37 | return new StartRecord(blr); 38 | case COMMIT: 39 | return new CommitRecord(blr); 40 | case ROLLBACK: 41 | return new RollBackRecord(blr); 42 | case SETINT: 43 | return new SetIntRecord(blr); 44 | case SETSTRING: 45 | return new SetStringRecord(blr); 46 | default: 47 | return null; 48 | } 49 | 50 | } 51 | 52 | public void remove() { 53 | throw new UnsupportedOperationException(); 54 | } 55 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/log/LogIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.log; 2 | 3 | import simpledb.file.Block; 4 | import simpledb.file.Page; 5 | 6 | import java.util.Iterator; 7 | 8 | import static simpledb.file.Page.INT_SIZE; 9 | import static simpledb.log.LogMgr.LAST_POS; 10 | 11 | /** 12 | * @ClassName LogIterator 13 | * @Deacription // TODO 14 | * @Author LiuZhian 15 | * @Date 2020-01-16 16:26 16 | * @Version 1.0 17 | **/ 18 | 19 | public class LogIterator implements Iterator { 20 | 21 | private Block blk; 22 | private Page page = new Page(); 23 | private int currentRec; // 迭代器当前遍历的日志记录结束位置 24 | 25 | public LogIterator(Block blk) { 26 | this.blk = blk; 27 | page.read(blk); 28 | // 初始化为最后一条日志记录的结束位置 29 | currentRec = page.getInt(LAST_POS); 30 | } 31 | 32 | @Override 33 | public boolean hasNext() { 34 | return currentRec > 0 || blk.number() > 0; 35 | } 36 | 37 | @Override 38 | public BasicLogRecord next() { 39 | if (0 == currentRec) 40 | moveToNextBlock(); 41 | // 继续往回迭代上一条 42 | currentRec = page.getInt(currentRec); 43 | return new BasicLogRecord(page, currentRec + INT_SIZE); 44 | } 45 | 46 | @Override 47 | public void remove() { 48 | throw new UnsupportedOperationException("Remove operation is not supported in LogIterator!"); 49 | } 50 | 51 | private void moveToNextBlock() { 52 | // 上一个块 53 | blk = new Block(blk.filename(), blk.number() - 1); 54 | page.read(blk); 55 | currentRec = page.getInt(LAST_POS); 56 | } 57 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/query/IndexSelectPlan.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.query; 2 | 3 | import simpledb.index.Index; 4 | import simpledb.metadata.IndexInfo; 5 | import simpledb.query.Constant; 6 | import simpledb.query.Plan; 7 | import simpledb.query.Scan; 8 | import simpledb.query.TableScan; 9 | import simpledb.record.Schema; 10 | import simpledb.tx.Transaction; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * @program simpledb 16 | * @description: TODO 17 | * @author: liuzhian 18 | * @create: 2020/10/05 14:15 19 | */ 20 | public class IndexSelectPlan implements Plan { 21 | private Plan plan; 22 | private IndexInfo indexInfo; 23 | private Constant constantVal; 24 | 25 | public IndexSelectPlan(Plan plan, IndexInfo indexInfo, Constant constantVal) { 26 | this.plan = plan; 27 | this.indexInfo = indexInfo; 28 | this.constantVal = constantVal; 29 | } 30 | 31 | @Override 32 | public Scan open() throws IOException { 33 | TableScan tableScan= (TableScan) plan.open(); 34 | Index idx=indexInfo.open(); // 打开索引 35 | return new IndexSelectScan(tableScan,idx,constantVal); 36 | } 37 | 38 | @Override 39 | public int blockAccessed() { 40 | return indexInfo.blocksAccessed(); 41 | } 42 | 43 | @Override 44 | public int recordsOutput() { 45 | return indexInfo.recordsOutput(); 46 | } 47 | 48 | @Override 49 | public int distinctValues(String fldName) { 50 | return indexInfo.distinctValues(fldName); 51 | } 52 | 53 | @Override 54 | public Schema schema() { 55 | return plan.schema(); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/file/FileTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.file; 2 | 3 | import simpledb.server.SimpleDB; 4 | 5 | 6 | /** 7 | * @ClassName FileTest 8 | * @Deacription // TODO 9 | * @Author LiuZhian 10 | * @Date 2020-01-14 20:02 11 | * @Version 1.0 12 | **/ 13 | 14 | public class FileTest { 15 | 16 | public static void main(String[] args) { 17 | try { 18 | SimpleDB.init("lzadb"); 19 | 20 | // 第0块 21 | Block blk = new Block("junk", 0); 22 | Page p1 = new Page(); 23 | p1.read(blk); 24 | // 将第0块的第105字节开始的int整数加1 25 | int n = p1.getInt(105); 26 | assert (n == 0); 27 | p1.setInt(105, n + 1); 28 | p1.write(blk); 29 | 30 | // 重新读回第0块 31 | Page p2 = new Page(); 32 | p2.read(blk); 33 | int added_n = p2.getInt(105); 34 | assert (added_n == n + 1); 35 | 36 | // 追加另一个block (即 block 1) 37 | Page p3 = new Page(); 38 | p3.setString(20, "hola"); 39 | blk = p3.append("junk"); 40 | assert (blk.number() == 1); 41 | 42 | // 再次重新读回追加的block到内存 43 | Page p4 = new Page(); 44 | p4.read(blk); 45 | String s = p4.getString(20); 46 | assert (s.equals("hola")); 47 | 48 | // 追加另一个block (即 block 2) 49 | Page p5 = new Page(); 50 | p5.setInt(88, 1); 51 | blk = p5.append("junk"); 52 | assert (p5.getInt(88) == 1); 53 | 54 | 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/SimpleResultSet.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.sql.ResultSetMetaData; 4 | import java.sql.SQLException; 5 | 6 | public class SimpleResultSet extends ResultSetAdapter { 7 | RemoteResultSet rrs; 8 | 9 | public SimpleResultSet(RemoteResultSet rrs) { 10 | this.rrs = rrs; 11 | } 12 | 13 | @Override 14 | public boolean next() throws SQLException { 15 | try { 16 | return rrs.next(); 17 | } catch (Exception e) { 18 | throw new SQLException(e); 19 | } 20 | } 21 | 22 | @Override 23 | public int getInt(String fieldName) throws SQLException { 24 | try { 25 | return rrs.getInt(fieldName); 26 | } catch (Exception e) { 27 | throw new SQLException(e); 28 | } 29 | 30 | } 31 | 32 | @Override 33 | public String getString(String fieldName) throws SQLException { 34 | try { 35 | return rrs.getString(fieldName); 36 | } catch (Exception e) { 37 | throw new SQLException(e); 38 | } 39 | } 40 | 41 | @Override 42 | public ResultSetMetaData getMetaData() throws SQLException { 43 | try { 44 | RemoteMetaData rmd = rrs.getMetaData(); 45 | return new SimpleMetaData(rmd); 46 | } catch (Exception e) { 47 | throw new SQLException(e); 48 | } 49 | } 50 | 51 | @Override 52 | public void close() throws SQLException { 53 | try { 54 | rrs.close(); 55 | } catch (Exception e) { 56 | throw new SQLException(e); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/concurrency/ConcurrencyMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.concurrency; 2 | 3 | import simpledb.file.Block; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @ClassName concurrency 10 | * @Deacription // TODO 11 | * @Author LiuZhian 12 | * @Date 2020-01-26 23:42 13 | * @Version 1.0 14 | **/ 15 | 16 | public class ConcurrencyMgr { 17 | // 全局锁表,所有的事务共享锁表 18 | private static LockTable lockTbl = new LockTable(); 19 | // 当前事务的持有锁情况 20 | private Map locks = new HashMap<>(); 21 | 22 | 23 | /** 24 | * 为当前事务请求指定块的共享锁。 25 | *

26 | * 如果当前事务未持有共享锁,才会去为此事务请求对应块的共享锁 27 | * 28 | * @param blk 指定的块 29 | */ 30 | public void sLock(Block blk) { 31 | if (null == locks.get(blk)) { 32 | lockTbl.sLock(blk); 33 | locks.put(blk, "S"); 34 | } 35 | } 36 | 37 | /** 38 | * 为当前事务请求指定块的互斥锁。 39 | *

40 | * 如果当前事务未持有互斥锁,才会去为此事务请求对应块的互斥锁 41 | *

42 | * 会先获得该块的共享锁,然后将该锁升级为互斥锁 43 | * 44 | * @param blk 指定的块 45 | */ 46 | public void xLock(Block blk) { 47 | if (!hasXLock(blk)) { 48 | sLock(blk); 49 | lockTbl.xLock(blk); 50 | locks.put(blk, "X"); 51 | } 52 | 53 | } 54 | 55 | /** 56 | * 释放当前事务持有的所有锁。 57 | */ 58 | public void release() { 59 | for (Block blk : locks.keySet()) 60 | lockTbl.unLock(blk); 61 | locks.clear(); 62 | } 63 | 64 | private boolean hasXLock(Block blk) { 65 | String lockType = locks.get(blk); 66 | return (lockType != null && lockType.equals("X")); 67 | } 68 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteMetaDataImpl.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import simpledb.record.Schema; 4 | 5 | import java.rmi.RemoteException; 6 | import java.rmi.server.UnicastRemoteObject; 7 | 8 | import static simpledb.record.Schema.INTEGER; 9 | import static simpledb.record.Schema.VARCHAR; 10 | 11 | /** 12 | * @ClassName RemoteMetaDataImpl 13 | * @Deacription // TODO 14 | * @Author LiuZhian 15 | * @Date 2020-03-14 20:32 16 | * @Version 1.0 17 | **/ 18 | 19 | public class RemoteMetaDataImpl extends UnicastRemoteObject 20 | implements RemoteMetaData { 21 | private Schema schema; 22 | private Object[] fieldNames; 23 | 24 | public RemoteMetaDataImpl(Schema schema) throws RemoteException { 25 | this.schema = schema; 26 | fieldNames = schema.fields().toArray(); 27 | } 28 | 29 | @Override 30 | public int getColumnCount() throws RemoteException { 31 | return fieldNames.length; 32 | } 33 | 34 | @Override 35 | public String getColumnName(int column) throws RemoteException { 36 | return (String) fieldNames[column]; 37 | } 38 | 39 | @Override 40 | public int getColumnType(int column) throws RemoteException { 41 | String fieldName = getColumnName(column); 42 | return schema.type(fieldName); 43 | } 44 | 45 | @Override 46 | public int getColumnDisplaySize(int column) throws RemoteException { 47 | String fieldName = getColumnName(column); 48 | int fieldType = schema.type(fieldName); 49 | // 字符串长度 50 | if (fieldType == VARCHAR) 51 | return schema.length(fieldName); 52 | else 53 | return 6; // 6位数来显示一个整数(可以不一样) 54 | } 55 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/IndexQueryTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.index; 2 | 3 | import simpledb.metadata.IndexInfo; 4 | import simpledb.metadata.MetadataMgr; 5 | import simpledb.planner.Planner; 6 | import simpledb.query.IntConstant; 7 | import simpledb.query.Plan; 8 | import simpledb.query.TablePlan; 9 | import simpledb.query.TableScan; 10 | import simpledb.record.RID; 11 | import simpledb.server.SimpleDB; 12 | import simpledb.tx.Transaction; 13 | 14 | import java.io.IOException; 15 | import java.util.Map; 16 | 17 | public class IndexQueryTest { 18 | 19 | public static void main(String[] args) throws IOException { 20 | SimpleDB.init("studentDB"); 21 | Transaction tx = new Transaction(); 22 | 23 | // open a scan to table 24 | Plan stuPlan = new TablePlan("student", tx); 25 | TableScan stuScan = (TableScan) stuPlan.open(); 26 | 27 | // open the index on majorid 28 | MetadataMgr mdMgr = SimpleDB.metadataMgr(); 29 | Map indexes = mdMgr.getIndexInfo("student", tx); 30 | IndexInfo ii = indexes.get("majorid"); 31 | Index idx = ii.open(); 32 | 33 | // retrieve all index records which have a dataval of 10 34 | idx.beforeFirst(new IntConstant(10)); 35 | while (idx.next()) { 36 | // use the datarid to go to the corresponding record in table STUDENT 37 | RID dataRid = idx.getDataRid(); 38 | stuScan.moveToRId(dataRid); 39 | System.out.println(stuScan.getString("sname")); 40 | } 41 | 42 | // close the idx, the table scan, and the transaction 43 | idx.close(); 44 | stuScan.close(); 45 | tx.commit(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/ProductPlan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * @ClassName ProductPlan 9 | * @Description TODO 10 | * @Author LiuZhian 11 | * @Date 2020/2/13 7:25 下午 12 | * @Version 1.0 13 | */ 14 | public class ProductPlan implements Plan { 15 | private Plan plan1, plan2; 16 | private Schema schema = new Schema(); 17 | 18 | public ProductPlan(Plan plan1, Plan plan2) { 19 | this.plan1 = plan1; 20 | this.plan2 = plan2; 21 | this.schema.addAll(plan1.schema()); 22 | this.schema.addAll(plan2.schema()); 23 | } 24 | 25 | @Override 26 | public Scan open() throws IOException { 27 | Scan scan1 = plan1.open(); 28 | Scan scan2 = plan2.open(); 29 | return new ProductScan(scan1, scan2); 30 | } 31 | 32 | /** 33 | * 图17-13中的 B(s_1) + R(s_1) * B(s_2) 34 | * 35 | * @return 36 | */ 37 | @Override 38 | public int blockAccessed() { 39 | return plan1.blockAccessed() + 40 | (plan1.recordsOutput() * plan2.blockAccessed()); 41 | } 42 | 43 | /** 44 | * 图17-13中的 R(s_1) * R(s_2) 45 | * @return 46 | */ 47 | @Override 48 | public int recordsOutput() { 49 | return plan1.recordsOutput() * plan2.recordsOutput(); 50 | } 51 | 52 | @Override 53 | public int distinctValues(String fldName) { 54 | if(plan1.schema().hasFiled(fldName)) 55 | return plan1.distinctValues(fldName); 56 | else 57 | return plan2.distinctValues(fldName); 58 | } 59 | 60 | @Override 61 | public Schema schema() { 62 | return schema; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/SelectPlan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * @ClassName SelectPlan 9 | * @Description TODO 10 | * @Author LiuZhian 11 | * @Date 2020/2/13 6:59 下午 12 | * @Version 1.0 13 | */ 14 | public class SelectPlan implements Plan { 15 | private Plan plan; 16 | private Predicate predicate; 17 | 18 | public SelectPlan(Plan plan, Predicate predicate) { 19 | this.plan = plan; 20 | this.predicate = predicate; 21 | } 22 | 23 | @Override 24 | public Scan open() throws IOException { 25 | Scan scan = plan.open(); 26 | return new SelectScan(scan, predicate); 27 | } 28 | 29 | @Override 30 | public int blockAccessed() { 31 | return plan.blockAccessed(); 32 | } 33 | 34 | @Override 35 | public int recordsOutput() { 36 | // predicate.reductionFactor(plan)就是 图17-13 中的 V(s_1, A) 37 | return plan.recordsOutput() / predicate.reductionFactor(plan); 38 | } 39 | 40 | @Override 41 | public int distinctValues(String fldName) { 42 | if (null != predicate.equatesWithConstant(fldName)) 43 | return 1; 44 | else { 45 | String theOtherFldName = predicate.equatesWithField(fldName); 46 | if (theOtherFldName != null) 47 | return Math.min(plan.distinctValues(fldName), 48 | plan.distinctValues(theOtherFldName)); 49 | else 50 | return Math.min(recordsOutput(), plan.distinctValues(fldName)); 51 | } 52 | 53 | } 54 | 55 | @Override 56 | public Schema schema() { 57 | return plan.schema(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/btree/BTreePageFormatter.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.btree; 2 | 3 | import simpledb.buffer.PageFormatter; 4 | import simpledb.file.Page; 5 | import simpledb.record.Schema; 6 | import simpledb.record.TableInfo; 7 | 8 | import static simpledb.file.Page.BLOCK_SIZE; 9 | import static simpledb.file.Page.INT_SIZE; 10 | 11 | /** 12 | * @program simpledb 13 | * @description: TODO 14 | * @author: liuzhian 15 | * @create: 2020/09/23 23:11 16 | */ 17 | public class BTreePageFormatter implements PageFormatter { 18 | 19 | private TableInfo tableInfo; 20 | private int flag; // B树页的标志位,-1表示B树的叶子结点 21 | 22 | public BTreePageFormatter(TableInfo tableInfo, int flag) { 23 | this.tableInfo = tableInfo; 24 | this.flag = flag; 25 | } 26 | 27 | /** 28 | * 格式化一个新的B树页。 29 | *

30 | * 尽可能在页中申请全部为空的records,字符串字段取值全为"",字符串字段取值全为0 31 | *

32 | * 页的前INT_SIZE个字节为标志位,页的INT_SIZE~INT_SIZE*2 个字节为页上的记录数量 33 | * 34 | * @param p 35 | */ 36 | @Override 37 | public void format(Page p) { 38 | p.setInt(0, flag); // 标志位 39 | p.setInt(INT_SIZE, 0); // 记录数格式化为0 40 | int recordLen = tableInfo.recordLength(); 41 | for (int pos = INT_SIZE * 2; pos + recordLen <= BLOCK_SIZE; pos += recordLen) 42 | initializeDefaultRecord(p, pos); 43 | } 44 | 45 | private void initializeDefaultRecord(Page p, int pos) { 46 | Schema schema = tableInfo.schema(); 47 | for (String fieldName : schema.fields()) { 48 | int fldOffset = tableInfo.offset(fieldName); 49 | if (tableInfo.schema().type(fieldName) == Schema.INTEGER) 50 | p.setInt(pos + fldOffset, 0); 51 | else 52 | p.setString(pos + fldOffset, ""); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/QueryData.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | import simpledb.query.Predicate; 4 | 5 | import java.util.Collection; 6 | 7 | /** 8 | * @ClassName QueryData 9 | * @Deacription // TODO 10 | * @Author LiuZhian 11 | * @Date 2020-02-20 20:30 12 | * @Version 1.0 13 | **/ 14 | 15 | public class QueryData { 16 | 17 | private Collection fields; 18 | private Collection tables; 19 | private Predicate pred; 20 | 21 | public QueryData(Collection fields, 22 | Collection tables, 23 | Predicate pred) { 24 | this.fields = fields; 25 | this.tables = tables; 26 | this.pred = pred; 27 | } 28 | 29 | public Collection getFields() { 30 | return fields; 31 | } 32 | 33 | public Collection getTables() { 34 | return tables; 35 | } 36 | 37 | public Predicate getPred() { 38 | return pred; 39 | } 40 | 41 | /** 42 | * 重新构造形如 Select XXX from XXX where XXX的SQL字符串。 43 | * 44 | * @return 45 | */ 46 | @Override 47 | public String toString() { 48 | StringBuilder result = new StringBuilder("select "); 49 | for (String f : fields) 50 | result.append(f).append(", "); 51 | // 移除循环最后添加的一个逗号 52 | result.substring(0, result.length() - 2); 53 | 54 | result.append(" from "); 55 | 56 | for (String tblName : tables) 57 | result.append(tblName).append(", "); 58 | // 再次移除循环最后添加的一个逗号 59 | result.substring(0, result.length() - 2); 60 | 61 | String predStr = pred.toString(); 62 | if (!predStr.equals("")) 63 | result.append(" where ").append(predStr); 64 | 65 | return result.toString(); 66 | } 67 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/BufferList.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx; 2 | 3 | import simpledb.buffer.Buffer; 4 | import simpledb.buffer.BufferMgr; 5 | import simpledb.buffer.PageFormatter; 6 | import simpledb.file.Block; 7 | import simpledb.server.SimpleDB; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @ClassName BufferList 16 | * @Deacription // TODO 17 | * @Author LiuZhian 18 | * @Date 2020-01-27 21:38 19 | * @Version 1.0 20 | **/ 21 | 22 | public class BufferList { 23 | 24 | private Map buffers = new HashMap<>(); 25 | // 已经固定的所有块,如果固定多次,则列表中有多个值 26 | private List pins = new ArrayList<>(); 27 | private BufferMgr bufferMgr = SimpleDB.bufferMgr(); 28 | 29 | Buffer getBuffer(Block blk) { 30 | return buffers.get(blk); 31 | } 32 | 33 | void pin(Block blk) { 34 | Buffer buff = bufferMgr.pin(blk); 35 | buffers.put(blk, buff); 36 | pins.add(blk); 37 | } 38 | 39 | Block pinNew(String fileName, PageFormatter pfmt) { 40 | Buffer buff = bufferMgr.pinNew(fileName, pfmt); 41 | Block blk = buff.block(); 42 | buffers.put(blk, buff); 43 | pins.add(blk); 44 | return blk; 45 | } 46 | 47 | void unpin(Block blk) { 48 | Buffer buff = buffers.get(blk); 49 | bufferMgr.unpin(buff); 50 | pins.remove(blk); 51 | 52 | // 如果块取消固定后,固定次数为0,则从缓存区map中移除该entry 53 | if (!pins.contains(blk)) 54 | buffers.remove(blk); 55 | } 56 | 57 | void unpinAll() { 58 | for (Block blk : pins) { 59 | Buffer buff = buffers.get(blk); 60 | bufferMgr.unpin(buff); 61 | } 62 | 63 | buffers.clear(); 64 | pins.clear(); 65 | } 66 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteStatementImpl.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import simpledb.query.Plan; 4 | import simpledb.server.SimpleDB; 5 | import simpledb.tx.Transaction; 6 | 7 | import java.io.IOException; 8 | import java.rmi.RemoteException; 9 | import java.rmi.server.UnicastRemoteObject; 10 | 11 | /** 12 | * @ClassName RemoteStatementImpl 13 | * @Deacription // TODO 14 | * @Author LiuZhian 15 | * @Date 2020-03-14 20:11 16 | * @Version 1.0 17 | **/ 18 | 19 | public class RemoteStatementImpl extends UnicastRemoteObject 20 | implements RemoteStatement { 21 | private RemoteConnectionImpl rconn; 22 | 23 | public RemoteStatementImpl(RemoteConnectionImpl rconn) throws RemoteException { 24 | this.rconn = rconn; 25 | } 26 | 27 | @Override 28 | public RemoteResultSet executeQuery(String qry) throws RemoteException { 29 | try { 30 | Transaction tx = rconn.getTransaction(); 31 | Plan plan = SimpleDB.planner().createQueryPlan(qry, tx); 32 | return new RemoteResultSetImpl(plan, rconn); 33 | } catch (IOException e) { 34 | rconn.rollback(); 35 | System.out.println("Error in RemoteStatementImpl executeQuery() method!"); 36 | return null; 37 | } 38 | } 39 | 40 | @Override 41 | public int executeUpdateCmd(String cmd) throws RemoteException { 42 | try { 43 | Transaction tx = rconn.getTransaction(); 44 | int result = SimpleDB.planner().executeUpdate(cmd, tx); 45 | rconn.commit(); 46 | return result; 47 | } catch (IOException e) { 48 | rconn.rollback(); 49 | System.out.println("Error in RemoteStatementImpl executeUpdateCmd() method!"); 50 | return -1; 51 | } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/ProjectScan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import java.io.IOException; 4 | import java.util.Collection; 5 | 6 | /** 7 | * @ClassName ProjectScan 8 | * @Description TODO 9 | * @Author LiuZhian 10 | * @Date 2020/2/12 4:28 下午 11 | * @Version 1.0 12 | */ 13 | public class ProjectScan implements Scan { 14 | private Scan scan; 15 | private Collection fieldList; 16 | 17 | public ProjectScan(Scan scan, Collection fieldList) { 18 | this.scan = scan; 19 | this.fieldList = fieldList; 20 | } 21 | 22 | // ============Scan 接口中相关方法的实现============== 23 | 24 | @Override 25 | public void beforeFirst() throws IOException { 26 | scan.beforeFirst(); 27 | } 28 | 29 | @Override 30 | public boolean next() throws IOException { 31 | return scan.next(); 32 | } 33 | 34 | @Override 35 | public void close() throws IOException { 36 | scan.close(); 37 | } 38 | 39 | @Override 40 | public Constant getVal(String fieldName) { 41 | if (hasField(fieldName)) 42 | return scan.getVal(fieldName); 43 | else 44 | throw new RuntimeException("Field "+fieldName+" is not found!"); 45 | } 46 | 47 | @Override 48 | public int getInt(String fieldName) { 49 | if (hasField(fieldName)) 50 | return scan.getInt(fieldName); 51 | else 52 | throw new RuntimeException("Field "+fieldName+" is not found!"); 53 | } 54 | 55 | @Override 56 | public String getString(String fieldName) { 57 | if (hasField(fieldName)) 58 | return scan.getString(fieldName); 59 | else 60 | throw new RuntimeException("Field "+fieldName+" is not found!"); 61 | } 62 | 63 | @Override 64 | public boolean hasField(String fieldName) { 65 | return fieldList.contains(fieldName); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/TableInfo.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import static simpledb.file.Page.*; 7 | import static simpledb.record.Schema.*; 8 | 9 | /** 10 | * @ClassName TableInfo 11 | * @Description TODO 12 | * @Author LiuZhian 13 | * @Date 2020/2/3 1:14 下午 14 | * @Version 1.0 15 | */ 16 | public class TableInfo { 17 | private Schema schema; 18 | private Map offsets; 19 | private int recordLen; 20 | private String tblName; 21 | 22 | public TableInfo(String tblName, Schema schema) { 23 | this.tblName = tblName; 24 | this.schema = schema; 25 | offsets = new HashMap<>(); 26 | int pos = 0; 27 | for (String fieldName : schema.fields()) { 28 | offsets.put(fieldName, pos); 29 | // 每个字段需要的字节数 30 | pos += lengthInBytes(fieldName); 31 | } 32 | recordLen = pos; 33 | } 34 | 35 | public TableInfo(String tblName, Schema schema, 36 | Map offsets, int recordLen) { 37 | this.tblName = tblName; 38 | this.schema = schema; 39 | this.offsets = offsets; 40 | this.recordLen = recordLen; 41 | } 42 | 43 | /** 44 | * 返回保存该类型记录的文件名 45 | * 46 | * @return 文件名(包含后缀) 47 | */ 48 | public String fileName() { 49 | return tblName + ".tbl"; 50 | } 51 | 52 | public Schema schema() { 53 | return schema; 54 | } 55 | 56 | public int offset(String fldname) { 57 | return offsets.get(fldname); 58 | } 59 | 60 | public int recordLength() { 61 | return recordLen; 62 | } 63 | 64 | private int lengthInBytes(String fldName) { 65 | int fldType = schema.type(fldName); 66 | if (fldType == INTEGER) { 67 | return INT_SIZE; 68 | } 69 | else 70 | return STR_SIZE(schema.length(fldName)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/buffer/BufferTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.buffer; 2 | 3 | import simpledb.file.Block; 4 | import simpledb.log.LogMgr; 5 | import simpledb.server.SimpleDB; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @ClassName BufferTest 11 | * @Deacription // TODO 12 | * @Author LiuZhian 13 | * @Date 2020-01-17 19:40 14 | * @Version 1.0 15 | **/ 16 | 17 | public class BufferTest { 18 | 19 | public static void main(String[] args) throws IOException { 20 | // Case 1 21 | SimpleDB.init("lzadb"); 22 | BufferMgr bufferMgr = SimpleDB.bufferMgr(); 23 | Block blk = new Block("junk", 3); 24 | Buffer buffer = bufferMgr.pin(blk); 25 | int n = buffer.getInt(392); 26 | String str = buffer.getString(20); 27 | // 客户端代码需要对unpin负责 28 | bufferMgr.unpin(buffer); 29 | System.out.println("Values are: " + n + ", " + str); 30 | 31 | 32 | // // Case 2 33 | // SimpleDB.init("studentdb"); 34 | // BufferMgr bufferMgr = SimpleDB.bufferMgr(); 35 | // Block blk = new Block("junk", 3); 36 | // Buffer buffer = bufferMgr.pin(blk); 37 | // int n = buffer.getInt(392); 38 | // 39 | // LogMgr logMgr = SimpleDB.logMgr(); 40 | // int myTxNum = 1; // 假设这里有个事务标识符1 41 | // Object[] logRec = new Object[]{"junk", 3, 392, n}; 42 | // int LSN=logMgr.append(logRec); 43 | // 44 | // buffer.setInt(392,n+1,myTxNum,LSN); 45 | // // 客户端代码需要对unpin负责 46 | // bufferMgr.unpin(buffer); 47 | 48 | // Case 3 49 | // SimpleDB.init("lzadb"); 50 | // BufferMgr bufferMgr = SimpleDB.bufferMgr(); 51 | // PageFormatter pf=new ABCStringFormatter(); 52 | // Buffer buffer = bufferMgr.pinNew("junk",pf); 53 | // 54 | // String str=buffer.getString(0); 55 | // assert (str.equals("abc")); 56 | // 57 | // int blkNum=buffer.block().number(); 58 | // System.out.println("Appended block number: "+blkNum); 59 | 60 | 61 | } 62 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/query/IndexSelectScan.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.query; 2 | 3 | import simpledb.index.Index; 4 | import simpledb.query.Constant; 5 | import simpledb.query.Scan; 6 | import simpledb.query.TableScan; 7 | import simpledb.record.RID; 8 | import sun.tools.jconsole.Tab; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * @program simpledb 14 | * @description: TODO 15 | * @author: liuzhian 16 | * @create: 2020/10/05 14:28 17 | */ 18 | public class IndexSelectScan implements Scan { 19 | private TableScan tableScan; 20 | private Index index; 21 | private Constant constantVal; 22 | 23 | public IndexSelectScan(TableScan tableScan, Index index, Constant constantVal) throws IOException { 24 | this.tableScan = tableScan; 25 | this.index = index; 26 | this.constantVal = constantVal; 27 | beforeFirst(); 28 | } 29 | 30 | @Override 31 | public void beforeFirst() throws IOException { 32 | index.beforeFirst(constantVal); 33 | } 34 | 35 | @Override 36 | public boolean next() throws IOException { 37 | boolean exist = index.next(); 38 | if (exist) { 39 | RID rid = index.getDataRid(); 40 | tableScan.moveToRId(rid); // 底层表文件直接定位到rid位置的记录 41 | } 42 | return exist; 43 | } 44 | 45 | @Override 46 | public void close() throws IOException { 47 | index.close(); 48 | tableScan.close(); 49 | } 50 | 51 | @Override 52 | public Constant getVal(String fieldName) { 53 | return tableScan.getVal(fieldName); 54 | } 55 | 56 | @Override 57 | public int getInt(String fieldName) { 58 | return tableScan.getInt(fieldName); 59 | } 60 | 61 | @Override 62 | public String getString(String fieldName) { 63 | return tableScan.getString(fieldName); 64 | } 65 | 66 | @Override 67 | public boolean hasField(String fieldName) { 68 | return tableScan.hasField(fieldName); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/ProductScan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * @ClassName ProductScan 7 | * @Description TODO 8 | * @Author LiuZhian 9 | * @Date 2020/2/12 4:35 下午 10 | * @Version 1.0 11 | */ 12 | public class ProductScan implements Scan { 13 | private Scan scan1,scan2; 14 | 15 | public ProductScan(Scan scan1, Scan scan2) throws IOException { 16 | this.scan1 = scan1; 17 | this.scan2 = scan2; 18 | scan1.next(); 19 | } 20 | 21 | // ============Scan 接口中相关方法的实现============== 22 | 23 | @Override 24 | public void beforeFirst() throws IOException { 25 | scan1.beforeFirst(); 26 | scan1.next(); 27 | scan2.beforeFirst(); 28 | } 29 | 30 | @Override 31 | public boolean next() throws IOException { 32 | if (scan2.next()) 33 | return true; 34 | else { 35 | scan2.beforeFirst(); 36 | return scan1.next() && scan2.next(); 37 | } 38 | } 39 | 40 | @Override 41 | public void close() throws IOException { 42 | scan1.next(); 43 | scan2.next(); 44 | } 45 | 46 | @Override 47 | public Constant getVal(String fieldName) { 48 | if(scan1.hasField(fieldName)) 49 | return scan1.getVal(fieldName); 50 | else 51 | return scan2.getVal(fieldName); 52 | } 53 | 54 | @Override 55 | public int getInt(String fieldName) { 56 | if(scan1.hasField(fieldName)) 57 | return scan1.getInt(fieldName); 58 | else 59 | return scan2.getInt(fieldName); 60 | } 61 | 62 | @Override 63 | public String getString(String fieldName) { 64 | if(scan1.hasField(fieldName)) 65 | return scan1.getString(fieldName); 66 | else 67 | return scan2.getString(fieldName); 68 | } 69 | 70 | @Override 71 | public boolean hasField(String fieldName) { 72 | return scan1.hasField(fieldName) || scan2.hasField(fieldName); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /part3/part3.md: -------------------------------------------------------------------------------- 1 | # Part 3 - 数据库服务器内部实现 2 | 3 | 现在我们已经学习了如何使用一个数据库系统,我们现在将移步至如何构建一个数据库系统的问题。理解现代数据库系统是如何工作的一个很好的方式就是学习一个真正的数据库系统的内部实现。这个`SimpleDB`数据库系统就是为了这个目的而构建的,我们将在本书的第3部分完成,它总共包含了大约3500行Java源代码,由12个packages中的85个类组织而成;并且在本书随后的第4部分——效率优化部分,代码量将缩小至大约一半,这虽然看起来有很多代码,但随后我们将会知道`SimpleDB`是一个单纯的系统骨架,其实现的采用了尽可能简单的算法,也只保留了一些最基本、也是必要的功能。商业化的数据库系统拥有和这一样的架构,但是使用了更复杂的算法、拥有鲁棒性更高的的错误检查机制以及大量的警告(原文为bells and whistles),这样一来,系统明显会变得庞大。例如Derby(一个数据库系统),代码量大概是`SimpleDB`的100倍以上(20万行以上),Oracle就更大了。 4 | 5 | 在本书中,我们将数据库服务器分解成拥有10层组件的系统,每个组件只知道它下层的组件细节,并为它上层的组件提供服务,下图展示了该系统中每个组件的功能。 6 | ![](part3-01.png) 7 | 8 | 说明该系统中各个组件功能的一个较好方法就是按照一条SQL语句的执行去解析。例如,假设现在有一个JDBC的客户端通过`executeUpdate`方法,提交了一条删除SQL语句到数据库服务器,以下的动作将会发生: 9 | - remote组件实现了`executeUpdate`方法,该方法为当前statement创建一个事务,并将string类型的SQL语句传递给planner 10 | - planner组件发送string类型的SQL语句传递给parser组件,parser组件会解析SQL语句,并返回一个`DeleteData`对象,该对象包含当前statement涉及的数据库表明和删除谓词(即删除成功or失败)。planner组件会使用paerser组件的数据去决定为当前statement创建一个计划(plan),该计划表示了一个为了找到待删除记录而创建的关系代数查询(relational algebra query)。最后planner组件将创建的计划送给查询处理器(query processor)。 11 | - 查询处理器(query processor)为这个plan创建一个扫描(scan),这个扫描包含了执行该计划时需要的运行时信息。为了创建这个扫描,查询处理器包含了从源数据管理器(metadata manager)获得的相关表模式(schema)。查询处理器随后创建一个对应该模式的记录管理器(record manager) 12 | - 查询处理器随后在刚创建的扫描上进行迭代,迭代过程中,它会检测数据库表中的每一条记录并且判断是否满足我们定义的删除谓词。每次需要获得一个记录(record)时,它都显式地向记录管理器发出请求。如果一条记录需要被删除,查询处理器会告诉记录管理器相应的执行。 13 | - 记录管理器知道记录是怎样保存在文件中的,当查询处理器请求一个记录的值时,它会知道文件中的哪一块(block)包含了所需的记录,并且计算相应的偏移量从而获得记录中的某个项具体的值(value)。它随后会向事务管理器发出通知,去检索(retrieve)指定块指定位置的值。 14 | - 事务管理器检查它的表锁(注:事务管理器为每个表维护一个锁,所有表的锁信息也由一个lock table维护 ),从而确保其它事务不会使用当前块。如果还没有没有上锁,则事务管理器则为当前块上锁,并调用缓冲区管理器(buffer manager)的相关方法,从而获得需要的值。 15 | - 缓冲管理器的目的是将很多块的内容缓存在内存中,如果被请求的块没有被缓存,则缓冲管理器从缓存区中选取一个页,并调用文件管理器(file manager)的相关方法,将相关的块读入当前页,亦可将当前页的内容写到磁盘中。(注:page是内存中的概念,而块是文件中的概念) 16 | - 文件管理器将某个块的内如读/写入内存中的相应页中。 17 | 18 | 如上的这种架构的相应变种就是当前大多数商业关系数据库系统的变种。该架构严重受到磁盘I/O特性的影响,因此在本书的这部分中,我们从最底层——**磁盘**——开始,一步步向上实现该数据库软件。 19 | 20 | 面向对象的开发者通常将一个对象对另一个类的某个方法调用称为`客户端(clinet)`,`客户端`这个术语其实和本书第二部分所提及的客户端-服务端很类似,无论是哪种说法,客户端对象都会通过另一个类对象的某个方法执行一次请求。要说区别,那就是在`C-S`例子中,请求是通过网络实现的(注:即客户端和服务端通过网络进行交互),而一般意义上的client就没有这种限定。在本书的剩余部分,我们提到的客户端都代表这面向对象层面一般术语的意思,而`JDBC 客户端`则更倾向于`C-S`层面的意思。 21 | 22 | 23 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/RecordPageTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | import simpledb.file.Block; 4 | import simpledb.server.SimpleDB; 5 | import simpledb.tx.Transaction; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @ClassName RecordTest 11 | * @Description TODO 12 | * @Author LiuZhian 13 | * @Date 2020/2/3 3:06 下午 14 | * @Version 1.0 15 | */ 16 | public class RecordPageTest { 17 | public static void main(String[] args) throws IOException { 18 | 19 | Schema sch = new Schema(); 20 | sch.addIntField("cid"); 21 | sch.addStringField("title", 20); 22 | sch.addIntField("deptid"); 23 | TableInfo ti = new TableInfo("course", sch); 24 | 25 | SimpleDB.init("lzadb"); 26 | Transaction tx = new Transaction(); 27 | Block blk = new Block(ti.fileName(), 3); 28 | RecordPage rp = new RecordPage(blk, ti, tx); 29 | 30 | // Part 1 31 | boolean ok = rp.insert(); 32 | if (ok) { 33 | rp.setInt("cid", 82); 34 | rp.setString("title", "OO design"); 35 | rp.setInt("deptid", 20); 36 | } 37 | ok = rp.insert(); 38 | if (ok) { 39 | rp.setInt("cid", 80); 40 | rp.setString("title", "VB programming"); 41 | rp.setInt("deptid", 30); 42 | } 43 | 44 | // Part 2 移到上第一条记录的前面,准备遍历 45 | // 这里一定要移到-1而不是0,因为searchFor()方法会先将currentSlot++ 46 | rp.moveToID(-1); 47 | while (rp.next()) { 48 | int dept = rp.getInt("deptid"); 49 | if (dept == 30) 50 | rp.delete(); 51 | else if (rp.getString("title").equals("OO design")) 52 | rp.setString("title", "Object-Oriented"); 53 | } 54 | 55 | tx.commit(); 56 | 57 | 58 | Transaction tx2 = new Transaction(); 59 | 60 | RecordFormatter recordFormatter = new RecordFormatter(ti); 61 | Block blk2 = tx2.append(ti.fileName(), recordFormatter); 62 | RecordPage rp2 = new RecordPage(blk2, ti, tx2); 63 | rp2.insert(); 64 | 65 | tx2.commit(); 66 | 67 | 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/MetadataMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.record.Schema; 4 | import simpledb.record.TableInfo; 5 | import simpledb.tx.Transaction; 6 | 7 | import java.io.IOException; 8 | import java.util.Map; 9 | 10 | /** 11 | * @ClassName MetadataMgr 12 | * @Description TODO 13 | * @Author LiuZhian 14 | * @Date 2020/2/4 10:39 下午 15 | * @Version 1.0 16 | */ 17 | 18 | public class MetadataMgr { 19 | private static TableMgr tableMgr; 20 | private static ViewMgr viewMgr; 21 | private static StatMgr statMgr; 22 | private static IndexMgr indexMgr; 23 | 24 | public MetadataMgr(boolean isNew, Transaction tx) throws IOException { 25 | tableMgr = new TableMgr(isNew, tx); 26 | viewMgr = new ViewMgr(isNew, tableMgr, tx); 27 | statMgr = new StatMgr(tableMgr, tx); 28 | indexMgr = new IndexMgr(isNew, tableMgr, tx); 29 | 30 | } 31 | 32 | public void createTable(String tblName, Schema schema, Transaction tx) throws IOException { 33 | tableMgr.createTable(tblName, schema, tx); 34 | } 35 | 36 | public TableInfo getTableInfo(String tblName, Transaction tx) throws IOException { 37 | return tableMgr.getTableInfo(tblName, tx); 38 | } 39 | 40 | public void createView(String viewName, String viewDef, Transaction tx) throws IOException { 41 | viewMgr.createView(viewName, viewDef, tx); 42 | } 43 | 44 | public String getViewDef(String viewName, Transaction tx) throws IOException { 45 | return viewMgr.getViewDef(viewName, tx); 46 | } 47 | 48 | public void createIndex(String indexName, String tblName, 49 | String fieldName, Transaction tx) throws IOException { 50 | indexMgr.createIndex(indexName,tblName,fieldName,tx); 51 | } 52 | 53 | public Map getIndexInfo(String tblName, Transaction tx) throws IOException { 54 | return indexMgr.getIndexInfo(tblName,tx); 55 | } 56 | 57 | public StatInfo getStatInfo(String tblName, Transaction tx) throws IOException { 58 | return statMgr.getStatInfo(tblName,tx); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/file/Page.java: -------------------------------------------------------------------------------- 1 | package simpledb.file; 2 | 3 | import simpledb.server.SimpleDB; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.charset.Charset; 7 | 8 | /** 9 | * @ClassName Page 10 | * @Deacription // TODO 11 | * @Author LiuZhian 12 | * @Date 2020-01-14 14:49 13 | * @Version 1.0 14 | **/ 15 | 16 | public class Page { 17 | public static final int BLOCK_SIZE = 400; 18 | public static final int INT_SIZE = Integer.SIZE / Byte.SIZE; 19 | 20 | public static final int STR_SIZE(int n) { 21 | float bytesPerChar = Charset.defaultCharset().newEncoder().maxBytesPerChar(); 22 | // 指示字符串长度的整数 + 各字符占的字节数 23 | return INT_SIZE + n * ((int) bytesPerChar); 24 | } 25 | 26 | // 页中的内容 27 | private ByteBuffer contents = ByteBuffer.allocateDirect(BLOCK_SIZE); 28 | private FileMgr fileMgr = SimpleDB.fileMgr(); 29 | 30 | 31 | public Page() { 32 | } 33 | 34 | /** 35 | * 页粒度的并发锁 36 | */ 37 | public synchronized int getInt(int offset) { 38 | contents.position(offset); 39 | return contents.getInt(); 40 | } 41 | 42 | public synchronized void setInt(int offset, int val) { 43 | contents.position(offset); 44 | contents.putInt(val); 45 | } 46 | 47 | public synchronized String getString(int offset) { 48 | contents.position(offset); 49 | int len = contents.getInt(); // 先获取字符串的长度 50 | byte[] byteVal = new byte[len]; // 再获取字符串中的后续字符 51 | contents.get(byteVal); 52 | return new String(byteVal); 53 | } 54 | 55 | public synchronized void setString(int offset, String val) { 56 | contents.position(offset); 57 | byte[] byteVal = val.getBytes(); 58 | int len = byteVal.length; // 获取字符串的长度 59 | 60 | contents.putInt(len); 61 | contents.put(byteVal); 62 | } 63 | 64 | public synchronized void read(Block blk) { 65 | fileMgr.read(blk, contents); 66 | } 67 | 68 | public synchronized void write(Block blk) { 69 | fileMgr.write(blk, contents); 70 | } 71 | 72 | public synchronized Block append(String fileName) { 73 | return fileMgr.append(fileName, contents); 74 | } 75 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/query/IndexJoinPlan.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.query; 2 | 3 | import simpledb.index.Index; 4 | import simpledb.metadata.IndexInfo; 5 | import simpledb.query.Plan; 6 | import simpledb.query.Scan; 7 | import simpledb.query.TableScan; 8 | import simpledb.record.Schema; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * @program simpledb 14 | * @description: TODO 15 | * @author: liuzhian 16 | * @create: 2020/10/05 15:43 17 | */ 18 | public class IndexJoinPlan implements Plan { 19 | private Plan plan1; 20 | private Plan plan2; 21 | private IndexInfo indexInfo; 22 | private Schema schema; 23 | private String joinField; 24 | 25 | public IndexJoinPlan(Plan plan1, Plan plan2, IndexInfo indexInfo, Schema schema, String joinField) { 26 | this.plan1 = plan1; 27 | this.plan2 = plan2; 28 | this.indexInfo = indexInfo; 29 | this.schema = new Schema(); 30 | schema.addAll(plan1.schema()); 31 | schema.addAll(plan2.schema()); 32 | this.joinField = joinField; 33 | } 34 | 35 | @Override 36 | public Scan open() throws IOException { 37 | Scan scan1 = plan1.open(); 38 | // 如果table2不是一张物理存储的表,抛出异常 39 | TableScan tableScan2 = (TableScan) plan2.open(); 40 | Index idx = indexInfo.open(); 41 | return new IndexJoinScan(scan1, tableScan2, idx, joinField); 42 | } 43 | 44 | @Override 45 | public int blockAccessed() { 46 | // 遍历plan1的块访问次数 47 | return plan1.blockAccessed() + 48 | // 对应plan1中的每个块,都需要检索一次索引 49 | plan1.recordsOutput() * indexInfo.blocksAccessed() + 50 | recordsOutput() ; // todo: why here? 51 | } 52 | 53 | @Override 54 | public int recordsOutput() { 55 | return plan1.recordsOutput() * indexInfo.recordsOutput(); 56 | } 57 | 58 | @Override 59 | public int distinctValues(String fldName) { 60 | if(plan1.schema().hasFiled(fldName)) 61 | return plan1.distinctValues(fldName); 62 | else 63 | return plan2.distinctValues(fldName); 64 | } 65 | 66 | @Override 67 | public Schema schema() { 68 | return schema; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/planner/Planner.java: -------------------------------------------------------------------------------- 1 | package simpledb.planner; 2 | 3 | import simpledb.parser.*; 4 | import simpledb.query.Plan; 5 | import simpledb.tx.Transaction; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @ClassName Planner 11 | * @Deacription // TODO 12 | * @Author LiuZhian 13 | * @Date 2020-02-26 15:48 14 | * @Version 1.0 15 | **/ 16 | 17 | public class Planner { 18 | private QueryPlanner queryPlanner; 19 | private UpdatePlanner updatePlanner; 20 | 21 | public Planner(QueryPlanner queryPlanner, UpdatePlanner updatePlanner) { 22 | this.queryPlanner = queryPlanner; 23 | this.updatePlanner = updatePlanner; 24 | } 25 | 26 | public Plan createQueryPlan(String query, Transaction tx) throws IOException { 27 | Parser parser=new Parser(new Lexer(query)); 28 | QueryData queryData=parser.query(); 29 | //================ TODO ================== 30 | // 验证SQL语句语义正确性的代码 31 | // 见 19.2 小节 32 | //================ TODO ================== 33 | return queryPlanner.createPlan(queryData,tx); 34 | } 35 | public int executeUpdate(String cmd, Transaction tx) throws IOException { 36 | Parser parser=new Parser(new Lexer(cmd)); 37 | Object obj=parser.updateCmd(); 38 | //================ TODO ================== 39 | // 验证update SQL语句语义正确性的代码 40 | // 见 19.2 小节 41 | //================ TODO ================== 42 | if(obj instanceof InsertData) 43 | return updatePlanner.executeInsert((InsertData) obj,tx); 44 | else if(obj instanceof DeleteData) 45 | return updatePlanner.executeDelete((DeleteData)obj,tx); 46 | else if(obj instanceof ModifyData) 47 | return updatePlanner.executeModify((ModifyData)obj,tx); 48 | else if(obj instanceof CreateTableData) 49 | return updatePlanner.executeCreateTable((CreateTableData)obj,tx); 50 | else if(obj instanceof CreateViewData) 51 | return updatePlanner.executeCreateView((CreateViewData)obj,tx); 52 | else if(obj instanceof CreateIndexData) 53 | return updatePlanner.executeCreateIndex((CreateIndexData)obj,tx); 54 | else 55 | return 0; 56 | } 57 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/Schema.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | 4 | import java.util.Collection; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | 9 | /** 10 | * @ClassName Schema 11 | * @Description TODO 12 | * @Author LiuZhian 13 | * @Date 2020/2/3 1:09 下午 14 | * @Version 1.0 15 | */ 16 | public class Schema { 17 | public static final int INTEGER = 0; 18 | public static final int VARCHAR = 1; 19 | 20 | private Map info = new HashMap<>(); 21 | 22 | public Schema() { 23 | 24 | } 25 | 26 | public void addField(String fldName, int type, int length) { 27 | info.put(fldName, new FieldInfo(type, length)); 28 | } 29 | 30 | public void addIntField(String fldName) { 31 | // int类型的长度设置为0,这里指的是逻辑长度,没什么实际意义,可以设置为任意值 32 | // 而不是实际物理存储所需字节长度 33 | addField(fldName, INTEGER, 0); 34 | } 35 | 36 | public void addStringField(String fldName, int length) { 37 | addField(fldName, VARCHAR, length); 38 | } 39 | 40 | /** 41 | * 从一个已有的schema中添加一个字段到该schema中 42 | * 43 | * @param fldName 字段名 44 | * @param schema 源schema 45 | */ 46 | public void add(String fldName, Schema schema) { 47 | int type = schema.type(fldName); 48 | int length = schema.length(fldName); 49 | addField(fldName, type, length); 50 | } 51 | 52 | /** 53 | * 将已有的schema中的所有字段到该schema中 54 | * 55 | * @param schema 源schema 56 | */ 57 | public void addAll(Schema schema) { 58 | info.putAll(schema.info); 59 | } 60 | 61 | public Collection fields() { 62 | return this.info.keySet(); 63 | } 64 | 65 | public boolean hasFiled(String fldName) { 66 | return this.info.keySet().contains(fldName); 67 | } 68 | 69 | public int type(String fldName) { 70 | return this.info.get(fldName).type; 71 | } 72 | 73 | public int length(String fldName) { 74 | return this.info.get(fldName).length; 75 | } 76 | 77 | private class FieldInfo { 78 | int type; 79 | int length; // 对于string类型的字段,length记录的是可能最长的字符数 80 | 81 | public FieldInfo(int type, int length) { 82 | this.type = type; 83 | this.length = length; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/SetIntRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | import simpledb.buffer.Buffer; 4 | import simpledb.buffer.BufferMgr; 5 | import simpledb.file.Block; 6 | import simpledb.log.BasicLogRecord; 7 | import simpledb.log.LogMgr; 8 | import simpledb.server.SimpleDB; 9 | 10 | /** 11 | * @ClassName SetIntReord 12 | * @Deacription // TODO 13 | * @Author LiuZhian 14 | * @Date 2020-01-21 15:06 15 | * @Version 1.0 16 | **/ 17 | 18 | public class SetIntRecord implements LogRecord { 19 | 20 | private int myTxNum; 21 | private int offset; 22 | private int oldVal; 23 | private Block blk; 24 | 25 | 26 | public SetIntRecord(int myTxNum, Block blk, int offset, int oldVal) { 27 | this.myTxNum = myTxNum; 28 | this.blk = blk; 29 | this.offset = offset; 30 | this.oldVal = oldVal; 31 | } 32 | 33 | /** 34 | * 根据一条BasicLogRecord来构造一条SetIntRecord。 35 | * 该构造函数是为了给 恢复/回滚 算法调用 36 | *

37 | * 注意,一条更新日志记录的格式为: 38 | *

39 | * 40 | * 41 | * @param blr 42 | */ 43 | public SetIntRecord(BasicLogRecord blr) { 44 | myTxNum = blr.nextInt(); 45 | String filename = blr.nextString(); 46 | int blkNum = blr.nextInt(); 47 | blk = new Block(filename, blkNum); 48 | offset = blr.nextInt(); 49 | oldVal = blr.nextInt(); 50 | } 51 | 52 | @Override 53 | public int writeToLog() { 54 | 55 | Object[] rec = new Object[]{SETINT, myTxNum, blk.filename(), 56 | blk.number(), offset, oldVal}; 57 | 58 | LogMgr logMgr = SimpleDB.logMgr(); 59 | return logMgr.append(rec); 60 | } 61 | 62 | @Override 63 | public int op() { 64 | return SETINT; 65 | } 66 | 67 | @Override 68 | public int txNumber() { 69 | return myTxNum; 70 | } 71 | 72 | @Override 73 | public void undo() { 74 | BufferMgr bufferMgr = SimpleDB.bufferMgr(); 75 | Buffer buffer = bufferMgr.pin(blk); 76 | buffer.setInt(offset, oldVal, myTxNum, -1); // undo操作无需日志记录 77 | bufferMgr.unpin(buffer); 78 | } 79 | 80 | public String toString() { 81 | return ""; 82 | } 83 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/RemoteResultSetImpl.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import simpledb.query.Plan; 4 | import simpledb.query.Scan; 5 | import simpledb.record.Schema; 6 | import simpledb.tx.Transaction; 7 | 8 | import java.io.IOException; 9 | import java.rmi.RemoteException; 10 | import java.rmi.server.UnicastRemoteObject; 11 | 12 | /** 13 | * @ClassName RemoteResultSetImpl 14 | * @Deacription // TODO 15 | * @Author LiuZhian 16 | * @Date 2020-03-14 20:21 17 | * @Version 1.0 18 | **/ 19 | 20 | public class RemoteResultSetImpl extends UnicastRemoteObject 21 | implements RemoteResultSet { 22 | private RemoteConnectionImpl rconn; 23 | private Scan s; 24 | private Schema sch; 25 | 26 | public RemoteResultSetImpl(Plan plan, RemoteConnectionImpl rconn) 27 | throws RemoteException { 28 | try { 29 | s = plan.open(); 30 | sch = plan.schema(); 31 | this.rconn = rconn; 32 | } catch (IOException e) { 33 | System.out.println("Error in RemoteResultSetImpl constructor!"); 34 | } 35 | 36 | } 37 | 38 | @Override 39 | public boolean next() throws RemoteException { 40 | try { 41 | return s.next(); 42 | } catch (IOException e) { 43 | rconn.rollback(); 44 | System.out.println("Error in RemoteResultSetImpl next() method!"); 45 | } 46 | return false; 47 | } 48 | 49 | @Override 50 | public int getInt(String fieldName) throws RemoteException { 51 | // 大小写不敏感 52 | fieldName=fieldName.toLowerCase(); 53 | return s.getInt(fieldName); 54 | } 55 | 56 | @Override 57 | public String getString(String fieldName) throws RemoteException { 58 | // 大小写不敏感 59 | fieldName=fieldName.toLowerCase(); 60 | return s.getString(fieldName); 61 | } 62 | 63 | @Override 64 | public RemoteMetaData getMetaData() throws RemoteException { 65 | return new RemoteMetaDataImpl(sch); 66 | } 67 | 68 | @Override 69 | public void close() throws RemoteException { 70 | try { 71 | s.close(); 72 | rconn.commit(); 73 | } catch (IOException e) { 74 | rconn.rollback(); 75 | System.out.println("Error in RemoteResultSetImpl close() method!"); 76 | } 77 | 78 | } 79 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/planner/PlannerTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.planner; 2 | 3 | import simpledb.query.Plan; 4 | import simpledb.query.Scan; 5 | import simpledb.record.TableInfo; 6 | import simpledb.server.SimpleDB; 7 | import simpledb.tx.Transaction; 8 | import simpledb.tx.recovery.LogRecord; 9 | import simpledb.tx.recovery.LogRecordIterator; 10 | 11 | import java.io.IOException; 12 | import java.util.Iterator; 13 | 14 | /** 15 | * @ClassName PlannerTest 16 | * @Deacription // TODO 17 | * @Author LiuZhian 18 | * @Date 2020-02-26 16:00 19 | * @Version 1.0 20 | **/ 21 | 22 | public class PlannerTest { 23 | 24 | public static void main(String[] args) throws IOException { 25 | SimpleDB.init("lzadb"); 26 | 27 | Planner planner = SimpleDB.planner(); 28 | Transaction tx = new Transaction(); 29 | 30 | // 新建一个表 31 | String str = "create table student (sid int, sname varchar(5), age int)"; 32 | planner.executeUpdate(str, tx); 33 | 34 | // 查询插入的表的信息 35 | TableInfo tableInfo = SimpleDB.metadataMgr().getTableInfo("student", tx); 36 | for (String fieldName : tableInfo.schema().fields()) 37 | System.out.println(fieldName); 38 | 39 | // 插入一条记录到新建的表中 40 | String insertSQL = "insert into student (sid,sname,age) values (88,'andy',22)"; 41 | int numRowsAffected = planner.executeUpdate(insertSQL, tx); 42 | System.out.println("插入" + numRowsAffected + "条数据成功!"); 43 | 44 | // 查询刚才插入的数据 45 | String querySQL = "select sid,sname,age from student"; 46 | Plan queryPlan = planner.createQueryPlan(querySQL, tx); 47 | Scan scan = queryPlan.open(); 48 | while (scan.next()) { 49 | System.out.println(scan.getInt("sid") + " " + 50 | scan.getString("sname") + " " + 51 | scan.getInt("age")); 52 | } 53 | scan.close(); 54 | 55 | tx.commit(); 56 | 57 | 58 | // 查看日志 59 | int logRecordNum = 0; 60 | Iterator iter = new LogRecordIterator(); 61 | while (iter.hasNext()) { 62 | LogRecord record = iter.next(); 63 | logRecordNum++; 64 | System.out.println(record); 65 | 66 | } 67 | System.out.println(logRecordNum); 68 | 69 | 70 | // // 处理一个update query 71 | // String cmd = "delete from STUDENT where MajorId=30"; // 大小写不敏感 72 | // int numAffected = planner.executeUpdate(cmd, tx); 73 | // System.out.println(numAffected + " records was affected!"); 74 | } 75 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/ViewMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.record.RecordFile; 4 | import simpledb.record.Schema; 5 | import simpledb.record.TableInfo; 6 | import simpledb.tx.Transaction; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * @ClassName ViewMgr 12 | * @Description TODO 13 | * @Author LiuZhian 14 | * @Date 2020/2/5 5:06 下午 15 | * @Version 1.0 16 | */ 17 | public class ViewMgr { 18 | // 视图定义的最长字符串为150字节 19 | private static final int VIEW_DEF_MAX_LEN = 150; 20 | 21 | TableMgr tableMgr; 22 | 23 | public ViewMgr(boolean isNew, TableMgr tableMgr, Transaction tx) throws IOException { 24 | this.tableMgr = tableMgr; 25 | 26 | // 如果是新创建的数据库,则会创建一个viewcat元数据表 27 | if (isNew) { 28 | Schema schema = new Schema(); 29 | // 视图名、表名、字段名 的最大长度都是一样的,为20个字符 30 | schema.addStringField("viewname", TableMgr.TABLE_AND_FIELD_NAME_MAX_LEN); 31 | schema.addStringField("viewdef", VIEW_DEF_MAX_LEN); 32 | tableMgr.createTable("viewcat", schema, tx); 33 | } 34 | } 35 | 36 | /** 37 | * 将一个新视图的元数据插入viewcat表中 38 | * 39 | * @param viewName 40 | * @param viewDef 41 | * @param tx 42 | */ 43 | public void createView(String viewName, String viewDef, Transaction tx) throws IOException { 44 | TableInfo viewCatTableInfo = this.tableMgr.getTableInfo("viewcat", tx); 45 | 46 | RecordFile viewCatRecordFile = new RecordFile(viewCatTableInfo, tx); 47 | viewCatRecordFile.insert(); // 找到一个插入的位置 48 | viewCatRecordFile.setString("viewname", viewName); 49 | viewCatRecordFile.setString("viewdef", viewDef); 50 | viewCatRecordFile.close(); 51 | 52 | } 53 | 54 | /** 55 | * 检索某个视图的定义 56 | * 57 | * @param viewName 指定视图名 58 | * @param tx 59 | * @return 60 | */ 61 | public String getViewDef(String viewName, Transaction tx) throws IOException { 62 | String result = null; 63 | TableInfo viewCatTableInfo = this.tableMgr.getTableInfo("viewcat", tx); 64 | RecordFile viewCatRecordFile = new RecordFile(viewCatTableInfo, tx); 65 | // 遍历各个视图元数据 66 | // TODO 新建一个RecordFile对象时会自动移到第一块,所以以下语句可以省略 67 | viewCatRecordFile.beforeFirst(); 68 | // TODO 69 | while (viewCatRecordFile.next()) { 70 | if (viewCatRecordFile.getString("viewname").equals(viewName)) { 71 | result = viewCatRecordFile.getString("viewdef"); 72 | break; 73 | } 74 | } 75 | viewCatRecordFile.close(); 76 | return result; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/recovery/SetStringRecord.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.recovery; 2 | 3 | import simpledb.buffer.Buffer; 4 | import simpledb.buffer.BufferMgr; 5 | import simpledb.file.Block; 6 | import simpledb.log.BasicLogRecord; 7 | import simpledb.log.LogMgr; 8 | import simpledb.server.SimpleDB; 9 | 10 | /** 11 | * @ClassName SetStringRecord 12 | * @Deacription // TODO 13 | * @Author LiuZhian 14 | * @Date 2020-01-21 14:29 15 | * @Version 1.0 16 | **/ 17 | 18 | public class SetStringRecord implements LogRecord { 19 | private int myTxNum; 20 | private int offset; 21 | 22 | private String oldVal; 23 | private Block blk; 24 | 25 | public SetStringRecord(int myTxNum, Block blk, int offset, String oldVal) { 26 | this.myTxNum = myTxNum; 27 | this.offset = offset; 28 | this.blk = blk; 29 | this.oldVal = oldVal; 30 | } 31 | 32 | /** 33 | * 根据一条BasicLogRecord来构造一条SetStringRecord。 34 | * 该构造函数是为了给 恢复/回滚 算法调用 35 | *

36 | * 注意,一条更新日志记录的格式为: 37 | *

38 | * 39 | * 40 | * @param blr 41 | */ 42 | public SetStringRecord(BasicLogRecord blr) { 43 | myTxNum = blr.nextInt(); 44 | String fileName = blr.nextString(); 45 | int blkNum = blr.nextInt(); 46 | blk = new Block(fileName, blkNum); 47 | offset = blr.nextInt(); 48 | oldVal = blr.nextString(); 49 | } 50 | 51 | /** 52 | * 将一条日志记录写入日志文件,返回LSN 53 | * 54 | * @return 55 | */ 56 | @Override 57 | public int writeToLog() { 58 | Object[] rec = new Object[]{SETSTRING, myTxNum, blk.filename(), 59 | blk.number(), offset, oldVal}; 60 | 61 | LogMgr logMgr = SimpleDB.logMgr(); 62 | return logMgr.append(rec); 63 | } 64 | 65 | /** 66 | * 返回日志记录的操作符。 67 | *

68 | * CHECKPOINT = 0, START = 1, 69 | * COMMIT = 2, ROLLBACK = 3, 70 | * SETINT = 4, SETSTRING = 5; 71 | * 72 | * @return integer 73 | */ 74 | @Override 75 | public int op() { 76 | return SETSTRING; 77 | } 78 | 79 | @Override 80 | public int txNumber() { 81 | return myTxNum; 82 | } 83 | 84 | @Override 85 | public void undo() { 86 | BufferMgr bufferMgr = SimpleDB.bufferMgr(); 87 | Buffer buff = bufferMgr.pin(blk); 88 | buff.setString(offset, oldVal, myTxNum, -1); // undo操作不需要对应的日志记录 89 | bufferMgr.unpin(buff); 90 | } 91 | 92 | public String toString() { 93 | return ""; 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/planner/BasicUpdatePlanner.java: -------------------------------------------------------------------------------- 1 | package simpledb.planner; 2 | 3 | import simpledb.parser.*; 4 | import simpledb.query.*; 5 | import simpledb.server.SimpleDB; 6 | import simpledb.tx.Transaction; 7 | 8 | import java.io.IOException; 9 | import java.util.Iterator; 10 | 11 | /** 12 | * @ClassName BasicUpdatePlanner 13 | * @Deacription // TODO 14 | * @Author LiuZhian 15 | * @Date 2020-02-26 18:12 16 | * @Version 1.0 17 | **/ 18 | 19 | public class BasicUpdatePlanner implements UpdatePlanner { 20 | @Override 21 | public int executeInsert(InsertData insertData, Transaction tx) throws IOException { 22 | Plan p = new TablePlan(insertData.getTblName(), tx); 23 | UpdateScan scan = (UpdateScan) p.open(); 24 | scan.insert(); 25 | // 插入的新记录各字段的取值 26 | Iterator iter = insertData.getVals().iterator(); 27 | for (String fieldName : insertData.getFields()) { 28 | Constant val = iter.next(); 29 | scan.setVal(fieldName, val); 30 | } 31 | scan.close(); 32 | return 1; 33 | } 34 | 35 | @Override 36 | public int executeDelete(DeleteData deleteData, Transaction tx) throws IOException { 37 | Plan p = new TablePlan(deleteData.getTblName(), tx); 38 | p = new SelectPlan(p, deleteData.getPred()); 39 | UpdateScan scan = (UpdateScan) p.open(); 40 | int cnt = 0; 41 | while (scan.next()) { 42 | scan.delete(); 43 | cnt++; 44 | } 45 | scan.close(); 46 | return cnt; 47 | 48 | } 49 | 50 | @Override 51 | public int executeModify(ModifyData modifyData, Transaction tx) throws IOException { 52 | Plan p = new TablePlan(modifyData.getTblName(), tx); 53 | p = new SelectPlan(p, modifyData.getPred()); 54 | UpdateScan scan = (UpdateScan) p.open(); 55 | int cnt = 0; 56 | while (scan.next()) { 57 | scan.setVal(modifyData.getFldName(), 58 | modifyData.getNewVal().asConstant()); 59 | cnt++; 60 | } 61 | scan.close(); 62 | return cnt; 63 | } 64 | 65 | @Override 66 | public int executeCreateTable(CreateTableData data, Transaction tx) throws IOException { 67 | SimpleDB.metadataMgr().createTable(data.getTblName(), 68 | data.getSchema(), 69 | tx); 70 | return 0; 71 | } 72 | 73 | @Override 74 | public int executeCreateView(CreateViewData data, Transaction tx) throws IOException { 75 | SimpleDB.metadataMgr().createView(data.getViewName(), 76 | data.getViewDef(), 77 | tx); 78 | return 0; 79 | } 80 | 81 | @Override 82 | public int executeCreateIndex(CreateIndexData data, Transaction tx) throws IOException { 83 | SimpleDB.metadataMgr().createIndex(data.getIndexName(), 84 | data.getTblName(), 85 | data.getFldName(), 86 | tx); 87 | return 0; 88 | } 89 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/buffer/BufferMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.buffer; 2 | 3 | import simpledb.file.Block; 4 | 5 | 6 | /** 7 | * @ClassName BufferMgr 8 | * @Deacription // TODO 9 | * @Author LiuZhian 10 | * @Date 2020-01-17 19:31 11 | * @Version 1.0 12 | **/ 13 | 14 | public class BufferMgr { 15 | 16 | // 最长等待时间 17 | private static final long MAX_TIME = 10000; 18 | 19 | private BasicBufferMgr basicBufferMgr; 20 | 21 | public BufferMgr(int numBuffers) { 22 | basicBufferMgr = new BasicBufferMgr(numBuffers); 23 | } 24 | 25 | /** 26 | * 对BasicBufferMgr类中pin方法的包装 27 | * 28 | * @param blk 29 | * @return 30 | */ 31 | public synchronized Buffer pin(Block blk) { 32 | try { 33 | long timestamp = System.currentTimeMillis(); 34 | Buffer buff = basicBufferMgr.pin(blk); 35 | while (null == buff && !waitTooLong(timestamp)) { 36 | // this.wait(),等待的对象是当前这个缓冲管理器, 37 | // 等待的目标是有一个未被固定的缓冲区 38 | wait(MAX_TIME); 39 | buff = basicBufferMgr.pin(blk); 40 | } 41 | // TODO 这里非常重要 42 | // 如果因为死锁或其他各种原因,导致等待时间超过阈值 MAX_TIM,则向客户端抛出异常,让客户端自行处理 43 | if (null == buff) 44 | throw new BufferAbortException(); 45 | 46 | return buff; 47 | } catch (InterruptedException e) { 48 | throw new BufferAbortException(); 49 | } 50 | } 51 | 52 | public synchronized void unpin(Buffer buffer) { 53 | basicBufferMgr.unpin(buffer); 54 | // 一旦有一个缓冲区“自由”,通知其他所有在缓冲管理器对象上等待的线程 55 | if (!buffer.isPinned()) 56 | notifyAll(); 57 | } 58 | 59 | /** 60 | * 对BasicBufferMgr类中pinNew方法的包装 61 | * 62 | * @param fileName 63 | * @param pageFormatter 64 | * @return 65 | */ 66 | public synchronized Buffer pinNew(String fileName, PageFormatter pageFormatter) { 67 | try { 68 | long timestamp=System.currentTimeMillis(); 69 | Buffer buff = basicBufferMgr.pinNew(fileName, pageFormatter); 70 | while (null == buff && !waitTooLong(timestamp)) { 71 | // this.wait(),等待的对象是当前这个缓冲管理器, 72 | // 等待的目标是有一个未被固定的缓冲区 73 | wait(MAX_TIME); 74 | buff = basicBufferMgr.pinNew(fileName, pageFormatter); 75 | } 76 | // TODO 这里非常重要 77 | // 如果发生了死锁 78 | if (null == buff) 79 | throw new BufferAbortException(); 80 | 81 | return buff; 82 | } catch (InterruptedException e) { 83 | throw new BufferAbortException(); 84 | } 85 | } 86 | 87 | public void flushAll(int txNum) { 88 | basicBufferMgr.flushAll(txNum); 89 | } 90 | 91 | public int available() { 92 | return basicBufferMgr.available(); 93 | } 94 | 95 | 96 | private boolean waitTooLong(long startTime) { 97 | return System.currentTimeMillis() - startTime > MAX_TIME; 98 | } 99 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/TableScan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.RID; 4 | import simpledb.record.RecordFile; 5 | import simpledb.record.Schema; 6 | import simpledb.record.TableInfo; 7 | import simpledb.tx.Transaction; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * @ClassName Table 13 | * @Description TODO 14 | * @Author LiuZhian 15 | * @Date 2020/2/11 9:46 下午 16 | * @Version 1.0 17 | */ 18 | public class TableScan implements UpdateScan { 19 | private RecordFile recordFile; 20 | private Schema schema; 21 | 22 | public TableScan(TableInfo tableInfo, Transaction tx) throws IOException { 23 | recordFile = new RecordFile(tableInfo, tx); 24 | schema = tableInfo.schema(); 25 | } 26 | //================Scan 接口中的方法实现=================== 27 | @Override 28 | public void beforeFirst() { 29 | recordFile.beforeFirst(); 30 | } 31 | 32 | @Override 33 | public boolean next() throws IOException { 34 | return recordFile.next(); 35 | } 36 | 37 | @Override 38 | public void close() { 39 | recordFile.close(); 40 | } 41 | 42 | @Override 43 | public Constant getVal(String fieldName) { 44 | if (schema.type(fieldName)==Schema.VARCHAR) 45 | return new StringConstant(recordFile.getString(fieldName)); 46 | else 47 | return new IntConstant(recordFile.getInt(fieldName)); 48 | } 49 | 50 | @Override 51 | public int getInt(String fieldName) { 52 | return recordFile.getInt(fieldName); 53 | } 54 | 55 | @Override 56 | public String getString(String fieldName) { 57 | return recordFile.getString(fieldName); 58 | } 59 | 60 | @Override 61 | public boolean hasField(String fieldName) { 62 | return schema.hasFiled(fieldName); 63 | } 64 | 65 | //================UpdateScan 接口中额外的方法实现=================== 66 | 67 | @Override 68 | public void setVal(String fieldName, Constant newVal) { 69 | if (schema.type(fieldName)==Schema.VARCHAR) 70 | recordFile.setString(fieldName,(String) newVal.asJavaVal()); 71 | else 72 | recordFile.setInt(fieldName,(Integer)newVal.asJavaVal()); 73 | 74 | } 75 | 76 | @Override 77 | public void setInt(String fieldName, int newVal) { 78 | recordFile.setInt(fieldName,newVal); 79 | } 80 | 81 | @Override 82 | public void setString(String fieldName, String newVal) { 83 | recordFile.setString(fieldName,newVal); 84 | } 85 | 86 | @Override 87 | public void insert() throws IOException { 88 | recordFile.insert(); 89 | } 90 | 91 | @Override 92 | public void delete() { 93 | recordFile.delete(); 94 | } 95 | 96 | @Override 97 | public RID getRID() { 98 | return recordFile.currentRID(); 99 | } 100 | 101 | @Override 102 | public void moveToRId(RID rid) { 103 | recordFile.moveToRID(rid); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/query/IndexJoinScan.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.query; 2 | 3 | import com.sun.xml.internal.bind.v2.model.core.ID; 4 | import simpledb.index.Index; 5 | import simpledb.query.Constant; 6 | import simpledb.query.Scan; 7 | import simpledb.query.TableScan; 8 | import simpledb.record.RID; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * @program simpledb 14 | * @description: TODO 15 | * @author: liuzhian 16 | * @create: 2020/10/05 16:01 17 | */ 18 | public class IndexJoinScan implements Scan { 19 | private Scan scan1; 20 | private TableScan tableScan2; 21 | private Index index; 22 | private String joinedField; 23 | 24 | public IndexJoinScan(Scan scan1, TableScan tableScan2, Index index, String joinedField) throws IOException { 25 | this.scan1 = scan1; 26 | this.tableScan2 = tableScan2; 27 | this.index = index; 28 | this.joinedField = joinedField; 29 | beforeFirst(); 30 | } 31 | 32 | @Override 33 | public void beforeFirst() throws IOException { 34 | scan1.beforeFirst(); 35 | scan1.next(); 36 | resetIndex(); // 重置table2的索引 37 | } 38 | 39 | @Override 40 | public boolean next() throws IOException { 41 | while (true) { 42 | if (index.next()) { 43 | RID rid = index.getDataRid(); 44 | tableScan2.moveToRId(rid); 45 | return true; 46 | } 47 | // scan1中也没有下一条记录了 48 | if (!scan1.next()) 49 | return false; 50 | // 如果scan1还有下一条记录,那么将那条记录的索引字段取值,去重置table2的索引 51 | resetIndex(); 52 | } 53 | } 54 | 55 | @Override 56 | public void close() throws IOException { 57 | scan1.close(); 58 | tableScan2.close(); 59 | index.close(); 60 | } 61 | 62 | @Override 63 | public Constant getVal(String fieldName) { 64 | if(tableScan2.hasField(fieldName)) 65 | return tableScan2.getVal(fieldName); 66 | else 67 | return scan1.getVal(fieldName); 68 | } 69 | 70 | @Override 71 | public int getInt(String fieldName) { 72 | if(tableScan2.hasField(fieldName)) 73 | return tableScan2.getInt(fieldName); 74 | else 75 | return scan1.getInt(fieldName); 76 | } 77 | 78 | @Override 79 | public String getString(String fieldName) { 80 | if(tableScan2.hasField(fieldName)) 81 | return tableScan2.getString(fieldName); 82 | else 83 | return scan1.getString(fieldName); 84 | } 85 | 86 | @Override 87 | public boolean hasField(String fieldName) { 88 | return scan1.hasField(fieldName) || tableScan2.hasField(fieldName); 89 | } 90 | 91 | /** 92 | * 重置table2的索引。 93 | *

94 | * 先获取索引字段在scan1上的当前取值,并用该取值作为索引的取值 95 | */ 96 | private void resetIndex() throws IOException { 97 | Constant searchKey = scan1.getVal(joinedField); 98 | index.beforeFirst(searchKey); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/IndexMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.record.RecordFile; 4 | import simpledb.record.Schema; 5 | import simpledb.record.TableInfo; 6 | import simpledb.tx.Transaction; 7 | 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | import static simpledb.metadata.TableMgr.TABLE_AND_FIELD_NAME_MAX_LEN; 13 | 14 | /** 15 | * @ClassName IndexMgr 16 | * @Description TODO 17 | * @Author LiuZhian 18 | * @Date 2020/2/6 2:00 下午 19 | * @Version 1.0 20 | */ 21 | public class IndexMgr { 22 | 23 | private TableInfo tableInfo; 24 | 25 | /** 26 | * 创建IndexMgr对象。 27 | *

28 | * IndexMgr会把索引的信息存储在表idxcat中,该表的schema如下: 29 | * (indexname, tablename, fieldname) 30 | *

31 | * 分别代表索引名、被索引的表名、被索引的字段名 32 | * 33 | * @param isNew 34 | * @param tableMgr 35 | * @param tx 36 | */ 37 | public IndexMgr(boolean isNew, TableMgr tableMgr, Transaction tx) throws IOException { 38 | 39 | if (isNew) { 40 | Schema schema = new Schema(); 41 | schema.addStringField("indexname", TABLE_AND_FIELD_NAME_MAX_LEN); 42 | schema.addStringField("tablename", TABLE_AND_FIELD_NAME_MAX_LEN); 43 | schema.addStringField("fieldname", TABLE_AND_FIELD_NAME_MAX_LEN); 44 | tableMgr.createTable("idxcat", schema, tx); 45 | } 46 | tableInfo = tableMgr.getTableInfo("idxcat", tx); 47 | } 48 | 49 | /** 50 | * 创建一个索引 51 | * 52 | * @param idxName 索引名 53 | * @param tblName 被索引的表名 54 | * @param fldName 被索引的字段名 55 | * @param tx 56 | * @throws IOException 57 | */ 58 | public void createIndex(String idxName, String tblName, 59 | String fldName, Transaction tx) throws IOException { 60 | RecordFile idxCatRecordFile = new RecordFile(tableInfo, tx); 61 | idxCatRecordFile.insert(); // 找到一个插入位置 62 | idxCatRecordFile.setString("indexname", idxName); 63 | idxCatRecordFile.setString("tablename", tblName); 64 | idxCatRecordFile.setString("fieldname", fldName); 65 | idxCatRecordFile.close(); 66 | } 67 | 68 | /** 69 | * 获取到指定表的所有索引信息 70 | * 71 | * @param tblName 被索引的表 72 | * @param tx 73 | * @return 74 | */ 75 | public Map getIndexInfo(String tblName, Transaction tx) throws IOException { 76 | Map result = new HashMap<>(); 77 | RecordFile idxCatRecordFile = new RecordFile(tableInfo, tx); 78 | idxCatRecordFile.beforeFirst(); 79 | while (idxCatRecordFile.next()) { 80 | if (idxCatRecordFile.getString("tablename").equals(tblName)) { 81 | String idxName = idxCatRecordFile.getString("indexname"); 82 | String fldName = idxCatRecordFile.getString("fieldname"); 83 | 84 | IndexInfo indexInfo = new IndexInfo(idxName, tblName, fldName, tx); 85 | result.put(fldName, indexInfo); 86 | } 87 | } 88 | idxCatRecordFile.close(); 89 | 90 | return result; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/SelectScan.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.RID; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * @ClassName SelectScan 9 | * @Description TODO 10 | * @Author LiuZhian 11 | * @Date 2020/2/12 3:56 下午 12 | * @Version 1.0 13 | */ 14 | public class SelectScan implements UpdateScan { 15 | 16 | private Scan scan; 17 | private Predicate predicate; 18 | 19 | public SelectScan(Scan scan, Predicate predicate) { 20 | this.scan = scan; 21 | this.predicate = predicate; 22 | } 23 | 24 | //================Scan 接口中的方法实现=================== 25 | 26 | @Override 27 | public void beforeFirst() throws IOException { 28 | scan.beforeFirst(); 29 | } 30 | 31 | @Override 32 | public boolean next() throws IOException { 33 | // 这里必须用while,而不是if 34 | while (scan.next()) 35 | if (predicate.isSatisified(scan)) 36 | return true; 37 | return false; 38 | } 39 | 40 | @Override 41 | public void close() throws IOException { 42 | scan.close(); 43 | } 44 | 45 | @Override 46 | public Constant getVal(String fieldName) { 47 | return scan.getVal(fieldName); 48 | } 49 | 50 | @Override 51 | public int getInt(String fieldName) { 52 | return scan.getInt(fieldName); 53 | } 54 | 55 | @Override 56 | public String getString(String fieldName) { 57 | return scan.getString(fieldName); 58 | } 59 | 60 | @Override 61 | public boolean hasField(String fieldName) { 62 | return scan.hasField(fieldName); 63 | } 64 | 65 | //================UpdateScan 接口中额外的方法实现=================== 66 | 67 | @Override 68 | public void setVal(String fieldName, Constant newVal) { 69 | // 这里必须把scan强转为UpdateScan类型 70 | // 也只有UpdateScan类型的对象才有setXXX()方法 71 | // 如果scan不是一个实现了UpdateScan接口的对象,运行时则会抛出ClassCastException异常 72 | UpdateScan updateScan = (UpdateScan) scan; 73 | updateScan.setVal(fieldName,newVal); 74 | } 75 | 76 | @Override 77 | public void setInt(String fieldName, int newVal) { 78 | UpdateScan updateScan = (UpdateScan) scan; 79 | updateScan.setInt(fieldName, newVal); 80 | } 81 | 82 | @Override 83 | public void setString(String fieldName, String newVal) { 84 | UpdateScan updateScan = (UpdateScan) scan; 85 | updateScan.setString(fieldName, newVal); 86 | } 87 | 88 | @Override 89 | public void insert() throws IOException { 90 | UpdateScan updateScan = (UpdateScan) scan; 91 | updateScan.insert(); 92 | } 93 | 94 | @Override 95 | public void delete() { 96 | UpdateScan updateScan = (UpdateScan) scan; 97 | updateScan.delete(); 98 | } 99 | 100 | @Override 101 | public RID getRID() { 102 | UpdateScan updateScan = (UpdateScan) scan; 103 | return updateScan.getRID(); 104 | } 105 | 106 | @Override 107 | public void moveToRId(RID rid) { 108 | UpdateScan updateScan = (UpdateScan) scan; 109 | updateScan.moveToRId(rid); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/IndexInfo.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.index.btree.BTreeIndex; 4 | import simpledb.index.hash.HashIndex; 5 | import simpledb.index.Index; 6 | import simpledb.record.Schema; 7 | import simpledb.record.TableInfo; 8 | import simpledb.server.SimpleDB; 9 | import simpledb.tx.Transaction; 10 | 11 | import java.io.IOException; 12 | 13 | import static simpledb.file.Page.BLOCK_SIZE; 14 | 15 | /** 16 | * @ClassName IndexInfo 17 | * @Description TODO 18 | * @Author LiuZhian 19 | * @Date 2020/2/6 12:42 下午 20 | * @Version 1.0 21 | */ 22 | public class IndexInfo { 23 | private String idxName; 24 | private String fieldName; 25 | private Transaction tx; 26 | private TableInfo tableInfo; 27 | private StatInfo statInfo; 28 | 29 | /** 30 | * 创建一个索引 31 | * 32 | * @param idxName 索引名 33 | * @param tblName 被索引的表名 34 | * @param fieldName 被索引的字段名,当前只支持单字段索引 35 | * @param tx 36 | */ 37 | public IndexInfo(String idxName, String tblName, String fieldName, Transaction tx) throws IOException { 38 | this.idxName = idxName; 39 | this.fieldName = fieldName; 40 | this.tx = tx; 41 | tableInfo = SimpleDB.metadataMgr().getTableInfo(tblName, tx); 42 | statInfo = SimpleDB.metadataMgr().getStatInfo(tblName, tx); 43 | } 44 | 45 | /** 46 | * 搜索索引所需访问的块数。 47 | *

48 | * 注意,在SimpleDB中只支持一个字段的索引 49 | * 不像在表记录文件中,索引不需要一个 EMPTY/INUSE flag 50 | * 51 | * @return 52 | */ 53 | public int blocksAccessed() { 54 | // 被索引的记录,每条的长度 55 | int recordLen = tableInfo.recordLength(); 56 | // 一个块可以存储多少条索引 57 | int recordNumPerBlock = BLOCK_SIZE / recordLen; 58 | // 总块数 59 | int numBlocks = statInfo.recordsOutput() / recordNumPerBlock; 60 | 61 | return BTreeIndex.searchCost(numBlocks, recordNumPerBlock); 62 | } 63 | 64 | public int recordsOutput() { 65 | return statInfo.recordsOutput() 66 | / statInfo.distinctValues(fieldName); 67 | } 68 | 69 | public int distinctValues(String fieldName) { 70 | if (fieldName.equals(this.fieldName)) 71 | return 1; 72 | else 73 | return Math.min(statInfo.distinctValues(fieldName), recordsOutput()); 74 | } 75 | 76 | public Index open() throws IOException { 77 | Schema sch = schema(); 78 | return new BTreeIndex(idxName, sch, tx); 79 | } 80 | 81 | /** 82 | * 构造索引的schema,如下: 83 | *

84 | * (block, id, dataval) 85 | * 86 | * @return 87 | */ 88 | private Schema schema() { 89 | Schema sch = new Schema(); 90 | sch.addIntField("block"); 91 | sch.addIntField("id"); 92 | // 索引字段是 int 类型字段 93 | if (tableInfo.schema().type(fieldName) == Schema.INTEGER) 94 | sch.addIntField("dataval"); 95 | // 索引字段是 string 类型字段 96 | else { 97 | int fieldLen = tableInfo.schema().length(fieldName); 98 | sch.addStringField("dataval", fieldLen); 99 | } 100 | return sch; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/buffer/Buffer.java: -------------------------------------------------------------------------------- 1 | package simpledb.buffer; 2 | 3 | import simpledb.file.Block; 4 | import simpledb.file.Page; 5 | import simpledb.server.SimpleDB; 6 | 7 | /** 8 | * @ClassName Buffer 9 | * @Deacription 10 | * // TODO 以后实现了事务模块后,需要再修改事务相关代码 11 | * @Author LiuZhian 12 | * @Date 2020-01-17 19:29 13 | * @Version 1.0 14 | **/ 15 | 16 | public class Buffer { 17 | private Page contents = new Page(); 18 | private Block blk = null; 19 | // 当前缓冲页固定的次数,有点多线程中ReentranLock的意思,一个客户端可以pin多次 20 | private int pins = 0; 21 | // 和事务相关 TODO 22 | private int modifiedBy = -1; // 当前缓冲区对应哪个事务 23 | private int logSequenceNum = -1; // LSN 24 | 25 | public int getInt(int offset) { 26 | return contents.getInt(offset); 27 | } 28 | 29 | public String getString(int offset) { 30 | return contents.getString(offset); 31 | } 32 | 33 | public void setInt(int offset, int val, int txNum, int LSN) { 34 | // 和事务相关 TODO 35 | modifiedBy = txNum; 36 | // LSN的当前实现就是日志文件块的块号 37 | if (LSN >= 0) 38 | logSequenceNum = LSN; 39 | contents.setInt(offset, val); 40 | } 41 | 42 | public void setString(int offset, String val, int txNum, int LSN) { 43 | // 和事务相关 TODO 44 | modifiedBy = txNum; 45 | // LSN的当前实现就是日志文件块的块号 46 | if (LSN >= 0) 47 | logSequenceNum = LSN; 48 | contents.setString(offset, val); 49 | } 50 | 51 | public Block block() { 52 | return blk; 53 | } 54 | 55 | /** 56 | * 将缓冲页中的内容写回到磁盘,且在写回数据到磁盘前,追加一条日志记录 57 | * (write-ahead logging 日志预写技术)。 58 | *

59 | * 此方法有点类似OS中处理内存的脏读位(dirty-read)的行为,当脏读位为1,即发生了时, 60 | * OS会先将内存中的值写回文件块,再执行后续的磁盘文件读入内存的操作。 61 | */ 62 | void flush() { 63 | // 如果有修改页中内容: 64 | // 1. 先flush一下日志记录, 65 | // 2. 再将内存页的内容写回磁盘 66 | if (modifiedBy >= 0) { 67 | SimpleDB.logMgr().flush(logSequenceNum); 68 | contents.write(blk); 69 | } 70 | } 71 | 72 | void pin() { 73 | pins++; 74 | } 75 | 76 | void unpin() { 77 | pins--; 78 | } 79 | 80 | boolean isPinned() { 81 | return pins > 0; 82 | } 83 | 84 | boolean isModifyiedBy(int txNum) { 85 | return txNum == modifiedBy; 86 | } 87 | 88 | /** 89 | * 将指定的块中内容,赋值到缓冲页上。 90 | *

91 | * 注意,在赋值前,要检查下当前页的内容是否被修改过! 92 | * 如果被修改过,必须先在写回磁盘前写一条日志记录,然后再执行相关操作。 93 | *

94 | * 有点类似Buffer类的构造函数。 95 | * 96 | * @param b 待写回的块 97 | */ 98 | void assignToBlock(Block b) { 99 | flush(); 100 | blk = b; 101 | contents.read(blk); 102 | pins = 0; 103 | } 104 | 105 | /** 106 | * 将缓冲页的内容格式化,再追加到文件块 107 | *

108 | * 注意,在追加磁盘块前,也要检查下当前页的内容是否被修改过! 109 | * 如果被修改过,必须先在写回磁盘前写一条日志记录, 110 | * 然后再执行缓冲页的格式化操作,再追加回磁盘块中。 111 | * 112 | * @param fileName 113 | * @param pfm 114 | */ 115 | void assignToNew(String fileName, PageFormatter pfm) { 116 | flush(); 117 | pfm.format(contents); 118 | blk = contents.append(fileName); 119 | pins = 0; 120 | } 121 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/hash/HashIndex.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.hash; 2 | 3 | import simpledb.index.Index; 4 | import simpledb.query.Constant; 5 | import simpledb.query.TableScan; 6 | import simpledb.record.RID; 7 | import simpledb.record.Schema; 8 | import simpledb.record.TableInfo; 9 | import simpledb.tx.Transaction; 10 | 11 | import java.io.IOException; 12 | 13 | /** 14 | * @program simpledb 15 | * @description: TODO 16 | * @author: liuzhian 17 | * @create: 2020/09/09 23:07 18 | */ 19 | public class HashIndex implements Index { 20 | private static final int NUM_BUCKETS = 100; 21 | private Constant searchKey; 22 | private String idxName; // 索引名 23 | private Schema schema; // 索引表对应的schema信息 24 | private Transaction tx; 25 | private TableScan tableScan; // 每个桶对应的tableScan对象 26 | 27 | public HashIndex(String idxName, Schema schema, Transaction tx) { 28 | this.idxName = idxName; 29 | this.schema = schema; 30 | this.tx = tx; 31 | } 32 | 33 | @Override 34 | public void beforeFirst(Constant searchKey) throws IOException { 35 | close(); // 如果有的话,先关闭之前的桶 36 | this.searchKey = searchKey; 37 | int bucket = searchKey.hashCode() % NUM_BUCKETS; // 简单的求模作为hash函数 38 | String specificBucketTableName = idxName + bucket; 39 | TableInfo tableInfo = new TableInfo(specificBucketTableName, schema); 40 | tableScan = new TableScan(tableInfo, tx); 41 | } 42 | 43 | @Override 44 | public boolean next() throws IOException { 45 | while (tableScan.next()) { 46 | if (tableScan.getVal("dataval").equals(searchKey)) 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | @Override 53 | public RID getDataRid() { 54 | // 当前索引记录对应的数据记录的RID 55 | int blockNum = tableScan.getInt("block"); 56 | int recordId = tableScan.getInt("id"); 57 | return new RID(blockNum, recordId); 58 | } 59 | 60 | @Override 61 | public void insert(Constant dataval, RID datarid) throws IOException { 62 | beforeFirst(dataval); 63 | tableScan.insert(); // 找到索引桶文件的插入位置 64 | // 填写索引记录中的dataval 和 datarid 部分取值 65 | tableScan.setVal("dataval", dataval); 66 | tableScan.setInt("block", datarid.blockNumber()); 67 | tableScan.setInt("id", datarid.id()); 68 | 69 | } 70 | 71 | @Override 72 | public void delete(Constant dataval, RID datarid) throws IOException { 73 | beforeFirst(dataval); 74 | while (next()) { 75 | if (getDataRid().equals(datarid)) { 76 | tableScan.delete(); 77 | return; 78 | } 79 | } 80 | } 81 | 82 | @Override 83 | public void close() { 84 | if (tableScan != null) { 85 | tableScan.close(); 86 | } 87 | } 88 | 89 | /** 90 | * @description: 返回遍历索引的搜索代价 91 | * @param num_blocks: 索引的总块数 92 | * @param rbp: 一个块中可以存放多少条索引记录 93 | * @return: int 94 | * @author: liuzhian 95 | * @date: 2020/9/9 96 | */ 97 | public static int searchCost(int num_blocks,int rbp) { 98 | return num_blocks / NUM_BUCKETS; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/server/SimpleDB.java: -------------------------------------------------------------------------------- 1 | package simpledb.server; 2 | 3 | import simpledb.buffer.BufferMgr; 4 | import simpledb.file.Block; 5 | import simpledb.file.FileMgr; 6 | import simpledb.file.Page; 7 | import simpledb.index.planner.IndexUpdatePlanner; 8 | import simpledb.log.LogMgr; 9 | import simpledb.metadata.MetadataMgr; 10 | import simpledb.metadata.TableMgr; 11 | import simpledb.metadata.ViewMgr; 12 | import simpledb.planner.*; 13 | import simpledb.tx.Transaction; 14 | 15 | import java.io.IOException; 16 | 17 | /** 18 | * @ClassName SimpleDB 19 | * @Deacription // TODO 20 | * @Author LiuZhian 21 | * @Date 2020-01-14 15:20 22 | * @Version 1.0 23 | **/ 24 | 25 | public class SimpleDB { 26 | 27 | private static FileMgr fm; 28 | private static LogMgr lm; 29 | private static BufferMgr bm; 30 | private static MetadataMgr mm; 31 | 32 | /** 33 | * 初始化数据库系统 34 | * 35 | * @param dirName 数据库保存的目录名 36 | */ 37 | public static void init(String dirName) throws IOException { 38 | // 初始化文件管理器 39 | initFileMgr(dirName); 40 | // 初始化日志管理器 TODO 41 | initLogMgr("testLog.log"); 42 | // 初始化缓冲管理器 43 | initBufferMgr(10); 44 | boolean isNew = fm.isNew(); 45 | 46 | Transaction tx = new Transaction(); 47 | if (isNew) { 48 | System.out.println("creating a new database"); 49 | } else { 50 | System.out.println("recovering the existing database"); 51 | tx.recover(); 52 | } 53 | // 初始化元数据管理器 54 | initMetadataMgr(isNew, tx); 55 | tx.commit(); 56 | 57 | } 58 | 59 | /** 60 | * 初始化文件管理器 61 | * 62 | * @param dirname 数据库文件夹名 63 | */ 64 | private static void initFileMgr(String dirname) { 65 | fm = new FileMgr(dirname); 66 | } 67 | 68 | /** 69 | * 初始化日志管理器 70 | * 71 | * @param logFileName 日志文件名 72 | */ 73 | private static void initLogMgr(String logFileName) { 74 | try { 75 | lm = new LogMgr(logFileName); 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | 81 | /** 82 | * 初始化缓冲管理器 83 | * 84 | * @param bufferNums 85 | */ 86 | private static void initBufferMgr(int bufferNums) { 87 | bm = new BufferMgr(bufferNums); 88 | } 89 | 90 | /** 91 | * 初始化元数据管理器 92 | */ 93 | private static void initMetadataMgr(boolean isNew, Transaction tx) throws IOException { 94 | mm = new MetadataMgr(isNew, tx); 95 | } 96 | 97 | public static FileMgr fileMgr() { 98 | return fm; 99 | } 100 | 101 | public static LogMgr logMgr() { 102 | return lm; 103 | } 104 | 105 | public static BufferMgr bufferMgr() { 106 | return bm; 107 | } 108 | 109 | public static MetadataMgr metadataMgr() { 110 | return mm; 111 | } 112 | 113 | /** 114 | * 新建planner对象 115 | * 116 | * @return 117 | */ 118 | public static Planner planner() { 119 | QueryPlanner qp = new BasicQueryPlanner(); 120 | UpdatePlanner up = new IndexUpdatePlanner(); 121 | return new Planner(qp, up); 122 | } 123 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/query/Term.java: -------------------------------------------------------------------------------- 1 | package simpledb.query; 2 | 3 | import simpledb.record.Schema; 4 | 5 | /** 6 | * @ClassName Trem 7 | * @Description TODO 8 | * @Author LiuZhian 9 | * @Date 2020/2/18 8:11 下午 10 | * @Version 1.0 11 | */ 12 | public class Term { 13 | private Expression lhs, rhs; 14 | 15 | public Term(Expression lhs, Expression rhs) { 16 | this.lhs = lhs; 17 | this.rhs = rhs; 18 | } 19 | 20 | /** 21 | * 一个谓词项使得记录数减少的因子。 22 | *

23 | * 详情参见图 17-12 24 | * 25 | * @param plan 26 | * @return 27 | */ 28 | public int reductionFactor(Plan plan) { 29 | String lhsName, rhsName; 30 | // 1. 左右两边都是字段 31 | // 图17-12 中的 max{V(s_1,A), V(s_1,B)} 32 | if (lhs.isFieldName() && rhs.isFieldName()) { 33 | lhsName = lhs.asFieldName(); 34 | rhsName = rhs.asFieldName(); 35 | return Math.max(plan.distinctValues(lhsName), 36 | plan.distinctValues(rhsName)); 37 | } 38 | // 2. 左边是字段名,右边是常量 39 | if (lhs.isFieldName()) { 40 | // 图17-12 中的 V(s_1,A) 41 | lhsName = lhs.asFieldName(); 42 | return plan.distinctValues(lhsName); 43 | } 44 | // 3. 左边是常量,右边是字段名 45 | if (rhs.isFieldName()) { 46 | // 图17-12 中的 V(s_1,A) 47 | rhsName = rhs.asFieldName(); 48 | return plan.distinctValues(rhsName); 49 | } 50 | // 4. 左右两边全是常量 51 | if (lhs.asConstant().equals(rhs.asConstant())) 52 | return 1; 53 | else 54 | return Integer.MAX_VALUE; 55 | } 56 | 57 | public Constant equatesWithConstant(String fldName) { 58 | // 左边是字段名,右边是常量 59 | if (lhs.isFieldName() && lhs.asFieldName().equals(fldName) && rhs.isConstant()) 60 | return rhs.asConstant(); 61 | // 左边是常量,右边是字段名 62 | else if (rhs.isFieldName() && rhs.asFieldName().equals(fldName) && lhs.isConstant()) 63 | return lhs.asConstant(); 64 | else 65 | return null; 66 | } 67 | 68 | /** 69 | * 判断当前项是否是形如"A=B"的项, 70 | *

71 | * 其中A是指定的字段名, B是另一个字段名 72 | * 73 | * @param fldName 指定的字段名 74 | * @return 另一个字段名 75 | */ 76 | public String equatesWithField(String fldName) { 77 | // 指定的字段在左边 78 | if (lhs.isFieldName() && 79 | lhs.asFieldName().equals(fldName) && 80 | rhs.isFieldName()) { 81 | return rhs.asFieldName(); 82 | // 指定的字段在左边 83 | } else if (rhs.isFieldName() && 84 | rhs.asFieldName().equals(fldName) && 85 | lhs.isFieldName()) { 86 | return lhs.asFieldName(); 87 | } else 88 | return null; 89 | } 90 | 91 | public boolean appliesTo(Schema schema) { 92 | return lhs.appliesTo(schema) && rhs.appliesTo(schema); 93 | } 94 | 95 | /** 96 | * 判断一个项左右两边是否相等 97 | * 98 | * @param scan 99 | * @return 100 | */ 101 | public boolean isSatisfied(Scan scan) { 102 | Constant lhsVal = lhs.evaluate(scan); 103 | Constant rhsVal = rhs.evaluate(scan); 104 | return lhsVal.equals(rhsVal); 105 | } 106 | 107 | public String toString() { 108 | return lhs.toString() + " = " + rhs.toString(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/metadata/StatMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.metadata; 2 | 3 | import simpledb.record.RecordFile; 4 | import simpledb.record.TableInfo; 5 | import simpledb.server.SimpleDB; 6 | import simpledb.tx.Transaction; 7 | 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * @ClassName StatMgr 14 | * @Description TODO 15 | * @Author LiuZhian 16 | * @Date 2020/2/5 9:36 下午 17 | * @Version 1.0 18 | */ 19 | public class StatMgr { 20 | 21 | // 每执行100次检索表的数据统计元数据后,刷新一次数据库统计信息 22 | public static final int REFRESH_EVERY_CALLS = 100; 23 | private Map tableStats; 24 | private int numCalls; 25 | private TableMgr tableMgr; 26 | 27 | 28 | public StatMgr(TableMgr tableMgr, Transaction tx) throws IOException { 29 | this.tableMgr = tableMgr; 30 | // 新建对象时统计一次 31 | refreshStatistics(tx); 32 | } 33 | 34 | public synchronized StatInfo getStatInfo(String tableName, Transaction tx) throws IOException { 35 | this.numCalls++; 36 | if (numCalls > REFRESH_EVERY_CALLS) 37 | refreshStatistics(tx); 38 | StatInfo statInfo = tableStats.get(tableName); 39 | 40 | // 如果某个客户端在刚好完成上一次所有表的数据统计后,创建了一个新的表 41 | // 而这时所有表的数据统计信息又还没到下一次重新计算的时候,那么可能会暂时查不到结果 42 | 43 | // 这时,我们再显示地执行一次查询指定表的数据统计元数据操作。 44 | if (null == statInfo) { 45 | refreshTableStats(tableName, tx); 46 | statInfo = tableStats.get(tableName); 47 | } 48 | return statInfo; 49 | } 50 | 51 | /** 52 | * 更新所有表的数据统计信息 53 | * 54 | * @param tx 55 | * @throws IOException 56 | */ 57 | public synchronized void refreshStatistics(Transaction tx) throws IOException { 58 | tableStats = new HashMap<>(); 59 | this.numCalls = 0; 60 | // 先获得tblcat表,获取各表的信息 61 | TableInfo tblCatTableInfo = tableMgr.getTableInfo("tblcat", tx); 62 | RecordFile tblCatRecordFile = new RecordFile(tblCatTableInfo, tx); 63 | 64 | // 更新每张表的数据统计信息 65 | // TODO 新建一个RecordFile对象时会自动移到第一块,所以以下语句可以省略 66 | // tblCatRecordFile.beforeFirst(); 67 | // TODO 68 | while (tblCatRecordFile.next()) { 69 | String tblName = tblCatRecordFile.getString("tblname"); 70 | // 更新具体的某张表的统计信息 71 | refreshTableStats(tblName, tx); 72 | } 73 | tblCatRecordFile.close(); 74 | } 75 | 76 | /** 77 | * 更新指定表的数据统计信息 78 | * 79 | * @param tblName 80 | * @param tx 81 | * @throws IOException 82 | */ 83 | private synchronized void refreshTableStats(String tblName, Transaction tx) throws IOException { 84 | int numRecords = 0; 85 | TableInfo tableInfo = tableMgr.getTableInfo(tblName, tx); 86 | RecordFile recordFile = new RecordFile(tableInfo, tx); 87 | // TODO 新建一个RecordFile对象时会自动移到第一块,所以以下语句可以省略 88 | recordFile.beforeFirst(); 89 | // TODO 90 | while (recordFile.next()) { 91 | numRecords++; 92 | } 93 | // 这个时候,recordFile肯定遍历到了表记录文件的最后一块, 94 | // 因此可以求得总块号(块号是从0开始的) 95 | int numBlocks = recordFile.currentRID().blockNumber() + 1; 96 | recordFile.close(); 97 | 98 | // 新建出表的数据统计信息,即块数+ 记录条数 99 | StatInfo statInfo = new StatInfo(numBlocks, numRecords); 100 | this.tableStats.put(tblName, statInfo); 101 | 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/file/FileMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.file; 2 | 3 | 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.RandomAccessFile; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.FileChannel; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * @ClassName FileMgr 14 | * @Deacription // TODO 15 | * @Author LiuZhian 16 | * @Date 2020-01-14 14:51 17 | * @Version 1.0 18 | **/ 19 | 20 | public class FileMgr { 21 | private File dbDirectory; 22 | private boolean isNew; 23 | private Map openFiles = new HashMap<>(); 24 | 25 | public FileMgr(String dbname) { 26 | // 默认路径为user的home路径 27 | String homedir = System.getProperty("user.home"); 28 | dbDirectory = new File(homedir, dbname); 29 | isNew = !dbDirectory.exists(); 30 | 31 | // 如果是新的数据库,则创建 32 | if (isNew && !dbDirectory.mkdir()) 33 | throw new RuntimeException("Cannot create " + dbname); 34 | // 删除临时表文件 35 | for (String filename : dbDirectory.list()) { 36 | if (filename.startsWith("temp")) 37 | new File(dbDirectory, filename).delete(); 38 | } 39 | 40 | } 41 | 42 | public boolean isNew() { 43 | return isNew; 44 | } 45 | 46 | /** 47 | * 返回当前文件的逻辑块数目 48 | * 49 | * @param fileName 文件名 50 | * @return 逻辑块数 51 | * @throws IOException 52 | */ 53 | public int size(String fileName) throws IOException { 54 | FileChannel fc = getFile(fileName); 55 | int sz = (int) fc.size() / Page.BLOCK_SIZE; 56 | 57 | return sz; 58 | } 59 | 60 | private synchronized FileChannel getFile(String fileName) throws IOException { 61 | FileChannel fc = openFiles.get(fileName); 62 | // 如果map中没打开过 63 | if (fc == null) { 64 | File dbTable = new File(dbDirectory, fileName); 65 | RandomAccessFile f = new RandomAccessFile(dbTable, "rws"); 66 | fc = f.getChannel(); 67 | openFiles.put(fileName, fc); 68 | } 69 | 70 | return fc; 71 | } 72 | 73 | synchronized void read(Block block, ByteBuffer buffer) { 74 | try { 75 | buffer.clear(); 76 | FileChannel fc = getFile(block.filename()); 77 | fc.read(buffer, block.number() * buffer.capacity()); 78 | } catch (IOException e) { 79 | throw new RuntimeException("Cannot read block " + block); 80 | } 81 | 82 | } 83 | 84 | synchronized void write(Block blk, ByteBuffer buffer) { 85 | try { 86 | buffer.rewind(); 87 | FileChannel fc = getFile(blk.filename()); 88 | fc.write(buffer, blk.number() * buffer.capacity()); 89 | } catch (IOException e) { 90 | throw new RuntimeException("Cannot write block " + blk); 91 | } 92 | } 93 | 94 | /** 95 | * 追加一个新块到文件尾部,并将缓冲区的内容写入 96 | * 97 | * @param filename 待追加的文件名 98 | * @param buffer 缓冲区,其中保存了要追加的内容 99 | * @return 新建的块 100 | */ 101 | synchronized Block append(String filename, ByteBuffer buffer) { 102 | try { 103 | int newBlkNum = size(filename); 104 | Block newBlk = new Block(filename, newBlkNum); 105 | write(newBlk, buffer); 106 | return newBlk; 107 | } catch (IOException e) { 108 | throw new RuntimeException("Cannot append block to file " + filename); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/concurrency/LockTable.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx.concurrency; 2 | 3 | import simpledb.file.Block; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @ClassName LockTable 10 | * @Deacription // TODO 11 | * @Author LiuZhian 12 | * @Date 2020-01-25 17:21 13 | * @Version 1.0 14 | **/ 15 | 16 | public class LockTable { 17 | 18 | private static final long MAX_TIME = 10000; // 10 s 19 | private Map locks = new HashMap<>(); 20 | 21 | /** 22 | * 请求持有指定块的 共享锁 23 | * 24 | * @param blk 指定的块 25 | */ 26 | public synchronized void sLock(Block blk) { 27 | try { 28 | long timestamp = System.currentTimeMillis(); 29 | // 当该块的互斥锁已经被持有时,线程等待 30 | while (hasXLock(blk) && !waitTooLong(timestamp)) 31 | // this.wait(),等待的对象是当前这个锁表, 32 | // 等待的目标是该块的互斥锁被释放 33 | wait(MAX_TIME); 34 | 35 | // 死锁或其他原因导致 等待超时 36 | if (hasXLock(blk)) 37 | throw new LockAbortException(); 38 | // 这个val肯定是个非负的值 39 | // 1. 如果这个块之前没有访问过,即lockVal=0 40 | // 2. 如果这个块的共享锁已经被持有,则lockVal > 0 41 | int val = getLockVal(blk); 42 | locks.put(blk, val + 1); 43 | } catch (InterruptedException e) { 44 | throw new LockAbortException(); 45 | } 46 | } 47 | 48 | /** 49 | * 请求持有指定块的 互斥锁 50 | *

51 | * 我们假定事务已经获取互斥锁前,都会获得该块的 共享锁。(即locks对应 blk的entry值至少为1) 52 | * 53 | * @param blk 指定的块 54 | */ 55 | public synchronized void xLock(Block blk) { 56 | try { 57 | long timestamp = System.currentTimeMillis(); 58 | // 当该块的共享锁已经被其他事务持有时,线程等待 59 | while (hasOtherSLocks(blk) && !waitTooLong(timestamp)) 60 | // this.wait(),等待的对象是当前这个锁表, 61 | // 等待的目标是该块的共享锁被释放 62 | wait(MAX_TIME); 63 | 64 | // 死锁或其他原因导致等待超时 65 | if (hasOtherSLocks(blk)) 66 | throw new LockAbortException(); 67 | 68 | locks.put(blk, -1); // 获得互斥锁,把锁表置为-1 69 | } catch (InterruptedException e) { 70 | throw new LockAbortException(); 71 | } 72 | } 73 | 74 | 75 | public synchronized void unLock(Block blk) { 76 | int val = getLockVal(blk); 77 | if (val > 1) // 多于一个客户端持有块的共享锁 78 | locks.put(blk, val - 1); 79 | else { // 某客户端拥有该块的共享锁 或 互斥锁 80 | locks.remove(blk); 81 | // 该块变得空闲,通知所有等待线程竞争 82 | notifyAll(); 83 | } 84 | } 85 | 86 | /** 87 | * 判断指定块的互斥锁是否已经被占用。 88 | *

89 | * getLockVal(block) 为 -1 时表示互斥锁被占用。 90 | * 91 | * @param block 92 | * @return 93 | */ 94 | private boolean hasXLock(Block block) { 95 | return getLockVal(block) < 0; 96 | } 97 | 98 | 99 | /** 100 | * 判断指定块的共享锁是否已经被持有。 101 | *

102 | * getLockVal(block) 为 被持有的次数。 103 | * 104 | * @param block 105 | * @return 106 | */ 107 | private boolean hasOtherSLocks(Block block) { 108 | return getLockVal(block) > 1; 109 | } 110 | 111 | private int getLockVal(Block block) { 112 | Integer val = locks.get(block); 113 | if (null == val) 114 | return 0; 115 | return val; 116 | } 117 | 118 | private boolean waitTooLong(long startTime) { 119 | return System.currentTimeMillis() - startTime > MAX_TIME; 120 | } 121 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/remote/MetaDataAdapter.java: -------------------------------------------------------------------------------- 1 | package simpledb.remote; 2 | 3 | import java.sql.ResultSetMetaData; 4 | import java.sql.SQLException; 5 | 6 | public class MetaDataAdapter implements ResultSetMetaData { 7 | public String getCatalogName(int column) throws SQLException { 8 | throw new SQLException("operation not implemented"); 9 | } 10 | 11 | public String getColumnClassName(int column) throws SQLException { 12 | throw new SQLException("operation not implemented"); 13 | } 14 | 15 | public int getColumnCount() throws SQLException { 16 | throw new SQLException("operation not implemented"); 17 | } 18 | 19 | public int getColumnDisplaySize(int column) throws SQLException { 20 | throw new SQLException("operation not implemented"); 21 | } 22 | 23 | public String getColumnLabel(int column) throws SQLException { 24 | throw new SQLException("operation not implemented"); 25 | } 26 | 27 | public String getColumnName(int column) throws SQLException { 28 | throw new SQLException("operation not implemented"); 29 | } 30 | 31 | public int getColumnType(int column) throws SQLException { 32 | throw new SQLException("operation not implemented"); 33 | } 34 | 35 | public String getColumnTypeName(int column) throws SQLException { 36 | throw new SQLException("operation not implemented"); 37 | } 38 | 39 | public int getPrecision(int column) throws SQLException { 40 | throw new SQLException("operation not implemented"); 41 | } 42 | 43 | public int getScale(int column) throws SQLException { 44 | throw new SQLException("operation not implemented"); 45 | } 46 | 47 | public String getSchemaName(int column) throws SQLException { 48 | throw new SQLException("operation not implemented"); 49 | } 50 | 51 | public String getTableName(int column) throws SQLException { 52 | throw new SQLException("operation not implemented"); 53 | } 54 | 55 | public boolean isAutoIncrement(int column) throws SQLException { 56 | throw new SQLException("operation not implemented"); 57 | } 58 | 59 | public boolean isCaseSensitive(int column) throws SQLException { 60 | throw new SQLException("operation not implemented"); 61 | } 62 | 63 | public boolean isCurrency(int column) throws SQLException { 64 | throw new SQLException("operation not implemented"); 65 | } 66 | 67 | public boolean isDefinitelyWritable(int column) throws SQLException { 68 | throw new SQLException("operation not implemented"); 69 | } 70 | 71 | public int isNullable(int column) throws SQLException { 72 | throw new SQLException("operation not implemented"); 73 | } 74 | 75 | public boolean isReadOnly(int column) throws SQLException { 76 | throw new SQLException("operation not implemented"); 77 | } 78 | 79 | public boolean isSearchable(int column) throws SQLException { 80 | throw new SQLException("operation not implemented"); 81 | } 82 | 83 | public boolean isSigned(int column) throws SQLException { 84 | throw new SQLException("operation not implemented"); 85 | } 86 | 87 | public boolean isWritable(int column) throws SQLException { 88 | throw new SQLException("operation not implemented"); 89 | } 90 | 91 | public boolean isWrapperFor(Class iface) throws SQLException { 92 | throw new SQLException("operation not implemented"); 93 | } 94 | 95 | public T unwrap(Class iface) throws SQLException { 96 | throw new SQLException("operation not implemented"); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/IndexUpdateTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.index; 2 | 3 | import simpledb.index.query.IndexSelectPlan; 4 | import simpledb.index.query.IndexSelectScan; 5 | import simpledb.metadata.IndexInfo; 6 | import simpledb.metadata.MetadataMgr; 7 | import simpledb.planner.Planner; 8 | import simpledb.query.*; 9 | import simpledb.record.RID; 10 | import simpledb.server.SimpleDB; 11 | import simpledb.tx.Transaction; 12 | 13 | import java.io.IOException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | public class IndexUpdateTest { 18 | 19 | public static void main(String[] args) throws IOException { 20 | SimpleDB.init("studentDB"); 21 | Transaction tx = new Transaction(); 22 | 23 | // create a table STUDENT(sname, majorid) and a index on STUDENT(majorid) 24 | Planner planner = SimpleDB.planner(); 25 | String str = "create table student (sname varchar(5), majorid int)"; 26 | planner.executeUpdate(str, tx); 27 | String createIndexStr = "create index stuMajorIdIndex on student(majorid)"; 28 | planner.executeUpdate(createIndexStr, tx); 29 | 30 | // open a scan to table 31 | Plan stuPlan = new TablePlan("student", tx); 32 | TableScan stuScan = (TableScan) stuPlan.open(); 33 | 34 | // open the index on MajorId 35 | MetadataMgr mdMgr = SimpleDB.metadataMgr(); 36 | Map indexInfos = mdMgr.getIndexInfo("student", tx); 37 | Map indexes = new HashMap<>(); 38 | for (String fldName : indexInfos.keySet()) { 39 | Index idx = indexInfos.get(fldName).open(); 40 | indexes.put(fldName, idx); 41 | } 42 | 43 | // Task 1: insert a two STUDENT record 44 | // insert the data record first 45 | stuScan.insert(); 46 | stuScan.setString("sname", "Sam"); 47 | stuScan.setInt("majorid", 10); 48 | // then insert a corresponding index record 49 | RID datarid = stuScan.getRID(); 50 | for (String fldName : indexes.keySet()) { 51 | Constant dataval = stuScan.getVal(fldName); 52 | Index idx = indexes.get(fldName); 53 | idx.insert(dataval, datarid); 54 | } 55 | 56 | // insert the data record first 57 | stuScan.insert(); 58 | stuScan.setString("sname", "Andy"); 59 | stuScan.setInt("majorid", 10); 60 | // then insert a corresponding index record 61 | for (String fldName : indexes.keySet()) { 62 | Constant dataval = stuScan.getVal(fldName); 63 | Index idx = indexes.get(fldName); 64 | idx.insert(dataval, stuScan.getRID()); 65 | } 66 | 67 | // Task 2: find and delete Sam's record 68 | stuScan.beforeFirst(); 69 | while (stuScan.next()) { 70 | if (stuScan.getString("sname").equals("Sam")) { 71 | // delete the corresponding index record(s) first 72 | RID rid = stuScan.getRID(); 73 | for (String idxFldName : indexes.keySet()) { 74 | Constant dataval = stuScan.getVal(idxFldName); 75 | Index idx = indexes.get(idxFldName); 76 | idx.delete(dataval, rid); 77 | } 78 | 79 | // then delete the data record 80 | stuScan.delete(); 81 | break; // 只删了一条名为Sam的数据,也只有一条 82 | } 83 | } 84 | 85 | // close the resources 86 | stuScan.close(); 87 | for (Index idx : indexes.values()) { 88 | idx.close(); 89 | } 90 | tx.commit(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 1. Overview 2 | This is a simple relational database(named SimpleDB) implementation in Java. And this project is a playground-level database implementation described in the book "Database Design And Implementation" written by Edward Sciore, Boston College. 3 | 4 | Just for educational use, I will follow the author to reimplemente the SimpleDB. I have commented some code in Chinese for convenient reading and translated mainly chapters of part 3 in Chinese. You are also highly recommended to take a look at the original textbook which is really easy to understand. 5 | 6 | 这是一个简单的关系数据库(名为simpleDB)实现,Java语言实现,这是《Database Design And Implementation》作者Edward Sciore在书中提供的一个游乐场级别的数据库实现。 为了方便学习使用,我会跟随作者的脚步去再次实现这个数据库。为方便大家阅读代码,我已经将部分关键代码添加了中文注释,并且尝试翻译了原书第3部分中的绝大部分章节,但仍然建议配合原书一起使用,因为相较于中文,英文没有那么地晦涩并且歧义更少,理解起来更简单。 7 | 8 | ## 2. 架构 9 | 10 | SimpleDB的整体架构如下图所示,下层组件为上层组件提供服务: 11 | 12 | ![](part3/part3-01.png) 13 | 14 | ## 3. 特点 15 | 16 | ### 3.1 磁盘和文件管理 17 | - 将文件块作为磁盘访问的最基本单元,缩短磁盘访问时间。 18 | - 文件块中当前只支持int和string类型的读写。 19 | 20 | ### 3.2 内存管理 21 | - 通过维护缓冲池来固定最常使用的用户数据块,目前支持Naive缓冲页替换算法。 22 | 23 | ### 3.3 事务管理 24 | - 支持日志来恢复数据库,采用的是undo-only恢复算法。 25 | - 运用xlock和slock来控制多事务对块的并发访问。 26 | - 事务对锁一直持有,直到事务commit或rollback。 27 | 28 | ### 3.4 记录管理 29 | - 当前支持定长字段,定长记录。 30 | - 一个记录文件中存储的是同类(homogeneous)的记录。 31 | - 当前支持非跨块的记录(non spanned records)。 32 | - 给客户端提供记录文件粒度的记录增、删、查、改方法,隐藏了底层的块、页细节。 33 | 34 | ### 3.5 元数据管理 35 | - SimpleDB实现了4类元数据: 36 | 1. 表的元数据,描述的是某张表的信息,例如该表每条记录的长度,每个字段的偏移量,类型。 37 | 2. 视图的元数据,描述的是每个视图的属性,例如视图的名称和定义。 38 | 3. 索引的元数据,描述的是每个索引的信息,包含索引名、被索引的表名、被索引的字段名。 39 | 4. 数据统计的元数据,描述的是每张表的占用块数,已经各字段的值分布情况。 40 | - 表的元数据存放在系统的catalog表tblcat和fldcat中,一张来存放表粒度的信息(例如记录长度),另一种表来存放表中各字段信息(例如字段名、字段长度、字段的类型)。表名和字段名最长字符限定为20。 41 | - 视图的元数据存放在系统的catalog表viewcat中,视图的定义最长字符数我们限定成了150。 42 | - 索引的元数据存放在系统的catalog表idxcay中,包含索引名、被索引的表名、被索引的字段名。 43 | - 数据统计信息没有用表来存,而是在系统每次启动的时候重新计算出,对于小型的数据库,这一统计计算时间不是很长,因此不会拖长整个系统的启动时间。 44 | 45 | ### 3.6 查询处理 46 | - 基于流水线的方式实现Scan。目前支持TableScan、SelectScan、ProductScan和ProjectScan,分别对应SQL语句中的表、谓词筛选、笛卡尔积和输出Select列名。 47 | - 对于一个SQL,可能存在多个等效的查询树,planner会比较这些查询树对应的Scan的执行代价,选择代价最低的那个。 48 | - 目前支持形如"A = c"和"A = B"形式的谓词,前者代表例如`where StuId=1`的形式,而后者代表例如`where Stu.StuId = Exam.Id`的形式。 49 | 50 | ### 3.7 SQL解析 51 | - SimpleDB中只支持SQL中的子集,具体有: 52 | 1. 简单的查询,单表多表均支持 53 | 2. 增删改记录 54 | 3. 创建表、视图、索引 55 | 4. 只支持where谓词,不支持group by等 56 | - 采用递归下降解析法(recursive descent)解析SQL语句。 57 | 58 | ### 3.8 SQL Planning 59 | - 目前实现了最简单的planning算法(包括query planning算法和update planning算法),没有作SQL语义验证和plan代价分析。 60 | - 设计了好了Planner的接口,增强代码的plug-and-play capability。 61 | 62 | ### 3.9 C/S 通信 63 | 64 | - 通过Java中的RMI机制来实现客户端和服务端之间的通信。 65 | - 服务端的每个远程实现类对象都在一个独自的线程中执行,等待客户端通过存根对象发送消息,SimpleDB启动代码会创建一个`RemoteDriver`类型的远程实现对象,并把这个对象的存根对象注册到RMI的注册表中。 66 | - 当客户端想要连接到数据库系统时,会先通过RMI注册表得到存根对象,并按照JDBC提供接口进行数据访问,目前实现了`SimpleConnection`,`SimpleStatement`等,分别对应JDBC中的`Connection`,`Statement`。 67 | 68 | ### 3.10 索引 69 | 70 | - 实现了`静态hash索引`和`B树索引`两种方式 71 | 72 | ___ 73 | 74 | ### Q&A 75 | #### 1. 本书的面向的读者? 76 | 本书的面向的读者是那些想要学习或研究数据库原理和底层实现的人,你最好对数据库的基本概念有个大概的了解,例如基本的SQL语句、数据库设计的几种范数、数据库的ACID原则等等,细节不清楚没关系,这本书就是给你展示细节的。本书假设你对操作系统、编译原理中的一些基本概念略有耳闻,例如磁盘访问、并发、多线程等等。 77 | 78 | #### 2. 关于错误 79 | 本书是译者在阅读英文版原书时翻译的手稿或者说笔记,难免会有很多错误,必然存在我的理解错误、口误、输入错误、翻译不准确等众多问题,如果你有任何问题,请先仔细思考或查阅相关资料,如果你的确认为有问题,非常欢迎提出issue讨论交流。 80 | 81 | #### 3. 译本进度 82 | 目前只翻译了第12—21章,由于本人时间有限,因此会不定时地更新本译本,欢迎任何人共同加入到翻译此书的工作中来,如果你有意向,email联系,也欢迎直接pull request。 83 | 84 | #### 4. 如何获得本书? 85 | 1. 你可以直接打开[SimpleDB中文版gitbook](https://929910266.gitbook.io/simpledb/)查看。 86 | 2. 你也可以在本地安装nodejs和gitbook,并且编译成PDF文件格式查看。 87 | 88 | ***本仓库所有译本只是本人学习时所翻译,默认可以转载,但请注明出处,也请勿用于任何商业用途。*** 89 | 90 | 译者:Liu Zhian 91 | 92 | email:csliuzhian@mail.scut.edu.cn 93 | 94 | ___ 95 | ### 打赏 96 | 你的支持将会是我最大的动力!打赏记得备注哟~ ¡Salud :beers: :beers: :beers: ​ 97 | 98 | ![](myQRcode.png) -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/Transaction.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx; 2 | 3 | import simpledb.buffer.Buffer; 4 | import simpledb.buffer.PageFormatter; 5 | import simpledb.file.Block; 6 | import simpledb.server.SimpleDB; 7 | import simpledb.tx.concurrency.ConcurrencyMgr; 8 | import simpledb.tx.recovery.RecoveryMgr; 9 | 10 | import java.awt.print.PageFormat; 11 | import java.io.IOException; 12 | 13 | /** 14 | * @ClassName Transaction 15 | * @Deacription // TODO 16 | * @Author LiuZhian 17 | * @Date 2020-01-19 19:19 18 | * @Version 1.0 19 | **/ 20 | 21 | public class Transaction { 22 | private static int nextTxNum = 0; 23 | private RecoveryMgr recoveryMgr; 24 | private ConcurrencyMgr concurMgr; 25 | private int txNum; 26 | private BufferList myBuffers = new BufferList(); 27 | 28 | public Transaction() { 29 | txNum = nextTxNumber(); 30 | recoveryMgr = new RecoveryMgr(txNum); 31 | concurMgr = new ConcurrencyMgr(); 32 | } 33 | 34 | public int getTxNum() { 35 | return txNum; 36 | } 37 | 38 | public void commit() { 39 | recoveryMgr.commit(); 40 | System.out.println("transaction " + txNum + " committed"); 41 | myBuffers.unpinAll(); 42 | concurMgr.release(); 43 | } 44 | 45 | public void rollback() { 46 | recoveryMgr.rollback(); 47 | System.out.println("transaction " + txNum + " rolled back"); 48 | myBuffers.unpinAll(); 49 | concurMgr.release(); 50 | } 51 | 52 | public void recover() { 53 | SimpleDB.bufferMgr().flushAll(txNum); 54 | recoveryMgr.recover(); 55 | } 56 | 57 | public void pin(Block blk) { 58 | myBuffers.pin(blk); 59 | } 60 | 61 | public void unpin(Block blk) { 62 | myBuffers.unpin(blk); 63 | } 64 | 65 | public int getInt(Block blk, int offset) { 66 | concurMgr.sLock(blk); 67 | // 客户端自己负责在调用getInt()方法前固定指定块 68 | Buffer buff = myBuffers.getBuffer(blk); 69 | return buff.getInt(offset); 70 | } 71 | 72 | public String getString(Block blk, int offset) { 73 | concurMgr.sLock(blk); 74 | Buffer buff = myBuffers.getBuffer(blk); 75 | return buff.getString(offset); 76 | } 77 | 78 | public void setInt(Block blk, int offset, int val) { 79 | concurMgr.xLock(blk); 80 | Buffer buff = myBuffers.getBuffer(blk); 81 | // 返回追加一条日志记录后的LSN 82 | int lsn = recoveryMgr.setInt(buff, offset, val); 83 | buff.setInt(offset, val, txNum, lsn); 84 | } 85 | 86 | public void setString(Block blk, int offset, String val) { 87 | concurMgr.xLock(blk); 88 | Buffer buff = myBuffers.getBuffer(blk); 89 | // 返回追加一条日志记录后的LSN 90 | int lsn = recoveryMgr.setString(buff, offset, val); 91 | buff.setString(offset, val, txNum, lsn); 92 | } 93 | 94 | /** 95 | * 获取文件的大小,即块的数量 96 | * 97 | * @param fileName 指定文件名 98 | * @return 块数 99 | * @throws IOException 100 | */ 101 | public int size(String fileName) throws IOException { 102 | // 模拟的文件EOF 103 | Block dummyBlk = new Block(fileName, -1); 104 | concurMgr.sLock(dummyBlk); 105 | return SimpleDB.fileMgr().size(fileName); 106 | } 107 | 108 | public Block append(String fileName, PageFormatter pfmt) { 109 | // 模拟的文件EOF 110 | Block dummyBlk = new Block(fileName, -1); 111 | concurMgr.xLock(dummyBlk); 112 | 113 | Block blk = myBuffers.pinNew(fileName, pfmt); 114 | unpin(blk); 115 | return blk; 116 | } 117 | 118 | private static synchronized int nextTxNumber() { 119 | nextTxNum++; 120 | System.out.println("new transaction: " + nextTxNum); 121 | return nextTxNum; 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/parser/Lexer.java: -------------------------------------------------------------------------------- 1 | package simpledb.parser; 2 | 3 | import java.io.IOException; 4 | import java.io.StreamTokenizer; 5 | import java.io.StringReader; 6 | import java.util.Arrays; 7 | import java.util.Collection; 8 | 9 | /** 10 | * @ClassName Lexer 11 | * @Deacription // TODO 12 | * @Author LiuZhian 13 | * @Date 2020-02-19 22:47 14 | * @Version 1.0 15 | **/ 16 | 17 | public class Lexer { 18 | private Collection keywords; 19 | private StreamTokenizer tokenizer; 20 | 21 | public Lexer(String s) { 22 | // 初始化关键字 23 | initKeywords(); 24 | tokenizer = new StreamTokenizer(new StringReader(s)); 25 | // 把'.'也视为一个字符,主要是为了到时候SQL中形如 stuTable.name的字段时, 26 | // 识别成stuTable,'.'和name三个token,而不是视stuTable.name为一整个token 27 | tokenizer.ordinaryChar('.'); 28 | // 使关键字和标识符大小写不敏感 29 | tokenizer.lowerCaseMode(true); 30 | nextToken(); 31 | } 32 | 33 | // =============判断token类型的相关方法============== 34 | 35 | /** 36 | * 判断当前token是否是指定的分隔符。 37 | *

38 | * 在StreamTokenizer中,单字符token的ttype就是字符对应的ASCII码。 39 | * 40 | * @param ch 41 | * @return 42 | */ 43 | public boolean matchDelim(char ch) { 44 | return ch == (char) tokenizer.ttype; 45 | } 46 | 47 | public boolean matchIntConstant() { 48 | return tokenizer.ttype == StreamTokenizer.TT_NUMBER; 49 | } 50 | 51 | /** 52 | * 匹配String的单引号。 53 | *

54 | * 注意,在SimpleDB中,字符串常量用单引号包围。 55 | * 56 | * @return 57 | */ 58 | public boolean matchStringConstant() { 59 | return '\'' == (char) tokenizer.ttype; 60 | } 61 | 62 | public boolean matchKeyword(String w) { 63 | return tokenizer.ttype == StreamTokenizer.TT_WORD && 64 | tokenizer.sval.equals(w); 65 | } 66 | 67 | /** 68 | * 判断是否是标识符 69 | *

70 | * 除了关键字以外的word都视为标识符。 71 | * 72 | * @return 73 | */ 74 | public boolean matchIdentifier() { 75 | return tokenizer.ttype == StreamTokenizer.TT_WORD && 76 | !keywords.contains(tokenizer.sval); 77 | } 78 | 79 | // =============词法分析器不断“吃掉”当前token的相关方法============== 80 | public void eatDelim(char ch) { 81 | if (!matchDelim(ch)) 82 | throw new BadSyntaxException(); 83 | nextToken(); 84 | } 85 | 86 | public int eatIntConstant() { 87 | if (!matchIntConstant()) 88 | throw new BadSyntaxException(); 89 | int i = (int) tokenizer.nval; 90 | nextToken(); 91 | return i; 92 | } 93 | 94 | public String eatStringConstant() { 95 | if (!matchStringConstant()) 96 | throw new BadSyntaxException(); 97 | String str = tokenizer.sval; 98 | nextToken(); 99 | return str; 100 | } 101 | 102 | public void eatKeyword(String w) { 103 | if (!matchKeyword(w)) 104 | throw new BadSyntaxException(); 105 | nextToken(); 106 | } 107 | 108 | public String eatIdentifier() { 109 | if (!matchIdentifier()) 110 | throw new BadSyntaxException(); 111 | String str = tokenizer.sval; 112 | nextToken(); 113 | return str; 114 | } 115 | 116 | /** 117 | * 得到下一个token 118 | */ 119 | private void nextToken() { 120 | try { 121 | tokenizer.nextToken(); 122 | } catch (IOException e) { 123 | throw new BadSyntaxException(); 124 | } 125 | } 126 | 127 | private void initKeywords() { 128 | keywords = Arrays.asList("select", "from", "where", "and", 129 | "insert", "into", "values", "delete", 130 | "update", "set", "create", "table", 131 | "varchar", "int", "view", "as", "index", "on"); 132 | } 133 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/buffer/BasicBufferMgr.java: -------------------------------------------------------------------------------- 1 | package simpledb.buffer; 2 | 3 | import simpledb.file.Block; 4 | 5 | /** 6 | * @ClassName BasicBufferMgr 7 | * @Deacription // TODO 8 | * @Author LiuZhian 9 | * @Date 2020-01-17 22:03 10 | * @Version 1.0 11 | **/ 12 | 13 | public class BasicBufferMgr { 14 | 15 | private Buffer[] bufferPool; // 缓冲池 16 | private int numAvailable; // 空闲缓冲区数量 17 | 18 | public BasicBufferMgr(int numBuffers) { 19 | this.numAvailable = numBuffers; 20 | bufferPool = new Buffer[numAvailable]; 21 | for (int i = 0; i < numBuffers; i++) { 22 | bufferPool[i] = new Buffer(); 23 | } 24 | } 25 | 26 | synchronized void flushAll(int txNum) { 27 | for (Buffer buffer : bufferPool) { 28 | if (buffer.isModifyiedBy(txNum)) { 29 | buffer.flush(); 30 | } 31 | } 32 | } 33 | 34 | /** 35 | * 固定一个块的内容到一个缓冲区中 36 | * 37 | * @param blk 待固定的块 38 | * @return 固定成功的缓冲区对象 或 null(表示需要等待) 39 | */ 40 | synchronized Buffer pin(Block blk) { 41 | Buffer buff = findExistingBuffer(blk); 42 | // 1. 如果没有缓冲区的内容就是待关联的块,则找一个没被固定的缓冲区 43 | if (null == buff) { 44 | // TODO 怎么选取一个空闲的缓冲区可以用不同的策略 45 | buff = chooseUnpinnedBuffer(); 46 | // 1.1 如果不存在没被固定的缓冲区,则返回null 47 | if (null == buff) 48 | return null; 49 | // 1.2 找到了一个没被固定的块,则将块的值赋上 50 | buff.assignToBlock(blk); 51 | } 52 | // 2. 如果存在一个缓冲区的内容就是待关联的块,此时有2种情况: 53 | // 2.1 该缓冲区已经被固定,即 pins > 0,有可能是当前客户端之前固定过该块,或者是其他客户端固定过该块, 54 | // 在这里,我们并不关心是被缓冲区是被哪个客户端pin的。 55 | // 2.2 如果该缓冲区没被固定,即该缓冲区上的block是新替换的,即 pins == 0 56 | if (!buff.isPinned()) 57 | numAvailable--; 58 | // pins++ 59 | buff.pin(); 60 | 61 | return buff; 62 | } 63 | 64 | /** 65 | * 以指定的格式化器,格式化缓冲区中页的内容,并将内容追加一个新块到文件末尾。 66 | * 67 | * @param fileName 文件名 68 | * @param pfmt 指定的格式化器 69 | * @return 固定成功的缓冲区对象 或 null(表示需要等待) 70 | */ 71 | synchronized Buffer pinNew(String fileName, PageFormatter pfmt) { 72 | Buffer buff = chooseUnpinnedBuffer(); 73 | if (null == buff) 74 | return null; 75 | buff.assignToNew(fileName, pfmt); 76 | numAvailable--; 77 | buff.pin(); 78 | 79 | return buff; 80 | } 81 | 82 | /** 83 | * 减少一个缓冲区的固定次数 84 | *

85 | * 注意,减少一次后不一定这个页就“自由了” 86 | * 87 | * @param buff 待取消固定的缓冲区 88 | */ 89 | synchronized void unpin(Buffer buff) { 90 | buff.unpin(); 91 | if (!buff.isPinned()) 92 | numAvailable++; 93 | 94 | } 95 | 96 | /** 97 | * 得到“自由的”缓冲区数量 98 | * 99 | * @return int 100 | */ 101 | int available() { 102 | return numAvailable; 103 | } 104 | 105 | /** 106 | * 查找是否存在一个缓冲区,其内容块引用与待查找的一致。 107 | * 108 | * @param block 待查找的块引用 109 | * @return 如果存在,就返回那个缓冲区;否则返回null 110 | */ 111 | private Buffer findExistingBuffer(Block block) { 112 | for (Buffer buff : bufferPool) { 113 | Block blkInBuffer = buff.block(); 114 | if (blkInBuffer != null && blkInBuffer.equals(block)) { 115 | return buff; 116 | } 117 | } 118 | return null; 119 | } 120 | 121 | /** 122 | * 在缓冲池中找一个没被固定的页 123 | *

124 | * TODO: 当前用的最简单的Naive算法,找到一个就OK,后期考虑其他策略。 125 | * 126 | * @return 如果存在,就返回那个缓冲区;否则返回null 127 | */ 128 | private Buffer chooseUnpinnedBuffer() { 129 | for (Buffer buff : bufferPool) { 130 | if (!buff.isPinned()) { 131 | return buff; 132 | } 133 | } 134 | return null; 135 | } 136 | } -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/btree/BTreeDir.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.btree; 2 | 3 | import simpledb.file.Block; 4 | import simpledb.query.Constant; 5 | import simpledb.record.TableInfo; 6 | import simpledb.tx.Transaction; 7 | 8 | /** 9 | * @program simpledb 10 | * @description: TODO 11 | * @author: liuzhian 12 | * @create: 2020/09/27 11:25 13 | */ 14 | public class BTreeDir { 15 | private TableInfo tableInfo; 16 | private Transaction tx; 17 | private String fileName; 18 | private BTreePage contents; 19 | 20 | public BTreeDir(Block block, TableInfo tableInfo, Transaction tx) { 21 | this.tableInfo = tableInfo; 22 | this.tx = tx; 23 | this.fileName = block.filename(); 24 | this.contents = new BTreePage(block, tableInfo, tx); 25 | } 26 | 27 | public void close() { 28 | this.contents.close(); 29 | } 30 | 31 | /** 32 | * 搜索dataval为searchKey所在索引块的块号 33 | * 34 | * @param searchKey 35 | * @return 36 | */ 37 | public int search(Constant searchKey) { 38 | Block childBlock = findChildBlock(searchKey); 39 | // flag只要不是0,即level-0目录块,则递归搜索 40 | while (contents.getFlag() > 0) { 41 | contents.close(); 42 | contents = new BTreePage(childBlock, tableInfo, tx); 43 | childBlock = findChildBlock(searchKey); 44 | } 45 | return childBlock.number(); 46 | } 47 | 48 | /** 49 | * 创建新的树根 50 | * 51 | * @param entry 在树根目录块中插入目录记录时引发的分块 52 | */ 53 | public void makeNewRoot(DirEntry entry) { 54 | Constant firstVal = contents.getDataVal(0); 55 | int level = contents.getFlag(); 56 | Block newBlock = contents.split(0, level); 57 | DirEntry oldRoot = new DirEntry(firstVal, newBlock.number()); 58 | insertEntry(oldRoot); 59 | insertEntry(entry); 60 | contents.setFlag(level + 1); 61 | } 62 | 63 | /** 64 | * 插入一条目录记录,如果引发了目录块拆分,则返回一条指向该目录块的目录记录 65 | * @param entry 66 | * @return 67 | */ 68 | public DirEntry insert(DirEntry entry) { 69 | if (contents.getFlag() == 0) 70 | return insertEntry(entry); 71 | Block childBlock = findChildBlock(entry.getDataval()); 72 | BTreeDir child = new BTreeDir(childBlock, tableInfo, tx); 73 | DirEntry myEntry = child.insert(entry); // 递归 74 | child.close(); 75 | // 如果子目录块产生了分块,则当前目录需要添加一个对应的目录记录 76 | return (myEntry != null) ? insertEntry(myEntry) : null; 77 | } 78 | 79 | // ===============私有方法================== 80 | 81 | /** 82 | * 插入一条新的目录记录 83 | * 84 | * @param entry 85 | * @return 如果引发了目录块拆分,则返回一个新目录块对应的目录记录 86 | */ 87 | private DirEntry insertEntry(DirEntry entry) { 88 | int newSlot = 1 + contents.findSlotBefore(entry.getDataval()); 89 | contents.insertDir(newSlot, entry.getDataval(), entry.getBlockNum()); 90 | if (!contents.isFull()) 91 | return null; 92 | int level = contents.getFlag(); 93 | int splitPos = contents.getNumRecords() / 2; 94 | Constant splitDataval = contents.getDataVal(splitPos); 95 | Block newBlock = contents.split(splitPos, level); 96 | return new DirEntry(splitDataval, newBlock.number()); 97 | } 98 | 99 | private Block findChildBlock(Constant searchKey) { 100 | int slotNum = contents.findSlotBefore(searchKey); 101 | 102 | // slotNum有两种情况: 103 | // 1. 找到了对应searchKey的目录记录 104 | // 2. 到了第一个大于等于searchKey的目录记录,但还没找到待搜索的目录记录,那肯定要往下搜索 105 | // 当前slotNum为匹配记录的上一条/第一个大于等于searchKey的目录记录的上一条 106 | if (contents.getDataVal(slotNum + 1).equals(searchKey)) 107 | slotNum++; 108 | int blockNum = contents.getChild(slotNum); 109 | return new Block(fileName, blockNum); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/record/RecordFile.java: -------------------------------------------------------------------------------- 1 | package simpledb.record; 2 | 3 | import simpledb.file.Block; 4 | import simpledb.tx.Transaction; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * @ClassName RecordFile 10 | * @Description TODO 11 | * @Author LiuZhian 12 | * @Date 2020/2/3 7:58 下午 13 | * @Version 1.0 14 | */ 15 | public class RecordFile { 16 | private TableInfo tableInfo; 17 | private Transaction tx; 18 | private String fileName; 19 | private RecordPage recordPage; 20 | private int currentBlkNum; 21 | 22 | public RecordFile(TableInfo tableInfo, Transaction tx) throws IOException { 23 | this.tableInfo = tableInfo; 24 | this.tx = tx; 25 | this.fileName = tableInfo.fileName(); 26 | // 如果记录文件当前为空,则新追加一个块 27 | if (tx.size(fileName) == 0) 28 | appendBlock(); 29 | moveTo(0); // 移动到第0个块 30 | } 31 | 32 | public void close() { 33 | recordPage.close(); 34 | } 35 | 36 | // 涉及移动当前记录位置的操作 37 | public void beforeFirst() { 38 | moveTo(0); 39 | } 40 | 41 | /** 42 | * 判断是否存在下一条记录 43 | * 44 | * @return 45 | * @throws IOException 46 | */ 47 | public boolean next() throws IOException { 48 | while (true) { 49 | // 有下一条记录 50 | if (recordPage.next()) 51 | return true; 52 | // 没有下一条记录,并且是在最后一个块 53 | if (atLastBlock()) 54 | return false; 55 | // 没有下一条记录,但是不是最后一个块 56 | moveTo(currentBlkNum + 1); // 移动到下一块 57 | } 58 | } 59 | 60 | public void moveToRID(RID rid) { 61 | moveTo(rid.blockNumber()); // 先移到指定块 62 | recordPage.moveToID(rid.id()); // 再移到块内指定ID的记录处 63 | } 64 | 65 | /** 66 | * 插入一个记录。 67 | *

68 | * 注意,插入总是成功的。 69 | * 1. 要么是在已有的块中找到一个空位置 70 | * 2. 要么是追加了一个新的块 71 | *

72 | * 新记录的起始位置都可以由recordPage.currentPos()方法获取到。 73 | *

74 | * 此外,这里的插入其实只修改了记录的 EMPTY/INUSE 标志字节, 75 | * 客户端代码需要紧接着修改实际的值。 76 | * 77 | * @throws IOException 78 | */ 79 | public void insert() throws IOException { 80 | // 先移到第0块 81 | moveTo(0); 82 | // 当始终找不到一个插入的位置时 83 | while (!recordPage.insert()) { 84 | // 如果现在是在最后一个块,那肯定是要追加一个新块了 85 | if (atLastBlock()) 86 | appendBlock(); 87 | // 否则不断往后面的块里面找 88 | moveTo(currentBlkNum + 1); 89 | } 90 | } 91 | 92 | // 访问当前记录具体数据的方法 93 | public int getInt(String fldname) { 94 | return recordPage.getInt(fldname); 95 | } 96 | 97 | public String getString(String fldname) { 98 | return recordPage.getString(fldname); 99 | } 100 | 101 | public void setInt(String fldname, int newVal) { 102 | recordPage.setInt(fldname, newVal); 103 | } 104 | 105 | public void setString(String fldname, String newVal) { 106 | recordPage.setString(fldname, newVal); 107 | } 108 | 109 | public RID currentRID() { 110 | return new RID(currentBlkNum, recordPage.currentID()); 111 | } 112 | 113 | public void delete() { 114 | recordPage.delete(); 115 | } 116 | 117 | /** 118 | * 移动至指定块 119 | * 120 | * @param specificBlkNum 指定的块 121 | */ 122 | private void moveTo(int specificBlkNum) { 123 | // 主动释放掉缓冲区中已经固定的,对应当前记录块的缓冲区 124 | if (recordPage != null) { 125 | recordPage.close(); 126 | } 127 | currentBlkNum = specificBlkNum; 128 | Block blk = new Block(fileName, currentBlkNum); 129 | recordPage = new RecordPage(blk, tableInfo, tx); 130 | } 131 | 132 | /** 133 | * 追加一个新的记录块 134 | */ 135 | private Block appendBlock() { 136 | RecordFormatter recordFormatter = new RecordFormatter(tableInfo); 137 | return tx.append(fileName, recordFormatter); 138 | } 139 | 140 | /** 141 | * 判断当前是否在记录文件的最后一块 142 | * 143 | * @return 144 | * @throws IOException 145 | */ 146 | private boolean atLastBlock() throws IOException { 147 | return currentBlkNum == tx.size(fileName) - 1; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/index/btree/BTreeLeaf.java: -------------------------------------------------------------------------------- 1 | package simpledb.index.btree; 2 | 3 | import simpledb.file.Block; 4 | import simpledb.query.Constant; 5 | import simpledb.record.RID; 6 | import simpledb.record.TableInfo; 7 | import simpledb.tx.Transaction; 8 | 9 | /** 10 | * @program simpledb 11 | * @description: TODO 12 | * @author: liuzhian 13 | * @create: 2020/09/27 09:13 14 | */ 15 | public class BTreeLeaf { 16 | 17 | private TableInfo tableInfo; 18 | private String fileName; 19 | private Constant searchKey; 20 | private BTreePage contents; 21 | private int currentSlot; 22 | private Transaction tx; 23 | 24 | public BTreeLeaf(Block block, TableInfo tableInfo, Constant searchKey, Transaction tx) { 25 | this.tableInfo = tableInfo; 26 | this.contents = new BTreePage(block, tableInfo, tx); 27 | this.fileName = block.filename(); 28 | this.searchKey = searchKey; 29 | this.currentSlot = this.contents.findSlotBefore(searchKey); 30 | this.tx = tx; 31 | } 32 | 33 | public void close() { 34 | this.contents.close(); 35 | } 36 | 37 | /** 38 | * 是否存在下一条dataval为this.searchKey的索引记录 39 | * 40 | * @return 41 | */ 42 | public boolean next() { 43 | currentSlot++; // todo: important here 44 | if (currentSlot >= contents.getNumRecords()) 45 | return tryOverflow(); 46 | else if (contents.getDataVal(currentSlot).equals(searchKey)) 47 | return true; 48 | else 49 | return tryOverflow(); 50 | } 51 | 52 | /** 53 | * 返回当前索引记录的RID 54 | * 55 | * @return 56 | */ 57 | public RID getDataRID() { 58 | return this.contents.getDataRID(currentSlot); 59 | } 60 | 61 | public void delete(RID datarid) { 62 | while (next()) { 63 | if (getDataRID().equals(datarid)) { 64 | contents.delete(currentSlot); 65 | return; 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * 插入一条新的索引记录,可能引发块拆分 72 | * 73 | * @param datarid 新索引记录的datarid 74 | * @return 如果引发块拆分,则返回一个目录记录对象 75 | */ 76 | public DirEntry insert(RID datarid) { 77 | currentSlot++; 78 | contents.insertLeaf(currentSlot, searchKey, datarid); 79 | if (!contents.isFull()) { 80 | return null; 81 | } 82 | // 如果插入后满了,需要拆分 83 | Constant firstKey = contents.getDataVal(0); 84 | Constant lastKey = contents.getDataVal(contents.getNumRecords() - 1); 85 | 86 | if (firstKey.equals(lastKey)) // 如果都是同样dataval的记录 87 | { 88 | Block newBlock = contents.split(1, contents.getFlag()); 89 | contents.setFlag(newBlock.number()); // flag标志位设置为溢出配块的块号 90 | return null; 91 | } else { 92 | int splitPos = contents.getNumRecords() / 2; // 从中间位置开始拆分 93 | Constant splitKey = contents.getDataVal(splitPos); 94 | if (splitKey.equals(firstKey)) { 95 | // 中间往右搜索,直到遇到下一个和splitKey不同的dataval 96 | while (contents.getDataVal(splitPos).equals(splitKey)) 97 | splitPos++; 98 | splitKey = contents.getDataVal(splitPos); 99 | } else { 100 | // 中间往左搜素,直到遇到下一个和splitKey不同的dataval 101 | while (contents.getDataVal(splitPos - 1).equals(splitKey)) 102 | splitPos--; 103 | } 104 | Block newBlock = contents.split(splitPos, -1); // todo: flag设置为-1,表示普通的索引块 105 | return new DirEntry(splitKey, newBlock.number()); // splitKey 和 块号组成一条 目录记录 106 | } 107 | } 108 | 109 | /** 110 | * 判断是否存在溢出块 111 | * 112 | * @return 113 | */ 114 | private boolean tryOverflow() { 115 | Constant firstKey = contents.getDataVal(0); 116 | int flag = contents.getFlag(); 117 | if (!searchKey.equals(firstKey) || flag < 0) 118 | return false; 119 | contents.close(); 120 | Block nextBlock = new Block(fileName, flag); // 下一块 121 | contents = new BTreePage(nextBlock, tableInfo, tx); 122 | currentSlot = 0; 123 | return true; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /simpledb/src/simpledb/tx/TransactionTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.tx; 2 | 3 | 4 | import simpledb.buffer.ABCStringFormatter; 5 | import simpledb.buffer.PageFormatter; 6 | import simpledb.file.Block; 7 | import simpledb.server.SimpleDB; 8 | 9 | import java.awt.print.PageFormat; 10 | import java.io.IOException; 11 | 12 | /** 13 | * @ClassName TransactionTest 14 | * @Deacription // TODO 15 | * @Author LiuZhian 16 | * @Date 2020-01-19 19:32 17 | * @Version 1.0 18 | **/ 19 | 20 | public class TransactionTest { 21 | 22 | public static void main(String[] args) throws IOException { 23 | SimpleDB.init("lzadb"); 24 | TestA tA = new TestA();new Thread(tA).start(); 25 | TestB tB = new TestB();new Thread(tB).start(); 26 | TestC tC = new TestC();new Thread(tC).start(); 27 | } 28 | } 29 | 30 | class TestA implements Runnable { 31 | @Override 32 | public void run() { 33 | try { 34 | Transaction tx = new Transaction(); 35 | System.out.println("Tx A --> TxNum: "+tx.getTxNum()); 36 | Block blk1 = new Block("junk", 1); 37 | Block blk2 = new Block("junk", 2); 38 | tx.pin(blk1); 39 | tx.pin(blk2); 40 | System.out.println("Tx A: read block 1 start"); 41 | String blk_1_pos_20_val = tx.getString(blk1, 20); 42 | System.out.println("Tx A read block 1 at pos 20: "+blk_1_pos_20_val); 43 | System.out.println("Tx A: read block 1 end"); 44 | 45 | Thread.sleep(1000); 46 | 47 | System.out.println("Tx A: read block 2 start"); 48 | int blk_2_pos_88_val = tx.getInt(blk2, 88); 49 | System.out.println("Tx A read block 2 at pos 88: "+blk_2_pos_88_val); 50 | System.out.println("Tx A: read block 2 end"); 51 | 52 | tx.commit(); 53 | } catch (InterruptedException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | } 58 | 59 | class TestB implements Runnable { 60 | @Override 61 | public void run() { 62 | try { 63 | Transaction tx = new Transaction(); 64 | System.out.println("Tx B --> TxNum: "+tx.getTxNum()); 65 | Block blk1 = new Block("junk", 1); 66 | Block blk2 = new Block("junk", 2); 67 | tx.pin(blk1); 68 | tx.pin(blk2); 69 | 70 | System.out.println("Tx B: write block 2 start"); 71 | tx.setInt(blk2, 88, 2); 72 | System.out.println("Tx B write block 2 at pos 88 with value \'2\' success!"); 73 | System.out.println("Tx B: write block 2 end"); 74 | 75 | Thread.sleep(1000); 76 | 77 | System.out.println("Tx B: read block 1 start"); 78 | String blk_1_pos_20_val = tx.getString(blk1, 20); 79 | System.out.println("Tx B read block 1 at pos 20: "+blk_1_pos_20_val); 80 | System.out.println("Tx B: read block 1 end"); 81 | 82 | tx.commit(); 83 | } catch (InterruptedException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | } 88 | 89 | class TestC implements Runnable { 90 | @Override 91 | public void run() { 92 | try { 93 | Transaction tx = new Transaction(); 94 | System.out.println("Tx C --> TxNum: "+tx.getTxNum()); 95 | Block blk1 = new Block("junk", 1); 96 | Block blk2 = new Block("junk", 2); 97 | tx.pin(blk1); 98 | tx.pin(blk2); 99 | 100 | System.out.println("Tx C: write block 1 start"); 101 | tx.setString(blk1, 20, "hello"); 102 | System.out.println("Tx C write block 1 at pos 20 with value \'hello\' success!"); 103 | System.out.println("Tx C: write block 1 end"); 104 | 105 | Thread.sleep(1000); 106 | 107 | System.out.println("Tx C: read block 2 start"); 108 | int blk_2_pos_88_val = tx.getInt(blk2, 88); 109 | System.out.println("Tx C read block 2 at pos 88: "+blk_2_pos_88_val); 110 | System.out.println("Tx C: read block 2 end"); 111 | 112 | tx.commit(); 113 | } catch (InterruptedException e) { 114 | e.printStackTrace(); 115 | } 116 | } 117 | } --------------------------------------------------------------------------------