1package exql
2
3import (
4	"errors"
5	"reflect"
6	"strings"
7)
8
9var errUnknownTemplateType = errors.New("Unknown template type")
10
11//  represents different kinds of SQL statements.
12type Statement struct {
13	Type
14	Table        Fragment
15	Database     Fragment
16	Columns      Fragment
17	Values       Fragment
18	Distinct     bool
19	ColumnValues Fragment
20	OrderBy      Fragment
21	GroupBy      Fragment
22	Joins        Fragment
23	Where        Fragment
24	Returning    Fragment
25
26	Limit
27	Offset
28
29	SQL string
30
31	hash    hash
32	amendFn func(string) string
33}
34
35func (layout *Template) doCompile(c Fragment) (string, error) {
36	if c != nil && !reflect.ValueOf(c).IsNil() {
37		return c.Compile(layout)
38	}
39	return "", nil
40}
41
42// Hash returns a unique identifier for the struct.
43func (s *Statement) Hash() string {
44	return s.hash.Hash(s)
45}
46
47func (s *Statement) SetAmendment(amendFn func(string) string) {
48	s.amendFn = amendFn
49}
50
51func (s *Statement) Amend(in string) string {
52	if s.amendFn == nil {
53		return in
54	}
55	return s.amendFn(in)
56}
57
58func (s *Statement) template(layout *Template) (string, error) {
59	switch s.Type {
60	case Truncate:
61		return layout.TruncateLayout, nil
62	case DropTable:
63		return layout.DropTableLayout, nil
64	case DropDatabase:
65		return layout.DropDatabaseLayout, nil
66	case Count:
67		return layout.CountLayout, nil
68	case Select:
69		return layout.SelectLayout, nil
70	case Delete:
71		return layout.DeleteLayout, nil
72	case Update:
73		return layout.UpdateLayout, nil
74	case Insert:
75		return layout.InsertLayout, nil
76	default:
77		return "", errUnknownTemplateType
78	}
79}
80
81// Compile transforms the Statement into an equivalent SQL query.
82func (s *Statement) Compile(layout *Template) (compiled string, err error) {
83	if s.Type == SQL {
84		// No need to hit the cache.
85		return s.SQL, nil
86	}
87
88	if z, ok := layout.Read(s); ok {
89		return s.Amend(z), nil
90	}
91
92	tpl, err := s.template(layout)
93	if err != nil {
94		return "", err
95	}
96
97	compiled = layout.MustCompile(tpl, s)
98
99	compiled = strings.TrimSpace(compiled)
100	layout.Write(s, compiled)
101
102	return s.Amend(compiled), nil
103}
104
105// RawSQL represents a raw SQL statement.
106func RawSQL(s string) *Statement {
107	return &Statement{
108		Type: SQL,
109		SQL:  s,
110	}
111}
112