bind.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package sqlx
  2. import (
  3. "bytes"
  4. "errors"
  5. "reflect"
  6. "strconv"
  7. "strings"
  8. "github.com/jmoiron/sqlx/reflectx"
  9. )
  10. // Bindvar types supported by Rebind, BindMap and BindStruct.
  11. const (
  12. UNKNOWN = iota
  13. QUESTION
  14. DOLLAR
  15. NAMED
  16. )
  17. // BindType returns the bindtype for a given database given a drivername.
  18. func BindType(driverName string) int {
  19. switch driverName {
  20. case "postgres", "pgx", "pq-timeouts":
  21. return DOLLAR
  22. case "mysql":
  23. return QUESTION
  24. case "sqlite3":
  25. return QUESTION
  26. case "oci8", "ora", "goracle":
  27. return NAMED
  28. }
  29. return UNKNOWN
  30. }
  31. // FIXME: this should be able to be tolerant of escaped ?'s in queries without
  32. // losing much speed, and should be to avoid confusion.
  33. // Rebind a query from the default bindtype (QUESTION) to the target bindtype.
  34. func Rebind(bindType int, query string) string {
  35. switch bindType {
  36. case QUESTION, UNKNOWN:
  37. return query
  38. }
  39. // Add space enough for 10 params before we have to allocate
  40. rqb := make([]byte, 0, len(query)+10)
  41. var i, j int
  42. for i = strings.Index(query, "?"); i != -1; i = strings.Index(query, "?") {
  43. rqb = append(rqb, query[:i]...)
  44. switch bindType {
  45. case DOLLAR:
  46. rqb = append(rqb, '$')
  47. case NAMED:
  48. rqb = append(rqb, ':', 'a', 'r', 'g')
  49. }
  50. j++
  51. rqb = strconv.AppendInt(rqb, int64(j), 10)
  52. query = query[i+1:]
  53. }
  54. return string(append(rqb, query...))
  55. }
  56. // Experimental implementation of Rebind which uses a bytes.Buffer. The code is
  57. // much simpler and should be more resistant to odd unicode, but it is twice as
  58. // slow. Kept here for benchmarking purposes and to possibly replace Rebind if
  59. // problems arise with its somewhat naive handling of unicode.
  60. func rebindBuff(bindType int, query string) string {
  61. if bindType != DOLLAR {
  62. return query
  63. }
  64. b := make([]byte, 0, len(query))
  65. rqb := bytes.NewBuffer(b)
  66. j := 1
  67. for _, r := range query {
  68. if r == '?' {
  69. rqb.WriteRune('$')
  70. rqb.WriteString(strconv.Itoa(j))
  71. j++
  72. } else {
  73. rqb.WriteRune(r)
  74. }
  75. }
  76. return rqb.String()
  77. }
  78. // In expands slice values in args, returning the modified query string
  79. // and a new arg list that can be executed by a database. The `query` should
  80. // use the `?` bindVar. The return value uses the `?` bindVar.
  81. func In(query string, args ...interface{}) (string, []interface{}, error) {
  82. // argMeta stores reflect.Value and length for slices and
  83. // the value itself for non-slice arguments
  84. type argMeta struct {
  85. v reflect.Value
  86. i interface{}
  87. length int
  88. }
  89. var flatArgsCount int
  90. var anySlices bool
  91. meta := make([]argMeta, len(args))
  92. for i, arg := range args {
  93. v := reflect.ValueOf(arg)
  94. t := reflectx.Deref(v.Type())
  95. if t.Kind() == reflect.Slice {
  96. meta[i].length = v.Len()
  97. meta[i].v = v
  98. anySlices = true
  99. flatArgsCount += meta[i].length
  100. if meta[i].length == 0 {
  101. return "", nil, errors.New("empty slice passed to 'in' query")
  102. }
  103. } else {
  104. meta[i].i = arg
  105. flatArgsCount++
  106. }
  107. }
  108. // don't do any parsing if there aren't any slices; note that this means
  109. // some errors that we might have caught below will not be returned.
  110. if !anySlices {
  111. return query, args, nil
  112. }
  113. newArgs := make([]interface{}, 0, flatArgsCount)
  114. buf := bytes.NewBuffer(make([]byte, 0, len(query)+len(", ?")*flatArgsCount))
  115. var arg, offset int
  116. for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
  117. if arg >= len(meta) {
  118. // if an argument wasn't passed, lets return an error; this is
  119. // not actually how database/sql Exec/Query works, but since we are
  120. // creating an argument list programmatically, we want to be able
  121. // to catch these programmer errors earlier.
  122. return "", nil, errors.New("number of bindVars exceeds arguments")
  123. }
  124. argMeta := meta[arg]
  125. arg++
  126. // not a slice, continue.
  127. // our questionmark will either be written before the next expansion
  128. // of a slice or after the loop when writing the rest of the query
  129. if argMeta.length == 0 {
  130. offset = offset + i + 1
  131. newArgs = append(newArgs, argMeta.i)
  132. continue
  133. }
  134. // write everything up to and including our ? character
  135. buf.WriteString(query[:offset+i+1])
  136. for si := 1; si < argMeta.length; si++ {
  137. buf.WriteString(", ?")
  138. }
  139. newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)
  140. // slice the query and reset the offset. this avoids some bookkeeping for
  141. // the write after the loop
  142. query = query[offset+i+1:]
  143. offset = 0
  144. }
  145. buf.WriteString(query)
  146. if arg < len(meta) {
  147. return "", nil, errors.New("number of bindVars less than number arguments")
  148. }
  149. return buf.String(), newArgs, nil
  150. }
  151. func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {
  152. switch val := v.Interface().(type) {
  153. case []interface{}:
  154. args = append(args, val...)
  155. case []int:
  156. for i := range val {
  157. args = append(args, val[i])
  158. }
  159. case []string:
  160. for i := range val {
  161. args = append(args, val[i])
  162. }
  163. default:
  164. for si := 0; si < vlen; si++ {
  165. args = append(args, v.Index(si).Interface())
  166. }
  167. }
  168. return args
  169. }