From 971302ecf56d5e8038f73db24428e9c4d637aa33 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Fri, 11 Jan 2019 11:17:43 +0000 Subject: [PATCH] Implement SPLIT keyword in SELECT statement --- db/iterator.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ sql/ast.go | 1 + sql/select.go | 24 ++++++++++++++++++++++++ sql/string.go | 3 ++- sql/tokens.go | 2 ++ 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/db/iterator.go b/db/iterator.go index 40d30056..bba2d68b 100644 --- a/db/iterator.go +++ b/db/iterator.go @@ -54,6 +54,7 @@ type iterator struct { expr sql.Fields what sql.Exprs cond sql.Expr + split sql.Idents group sql.Groups order sql.Orders limit int @@ -133,6 +134,7 @@ func (i *iterator) Close() { i.expr = nil i.what = nil i.cond = nil + i.split = nil i.group = nil i.order = nil i.limit = -1 @@ -149,8 +151,10 @@ func (i *iterator) setupState(ctx context.Context) { i.expr = nil i.what = nil i.cond = nil + i.split = nil i.group = nil i.order = nil + i.split = nil i.limit = -1 i.start = -1 i.versn = math.MaxInt64 @@ -160,6 +164,7 @@ func (i *iterator) setupState(ctx context.Context) { i.expr = stm.Expr i.what = stm.What i.cond = stm.Cond + i.split = stm.Split i.group = stm.Group i.order = stm.Order i.tasks = stm.Parallel @@ -865,6 +870,10 @@ func (i *iterator) Yield(ctx context.Context) (out []interface{}, err error) { } } + if len(i.split) > 0 { + i.res = i.Split(ctx, i.res) + } + if len(i.group) > 0 { i.res = i.Group(ctx, i.res) } @@ -887,6 +896,42 @@ func (i *iterator) Yield(ctx context.Context) (out []interface{}, err error) { } +func (i *iterator) Split(ctx context.Context, arr []interface{}) (out []interface{}) { + + for _, s := range i.split { + + out = make([]interface{}, 0) + + for _, a := range arr { + + doc := data.Consume(a) + + pth := make([]string, 0) + + switch doc.Get(s.VA).Data().(type) { + case []interface{}: + pth = append(pth, s.VA, "*") + default: + pth = append(pth, s.VA) + } + + doc.Walk(func(key string, val interface{}, exi bool) error { + doc := doc.Copy() + doc.Set(val, s.VA) + out = append(out, doc.Data()) + return nil + }, pth...) + + } + + arr = out + + } + + return out + +} + func (i *iterator) Group(ctx context.Context, arr []interface{}) (out []interface{}) { var grp []*groupable diff --git a/sql/ast.go b/sql/ast.go index ac733151..fa50155d 100644 --- a/sql/ast.go +++ b/sql/ast.go @@ -170,6 +170,7 @@ type SelectStatement struct { Expr Fields What Exprs Cond Expr + Split Idents Group Groups Order Orders Limit Expr diff --git a/sql/select.go b/sql/select.go index 7d1c403f..a89e4fda 100644 --- a/sql/select.go +++ b/sql/select.go @@ -39,6 +39,10 @@ func (p *parser) parseSelectStatement() (stmt *SelectStatement, err error) { return nil, err } + if stmt.Split, err = p.parseSplit(); err != nil { + return nil, err + } + if stmt.Group, err = p.parseGroup(); err != nil { return nil, err } @@ -142,6 +146,26 @@ func (p *parser) parseFields() (mul Fields, err error) { } +func (p *parser) parseSplit() (Idents, error) { + + // The next token that we expect to see is a + // SPLIT token, and if we don't find one then + // return nil, with no error. + + if _, _, exi := p.mightBe(SPLIT); !exi { + return nil, nil + } + + // We don't need to have a ON token, but we + // allow it so that the SQL query would read + // better when compared to english. + + _, _, _ = p.mightBe(ON) + + return p.parseIdioms() + +} + func (p *parser) parseGroup() (mul Groups, err error) { // The next token that we expect to see is a diff --git a/sql/string.go b/sql/string.go index 82a993f0..e0753dcc 100644 --- a/sql/string.go +++ b/sql/string.go @@ -225,10 +225,11 @@ func (this KillStatement) String() string { } func (this SelectStatement) String() string { - return print("SELECT %v FROM %v%v%v%v%v%v%v%v%v", + return print("SELECT %v FROM %v%v%v%v%v%v%v%v%v%v", this.Expr, this.What, maybe(this.Cond != nil, print(" WHERE %v", this.Cond)), + maybe(this.Split != nil, print(" SPLIT %v", this.Split)), this.Group, this.Order, maybe(this.Limit != nil, print(" LIMIT %v", this.Limit)), diff --git a/sql/tokens.go b/sql/tokens.go index 1bc12716..eeb486e2 100644 --- a/sql/tokens.go +++ b/sql/tokens.go @@ -183,6 +183,7 @@ const ( SIGNIN SIGNUP SOMECONTAINEDIN + SPLIT START TABLE THEN @@ -360,6 +361,7 @@ var tokens = [...]string{ SIGNIN: "SIGNIN", SIGNUP: "SIGNUP", SOMECONTAINEDIN: "SOMECONTAINEDIN", + SPLIT: "SPLIT", START: "START", TABLE: "TABLE", THEN: "THEN",