@@ -14,16 +14,65 @@ type templateEntry struct {
1414 fullPath string
1515}
1616
17- // bindings stores the values of any placeholder parameter in the query.
18- type bindings struct {
19- values []interface {}
17+ type placeholderFunc func (value any , index int ) string
18+
19+ func defaultPlaceholderFunc (_ any , _ int ) string { return "?" }
20+
21+ type bindingEngine interface {
22+ // StoreValue stores the given `value` and return the index order of
23+ // the stored value.
24+ storeValue (any ) int
25+ // GetValues get the stored values.
26+ getValues () []any
27+ // new returns an new instance.
28+ new () bindingEngine
29+ // SetPlaceholderFunc allows to set custom placeholderFunc.
30+ SetPlaceholderFunc (placeholderFunc )
31+ // getPlaceholderFunc returns the placeholder function.
32+ getPlaceholderFunc () placeholderFunc
2033}
2134
22- // bind stores the given `value` and returns a placeholder parameter.
23- func (b * bindings ) bind (value interface {}) string {
35+ // DefaultBindingEngine is a base engine to handle bindings (placeholder "?" for MySQL-like dbe).
36+ // It can be oveload to provide other type if bindings (placeholder "$i" for PostgreSQL-like dbe).
37+ type DefaultBindingEngine struct {
38+ values []any
39+ index int
40+ placeholderFunc placeholderFunc
41+ }
42+
43+ func (b * DefaultBindingEngine ) new () bindingEngine {
44+ newBE := NewBindingEngine ()
45+ newBE .SetPlaceholderFunc (b .placeholderFunc )
46+
47+ return newBE
48+ }
49+
50+ func (b * DefaultBindingEngine ) getPlaceholderFunc () placeholderFunc {
51+ if b .placeholderFunc == nil {
52+ return defaultPlaceholderFunc
53+ }
54+
55+ return b .placeholderFunc
56+ }
57+
58+ // SetPlaceholderFunc allows to set custom placeholder function.
59+ func (b * DefaultBindingEngine ) SetPlaceholderFunc (placeholderfunc placeholderFunc ) {
60+ b .placeholderFunc = placeholderfunc
61+ }
62+
63+ func (b * DefaultBindingEngine ) storeValue (value any ) int {
2464 b .values = append (b .values , value )
65+ b .index ++
2566
26- return "?"
67+ return b .index - 1
68+ }
69+
70+ func (b * DefaultBindingEngine ) getValues () []any {
71+ return b .values
72+ }
73+
74+ func NewBindingEngine () bindingEngine {
75+ return & DefaultBindingEngine {values : []any {}, index : 0 , placeholderFunc : defaultPlaceholderFunc }
2776}
2877
2978// repository stores SQL templates.
@@ -68,34 +117,42 @@ func (r *repository) add(namespace string, filesystem fs.FS, extension string) e
68117}
69118
70119// parse executes the template and returns the resulting SQL or an error.
71- func (r * repository ) parse (namespace string , name string , data interface {}) (string , []interface {}, error ) {
120+ func (r * repository ) parse (namespace string , name string , data interface {}, bEngine bindingEngine ) (string , []interface {}, error ) {
72121 entry , ok := r .templates [namespace ]
73122 if ! ok {
74123 return "" , nil , errors .New ("unable to locate namespace " + namespace )
75124 }
76125
77126 // We clone the template to prevent simultaneous mutation of the template.FuncMap
78- // otherwise the bind function might be replaced during execution of a template
127+ // otherwise the binds functions might be replaced during execution of a template
79128 clonedTmpl , err := entry .template .Clone ()
80129 if err != nil {
81130 return "" , nil , fmt .Errorf ("unable to parse template %w" , err )
82131 }
83132
84133 // Apply the bind function which stores the values for any placeholder parameters
85- values := & bindings {values : []interface {}{}}
134+ if bEngine == nil {
135+ bEngine = & DefaultBindingEngine {values : []any {}, index : 0 , placeholderFunc : defaultPlaceholderFunc }
136+ } else {
137+ bEngine = bEngine .new ()
138+ }
139+
140+ clonedTmpl .Funcs (template.FuncMap {"bind" : func (value any ) string {
141+ index := bEngine .storeValue (value )
86142
87- clonedTmpl .Funcs (template.FuncMap {"bind" : values .bind })
143+ return bEngine .getPlaceholderFunc ()(value , index )
144+ }})
88145
89146 var b bytes.Buffer
90147 if err := clonedTmpl .ExecuteTemplate (& b , name , data ); err != nil {
91148 return "" , nil , fmt .Errorf ("unable to execute template %w" , err )
92149 }
93150
94- return b .String (), values . values , nil
151+ return b .String (), bEngine . getValues () , nil
95152}
96153
97154// bind is a dummy function which is never used while executing a template.
98- func bind (param interface {}) string {
155+ func bind (_ interface {}) string {
99156 return "?"
100157}
101158
0 commit comments