forked from mgutz/dat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
scope.go
114 lines (95 loc) · 2.5 KB
/
scope.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package dat
import (
"bytes"
"regexp"
"strings"
)
// M is a generic map from string to interface{}
type M map[string]interface{}
var reField = regexp.MustCompile(`\B:[A-Za-z_]\w*`)
// Scope predefines parameterized JOIN and WHERE conditions.
type Scope interface {
ToSQL(table string) (string, []interface{})
}
// ScopeFunc is an ad-hoc scope function.
type ScopeFunc func(table string) (string, []interface{})
// ToSQL converts scoped func to sql.
func (sf ScopeFunc) ToSQL(table string) (string, []interface{}) {
return sf(table)
}
// MapScope defines scope for using a fields map.
type MapScope struct {
SQL string
Fields M
}
// NewScope creates a new scope.
func NewScope(sql string, fields M) *MapScope {
return &MapScope{SQL: sql, Fields: fields}
}
// Clone creates a clone of scope and merges fields.
func (scope *MapScope) mergeClone(fields M) *MapScope {
newm := M{}
for key, val := range scope.Fields {
if fields != nil {
if val, ok := fields[key]; ok {
newm[key] = val
continue
}
}
newm[key] = val
}
clone := &MapScope{SQL: scope.SQL, Fields: newm}
return clone
}
// ToSQL converts this scope's SQL to SQL and args.
func (scope *MapScope) ToSQL(table string) (string, []interface{}) {
buf := bufPool.Get()
defer bufPool.Put(buf)
var n = 1
var args []interface{}
sql := reField.ReplaceAllStringFunc(scope.SQL, func(found string) string {
buf.Reset()
if found == ":TABLE" {
Dialect.WriteIdentifier(buf, table)
return buf.String()
}
if args == nil {
args = []interface{}{}
}
field := found[1:]
args = append(args, scope.Fields[field])
writePlaceholder(buf, n)
n++
return buf.String()
})
return sql, args
}
// escapeScopeTable escapes :TABLE in sql using Dialect.WriteIdentifer.
func escapeScopeTable(sql string, table string) string {
if !strings.Contains(sql, ":TABLE") {
return sql
}
var buf bytes.Buffer
Dialect.WriteIdentifier(&buf, table)
quoted := buf.String()
return strings.Replace(sql, ":TABLE", quoted, -1)
}
var reWhereClause = regexp.MustCompile(`\s*(WHERE|where)\b`)
// splitWhere splits a query on the word WHERE
func splitWhere(query string) (sql string, where string) {
indices := reWhereClause.FindStringIndex(query)
// grab only the first location
if len(indices) == 0 {
return query, ""
}
// may have leading spaces
where = query[indices[0]:]
idx := strings.Index(where, "WHERE")
if idx == -1 {
idx = strings.Index(where, "where")
}
// 5 == len("WHERE")
where = where[idx+5:]
sql = query[0:indices[0]]
return sql, where
}