1package exql
2
3import (
4	"bytes"
5	"reflect"
6	"sync"
7	"text/template"
8
9	db "upper.io/db.v3"
10	"upper.io/db.v3/internal/cache"
11)
12
13// Type is the type of SQL query the statement represents.
14type Type uint
15
16// Values for Type.
17const (
18	NoOp = Type(iota)
19
20	Truncate
21	DropTable
22	DropDatabase
23	Count
24	Insert
25	Select
26	Update
27	Delete
28
29	SQL
30)
31
32type (
33	// Limit represents the SQL limit in a query.
34	Limit int
35	// Offset represents the SQL offset in a query.
36	Offset int
37)
38
39// Template is an SQL template.
40type Template struct {
41	AndKeyword          string
42	AscKeyword          string
43	AssignmentOperator  string
44	ClauseGroup         string
45	ClauseOperator      string
46	ColumnAliasLayout   string
47	ColumnSeparator     string
48	ColumnValue         string
49	CountLayout         string
50	DeleteLayout        string
51	DescKeyword         string
52	DropDatabaseLayout  string
53	DropTableLayout     string
54	GroupByLayout       string
55	IdentifierQuote     string
56	IdentifierSeparator string
57	InsertLayout        string
58	JoinLayout          string
59	OnLayout            string
60	OrKeyword           string
61	OrderByLayout       string
62	SelectLayout        string
63	SortByColumnLayout  string
64	TableAliasLayout    string
65	TruncateLayout      string
66	UpdateLayout        string
67	UsingLayout         string
68	ValueQuote          string
69	ValueSeparator      string
70	WhereLayout         string
71
72	ComparisonOperator map[db.ComparisonOperator]string
73
74	templateMutex sync.RWMutex
75	templateMap   map[string]*template.Template
76
77	*cache.Cache
78}
79
80func (layout *Template) MustCompile(templateText string, data interface{}) string {
81	var b bytes.Buffer
82
83	v, ok := layout.getTemplate(templateText)
84	if !ok || true {
85		v = template.
86			Must(template.New("").
87				Funcs(map[string]interface{}{
88					"defined": func(in Fragment) bool {
89						if in == nil || reflect.ValueOf(in).IsNil() {
90							return false
91						}
92						if check, ok := in.(hasIsEmpty); ok {
93							if check.IsEmpty() {
94								return false
95							}
96						}
97						return true
98					},
99					"compile": func(in Fragment) (string, error) {
100						s, err := layout.doCompile(in)
101						if err != nil {
102							return "", err
103						}
104						return s, nil
105					},
106				}).
107				Parse(templateText))
108
109		layout.setTemplate(templateText, v)
110	}
111
112	if err := v.Execute(&b, data); err != nil {
113		panic("There was an error compiling the following template:\n" + templateText + "\nError was: " + err.Error())
114	}
115
116	return b.String()
117}
118
119func (t *Template) getTemplate(k string) (*template.Template, bool) {
120	t.templateMutex.RLock()
121	defer t.templateMutex.RUnlock()
122
123	if t.templateMap == nil {
124		t.templateMap = make(map[string]*template.Template)
125	}
126
127	v, ok := t.templateMap[k]
128	return v, ok
129}
130
131func (t *Template) setTemplate(k string, v *template.Template) {
132	t.templateMutex.Lock()
133	defer t.templateMutex.Unlock()
134
135	t.templateMap[k] = v
136}
137