From b70bc9b2c836751e1905d9043c18f9e5f8238a85 Mon Sep 17 00:00:00 2001 From: minodisk Date: Fri, 24 Feb 2017 19:23:54 +0900 Subject: [PATCH 1/8] Rename --- builder/builder.go | 4 +-- statement/nodeizer.go | 44 ++++++++++++++++++++++++++ statement/traverser.go | 50 +----------------------------- token/{printer.go => generator.go} | 6 ++-- 4 files changed, 49 insertions(+), 55 deletions(-) create mode 100644 statement/nodeizer.go rename token/{printer.go => generator.go} (74%) diff --git a/builder/builder.go b/builder/builder.go index 88af1ff..e404fd5 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -33,7 +33,7 @@ func (b Builder) Build(stmt statement.Statement) (string, []interface{}) { if n, ok := stmt.(statement.Nodeizer); ok { root := statement.Traverse(n) tokenizers, values := statement.Nodize(root) - sql := token.Print(tokenizers.Tokenize(0), b.Format) + sql := token.Generate(tokenizers.Tokenize(0), b.Format) if len(values) == 0 { values = nil } @@ -41,7 +41,7 @@ func (b Builder) Build(stmt statement.Statement) (string, []interface{}) { } tokenizer, values := statement.Nodeize(stmt) - query := token.Print(tokenizer.Tokenize(0), b.Format) + query := token.Generate(tokenizer.Tokenize(0), b.Format) if len(values) == 0 { values = nil } diff --git a/statement/nodeizer.go b/statement/nodeizer.go new file mode 100644 index 0000000..1a58112 --- /dev/null +++ b/statement/nodeizer.go @@ -0,0 +1,44 @@ +package statement + +import "github.com/minodisk/sqlabble/tokenizer" + +type Nodeizer interface { + nodeize() (tokenizer.Tokenizer, []interface{}) +} + +func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { + var tokenizers tokenizer.Tokenizers + values := []interface{}{} + + if childer, ok := n.(Childer); ok { + if child := childer.child(); child != nil { + t1, vals1 := n.nodeize() + t2, vals2 := Nodize(child) + first, _ := t1.FirstLine() + _, last := t1.LastLine() + t12 := tokenizer. + NewContainer(first). + SetMiddle(t2.Prefix(childer.separator()...)). + SetLast(last) + + tokenizers = tokenizer.NewTokenizers(t12) + values = append(append(values, vals1...), vals2...) + } + } + + if tokenizers == nil { + t, vals := n.nodeize() + tokenizers = tokenizer.NewTokenizers(t) + values = vals + } + + if nexter, ok := n.(Nexter); ok { + if next := nexter.next(); next != nil { + ts, vals := Nodize(next) + tokenizers = append(tokenizers, ts...) + values = append(values, vals...) + } + } + + return tokenizers, values +} diff --git a/statement/traverser.go b/statement/traverser.go index d6611cc..9868e51 100644 --- a/statement/traverser.go +++ b/statement/traverser.go @@ -1,9 +1,6 @@ package statement -import ( - "github.com/minodisk/sqlabble/token" - "github.com/minodisk/sqlabble/tokenizer" -) +import "github.com/minodisk/sqlabble/token" func Link(n1, n2 Nodeizer) { switch t1 := n1.(type) { @@ -22,10 +19,6 @@ func Link(n1, n2 Nodeizer) { } } -type Nodeizer interface { - nodeize() (tokenizer.Tokenizer, []interface{}) -} - type Childer interface { Nodeizer child() Parenter @@ -122,44 +115,3 @@ func Traverse(t Nodeizer) Nodeizer { } return t } - -func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { - if n == nil { - return nil, nil - } - - var tokenizers tokenizer.Tokenizers - values := []interface{}{} - - if childer, ok := n.(Childer); ok { - if child := childer.child(); child != nil { - t1, vals1 := n.nodeize() - t2, vals2 := Nodize(child) - first, _ := t1.FirstLine() - _, last := t1.LastLine() - t12 := tokenizer. - NewContainer(first). - SetMiddle(t2.Prefix(childer.separator()...)). - SetLast(last) - - tokenizers = tokenizer.NewTokenizers(t12) - values = append(append(values, vals1...), vals2...) - } - } - - if tokenizers == nil { - t, vals := n.nodeize() - tokenizers = tokenizer.NewTokenizers(t) - values = vals - } - - if nexter, ok := n.(Nexter); ok { - if next := nexter.next(); next != nil { - ts, vals := Nodize(next) - tokenizers = append(tokenizers, ts...) - values = append(values, vals...) - } - } - - return tokenizers, values -} diff --git a/token/printer.go b/token/generator.go similarity index 74% rename from token/printer.go rename to token/generator.go index c6189e3..9b36ffb 100644 --- a/token/printer.go +++ b/token/generator.go @@ -1,9 +1,7 @@ package token -// Print converts the tokens to text according to format. -// Print behaves in contrast to the scan in -// the implementation of a parser. -func Print(tokens Tokens, format Format) string { +// Generate converts the tokens to text according to format. +func Generate(tokens Tokens, format Format) string { if format.IsBreaking { return tokens.String(format) } From 29581ece21dfa81edc34d4950bf72b00cfbd56ad Mon Sep 17 00:00:00 2001 From: minodisk Date: Fri, 24 Feb 2017 21:15:15 +0900 Subject: [PATCH 2/8] Use traverser in clause and subquery --- statement/case_searched.go | 2 +- statement/case_simple.go | 2 +- statement/delete.go | 22 ++++------ statement/from.go | 33 ++++++--------- statement/from_test.go | 7 ---- statement/group_by.go | 52 +++++++++++------------- statement/group_by_test.go | 7 ---- statement/having.go | 37 +++++++---------- statement/having_test.go | 7 ---- statement/limit.go | 21 ++++------ statement/limit_test.go | 7 ---- statement/nodeizer.go | 71 ++++++++++++++++++++++----------- statement/offset.go | 14 ++----- statement/offset_test.go | 7 ---- statement/order_by.go | 27 +++++-------- statement/select.go | 23 ++++------- statement/select_test.go | 7 ---- statement/set.go | 25 +++++------- statement/set_operation.go | 46 ++++++++++----------- statement/set_operation_test.go | 10 ----- statement/subquery.go | 2 +- statement/traverser.go | 31 ++++++-------- statement/update.go | 24 ++++------- statement/where.go | 45 +++++++++------------ statement/where_test.go | 7 ---- 25 files changed, 207 insertions(+), 329 deletions(-) diff --git a/statement/case_searched.go b/statement/case_searched.go index 1a630fe..798efbb 100644 --- a/statement/case_searched.go +++ b/statement/case_searched.go @@ -16,7 +16,7 @@ func NewSearchedCase() *SearchedCase { func (c *SearchedCase) When(condition ComparisonOrLogicalOperation) *SearchedWhen { w := NewSearchedWhen(condition) - Link(c, w) + Contract(c, w) return w } diff --git a/statement/case_simple.go b/statement/case_simple.go index 5786c09..2289787 100644 --- a/statement/case_simple.go +++ b/statement/case_simple.go @@ -19,7 +19,7 @@ func NewSimpleCase(param ValOrColOrFuncOrSub) *SimpleCase { func (c *SimpleCase) When(param Param) *SimpleWhen { w := NewSimpleWhen(param) - Link(c, w) + Contract(c, w) return w } diff --git a/statement/delete.go b/statement/delete.go index 6146a2b..0e64247 100644 --- a/statement/delete.go +++ b/statement/delete.go @@ -6,28 +6,22 @@ import ( "github.com/minodisk/sqlabble/tokenizer" ) -type Delete struct{} +type Delete struct { + Prev +} -func NewDelete() Delete { - return Delete{} +func NewDelete() *Delete { + return &Delete{} } -func (d Delete) From(t Table) From { +func (d *Delete) From(t Table) *From { f := NewFrom(t) - f.prev = d + Link(d, f) return f } -func (d Delete) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(d) -} - -func (d Delete) self() (tokenizer.Tokenizer, []interface{}) { +func (d *Delete) nodeize() (tokenizer.Tokenizer, []interface{}) { return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.Delete)), ), nil } - -func (d Delete) previous() Clause { - return nil -} diff --git a/statement/from.go b/statement/from.go index 8d84cba..1b315a6 100644 --- a/statement/from.go +++ b/statement/from.go @@ -7,45 +7,42 @@ import ( ) type From struct { - prev Clause + Prev + Next table TableOrAliasOrJoiner } -func NewFrom(table TableOrAliasOrJoiner) From { - return From{ +func NewFrom(table TableOrAliasOrJoiner) *From { + return &From{ table: table, } } -func (f From) Where(op ComparisonOrLogicalOperation) Where { +func (f *From) Where(op ComparisonOrLogicalOperation) *Where { w := NewWhere(op) - w.prev = f + Link(f, w) return w } -func (f From) GroupBy(col Column, columns ...Column) GroupBy { +func (f *From) GroupBy(col Column, columns ...Column) *GroupBy { g := NewGroupBy(col, columns...) - g.prev = f + Link(f, g) return g } -func (f From) OrderBy(orders ...Order) OrderBy { +func (f *From) OrderBy(orders ...Order) *OrderBy { o := NewOrderBy(orders...) - o.prev = f + Link(f, o) return o } -func (f From) Limit(count int) Limit { +func (f *From) Limit(count int) *Limit { l := NewLimit(count) - l.prev = f + Link(f, l) return l } -func (f From) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(f) -} - -func (f From) self() (tokenizer.Tokenizer, []interface{}) { +func (f *From) nodeize() (tokenizer.Tokenizer, []interface{}) { middle, values := f.table.nodeize() return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.From)), @@ -53,7 +50,3 @@ func (f From) self() (tokenizer.Tokenizer, []interface{}) { middle, ), values } - -func (f From) previous() Clause { - return f.prev -} diff --git a/statement/from_test.go b/statement/from_test.go index 76bee04..26c32dc 100644 --- a/statement/from_test.go +++ b/statement/from_test.go @@ -9,13 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestFromType(t *testing.T) { - f := statement.From{} - if _, ok := interface{}(f).(statement.Clause); !ok { - t.Errorf("%T should implement statement.Clause", f) - } -} - func TestFromSQL(t *testing.T) { for i, c := range []struct { statement statement.Statement diff --git a/statement/group_by.go b/statement/group_by.go index b6a6cd5..757a7a8 100644 --- a/statement/group_by.go +++ b/statement/group_by.go @@ -7,21 +7,37 @@ import ( ) type GroupBy struct { - prev Clause + Prev + Next columns []Column } -func NewGroupBy(column Column, columns ...Column) GroupBy { - return GroupBy{ - columns: append([]Column{column}, columns...), +func NewGroupBy(column Column, columns ...Column) *GroupBy { + cols := append([]Column{column}, columns...) + return &GroupBy{ + columns: cols, } } -func (g GroupBy) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(g) +func (g *GroupBy) Having(operation ComparisonOrLogicalOperation) *Having { + l := NewHaving(operation) + Link(g, l) + return l +} + +func (g *GroupBy) OrderBy(orders ...Order) *OrderBy { + o := NewOrderBy(orders...) + Link(g, o) + return o } -func (g GroupBy) self() (tokenizer.Tokenizer, []interface{}) { +func (g *GroupBy) Limit(count int) *Limit { + l := NewLimit(count) + Link(g, l) + return l +} + +func (g *GroupBy) nodeize() (tokenizer.Tokenizer, []interface{}) { ts := make(tokenizer.Tokenizers, len(g.columns)) values := []interface{}{} for i, c := range g.columns { @@ -38,25 +54,3 @@ func (g GroupBy) self() (tokenizer.Tokenizer, []interface{}) { ), ), values } - -func (g GroupBy) previous() Clause { - return g.prev -} - -func (g GroupBy) Having(operation ComparisonOrLogicalOperation) Having { - l := NewHaving(operation) - l.prev = g - return l -} - -func (g GroupBy) OrderBy(orders ...Order) OrderBy { - o := NewOrderBy(orders...) - o.prev = g - return o -} - -func (g GroupBy) Limit(count int) Limit { - l := NewLimit(count) - l.prev = g - return l -} diff --git a/statement/group_by_test.go b/statement/group_by_test.go index e76973f..37994cd 100644 --- a/statement/group_by_test.go +++ b/statement/group_by_test.go @@ -9,13 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestGroupByType(t *testing.T) { - g := statement.GroupBy{} - if _, ok := interface{}(g).(statement.Clause); !ok { - t.Errorf("%T doesn't implement statement.Clause", g) - } -} - func TestGroupBySQL(t *testing.T) { for i, c := range []struct { statement statement.Statement diff --git a/statement/having.go b/statement/having.go index 0eeb04c..e8b81c6 100644 --- a/statement/having.go +++ b/statement/having.go @@ -7,21 +7,30 @@ import ( ) type Having struct { - prev Clause + Prev + Next operation ComparisonOrLogicalOperation } -func NewHaving(operation ComparisonOrLogicalOperation) Having { - return Having{ +func NewHaving(operation ComparisonOrLogicalOperation) *Having { + return &Having{ operation: operation, } } -func (h Having) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(h) +func (h *Having) OrderBy(orders ...Order) *OrderBy { + o := NewOrderBy(orders...) + Link(h, o) + return o +} + +func (h *Having) Limit(count int) *Limit { + l := NewLimit(count) + Link(h, l) + return l } -func (h Having) self() (tokenizer.Tokenizer, []interface{}) { +func (h *Having) nodeize() (tokenizer.Tokenizer, []interface{}) { middle, values := h.operation.nodeize() return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.Having)), @@ -29,19 +38,3 @@ func (h Having) self() (tokenizer.Tokenizer, []interface{}) { middle, ), values } - -func (h Having) previous() Clause { - return h.prev -} - -func (h Having) OrderBy(orders ...Order) OrderBy { - o := NewOrderBy(orders...) - o.prev = h - return o -} - -func (h Having) Limit(count int) Limit { - l := NewLimit(count) - l.prev = h - return l -} diff --git a/statement/having_test.go b/statement/having_test.go index fe187ea..a3e1a6b 100644 --- a/statement/having_test.go +++ b/statement/having_test.go @@ -9,13 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestHavingType(t *testing.T) { - h := statement.Having{} - if _, ok := interface{}(h).(statement.Clause); !ok { - t.Errorf("%T should implement statement.Clause", h) - } -} - func TestHavingSQL(t *testing.T) { for i, c := range []struct { statement statement.Statement diff --git a/statement/limit.go b/statement/limit.go index d376363..1c684d5 100644 --- a/statement/limit.go +++ b/statement/limit.go @@ -7,27 +7,24 @@ import ( ) type Limit struct { - prev Clause + Next + Prev count int } -func NewLimit(count int) Limit { - return Limit{ +func NewLimit(count int) *Limit { + return &Limit{ count: count, } } -func (l Limit) Offset(count int) Offset { +func (l *Limit) Offset(count int) *Offset { o := NewOffset(count) - o.prev = l + Link(l, o) return o } -func (l Limit) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(l) -} - -func (l Limit) self() (tokenizer.Tokenizer, []interface{}) { +func (l *Limit) nodeize() (tokenizer.Tokenizer, []interface{}) { line, values := tokenizer.ParamsToLine(l.count) return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.Limit)), @@ -35,7 +32,3 @@ func (l Limit) self() (tokenizer.Tokenizer, []interface{}) { line, ), values } - -func (l Limit) previous() Clause { - return l.prev -} diff --git a/statement/limit_test.go b/statement/limit_test.go index 37d6c19..5e47c6c 100644 --- a/statement/limit_test.go +++ b/statement/limit_test.go @@ -9,13 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestLimitType(t *testing.T) { - l := statement.Limit{} - if _, ok := interface{}(l).(statement.Clause); !ok { - t.Errorf("%T should implement statement.Clause", l) - } -} - func TestLimitSQL(t *testing.T) { for i, c := range []struct { statement statement.Statement diff --git a/statement/nodeizer.go b/statement/nodeizer.go index 1a58112..01cb715 100644 --- a/statement/nodeizer.go +++ b/statement/nodeizer.go @@ -10,34 +10,59 @@ func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { var tokenizers tokenizer.Tokenizers values := []interface{}{} - if childer, ok := n.(Childer); ok { - if child := childer.child(); child != nil { - t1, vals1 := n.nodeize() - t2, vals2 := Nodize(child) - first, _ := t1.FirstLine() - _, last := t1.LastLine() - t12 := tokenizer. - NewContainer(first). - SetMiddle(t2.Prefix(childer.separator()...)). - SetLast(last) - - tokenizers = tokenizer.NewTokenizers(t12) - values = append(append(values, vals1...), vals2...) + ns := []Nodeizer{} + + { + p := n + for { + prever, ok := p.(Prever) + if !ok { + break + } + p = prever.prev() + if p == nil { + break + } + ns = append([]Nodeizer{p}, ns...) } } - - if tokenizers == nil { - t, vals := n.nodeize() - tokenizers = tokenizer.NewTokenizers(t) - values = vals + ns = append(ns, n) + { + p := n + for { + nexter, ok := p.(Nexter) + if !ok { + break + } + p = nexter.next() + if p == nil { + break + } + ns = append(ns, p) + } } - if nexter, ok := n.(Nexter); ok { - if next := nexter.next(); next != nil { - ts, vals := Nodize(next) - tokenizers = append(tokenizers, ts...) - values = append(values, vals...) + for _, n := range ns { + if childer, ok := n.(Childer); ok { + for _, child := range childer.children() { + t1, vals1 := n.nodeize() + t2, vals2 := Nodize(child) + first, _ := t1.FirstLine() + _, last := t1.LastLine() + t12 := tokenizer. + NewContainer(first). + SetMiddle(t2.Prefix(childer.separator()...)). + SetLast(last) + + tokenizers = append(tokenizers, t12) + values = append(append(values, vals1...), vals2...) + } + continue } + + t1, values1 := n.nodeize() + tokenizers = append(tokenizers, t1) + values = append(values, values1...) } return tokenizers, values diff --git a/statement/offset.go b/statement/offset.go index 8901142..4996bc9 100644 --- a/statement/offset.go +++ b/statement/offset.go @@ -8,22 +8,18 @@ import ( // Offset skips specified rows before beginning to return rows. type Offset struct { - prev Clause + Next count int } // NewOffset return a new Offset. -func NewOffset(count int) Offset { - return Offset{ +func NewOffset(count int) *Offset { + return &Offset{ count: count, } } func (o Offset) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(o) -} - -func (o Offset) self() (tokenizer.Tokenizer, []interface{}) { line, values := tokenizer.ParamsToLine(o.count) return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.Offset)), @@ -31,7 +27,3 @@ func (o Offset) self() (tokenizer.Tokenizer, []interface{}) { line, ), values } - -func (o Offset) previous() Clause { - return o.prev -} diff --git a/statement/offset_test.go b/statement/offset_test.go index b1c17da..0060fbe 100644 --- a/statement/offset_test.go +++ b/statement/offset_test.go @@ -9,13 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestOffsetType(t *testing.T) { - o := statement.Offset{} - if _, ok := interface{}(o).(statement.Clause); !ok { - t.Errorf("%T should implement statement.Clause", o) - } -} - func TestOffset(t *testing.T) { for i, c := range []struct { statement statement.Statement diff --git a/statement/order_by.go b/statement/order_by.go index ba95d50..a233fb3 100644 --- a/statement/order_by.go +++ b/statement/order_by.go @@ -7,19 +7,24 @@ import ( ) type OrderBy struct { - prev Clause + Prev + Next orders []Order } -func NewOrderBy(os ...Order) OrderBy { - return OrderBy{orders: os} +func NewOrderBy(orders ...Order) *OrderBy { + return &OrderBy{ + orders: orders, + } } -func (o OrderBy) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(o) +func (o *OrderBy) Limit(count int) *Limit { + l := NewLimit(count) + Link(o, l) + return l } -func (o OrderBy) self() (tokenizer.Tokenizer, []interface{}) { +func (o *OrderBy) nodeize() (tokenizer.Tokenizer, []interface{}) { tokenizers := make(tokenizer.Tokenizers, len(o.orders)) values := []interface{}{} for i, o := range o.orders { @@ -33,13 +38,3 @@ func (o OrderBy) self() (tokenizer.Tokenizer, []interface{}) { tokenizers.Prefix(token.Comma, token.Space), ), values } - -func (o OrderBy) previous() Clause { - return o.prev -} - -func (o OrderBy) Limit(count int) Limit { - l := NewLimit(count) - l.prev = o - return l -} diff --git a/statement/select.go b/statement/select.go index 90637dd..df30bb1 100644 --- a/statement/select.go +++ b/statement/select.go @@ -7,35 +7,32 @@ import ( ) type Select struct { + Prev distinct bool columns []ColOrAliasOrFuncOrSub } -func NewSelect(columns ...ColOrAliasOrFuncOrSub) Select { - return Select{ +func NewSelect(columns ...ColOrAliasOrFuncOrSub) *Select { + return &Select{ distinct: false, columns: columns, } } -func NewSelectDistinct(columns ...ColOrAliasOrFuncOrSub) Select { - return Select{ +func NewSelectDistinct(columns ...ColOrAliasOrFuncOrSub) *Select { + return &Select{ distinct: true, columns: columns, } } -func (s Select) From(t TableOrAliasOrJoiner) From { +func (s *Select) From(t TableOrAliasOrJoiner) *From { f := NewFrom(t) - f.prev = s + Link(s, f) return f } -func (s Select) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(s) -} - -func (s Select) self() (tokenizer.Tokenizer, []interface{}) { +func (s *Select) nodeize() (tokenizer.Tokenizer, []interface{}) { tokenizers := make(tokenizer.Tokenizers, len(s.columns)) values := []interface{}{} for i, c := range s.columns { @@ -56,7 +53,3 @@ func (s Select) self() (tokenizer.Tokenizer, []interface{}) { tokenizers.Prefix(token.Comma, token.Space), ), values } - -func (s Select) previous() Clause { - return nil -} diff --git a/statement/select_test.go b/statement/select_test.go index af69187..ca14c1c 100644 --- a/statement/select_test.go +++ b/statement/select_test.go @@ -9,13 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestSelectType(t *testing.T) { - s := statement.Select{} - if _, ok := interface{}(s).(statement.Clause); !ok { - t.Errorf("%T should implement statement.Clause", s) - } -} - func TestSelectSQL(t *testing.T) { for i, c := range []struct { statement statement.Statement diff --git a/statement/set.go b/statement/set.go index bde8854..65d2180 100644 --- a/statement/set.go +++ b/statement/set.go @@ -7,21 +7,24 @@ import ( ) type Set struct { - prev Clause + Prev + Next assigns []Assign } -func NewSet(assigns ...Assign) Set { - return Set{ +func NewSet(assigns ...Assign) *Set { + return &Set{ assigns: assigns, } } -func (s Set) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(s) +func (s *Set) Where(operation ComparisonOrLogicalOperation) *Where { + w := NewWhere(operation) + Link(s, w) + return w } -func (s Set) self() (tokenizer.Tokenizer, []interface{}) { +func (s *Set) nodeize() (tokenizer.Tokenizer, []interface{}) { tokenizers := make(tokenizer.Tokenizers, len(s.assigns)) values := []interface{}{} for i, a := range s.assigns { @@ -37,13 +40,3 @@ func (s Set) self() (tokenizer.Tokenizer, []interface{}) { tokenizers.Prefix(token.Comma, token.Space), ), values } - -func (s Set) previous() Clause { - return s.prev -} - -func (s Set) Where(operation ComparisonOrLogicalOperation) Where { - w := NewWhere(operation) - w.prev = s - return w -} diff --git a/statement/set_operation.go b/statement/set_operation.go index 896b69e..64ff20c 100644 --- a/statement/set_operation.go +++ b/statement/set_operation.go @@ -7,57 +7,61 @@ import ( ) type SetOperation struct { + Prev + Next op keyword.Operator statements []Statement } -func NewUnion(statements ...Statement) SetOperation { - return SetOperation{ +func NewUnion(statements ...Statement) *SetOperation { + return &SetOperation{ op: keyword.Union, statements: statements, } } -func NewUnionAll(statements ...Statement) SetOperation { - return SetOperation{ +func NewUnionAll(statements ...Statement) *SetOperation { + return &SetOperation{ op: keyword.UnionAll, statements: statements, } } -func NewIntersect(statements ...Statement) SetOperation { - return SetOperation{ +func NewIntersect(statements ...Statement) *SetOperation { + return &SetOperation{ op: keyword.Intersect, statements: statements, } } -func NewIntersectAll(statements ...Statement) SetOperation { - return SetOperation{ +func NewIntersectAll(statements ...Statement) *SetOperation { + return &SetOperation{ op: keyword.IntersectAll, statements: statements, } } -func NewExcept(statements ...Statement) SetOperation { - return SetOperation{ +func NewExcept(statements ...Statement) *SetOperation { + return &SetOperation{ op: keyword.Except, statements: statements, } } -func NewExceptAll(statements ...Statement) SetOperation { - return SetOperation{ +func NewExceptAll(statements ...Statement) *SetOperation { + return &SetOperation{ op: keyword.ExceptAll, statements: statements, } } -func (u SetOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { - return u.self() +func (u *SetOperation) OrderBy(os ...Order) *OrderBy { + o := NewOrderBy(os...) + Link(u, o) + return o } -func (u SetOperation) self() (tokenizer.Tokenizer, []interface{}) { +func (u *SetOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { tokenizers := make(tokenizer.Tokenizers, len(u.statements)) values := []interface{}{} for i, s := range u.statements { @@ -75,16 +79,6 @@ func (u SetOperation) self() (tokenizer.Tokenizer, []interface{}) { return tokenizers, values } -func (u SetOperation) previous() Clause { - return nil -} - -func (u SetOperation) keyword() keyword.Operator { +func (u *SetOperation) keyword() keyword.Operator { return u.op } - -func (u SetOperation) OrderBy(os ...Order) OrderBy { - o := NewOrderBy(os...) - o.prev = u - return o -} diff --git a/statement/set_operation_test.go b/statement/set_operation_test.go index bd8b463..fd2b024 100644 --- a/statement/set_operation_test.go +++ b/statement/set_operation_test.go @@ -9,16 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestSetOperationType(t *testing.T) { - for _, s := range []interface{}{ - statement.SetOperation{}, - } { - if _, ok := s.(statement.Clause); !ok { - t.Errorf("%T should implement statement.Clause", s) - } - } -} - func TestUnionSQL(t *testing.T) { for i, c := range []struct { statement statement.Statement diff --git a/statement/subquery.go b/statement/subquery.go index 0463edc..6687514 100644 --- a/statement/subquery.go +++ b/statement/subquery.go @@ -97,7 +97,7 @@ func (s Subquery) IsNotNull() NullOperation { } func (s Subquery) nodeize() (tokenizer.Tokenizer, []interface{}) { - t, values := s.statement.nodeize() + t, values := Nodize(s.statement) return tokenizer.NewParentheses(t), values } diff --git a/statement/traverser.go b/statement/traverser.go index 9868e51..0ba2589 100644 --- a/statement/traverser.go +++ b/statement/traverser.go @@ -2,40 +2,33 @@ package statement import "github.com/minodisk/sqlabble/token" -func Link(n1, n2 Nodeizer) { - switch t1 := n1.(type) { - case Childer: - switch t2 := n2.(type) { - case Parenter: - t1.setChild(t2) - t2.setParent(t1) - } - case Nexter: - switch t2 := n2.(type) { - case Prever: - t1.setNext(t2) - t2.setPrev(t1) - } - } +func Contract(t1 Childer, t2 Parenter) { + t1.setChild(t2) + t2.setParent(t1) +} + +func Link(t1 Nexter, t2 Prever) { + t1.setNext(t2) + t2.setPrev(t1) } type Childer interface { Nodeizer - child() Parenter + children() []Parenter setChild(Parenter) separator() token.Tokens } type Parent struct { - c Parenter + c []Parenter } -func (p *Parent) child() Parenter { +func (p *Parent) children() []Parenter { return p.c } func (p *Parent) setChild(c Parenter) { - p.c = c + p.c = append(p.c, c) } type Parenter interface { diff --git a/statement/update.go b/statement/update.go index 8744ad2..ba55609 100644 --- a/statement/update.go +++ b/statement/update.go @@ -7,21 +7,23 @@ import ( ) type Update struct { - prev Clause + Prev table Table } -func NewUpdate(table Table) Update { - return Update{ +func NewUpdate(table Table) *Update { + return &Update{ table: table, } } -func (u Update) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(u) +func (u *Update) Set(assigns ...Assign) *Set { + s := NewSet(assigns...) + Link(u, s) + return s } -func (u Update) self() (tokenizer.Tokenizer, []interface{}) { +func (u *Update) nodeize() (tokenizer.Tokenizer, []interface{}) { middle, values := u.table.nodeize() return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.Update)), @@ -29,13 +31,3 @@ func (u Update) self() (tokenizer.Tokenizer, []interface{}) { middle, ), values } - -func (u Update) previous() Clause { - return u.prev -} - -func (u Update) Set(assigns ...Assign) Set { - s := NewSet(assigns...) - s.prev = u - return s -} diff --git a/statement/where.go b/statement/where.go index 4cef14d..9c79333 100644 --- a/statement/where.go +++ b/statement/where.go @@ -7,47 +7,40 @@ import ( ) type Where struct { - prev Clause + Prev + Next operation ComparisonOrLogicalOperation } -func NewWhere(operation ComparisonOrLogicalOperation) Where { - return Where{ +func NewWhere(operation ComparisonOrLogicalOperation) *Where { + return &Where{ operation: operation, } } -func (w Where) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(w) -} - -func (w Where) self() (tokenizer.Tokenizer, []interface{}) { - middle, values := w.operation.nodeize() - return tokenizer.NewContainer( - tokenizer.NewLine(token.Word(keyword.Where)), - ).SetMiddle( - middle, - ), values -} - -func (w Where) previous() Clause { - return w.prev -} - -func (w Where) GroupBy(column Column, columns ...Column) GroupBy { +func (w *Where) GroupBy(column Column, columns ...Column) *GroupBy { g := NewGroupBy(column, columns...) - g.prev = w + Link(w, g) return g } -func (w Where) OrderBy(orders ...Order) OrderBy { +func (w *Where) OrderBy(orders ...Order) *OrderBy { o := NewOrderBy(orders...) - o.prev = w + Link(w, o) return o } -func (w Where) Limit(count int) Limit { +func (w *Where) Limit(count int) *Limit { l := NewLimit(count) - l.prev = w + Link(w, l) return l } + +func (w *Where) nodeize() (tokenizer.Tokenizer, []interface{}) { + middle, values := w.operation.nodeize() + return tokenizer.NewContainer( + tokenizer.NewLine(token.Word(keyword.Where)), + ).SetMiddle( + middle, + ), values +} diff --git a/statement/where_test.go b/statement/where_test.go index 5137dbd..c2d0416 100644 --- a/statement/where_test.go +++ b/statement/where_test.go @@ -9,13 +9,6 @@ import ( "github.com/minodisk/sqlabble/statement" ) -func TestWhereType(t *testing.T) { - w := statement.Where{} - if _, ok := interface{}(w).(statement.Clause); !ok { - t.Errorf("%s should implement statement.Clause", w) - } -} - func TestWhereOperation(t *testing.T) { for i, c := range []struct { statement statement.Statement From e51d59a7e07e9a6f548a201f3d9627cbdc5c382e Mon Sep 17 00:00:00 2001 From: minodisk Date: Fri, 24 Feb 2017 22:11:42 +0900 Subject: [PATCH 3/8] Use traverser in CREATE TABLE --- statement/column.go | 2 +- statement/create_table.go | 50 +++++++++++++++++----------------- statement/create_table_test.go | 22 +++++++-------- statement/definition.go | 44 +++++++++++------------------- statement/nodeizer.go | 27 ++++++++++-------- tokenizer/container.go | 3 ++ 6 files changed, 71 insertions(+), 77 deletions(-) diff --git a/statement/column.go b/statement/column.go index 67c93f6..c924300 100644 --- a/statement/column.go +++ b/statement/column.go @@ -28,7 +28,7 @@ func (c Column) As(alias string) ColumnAs { // Define is used to specify a definition for the column. // This constitutes a part of the table creation. // Returns a new Definition. -func (c Column) Define(definition string) Definition { +func (c Column) Define(definition string) *Definition { d := NewDefinition(definition) d.column = c return d diff --git a/statement/create_table.go b/statement/create_table.go index fe5a351..837a1d6 100644 --- a/statement/create_table.go +++ b/statement/create_table.go @@ -7,47 +7,47 @@ import ( ) type CreateTable struct { + Parent ifNotExists bool - joiner Joiner + table Table } -func NewCreateTable(joiner Joiner) CreateTable { - return CreateTable{ +func NewCreateTable(table Table) *CreateTable { + return &CreateTable{ ifNotExists: false, - joiner: joiner, + table: table, } } -func NewCreateTableIfNotExists(joiner Joiner) CreateTable { - return CreateTable{ +func NewCreateTableIfNotExists(table Table) *CreateTable { + return &CreateTable{ ifNotExists: true, - joiner: joiner, + table: table, } } -func (c CreateTable) nodeize() (tokenizer.Tokenizer, []interface{}) { - return c.container() +func (c *CreateTable) Definitions(defs ...*Definition) *Definitions { + ds := NewDefinitions(defs...) + Contract(c, ds) + return ds +} + +func (c *CreateTable) separator() token.Tokens { + return nil } -func (c CreateTable) container() (tokenizer.Container, []interface{}) { - line := tokenizer.NewLine(token.Word(keyword.CreateTable)) +func (c *CreateTable) nodeize() (tokenizer.Tokenizer, []interface{}) { + tokens := token.Tokens{ + token.Word(keyword.CreateTable), + token.Space, + } if c.ifNotExists { - line = line.A( - token.Space, + tokens = tokens.Append( token.Word(keyword.IfNotExists), + token.Space, ) } - middle, values := c.joiner.nodeize() - return tokenizer.NewContainer( - line, - ).SetMiddle( - middle, - ), values -} - -func (c CreateTable) Definitions(defs ...Definition) Definitions { - ds := NewDefinitions(defs...) - ds.createTable = c - return ds + t, values := c.table.nodeize() + return tokenizer.NewContainer(t.Prepend(tokens...)), values } diff --git a/statement/create_table_test.go b/statement/create_table_test.go index 09259ff..881d039 100644 --- a/statement/create_table_test.go +++ b/statement/create_table_test.go @@ -24,8 +24,7 @@ func TestCreateTableSQL(t *testing.T) { statement.NewTable("foo"), ), "CREATE TABLE foo", - `> CREATE TABLE -> foo + `> CREATE TABLE foo `, nil, }, @@ -34,8 +33,7 @@ func TestCreateTableSQL(t *testing.T) { statement.NewTable("foo"), ), "CREATE TABLE IF NOT EXISTS foo", - `> CREATE TABLE IF NOT EXISTS -> foo + `> CREATE TABLE IF NOT EXISTS foo `, nil, }, @@ -44,8 +42,8 @@ func TestCreateTableSQL(t *testing.T) { statement.NewTable("foo"), ).Definitions(), "CREATE TABLE foo ()", - `> CREATE TABLE -> foo ( + `> CREATE TABLE foo +> ( > ) `, nil, @@ -57,8 +55,8 @@ func TestCreateTableSQL(t *testing.T) { statement.NewColumn("name").Define("VARCHAR(255)"), ), "CREATE TABLE foo (name VARCHAR(255))", - `> CREATE TABLE -> foo ( + `> CREATE TABLE foo +> ( > name VARCHAR(255) > ) `, @@ -72,8 +70,8 @@ func TestCreateTableSQL(t *testing.T) { statement.NewColumn("gender").Define("ENUM('M', 'F')"), ), "CREATE TABLE foo (name VARCHAR(255), gender ENUM('M', 'F'))", - `> CREATE TABLE -> foo ( + `> CREATE TABLE foo +> ( > name VARCHAR(255) > , gender ENUM('M', 'F') > ) @@ -89,8 +87,8 @@ func TestCreateTableSQL(t *testing.T) { statement.NewColumn("birth_date").Define("DATE"), ), "CREATE TABLE foo (name VARCHAR(255), gender ENUM('M', 'F'), birth_date DATE)", - `> CREATE TABLE -> foo ( + `> CREATE TABLE foo +> ( > name VARCHAR(255) > , gender ENUM('M', 'F') > , birth_date DATE diff --git a/statement/definition.go b/statement/definition.go index e1da4c5..45104c2 100644 --- a/statement/definition.go +++ b/statement/definition.go @@ -6,12 +6,13 @@ import ( ) type Definition struct { + Child column Column definition string } -func NewDefinition(definition string) Definition { - return Definition{ +func NewDefinition(definition string) *Definition { + return &Definition{ definition: definition, } } @@ -26,38 +27,25 @@ func (d Definition) nodeize() (tokenizer.Tokenizer, []interface{}) { } type Definitions struct { - createTable CreateTable - definitions []Definition + Child + Parent } -func NewDefinitions(definitions ...Definition) Definitions { - return Definitions{ - definitions: definitions, +func NewDefinitions(defs ...*Definition) *Definitions { + ds := &Definitions{} + for _, d := range defs { + Contract(ds, d) } + return ds } -func (ds Definitions) nodeize() (tokenizer.Tokenizer, []interface{}) { - ts := make(tokenizer.Tokenizers, len(ds.definitions)) - values := []interface{}{} - for i, d := range ds.definitions { - var vals []interface{} - ts[i], vals = d.nodeize() - values = append(values, vals...) - } - ts = ts.Prefix( +func (d Definitions) nodeize() (tokenizer.Tokenizer, []interface{}) { + return tokenizer.NewParentheses(nil), nil +} + +func (d Definitions) separator() token.Tokens { + return token.NewTokens( token.Comma, token.Space, ) - - c, values := ds.createTable.container() - middle := c.Middle() - def := tokenizer.NewParentheses(ts) - - return c.SetMiddle( - tokenizer.ConcatTokenizers( - middle, - def, - tokenizer.NewLine(token.Space), - ), - ), values } diff --git a/statement/nodeizer.go b/statement/nodeizer.go index 01cb715..c0c66f3 100644 --- a/statement/nodeizer.go +++ b/statement/nodeizer.go @@ -44,19 +44,24 @@ func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { for _, n := range ns { if childer, ok := n.(Childer); ok { - for _, child := range childer.children() { - t1, vals1 := n.nodeize() - t2, vals2 := Nodize(child) - first, _ := t1.FirstLine() - _, last := t1.LastLine() - t12 := tokenizer. - NewContainer(first). - SetMiddle(t2.Prefix(childer.separator()...)). - SetLast(last) + t1, vals1 := n.nodeize() + values = append(values, vals1...) + first, _ := t1.FirstLine() + _, last := t1.LastLine() - tokenizers = append(tokenizers, t12) - values = append(append(values, vals1...), vals2...) + children := childer.children() + ts := make(tokenizer.Tokenizers, len(children)) + for i, child := range children { + var vals []interface{} + ts[i], vals = Nodize(child) + values = append(values, vals...) } + + t12 := tokenizer. + NewContainer(first). + SetMiddle(ts.Prefix(childer.separator()...)). + SetLast(last) + tokenizers = append(tokenizers, t12) continue } diff --git a/tokenizer/container.go b/tokenizer/container.go index 4ebb31f..48c66e3 100644 --- a/tokenizer/container.go +++ b/tokenizer/container.go @@ -81,6 +81,9 @@ func (c Container) FirstLine() (Line, Tokenizer) { } func (c Container) LastLine() (Tokenizer, Line) { + if c.last == nil { + return nil, EmptyLine + } t, l := c.last.LastLine() return NewContainer( c.first, From 67869f25302a114b0751d3986c6496be3cd37f1f Mon Sep 17 00:00:00 2001 From: minodisk Date: Sat, 25 Feb 2017 01:19:13 +0900 Subject: [PATCH 4/8] Add separatorer interface --- statement/case_searched.go | 4 ---- statement/case_simple.go | 4 ---- statement/create_table.go | 4 ---- statement/nodeizer.go | 7 ++++++- statement/traverser.go | 4 ++++ 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/statement/case_searched.go b/statement/case_searched.go index 798efbb..18093d6 100644 --- a/statement/case_searched.go +++ b/statement/case_searched.go @@ -20,10 +20,6 @@ func (c *SearchedCase) When(condition ComparisonOrLogicalOperation) *SearchedWhe return w } -func (c *SearchedCase) separator() token.Tokens { - return nil -} - func (c *SearchedCase) nodeize() (tokenizer.Tokenizer, []interface{}) { return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.Case)), diff --git a/statement/case_simple.go b/statement/case_simple.go index 2289787..2828cf8 100644 --- a/statement/case_simple.go +++ b/statement/case_simple.go @@ -23,10 +23,6 @@ func (c *SimpleCase) When(param Param) *SimpleWhen { return w } -func (c *SimpleCase) separator() token.Tokens { - return nil -} - func (c *SimpleCase) nodeize() (tokenizer.Tokenizer, []interface{}) { t, vals := c.param.nodeize() return tokenizer.NewContainer( diff --git a/statement/create_table.go b/statement/create_table.go index 837a1d6..23eeee9 100644 --- a/statement/create_table.go +++ b/statement/create_table.go @@ -32,10 +32,6 @@ func (c *CreateTable) Definitions(defs ...*Definition) *Definitions { return ds } -func (c *CreateTable) separator() token.Tokens { - return nil -} - func (c *CreateTable) nodeize() (tokenizer.Tokenizer, []interface{}) { tokens := token.Tokens{ token.Word(keyword.CreateTable), diff --git a/statement/nodeizer.go b/statement/nodeizer.go index c0c66f3..88e11e8 100644 --- a/statement/nodeizer.go +++ b/statement/nodeizer.go @@ -57,9 +57,14 @@ func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { values = append(values, vals...) } + if separatorer, ok := childer.(Separatorer); ok { + seps := separatorer.separator() + ts = ts.Prefix(seps...) + } + t12 := tokenizer. NewContainer(first). - SetMiddle(ts.Prefix(childer.separator()...)). + SetMiddle(ts). SetLast(last) tokenizers = append(tokenizers, t12) continue diff --git a/statement/traverser.go b/statement/traverser.go index 0ba2589..01a7388 100644 --- a/statement/traverser.go +++ b/statement/traverser.go @@ -16,6 +16,10 @@ type Childer interface { Nodeizer children() []Parenter setChild(Parenter) +} + +type Separatorer interface { + Childer separator() token.Tokens } From 65f700a4d5c64e8cf14a6e9a159d65d0c544d1fc Mon Sep 17 00:00:00 2001 From: minodisk Date: Sat, 25 Feb 2017 12:53:37 +0900 Subject: [PATCH 5/8] Insert space in generator --- builder/builder.go | 1 - statement/assign.go | 2 - statement/case_simple.go | 2 +- statement/column_as.go | 15 +++---- statement/create_table.go | 2 - statement/definition.go | 2 - statement/func.go | 6 +-- statement/func_date.go | 2 - statement/group_by.go | 1 - statement/insert.go | 3 +- statement/join.go | 1 - statement/on.go | 4 -- statement/on_test.go | 20 ++++----- statement/op.go | 12 ------ statement/op_nonscalar.go | 4 -- statement/order.go | 1 - statement/order_by.go | 2 +- statement/params.go | 4 +- statement/select.go | 3 +- statement/set.go | 2 +- statement/set_operation.go | 1 - statement/set_operation_test.go | 6 ++- statement/subquery_as.go | 2 - statement/table_as.go | 9 ++-- statement/using.go | 2 - statement/using_test.go | 20 ++++----- statement/values.go | 1 - token/debugger.go | 14 ++++++ token/generator.go | 75 +++++++++++++++++++++++++-------- token/interfaces.go | 1 + token/token.go | 25 +++++++++-- token/tokens.go | 2 +- token/tokens_test.go | 4 +- token/word.go | 20 ++++++--- tokenizer/container.go | 8 ++-- 35 files changed, 157 insertions(+), 122 deletions(-) create mode 100644 token/debugger.go diff --git a/builder/builder.go b/builder/builder.go index e404fd5..981dc07 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -29,7 +29,6 @@ func NewBuilder(format token.Format) Builder { // Build converts a statement into a query and a slice of values. func (b Builder) Build(stmt statement.Statement) (string, []interface{}) { - if n, ok := stmt.(statement.Nodeizer); ok { root := statement.Traverse(n) tokenizers, values := statement.Nodize(root) diff --git a/statement/assign.go b/statement/assign.go index 474b4ec..12d2cb7 100644 --- a/statement/assign.go +++ b/statement/assign.go @@ -25,9 +25,7 @@ func (a Assign) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(keyword.Eq), - token.Space, ), ), append(v1, v2...) } diff --git a/statement/case_simple.go b/statement/case_simple.go index 2828cf8..14337c0 100644 --- a/statement/case_simple.go +++ b/statement/case_simple.go @@ -26,7 +26,7 @@ func (c *SimpleCase) When(param Param) *SimpleWhen { func (c *SimpleCase) nodeize() (tokenizer.Tokenizer, []interface{}) { t, vals := c.param.nodeize() return tokenizer.NewContainer( - t.Prepend(token.Word(keyword.Case), token.Space), + t.Prepend(token.Word(keyword.Case)), ).SetLast( tokenizer.NewLine(token.Word(keyword.End)), ), vals diff --git a/statement/column_as.go b/statement/column_as.go index dca229b..8172acf 100644 --- a/statement/column_as.go +++ b/statement/column_as.go @@ -21,11 +21,11 @@ func (c ColumnAs) nodeize() (tokenizer.Tokenizer, []interface{}) { if c.column == nil { return tokenizer.NewLine( token.Word(keyword.As), - token.Space, ).Append( - token.Wrap( + token.NewTokens( + token.QuoteStart, token.Word(c.alias), - token.Quote, + token.QuoteEnd, )..., ), nil } @@ -34,15 +34,12 @@ func (c ColumnAs) nodeize() (tokenizer.Tokenizer, []interface{}) { return tokenizer.ConcatTokenizers( t1, tokenizer.NewLine( - token.Wrap( - token.Word(c.alias), - token.Quote, - )..., + token.QuoteStart, + token.Word(c.alias), + token.QuoteEnd, ), tokenizer.NewLine( - token.Space, token.Word(keyword.As), - token.Space, ), ), v1 } diff --git a/statement/create_table.go b/statement/create_table.go index 23eeee9..a44447f 100644 --- a/statement/create_table.go +++ b/statement/create_table.go @@ -35,12 +35,10 @@ func (c *CreateTable) Definitions(defs ...*Definition) *Definitions { func (c *CreateTable) nodeize() (tokenizer.Tokenizer, []interface{}) { tokens := token.Tokens{ token.Word(keyword.CreateTable), - token.Space, } if c.ifNotExists { tokens = tokens.Append( token.Word(keyword.IfNotExists), - token.Space, ) } diff --git a/statement/definition.go b/statement/definition.go index 45104c2..5367e33 100644 --- a/statement/definition.go +++ b/statement/definition.go @@ -21,7 +21,6 @@ func (d Definition) nodeize() (tokenizer.Tokenizer, []interface{}) { t, values := d.column.nodeize() return t. Append( - token.Space, token.Word(d.definition), ), values } @@ -46,6 +45,5 @@ func (d Definitions) nodeize() (tokenizer.Tokenizer, []interface{}) { func (d Definitions) separator() token.Tokens { return token.NewTokens( token.Comma, - token.Space, ) } diff --git a/statement/func.go b/statement/func.go index cc16a39..70c8aff 100644 --- a/statement/func.go +++ b/statement/func.go @@ -10,7 +10,7 @@ type Args []Statement func (ps Args) nodeize() (tokenizer.Tokenizer, []interface{}) { if len(ps) == 0 { - return tokenizer.NewLine(token.ParenthesesStart, token.ParenthesesEnd), nil + return tokenizer.NewLine(token.FuncParenStart, token.FuncParenEnd), nil } var tk tokenizer.Tokenizer @@ -22,10 +22,10 @@ func (ps Args) nodeize() (tokenizer.Tokenizer, []interface{}) { values = append(values, vals...) continue } - tk = tokenizer.ConcatTokenizers(tk, t, tokenizer.NewLine(token.Comma, token.Space)) + tk = tokenizer.ConcatTokenizers(tk, t, tokenizer.NewLine(token.Comma)) values = append(values, vals...) } - return tk.Prepend(token.ParenthesesStart).Append(token.ParenthesesEnd), values + return tk.Prepend(token.FuncParenStart).Append(token.FuncParenEnd), values } type ValOrColOrFuncOrSubs []ValOrColOrFuncOrSub diff --git a/statement/func_date.go b/statement/func_date.go index 8177c2b..ca9c59b 100644 --- a/statement/func_date.go +++ b/statement/func_date.go @@ -235,7 +235,6 @@ func (i Interval) nodeize() (tokenizer.Tokenizer, []interface{}) { t, values := i.duration.nodeize() return t.Prepend( token.Word("INTERVAL"), - token.Space, ), values } @@ -247,7 +246,6 @@ type IntervalUnit struct { func (i IntervalUnit) nodeize() (tokenizer.Tokenizer, []interface{}) { t, values := i.interval.nodeize() return t.Append( - token.Space, token.Word(i.unit), ), values } diff --git a/statement/group_by.go b/statement/group_by.go index 757a7a8..fff142a 100644 --- a/statement/group_by.go +++ b/statement/group_by.go @@ -50,7 +50,6 @@ func (g *GroupBy) nodeize() (tokenizer.Tokenizer, []interface{}) { ).SetMiddle( ts.Prefix( token.Comma, - token.Space, ), ), values } diff --git a/statement/insert.go b/statement/insert.go index b2f9d1e..87825aa 100644 --- a/statement/insert.go +++ b/statement/insert.go @@ -38,10 +38,9 @@ func (i InsertInto) self() (tokenizer.Tokenizer, []interface{}) { tokenizer.NewParentheses( ts.Prefix( token.Comma, - token.Space, ), ), - tokenizer.NewLine(token.Space), + tokenizer.EmptyLine, ), ), values } diff --git a/statement/join.go b/statement/join.go index 46e684d..23e2048 100644 --- a/statement/join.go +++ b/statement/join.go @@ -87,7 +87,6 @@ func (j Join) self() (tokenizer.Tokenizer, []interface{}) { t, v := j.table.nodeize() return t.Prepend( token.Word(j.joinType), - token.Space, ), v } diff --git a/statement/on.go b/statement/on.go index b10e79c..a6abcdf 100644 --- a/statement/on.go +++ b/statement/on.go @@ -56,15 +56,11 @@ func (o On) self() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(keyword.Eq), - token.Space, ), ), tokenizer.NewLine( - token.Space, token.Word(keyword.On), - token.Space, ), ), append(append(v0, v1...), v2...) } diff --git a/statement/on_test.go b/statement/on_test.go index 4a2eb30..4fd277d 100644 --- a/statement/on_test.go +++ b/statement/on_test.go @@ -21,8 +21,8 @@ func TestOn(t *testing.T) { statement.NewColumn("f.id"), statement.NewColumn("b.id"), ), - " ON f.id = b.id", - `> ON f.id = b.id + "ON f.id = b.id", + `> ON f.id = b.id `, nil, }, @@ -33,8 +33,8 @@ func TestOn(t *testing.T) { ).Join( statement.NewTable("bar"), ), - " ON f.id = b.id JOIN bar", - `> ON f.id = b.id + "ON f.id = b.id JOIN bar", + `> ON f.id = b.id > JOIN bar `, nil, @@ -46,8 +46,8 @@ func TestOn(t *testing.T) { ).InnerJoin( statement.NewTable("bar"), ), - " ON f.id = b.id INNER JOIN bar", - `> ON f.id = b.id + "ON f.id = b.id INNER JOIN bar", + `> ON f.id = b.id > INNER JOIN bar `, nil, @@ -59,8 +59,8 @@ func TestOn(t *testing.T) { ).LeftJoin( statement.NewTable("bar"), ), - " ON f.id = b.id LEFT JOIN bar", - `> ON f.id = b.id + "ON f.id = b.id LEFT JOIN bar", + `> ON f.id = b.id > LEFT JOIN bar `, nil, @@ -72,8 +72,8 @@ func TestOn(t *testing.T) { ).RightJoin( statement.NewTable("bar"), ), - " ON f.id = b.id RIGHT JOIN bar", - `> ON f.id = b.id + "ON f.id = b.id RIGHT JOIN bar", + `> ON f.id = b.id > RIGHT JOIN bar `, nil, diff --git a/statement/op.go b/statement/op.go index 605965b..da928f6 100644 --- a/statement/op.go +++ b/statement/op.go @@ -38,7 +38,6 @@ func (o JoinOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { } return tokenizer.NewTokenizers(ts...).Prefix( token.Word(o.keyword()), - token.Space, ), values } @@ -64,7 +63,6 @@ func (o Not) nodeize() (tokenizer.Tokenizer, []interface{}) { middle, ).Prepend( token.Word(o.keyword()), - token.Space, ), values } @@ -145,9 +143,7 @@ func (o ComparisonOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(o.keyword()), - token.Space, ), ), append(v1, v2...) } @@ -177,16 +173,12 @@ func (o Between) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(o.keyword()), - token.Space, ), ), t3, tokenizer.NewLine( - token.Space, token.Word(keyword.And), - token.Space, ), ), append(append(v1, v2...), v3...) } @@ -222,9 +214,7 @@ func (o ContainingOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(o.keyword()), - token.Space, ), ), append(v1, v2...) } @@ -256,9 +246,7 @@ func (o NullOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, tokenizer.NewLine(token.Word(keyword.Null)), tokenizer.NewLine( - token.Space, token.Word(o.keyword()), - token.Space, ), ), v1 } diff --git a/statement/op_nonscalar.go b/statement/op_nonscalar.go index c97f806..b6c6a54 100644 --- a/statement/op_nonscalar.go +++ b/statement/op_nonscalar.go @@ -101,7 +101,6 @@ func (n NonScalarOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { if n.column == nil { return t2.Prepend( token.Word(n.keyword()), - token.Space, ), v2 } @@ -110,9 +109,7 @@ func (n NonScalarOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(n.keyword()), - token.Space, ), ), append(v1, v2...) } @@ -144,7 +141,6 @@ func (e ExistanceOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { t2, v2 := e.params.nodeize() return t2.Prepend( token.Word(e.keyword()), - token.Space, ), v2 } diff --git a/statement/order.go b/statement/order.go index 46fd9af..4541818 100644 --- a/statement/order.go +++ b/statement/order.go @@ -26,7 +26,6 @@ func NewDesc() Order { func (o Order) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, v1 := o.column.nodeize() return t1.Append( - token.Space, token.Word(o.keyword()), ), v1 } diff --git a/statement/order_by.go b/statement/order_by.go index a233fb3..9abc8b3 100644 --- a/statement/order_by.go +++ b/statement/order_by.go @@ -35,6 +35,6 @@ func (o *OrderBy) nodeize() (tokenizer.Tokenizer, []interface{}) { return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.OrderBy)), ).SetMiddle( - tokenizers.Prefix(token.Comma, token.Space), + tokenizers.Prefix(token.Comma), ), values } diff --git a/statement/params.go b/statement/params.go index ab02b67..b60cd0e 100644 --- a/statement/params.go +++ b/statement/params.go @@ -50,8 +50,8 @@ func (ps Params) nodeize() (tokenizer.Tokenizer, []interface{}) { } line, values := tokenizer.ParamsToLine(values...) return line. - Prepend(token.ParenthesesStart). - Append(token.ParenthesesEnd), values + Prepend(token.ParenStart). + Append(token.ParenEnd), values } // isParamsOrSubquery always returns true. diff --git a/statement/select.go b/statement/select.go index df30bb1..99a9ca4 100644 --- a/statement/select.go +++ b/statement/select.go @@ -43,13 +43,12 @@ func (s *Select) nodeize() (tokenizer.Tokenizer, []interface{}) { tokens := token.NewTokens(token.Word(keyword.Select)) if s.distinct { tokens = tokens.Append( - token.Space, token.Word(keyword.Distinct), ) } return tokenizer.NewContainer( tokenizer.NewLine(tokens...), ).SetMiddle( - tokenizers.Prefix(token.Comma, token.Space), + tokenizers.Prefix(token.Comma), ), values } diff --git a/statement/set.go b/statement/set.go index 65d2180..c2e488d 100644 --- a/statement/set.go +++ b/statement/set.go @@ -37,6 +37,6 @@ func (s *Set) nodeize() (tokenizer.Tokenizer, []interface{}) { token.Word(keyword.Set), ), ).SetMiddle( - tokenizers.Prefix(token.Comma, token.Space), + tokenizers.Prefix(token.Comma), ), values } diff --git a/statement/set_operation.go b/statement/set_operation.go index 64ff20c..617dc1c 100644 --- a/statement/set_operation.go +++ b/statement/set_operation.go @@ -70,7 +70,6 @@ func (u *SetOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { if i != 0 { t = t.Prepend( token.Word(u.keyword()), - token.Space, ) } tokenizers[i] = t diff --git a/statement/set_operation_test.go b/statement/set_operation_test.go index fd2b024..a890bc8 100644 --- a/statement/set_operation_test.go +++ b/statement/set_operation_test.go @@ -18,15 +18,17 @@ func TestUnionSQL(t *testing.T) { }{ { statement.NewUnion( - statement.NewSelect(statement.NewColumn("a")), + statement.NewSelect(statement.NewColumn("a")).From(statement.NewTable("aaa")), statement.NewSelect(statement.NewColumn("b")), ).OrderBy( statement.NewColumn("foo").Asc(), ), - "(SELECT a) UNION (SELECT b) ORDER BY foo ASC", + "(SELECT a FROM aaa) UNION (SELECT b) ORDER BY foo ASC", `> ( > SELECT > a +> FROM +> aaa > ) > UNION ( > SELECT diff --git a/statement/subquery_as.go b/statement/subquery_as.go index 000fa01..577b946 100644 --- a/statement/subquery_as.go +++ b/statement/subquery_as.go @@ -24,9 +24,7 @@ func (a SubqueryAs) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(keyword.As), - token.Space, ), ), v1 } diff --git a/statement/table_as.go b/statement/table_as.go index 09d56f4..7065398 100644 --- a/statement/table_as.go +++ b/statement/table_as.go @@ -42,19 +42,16 @@ func (t TableAs) nodeize() (tokenizer.Tokenizer, []interface{}) { func (t TableAs) self() (tokenizer.Tokenizer, []interface{}) { t1, v1 := t.table.nodeize() t2 := tokenizer.NewLine( - token.Wrap( - token.Word(t.alias), - token.Quote, - )..., + token.QuoteStart, + token.Word(t.alias), + token.QuoteEnd, ) return tokenizer.ConcatTokenizers( t1, t2, tokenizer.NewLine( - token.Space, token.Word(keyword.As), - token.Space, ), ), v1 } diff --git a/statement/using.go b/statement/using.go index d1dca91..59711f3 100644 --- a/statement/using.go +++ b/statement/using.go @@ -52,9 +52,7 @@ func (u Using) self() (tokenizer.Tokenizer, []interface{}) { t1, t2, tokenizer.NewLine( - token.Space, token.Word(keyword.Using), - token.Space, ), ), append(v1, v2...) } diff --git a/statement/using_test.go b/statement/using_test.go index 6bd406a..2a074c2 100644 --- a/statement/using_test.go +++ b/statement/using_test.go @@ -20,8 +20,8 @@ func TestUsing(t *testing.T) { statement.NewUsing( statement.NewColumn("id"), ), - " USING id", - `> USING id + "USING id", + `> USING id `, nil, }, @@ -31,8 +31,8 @@ func TestUsing(t *testing.T) { ).Join( statement.NewTable("bar"), ), - " USING id JOIN bar", - `> USING id + "USING id JOIN bar", + `> USING id > JOIN bar `, nil, @@ -43,8 +43,8 @@ func TestUsing(t *testing.T) { ).InnerJoin( statement.NewTable("bar"), ), - " USING id INNER JOIN bar", - `> USING id + "USING id INNER JOIN bar", + `> USING id > INNER JOIN bar `, nil, @@ -55,8 +55,8 @@ func TestUsing(t *testing.T) { ).LeftJoin( statement.NewTable("bar"), ), - " USING id LEFT JOIN bar", - `> USING id + "USING id LEFT JOIN bar", + `> USING id > LEFT JOIN bar `, nil, @@ -67,8 +67,8 @@ func TestUsing(t *testing.T) { ).RightJoin( statement.NewTable("bar"), ), - " USING id RIGHT JOIN bar", - `> USING id + "USING id RIGHT JOIN bar", + `> USING id > RIGHT JOIN bar `, nil, diff --git a/statement/values.go b/statement/values.go index 1c89a4c..ef39654 100644 --- a/statement/values.go +++ b/statement/values.go @@ -35,7 +35,6 @@ func (v Values) self() (tokenizer.Tokenizer, []interface{}) { ).SetMiddle( tokenizer.NewTokenizers(tokenizers...).Prefix( token.Comma, - token.Space, ), ), values } diff --git a/token/debugger.go b/token/debugger.go new file mode 100644 index 0000000..2b0bfd3 --- /dev/null +++ b/token/debugger.go @@ -0,0 +1,14 @@ +package token + +import ( + "fmt" + "strings" +) + +func Dump(tokens Tokens) string { + strs := make([]string, len(tokens)) + for i, t := range tokens { + strs[i] = fmt.Sprintf("[%s]", t.Debug()) + } + return strings.Join(strs, "") +} diff --git a/token/generator.go b/token/generator.go index 9b36ffb..299207c 100644 --- a/token/generator.go +++ b/token/generator.go @@ -3,36 +3,75 @@ package token // Generate converts the tokens to text according to format. func Generate(tokens Tokens, format Format) string { if format.IsBreaking { - return tokens.String(format) + return generateWithLineBreak(tokens, format).String(format) } + return generateWithoutLineBreak(tokens, format).String(format) +} +func generateWithLineBreak(tokens Tokens, format Format) Tokens { ts := Tokens{} - var prev Token + last := len(tokens) - 1 for i, t := range tokens { + var next Token + if i != last { + next = tokens[i+1] + } switch t { - case Indent: - continue - case LineEnd: + case LineStart, Indent, LineEnd, QuoteStart, ParenStart, FuncParenStart: + ts = append(ts, t) continue - case LineStart: - if i == 0 || prev == ParenthesesStart { + default: + switch next { + case LineEnd, Comma, QuoteEnd, ParenEnd, FuncParenStart, FuncParenEnd: + ts = append(ts, t) + continue + default: + ts = append(ts, t, Space) continue } - prev = Space - ts = append(ts, prev) + } + ts = append(ts, t) + } + return ts +} + +func generateWithoutLineBreak(tokens Tokens, format Format) Tokens { + ts1 := Tokens{} + for _, t := range tokens { + switch t { + case LineStart, LineEnd, Indent: continue - case ParenthesesEnd: - if prev == Space { - ts = ts[:len(ts)-1] + default: + ts1 = append(ts1, t) + } + } + + ts2 := Tokens{} + last := len(ts1) - 1 + for i, t := range ts1 { + var next Token + if i != last { + next = ts1[i+1] + } + switch t { + case QuoteStart, ParenStart, FuncParenStart: + ts2 = append(ts2, t) + continue + default: + if i == last { + ts2 = append(ts2, t) + continue } - case Comma: - if prev == Space { - ts = ts[:len(ts)-1] + switch next { + case Comma, QuoteEnd, ParenEnd, FuncParenStart, FuncParenEnd: + ts2 = append(ts2, t) + continue + default: + ts2 = append(ts2, t, Space) + continue } } - ts = append(ts, t) - prev = t } - return ts.String(format) + return ts2 } diff --git a/token/interfaces.go b/token/interfaces.go index ef6ab80..d73ee2d 100644 --- a/token/interfaces.go +++ b/token/interfaces.go @@ -2,4 +2,5 @@ package token type Token interface { String(Format) string + Debug() string } diff --git a/token/token.go b/token/token.go index 1c2fe38..f5bf170 100644 --- a/token/token.go +++ b/token/token.go @@ -2,10 +2,11 @@ package token // Tokens that change behavior by setting Format. var ( - Quote = new(quote) - LineStart = new(lineStart) - LineEnd = new(lineEnd) - Indent = new(indent) + QuoteStart = new(quote) + QuoteEnd = new(quote) + LineStart = new(lineStart) + LineEnd = new(lineEnd) + Indent = new(indent) ) type quote string @@ -14,20 +15,36 @@ func (q quote) String(format Format) string { return format.Quote } +func (q quote) Debug() string { + return "Quote" +} + type lineStart string func (l lineStart) String(format Format) string { return format.Prefix } +func (l lineStart) Debug() string { + return "LineStart" +} + type lineEnd string func (l lineEnd) String(format Format) string { return format.LineEnd } +func (l lineEnd) Debug() string { + return "LineEnd" +} + type indent string func (i indent) String(format Format) string { return format.Indent } + +func (l indent) Debug() string { + return "Indent" +} diff --git a/token/tokens.go b/token/tokens.go index f195447..e1e0046 100644 --- a/token/tokens.go +++ b/token/tokens.go @@ -16,7 +16,7 @@ func PlaceholderTokens(i int) Tokens { tokens := Tokens{} for ; i > 0; i-- { if i > 1 { - tokens = append(tokens, Placeholder, Comma, Space) + tokens = append(tokens, Placeholder, Comma) continue } tokens = append(tokens, Placeholder) diff --git a/token/tokens_test.go b/token/tokens_test.go index 2fa1190..347e5db 100644 --- a/token/tokens_test.go +++ b/token/tokens_test.go @@ -16,9 +16,9 @@ func TestToken(t *testing.T) { }{ { token.Tokens{ - token.Quote, + token.QuoteStart, token.Word("foo"), - token.Quote, + token.QuoteEnd, }, `"foo"`, "`foo`", diff --git a/token/word.go b/token/word.go index 60761ad..b7c26ed 100644 --- a/token/word.go +++ b/token/word.go @@ -1,12 +1,16 @@ package token +import "fmt" + // Tokens that don't change behavior according to Format setting. -const ( - Placeholder = Word("?") - Space = Word(" ") - Comma = Word(",") - ParenthesesStart = Word("(") - ParenthesesEnd = Word(")") +var ( + Space = NewWord(" ") + Placeholder = NewWord("?") + Comma = NewWord(",") + ParenStart = NewWord("(") + ParenEnd = NewWord(")") + FuncParenStart = NewWord("(") + FuncParenEnd = NewWord(")") ) type Word string @@ -20,6 +24,10 @@ func (w Word) String(format Format) string { return string(w) } +func (w Word) Debug() string { + return fmt.Sprintf("Word(%s)", w) +} + func (w Word) Append(tokens ...Token) []Token { return append([]Token{w}, tokens...) } diff --git a/tokenizer/container.go b/tokenizer/container.go index 48c66e3..9322e62 100644 --- a/tokenizer/container.go +++ b/tokenizer/container.go @@ -10,11 +10,11 @@ type Container struct { func WrapParenthesesLines(lines ...Line) Container { return NewContainer( - NewLine(token.ParenthesesStart), + NewLine(token.ParenStart), ).SetMiddle( NewLines(lines...), ).SetLast( - NewLine(token.ParenthesesEnd), + NewLine(token.ParenEnd), ) } @@ -26,9 +26,9 @@ func NewContainer(first Tokenizer) Container { func NewParentheses(middle Tokenizer) Container { return Container{ - first: NewLine(token.ParenthesesStart), + first: NewLine(token.ParenStart), middle: middle, - last: NewLine(token.ParenthesesEnd), + last: NewLine(token.ParenEnd), } } From e81b1a6d76fa09783c89dc54cc594cb19e37ba75 Mon Sep 17 00:00:00 2001 From: minodisk Date: Mon, 27 Feb 2017 12:40:25 +0900 Subject: [PATCH 6/8] Re-implement UNION/INTERSECT/EXCEPT with traverser --- statement/column.go | 24 +++++------ statement/func.go | 24 +++++------ statement/nodeizer.go | 8 +++- statement/op_nonscalar.go | 32 +++++++------- statement/set_operation.go | 56 ++++++++++-------------- statement/set_operation_test.go | 75 ++++++++++++++++++++++----------- statement/subquery.go | 47 +++++++++++---------- statement/subquery_as.go | 2 +- statement/traverser.go | 5 +++ 9 files changed, 149 insertions(+), 124 deletions(-) diff --git a/statement/column.go b/statement/column.go index c924300..779053b 100644 --- a/statement/column.go +++ b/statement/column.go @@ -119,73 +119,73 @@ func (c Column) IsNotNull() NullOperation { return i } -func (c Column) EqAll(params Subquery) NonScalarOperation { +func (c Column) EqAll(params *Subquery) NonScalarOperation { n := NewEqAll(params) n.column = c return n } -func (c Column) NotEqAll(params Subquery) NonScalarOperation { +func (c Column) NotEqAll(params *Subquery) NonScalarOperation { n := NewNotEqAll(params) n.column = c return n } -func (c Column) GtAll(params Subquery) NonScalarOperation { +func (c Column) GtAll(params *Subquery) NonScalarOperation { n := NewGtAll(params) n.column = c return n } -func (c Column) GteAll(params Subquery) NonScalarOperation { +func (c Column) GteAll(params *Subquery) NonScalarOperation { n := NewGteAll(params) n.column = c return n } -func (c Column) LtAll(params Subquery) NonScalarOperation { +func (c Column) LtAll(params *Subquery) NonScalarOperation { n := NewLtAll(params) n.column = c return n } -func (c Column) LteAll(params Subquery) NonScalarOperation { +func (c Column) LteAll(params *Subquery) NonScalarOperation { n := NewLteAll(params) n.column = c return n } -func (c Column) EqAny(params Subquery) NonScalarOperation { +func (c Column) EqAny(params *Subquery) NonScalarOperation { n := NewEqAny(params) n.column = c return n } -func (c Column) NotEqAny(params Subquery) NonScalarOperation { +func (c Column) NotEqAny(params *Subquery) NonScalarOperation { n := NewNotEqAny(params) n.column = c return n } -func (c Column) GtAny(params Subquery) NonScalarOperation { +func (c Column) GtAny(params *Subquery) NonScalarOperation { n := NewGtAny(params) n.column = c return n } -func (c Column) GteAny(params Subquery) NonScalarOperation { +func (c Column) GteAny(params *Subquery) NonScalarOperation { n := NewGteAny(params) n.column = c return n } -func (c Column) LtAny(params Subquery) NonScalarOperation { +func (c Column) LtAny(params *Subquery) NonScalarOperation { n := NewLtAny(params) n.column = c return n } -func (c Column) LteAny(params Subquery) NonScalarOperation { +func (c Column) LteAny(params *Subquery) NonScalarOperation { n := NewLteAny(params) n.column = c return n diff --git a/statement/func.go b/statement/func.go index 70c8aff..fd26293 100644 --- a/statement/func.go +++ b/statement/func.go @@ -109,73 +109,73 @@ func (c Func) NotIn(params ValsOrSub) ContainingOperation { return n } -func (c Func) EqAll(params Subquery) NonScalarOperation { +func (c Func) EqAll(params *Subquery) NonScalarOperation { n := NewEqAll(params) n.column = c return n } -func (c Func) NotEqAll(params Subquery) NonScalarOperation { +func (c Func) NotEqAll(params *Subquery) NonScalarOperation { n := NewNotEqAll(params) n.column = c return n } -func (c Func) GtAll(params Subquery) NonScalarOperation { +func (c Func) GtAll(params *Subquery) NonScalarOperation { n := NewGtAll(params) n.column = c return n } -func (c Func) GteAll(params Subquery) NonScalarOperation { +func (c Func) GteAll(params *Subquery) NonScalarOperation { n := NewGteAll(params) n.column = c return n } -func (c Func) LtAll(params Subquery) NonScalarOperation { +func (c Func) LtAll(params *Subquery) NonScalarOperation { n := NewLtAll(params) n.column = c return n } -func (c Func) LteAll(params Subquery) NonScalarOperation { +func (c Func) LteAll(params *Subquery) NonScalarOperation { n := NewLteAll(params) n.column = c return n } -func (c Func) EqAny(params Subquery) NonScalarOperation { +func (c Func) EqAny(params *Subquery) NonScalarOperation { n := NewEqAny(params) n.column = c return n } -func (c Func) NotEqAny(params Subquery) NonScalarOperation { +func (c Func) NotEqAny(params *Subquery) NonScalarOperation { n := NewNotEqAny(params) n.column = c return n } -func (c Func) GtAny(params Subquery) NonScalarOperation { +func (c Func) GtAny(params *Subquery) NonScalarOperation { n := NewGtAny(params) n.column = c return n } -func (c Func) GteAny(params Subquery) NonScalarOperation { +func (c Func) GteAny(params *Subquery) NonScalarOperation { n := NewGteAny(params) n.column = c return n } -func (c Func) LtAny(params Subquery) NonScalarOperation { +func (c Func) LtAny(params *Subquery) NonScalarOperation { n := NewLtAny(params) n.column = c return n } -func (c Func) LteAny(params Subquery) NonScalarOperation { +func (c Func) LteAny(params *Subquery) NonScalarOperation { n := NewLteAny(params) n.column = c return n diff --git a/statement/nodeizer.go b/statement/nodeizer.go index 88e11e8..304c485 100644 --- a/statement/nodeizer.go +++ b/statement/nodeizer.go @@ -26,7 +26,13 @@ func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { ns = append([]Nodeizer{p}, ns...) } } - ns = append(ns, n) + + if c, ok := n.(Lister); ok { + ns = append(ns, c.list()...) + } else { + ns = append(ns, n) + } + { p := n for { diff --git a/statement/op_nonscalar.go b/statement/op_nonscalar.go index b6c6a54..dec314a 100644 --- a/statement/op_nonscalar.go +++ b/statement/op_nonscalar.go @@ -9,87 +9,87 @@ import ( type NonScalarOperation struct { op keyword.Operator column ValOrColOrFuncOrSub - params Subquery + params *Subquery } -func NewEqAll(params Subquery) NonScalarOperation { +func NewEqAll(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.EqAll, params: params, } } -func NewNotEqAll(params Subquery) NonScalarOperation { +func NewNotEqAll(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.NotEqAll, params: params, } } -func NewGtAll(params Subquery) NonScalarOperation { +func NewGtAll(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.GtAll, params: params, } } -func NewGteAll(params Subquery) NonScalarOperation { +func NewGteAll(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.GteAll, params: params, } } -func NewLtAll(params Subquery) NonScalarOperation { +func NewLtAll(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.LtAll, params: params, } } -func NewLteAll(params Subquery) NonScalarOperation { +func NewLteAll(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.LteAll, params: params, } } -func NewEqAny(params Subquery) NonScalarOperation { +func NewEqAny(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.EqAny, params: params, } } -func NewNotEqAny(params Subquery) NonScalarOperation { +func NewNotEqAny(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.NotEqAny, params: params, } } -func NewGtAny(params Subquery) NonScalarOperation { +func NewGtAny(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.GtAny, params: params, } } -func NewGteAny(params Subquery) NonScalarOperation { +func NewGteAny(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.GteAny, params: params, } } -func NewLtAny(params Subquery) NonScalarOperation { +func NewLtAny(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.LtAny, params: params, } } -func NewLteAny(params Subquery) NonScalarOperation { +func NewLteAny(params *Subquery) NonScalarOperation { return NonScalarOperation{ op: keyword.LteAny, params: params, @@ -120,17 +120,17 @@ func (n NonScalarOperation) keyword() keyword.Operator { type ExistanceOperation struct { op keyword.Operator - params Subquery + params *Subquery } -func NewExists(params Subquery) ExistanceOperation { +func NewExists(params *Subquery) ExistanceOperation { return ExistanceOperation{ op: keyword.Exists, params: params, } } -func NewNotExists(params Subquery) ExistanceOperation { +func NewNotExists(params *Subquery) ExistanceOperation { return ExistanceOperation{ op: keyword.NotExists, params: params, diff --git a/statement/set_operation.go b/statement/set_operation.go index 617dc1c..00ae137 100644 --- a/statement/set_operation.go +++ b/statement/set_operation.go @@ -14,38 +14,31 @@ type SetOperation struct { } func NewUnion(statements ...Statement) *SetOperation { - return &SetOperation{ - op: keyword.Union, - statements: statements, - } + return NewSetOperation(keyword.Union, statements...) } func NewUnionAll(statements ...Statement) *SetOperation { - return &SetOperation{ - op: keyword.UnionAll, - statements: statements, - } + return NewSetOperation(keyword.UnionAll, statements...) } func NewIntersect(statements ...Statement) *SetOperation { - return &SetOperation{ - op: keyword.Intersect, - statements: statements, - } + return NewSetOperation(keyword.Intersect, statements...) } func NewIntersectAll(statements ...Statement) *SetOperation { - return &SetOperation{ - op: keyword.IntersectAll, - statements: statements, - } + return NewSetOperation(keyword.IntersectAll, statements...) } func NewExcept(statements ...Statement) *SetOperation { - return &SetOperation{ - op: keyword.Except, - statements: statements, + return NewSetOperation(keyword.Except, statements...) +} + +func NewSetOperation(op keyword.Operator, stmts ...Statement) *SetOperation { + s := &SetOperation{ + op: op, + statements: stmts, } + return s } func NewExceptAll(statements ...Statement) *SetOperation { @@ -62,22 +55,17 @@ func (u *SetOperation) OrderBy(os ...Order) *OrderBy { } func (u *SetOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { - tokenizers := make(tokenizer.Tokenizers, len(u.statements)) - values := []interface{}{} + return tokenizer.NewLine(token.Word(u.op)), nil +} + +func (u *SetOperation) list() []Nodeizer { + ns := []Nodeizer{} for i, s := range u.statements { - t, vals := s.nodeize() - t = tokenizer.NewParentheses(t) - if i != 0 { - t = t.Prepend( - token.Word(u.keyword()), - ) + if i == 0 { + ns = append(ns, NewSubquery(s)) + continue } - tokenizers[i] = t - values = append(values, vals...) + ns = append(ns, u, NewSubquery(s)) } - return tokenizers, values -} - -func (u *SetOperation) keyword() keyword.Operator { - return u.op + return ns } diff --git a/statement/set_operation_test.go b/statement/set_operation_test.go index a890bc8..e37a06f 100644 --- a/statement/set_operation_test.go +++ b/statement/set_operation_test.go @@ -30,7 +30,8 @@ func TestUnionSQL(t *testing.T) { > FROM > aaa > ) -> UNION ( +> UNION +> ( > SELECT > b > ) @@ -56,22 +57,26 @@ func TestUnionSQL(t *testing.T) { > SELECT > a > ) -> UNION ( +> UNION +> ( > ( > ( > SELECT > b > ) -> UNION ( +> UNION +> ( > SELECT > c > ) > ) -> UNION ( +> UNION +> ( > SELECT > d > ) -> UNION ( +> UNION +> ( > SELECT > e > ) @@ -125,22 +130,26 @@ func TestUnionAllSQL(t *testing.T) { > SELECT > a > ) -> UNION ALL ( +> UNION ALL +> ( > ( > ( > SELECT > b > ) -> UNION ALL ( +> UNION ALL +> ( > SELECT > c > ) > ) -> UNION ALL ( +> UNION ALL +> ( > SELECT > d > ) -> UNION ALL ( +> UNION ALL +> ( > SELECT > e > ) @@ -194,22 +203,26 @@ func TestIntersectSQL(t *testing.T) { > SELECT > a > ) -> INTERSECT ( +> INTERSECT +> ( > ( > ( > SELECT > b > ) -> INTERSECT ( +> INTERSECT +> ( > SELECT > c > ) > ) -> INTERSECT ( +> INTERSECT +> ( > SELECT > d > ) -> INTERSECT ( +> INTERSECT +> ( > SELECT > e > ) @@ -263,22 +276,26 @@ func TestIntersectAllSQL(t *testing.T) { > SELECT > a > ) -> INTERSECT ALL ( +> INTERSECT ALL +> ( > ( > ( > SELECT > b > ) -> INTERSECT ALL ( +> INTERSECT ALL +> ( > SELECT > c > ) > ) -> INTERSECT ALL ( +> INTERSECT ALL +> ( > SELECT > d > ) -> INTERSECT ALL ( +> INTERSECT ALL +> ( > SELECT > e > ) @@ -332,22 +349,26 @@ func TestExceptSQL(t *testing.T) { > SELECT > a > ) -> EXCEPT ( +> EXCEPT +> ( > ( > ( > SELECT > b > ) -> EXCEPT ( +> EXCEPT +> ( > SELECT > c > ) > ) -> EXCEPT ( +> EXCEPT +> ( > SELECT > d > ) -> EXCEPT ( +> EXCEPT +> ( > SELECT > e > ) @@ -401,22 +422,26 @@ func TestExceptAllSQL(t *testing.T) { > SELECT > a > ) -> EXCEPT ALL ( +> EXCEPT ALL +> ( > ( > ( > SELECT > b > ) -> EXCEPT ALL ( +> EXCEPT ALL +> ( > SELECT > c > ) > ) -> EXCEPT ALL ( +> EXCEPT ALL +> ( > SELECT > d > ) -> EXCEPT ALL ( +> EXCEPT ALL +> ( > SELECT > e > ) diff --git a/statement/subquery.go b/statement/subquery.go index 6687514..aabc6b6 100644 --- a/statement/subquery.go +++ b/statement/subquery.go @@ -3,100 +3,101 @@ package statement import "github.com/minodisk/sqlabble/tokenizer" type Subquery struct { + Child statement Statement } -func NewSubquery(statement Statement) Subquery { - return Subquery{ +func NewSubquery(statement Statement) *Subquery { + return &Subquery{ statement: statement, } } -func (s Subquery) As(alias string) SubqueryAs { +func (s *Subquery) As(alias string) SubqueryAs { a := NewSubqueryAs(alias) a.subquery = s return a } -func (s Subquery) Eq(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) Eq(value ValOrColOrFuncOrSub) ComparisonOperation { e := NewEq(value) e.column = s return e } -func (s Subquery) NotEq(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) NotEq(value ValOrColOrFuncOrSub) ComparisonOperation { n := NewNotEq(value) n.column = s return n } -func (s Subquery) Gt(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) Gt(value ValOrColOrFuncOrSub) ComparisonOperation { g := NewGt(value) g.column = s return g } -func (s Subquery) Gte(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) Gte(value ValOrColOrFuncOrSub) ComparisonOperation { g := NewGte(value) g.column = s return g } -func (s Subquery) Lt(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) Lt(value ValOrColOrFuncOrSub) ComparisonOperation { l := NewLt(value) l.column = s return l } -func (s Subquery) Lte(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) Lte(value ValOrColOrFuncOrSub) ComparisonOperation { l := NewLte(value) l.column = s return l } -func (s Subquery) Like(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) Like(value ValOrColOrFuncOrSub) ComparisonOperation { l := NewLike(value) l.column = s return l } -func (s Subquery) RegExp(value ValOrColOrFuncOrSub) ComparisonOperation { +func (s *Subquery) RegExp(value ValOrColOrFuncOrSub) ComparisonOperation { r := NewRegExp(value) r.column = s return r } -func (s Subquery) Between(from, to ValOrColOrFuncOrSub) Between { +func (s *Subquery) Between(from, to ValOrColOrFuncOrSub) Between { b := NewBetween(from, to) b.column = s return b } -func (s Subquery) In(params ValsOrSub) ContainingOperation { +func (s *Subquery) In(params ValsOrSub) ContainingOperation { i := NewIn(params) i.column = s return i } -func (s Subquery) NotIn(params ValsOrSub) ContainingOperation { +func (s *Subquery) NotIn(params ValsOrSub) ContainingOperation { n := NewNotIn(params) n.column = s return n } -func (s Subquery) IsNull() NullOperation { +func (s *Subquery) IsNull() NullOperation { i := NewIsNull() i.column = s return i } -func (s Subquery) IsNotNull() NullOperation { +func (s *Subquery) IsNotNull() NullOperation { i := NewIsNotNull() i.column = s return i } -func (s Subquery) nodeize() (tokenizer.Tokenizer, []interface{}) { +func (s *Subquery) nodeize() (tokenizer.Tokenizer, []interface{}) { t, values := Nodize(s.statement) return tokenizer.NewParentheses(t), values } @@ -104,41 +105,41 @@ func (s Subquery) nodeize() (tokenizer.Tokenizer, []interface{}) { // isValsOrSub always returns true. // This method exists only to implement the interface isValsOrSub. // This is a shit of duck typing, but anyway it works. -func (s Subquery) isValsOrSub() bool { +func (s *Subquery) isValsOrSub() bool { return true } // isValOrFuncOrSub always returns true. // This method exists only to implement the interface ValOrFuncOrSub. // This is a shit of duck typing, but anyway it works. -func (s Subquery) isValOrFuncOrSub() bool { +func (s *Subquery) isValOrFuncOrSub() bool { return true } // isValOrColOrFuncOrSub always returns true. // This method exists only to implement the interface ValOrColOrFuncOrSub. // This is a shit of duck typing, but anyway it works. -func (s Subquery) isValOrColOrFuncOrSub() bool { +func (s *Subquery) isValOrColOrFuncOrSub() bool { return true } // isColOrSub always returns true. // This method exists only to implement the interface ColOrSub. // This is a shit of duck typing, but anyway it works. -func (s Subquery) isColOrSub() bool { +func (s *Subquery) isColOrSub() bool { return true } // isColOrAliasOrSub always returns true. // This method exists only to implement the interface ColOrAliasOrSub. // This is a shit of duck typing, but anyway it works. -func (s Subquery) isColOrAliasOrSub() bool { +func (s *Subquery) isColOrAliasOrSub() bool { return true } // isColOrAliasOrFuncOrSub always returns true. // This method exists only to implement the interface ColOrAliasOrFuncOrSub. // This is a shit of duck typing, but anyway it works. -func (s Subquery) isColOrAliasOrFuncOrSub() bool { +func (s *Subquery) isColOrAliasOrFuncOrSub() bool { return true } diff --git a/statement/subquery_as.go b/statement/subquery_as.go index 577b946..c639ee9 100644 --- a/statement/subquery_as.go +++ b/statement/subquery_as.go @@ -7,7 +7,7 @@ import ( ) type SubqueryAs struct { - subquery Subquery + subquery *Subquery alias string } diff --git a/statement/traverser.go b/statement/traverser.go index 01a7388..d8959cd 100644 --- a/statement/traverser.go +++ b/statement/traverser.go @@ -112,3 +112,8 @@ func Traverse(t Nodeizer) Nodeizer { } return t } + +type Lister interface { + Nodeizer + list() []Nodeizer +} From 4707024ce4991447727c1fb8ffc782af6bccd224 Mon Sep 17 00:00:00 2001 From: minodisk Date: Tue, 28 Feb 2017 11:06:28 +0900 Subject: [PATCH 7/8] Replace traversing clauses with traverser --- statement/helpers.go | 21 --------------------- statement/insert.go | 35 ++++++++++++++--------------------- statement/values.go | 32 ++++++++------------------------ 3 files changed, 22 insertions(+), 66 deletions(-) diff --git a/statement/helpers.go b/statement/helpers.go index 5d390bf..2c1ab1d 100644 --- a/statement/helpers.go +++ b/statement/helpers.go @@ -6,27 +6,6 @@ func Nodeize(stmt Statement) (tokenizer.Tokenizer, []interface{}) { return stmt.nodeize() } -func nodeizeClauses(c Clause) (tokenizer.Tokenizer, []interface{}) { - clauses := collectClauses(c) - ts := make(tokenizer.Tokenizers, len(clauses)) - values := []interface{}{} - for i, c := range clauses { - var vals []interface{} - ts[i], vals = c.self() - values = append(values, vals...) - } - return ts, values -} - -func collectClauses(c Clause) []Clause { - cs := []Clause{} - for c != nil { - cs = append([]Clause{c}, cs...) - c = c.previous() - } - return cs -} - func nodeizeJoiners(j Joiner) (tokenizer.Tokenizer, []interface{}) { joiners := collectJoiners(j) ts := make(tokenizer.Tokenizers, len(joiners)) diff --git a/statement/insert.go b/statement/insert.go index 87825aa..4c1bf3b 100644 --- a/statement/insert.go +++ b/statement/insert.go @@ -7,22 +7,31 @@ import ( ) type InsertInto struct { + Prev table Table columns []Column } -func NewInsertInto(table Table, columns ...Column) InsertInto { - return InsertInto{ +func NewInsertInto(table Table, columns ...Column) *InsertInto { + return &InsertInto{ table: table, columns: columns, } } -func (i InsertInto) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(i) +func (i *InsertInto) Values(paramses ...Params) *Values { + v := NewValues(paramses...) + Link(i, v) + return v +} + +func (i *InsertInto) DefaultValues() *DefaultValues { + v := NewDefaultValues() + Link(i, v) + return v } -func (i InsertInto) self() (tokenizer.Tokenizer, []interface{}) { +func (i *InsertInto) nodeize() (tokenizer.Tokenizer, []interface{}) { tableTokenizer, values := i.table.nodeize() ts := make(tokenizer.Tokenizers, len(i.columns)) for j, c := range i.columns { @@ -44,19 +53,3 @@ func (i InsertInto) self() (tokenizer.Tokenizer, []interface{}) { ), ), values } - -func (i InsertInto) previous() Clause { - return nil -} - -func (i InsertInto) Values(paramses ...Params) Values { - v := NewValues(paramses...) - v.prev = i - return v -} - -func (i InsertInto) DefaultValues() DefaultValues { - v := NewDefaultValues() - v.prev = i - return v -} diff --git a/statement/values.go b/statement/values.go index ef39654..2cd6ffd 100644 --- a/statement/values.go +++ b/statement/values.go @@ -7,21 +7,17 @@ import ( ) type Values struct { - prev Clause + Next paramses []Params } -func NewValues(paramses ...Params) Values { - return Values{ +func NewValues(paramses ...Params) *Values { + return &Values{ paramses: paramses, } } -func (v Values) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(v) -} - -func (v Values) self() (tokenizer.Tokenizer, []interface{}) { +func (v *Values) nodeize() (tokenizer.Tokenizer, []interface{}) { tokenizers := make(tokenizer.Tokenizers, len(v.paramses)) values := []interface{}{} for i, p := range v.paramses { @@ -39,28 +35,16 @@ func (v Values) self() (tokenizer.Tokenizer, []interface{}) { ), values } -func (v Values) previous() Clause { - return v.prev -} - type DefaultValues struct { - prev Clause + Next } -func NewDefaultValues() DefaultValues { - return DefaultValues{} +func NewDefaultValues() *DefaultValues { + return &DefaultValues{} } -func (v DefaultValues) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeClauses(v) -} - -func (v DefaultValues) self() (tokenizer.Tokenizer, []interface{}) { +func (v *DefaultValues) nodeize() (tokenizer.Tokenizer, []interface{}) { return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.DefaultValues)), ), nil } - -func (v DefaultValues) previous() Clause { - return v.prev -} From fe8919a4350dc3cc6cb375c15546a2cb8cb3cced Mon Sep 17 00:00:00 2001 From: minodisk Date: Tue, 28 Feb 2017 12:03:02 +0900 Subject: [PATCH 8/8] Implement the Prev/Next interface for all Clauses and Joiners --- builder/builder.go | 28 +++++++------- builder/builder_test.go | 3 +- statement/case_simple.go | 4 +- statement/create_table.go | 6 +-- statement/delete.go | 2 +- statement/from.go | 2 +- statement/from_test.go | 16 +++++++- statement/helpers.go | 29 ++------------- statement/insert.go | 4 +- statement/interfaces.go | 16 ++------ statement/join.go | 76 +++++++++++++++++--------------------- statement/join_test.go | 50 ++++++++++++++++--------- statement/nodeizer.go | 18 ++++----- statement/on.go | 45 ++++++++-------------- statement/on_test.go | 3 +- statement/set_operation.go | 4 +- statement/table.go | 59 ++++++++++++++--------------- statement/table_as.go | 54 ++++++++++++++------------- statement/table_as_test.go | 2 +- statement/table_test.go | 2 +- statement/traverser.go | 14 +++---- statement/update.go | 4 +- statement/using.go | 46 ++++++++--------------- statement/using_test.go | 3 +- 24 files changed, 226 insertions(+), 264 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index 981dc07..7047d56 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -28,21 +28,21 @@ func NewBuilder(format token.Format) Builder { } // Build converts a statement into a query and a slice of values. -func (b Builder) Build(stmt statement.Statement) (string, []interface{}) { - if n, ok := stmt.(statement.Nodeizer); ok { - root := statement.Traverse(n) - tokenizers, values := statement.Nodize(root) - sql := token.Generate(tokenizers.Tokenize(0), b.Format) - if len(values) == 0 { - values = nil - } - return sql, values - } - - tokenizer, values := statement.Nodeize(stmt) - query := token.Generate(tokenizer.Tokenize(0), b.Format) +func (b Builder) Build(n statement.Statement) (string, []interface{}) { + // if n, ok := stmt.(statement.Nodeizer); ok { + root := statement.Traverse(n) + tokenizers, values := statement.Nodize(root) + sql := token.Generate(tokenizers.Tokenize(0), b.Format) if len(values) == 0 { values = nil } - return query, values + return sql, values + // } + + // tokenizer, values := statement.Nodeize(stmt) + // query := token.Generate(tokenizer.Tokenize(0), b.Format) + // if len(values) == 0 { + // values = nil + // } + // return query, values } diff --git a/builder/builder_test.go b/builder/builder_test.go index 72e2ab7..0af5c8d 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -163,7 +163,8 @@ func TestBuilder(t *testing.T) { > OR title = ? > ) > ) -> UNION ( +> UNION +> ( > SELECT DISTINCT > open_emp_id > FROM diff --git a/statement/case_simple.go b/statement/case_simple.go index 14337c0..b800dce 100644 --- a/statement/case_simple.go +++ b/statement/case_simple.go @@ -37,7 +37,7 @@ type SimpleWhen struct { Prev Next param Param - paren Nodeizer + paren Statement } func NewSimpleWhen(param Param) *SimpleWhen { @@ -97,7 +97,7 @@ func (t SimpleThen) nodeize() (tokenizer.Tokenizer, []interface{}) { type Else struct { Next param ValOrColOrFuncOrSub - pre Nodeizer + pre Statement } func NewElse(param ValOrColOrFuncOrSub) *Else { diff --git a/statement/create_table.go b/statement/create_table.go index a44447f..249f0f2 100644 --- a/statement/create_table.go +++ b/statement/create_table.go @@ -9,17 +9,17 @@ import ( type CreateTable struct { Parent ifNotExists bool - table Table + table *Table } -func NewCreateTable(table Table) *CreateTable { +func NewCreateTable(table *Table) *CreateTable { return &CreateTable{ ifNotExists: false, table: table, } } -func NewCreateTableIfNotExists(table Table) *CreateTable { +func NewCreateTableIfNotExists(table *Table) *CreateTable { return &CreateTable{ ifNotExists: true, table: table, diff --git a/statement/delete.go b/statement/delete.go index 0e64247..eaf8270 100644 --- a/statement/delete.go +++ b/statement/delete.go @@ -14,7 +14,7 @@ func NewDelete() *Delete { return &Delete{} } -func (d *Delete) From(t Table) *From { +func (d *Delete) From(t *Table) *From { f := NewFrom(t) Link(d, f) return f diff --git a/statement/from.go b/statement/from.go index 1b315a6..113377d 100644 --- a/statement/from.go +++ b/statement/from.go @@ -43,7 +43,7 @@ func (f *From) Limit(count int) *Limit { } func (f *From) nodeize() (tokenizer.Tokenizer, []interface{}) { - middle, values := f.table.nodeize() + middle, values := Nodize(f.table) return tokenizer.NewContainer( tokenizer.NewLine(token.Word(keyword.From)), ).SetMiddle( diff --git a/statement/from_test.go b/statement/from_test.go index 26c32dc..350e851 100644 --- a/statement/from_test.go +++ b/statement/from_test.go @@ -44,7 +44,21 @@ func TestFromSQL(t *testing.T) { `FROM foo AS "a" JOIN bar USING id`, `> FROM > foo AS "a" -> JOIN bar USING id +> JOIN bar +> USING id +`, + nil, + }, + { + statement.NewFrom( + statement.NewTable("foo").As("a"). + Join(statement.NewTable("bar")).On(statement.NewColumn("a.id"), statement.NewColumn("bar.id")), + ), + `FROM foo AS "a" JOIN bar ON a.id = bar.id`, + `> FROM +> foo AS "a" +> JOIN bar +> ON a.id = bar.id `, nil, }, diff --git a/statement/helpers.go b/statement/helpers.go index 2c1ab1d..75d4f68 100644 --- a/statement/helpers.go +++ b/statement/helpers.go @@ -1,28 +1,5 @@ package statement -import "github.com/minodisk/sqlabble/tokenizer" - -func Nodeize(stmt Statement) (tokenizer.Tokenizer, []interface{}) { - return stmt.nodeize() -} - -func nodeizeJoiners(j Joiner) (tokenizer.Tokenizer, []interface{}) { - joiners := collectJoiners(j) - ts := make(tokenizer.Tokenizers, len(joiners)) - values := []interface{}{} - for i, j := range joiners { - var vals []interface{} - ts[i], vals = j.self() - values = append(values, vals...) - } - return ts, values -} - -func collectJoiners(c Joiner) []Joiner { - cs := []Joiner{} - for c != nil { - cs = append([]Joiner{c}, cs...) - c = c.previous() - } - return cs -} +// func Nodeize(stmt Statement) (tokenizer.Tokenizer, []interface{}) { +// return stmt.nodeize() +// } diff --git a/statement/insert.go b/statement/insert.go index 4c1bf3b..122566d 100644 --- a/statement/insert.go +++ b/statement/insert.go @@ -8,11 +8,11 @@ import ( type InsertInto struct { Prev - table Table + table *Table columns []Column } -func NewInsertInto(table Table, columns ...Column) *InsertInto { +func NewInsertInto(table *Table, columns ...Column) *InsertInto { return &InsertInto{ table: table, columns: columns, diff --git a/statement/interfaces.go b/statement/interfaces.go index 8dc1192..ad1c910 100644 --- a/statement/interfaces.go +++ b/statement/interfaces.go @@ -13,20 +13,12 @@ type Statement interface { nodeize() (tokenizer.Tokenizer, []interface{}) } -type Clause interface { - Statement - self() (tokenizer.Tokenizer, []interface{}) - previous() Clause -} - type Joiner interface { Statement - Join(TableOrAlias) Join - InnerJoin(TableOrAlias) Join - LeftJoin(TableOrAlias) Join - RightJoin(TableOrAlias) Join - self() (tokenizer.Tokenizer, []interface{}) - previous() Joiner + Join(TableOrAlias) *Join + InnerJoin(TableOrAlias) *Join + LeftJoin(TableOrAlias) *Join + RightJoin(TableOrAlias) *Join } type ValOrFuncOrSub interface { diff --git a/statement/join.go b/statement/join.go index 23e2048..e57477a 100644 --- a/statement/join.go +++ b/statement/join.go @@ -7,80 +7,77 @@ import ( ) type Join struct { + Prev + Next joinType string table TableOrAlias - prev Joiner } -func NewJoin(table TableOrAlias) Join { - return Join{ +func NewJoin(table TableOrAlias) *Join { + return &Join{ joinType: keyword.Join, table: table, } } -func NewInnerJoin(table TableOrAlias) Join { - return Join{ +func NewInnerJoin(table TableOrAlias) *Join { + return &Join{ joinType: keyword.InnerJoin, table: table, } } -func NewLeftJoin(table TableOrAlias) Join { - return Join{ +func NewLeftJoin(table TableOrAlias) *Join { + return &Join{ joinType: keyword.LeftJoin, table: table, } } -func NewRightJoin(table TableOrAlias) Join { - return Join{ +func NewRightJoin(table TableOrAlias) *Join { + return &Join{ joinType: keyword.RightJoin, table: table, } } -func (j Join) Join(table TableOrAlias) Join { - j1 := NewJoin(table) - j1.prev = j - return j1 +func (j *Join) Join(table TableOrAlias) *Join { + nj := NewJoin(table) + Link(j, nj) + return nj } -func (j Join) InnerJoin(table TableOrAlias) Join { - j1 := NewInnerJoin(table) - j1.prev = j - return j1 +func (j *Join) InnerJoin(table TableOrAlias) *Join { + nj := NewInnerJoin(table) + Link(j, nj) + return nj } -func (j Join) LeftJoin(table TableOrAlias) Join { - j1 := NewLeftJoin(table) - j1.prev = j - return j1 +func (j *Join) LeftJoin(table TableOrAlias) *Join { + nj := NewLeftJoin(table) + Link(j, nj) + return nj } -func (j Join) RightJoin(table TableOrAlias) Join { - j1 := NewRightJoin(table) - j1.prev = j - return j1 +func (j *Join) RightJoin(table TableOrAlias) *Join { + nj := NewRightJoin(table) + Link(j, nj) + return nj } -func (j Join) On(column1, column2 Column) On { +func (j *Join) On(column1, column2 Column) *On { o := NewOn(column1, column2) - o.join = j + Link(j, o) return o } -func (j Join) Using(col Column) Using { - o := NewUsing(col) - o.join = j - return o -} - -func (j Join) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeJoiners(j) +func (j *Join) Using(col Column) *Using { + u := NewUsing(col) + Link(j, u) + return u } -func (j Join) self() (tokenizer.Tokenizer, []interface{}) { +func (j *Join) nodeize() (tokenizer.Tokenizer, []interface{}) { if j.table == nil { return nil, nil } @@ -89,10 +86,3 @@ func (j Join) self() (tokenizer.Tokenizer, []interface{}) { token.Word(j.joinType), ), v } - -func (j Join) previous() Joiner { - if j.prev == nil { - return nil - } - return j.prev -} diff --git a/statement/join_test.go b/statement/join_test.go index 4d55def..5d3a7b2 100644 --- a/statement/join_test.go +++ b/statement/join_test.go @@ -11,7 +11,7 @@ import ( func TestJoinType(t *testing.T) { for _, c := range []interface{}{ - statement.Join{}, + &statement.Join{}, } { t.Run(fmt.Sprintf("%T", c), func(t *testing.T) { if _, ok := c.(statement.Joiner); !ok { @@ -122,7 +122,8 @@ func TestJoin(t *testing.T) { statement.NewColumn("bar.id"), ), `JOIN foo ON foo.id = bar.id`, - `> JOIN foo ON foo.id = bar.id + `> JOIN foo +> ON foo.id = bar.id `, nil, }, @@ -134,7 +135,8 @@ func TestJoin(t *testing.T) { statement.NewColumn("b.id"), ), `JOIN foo AS "f" ON f.id = b.id`, - `> JOIN foo AS "f" ON f.id = b.id + `> JOIN foo AS "f" +> ON f.id = b.id `, nil, }, @@ -145,7 +147,8 @@ func TestJoin(t *testing.T) { statement.NewColumn("id"), ), `JOIN foo USING id`, - `> JOIN foo USING id + `> JOIN foo +> USING id `, nil, }, @@ -156,7 +159,8 @@ func TestJoin(t *testing.T) { statement.NewColumn("id"), ), `JOIN foo AS "f" USING id`, - `> JOIN foo AS "f" USING id + `> JOIN foo AS "f" +> USING id `, nil, }, @@ -237,7 +241,8 @@ func TestInnerJoin(t *testing.T) { statement.NewColumn("bar.id"), ), `INNER JOIN foo ON foo.id = bar.id`, - `> INNER JOIN foo ON foo.id = bar.id + `> INNER JOIN foo +> ON foo.id = bar.id `, nil, }, @@ -250,7 +255,8 @@ func TestInnerJoin(t *testing.T) { statement.NewColumn("b.id"), ), `INNER JOIN foo AS "f" ON f.id = b.id`, - `> INNER JOIN foo AS "f" ON f.id = b.id + `> INNER JOIN foo AS "f" +> ON f.id = b.id `, nil, }, @@ -262,7 +268,8 @@ func TestInnerJoin(t *testing.T) { statement.NewColumn("id"), ), `INNER JOIN foo USING id`, - `> INNER JOIN foo USING id + `> INNER JOIN foo +> USING id `, nil, }, @@ -274,7 +281,8 @@ func TestInnerJoin(t *testing.T) { statement.NewColumn("id"), ), `INNER JOIN foo AS "f" USING id`, - `> INNER JOIN foo AS "f" USING id + `> INNER JOIN foo AS "f" +> USING id `, nil, }, @@ -353,7 +361,8 @@ func TestLeftJoin(t *testing.T) { statement.NewColumn("bar.id"), ), `LEFT JOIN foo ON foo.id = bar.id`, - `> LEFT JOIN foo ON foo.id = bar.id + `> LEFT JOIN foo +> ON foo.id = bar.id `, nil, }, @@ -365,7 +374,8 @@ func TestLeftJoin(t *testing.T) { statement.NewColumn("b.id"), ), `LEFT JOIN foo AS "f" ON f.id = b.id`, - `> LEFT JOIN foo AS "f" ON f.id = b.id + `> LEFT JOIN foo AS "f" +> ON f.id = b.id `, nil, }, @@ -376,7 +386,8 @@ func TestLeftJoin(t *testing.T) { statement.NewColumn("id"), ), `LEFT JOIN foo USING id`, - `> LEFT JOIN foo USING id + `> LEFT JOIN foo +> USING id `, nil, }, @@ -387,7 +398,8 @@ func TestLeftJoin(t *testing.T) { statement.NewColumn("id"), ), `LEFT JOIN foo AS "f" USING id`, - `> LEFT JOIN foo AS "f" USING id + `> LEFT JOIN foo AS "f" +> USING id `, nil, }, @@ -466,7 +478,8 @@ func TestRightJoin(t *testing.T) { statement.NewColumn("bar.id"), ), `RIGHT JOIN foo ON foo.id = bar.id`, - `> RIGHT JOIN foo ON foo.id = bar.id + `> RIGHT JOIN foo +> ON foo.id = bar.id `, nil, }, @@ -478,7 +491,8 @@ func TestRightJoin(t *testing.T) { statement.NewColumn("b.id"), ), `RIGHT JOIN foo AS "f" ON f.id = b.id`, - `> RIGHT JOIN foo AS "f" ON f.id = b.id + `> RIGHT JOIN foo AS "f" +> ON f.id = b.id `, nil, }, @@ -489,7 +503,8 @@ func TestRightJoin(t *testing.T) { statement.NewColumn("id"), ), `RIGHT JOIN foo USING id`, - `> RIGHT JOIN foo USING id + `> RIGHT JOIN foo +> USING id `, nil, }, @@ -500,7 +515,8 @@ func TestRightJoin(t *testing.T) { statement.NewColumn("id"), ), `RIGHT JOIN foo AS "f" USING id`, - `> RIGHT JOIN foo AS "f" USING id + `> RIGHT JOIN foo AS "f" +> USING id `, nil, }, diff --git a/statement/nodeizer.go b/statement/nodeizer.go index 304c485..896b598 100644 --- a/statement/nodeizer.go +++ b/statement/nodeizer.go @@ -2,18 +2,14 @@ package statement import "github.com/minodisk/sqlabble/tokenizer" -type Nodeizer interface { - nodeize() (tokenizer.Tokenizer, []interface{}) -} - -func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { +func Nodize(stmt Statement) (tokenizer.Tokenizers, []interface{}) { var tokenizers tokenizer.Tokenizers values := []interface{}{} - ns := []Nodeizer{} + ns := []Statement{} { - p := n + p := stmt for { prever, ok := p.(Prever) if !ok { @@ -23,18 +19,18 @@ func Nodize(n Nodeizer) (tokenizer.Tokenizers, []interface{}) { if p == nil { break } - ns = append([]Nodeizer{p}, ns...) + ns = append([]Statement{p}, ns...) } } - if c, ok := n.(Lister); ok { + if c, ok := stmt.(Lister); ok { ns = append(ns, c.list()...) } else { - ns = append(ns, n) + ns = append(ns, stmt) } { - p := n + p := stmt for { nexter, ok := p.(Nexter) if !ok { diff --git a/statement/on.go b/statement/on.go index a6abcdf..1da452c 100644 --- a/statement/on.go +++ b/statement/on.go @@ -7,71 +7,58 @@ import ( ) type On struct { - join Join + Prev + Next column1, column2 Column } -func NewOn(column1, column2 Column) On { - return On{ +func NewOn(column1, column2 Column) *On { + return &On{ column1: column1, column2: column2, } } -func (o On) Join(table TableOrAlias) Join { +func (o *On) Join(table TableOrAlias) *Join { j := NewJoin(table) - j.prev = o + Link(o, j) return j } -func (o On) InnerJoin(table TableOrAlias) Join { +func (o *On) InnerJoin(table TableOrAlias) *Join { j := NewInnerJoin(table) - j.prev = o + Link(o, j) return j } -func (o On) LeftJoin(table TableOrAlias) Join { +func (o *On) LeftJoin(table TableOrAlias) *Join { j := NewLeftJoin(table) - j.prev = o + Link(o, j) return j } -func (o On) RightJoin(table TableOrAlias) Join { +func (o *On) RightJoin(table TableOrAlias) *Join { j := NewRightJoin(table) - j.prev = o + Link(o, j) return j } -func (o On) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeJoiners(o) -} - -func (o On) self() (tokenizer.Tokenizer, []interface{}) { - t0, v0 := o.join.self() +func (o *On) nodeize() (tokenizer.Tokenizer, []interface{}) { t1, v1 := o.column1.nodeize() t2, v2 := o.column2.nodeize() return tokenizer.ConcatTokenizers( - t0, - tokenizer.ConcatTokenizers( t1, t2, tokenizer.NewLine( token.Word(keyword.Eq), ), - ), - tokenizer.NewLine( - token.Word(keyword.On), - ), - ), append(append(v0, v1...), v2...) -} - -func (o On) previous() Joiner { - return o.join.previous() + ).Prepend(token.Word(keyword.On)), + append(v1, v2...) } // isTableOrAliasOrJoiner always returns true. // This method exists only to implement the interface TableOrAliasOrJoiner. // This is a shit of duck typing, but anyway it works. -func (o On) isTableOrAliasOrJoiner() bool { +func (o *On) isTableOrAliasOrJoiner() bool { return true } diff --git a/statement/on_test.go b/statement/on_test.go index 4fd277d..1187ece 100644 --- a/statement/on_test.go +++ b/statement/on_test.go @@ -86,7 +86,8 @@ func TestOn(t *testing.T) { ), `foo AS "a" JOIN bar ON a.id = bar.id`, `> foo AS "a" -> JOIN bar ON a.id = bar.id +> JOIN bar +> ON a.id = bar.id `, nil, }, diff --git a/statement/set_operation.go b/statement/set_operation.go index 00ae137..5a7c9b5 100644 --- a/statement/set_operation.go +++ b/statement/set_operation.go @@ -58,8 +58,8 @@ func (u *SetOperation) nodeize() (tokenizer.Tokenizer, []interface{}) { return tokenizer.NewLine(token.Word(u.op)), nil } -func (u *SetOperation) list() []Nodeizer { - ns := []Nodeizer{} +func (u *SetOperation) list() []Statement { + ns := []Statement{} for i, s := range u.statements { if i == 0 { ns = append(ns, NewSubquery(s)) diff --git a/statement/table.go b/statement/table.go index 08cbe7c..01ac9ff 100644 --- a/statement/table.go +++ b/statement/table.go @@ -6,71 +6,68 @@ import ( ) type Table struct { + Prev + Next name string } -func NewTable(name string) Table { - return Table{ +func NewTable(name string) *Table { + return &Table{ name: name, } } -func (t Table) As(alias string) TableAs { - return TableAs{ - table: t, - alias: alias, - } -} - -func (t Table) Join(table TableOrAlias) Join { - nj := NewJoin(table) - nj.prev = t - return nj +func (t *Table) As(alias string) *TableAs { + a := NewTableAs(alias) + a.table = t + return a } -func (t Table) InnerJoin(table TableOrAlias) Join { - ij := NewInnerJoin(table) - ij.prev = t - return ij +func (t *Table) Join(table TableOrAlias) *Join { + j := NewJoin(table) + Link(t, j) + return j } -func (t Table) LeftJoin(table TableOrAlias) Join { - lj := NewLeftJoin(table) - lj.prev = t - return lj +func (t *Table) InnerJoin(table TableOrAlias) *Join { + j := NewInnerJoin(table) + Link(t, j) + return j } -func (t Table) RightJoin(table TableOrAlias) Join { - rj := NewRightJoin(table) - rj.prev = t - return rj +func (t *Table) LeftJoin(table TableOrAlias) *Join { + j := NewLeftJoin(table) + Link(t, j) + return j } -func (t Table) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeJoiners(t) +func (t *Table) RightJoin(table TableOrAlias) *Join { + j := NewRightJoin(table) + Link(t, j) + return j } -func (t Table) self() (tokenizer.Tokenizer, []interface{}) { +func (t *Table) nodeize() (tokenizer.Tokenizer, []interface{}) { if t.name == "" { return nil, nil } return tokenizer.NewLine(token.Word(t.name)), nil } -func (t Table) previous() Joiner { +func (t *Table) previous() Joiner { return nil } // isTableOrAlias always returns true. // This method exists only to implement the interface TableOrAlias. // This is a shit of duck typing, but anyway it works. -func (t Table) isTableOrAlias() bool { +func (t *Table) isTableOrAlias() bool { return true } // isTableOrAliasOrJoiner always returns true. // This method exists only to implement the interface TableOrAliasOrJoiner. // This is a shit of duck typing, but anyway it works. -func (t Table) isTableOrAliasOrJoiner() bool { +func (t *Table) isTableOrAliasOrJoiner() bool { return true } diff --git a/statement/table_as.go b/statement/table_as.go index 7065398..7823669 100644 --- a/statement/table_as.go +++ b/statement/table_as.go @@ -7,39 +7,47 @@ import ( ) type TableAs struct { - table Table + Prev + Next + table *Table alias string } -func (t TableAs) Join(table TableOrAlias) Join { - nj := NewJoin(table) - nj.prev = t - return nj +func NewTableAs(alias string) *TableAs { + return &TableAs{ + alias: alias, + } } -func (t TableAs) InnerJoin(table TableOrAlias) Join { - ij := NewInnerJoin(table) - ij.prev = t - return ij +func (t *TableAs) Join(table TableOrAlias) *Join { + j := NewJoin(table) + Link(t, j) + return j } -func (t TableAs) LeftJoin(table TableOrAlias) Join { - lj := NewLeftJoin(table) - lj.prev = t - return lj +func (t *TableAs) InnerJoin(table TableOrAlias) *Join { + j := NewInnerJoin(table) + Link(t, j) + return j } -func (t TableAs) RightJoin(table TableOrAlias) Join { - rj := NewRightJoin(table) - rj.prev = t - return rj +func (t *TableAs) LeftJoin(table TableOrAlias) *Join { + j := NewLeftJoin(table) + Link(t, j) + return j } -func (t TableAs) nodeize() (tokenizer.Tokenizer, []interface{}) { +func (t *TableAs) RightJoin(table TableOrAlias) *Join { + j := NewRightJoin(table) + Link(t, j) + return j +} + +func (t *TableAs) nodeize() (tokenizer.Tokenizer, []interface{}) { return t.self() } -func (t TableAs) self() (tokenizer.Tokenizer, []interface{}) { +func (t *TableAs) self() (tokenizer.Tokenizer, []interface{}) { t1, v1 := t.table.nodeize() t2 := tokenizer.NewLine( token.QuoteStart, @@ -56,20 +64,16 @@ func (t TableAs) self() (tokenizer.Tokenizer, []interface{}) { ), v1 } -func (t TableAs) previous() Joiner { - return nil -} - // isTableOrAlias always returns true. // This method exists only to implement the interface TableOrAlias. // This is a shit of duck typing, but anyway it works. -func (t TableAs) isTableOrAlias() bool { +func (t *TableAs) isTableOrAlias() bool { return true } // isTableOrAliasOrJoiner always returns true. // This method exists only to implement the interface TableOrAliasOrJoiner. // This is a shit of duck typing, but anyway it works. -func (t TableAs) isTableOrAliasOrJoiner() bool { +func (t *TableAs) isTableOrAliasOrJoiner() bool { return true } diff --git a/statement/table_as_test.go b/statement/table_as_test.go index 07183b6..77084c8 100644 --- a/statement/table_as_test.go +++ b/statement/table_as_test.go @@ -11,7 +11,7 @@ import ( func TestTableAsType(t *testing.T) { for _, c := range []interface{}{ - statement.TableAs{}, + &statement.TableAs{}, } { t.Run(fmt.Sprintf("%T", c), func(t *testing.T) { if _, ok := c.(statement.Joiner); !ok { diff --git a/statement/table_test.go b/statement/table_test.go index 86f4449..d2cf1af 100644 --- a/statement/table_test.go +++ b/statement/table_test.go @@ -11,7 +11,7 @@ import ( func TestTableType(t *testing.T) { for _, c := range []interface{}{ - statement.Table{}, + &statement.Table{}, } { t.Run(fmt.Sprintf("%T", c), func(t *testing.T) { if _, ok := c.(statement.Joiner); !ok { diff --git a/statement/traverser.go b/statement/traverser.go index d8959cd..cd43092 100644 --- a/statement/traverser.go +++ b/statement/traverser.go @@ -13,7 +13,7 @@ func Link(t1 Nexter, t2 Prever) { } type Childer interface { - Nodeizer + Statement children() []Parenter setChild(Parenter) } @@ -36,7 +36,7 @@ func (p *Parent) setChild(c Parenter) { } type Parenter interface { - Nodeizer + Statement parent() Childer setParent(Childer) } @@ -54,7 +54,7 @@ func (c *Child) setParent(p Childer) { } type Prever interface { - Nodeizer + Statement prev() Nexter setPrev(Nexter) } @@ -72,7 +72,7 @@ func (n *Next) setPrev(p Nexter) { } type Nexter interface { - Nodeizer + Statement next() Prever setNext(Prever) } @@ -89,7 +89,7 @@ func (p *Prev) setNext(n Prever) { p.n = n } -func Traverse(t Nodeizer) Nodeizer { +func Traverse(t Statement) Statement { { for p := t; p != nil; { t = p @@ -114,6 +114,6 @@ func Traverse(t Nodeizer) Nodeizer { } type Lister interface { - Nodeizer - list() []Nodeizer + Statement + list() []Statement } diff --git a/statement/update.go b/statement/update.go index ba55609..1e1909e 100644 --- a/statement/update.go +++ b/statement/update.go @@ -8,10 +8,10 @@ import ( type Update struct { Prev - table Table + table *Table } -func NewUpdate(table Table) *Update { +func NewUpdate(table *Table) *Update { return &Update{ table: table, } diff --git a/statement/using.go b/statement/using.go index 59711f3..73537fb 100644 --- a/statement/using.go +++ b/statement/using.go @@ -7,63 +7,49 @@ import ( ) type Using struct { - join Join + Prev + Next column Column } -func NewUsing(column Column) Using { - return Using{ +func NewUsing(column Column) *Using { + return &Using{ column: column, } } -func (u Using) Join(table TableOrAlias) Join { +func (u *Using) Join(table TableOrAlias) *Join { j := NewJoin(table) - j.prev = u + Link(u, j) return j } -func (u Using) InnerJoin(table TableOrAlias) Join { +func (u *Using) InnerJoin(table TableOrAlias) *Join { j := NewInnerJoin(table) - j.prev = u + Link(u, j) return j } -func (u Using) LeftJoin(table TableOrAlias) Join { +func (u *Using) LeftJoin(table TableOrAlias) *Join { j := NewLeftJoin(table) - j.prev = u + Link(u, j) return j } -func (u Using) RightJoin(table TableOrAlias) Join { +func (u *Using) RightJoin(table TableOrAlias) *Join { j := NewRightJoin(table) - j.prev = u + Link(u, j) return j } -func (u Using) nodeize() (tokenizer.Tokenizer, []interface{}) { - return nodeizeJoiners(u) -} - -func (u Using) self() (tokenizer.Tokenizer, []interface{}) { - t1, v1 := u.join.self() - t2, v2 := u.column.nodeize() - return tokenizer.ConcatTokenizers( - t1, - t2, - tokenizer.NewLine( - token.Word(keyword.Using), - ), - ), append(v1, v2...) -} - -func (u Using) previous() Joiner { - return u.join.previous() +func (u *Using) nodeize() (tokenizer.Tokenizer, []interface{}) { + t1, v1 := u.column.nodeize() + return t1.Prepend(token.Word(keyword.Using)), v1 } // isTableOrAliasOrJoiner always returns true. // This method exists only to implement the interface TableOrAliasOrJoiner. // This is a shit of duck typing, but anyway it works. -func (u Using) isTableOrAliasOrJoiner() bool { +func (u *Using) isTableOrAliasOrJoiner() bool { return true } diff --git a/statement/using_test.go b/statement/using_test.go index 2a074c2..1b35497 100644 --- a/statement/using_test.go +++ b/statement/using_test.go @@ -78,7 +78,8 @@ func TestUsing(t *testing.T) { Join(statement.NewTable("bar")).Using(statement.NewColumn("id")), `foo AS "a" JOIN bar USING id`, `> foo AS "a" -> JOIN bar USING id +> JOIN bar +> USING id `, nil, },