From 8f6ad9817b01285b90b7f0684a11617e3297097f Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sun, 24 Nov 2024 23:30:40 +0530 Subject: [PATCH 01/18] FEAT : added logic for a basic REPL --- main.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/main.go b/main.go index e69de29..9598072 100644 --- a/main.go +++ b/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "os" +) + +type inputBuffer struct { + buffer string + bufferLength int + inputLength int +} + +func createInputBuffer() inputBuffer { + newInputBuffer := inputBuffer{"", 0, 0} + return newInputBuffer +} + +func printConsole() { + fmt.Print("godb > ") +} + +func readConsole(inputBuffer inputBuffer) inputBuffer { + fmt.Scanf("%s", &inputBuffer.buffer) + bytesRead := len(inputBuffer.buffer) + if bytesRead <= 0 { + fmt.Println("Error reading input") + os.Exit(1) + } + inputBuffer.inputLength = bytesRead - 1 + + return inputBuffer +} + +func main() { + inputBuffer := createInputBuffer() + condition := true + for condition { + printConsole() + inputBuffer = readConsole(inputBuffer) + + if inputBuffer.buffer == ".exit" { + fmt.Println("bye!") + os.Exit(0) + } else { + fmt.Printf("unrecognised command %v\n", inputBuffer.buffer) + } + + } +} -- 2.54.0 From ecc188de14f05bdad3e27a8ac8e88a6d569034bf Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sun, 24 Nov 2024 23:34:30 +0530 Subject: [PATCH 02/18] FIX : assigned value for buffer length --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 9598072..75d4415 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ func readConsole(inputBuffer inputBuffer) inputBuffer { fmt.Println("Error reading input") os.Exit(1) } + inputBuffer.bufferLength = len(inputBuffer.buffer) inputBuffer.inputLength = bytesRead - 1 return inputBuffer -- 2.54.0 From b5a31e7775b31c943389ec90ee849944ab7c3811 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Fri, 29 Nov 2024 01:42:48 +0530 Subject: [PATCH 03/18] FEAT : added a simple compiler and support for commands --- main.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 7 deletions(-) diff --git a/main.go b/main.go index 75d4415..e14f472 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,30 @@ package main import ( + "bufio" "fmt" + "io" "os" + "strings" +) + +type metaCommandResults int +type prepareResults int +type statementType int + +const ( + metaCommandSuccess metaCommandResults = iota + metaCommandUnrecognised +) + +const ( + prepareSuccess prepareResults = iota + prepareUnrecognised +) + +const ( + statementSelect statementType = iota + statementInsert ) type inputBuffer struct { @@ -11,6 +33,44 @@ type inputBuffer struct { inputLength int } +type statement struct { + stype statementType +} + +func doMetaCommands(inputBuffer inputBuffer) metaCommandResults { + metacommand := metaCommandUnrecognised + if inputBuffer.buffer == ".exit" { + fmt.Println("bye!") + os.Exit(0) + } else { + metacommand = metaCommandUnrecognised + } + return metacommand +} + +func prepareStatements(inputBuffer inputBuffer, statement *statement) prepareResults { + if strings.Split(inputBuffer.buffer, " ")[0] == "insert" { + statement.stype = statementInsert + return prepareSuccess + } + if strings.Split(inputBuffer.buffer, " ")[0] == "select" { + statement.stype = statementSelect + return prepareSuccess + } + return prepareUnrecognised +} + +func executeStatement(statement *statement) { + switch statement.stype { + case statementInsert: + fmt.Println("This is where the logic of insert statement should go") + break + case statementSelect: + fmt.Println("This is where the logic of select statement should go") + break + } +} + func createInputBuffer() inputBuffer { newInputBuffer := inputBuffer{"", 0, 0} return newInputBuffer @@ -21,7 +81,21 @@ func printConsole() { } func readConsole(inputBuffer inputBuffer) inputBuffer { - fmt.Scanf("%s", &inputBuffer.buffer) + var stdin *bufio.Reader + var line []rune + stdin = bufio.NewReader(os.Stdin) + for { + c, _, err := stdin.ReadRune() + if err == io.EOF || c == '\n' { + break + } + if err != nil { + fmt.Println("Error reading input") + os.Exit(1) + } + line = append(line, c) + } + inputBuffer.buffer = string(line) bytesRead := len(inputBuffer.buffer) if bytesRead <= 0 { fmt.Println("Error reading input") @@ -35,17 +109,31 @@ func readConsole(inputBuffer inputBuffer) inputBuffer { func main() { inputBuffer := createInputBuffer() + statement := statement{0} condition := true for condition { printConsole() inputBuffer = readConsole(inputBuffer) - - if inputBuffer.buffer == ".exit" { - fmt.Println("bye!") - os.Exit(0) - } else { - fmt.Printf("unrecognised command %v\n", inputBuffer.buffer) + if strings.Split(inputBuffer.buffer, "")[0] == "." { + switch doMetaCommands(inputBuffer) { + case metaCommandSuccess: + continue + case metaCommandUnrecognised: + fmt.Printf("unrecognised command %v\n", inputBuffer.buffer) + continue + } } + switch prepareStatements(inputBuffer, &statement) { + case prepareSuccess: + break + case prepareUnrecognised: + fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.buffer) + continue + } + + executeStatement(&statement) + fmt.Println("Executed") + } } -- 2.54.0 From d2a5477217fd620c63da3b43f6cedd5c4355a9ec Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Fri, 29 Nov 2024 02:08:45 +0530 Subject: [PATCH 04/18] FEAT : skeleton for a in memory table --- main.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index e14f472..2a9f3c6 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,7 @@ const ( const ( prepareSuccess prepareResults = iota prepareUnrecognised + prepareSyntaxError ) const ( @@ -33,8 +34,15 @@ type inputBuffer struct { inputLength int } +type row struct { + id uint + username string + email string +} + type statement struct { stype statementType + row row } func doMetaCommands(inputBuffer inputBuffer) metaCommandResults { @@ -50,6 +58,14 @@ func doMetaCommands(inputBuffer inputBuffer) metaCommandResults { func prepareStatements(inputBuffer inputBuffer, statement *statement) prepareResults { if strings.Split(inputBuffer.buffer, " ")[0] == "insert" { + argsAssigned, err := fmt.Sscanf(inputBuffer.buffer, "insert %v %s %s", &statement.row.id, &statement.row.username, &statement.row.email) + if err != nil { + fmt.Println("error parsing insert statement") + fmt.Println(err.Error()) + } + if argsAssigned > 3 { + return prepareSyntaxError + } statement.stype = statementInsert return prepareSuccess } @@ -109,7 +125,7 @@ func readConsole(inputBuffer inputBuffer) inputBuffer { func main() { inputBuffer := createInputBuffer() - statement := statement{0} + statement := statement{0, row{0, "", ""}} condition := true for condition { printConsole() -- 2.54.0 From 82f77db5924d5d6c51a658d14e388a3378cb8f84 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Wed, 5 Feb 2025 23:08:55 +0530 Subject: [PATCH 05/18] feat: added readme for the project --- readme.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..aa9a01e --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +### Godb + +A SQLite like database made in golang -- 2.54.0 From c3016be8e922e07abed70d828b4d2572916c0c20 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Tue, 30 Jun 2026 00:06:37 +0530 Subject: [PATCH 06/18] chor: minor code cleanup --- main.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/main.go b/main.go index 2a9f3c6..00f9021 100644 --- a/main.go +++ b/main.go @@ -15,15 +15,9 @@ type statementType int const ( metaCommandSuccess metaCommandResults = iota metaCommandUnrecognised -) - -const ( prepareSuccess prepareResults = iota prepareUnrecognised prepareSyntaxError -) - -const ( statementSelect statementType = iota statementInsert ) -- 2.54.0 From df75480238d0d217159f4e2a108836c4368181df Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sun, 19 Jul 2026 20:37:26 +0530 Subject: [PATCH 07/18] feat: added a makefile and switched to a more idiomatic structure --- main.go => cmd/godb/main.go | 0 makefile | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+) rename main.go => cmd/godb/main.go (100%) create mode 100644 makefile diff --git a/main.go b/cmd/godb/main.go similarity index 100% rename from main.go rename to cmd/godb/main.go diff --git a/makefile b/makefile new file mode 100644 index 0000000..f0b5817 --- /dev/null +++ b/makefile @@ -0,0 +1,18 @@ +BINARY_NAME=godb + +run: + @go run cmd/godb/main.go + +build: + @go build -o ${BINARY_NAME} cmd/godb/main.go + +start: + @./${BINARY_NAME} + +clean: + @go clean + @rm -rf ${BINARY_NAME} + +reset: clean build + +.PHONY: clean build -- 2.54.0 From a522c63f4727ca6c87084f4945b9a73ad275fabb Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sun, 19 Jul 2026 22:16:05 +0530 Subject: [PATCH 08/18] feat: upated the codebase to be a bit more idiomatic --- cmd/godb/main.go | 70 +++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/cmd/godb/main.go b/cmd/godb/main.go index 00f9021..517086a 100644 --- a/cmd/godb/main.go +++ b/cmd/godb/main.go @@ -9,23 +9,29 @@ import ( ) type metaCommandResults int -type prepareResults int -type statementType int const ( metaCommandSuccess metaCommandResults = iota metaCommandUnrecognised +) + +type prepareResults int + +const ( prepareSuccess prepareResults = iota prepareUnrecognised prepareSyntaxError +) + +type statementType int + +const ( statementSelect statementType = iota statementInsert ) type inputBuffer struct { - buffer string - bufferLength int - inputLength int + buffer string } type row struct { @@ -39,7 +45,7 @@ type statement struct { row row } -func doMetaCommands(inputBuffer inputBuffer) metaCommandResults { +func doMetaCommands(inputBuffer *inputBuffer) metaCommandResults { metacommand := metaCommandUnrecognised if inputBuffer.buffer == ".exit" { fmt.Println("bye!") @@ -50,9 +56,9 @@ func doMetaCommands(inputBuffer inputBuffer) metaCommandResults { return metacommand } -func prepareStatements(inputBuffer inputBuffer, statement *statement) prepareResults { - if strings.Split(inputBuffer.buffer, " ")[0] == "insert" { - argsAssigned, err := fmt.Sscanf(inputBuffer.buffer, "insert %v %s %s", &statement.row.id, &statement.row.username, &statement.row.email) +func prepareStatements(inputBuffer *inputBuffer, statement *statement) prepareResults { + if strings.HasPrefix(inputBuffer.buffer, "insert") { + argsAssigned, err := fmt.Sscanf(inputBuffer.buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email) if err != nil { fmt.Println("error parsing insert statement") fmt.Println(err.Error()) @@ -63,7 +69,7 @@ func prepareStatements(inputBuffer inputBuffer, statement *statement) prepareRes statement.stype = statementInsert return prepareSuccess } - if strings.Split(inputBuffer.buffer, " ")[0] == "select" { + if strings.HasPrefix(inputBuffer.buffer, "select") { statement.stype = statementSelect return prepareSuccess } @@ -74,57 +80,37 @@ func executeStatement(statement *statement) { switch statement.stype { case statementInsert: fmt.Println("This is where the logic of insert statement should go") - break case statementSelect: fmt.Println("This is where the logic of select statement should go") - break } } -func createInputBuffer() inputBuffer { - newInputBuffer := inputBuffer{"", 0, 0} - return newInputBuffer +func createInputBuffer() *inputBuffer { + return &inputBuffer{""} } func printConsole() { fmt.Print("godb > ") } -func readConsole(inputBuffer inputBuffer) inputBuffer { - var stdin *bufio.Reader - var line []rune - stdin = bufio.NewReader(os.Stdin) - for { - c, _, err := stdin.ReadRune() - if err == io.EOF || c == '\n' { - break - } - if err != nil { - fmt.Println("Error reading input") - os.Exit(1) - } - line = append(line, c) - } - inputBuffer.buffer = string(line) - bytesRead := len(inputBuffer.buffer) - if bytesRead <= 0 { - fmt.Println("Error reading input") +func readConsole(inputBuffer *inputBuffer, stdin *bufio.Reader) *inputBuffer { + line, err := stdin.ReadString('\n') + if err != nil && err != io.EOF { + fmt.Println("Error reading input", err) os.Exit(1) } - inputBuffer.bufferLength = len(inputBuffer.buffer) - inputBuffer.inputLength = bytesRead - 1 - + inputBuffer.buffer = strings.TrimSpace(line) return inputBuffer } func main() { + var stdin = bufio.NewReader(os.Stdin) inputBuffer := createInputBuffer() statement := statement{0, row{0, "", ""}} - condition := true - for condition { + for { printConsole() - inputBuffer = readConsole(inputBuffer) - if strings.Split(inputBuffer.buffer, "")[0] == "." { + inputBuffer = readConsole(inputBuffer, stdin) + if strings.HasPrefix(inputBuffer.buffer, ".") { switch doMetaCommands(inputBuffer) { case metaCommandSuccess: continue @@ -136,7 +122,7 @@ func main() { switch prepareStatements(inputBuffer, &statement) { case prepareSuccess: - break + //todo case prepareUnrecognised: fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.buffer) continue -- 2.54.0 From d97308b4f1350aec6a192eaf726ce4e2b8e8a89e Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sun, 19 Jul 2026 22:18:21 +0530 Subject: [PATCH 09/18] feat: added a git ignore and changed the build output path --- .gitignore | 1 + makefile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9a5aec --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tmp diff --git a/makefile b/makefile index f0b5817..dd6088a 100644 --- a/makefile +++ b/makefile @@ -4,10 +4,10 @@ run: @go run cmd/godb/main.go build: - @go build -o ${BINARY_NAME} cmd/godb/main.go + @go build -o tmp/${BINARY_NAME} cmd/godb/main.go start: - @./${BINARY_NAME} + @./tmp/${BINARY_NAME} clean: @go clean -- 2.54.0 From 5550c92c23053748bad125d8ef3b96ae16cb42f5 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sun, 19 Jul 2026 23:33:02 +0530 Subject: [PATCH 10/18] feat: split code for repl --- cmd/godb/main.go | 51 ++++++++++++------------------------------ go.mod | 4 ++-- internals/repl/repl.go | 34 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 internals/repl/repl.go diff --git a/cmd/godb/main.go b/cmd/godb/main.go index 517086a..5227fc4 100644 --- a/cmd/godb/main.go +++ b/cmd/godb/main.go @@ -1,11 +1,11 @@ package main import ( - "bufio" "fmt" - "io" "os" "strings" + + . "godb/internals/repl" ) type metaCommandResults int @@ -30,10 +30,6 @@ const ( statementInsert ) -type inputBuffer struct { - buffer string -} - type row struct { id uint username string @@ -45,9 +41,9 @@ type statement struct { row row } -func doMetaCommands(inputBuffer *inputBuffer) metaCommandResults { +func doMetaCommands(inputBuffer *InputBuffer) metaCommandResults { metacommand := metaCommandUnrecognised - if inputBuffer.buffer == ".exit" { + if inputBuffer.Buffer == ".exit" { fmt.Println("bye!") os.Exit(0) } else { @@ -56,9 +52,9 @@ func doMetaCommands(inputBuffer *inputBuffer) metaCommandResults { return metacommand } -func prepareStatements(inputBuffer *inputBuffer, statement *statement) prepareResults { - if strings.HasPrefix(inputBuffer.buffer, "insert") { - argsAssigned, err := fmt.Sscanf(inputBuffer.buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email) +func prepareStatements(inputBuffer *InputBuffer, statement *statement) prepareResults { + if strings.HasPrefix(inputBuffer.Buffer, "insert") { + argsAssigned, err := fmt.Sscanf(inputBuffer.Buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email) if err != nil { fmt.Println("error parsing insert statement") fmt.Println(err.Error()) @@ -69,7 +65,7 @@ func prepareStatements(inputBuffer *inputBuffer, statement *statement) prepareRe statement.stype = statementInsert return prepareSuccess } - if strings.HasPrefix(inputBuffer.buffer, "select") { + if strings.HasPrefix(inputBuffer.Buffer, "select") { statement.stype = statementSelect return prepareSuccess } @@ -85,37 +81,18 @@ func executeStatement(statement *statement) { } } -func createInputBuffer() *inputBuffer { - return &inputBuffer{""} -} - -func printConsole() { - fmt.Print("godb > ") -} - -func readConsole(inputBuffer *inputBuffer, stdin *bufio.Reader) *inputBuffer { - line, err := stdin.ReadString('\n') - if err != nil && err != io.EOF { - fmt.Println("Error reading input", err) - os.Exit(1) - } - inputBuffer.buffer = strings.TrimSpace(line) - return inputBuffer -} - func main() { - var stdin = bufio.NewReader(os.Stdin) - inputBuffer := createInputBuffer() + inputBuffer := NewInputBuffer() statement := statement{0, row{0, "", ""}} for { - printConsole() - inputBuffer = readConsole(inputBuffer, stdin) - if strings.HasPrefix(inputBuffer.buffer, ".") { + PrintConsole() + inputBuffer.ReadConsole() + if strings.HasPrefix(inputBuffer.Buffer, ".") { switch doMetaCommands(inputBuffer) { case metaCommandSuccess: continue case metaCommandUnrecognised: - fmt.Printf("unrecognised command %v\n", inputBuffer.buffer) + fmt.Printf("unrecognised comma%v\n", inputBuffer.Buffer) continue } } @@ -124,7 +101,7 @@ func main() { case prepareSuccess: //todo case prepareUnrecognised: - fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.buffer) + fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.Buffer) continue } diff --git a/go.mod b/go.mod index cfeceaf..672f71b 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module godb/main +module godb -go 1.23.2 +go 1.26.3 diff --git a/internals/repl/repl.go b/internals/repl/repl.go new file mode 100644 index 0000000..56119ea --- /dev/null +++ b/internals/repl/repl.go @@ -0,0 +1,34 @@ +package repl + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" +) + +type InputBuffer struct { + Buffer string + Reader *bufio.Reader +} + +func NewInputBuffer() *InputBuffer { + return &InputBuffer{ + Buffer: "", + Reader: bufio.NewReader(os.Stdin), + } +} + +func PrintConsole() { + fmt.Print("godb > ") +} + +func (inputBuffer *InputBuffer) ReadConsole() { + line, err := inputBuffer.Reader.ReadString('\n') + if err != nil && err != io.EOF { + fmt.Println("Error reading input", err) + os.Exit(1) + } + inputBuffer.Buffer = strings.TrimSpace(line) +} -- 2.54.0 From 208820217c4dfb27c3030c0412fe1723b91f3435 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Mon, 20 Jul 2026 00:32:34 +0530 Subject: [PATCH 11/18] feat: split into code for buffer and vm --- cmd/godb/main.go | 107 +------------------------------------ internals/buffer/buffer.go | 30 +++++++++++ internals/repl/repl.go | 52 ++++++++++-------- internals/vm/vm.go | 86 +++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 126 deletions(-) create mode 100644 internals/buffer/buffer.go create mode 100644 internals/vm/vm.go diff --git a/cmd/godb/main.go b/cmd/godb/main.go index 5227fc4..2e2791d 100644 --- a/cmd/godb/main.go +++ b/cmd/godb/main.go @@ -1,112 +1,9 @@ package main import ( - "fmt" - "os" - "strings" - - . "godb/internals/repl" + "godb/internals/repl" ) -type metaCommandResults int - -const ( - metaCommandSuccess metaCommandResults = iota - metaCommandUnrecognised -) - -type prepareResults int - -const ( - prepareSuccess prepareResults = iota - prepareUnrecognised - prepareSyntaxError -) - -type statementType int - -const ( - statementSelect statementType = iota - statementInsert -) - -type row struct { - id uint - username string - email string -} - -type statement struct { - stype statementType - row row -} - -func doMetaCommands(inputBuffer *InputBuffer) metaCommandResults { - metacommand := metaCommandUnrecognised - if inputBuffer.Buffer == ".exit" { - fmt.Println("bye!") - os.Exit(0) - } else { - metacommand = metaCommandUnrecognised - } - return metacommand -} - -func prepareStatements(inputBuffer *InputBuffer, statement *statement) prepareResults { - if strings.HasPrefix(inputBuffer.Buffer, "insert") { - argsAssigned, err := fmt.Sscanf(inputBuffer.Buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email) - if err != nil { - fmt.Println("error parsing insert statement") - fmt.Println(err.Error()) - } - if argsAssigned > 3 { - return prepareSyntaxError - } - statement.stype = statementInsert - return prepareSuccess - } - if strings.HasPrefix(inputBuffer.Buffer, "select") { - statement.stype = statementSelect - return prepareSuccess - } - return prepareUnrecognised -} - -func executeStatement(statement *statement) { - switch statement.stype { - case statementInsert: - fmt.Println("This is where the logic of insert statement should go") - case statementSelect: - fmt.Println("This is where the logic of select statement should go") - } -} - func main() { - inputBuffer := NewInputBuffer() - statement := statement{0, row{0, "", ""}} - for { - PrintConsole() - inputBuffer.ReadConsole() - if strings.HasPrefix(inputBuffer.Buffer, ".") { - switch doMetaCommands(inputBuffer) { - case metaCommandSuccess: - continue - case metaCommandUnrecognised: - fmt.Printf("unrecognised comma%v\n", inputBuffer.Buffer) - continue - } - } - - switch prepareStatements(inputBuffer, &statement) { - case prepareSuccess: - //todo - case prepareUnrecognised: - fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.Buffer) - continue - } - - executeStatement(&statement) - fmt.Println("Executed") - - } + repl.Run() } diff --git a/internals/buffer/buffer.go b/internals/buffer/buffer.go new file mode 100644 index 0000000..f6709dc --- /dev/null +++ b/internals/buffer/buffer.go @@ -0,0 +1,30 @@ +package buffer + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" +) + +type InputBuffer struct { + Buffer string + Reader *bufio.Reader +} + +func NewInputBuffer() *InputBuffer { + return &InputBuffer{ + Buffer: "", + Reader: bufio.NewReader(os.Stdin), + } +} + +func (inputBuffer *InputBuffer) ReadConsole() { + line, err := inputBuffer.Reader.ReadString('\n') + if err != nil && err != io.EOF { + fmt.Println("Error reading input", err) + os.Exit(1) + } + inputBuffer.Buffer = strings.TrimSpace(line) +} diff --git a/internals/repl/repl.go b/internals/repl/repl.go index 56119ea..b6cccb7 100644 --- a/internals/repl/repl.go +++ b/internals/repl/repl.go @@ -1,34 +1,44 @@ package repl import ( - "bufio" "fmt" - "io" - "os" "strings" + + . "godb/internals/buffer" + . "godb/internals/vm" ) -type InputBuffer struct { - Buffer string - Reader *bufio.Reader -} - -func NewInputBuffer() *InputBuffer { - return &InputBuffer{ - Buffer: "", - Reader: bufio.NewReader(os.Stdin), - } -} - func PrintConsole() { fmt.Print("godb > ") } -func (inputBuffer *InputBuffer) ReadConsole() { - line, err := inputBuffer.Reader.ReadString('\n') - if err != nil && err != io.EOF { - fmt.Println("Error reading input", err) - os.Exit(1) +func Run() { + + inputBuffer := NewInputBuffer() + statement := NewStatement() + for { + PrintConsole() + inputBuffer.ReadConsole() + if len(inputBuffer.Buffer) > 0 && strings.HasPrefix(inputBuffer.Buffer, ".") { + switch DoMetaCommands(inputBuffer) { + case MetaCommandSuccess: + continue + case MetaCommandUnrecognised: + fmt.Printf("unrecognised command %v\n", inputBuffer.Buffer) + continue + } + } + + switch statement.PrepareStatements(inputBuffer) { + case PrepareSuccess: + //todo + case PrepareUnrecognised: + fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.Buffer) + continue + } + + statement.ExecuteStatement() + fmt.Println("Executed") + } - inputBuffer.Buffer = strings.TrimSpace(line) } diff --git a/internals/vm/vm.go b/internals/vm/vm.go new file mode 100644 index 0000000..e7ff626 --- /dev/null +++ b/internals/vm/vm.go @@ -0,0 +1,86 @@ +package vm + +import ( + "fmt" + "os" + "strings" + + . "godb/internals/buffer" +) + +type MetaCommandResults int + +const ( + MetaCommandSuccess MetaCommandResults = iota + MetaCommandUnrecognised +) + +type PrepareResults int + +const ( + PrepareSuccess PrepareResults = iota + PrepareUnrecognised + PrepareSyntaxError +) + +type statementType int + +const ( + StatementSelect statementType = iota + StatementInsert +) + +type row struct { + id uint + username string + email string +} + +type Statement struct { + stype statementType + row row +} + +func NewStatement() *Statement { + return &Statement{0, row{0, "", ""}} +} + +func DoMetaCommands(inputBuffer *InputBuffer) MetaCommandResults { + metacommand := MetaCommandUnrecognised + if inputBuffer.Buffer == ".exit" { + fmt.Println("bye!") + os.Exit(0) + } else { + metacommand = MetaCommandUnrecognised + } + return metacommand +} + +func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareResults { + if strings.HasPrefix(inputBuffer.Buffer, "insert") { + argsAssigned, err := fmt.Sscanf(inputBuffer.Buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email) + if err != nil { + fmt.Println("error parsing insert statement") + fmt.Println(err.Error()) + } + if argsAssigned > 3 { + return PrepareSyntaxError + } + statement.stype = StatementInsert + return PrepareSuccess + } + if strings.HasPrefix(inputBuffer.Buffer, "select") { + statement.stype = StatementSelect + return PrepareSuccess + } + return PrepareUnrecognised +} + +func (statement *Statement) ExecuteStatement() { + switch statement.stype { + case StatementInsert: + fmt.Println("This is where the logic of insert statement should go") + case StatementSelect: + fmt.Println("This is where the logic of select statement should go") + } +} -- 2.54.0 From 1ec9de4b0b5717d6643c2ef1f967becedffe3c27 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Mon, 20 Jul 2026 00:38:35 +0530 Subject: [PATCH 12/18] chor: minor readme change --- readme.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/readme.md b/readme.md index aa9a01e..09a036a 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,23 @@ ### Godb A SQLite like database made in golang + +Inorder to run this locally, after cloning this repo run `go mod tidy` + +Then use the appropriate `make` command listed below + +Run + +```make run``` + +Build + +```make build``` + +Start + +```make start``` + +Cleanup + +```make clean``` -- 2.54.0 From b2a61c66e49aef16e00077a4b6dc39ad6258d1ba Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Tue, 21 Jul 2026 23:22:15 +0530 Subject: [PATCH 13/18] feat: added a struct for table --- internals/repl/repl.go | 3 ++- internals/vm/vm.go | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/internals/repl/repl.go b/internals/repl/repl.go index b6cccb7..49ab463 100644 --- a/internals/repl/repl.go +++ b/internals/repl/repl.go @@ -16,6 +16,7 @@ func Run() { inputBuffer := NewInputBuffer() statement := NewStatement() + table := NewTable() for { PrintConsole() inputBuffer.ReadConsole() @@ -37,7 +38,7 @@ func Run() { continue } - statement.ExecuteStatement() + statement.ExecuteStatement(table) fmt.Println("Executed") } diff --git a/internals/vm/vm.go b/internals/vm/vm.go index e7ff626..354baf4 100644 --- a/internals/vm/vm.go +++ b/internals/vm/vm.go @@ -41,10 +41,28 @@ type Statement struct { row row } +const ( + _pageSize = 4096 + maxPages = 100 +) + +type Table struct { + numOfRows uint + pages [maxPages]any +} + func NewStatement() *Statement { return &Statement{0, row{0, "", ""}} } +func NewTable() *Table { + newTable := Table{numOfRows: 0} + for i := range maxPages { + newTable.pages[i] = nil + } + return &newTable +} + func DoMetaCommands(inputBuffer *InputBuffer) MetaCommandResults { metacommand := MetaCommandUnrecognised if inputBuffer.Buffer == ".exit" { @@ -76,7 +94,7 @@ func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareR return PrepareUnrecognised } -func (statement *Statement) ExecuteStatement() { +func (statement *Statement) ExecuteStatement(table *Table) { switch statement.stype { case StatementInsert: fmt.Println("This is where the logic of insert statement should go") -- 2.54.0 From c719848c251522127949edde691919073f7a542a Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Wed, 22 Jul 2026 01:46:52 +0530 Subject: [PATCH 14/18] feat: added functionality for insert and select --- internals/repl/repl.go | 8 +++- internals/vm/vm.go | 92 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/internals/repl/repl.go b/internals/repl/repl.go index 49ab463..173a2a3 100644 --- a/internals/repl/repl.go +++ b/internals/repl/repl.go @@ -38,8 +38,12 @@ func Run() { continue } - statement.ExecuteStatement(table) - fmt.Println("Executed") + switch statement.ExecuteStatement(table) { + case ExecuteSuccess: + fmt.Println("Executed.") + case ExecuteTableFull: + fmt.Println("Error: Table is full") + } } } diff --git a/internals/vm/vm.go b/internals/vm/vm.go index 354baf4..794d4ae 100644 --- a/internals/vm/vm.go +++ b/internals/vm/vm.go @@ -1,6 +1,7 @@ package vm import ( + "encoding/binary" "fmt" "os" "strings" @@ -30,6 +31,29 @@ const ( StatementInsert ) +const ( + idSize = 8 + usernameSize = 32 + emailSize = 255 + + idOffset = 0 + usernameOffset = idOffset + idSize + emailOffset = usernameOffset + usernameSize + + pageSize = 4096 + maxPages = 100 + rowSize = idSize + usernameSize + emailSize + rowsPerPage = pageSize / rowSize + maxRows = rowsPerPage * maxPages +) + +type ExecuteResult int + +const ( + ExecuteSuccess ExecuteResult = iota + ExecuteTableFull +) + type row struct { id uint username string @@ -41,11 +65,6 @@ type Statement struct { row row } -const ( - _pageSize = 4096 - maxPages = 100 -) - type Table struct { numOfRows uint pages [maxPages]any @@ -63,6 +82,61 @@ func NewTable() *Table { return &newTable } +func rowToUse(table *Table, rowNo uint) []byte { + pageNo := rowNo / rowsPerPage + var page []byte + if table.pages[pageNo] == nil { + page = make([]byte, pageSize) + table.pages[pageNo] = page + } else { + i := table.pages[pageNo] + if t, ok := i.([]byte); !ok { + page = []byte(t) + } else { + page = t + } + } + rowOffset := rowNo % rowsPerPage + byteOffset := rowOffset * rowSize + return page[byteOffset : byteOffset+rowSize] +} + +func (row *row) seralizeRow(page []byte) { + binary.NativeEndian.PutUint64(page[idOffset:], uint64(row.id)) + + usernameBuff := page[usernameOffset : usernameOffset+usernameSize] + clear(usernameBuff) + copy(usernameBuff, row.username) + + emailBuff := page[emailOffset : emailOffset+emailSize] + clear(emailBuff) + copy(emailBuff, row.email) +} + +func (row *row) deSeralizeRow(page []byte) { + row.id = uint(binary.NativeEndian.Uint64(page[idOffset:idSize])) + row.username = string(page[usernameOffset : usernameOffset+usernameSize]) + row.email = string(page[emailOffset : emailOffset+emailSize]) +} + +func (statement *Statement) execInsert(table *Table) ExecuteResult { + if table.numOfRows >= maxRows { + return ExecuteTableFull + } + statement.row.seralizeRow(rowToUse(table, table.numOfRows)) + table.numOfRows += 1 + return ExecuteSuccess +} + +func (statement *Statement) execSelect(table *Table) ExecuteResult { + var row row + for i := range table.numOfRows { + row.deSeralizeRow(rowToUse(table, i)) + fmt.Printf("%v\n", row) + } + return ExecuteSuccess +} + func DoMetaCommands(inputBuffer *InputBuffer) MetaCommandResults { metacommand := MetaCommandUnrecognised if inputBuffer.Buffer == ".exit" { @@ -94,11 +168,13 @@ func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareR return PrepareUnrecognised } -func (statement *Statement) ExecuteStatement(table *Table) { +func (statement *Statement) ExecuteStatement(table *Table) ExecuteResult { + var result ExecuteResult switch statement.stype { case StatementInsert: - fmt.Println("This is where the logic of insert statement should go") + result = statement.execInsert(table) case StatementSelect: - fmt.Println("This is where the logic of select statement should go") + result = statement.execSelect(table) } + return result } -- 2.54.0 From e625b29d3cd62cda918610340d9f2de341073117 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Thu, 23 Jul 2026 23:40:10 +0530 Subject: [PATCH 15/18] feat: added functionality for insert and select --- internals/repl/repl.go | 3 ++- internals/vm/vm.go | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/internals/repl/repl.go b/internals/repl/repl.go index 173a2a3..7f60529 100644 --- a/internals/repl/repl.go +++ b/internals/repl/repl.go @@ -33,9 +33,10 @@ func Run() { switch statement.PrepareStatements(inputBuffer) { case PrepareSuccess: //todo + case PrepareSyntaxError: + fmt.Println("Syntax error, could not parse statement") case PrepareUnrecognised: fmt.Printf("Unrecognised keyword at the start of '%s'.\n", inputBuffer.Buffer) - continue } switch statement.ExecuteStatement(table) { diff --git a/internals/vm/vm.go b/internals/vm/vm.go index 794d4ae..86f7162 100644 --- a/internals/vm/vm.go +++ b/internals/vm/vm.go @@ -151,11 +151,11 @@ func DoMetaCommands(inputBuffer *InputBuffer) MetaCommandResults { func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareResults { if strings.HasPrefix(inputBuffer.Buffer, "insert") { argsAssigned, err := fmt.Sscanf(inputBuffer.Buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email) - if err != nil { - fmt.Println("error parsing insert statement") - fmt.Println(err.Error()) - } - if argsAssigned > 3 { + // if err != nil { + // fmt.Println("error parsing insert statement") + // fmt.Println(err.Error()) + // } + if err != nil || argsAssigned > 3 { return PrepareSyntaxError } statement.stype = StatementInsert -- 2.54.0 From 318295f677546b68ba31defd19a481f4fb483369 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Fri, 24 Jul 2026 23:59:41 +0530 Subject: [PATCH 16/18] feat: added unit tests --- internals/buffer/buffer_test.go | 23 +++++++++++++++++++++++ makefile | 12 ++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 internals/buffer/buffer_test.go diff --git a/internals/buffer/buffer_test.go b/internals/buffer/buffer_test.go new file mode 100644 index 0000000..097788a --- /dev/null +++ b/internals/buffer/buffer_test.go @@ -0,0 +1,23 @@ +package buffer + +import ( + "bufio" + "strings" + "testing" +) + +func TestReadConsole(t *testing.T) { + mockMessage := "Reading for Unit test of readconsole" + + inputBuffer := &InputBuffer{ + Buffer: "", + Reader: bufio.NewReader(strings.NewReader(mockMessage)), + } + + inputBuffer.ReadConsole() + + if inputBuffer.Buffer != mockMessage { + t.Errorf("ReadConsole() should be %q but is %q", mockMessage, inputBuffer.Buffer) + } + +} diff --git a/makefile b/makefile index dd6088a..7ac398b 100644 --- a/makefile +++ b/makefile @@ -9,10 +9,18 @@ build: start: @./tmp/${BINARY_NAME} +test-unit: + @go test ./internals/... -v + +test-inte: + @go test ./tests/... -v + +test: build test-unit test-inte + clean: @go clean @rm -rf ${BINARY_NAME} -reset: clean build +reset: clean build test clean -.PHONY: clean build +.PHONY: clean build test -- 2.54.0 From e666b26d3dc211dab66bca24454a66c385d6b559 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sat, 25 Jul 2026 01:04:58 +0530 Subject: [PATCH 17/18] feat: added first e2e test for insert and select --- tests/main_test.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tests/main_test.go diff --git a/tests/main_test.go b/tests/main_test.go new file mode 100644 index 0000000..6d4551d --- /dev/null +++ b/tests/main_test.go @@ -0,0 +1,85 @@ +package tests + +import ( + "bufio" + "fmt" + "io" + "os/exec" + "slices" + "strings" + "testing" +) + +func runner(commands []string, t *testing.T) []string { + executablePath := "../tmp/godb" + t.Logf("starting godb from %v\n", executablePath) + + executable := exec.Command(executablePath) + + results := make([]string, 0) + + stdin, err := executable.StdinPipe() + if err != nil { + t.Errorf("Failed to create stdin %q\n", err) + } + + stdout, err := executable.StdoutPipe() + if err != nil { + t.Errorf("Failed to create stdout %q\n", err) + } + + if err := executable.Start(); err != nil { + t.Errorf("Failed to start executable %q\n", err) + } + + go func() { + defer stdin.Close() + for _, c := range commands { + _, _ = io.WriteString(stdin, c+"\n") + } + }() + + reader := bufio.NewReader(stdout) + for { + line, err := reader.ReadString('\n') + if line != "" { + formattedline := fmt.Sprintf("%v", strings.Trim(strings.ReplaceAll(line, "\x00", ""), "\n")) + results = append(results, formattedline) + } + if err != nil { + t.Logf("stoped reading due to %q\n", err) + break + } + + } + + if err := executable.Wait(); err != nil { + t.Logf("Error occured while waiting for cleanup %q\n", err) + } + + return results +} + +func TestMain(t *testing.T) { + commands := []string{ + "insert 1 user1 person1@example.com", + "select", + ".exit", + } + + expected := []string{ + "godb > Executed.", + "godb > {1 user1 person1@example.com}", + "Executed.", + "godb > bye!", + } + results := runner(commands, t) + + for i := range results { + results[i] = strings.ReplaceAll(results[i], "\n", "") + } + + if !slices.Equal(expected, results) { + t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results) + } +} -- 2.54.0 From 23fd1d5a1e6fbbbc04808f0017a98842a4ce7002 Mon Sep 17 00:00:00 2001 From: TakshakRamteke Date: Sat, 25 Jul 2026 13:28:00 +0530 Subject: [PATCH 18/18] feat: added licence --- licence | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 licence diff --git a/licence b/licence new file mode 100644 index 0000000..43f07fe --- /dev/null +++ b/licence @@ -0,0 +1,19 @@ +MIT License + +Copyright (c) 2026 TakshakRamteke + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + -- 2.54.0