├── src
├── main
│ ├── resources
│ │ ├── application-prod.yml
│ │ ├── application-dev.yml
│ │ ├── static
│ │ │ ├── favicon.ico
│ │ │ ├── demo.5889606d.css
│ │ │ ├── demo.5889606d.css.map
│ │ │ ├── runtime.b0a62080.js
│ │ │ ├── index.html
│ │ │ └── runtime.b0a62080.js.map
│ │ ├── application.yml
│ │ └── logback-spring.xml
│ └── java
│ │ └── com
│ │ └── dyx
│ │ └── simpledb
│ │ ├── backend
│ │ ├── parser
│ │ │ ├── statement
│ │ │ │ ├── Abort.java
│ │ │ │ ├── Commit.java
│ │ │ │ ├── Show.java
│ │ │ │ ├── DropObj.java
│ │ │ │ ├── DeleteObj.java
│ │ │ │ ├── Begin.java
│ │ │ │ ├── InsertObj.java
│ │ │ │ ├── SingleExpression.java
│ │ │ │ ├── UpdateObj.java
│ │ │ │ ├── SelectObj.java
│ │ │ │ ├── OrderByExpression.java
│ │ │ │ ├── Where.java
│ │ │ │ └── Create.java
│ │ │ └── Tokenizer.java
│ │ ├── tbm
│ │ │ ├── BeginRes.java
│ │ │ ├── FieldCalRes.java
│ │ │ ├── TableManager.java
│ │ │ ├── Booter.java
│ │ │ └── TableManagerImpl.java
│ │ ├── utils
│ │ │ ├── Panic.java
│ │ │ ├── ParseStringRes.java
│ │ │ ├── RandomUtil.java
│ │ │ ├── Parser.java
│ │ │ └── PrintUtil.java
│ │ ├── dm
│ │ │ ├── page
│ │ │ │ ├── Page.java
│ │ │ │ ├── PageImpl.java
│ │ │ │ ├── PageOne.java
│ │ │ │ └── PageX.java
│ │ │ ├── pageIndex
│ │ │ │ ├── PageInfo.java
│ │ │ │ └── PageIndex.java
│ │ │ ├── DataManager.java
│ │ │ ├── dataItem
│ │ │ │ ├── DataItem.java
│ │ │ │ └── DataItemImpl.java
│ │ │ ├── pageCache
│ │ │ │ ├── PageCache.java
│ │ │ │ └── PageCacheImpl.java
│ │ │ ├── logger
│ │ │ │ ├── Logger.java
│ │ │ │ └── LoggerImpl.java
│ │ │ ├── DataManagerImpl.java
│ │ │ └── Recover.java
│ │ ├── common
│ │ │ ├── SubArray.java
│ │ │ └── AbstractCache.java
│ │ ├── vm
│ │ │ ├── IsolationLevel.java
│ │ │ ├── VersionManager.java
│ │ │ ├── Transaction.java
│ │ │ ├── Entry.java
│ │ │ ├── Visibility.java
│ │ │ ├── VersionManagerImpl.java
│ │ │ └── LockTable.java
│ │ ├── tm
│ │ │ ├── TransactionManager.java
│ │ │ └── TransactionManagerImpl.java
│ │ ├── server
│ │ │ ├── Executor.java
│ │ │ └── Server.java
│ │ ├── Launcher.java
│ │ └── im
│ │ │ └── UniqueIndex.java
│ │ ├── transport
│ │ ├── Package.java
│ │ ├── Packager.java
│ │ ├── Encoder.java
│ │ └── Transporter.java
│ │ ├── SimpleSqlDatabaseApplication.java
│ │ ├── client
│ │ ├── RoundTripper.java
│ │ ├── Launcher.java
│ │ ├── Client.java
│ │ ├── l2.java
│ │ ├── l3.java
│ │ └── Shell.java
│ │ ├── websocket
│ │ ├── WebConfig.java
│ │ ├── WebSocketConfig.java
│ │ ├── UserSession.java
│ │ ├── HttpSessionHandshakeInterceptor.java
│ │ ├── UserManager.java
│ │ └── TerminalWebSocketHandler.java
│ │ └── common
│ │ └── Error.java
└── test
│ └── java
│ └── com
│ └── dyx
│ └── simpledb
│ ├── ParserByteTest.java
│ ├── tbm
│ └── TableManagerTest.java
│ ├── SimpleSqlDatabaseApplicationTests.java
│ ├── vm
│ ├── TransactionTimeoutTest.java
│ └── LockTableTest.java
│ ├── im
│ └── UniqueIndexTest.java
│ └── parser
│ └── ParserTest.java
├── .gitignore
├── .mvn
└── wrapper
│ └── maven-wrapper.properties
├── README.md
├── pom.xml
└── mvnw.cmd
/src/main/resources/application-prod.yml:
--------------------------------------------------------------------------------
1 | custom:
2 | db:
3 | path: /www/wwwroot/db/
--------------------------------------------------------------------------------
/src/main/resources/application-dev.yml:
--------------------------------------------------------------------------------
1 | custom:
2 | db:
3 | path: D:/JavaCount/mydb/windows/
--------------------------------------------------------------------------------
/src/main/resources/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blockCloth/EasyDB/HEAD/src/main/resources/static/favicon.ico
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/Abort.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | public class Abort {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/Commit.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | public class Commit {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/tbm/BeginRes.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.tbm;
2 |
3 | public class BeginRes {
4 | public long xid;
5 | public byte[] result;
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/tbm/FieldCalRes.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.tbm;
2 |
3 | public class FieldCalRes {
4 | public long left;
5 | public long right;
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/Show.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | public class Show {
4 | public String tableName;
5 | public boolean isTable;
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/DropObj.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class DropObj {
7 | public String tableName;
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | devtools:
3 | livereload:
4 | enabled: true
5 | config:
6 | import: application-prod.yml
7 | application:
8 | name: simple-sql-database
9 | server:
10 | port: 8081
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/utils/Panic.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.utils;
2 |
3 | public class Panic {
4 | public static void panic(Exception err) {
5 | err.printStackTrace();
6 | // System.exit(1);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/DeleteObj.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class DeleteObj {
7 | public String tableName;
8 | public Where where;
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/resources/static/demo.5889606d.css:
--------------------------------------------------------------------------------
1 | html{background:linear-gradient(-90deg,#9c9a9a 10%,#4b4747 90%)}::-webkit-scrollbar{width:0}.fade{opacity:0;transition:all .6s!important;transform:translateY(200px)}.fade.in{opacity:1;transform:none}
2 | /*# sourceMappingURL=demo.5889606d.css.map */
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/Begin.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import com.dyx.simpledb.backend.vm.IsolationLevel;
4 | import lombok.ToString;
5 |
6 | @ToString
7 | public class Begin {
8 | public IsolationLevel isolationLevel;
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/InsertObj.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class InsertObj {
7 | public String tableName;
8 | public String[] fields;
9 | public String[] values;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/SingleExpression.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class SingleExpression {
7 | public String field;
8 | public String compareOp;
9 | public String value;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/page/Page.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.page;
2 |
3 | public interface Page {
4 | void lock();
5 | void unlock();
6 | void release();
7 | void setDirty(boolean dirty);
8 | boolean isDirty();
9 | int getPageNumber();
10 | byte[] getData();
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/utils/ParseStringRes.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.utils;
2 |
3 | public class ParseStringRes {
4 | public String str;
5 | public int next;
6 |
7 | public ParseStringRes(String str, int next) {
8 | this.str = str;
9 | this.next = next;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/UpdateObj.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class UpdateObj {
7 | public String tableName;
8 | public String[] fieldName;
9 | public String[] value;
10 | public Where where;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/pageIndex/PageInfo.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.pageIndex;
2 |
3 | public class PageInfo {
4 | public int pgno;
5 | public int freeSpace;
6 |
7 | public PageInfo(int pgno, int freeSpace) {
8 | this.pgno = pgno;
9 | this.freeSpace = freeSpace;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/SelectObj.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class SelectObj {
7 | public String tableName;
8 | public String[] fields;
9 | public Where where;
10 | public OrderByExpression orderByExpression;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/common/SubArray.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.common;
2 |
3 | public class SubArray {
4 | public byte[] raw;
5 | public int start;
6 | public int end;
7 |
8 | public SubArray(byte[] raw, int start, int end) {
9 | this.raw = raw;
10 | this.start = start;
11 | this.end = end;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/vm/IsolationLevel.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.vm;
2 |
3 | /**
4 | * @User Administrator
5 | * @CreateTime 2024/8/8 22:26
6 | * @className com.dyx.simpledb.backend.vm.IsolationLevel
7 | */
8 | public enum IsolationLevel {
9 | READ_COMMITTED,
10 | READ_UNCOMMITTED,
11 | REPEATABLE_READ,
12 | SERIALIZABLE
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/utils/RandomUtil.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.utils;
2 |
3 | import java.security.SecureRandom;
4 | import java.util.Random;
5 |
6 | public class RandomUtil {
7 | public static byte[] randomBytes(int length) {
8 | Random r = new SecureRandom();
9 | byte[] buf = new byte[length];
10 | r.nextBytes(buf);
11 | return buf;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/OrderByExpression.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | /**
4 | * @User Administrator
5 | * @CreateTime 2024/8/6 20:49
6 | * @className com.dyx.simpledb.backend.parser.statement.OrderByExpression
7 | */
8 | public class OrderByExpression {
9 | public String[] fields;
10 | public Boolean[] order;
11 |
12 | public OrderByExpression() {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/Where.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class Where {
7 | public SingleExpression singleExp1;
8 | public SingleExpression singleExp2;
9 | public String logicOp;
10 |
11 | public Where() {}
12 |
13 | public Where(SingleExpression exp) {
14 | this.singleExp1 = exp;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/transport/Package.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.transport;
2 |
3 | public class Package {
4 | byte[] data;
5 | Exception err;
6 |
7 | public Package(byte[] data, Exception err) {
8 | this.data = data;
9 | this.err = err;
10 | }
11 |
12 | public byte[] getData() {
13 | return data;
14 | }
15 |
16 | public Exception getErr() {
17 | return err;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/parser/statement/Create.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.parser.statement;
2 |
3 | import lombok.ToString;
4 |
5 | @ToString
6 | public class Create {
7 | public String tableName;
8 | public String[] fieldName;
9 | public String[] fieldType;
10 | public String[] index;
11 | public String primaryKey;
12 | public String[] autoIncrement;
13 | public String[] notNull;
14 | public String[] unique;
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/src/test/java/com/dyx/simpledb/ParserByteTest.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb;
2 |
3 | import com.dyx.simpledb.backend.utils.Parser;
4 | import org.junit.Test;
5 |
6 | import java.util.Arrays;
7 |
8 | /**
9 | * @User Administrator
10 | * @CreateTime 2024/9/4 15:54
11 | * @className com.dyx.simpledb.ParserByteTest
12 | */
13 | public class ParserByteTest {
14 |
15 | @Test
16 | public void constraintByteTest(){
17 | byte[] bytes = Parser.constraintByte(true, false, true, true);
18 | System.out.println(Arrays.toString(bytes));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/SimpleSqlDatabaseApplication.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.ComponentScan;
6 | import org.springframework.scheduling.annotation.EnableScheduling;
7 |
8 | @EnableScheduling
9 | @SpringBootApplication
10 | public class SimpleSqlDatabaseApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(SimpleSqlDatabaseApplication.class, args);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/client/RoundTripper.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.client;
2 |
3 | import com.dyx.simpledb.transport.Package;
4 | import com.dyx.simpledb.transport.Packager;
5 |
6 | public class RoundTripper {
7 | private Packager packager;
8 |
9 | public RoundTripper(Packager packager) {
10 | this.packager = packager;
11 | }
12 |
13 | public Package roundTrip(Package pkg) throws Exception {
14 | packager.send(pkg);
15 | return packager.receive();
16 | }
17 |
18 | public void close() throws Exception {
19 | packager.close();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/resources/static/demo.5889606d.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["css ./node_modules/css-loader/dist/cjs.js??ref--11-1!./node_modules/postcss-loader/src??postcss!./demo/src/index.css"],"names":[],"mappings":"AAAA,KACA,0DACA,CACA,oBACA,OACA,CACA,MACA,SAAA,CACA,4BAAA,CACA,2BACA,CAEA,SACA,SAAA,CACA,cACA","file":"demo.5889606d.css","sourcesContent":["html {\n background: linear-gradient( -90deg, rgb(156, 154, 154) 10%, rgb(75, 71, 71) 90% );\n}\n::-webkit-scrollbar {\n width: 0px;\n}\n.fade {\n opacity: 0;\n transition: all 0.6s !important;\n transform: translateY(200px);\n}\n\n.fade.in {\n opacity: 1;\n transform: none;\n}\n"]}
--------------------------------------------------------------------------------
/src/test/java/com/dyx/simpledb/tbm/TableManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.tbm;
2 |
3 | import com.dyx.simpledb.backend.server.Executor;
4 | import org.junit.Test;
5 |
6 | /**
7 | * @User Administrator
8 | * @CreateTime 2024/7/25 21:27
9 | * @className com.dyx.simpledb.tbm.TableManagerTest
10 | */
11 | public class TableManagerTest {
12 | @Test
13 | public void createTable(){
14 | String sql = "create table stu(id int,name varchar,index idx_name (name));";
15 |
16 | // Executor executor = new Executor();
17 | }
18 |
19 | @Test
20 | public void init(){
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/client/Launcher.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.client;
2 |
3 | import java.io.IOException;
4 | import java.net.Socket;
5 | import java.net.UnknownHostException;
6 |
7 | import com.dyx.simpledb.transport.Encoder;
8 | import com.dyx.simpledb.transport.Packager;
9 | import com.dyx.simpledb.transport.Transporter;
10 |
11 | public class Launcher {
12 | public static void main(String[] args) throws UnknownHostException, IOException {
13 | Socket socket = new Socket("127.0.0.1", 9999);
14 | Encoder e = new Encoder();
15 | Transporter t = new Transporter(socket);
16 | Packager packager = new Packager(t, e);
17 |
18 | Client client = new Client(packager);
19 | Shell shell = new Shell(client);
20 | shell.run();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/client/Client.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.client;
2 |
3 | import com.dyx.simpledb.transport.Package;
4 | import com.dyx.simpledb.transport.Packager;
5 |
6 | public class Client {
7 | private RoundTripper rt;
8 |
9 | public Client(Packager packager) {
10 | this.rt = new RoundTripper(packager);
11 | }
12 |
13 | public byte[] execute(byte[] stat) throws Exception {
14 | Package pkg = new Package(stat, null);
15 | Package resPkg = rt.roundTrip(pkg);
16 | if(resPkg.getErr() != null) {
17 | throw resPkg.getErr();
18 | }
19 | return resPkg.getData();
20 | }
21 |
22 | public void close() {
23 | try {
24 | rt.close();
25 | } catch (Exception e) {
26 | }
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/transport/Packager.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.transport;
2 |
3 | import com.dyx.simpledb.transport.Packager;
4 |
5 | public class Packager {
6 | private Transporter transpoter;
7 | private Encoder encoder;
8 |
9 | public Packager(Transporter transpoter, Encoder encoder) {
10 | this.transpoter = transpoter;
11 | this.encoder = encoder;
12 | }
13 |
14 | public void send(Package pkg) throws Exception {
15 | byte[] data = encoder.encode(pkg);
16 | transpoter.send(data);
17 | }
18 |
19 | public Package receive() throws Exception {
20 | byte[] data = transpoter.receive();
21 | return encoder.decode(data);
22 | }
23 |
24 | public void close() throws Exception {
25 | transpoter.close();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/websocket/WebConfig.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.websocket;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.web.servlet.config.annotation.CorsRegistry;
5 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7 |
8 | @Configuration
9 | public class WebConfig implements WebMvcConfigurer {
10 |
11 | @Override
12 | public void addCorsMappings(CorsRegistry registry) {
13 | registry.addMapping("/**")
14 | .allowedOrigins("*") // 允许所有来源
15 | .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
16 | .allowedHeaders("*")
17 | .allowCredentials(false); // 如果不需要Cookie支持,可以设置为false
18 | }
19 | }
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/vm/VersionManager.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.vm;
2 |
3 | import com.dyx.simpledb.backend.dm.DataManager;
4 | import com.dyx.simpledb.backend.tm.TransactionManager;
5 |
6 | public interface VersionManager {
7 | byte[] read(long xid, long uid) throws Exception;
8 | long insert(long xid, byte[] data) throws Exception;
9 | boolean delete(long xid, long uid) throws Exception;
10 |
11 | long begin(IsolationLevel isolationLevel);
12 | void commit(long xid) throws Exception;
13 | void abort(long xid);
14 |
15 | public static VersionManager newVersionManager(TransactionManager tm, DataManager dm) {
16 | return new VersionManagerImpl(tm, dm);
17 | }
18 | void physicalDelete(long xid, Long uid) throws Exception;
19 |
20 | Transaction getActiveTransaction(long xid);
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/client/l2.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.client;
2 |
3 | import com.dyx.simpledb.transport.Encoder;
4 | import com.dyx.simpledb.transport.Packager;
5 | import com.dyx.simpledb.transport.Transporter;
6 |
7 | import java.io.IOException;
8 | import java.net.Socket;
9 | import java.net.UnknownHostException;
10 |
11 | /**
12 | * @User Administrator
13 | * @CreateTime 2024/8/14 22:47
14 | * @className com.dyx.simpledb.client.l2
15 | */
16 | public class l2 {
17 | public static void main(String[] args) throws UnknownHostException, IOException {
18 | Socket socket = new Socket("127.0.0.1", 9999);
19 | Encoder e = new Encoder();
20 | Transporter t = new Transporter(socket);
21 | Packager packager = new Packager(t, e);
22 |
23 | Client client = new Client(packager);
24 | Shell shell = new Shell(client);
25 | shell.run();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/client/l3.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.client;
2 |
3 | import com.dyx.simpledb.transport.Encoder;
4 | import com.dyx.simpledb.transport.Packager;
5 | import com.dyx.simpledb.transport.Transporter;
6 |
7 | import java.io.IOException;
8 | import java.net.Socket;
9 | import java.net.UnknownHostException;
10 |
11 | /**
12 | * @User Administrator
13 | * @CreateTime 2024/8/14 22:48
14 | * @className com.dyx.simpledb.client.l3
15 | */
16 | public class l3 {
17 | public static void main(String[] args) throws UnknownHostException, IOException {
18 | Socket socket = new Socket("127.0.0.1", 9999);
19 | Encoder e = new Encoder();
20 | Transporter t = new Transporter(socket);
21 | Packager packager = new Packager(t, e);
22 |
23 | Client client = new Client(packager);
24 | Shell shell = new Shell(client);
25 | shell.run();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/client/Shell.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.client;
2 |
3 | import java.util.Scanner;
4 |
5 | public class Shell {
6 | private Client client;
7 |
8 | public Shell(Client client) {
9 | this.client = client;
10 | }
11 |
12 | public void run() {
13 | Scanner sc = new Scanner(System.in);
14 | try {
15 | while(true) {
16 | System.out.print(":> ");
17 | String statStr = sc.nextLine();
18 | if("exit".equals(statStr) || "quit".equals(statStr)) {
19 | break;
20 | }
21 | try {
22 | byte[] res = client.execute(statStr.getBytes());
23 | System.out.println(new String(res));
24 | } catch(Exception e) {
25 | System.out.println(e.getMessage());
26 | }
27 |
28 | }
29 | } finally {
30 | sc.close();
31 | client.close();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # https://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
20 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/page/PageImpl.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.page;
2 |
3 | import java.util.concurrent.locks.Lock;
4 | import java.util.concurrent.locks.ReentrantLock;
5 |
6 | import com.dyx.simpledb.backend.dm.pageCache.PageCache;
7 |
8 | public class PageImpl implements Page {
9 | private int pageNumber;
10 | private byte[] data;
11 | private boolean dirty;
12 | private Lock lock;
13 |
14 | private PageCache pc;
15 |
16 | public PageImpl(int pageNumber, byte[] data, PageCache pc) {
17 | this.pageNumber = pageNumber;
18 | this.data = data;
19 | this.pc = pc;
20 | lock = new ReentrantLock();
21 | }
22 |
23 | public void lock() {
24 | lock.lock();
25 | }
26 |
27 | public void unlock() {
28 | lock.unlock();
29 | }
30 |
31 | public void release() {
32 | pc.release(this);
33 | }
34 |
35 | public void setDirty(boolean dirty) {
36 | this.dirty = dirty;
37 | }
38 |
39 | public boolean isDirty() {
40 | return dirty;
41 | }
42 |
43 | public int getPageNumber() {
44 | return pageNumber;
45 | }
46 |
47 | public byte[] getData() {
48 | return data;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/transport/Encoder.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.transport;
2 |
3 | import java.util.Arrays;
4 |
5 | import com.google.common.primitives.Bytes;
6 |
7 | import com.dyx.simpledb.common.Error;
8 |
9 | public class Encoder {
10 |
11 | public byte[] encode(Package pkg) {
12 | if(pkg.getErr() != null) {
13 | Exception err = pkg.getErr();
14 | String msg = "Intern server error!";
15 | if(err.getMessage() != null) {
16 | msg = err.getMessage();
17 | }
18 | return Bytes.concat(new byte[]{1}, msg.getBytes());
19 | } else {
20 | return Bytes.concat(new byte[]{0}, pkg.getData());
21 | }
22 | }
23 |
24 | public Package decode(byte[] data) throws Exception {
25 | if(data.length < 1) {
26 | throw Error.InvalidPkgDataException;
27 | }
28 | if(data[0] == 0) {
29 | return new Package(Arrays.copyOfRange(data, 1, data.length), null);
30 | } else if(data[0] == 1) {
31 | return new Package(null, new RuntimeException(new String(Arrays.copyOfRange(data, 1, data.length))));
32 | } else {
33 | throw Error.InvalidPkgDataException;
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/java/com/dyx/simpledb/SimpleSqlDatabaseApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb;
2 |
3 | import net.sf.jsqlparser.parser.CCJSqlParserUtil;
4 | import net.sf.jsqlparser.statement.Statement;
5 | import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
6 | import net.sf.jsqlparser.statement.create.table.CreateTable;
7 | import org.junit.Test;
8 |
9 |
10 | public class SimpleSqlDatabaseApplicationTests {
11 |
12 | @Test
13 | public void testCreateTableParsing() {
14 | String createTableSQL = "CREATE TABLEs my_table (id INT PRIMARY KEY, name VARCHAR(100))";
15 |
16 | try {
17 | Statement statement = CCJSqlParserUtil.parse(createTableSQL);
18 | if (statement instanceof CreateTable) {
19 | CreateTable createTable = (CreateTable) statement;
20 | System.out.println("Table Name: " + createTable.getTable().getName());
21 | for (ColumnDefinition columnDefinition : createTable.getColumnDefinitions()) {
22 | System.out.println("Column: " + columnDefinition.getColumnName() + " " + columnDefinition.getColDataType());
23 | }
24 | }
25 | } catch (Exception e) {
26 | e.printStackTrace();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/DataManager.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm;
2 |
3 | import com.dyx.simpledb.backend.dm.dataItem.DataItem;
4 | import com.dyx.simpledb.backend.dm.logger.Logger;
5 | import com.dyx.simpledb.backend.dm.page.PageOne;
6 | import com.dyx.simpledb.backend.dm.pageCache.PageCache;
7 | import com.dyx.simpledb.backend.tm.TransactionManager;
8 |
9 | public interface DataManager {
10 | DataItem read(long uid) throws Exception;
11 | long insert(long xid, byte[] data) throws Exception;
12 | void physicalDelete(Long uid) throws Exception;
13 | void close();
14 |
15 | public static DataManager create(String path, long mem, TransactionManager tm) {
16 | PageCache pc = PageCache.create(path, mem);
17 | Logger lg = Logger.create(path);
18 |
19 | DataManagerImpl dm = new DataManagerImpl(pc, lg, tm);
20 | dm.initPageOne();
21 | return dm;
22 | }
23 |
24 | public static DataManager open(String path, long mem, TransactionManager tm) {
25 | PageCache pc = PageCache.open(path, mem);
26 | Logger lg = Logger.open(path);
27 | DataManagerImpl dm = new DataManagerImpl(pc, lg, tm);
28 | if(!dm.loadCheckPageOne()) {
29 | Recover.recover(tm, lg, pc);
30 | }
31 | dm.fillPageIndex();
32 | PageOne.setVcOpen(dm.pageOne);
33 | dm.pc.flushPage(dm.pageOne);
34 |
35 | return dm;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/resources/logback-spring.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 | logs/easydb.log
13 |
14 |
15 |
16 |
17 | logs/easydb-%d{yyyy-MM-dd}.%i.log
18 |
19 | 7
20 |
21 |
22 | 10MB
23 |
24 |
25 |
26 |
27 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/tbm/TableManager.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.tbm;
2 |
3 | import com.dyx.simpledb.backend.dm.DataManager;
4 | import com.dyx.simpledb.backend.parser.statement.*;
5 | import com.dyx.simpledb.backend.parser.statement.DeleteObj;
6 | import com.dyx.simpledb.backend.utils.Parser;
7 | import com.dyx.simpledb.backend.vm.VersionManager;
8 |
9 | public interface TableManager {
10 | BeginRes begin(Begin begin);
11 | byte[] commit(long xid) throws Exception;
12 | byte[] abort(long xid);
13 |
14 | byte[] show(long xid, Show stat);
15 | byte[] create(long xid, Create create) throws Exception;
16 | byte[] drop(long xid, DropObj stat) throws Exception;
17 |
18 | byte[] insert(long xid, InsertObj insertObj) throws Exception;
19 | byte[] read(long xid, SelectObj selectObj) throws Exception;
20 | byte[] update(long xid, UpdateObj updateObj) throws Exception;
21 | byte[] delete(long xid, DeleteObj deleteObj) throws Exception;
22 |
23 | // void close();
24 |
25 | public static TableManager create(String path, VersionManager vm, DataManager dm) {
26 | Booter booter = Booter.create(path);
27 | booter.update(Parser.long2Byte(0));
28 | return new TableManagerImpl(vm, dm, booter);
29 | }
30 |
31 | public static TableManager open(String path, VersionManager vm, DataManager dm) {
32 | Booter booter = Booter.open(path);
33 | return new TableManagerImpl(vm, dm, booter);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/page/PageOne.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.page;
2 |
3 | import java.util.Arrays;
4 |
5 | import com.dyx.simpledb.backend.dm.pageCache.PageCache;
6 | import com.dyx.simpledb.backend.utils.RandomUtil;
7 |
8 | /**
9 | * 特殊管理第一页
10 | * ValidCheck
11 | * db启动时给100~107字节处填入一个随机字节,db关闭时将其拷贝到108~115字节
12 | * 用于判断上一次数据库是否正常关闭
13 | */
14 | public class PageOne {
15 | private static final int OF_VC = 100;
16 | private static final int LEN_VC = 8;
17 |
18 | public static byte[] InitRaw() {
19 | byte[] raw = new byte[PageCache.PAGE_SIZE];
20 | setVcOpen(raw);
21 | return raw;
22 | }
23 |
24 | public static void setVcOpen(Page pg) {
25 | pg.setDirty(true);
26 | setVcOpen(pg.getData());
27 | }
28 |
29 | private static void setVcOpen(byte[] raw) {
30 | System.arraycopy(RandomUtil.randomBytes(LEN_VC), 0, raw, OF_VC, LEN_VC);
31 | }
32 |
33 | public static void setVcClose(Page pg) {
34 | pg.setDirty(true);
35 | setVcClose(pg.getData());
36 | }
37 |
38 | private static void setVcClose(byte[] raw) {
39 | System.arraycopy(raw, OF_VC, raw, OF_VC+LEN_VC, LEN_VC);
40 | }
41 |
42 | public static boolean checkVc(Page pg) {
43 | return checkVc(pg.getData());
44 | }
45 |
46 | private static boolean checkVc(byte[] raw) {
47 | return Arrays.equals(Arrays.copyOfRange(raw, OF_VC, OF_VC+LEN_VC), Arrays.copyOfRange(raw, OF_VC+LEN_VC, OF_VC+2*LEN_VC));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/resources/static/runtime.b0a62080.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,l,f=r[0],i=r[1],a=r[2],c=0,s=[];c[] lists;
17 |
18 | @SuppressWarnings("unchecked")
19 | public PageIndex() {
20 | lock = new ReentrantLock();
21 | lists = new List[INTERVALS_NO+1];
22 | for (int i = 0; i < INTERVALS_NO+1; i ++) {
23 | lists[i] = new ArrayList<>();
24 | }
25 | }
26 |
27 | public void add(int pgno, int freeSpace) {
28 | lock.lock();
29 | try {
30 | int number = freeSpace / THRESHOLD;
31 | lists[number].add(new PageInfo(pgno, freeSpace));
32 | } finally {
33 | lock.unlock();
34 | }
35 | }
36 |
37 | public PageInfo select(int spaceSize) {
38 | lock.lock();
39 | try {
40 | int number = spaceSize / THRESHOLD;
41 | if(number < INTERVALS_NO) number ++;
42 | while(number <= INTERVALS_NO) {
43 | if(lists[number].size() == 0) {
44 | number ++;
45 | continue;
46 | }
47 | return lists[number].remove(0);
48 | }
49 | return null;
50 | } finally {
51 | lock.unlock();
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/vm/Transaction.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.vm;
2 |
3 | import java.util.HashMap;
4 | import java.util.HashSet;
5 | import java.util.Map;
6 | import java.util.Set;
7 |
8 | import com.dyx.simpledb.backend.tbm.Table;
9 | import com.dyx.simpledb.backend.tm.TransactionManagerImpl;
10 |
11 | // vm对一个事务的抽象
12 | public class Transaction {
13 | public long xid;
14 | public IsolationLevel isolationLevel;
15 | public Map snapshot;
16 | public Exception err;
17 | public boolean autoAborted;
18 | public long startTime; // 添加开始时间属性
19 | // 新增字段:记录事务中修改的表
20 | private Set modifiedTables = new HashSet<>();
21 |
22 | // 添加修改表的方法
23 | public void addModifiedTable(Table table) {
24 | modifiedTables.add(table);
25 | }
26 |
27 | // 获取被修改的表
28 | public Set getModifiedTables() {
29 | return modifiedTables;
30 | }
31 |
32 | public static Transaction newTransaction(long xid, IsolationLevel isolationLevel, Map active) {
33 | Transaction t = new Transaction();
34 | t.xid = xid;
35 | t.isolationLevel = isolationLevel;
36 | t.startTime = System.currentTimeMillis();
37 | if(isolationLevel != IsolationLevel.READ_COMMITTED && isolationLevel != IsolationLevel.READ_UNCOMMITTED) {
38 | t.snapshot = new HashMap<>();
39 | for(Long x : active.keySet()) {
40 | t.snapshot.put(x, true);
41 | }
42 | }
43 | return t;
44 | }
45 |
46 | public boolean isInSnapshot(long xid) {
47 | if(xid == TransactionManagerImpl.SUPER_XID) {
48 | return false;
49 | }
50 | return snapshot.containsKey(xid);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/websocket/UserSession.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.websocket;
2 |
3 | import com.dyx.simpledb.backend.dm.DataManager;
4 | import com.dyx.simpledb.backend.server.Executor;
5 | import com.dyx.simpledb.backend.tbm.TableManager;
6 | import com.dyx.simpledb.backend.tm.TransactionManager;
7 | import lombok.Getter;
8 | import lombok.Setter;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 | import java.util.Set;
13 | import java.util.concurrent.ConcurrentHashMap;
14 |
15 | @Setter
16 | @Getter
17 | public class UserSession {
18 | private String userId;
19 | private long startTime;
20 | private long lastAccessedTime;
21 | private TableManager tableManager;
22 | private TransactionManager transactionManager;
23 | private DataManager dataManager;
24 | private Map executorMap;
25 | private final Set sessionIds = ConcurrentHashMap.newKeySet();
26 |
27 | public UserSession(String userId, long startTime) {
28 | this.userId = userId;
29 | this.startTime = startTime;
30 | this.lastAccessedTime = startTime; // 初始化最后访问时间
31 | executorMap = new HashMap<>();
32 | }
33 |
34 | public void updateLastAccessedTime() {
35 | this.lastAccessedTime = System.currentTimeMillis();
36 | }
37 |
38 | public void close() {
39 | if (dataManager != null) {
40 | dataManager.close();
41 | }
42 | if (transactionManager != null) {
43 | transactionManager.close();
44 | }
45 | }
46 |
47 | public Executor getExecutor(String sessionId) {
48 | return executorMap.get(sessionId);
49 | }
50 |
51 | public Executor removeExecutor(String sessionId) {
52 | return executorMap.remove(sessionId);
53 | }
54 |
55 | public void setExecutor(String sessionId, Executor executor) {
56 | executorMap.put(sessionId,executor);
57 | }
58 |
59 | public void addSession(String sessionId) {
60 | sessionIds.add(sessionId);
61 | }
62 |
63 | public void removeSession(String sessionId) {
64 | sessionIds.remove(sessionId);
65 | }
66 |
67 | public boolean hasActiveSessions() {
68 | return !sessionIds.isEmpty();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/pageCache/PageCache.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.pageCache;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.RandomAccessFile;
6 | import java.nio.channels.FileChannel;
7 |
8 | import com.dyx.simpledb.backend.dm.page.Page;
9 | import com.dyx.simpledb.backend.utils.Panic;
10 | import com.dyx.simpledb.common.Error;
11 |
12 | public interface PageCache {
13 |
14 | public static final int PAGE_SIZE = 1 << 13;
15 |
16 | int newPage(byte[] initData);
17 | Page getPage(int pgno) throws Exception;
18 | void close();
19 | void release(Page page);
20 |
21 | void truncateByBgno(int maxPgno);
22 | int getPageNumber();
23 | void flushPage(Page pg);
24 |
25 | public static PageCacheImpl create(String path, long memory) {
26 | File f = new File(path+PageCacheImpl.DB_SUFFIX);
27 | try {
28 | if(!f.createNewFile()) {
29 | Panic.panic(Error.FileExistsException);
30 | }
31 | } catch (Exception e) {
32 | Panic.panic(e);
33 | }
34 | if(!f.canRead() || !f.canWrite()) {
35 | Panic.panic(Error.FileCannotRWException);
36 | }
37 |
38 | FileChannel fc = null;
39 | RandomAccessFile raf = null;
40 | try {
41 | raf = new RandomAccessFile(f, "rw");
42 | fc = raf.getChannel();
43 | } catch (FileNotFoundException e) {
44 | Panic.panic(e);
45 | }
46 | return new PageCacheImpl(raf, fc, (int)memory/PAGE_SIZE);
47 | }
48 |
49 | public static PageCacheImpl open(String path, long memory) {
50 | File f = new File(path+PageCacheImpl.DB_SUFFIX);
51 | if(!f.exists()) {
52 | Panic.panic(Error.FileNotExistsException);
53 | }
54 | if(!f.canRead() || !f.canWrite()) {
55 | Panic.panic(Error.FileCannotRWException);
56 | }
57 |
58 | FileChannel fc = null;
59 | RandomAccessFile raf = null;
60 | try {
61 | raf = new RandomAccessFile(f, "rw");
62 | fc = raf.getChannel();
63 | } catch (FileNotFoundException e) {
64 | Panic.panic(e);
65 | }
66 | return new PageCacheImpl(raf, fc, (int)memory/PAGE_SIZE);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/page/PageX.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.page;
2 |
3 | import java.util.Arrays;
4 |
5 | import com.dyx.simpledb.backend.dm.pageCache.PageCache;
6 | import com.dyx.simpledb.backend.utils.Parser;
7 |
8 | /**
9 | * PageX管理普通页
10 | * 普通页结构
11 | * [FreeSpaceOffset] [Data]
12 | * FreeSpaceOffset: 2字节 空闲位置开始偏移
13 | */
14 | public class PageX {
15 |
16 | private static final short OF_FREE = 0;
17 | private static final short OF_DATA = 2;
18 | public static final int MAX_FREE_SPACE = PageCache.PAGE_SIZE - OF_DATA;
19 |
20 | public static byte[] initRaw() {
21 | byte[] raw = new byte[PageCache.PAGE_SIZE];
22 | setFSO(raw, OF_DATA);
23 | return raw;
24 | }
25 |
26 | private static void setFSO(byte[] raw, short ofData) {
27 | System.arraycopy(Parser.short2Byte(ofData), 0, raw, OF_FREE, OF_DATA);
28 | }
29 |
30 | // 获取pg的FSO
31 | public static short getFSO(Page pg) {
32 | return getFSO(pg.getData());
33 | }
34 |
35 | private static short getFSO(byte[] raw) {
36 | return Parser.parseShort(Arrays.copyOfRange(raw, 0, 2));
37 | }
38 |
39 | // 将raw插入pg中,返回插入位置
40 | public static short insert(Page pg, byte[] raw) {
41 | pg.setDirty(true);
42 | short offset = getFSO(pg.getData());
43 | System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
44 | setFSO(pg.getData(), (short)(offset + raw.length));
45 | return offset;
46 | }
47 |
48 | // 获取页面的空闲空间大小
49 | public static int getFreeSpace(Page pg) {
50 | return PageCache.PAGE_SIZE - (int)getFSO(pg.getData());
51 | }
52 |
53 | // 将raw插入pg中的offset位置,并将pg的offset设置为较大的offset
54 | public static void recoverInsert(Page pg, byte[] raw, short offset) {
55 | pg.setDirty(true);
56 | System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
57 |
58 | short rawFSO = getFSO(pg.getData());
59 | if(rawFSO < offset + raw.length) {
60 | setFSO(pg.getData(), (short)(offset+raw.length));
61 | }
62 | }
63 |
64 | // 将raw插入pg中的offset位置,不更新update
65 | public static void recoverUpdate(Page pg, byte[] raw, short offset) {
66 | pg.setDirty(true);
67 | System.arraycopy(raw, 0, pg.getData(), offset, raw.length);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/test/java/com/dyx/simpledb/vm/TransactionTimeoutTest.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.vm;
2 |
3 | import com.dyx.simpledb.backend.dm.DataManager;
4 | import com.dyx.simpledb.backend.parser.statement.Create;
5 | import com.dyx.simpledb.backend.tm.TransactionManager;
6 | import com.dyx.simpledb.backend.tm.TransactionManagerImpl;
7 | import com.dyx.simpledb.backend.vm.IsolationLevel;
8 | import com.dyx.simpledb.backend.vm.VersionManager;
9 | import com.dyx.simpledb.backend.vm.VersionManagerImpl;
10 | import org.junit.Test;
11 |
12 | public class TransactionTimeoutTest {
13 |
14 | @Test
15 | public void timeoutTest() {
16 | // 初始化事务管理器和数据管理器
17 | TransactionManager tm = TransactionManager.open("D:\\JavaCount\\mydb\\windows\\127.0.0.1");
18 | // 打开数据管理器,传入路径、内存大小和事务管理器
19 | DataManager dm = DataManager.open("D:\\JavaCount\\mydb\\windows\\127.0.0.1", (1 << 20) * 64, tm);
20 | // 创建版本管理器,传入事务管理器和数据管理器
21 | VersionManager vm = new VersionManagerImpl(tm, dm);
22 |
23 | // 开启一个长时间运行的事务
24 | long xid1 = vm.begin(IsolationLevel.READ_COMMITTED);
25 |
26 | // 在另一个线程中执行此事务,模拟长时间操作
27 | new Thread(() -> {
28 | try {
29 | // 模拟长时间操作
30 | System.out.println("Transaction " + xid1 + " started and sleeping...");
31 | Thread.sleep(35000); // 超过30秒的超时时间
32 | System.out.println("Transaction " + xid1 + " completed.");
33 | } catch (Exception e) {
34 | e.printStackTrace();
35 | }
36 | }).start();
37 |
38 | // 在主线程中启动一个短时间事务以测试超时效果
39 | long xid2 = vm.begin(IsolationLevel.READ_COMMITTED);
40 |
41 | try {
42 | // 执行一些操作
43 | System.out.println("Transaction " + xid2 + " started.");
44 | vm.insert(xid2, "insert into user(id) values (10)".getBytes());
45 | vm.commit(xid2);
46 | System.out.println("Transaction " + xid2 + " committed.");
47 |
48 | // 等待足够的时间让第一个事务超时
49 | Thread.sleep(40000);
50 | } catch (Exception e) {
51 | e.printStackTrace();
52 | }
53 |
54 | // 检查第一个事务的状态
55 | if (tm.isAborted(xid1)) {
56 | System.out.println("Transaction " + xid1 + " was rolled back due to timeout.");
57 | } else {
58 | System.out.println("Transaction " + xid1 + " completed without timeout.");
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/logger/Logger.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.logger;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.IOException;
6 | import java.io.RandomAccessFile;
7 | import java.nio.ByteBuffer;
8 | import java.nio.channels.FileChannel;
9 |
10 | import com.dyx.simpledb.backend.utils.Panic;
11 | import com.dyx.simpledb.backend.utils.Parser;
12 | import com.dyx.simpledb.common.Error;
13 |
14 | public interface Logger {
15 | void log(byte[] data);
16 | void truncate(long x) throws Exception;
17 | byte[] next();
18 | void rewind();
19 | void close();
20 |
21 | public static Logger create(String path) {
22 | File f = new File(path+LoggerImpl.LOG_SUFFIX);
23 | try {
24 | if(!f.createNewFile()) {
25 | Panic.panic(Error.FileExistsException);
26 | }
27 | } catch (Exception e) {
28 | Panic.panic(e);
29 | }
30 | if(!f.canRead() || !f.canWrite()) {
31 | Panic.panic(Error.FileCannotRWException);
32 | }
33 |
34 | FileChannel fc = null;
35 | RandomAccessFile raf = null;
36 | try {
37 | raf = new RandomAccessFile(f, "rw");
38 | fc = raf.getChannel();
39 | } catch (FileNotFoundException e) {
40 | Panic.panic(e);
41 | }
42 |
43 | ByteBuffer buf = ByteBuffer.wrap(Parser.int2Byte(0));
44 | try {
45 | fc.position(0);
46 | fc.write(buf);
47 | fc.force(false);
48 | } catch (IOException e) {
49 | Panic.panic(e);
50 | }
51 |
52 | return new LoggerImpl(raf, fc, 0);
53 | }
54 |
55 | public static Logger open(String path) {
56 | File f = new File(path+LoggerImpl.LOG_SUFFIX);
57 | if(!f.exists()) {
58 | Panic.panic(Error.FileNotExistsException);
59 | }
60 | if(!f.canRead() || !f.canWrite()) {
61 | Panic.panic(Error.FileCannotRWException);
62 | }
63 |
64 | FileChannel fc = null;
65 | RandomAccessFile raf = null;
66 | try {
67 | raf = new RandomAccessFile(f, "rw");
68 | fc = raf.getChannel();
69 | } catch (FileNotFoundException e) {
70 | Panic.panic(e);
71 | }
72 |
73 | LoggerImpl lg = new LoggerImpl(raf, fc);
74 | lg.init();
75 |
76 | return lg;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/tm/TransactionManager.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.tm;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.IOException;
6 | import java.io.RandomAccessFile;
7 | import java.nio.ByteBuffer;
8 | import java.nio.channels.FileChannel;
9 |
10 | import com.dyx.simpledb.backend.utils.Panic;
11 | import com.dyx.simpledb.common.Error;
12 |
13 | public interface TransactionManager {
14 | long begin();
15 | void commit(long xid);
16 | void abort(long xid);
17 | boolean isActive(long xid);
18 | boolean isCommitted(long xid);
19 | boolean isAborted(long xid);
20 | void close();
21 |
22 | public static TransactionManagerImpl create(String path) {
23 | File f = new File(path+TransactionManagerImpl.XID_SUFFIX);
24 | try {
25 | if(!f.createNewFile()) {
26 | Panic.panic(Error.FileExistsException);
27 | }
28 | } catch (Exception e) {
29 | Panic.panic(e);
30 | }
31 | if(!f.canRead() || !f.canWrite()) {
32 | Panic.panic(Error.FileCannotRWException);
33 | }
34 |
35 | FileChannel fc = null;
36 | RandomAccessFile raf = null;
37 | try {
38 | raf = new RandomAccessFile(f, "rw");
39 | fc = raf.getChannel();
40 | } catch (FileNotFoundException e) {
41 | Panic.panic(e);
42 | }
43 |
44 | // 写空XID文件头
45 | ByteBuffer buf = ByteBuffer.wrap(new byte[TransactionManagerImpl.LEN_XID_HEADER_LENGTH]);
46 | try {
47 | fc.position(0);
48 | fc.write(buf);
49 | } catch (IOException e) {
50 | Panic.panic(e);
51 | }
52 |
53 | return new TransactionManagerImpl(raf, fc);
54 | }
55 |
56 | public static TransactionManagerImpl open(String path) {
57 | File f = new File(path+TransactionManagerImpl.XID_SUFFIX);
58 | if(!f.exists()) {
59 | Panic.panic(Error.FileNotExistsException);
60 | }
61 | if(!f.canRead() || !f.canWrite()) {
62 | Panic.panic(Error.FileCannotRWException);
63 | }
64 |
65 | FileChannel fc = null;
66 | RandomAccessFile raf = null;
67 | try {
68 | raf = new RandomAccessFile(f, "rw");
69 | fc = raf.getChannel();
70 | } catch (FileNotFoundException e) {
71 | Panic.panic(e);
72 | }
73 |
74 | return new TransactionManagerImpl(raf, fc);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/websocket/HttpSessionHandshakeInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.websocket;
2 |
3 | import org.springframework.http.server.ServletServerHttpRequest;
4 | import org.springframework.http.server.ServerHttpRequest;
5 | import org.springframework.http.server.ServerHttpResponse;
6 | import org.springframework.stereotype.Component;
7 | import org.springframework.web.socket.WebSocketHandler;
8 | import org.springframework.web.socket.server.HandshakeInterceptor;
9 |
10 | import javax.servlet.http.HttpServletRequest;
11 | import java.util.Map;
12 |
13 | @Component
14 | public class HttpSessionHandshakeInterceptor implements HandshakeInterceptor {
15 |
16 | @Override
17 | public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
18 | if (request instanceof ServletServerHttpRequest) {
19 | HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
20 | String clientIp = getClientIp(servletRequest);
21 | attributes.put("clientIp", clientIp);
22 | }
23 | return true;
24 | }
25 |
26 | @Override
27 | public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
28 | }
29 |
30 | private String getClientIp(HttpServletRequest request) {
31 | String clientIp = request.getHeader("X-Forwarded-For");
32 | if (clientIp != null && !clientIp.isEmpty() && !"unknown".equalsIgnoreCase(clientIp)) {
33 | // 多重代理情况下,第一个IP是真实客户端IP
34 | clientIp = clientIp.split(",")[0];
35 | } else {
36 | clientIp = request.getHeader("Proxy-Client-IP");
37 | if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
38 | clientIp = request.getHeader("WL-Proxy-Client-IP");
39 | }
40 | if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
41 | clientIp = request.getHeader("HTTP_CLIENT_IP");
42 | }
43 | if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
44 | clientIp = request.getHeader("HTTP_X_FORWARDED_FOR");
45 | }
46 | if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
47 | clientIp = request.getRemoteAddr();
48 | }
49 | }
50 |
51 | if ("0:0:0:0:0:0:0:1".equals(clientIp)) {
52 | clientIp = "127.0.0.1";
53 | }
54 |
55 | return clientIp;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EasyDB
2 |
3 |
4 |

5 |
6 |
7 | **轻量级、高性能的自定义数据库解决方案**
8 |
9 | [](https://opensource.org/licenses/MIT)
10 | [](https://github.com/blockCloth/EasyDB)
11 |
12 | ---
13 |
14 | ## 简介
15 |
16 | **EasyDB** 是一个使用 Java 实现的轻量级数据库,灵感来源于 MySQL、PostgreSQL 和 SQLite。项目参考了 MYDB 的设计,专注于提供高效的数据库解决方案,支持数据存储、事务管理、多版本并发控制(MVCC)和索引管理等核心功能,同时集成了日志管理、事务隔离与死锁检测等高级特性。
17 |
18 | ---
19 |
20 | ## 主要特性
21 |
22 | ### 🛠️ 核心功能
23 |
24 | - **可靠性**:采用 MySQL、PostgreSQL 和 SQLite 的部分原理,确保数据的安全性与一致性。
25 | - **两阶段锁协议(2PL)**:实现串行化调度,支持多种事务隔离级别。
26 | - **多版本并发控制(MVCC)**:优化并发操作,减少阻塞,提高系统性能。
27 |
28 | ### 🌐 WebSocket 实时通信
29 |
30 | - **独立的数据区**:每个用户拥有独立的数据区,确保数据安全性和用户操作的互不干扰。
31 | - **多页面管理**:优化多页面访问体验,提升用户操作的流畅度。
32 |
33 | ### 🔍 高效 SQL 解析
34 |
35 | - **JSQLParser 集成**:使用 JSQLParser 将 SQL 语句解析为抽象语法树 (AST),简化 SQL 查询的分析与修改。
36 |
37 | ### ⚙️ 数据管理与优化
38 |
39 | - **全表扫描与索引处理**:支持在字段未建立索引的情况下进行条件筛选操作。
40 | - **条件约束**:内置丰富的条件约束与主键索引功能,支持唯一性、非空性、自增性等多种约束条件。
41 |
42 | ### 🚦 事务控制与死锁检测
43 |
44 | - **事务隔离机制**:支持从读未提交到串行化的多种隔离级别。
45 | - **死锁检测**:通过超时检测功能防止系统资源长期占用,增强系统的可靠性。
46 |
47 | ### 📝 日志管理与故障恢复
48 |
49 | - **日志管理**:内置强大的日志管理机制,确保数据库操作的可追溯性和数据一致性保障。
50 | - **故障恢复**:支持故障恢复功能,增强系统的容错能力和数据安全性。
51 |
52 | ---
53 |
54 | ## 快速开始
55 |
56 | ### 1. 克隆项目
57 |
58 | ```bash
59 | git clone https://github.com/blockCloth/EasyDB.git
60 | cd EasyDB
61 | ```
62 |
63 | ### 2. 配置环境
64 |
65 | 请确保你已经安装了以下工具:
66 |
67 | - **JDK 8+**:Java 运行环境。
68 | - **Maven**:用于管理项目依赖。
69 |
70 | ### 3. 启动项目
71 |
72 | ```bash
73 | 启动SimpleSqlDatabaseApplication.java类即可
74 | ```
75 |
76 | 项目启动成功后,可以通过访问 `http://localhost:8081/index.html` 进行体验。
77 |
78 | ---
79 |
80 | ## 使用指南
81 |
82 | 欲了解如何使用 EasyDB 的详细信息,请查阅以下文档:
83 |
84 | - [项目体验](http://db.blockcloth.cn/)
85 | - [使用指南](http://easydb.blockcloth.cn/document/)
86 | - [项目文档](http://easydb.blockcloth.cn/demo)
87 |
88 | ---
89 |
90 | ## 贡献
91 |
92 | 欢迎贡献代码和提交 Issue。如果你有任何问题或建议,请随时提交。
93 |
94 | 1. Fork 本仓库
95 | 2. 创建你的特性分支 (`git checkout -b feature/AmazingFeature`)
96 | 3. 提交你的修改 (`git commit -m 'Add some AmazingFeature'`)
97 | 4. 推送到分支 (`git push origin feature/AmazingFeature`)
98 | 5. 提交 Pull Request
99 |
100 | ---
101 |
102 | ## 许可证
103 |
104 | 本项目使用 [MIT 许可证](https://opensource.org/licenses/MIT) 开源,详情请查阅 `LICENSE` 文件。
105 |
106 | ---
107 |
108 | © 2024-至今 blockCloth
109 |
110 | ---
111 |
112 | ### 注意事项
113 |
114 | - 项目使用 Spring Boot 构建,确保项目配置正确。
115 | - 为了项目的顺利运行,请参阅文档中的详细配置步骤。
116 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/utils/Parser.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.utils;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.util.Arrays;
5 |
6 | import com.google.common.primitives.Bytes;
7 |
8 | public class Parser {
9 |
10 | public static byte[] short2Byte(short value) {
11 | return ByteBuffer.allocate(Short.SIZE / Byte.SIZE).putShort(value).array();
12 | }
13 |
14 | public static short parseShort(byte[] buf) {
15 | ByteBuffer buffer = ByteBuffer.wrap(buf, 0, 2);
16 | return buffer.getShort();
17 | }
18 |
19 | public static byte[] int2Byte(int value) {
20 | return ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
21 | }
22 |
23 | public static int parseInt(byte[] buf) {
24 | ByteBuffer buffer = ByteBuffer.wrap(buf, 0, 4);
25 | return buffer.getInt();
26 | }
27 |
28 | public static float parseFloat(byte[] buf) {
29 | ByteBuffer buffer = ByteBuffer.wrap(buf, 0, 4);
30 | return buffer.getFloat();
31 | }
32 |
33 | public static long parseLong(byte[] buf) {
34 | ByteBuffer buffer = ByteBuffer.wrap(buf, 0, 8);
35 | return buffer.getLong();
36 | }
37 |
38 | public static double parseDouble(byte[] buf) {
39 | ByteBuffer buffer = ByteBuffer.wrap(buf, 0, 8);
40 | return buffer.getDouble();
41 | }
42 |
43 | public static byte[] long2Byte(long value) {
44 | return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(value).array();
45 | }
46 |
47 | public static byte[] double2Byte(double value) {
48 | return ByteBuffer.allocate(Double.SIZE / Byte.SIZE).putDouble(value).array();
49 | }
50 |
51 | public static byte[] float2Byte(float value) {
52 | return ByteBuffer.allocate(Float.SIZE / Byte.SIZE).putFloat(value).array();
53 | }
54 |
55 | public static ParseStringRes parseString(byte[] raw) {
56 | int length = parseInt(Arrays.copyOf(raw, 4));
57 | String str = new String(Arrays.copyOfRange(raw, 4, 4+length));
58 | return new ParseStringRes(str, length+4);
59 | }
60 |
61 | public static byte[] string2Byte(String str) {
62 | byte[] l = int2Byte(str.length());
63 | return Bytes.concat(l, str.getBytes());
64 | }
65 |
66 | public static long str2Uid(String key) {
67 | long seed = 13331;
68 | long res = 0;
69 | for(byte b : key.getBytes()) {
70 | res = res * seed + (long)b;
71 | }
72 | return res;
73 | }
74 |
75 | public static byte[] constraintByte(boolean isPrimaryKey, boolean isAutoIncrement, boolean isNotNull, boolean isUnique) {
76 | return Bytes.concat(
77 | new byte[] {isPrimaryKey ? (byte) 1 : (byte) 0},
78 | new byte[] {isAutoIncrement ? (byte) 1 : (byte) 0},
79 | new byte[] {isNotNull ? (byte) 1 : (byte) 0},
80 | new byte[] {isUnique ? (byte) 1 : (byte) 0});
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/tbm/Booter.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.tbm;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.nio.file.Files;
7 | import java.nio.file.StandardCopyOption;
8 |
9 | import com.dyx.simpledb.backend.utils.Panic;
10 | import com.dyx.simpledb.common.Error;
11 |
12 | // 记录第一个表的uid
13 | public class Booter {
14 | public static final String BOOTER_SUFFIX = ".bt";
15 | public static final String BOOTER_TMP_SUFFIX = ".bt_tmp";
16 |
17 | String path;
18 | File file;
19 |
20 | public static Booter create(String path) {
21 | removeBadTmp(path);
22 | File f = new File(path+BOOTER_SUFFIX);
23 | try {
24 | if(!f.createNewFile()) {
25 | Panic.panic(Error.FileExistsException);
26 | }
27 | } catch (Exception e) {
28 | Panic.panic(e);
29 | }
30 | if(!f.canRead() || !f.canWrite()) {
31 | Panic.panic(Error.FileCannotRWException);
32 | }
33 | return new Booter(path, f);
34 | }
35 |
36 | public static Booter open(String path) {
37 | removeBadTmp(path);
38 | File f = new File(path+BOOTER_SUFFIX);
39 | if(!f.exists()) {
40 | Panic.panic(Error.FileNotExistsException);
41 | }
42 | if(!f.canRead() || !f.canWrite()) {
43 | Panic.panic(Error.FileCannotRWException);
44 | }
45 | return new Booter(path, f);
46 | }
47 |
48 | private static void removeBadTmp(String path) {
49 | new File(path+BOOTER_TMP_SUFFIX).delete();
50 | }
51 |
52 | private Booter(String path, File file) {
53 | this.path = path;
54 | this.file = file;
55 | }
56 |
57 | public byte[] load() {
58 | byte[] buf = null;
59 | try {
60 | buf = Files.readAllBytes(file.toPath());
61 | } catch (IOException e) {
62 | Panic.panic(e);
63 | }
64 | return buf;
65 | }
66 |
67 | public void update(byte[] data) {
68 | File tmp = new File(path + BOOTER_TMP_SUFFIX);
69 | try {
70 | tmp.createNewFile();
71 | } catch (Exception e) {
72 | Panic.panic(e);
73 | }
74 | if(!tmp.canRead() || !tmp.canWrite()) {
75 | Panic.panic(Error.FileCannotRWException);
76 | }
77 | try(FileOutputStream out = new FileOutputStream(tmp)) {
78 | out.write(data);
79 | out.flush();
80 | } catch(IOException e) {
81 | Panic.panic(e);
82 | }
83 | try {
84 | Files.move(tmp.toPath(), new File(path+BOOTER_SUFFIX).toPath(), StandardCopyOption.REPLACE_EXISTING);
85 | } catch(IOException e) {
86 | Panic.panic(e);
87 | }
88 | file = new File(path+BOOTER_SUFFIX);
89 | if(!file.canRead() || !file.canWrite()) {
90 | Panic.panic(Error.FileCannotRWException);
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/dm/dataItem/DataItemImpl.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.dm.dataItem;
2 |
3 | import java.util.concurrent.locks.Lock;
4 | import java.util.concurrent.locks.ReadWriteLock;
5 | import java.util.concurrent.locks.ReentrantReadWriteLock;
6 |
7 | import com.dyx.simpledb.backend.common.SubArray;
8 | import com.dyx.simpledb.backend.dm.DataManagerImpl;
9 | import com.dyx.simpledb.backend.dm.page.Page;
10 |
11 | /**
12 | * dataItem 结构如下:
13 | * [ValidFlag] [DataSize] [Data]
14 | * ValidFlag 1字节,0为合法,1为非法
15 | * DataSize 2字节,标识Data的长度
16 | */
17 | public class DataItemImpl implements DataItem {
18 |
19 | public static final int OF_VALID = 0;
20 | public static final int OF_SIZE = 1;
21 | public static final int OF_DATA = 3;
22 |
23 | private SubArray raw;
24 | private byte[] oldRaw;
25 | private Lock rLock;
26 | private Lock wLock;
27 | private DataManagerImpl dm;
28 | private long uid;
29 | private Page pg;
30 |
31 | public DataItemImpl(SubArray raw, byte[] oldRaw, Page pg, long uid, DataManagerImpl dm) {
32 | this.raw = raw;
33 | this.oldRaw = oldRaw;
34 | ReadWriteLock lock = new ReentrantReadWriteLock();
35 | rLock = lock.readLock();
36 | wLock = lock.writeLock();
37 | this.dm = dm;
38 | this.uid = uid;
39 | this.pg = pg;
40 | }
41 |
42 | public boolean isValid() {
43 | return raw.raw[raw.start+OF_VALID] == (byte)0;
44 | }
45 |
46 | @Override
47 | public SubArray data() {
48 | return new SubArray(raw.raw, raw.start+OF_DATA, raw.end);
49 | }
50 |
51 | @Override
52 | public void before() {
53 | wLock.lock();
54 | pg.setDirty(true);
55 | System.arraycopy(raw.raw, raw.start, oldRaw, 0, oldRaw.length);
56 | }
57 |
58 | @Override
59 | public void unBefore() {
60 | System.arraycopy(oldRaw, 0, raw.raw, raw.start, oldRaw.length);
61 | wLock.unlock();
62 | }
63 |
64 | @Override
65 | public void after(long xid) {
66 | dm.logDataItem(xid, this);
67 | wLock.unlock();
68 | }
69 |
70 | @Override
71 | public void release() {
72 | dm.releaseDataItem(this);
73 | }
74 |
75 | @Override
76 | public void lock() {
77 | wLock.lock();
78 | }
79 |
80 | @Override
81 | public void unlock() {
82 | wLock.unlock();
83 | }
84 |
85 | @Override
86 | public void rLock() {
87 | rLock.lock();
88 | }
89 |
90 | @Override
91 | public void rUnLock() {
92 | rLock.unlock();
93 | }
94 |
95 | @Override
96 | public Page page() {
97 | return pg;
98 | }
99 |
100 | @Override
101 | public long getUid() {
102 | return uid;
103 | }
104 |
105 | @Override
106 | public byte[] getOldRaw() {
107 | return oldRaw;
108 | }
109 |
110 | @Override
111 | public SubArray getRaw() {
112 | return raw;
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/vm/Entry.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.vm;
2 |
3 | import java.util.Arrays;
4 |
5 | import com.google.common.primitives.Bytes;
6 |
7 | import com.dyx.simpledb.backend.common.SubArray;
8 | import com.dyx.simpledb.backend.dm.dataItem.DataItem;
9 | import com.dyx.simpledb.backend.utils.Parser;
10 |
11 | /**
12 | * VM向上层抽象出entry
13 | * entry结构:
14 | * [XMIN] [XMAX] [data]
15 | */
16 | public class Entry {
17 |
18 | private static final int OF_XMIN = 0;
19 | private static final int OF_XMAX = OF_XMIN+8;
20 | private static final int OF_DATA = OF_XMAX+8;
21 |
22 | private long uid;
23 | private DataItem dataItem;
24 | private VersionManager vm;
25 |
26 | public static Entry newEntry(VersionManager vm, DataItem dataItem, long uid) {
27 | if (dataItem == null) {
28 | return null;
29 | }
30 | Entry entry = new Entry();
31 | entry.uid = uid;
32 | entry.dataItem = dataItem;
33 | entry.vm = vm;
34 | return entry;
35 | }
36 |
37 | public static Entry loadEntry(VersionManager vm, long uid) throws Exception {
38 | DataItem di = ((VersionManagerImpl)vm).dm.read(uid);
39 | return newEntry(vm, di, uid);
40 | }
41 |
42 | public static byte[] wrapEntryRaw(long xid, byte[] data) {
43 | byte[] xmin = Parser.long2Byte(xid);
44 | byte[] xmax = new byte[8];
45 | return Bytes.concat(xmin, xmax, data);
46 | }
47 |
48 | public void release() {
49 | ((VersionManagerImpl)vm).releaseEntry(this);
50 | }
51 |
52 | public void remove() {
53 | dataItem.release();
54 | }
55 |
56 | // 以拷贝的形式返回内容
57 | public byte[] data() {
58 | dataItem.rLock();
59 | try {
60 | SubArray sa = dataItem.data();
61 | byte[] data = new byte[sa.end - sa.start - OF_DATA];
62 | System.arraycopy(sa.raw, sa.start+OF_DATA, data, 0, data.length);
63 | return data;
64 | } finally {
65 | dataItem.rUnLock();
66 | }
67 | }
68 |
69 | public long getXmin() {
70 | dataItem.rLock();
71 | try {
72 | SubArray sa = dataItem.data();
73 | return Parser.parseLong(Arrays.copyOfRange(sa.raw, sa.start+OF_XMIN, sa.start+OF_XMAX));
74 | } finally {
75 | dataItem.rUnLock();
76 | }
77 | }
78 |
79 | public long getXmax() {
80 | dataItem.rLock();
81 | try {
82 | SubArray sa = dataItem.data();
83 | return Parser.parseLong(Arrays.copyOfRange(sa.raw, sa.start+OF_XMAX, sa.start+OF_DATA));
84 | } finally {
85 | dataItem.rUnLock();
86 | }
87 | }
88 |
89 | public void setXmax(long xid) {
90 | dataItem.before();
91 | try {
92 | SubArray sa = dataItem.data();
93 | System.arraycopy(Parser.long2Byte(xid), 0, sa.raw, sa.start+OF_XMAX, 8);
94 | } finally {
95 | dataItem.after(xid);
96 | }
97 | }
98 |
99 | public long getUid() {
100 | return uid;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/server/Executor.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.server;
2 |
3 | import com.dyx.simpledb.backend.parser.Parser;
4 | import com.dyx.simpledb.backend.parser.statement.*;
5 | import com.dyx.simpledb.backend.parser.statement.DeleteObj;
6 | import com.dyx.simpledb.backend.tbm.BeginRes;
7 | import com.dyx.simpledb.backend.tbm.TableManager;
8 | import com.dyx.simpledb.common.Error;
9 |
10 | public class Executor {
11 | private long xid;
12 | TableManager tbm;
13 |
14 | public Executor(TableManager tbm) {
15 | this.tbm = tbm;
16 | this.xid = 0;
17 | }
18 |
19 | public void close() {
20 | if(xid != 0) {
21 | System.out.println("Abnormal Abort: " + xid);
22 | tbm.abort(xid);
23 | }
24 | }
25 |
26 | public byte[] execute(byte[] sql) throws Exception {
27 | System.out.println("Execute: " + new String(sql));
28 | Object stat = Parser.Parse(sql);
29 | if(Begin.class.isInstance(stat)) {
30 | if(xid != 0) {
31 | throw Error.NestedTransactionException;
32 | }
33 | BeginRes r = tbm.begin((Begin)stat);
34 | xid = r.xid;
35 | return r.result;
36 | } else if(Commit.class.isInstance(stat)) {
37 | if(xid == 0) {
38 | throw Error.NoTransactionException;
39 | }
40 | byte[] res = tbm.commit(xid);
41 | xid = 0;
42 | return res;
43 | } else if(Abort.class.isInstance(stat)) {
44 | if(xid == 0) {
45 | throw Error.NoTransactionException;
46 | }
47 | byte[] res = tbm.abort(xid);
48 | xid = 0;
49 | return res;
50 | } else {
51 | return execute2(stat);
52 | }
53 | }
54 |
55 | private byte[] execute2(Object stat) throws Exception {
56 | boolean tmpTransaction = false;
57 | Exception e = null;
58 | if(xid == 0) {
59 | tmpTransaction = true;
60 | BeginRes r = tbm.begin(new Begin());
61 | xid = r.xid;
62 | }
63 | try {
64 | byte[] res = null;
65 | if(Show.class.isInstance(stat)) {
66 | res = tbm.show(xid,(Show)stat);
67 | } else if(Create.class.isInstance(stat)) {
68 | res = tbm.create(xid, (Create)stat);
69 | } else if(SelectObj.class.isInstance(stat)) {
70 | res = tbm.read(xid, (SelectObj)stat);
71 | } else if(InsertObj.class.isInstance(stat)) {
72 | res = tbm.insert(xid, (InsertObj)stat);
73 | } else if(DeleteObj.class.isInstance(stat)) {
74 | res = tbm.delete(xid, (DeleteObj)stat);
75 | } else if(UpdateObj.class.isInstance(stat)) {
76 | res = tbm.update(xid, (UpdateObj)stat);
77 | }else if(DropObj.class.isInstance(stat)) {
78 | res = tbm.drop(xid, (DropObj)stat);
79 | }
80 | return res;
81 | } catch(Exception e1) {
82 | e = e1;
83 | throw e;
84 | } finally {
85 | if(tmpTransaction) {
86 | if(e != null) {
87 | tbm.abort(xid);
88 | } else {
89 | tbm.commit(xid);
90 | }
91 | xid = 0;
92 | }
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/dyx/simpledb/backend/utils/PrintUtil.java:
--------------------------------------------------------------------------------
1 | package com.dyx.simpledb.backend.utils;
2 |
3 | import java.util.HashMap;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | public class PrintUtil {
8 |
9 | /**
10 | * 生成单列或多列表格并返回其字符串表示
11 | *
12 | * @param columnNames 列名数组
13 | * @param entries 数据列表,每个Map代表一行,key是列名,value是列值
14 | * @return 格式化的表格字符串
15 | */
16 | public static String printTable(String[] columnNames, List