From 06e6c9bcd3b168d1190175a386a3bb226223add7 Mon Sep 17 00:00:00 2001 From: vahagz Date: Mon, 5 Feb 2024 18:12:00 +0400 Subject: [PATCH] prepare space feature --- client/main.go | 8 +++- pkg/bptree | 2 +- pkg/index/index.go | 4 +- pkg/table/table.go | 6 +-- services/executor/dml_prepare.go | 39 ++++++++++++++++ services/executor/executor.go | 13 +++--- services/parser/kwords/kwords.go | 4 ++ services/parser/parser.go | 2 +- services/parser/query/dml/dml.go | 11 +++-- services/parser/query/dml/prepare.go | 70 ++++++++++++++++++++++++++++ services/parser/query/query.go | 1 + 11 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 services/executor/dml_prepare.go create mode 100644 services/parser/query/dml/prepare.go diff --git a/client/main.go b/client/main.go index e431e0f..33b17bc 100644 --- a/client/main.go +++ b/client/main.go @@ -95,6 +95,12 @@ func main() { // for rows.Next() { } // fmt.Printf("[create] %v\n", time.Since(t)) + // t = time.Now() + // rows, err = client.Query([]byte(`PREPARE TABLE testtable ROWS 1000000;`)) + // exitIfErr(errors.Wrap(err, "query failed")) + // for rows.Next() { } + // fmt.Printf("[prepare] %v\n", time.Since(t)) + // t = time.Now() // var insertId int // setInterval(time.Second, func() { @@ -224,7 +230,7 @@ func main() { rows, err = client.Query([]byte(` SELECT id, firstname, lastname FROM testtable - WHERE_INDEX id id >= 1 AND id <= 1000000; + WHERE_INDEX id id >= 450000 AND id <= 460000; `)) exitIfErr(errors.Wrap(err, "query failed")) var ( diff --git a/pkg/bptree b/pkg/bptree index 5dce93e..9616b0a 160000 --- a/pkg/bptree +++ b/pkg/bptree @@ -1 +1 @@ -Subproject commit 5dce93e9521468fd56b2c03b8864cc3e2e255f41 +Subproject commit 9616b0a311b393b354bda6559371ecc76946f542 diff --git a/pkg/index/index.go b/pkg/index/index.go index 942d6f4..757c781 100644 --- a/pkg/index/index.go +++ b/pkg/index/index.go @@ -53,8 +53,8 @@ func (i *Index) Options() bptree.Options { return i.tree.Options() } -func (i *Index) PrepareSpace(size uint32) { - i.tree.PrepareSpace(size) +func (i *Index) PrepareSpace(rows int) { + i.tree.PrepareSpace(uint32(rows * int(i.tree.HeapSize() / i.tree.Count()))) } func (i *Index) Close() error { diff --git a/pkg/table/table.go b/pkg/table/table.go index b4b504e..cb618e0 100644 --- a/pkg/table/table.go +++ b/pkg/table/table.go @@ -83,10 +83,10 @@ func (t* Table) Column(name string) *column.Column { return t.meta.ColumnsMap[name] } -func (t* Table) PrepareSpace(size uint32) { - t.df.PrepareSpace(size) +func (t* Table) PrepareSpace(rows int) { + t.df.PrepareSpace(uint32(rows * int(t.df.HeapSize() / t.df.Count()))) for _, i := range t.indexes { - i.PrepareSpace(size) + i.PrepareSpace(rows) } } diff --git a/services/executor/dml_prepare.go b/services/executor/dml_prepare.go new file mode 100644 index 0000000..e1b4301 --- /dev/null +++ b/services/executor/dml_prepare.go @@ -0,0 +1,39 @@ +package executor + +import ( + "fmt" + "io" + + "go-dbms/services/parser/query/dml" + + "github.com/pkg/errors" +) + +func (es *ExecutorServiceT) dmlPrepareValidate(q *dml.QueryPrepare) error { + _, ok := es.tables[q.Table] + if !ok { + return fmt.Errorf("table not found: '%s'", q.Table) + } + + if q.Rows < 0 { + return fmt.Errorf("rows must be positive integer") + } + + return nil +} + +func (es *ExecutorServiceT) dmlPrepare(q *dml.QueryPrepare) (io.WriterTo, error) { + if err := es.dmlPrepareValidate(q); err != nil { + return nil, errors.Wrapf(err, "validation error") + } + + p := newPipe(EOS) + go func() { + es.tables[q.Table].PrepareSpace(q.Rows) + if _, err := p.Write(EOS); err != nil { + panic(err) + } + }() + + return p, nil +} diff --git a/services/executor/executor.go b/services/executor/executor.go index 15d3544..0b6322f 100644 --- a/services/executor/executor.go +++ b/services/executor/executor.go @@ -48,12 +48,13 @@ func New(dataPath string) (*ExecutorServiceT, error) { func (es *ExecutorServiceT) Exec(q query.Querier) (io.WriterTo, error) { switch q.GetType() { - case query.CREATE: return es.ddlCreate(q.(create.Creater)) - case query.DELETE: return es.dmlDelete(q.(*dml.QueryDelete)) - case query.INSERT: return es.dmlInsert(q.(*dml.QueryInsert)) - case query.SELECT: return es.dmlSelect(q.(*dml.QuerySelect)) - case query.UPDATE: return es.dmlUpdate(q.(*dml.QueryUpdate)) - default: panic(fmt.Errorf("invalid query type: '%s'", q.GetType())) + case query.CREATE: return es.ddlCreate(q.(create.Creater)) + case query.DELETE: return es.dmlDelete(q.(*dml.QueryDelete)) + case query.INSERT: return es.dmlInsert(q.(*dml.QueryInsert)) + case query.SELECT: return es.dmlSelect(q.(*dml.QuerySelect)) + case query.UPDATE: return es.dmlUpdate(q.(*dml.QueryUpdate)) + case query.PREPARE: return es.dmlPrepare(q.(*dml.QueryPrepare)) + default: panic(fmt.Errorf("invalid query type: '%s'", q.GetType())) } } diff --git a/services/parser/kwords/kwords.go b/services/parser/kwords/kwords.go index f76ec60..c2185ae 100644 --- a/services/parser/kwords/kwords.go +++ b/services/parser/kwords/kwords.go @@ -17,6 +17,10 @@ var KeyWords = map[string]struct{}{ "UPDATE": {}, "SET": {}, + + "PREPARE": {}, + "TABLE": {}, + "ROWS": {}, } var IndexOperators = map[string]struct{}{ diff --git a/services/parser/parser.go b/services/parser/parser.go index cbe35d6..4de3a67 100644 --- a/services/parser/parser.go +++ b/services/parser/parser.go @@ -31,7 +31,7 @@ func (ps *ParserServiceT) ParseQuery(data []byte) (query.Querier, error) { switch qt { case query.CREATE, query.DROP: return ddl.Parse(s, qt) - case query.DELETE, query.INSERT, query.SELECT, query.UPDATE: + case query.DELETE, query.INSERT, query.SELECT, query.UPDATE, query.PREPARE: return dml.Parse(s, qt) } } diff --git a/services/parser/query/dml/dml.go b/services/parser/query/dml/dml.go index 3b9ea19..1ae8c76 100644 --- a/services/parser/query/dml/dml.go +++ b/services/parser/query/dml/dml.go @@ -15,11 +15,12 @@ func Parse(s *scanner.Scanner, queryType query.QueryType) (query.Querier, error) var q query.Querier switch queryType { - case query.DELETE: q = &QueryDelete{} - case query.INSERT: q = &QueryInsert{} - case query.SELECT: q = &QuerySelect{} - case query.UPDATE: q = &QueryUpdate{} - default: return nil, errors.New(fmt.Sprintf("unsupported query type: '%s'", queryType)) + case query.DELETE: q = &QueryDelete{} + case query.INSERT: q = &QueryInsert{} + case query.SELECT: q = &QuerySelect{} + case query.UPDATE: q = &QueryUpdate{} + case query.PREPARE: q = &QueryPrepare{} + default: return nil, errors.New(fmt.Sprintf("unsupported query type: '%s'", queryType)) } return q, q.Parse(s) diff --git a/services/parser/query/dml/prepare.go b/services/parser/query/dml/prepare.go new file mode 100644 index 0000000..86f44fb --- /dev/null +++ b/services/parser/query/dml/prepare.go @@ -0,0 +1,70 @@ +package dml + +import ( + "strconv" + "text/scanner" + + "go-dbms/services/parser/errors" + "go-dbms/services/parser/kwords" + "go-dbms/services/parser/query" +) + +type QueryPrepare struct { + query.Query + DB string + Table string + Rows int +} + + +func (qp *QueryPrepare) Parse(s *scanner.Scanner) (err error) { + defer func () { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + panic(r) + } + } + }() + + qp.Type = query.PREPARE + + qp.parseTable(s) + qp.parseRows(s) + + return nil +} + +func (qp *QueryPrepare) parseTable(s *scanner.Scanner) { + s.Scan() + if s.TokenText() != "TABLE" { + panic(errors.ErrSyntax) + } + + s.Scan() + qp.Table = s.TokenText() + if _, isKW := kwords.KeyWords[qp.Table]; isKW { + panic(errors.ErrSyntax) + } +} + +func (qp *QueryPrepare) parseRows(s *scanner.Scanner) { + s.Scan() + if s.TokenText() != "ROWS" { + panic(errors.ErrSyntax) + } + + s.Scan() + rows, err := strconv.Atoi(s.TokenText()) + if err != nil { + panic(err) + } + + qp.Rows = rows + + s.Scan() + if s.TokenText() != ";" { + panic(errors.ErrSyntax) + } +} diff --git a/services/parser/query/query.go b/services/parser/query/query.go index 9f55664..f334318 100644 --- a/services/parser/query/query.go +++ b/services/parser/query/query.go @@ -14,6 +14,7 @@ const ( DROP QueryType = "DROP" TRUNCATE QueryType = "TRUNCATE" RENAME QueryType = "RENAME" + PREPARE QueryType = "PREPARE" ) type Querier interface {