@@ -6,9 +6,12 @@ package sqlbuilder
66import (
77 "database/sql"
88 "fmt"
9+ "reflect"
910 "sort"
1011 "strconv"
1112 "strings"
13+
14+ "github.com/huandu/go-clone"
1215)
1316
1417// Args stores arguments associated with a SQL.
@@ -17,7 +20,7 @@ type Args struct {
1720 Flavor Flavor
1821
1922 indexBase int
20- argValues [] interface {}
23+ argValues * valueStore
2124 namedArgs map [string ]int
2225 sqlNamedArgs map [string ]int
2326 onlyNamed bool
@@ -48,7 +51,7 @@ func (args *Args) Add(arg interface{}) string {
4851}
4952
5053func (args * Args ) add (arg interface {}) int {
51- idx := len ( args .argValues ) + args .indexBase
54+ idx := args .argValues . Len ( ) + args .indexBase
5255
5356 switch a := arg .(type ) {
5457 case sql.NamedArg :
@@ -57,7 +60,7 @@ func (args *Args) add(arg interface{}) int {
5760 }
5861
5962 if p , ok := args .sqlNamedArgs [a .Name ]; ok {
60- arg = args .argValues [ p ]
63+ arg = args .argValues . Load ( p )
6164 break
6265 }
6366
@@ -68,7 +71,7 @@ func (args *Args) add(arg interface{}) int {
6871 }
6972
7073 if p , ok := args .namedArgs [a .name ]; ok {
71- arg = args .argValues [ p ]
74+ arg = args .argValues . Load ( p )
7275 break
7376 }
7477
@@ -78,10 +81,31 @@ func (args *Args) add(arg interface{}) int {
7881 return idx
7982 }
8083
81- args .argValues = append (args .argValues , arg )
84+ if args .argValues == nil {
85+ args .argValues = & valueStore {}
86+ }
87+
88+ args .argValues .Add (arg )
8289 return idx
8390}
8491
92+ // Replace replaces the placeholder with arg.
93+ //
94+ // The placeholder must be the value returned by `Add`, e.g. "$1".
95+ // If the placeholder is not found, this method does nothing.
96+ func (args * Args ) Replace (placeholder string , arg interface {}) {
97+ dollar := strings .IndexRune (placeholder , '$' )
98+
99+ if dollar != 0 {
100+ return
101+ }
102+
103+ if i , err := strconv .Atoi (placeholder [1 :]); err == nil {
104+ i -= args .indexBase
105+ args .argValues .Set (i , arg )
106+ }
107+ }
108+
85109// Compile compiles builder's format to standard sql and returns associated args.
86110//
87111// The format string uses a special syntax to represent arguments.
@@ -201,14 +225,14 @@ func (args *Args) compileDigits(ctx *argsCompileContext, format string, offset i
201225}
202226
203227func (args * Args ) compileSuccessive (ctx * argsCompileContext , format string , offset int ) (string , int ) {
204- if offset < 0 || offset >= len ( args .argValues ) {
228+ if offset < 0 || offset >= args .argValues . Len ( ) {
205229 ctx .WriteString ("/* INVALID ARG $" )
206230 ctx .WriteString (strconv .Itoa (offset ))
207231 ctx .WriteString (" */" )
208232 return format , offset
209233 }
210234
211- arg := args .argValues [ offset ]
235+ arg := args .argValues . Load ( offset )
212236 ctx .WriteValue (arg )
213237
214238 return format , offset + 1
@@ -245,7 +269,7 @@ func (args *Args) mergeSQLNamedArgs(ctx *argsCompileContext) []interface{} {
245269 sort .Ints (ints )
246270
247271 for _ , i := range ints {
248- values = append (values , args .argValues [ i ] )
272+ values = append (values , args .argValues . Load ( i ) )
249273 }
250274
251275 return values
@@ -364,3 +388,50 @@ func (ctx *argsCompileContext) WriteValues(values []interface{}, sep string) {
364388 ctx .WriteValue (v )
365389 }
366390}
391+
392+ type valueStore struct {
393+ Values []interface {}
394+ }
395+
396+ func init () {
397+ // The values in valueStore should be shadow-copied to avoid unnecessary cost.
398+ t := reflect .TypeOf (valueStore {})
399+ clone .SetCustomFunc (t , func (allocator * clone.Allocator , old , new reflect.Value ) {
400+ values := old .FieldByName ("Values" )
401+ newValues := allocator .Clone (values )
402+ new .FieldByName ("Values" ).Set (newValues )
403+ })
404+ }
405+
406+ func (as * valueStore ) Len () int {
407+ if as == nil {
408+ return 0
409+ }
410+
411+ return len (as .Values )
412+ }
413+
414+ // Add adds an arg to argsValues and returns its index.
415+ func (as * valueStore ) Add (arg interface {}) int {
416+ as .Values = append (as .Values , arg )
417+ return len (as .Values ) - 1
418+ }
419+
420+ // Set sets the arg value by index.
421+ func (as * valueStore ) Set (index int , arg interface {}) {
422+ if as == nil || index < 0 || index >= len (as .Values ) {
423+ return
424+ }
425+
426+ as .Values [index ] = arg
427+ }
428+
429+ // Load returns the arg value by index.
430+ // Returns nil if index is out of range or as itself is nil.
431+ func (as * valueStore ) Load (index int ) interface {} {
432+ if as == nil || index < 0 || index >= len (as .Values ) {
433+ return nil
434+ }
435+
436+ return as .Values [index ]
437+ }
0 commit comments