Compare commits
1
Commits
dev
..
28acfd8d7e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28acfd8d7e |
@@ -37,10 +37,6 @@ func Run() {
|
|||||||
fmt.Println("Syntax error, could not parse statement")
|
fmt.Println("Syntax error, could not parse statement")
|
||||||
case PrepareUnrecognised:
|
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)
|
||||||
case PrepareTooManyArgs:
|
|
||||||
fmt.Printf("Too many arguments in the insert statement '%v'\n", inputBuffer.Buffer)
|
|
||||||
case PrepareValueTooLong:
|
|
||||||
fmt.Printf("Values for either username or email are too long\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch statement.ExecuteStatement(table) {
|
switch statement.ExecuteStatement(table) {
|
||||||
@@ -48,8 +44,6 @@ func Run() {
|
|||||||
fmt.Println("Executed.")
|
fmt.Println("Executed.")
|
||||||
case ExecuteTableFull:
|
case ExecuteTableFull:
|
||||||
fmt.Println("Error: Table is full")
|
fmt.Println("Error: Table is full")
|
||||||
case ExecuteFail:
|
|
||||||
//todo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package repl
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
// in trying to implement unit test for Run we would kinda endup implementing a test which would be somewhat similar to
|
|
||||||
// e2e test which we already have in place under ../tests directory
|
|
||||||
// PrintConsole simply prints godb > to the console, so not sure if we would need a test for that
|
|
||||||
// Skipping unit test for this, while marking it as a TODO
|
|
||||||
+11
-45
@@ -4,8 +4,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
. "godb/internals/buffer"
|
. "godb/internals/buffer"
|
||||||
@@ -23,8 +21,6 @@ type PrepareResults int
|
|||||||
const (
|
const (
|
||||||
PrepareSuccess PrepareResults = iota
|
PrepareSuccess PrepareResults = iota
|
||||||
PrepareUnrecognised
|
PrepareUnrecognised
|
||||||
PrepareTooManyArgs
|
|
||||||
PrepareValueTooLong
|
|
||||||
PrepareSyntaxError
|
PrepareSyntaxError
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +29,6 @@ type statementType int
|
|||||||
const (
|
const (
|
||||||
StatementSelect statementType = iota
|
StatementSelect statementType = iota
|
||||||
StatementInsert
|
StatementInsert
|
||||||
StatementUnrecognized
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -57,7 +52,6 @@ type ExecuteResult int
|
|||||||
const (
|
const (
|
||||||
ExecuteSuccess ExecuteResult = iota
|
ExecuteSuccess ExecuteResult = iota
|
||||||
ExecuteTableFull
|
ExecuteTableFull
|
||||||
ExecuteFail
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type row struct {
|
type row struct {
|
||||||
@@ -77,8 +71,7 @@ type Table struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewStatement() *Statement {
|
func NewStatement() *Statement {
|
||||||
statement := &Statement{}
|
return &Statement{0, row{0, "", ""}}
|
||||||
return statement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTable() *Table {
|
func NewTable() *Table {
|
||||||
@@ -155,47 +148,20 @@ func DoMetaCommands(inputBuffer *InputBuffer) MetaCommandResults {
|
|||||||
return metacommand
|
return metacommand
|
||||||
}
|
}
|
||||||
|
|
||||||
func (statement *Statement) perpareInsert(inputBuffer *InputBuffer) PrepareResults {
|
func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareResults {
|
||||||
inputs := strings.Split(inputBuffer.Buffer, " ")[1:]
|
if strings.HasPrefix(inputBuffer.Buffer, "insert") {
|
||||||
if len(inputs) < 3 {
|
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 err != nil || argsAssigned > 3 {
|
||||||
return PrepareSyntaxError
|
return PrepareSyntaxError
|
||||||
}
|
}
|
||||||
if len(inputs) > 3 {
|
|
||||||
return PrepareTooManyArgs
|
|
||||||
}
|
|
||||||
if slices.Contains(inputs, "null") || slices.Contains(inputs, "NULL") || slices.Contains(inputs, "Null") || slices.Contains(inputs, " ") {
|
|
||||||
return PrepareSyntaxError
|
|
||||||
}
|
|
||||||
|
|
||||||
tid, err := strconv.ParseUint(inputs[0], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
// fmt.Printf("supplied ID: %v isn't acceptable, please correct and retry\n", inputs[0])
|
|
||||||
return PrepareSyntaxError
|
|
||||||
}
|
|
||||||
id := uint(tid)
|
|
||||||
if len(inputs[1]) > usernameSize {
|
|
||||||
// fmt.Printf("%v is too long for username\n", inputs[1])
|
|
||||||
return PrepareValueTooLong
|
|
||||||
}
|
|
||||||
if len(inputs[2]) > emailSize {
|
|
||||||
// fmt.Printf("%v is too long for email\n", inputs[2])
|
|
||||||
return PrepareValueTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
statement.row.id = id
|
|
||||||
statement.row.username = inputs[1]
|
|
||||||
statement.row.email = inputs[2]
|
|
||||||
|
|
||||||
statement.stype = StatementInsert
|
statement.stype = StatementInsert
|
||||||
return PrepareSuccess
|
return PrepareSuccess
|
||||||
}
|
|
||||||
|
|
||||||
func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareResults {
|
|
||||||
statement.stype = StatementUnrecognized
|
|
||||||
if strings.HasPrefix(inputBuffer.Buffer, "insert") {
|
|
||||||
return statement.perpareInsert(inputBuffer)
|
|
||||||
}
|
}
|
||||||
if inputBuffer.Buffer == "select" {
|
if strings.HasPrefix(inputBuffer.Buffer, "select") {
|
||||||
statement.stype = StatementSelect
|
statement.stype = StatementSelect
|
||||||
return PrepareSuccess
|
return PrepareSuccess
|
||||||
}
|
}
|
||||||
@@ -203,7 +169,7 @@ func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareR
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (statement *Statement) ExecuteStatement(table *Table) ExecuteResult {
|
func (statement *Statement) ExecuteStatement(table *Table) ExecuteResult {
|
||||||
var result ExecuteResult = ExecuteFail
|
var result ExecuteResult
|
||||||
switch statement.stype {
|
switch statement.stype {
|
||||||
case StatementInsert:
|
case StatementInsert:
|
||||||
result = statement.execInsert(table)
|
result = statement.execInsert(table)
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"godb/internals/buffer"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var inputBuffer = buffer.NewInputBuffer()
|
|
||||||
|
|
||||||
var statement = NewStatement()
|
|
||||||
|
|
||||||
func TestVM(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
prepareStatusExpected PrepareResults
|
|
||||||
statementResultExpected statementType
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Testing Select Statement",
|
|
||||||
input: "select",
|
|
||||||
prepareStatusExpected: PrepareSuccess,
|
|
||||||
statementResultExpected: StatementSelect,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Testing Insert Statement",
|
|
||||||
input: "insert 0 hello hello.com",
|
|
||||||
prepareStatusExpected: PrepareSuccess,
|
|
||||||
statementResultExpected: StatementInsert,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Testing Unrecognized Statement",
|
|
||||||
input: "something",
|
|
||||||
prepareStatusExpected: PrepareUnrecognised,
|
|
||||||
statementResultExpected: StatementUnrecognized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Testing Syntax Errors",
|
|
||||||
input: "insert 0 hello",
|
|
||||||
prepareStatusExpected: PrepareSyntaxError,
|
|
||||||
statementResultExpected: StatementUnrecognized,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
inputBuffer.Buffer = test.input
|
|
||||||
result := statement.PrepareStatements(inputBuffer)
|
|
||||||
if result != test.prepareStatusExpected && statement.stype == test.statementResultExpected {
|
|
||||||
t.Errorf("Expected prepareResult and stype to be: (%v, %v), got: (%v,%v)\n", test.prepareStatusExpected, test.statementResultExpected, result, statement.stype)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,9 +21,3 @@ Start
|
|||||||
Cleanup
|
Cleanup
|
||||||
|
|
||||||
```make clean```
|
```make clean```
|
||||||
|
|
||||||
Test
|
|
||||||
|
|
||||||
```make test```
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
// implement benchmark test for the main loop
|
|
||||||
+16
-53
@@ -35,21 +35,15 @@ func runner(commands []string, t *testing.T) []string {
|
|||||||
go func() {
|
go func() {
|
||||||
defer stdin.Close()
|
defer stdin.Close()
|
||||||
for _, c := range commands {
|
for _, c := range commands {
|
||||||
_, err = io.WriteString(stdin, c+"\n")
|
_, _ = io.WriteString(stdin, c+"\n")
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to execute %v because of %q\n", c, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
reader := bufio.NewReader(stdout)
|
reader := bufio.NewReader(stdout)
|
||||||
for {
|
for {
|
||||||
// fmt.Printf("reading command outputs...\n")
|
|
||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
if line != "" {
|
if line != "" {
|
||||||
formattedline := fmt.Sprintf("%v", strings.Trim(strings.ReplaceAll(line, "\x00", ""), "\n"))
|
formattedline := fmt.Sprintf("%v", strings.Trim(strings.ReplaceAll(line, "\x00", ""), "\n"))
|
||||||
// fmt.Printf("formatting and appending result: %v\n", formattedline)
|
|
||||||
results = append(results, formattedline)
|
results = append(results, formattedline)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -66,57 +60,26 @@ func runner(commands []string, t *testing.T) []string {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestE2E(t *testing.T) {
|
func TestMain(t *testing.T) {
|
||||||
tests := []struct {
|
commands := []string{
|
||||||
name string
|
"insert 1 user1 person1@example.com",
|
||||||
commands []string
|
"select",
|
||||||
expected []string
|
".exit",
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Test Main Loop",
|
|
||||||
commands: []string{"insert 1 user1 person1@example.com", "select", ".exit"},
|
|
||||||
expected: []string{"godb > Executed.", "godb > {1 user1 person1@example.com}", "Executed.", "godb > bye!"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Maximum Length For Values",
|
|
||||||
commands: []string{"insert 0 " + strings.Repeat("n", 32) + " " + strings.Repeat("n", 255), ".exit"},
|
|
||||||
expected: []string{"godb > Executed.", "godb > bye!"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Over Maximum Length For Values",
|
|
||||||
commands: []string{"insert 0 " + strings.Repeat("n", 42) + " " + strings.Repeat("n", 275), ".exit"},
|
|
||||||
expected: []string{"godb > Values for either username or email are too long", "godb > bye!"},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
expected := []string{
|
||||||
t.Run(test.name, func(t *testing.T) {
|
"godb > Executed.",
|
||||||
results := runner(test.commands, t)
|
"godb > {1 user1 person1@example.com}",
|
||||||
|
"Executed.",
|
||||||
|
"godb > bye!",
|
||||||
|
}
|
||||||
|
results := runner(commands, t)
|
||||||
|
|
||||||
for i := range results {
|
for i := range results {
|
||||||
results[i] = strings.ReplaceAll(results[i], "\n", "")
|
results[i] = strings.ReplaceAll(results[i], "\n", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slices.Equal(test.expected, results) {
|
if !slices.Equal(expected, results) {
|
||||||
t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", test.expected, results)
|
t.Errorf("Output Mismatch\nExpected: %v\nGot: %v\n", expected, results)
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTableFull(t *testing.T) {
|
|
||||||
maxRows := 1400
|
|
||||||
commands := make([]string, 0)
|
|
||||||
|
|
||||||
for i := range maxRows {
|
|
||||||
c := fmt.Sprintf("insert %d user%d person%d@example.com", i, i, i)
|
|
||||||
commands = append(commands, c)
|
|
||||||
}
|
|
||||||
commands = append(commands, ".exit")
|
|
||||||
|
|
||||||
results := runner(commands, t)
|
|
||||||
|
|
||||||
if results[1300] != "godb > Error: Table is full" {
|
|
||||||
t.Errorf("Our Tables max capacity is 1300 rows, but we're able to insert %v\n", len(results))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user