mirror of
https://github.com/documize/community.git
synced 2025-07-19 13:19:43 +02:00
initial commit
This commit is contained in:
commit
18933c6767
1841 changed files with 810642 additions and 0 deletions
186
vendor/github.com/jmoiron/sqlx/bind.go
generated
vendored
Normal file
186
vendor/github.com/jmoiron/sqlx/bind.go
generated
vendored
Normal file
|
@ -0,0 +1,186 @@
|
|||
package sqlx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx/reflectx"
|
||||
)
|
||||
|
||||
// Bindvar types supported by Rebind, BindMap and BindStruct.
|
||||
const (
|
||||
UNKNOWN = iota
|
||||
QUESTION
|
||||
DOLLAR
|
||||
NAMED
|
||||
)
|
||||
|
||||
// BindType returns the bindtype for a given database given a drivername.
|
||||
func BindType(driverName string) int {
|
||||
switch driverName {
|
||||
case "postgres", "pgx":
|
||||
return DOLLAR
|
||||
case "mysql":
|
||||
return QUESTION
|
||||
case "sqlite3":
|
||||
return QUESTION
|
||||
case "oci8":
|
||||
return NAMED
|
||||
}
|
||||
return UNKNOWN
|
||||
}
|
||||
|
||||
// FIXME: this should be able to be tolerant of escaped ?'s in queries without
|
||||
// losing much speed, and should be to avoid confusion.
|
||||
|
||||
// Rebind a query from the default bindtype (QUESTION) to the target bindtype.
|
||||
func Rebind(bindType int, query string) string {
|
||||
switch bindType {
|
||||
case QUESTION, UNKNOWN:
|
||||
return query
|
||||
}
|
||||
|
||||
qb := []byte(query)
|
||||
// Add space enough for 10 params before we have to allocate
|
||||
rqb := make([]byte, 0, len(qb)+10)
|
||||
j := 1
|
||||
for _, b := range qb {
|
||||
if b == '?' {
|
||||
switch bindType {
|
||||
case DOLLAR:
|
||||
rqb = append(rqb, '$')
|
||||
case NAMED:
|
||||
rqb = append(rqb, ':', 'a', 'r', 'g')
|
||||
}
|
||||
for _, b := range strconv.Itoa(j) {
|
||||
rqb = append(rqb, byte(b))
|
||||
}
|
||||
j++
|
||||
} else {
|
||||
rqb = append(rqb, b)
|
||||
}
|
||||
}
|
||||
return string(rqb)
|
||||
}
|
||||
|
||||
// Experimental implementation of Rebind which uses a bytes.Buffer. The code is
|
||||
// much simpler and should be more resistant to odd unicode, but it is twice as
|
||||
// slow. Kept here for benchmarking purposes and to possibly replace Rebind if
|
||||
// problems arise with its somewhat naive handling of unicode.
|
||||
func rebindBuff(bindType int, query string) string {
|
||||
if bindType != DOLLAR {
|
||||
return query
|
||||
}
|
||||
|
||||
b := make([]byte, 0, len(query))
|
||||
rqb := bytes.NewBuffer(b)
|
||||
j := 1
|
||||
for _, r := range query {
|
||||
if r == '?' {
|
||||
rqb.WriteRune('$')
|
||||
rqb.WriteString(strconv.Itoa(j))
|
||||
j++
|
||||
} else {
|
||||
rqb.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
return rqb.String()
|
||||
}
|
||||
|
||||
// In expands slice values in args, returning the modified query string
|
||||
// and a new arg list that can be executed by a database. The `query` should
|
||||
// use the `?` bindVar. The return value uses the `?` bindVar.
|
||||
func In(query string, args ...interface{}) (string, []interface{}, error) {
|
||||
// argMeta stores reflect.Value and length for slices and
|
||||
// the value itself for non-slice arguments
|
||||
type argMeta struct {
|
||||
v reflect.Value
|
||||
i interface{}
|
||||
length int
|
||||
}
|
||||
|
||||
var flatArgsCount int
|
||||
var anySlices bool
|
||||
|
||||
meta := make([]argMeta, len(args))
|
||||
|
||||
for i, arg := range args {
|
||||
v := reflect.ValueOf(arg)
|
||||
t := reflectx.Deref(v.Type())
|
||||
|
||||
if t.Kind() == reflect.Slice {
|
||||
meta[i].length = v.Len()
|
||||
meta[i].v = v
|
||||
|
||||
anySlices = true
|
||||
flatArgsCount += meta[i].length
|
||||
|
||||
if meta[i].length == 0 {
|
||||
return "", nil, errors.New("empty slice passed to 'in' query")
|
||||
}
|
||||
} else {
|
||||
meta[i].i = arg
|
||||
flatArgsCount++
|
||||
}
|
||||
}
|
||||
|
||||
// don't do any parsing if there aren't any slices; note that this means
|
||||
// some errors that we might have caught below will not be returned.
|
||||
if !anySlices {
|
||||
return query, args, nil
|
||||
}
|
||||
|
||||
newArgs := make([]interface{}, 0, flatArgsCount)
|
||||
|
||||
var arg, offset int
|
||||
var buf bytes.Buffer
|
||||
|
||||
for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
|
||||
if arg >= len(meta) {
|
||||
// if an argument wasn't passed, lets return an error; this is
|
||||
// not actually how database/sql Exec/Query works, but since we are
|
||||
// creating an argument list programmatically, we want to be able
|
||||
// to catch these programmer errors earlier.
|
||||
return "", nil, errors.New("number of bindVars exceeds arguments")
|
||||
}
|
||||
|
||||
argMeta := meta[arg]
|
||||
arg++
|
||||
|
||||
// not a slice, continue.
|
||||
// our questionmark will either be written before the next expansion
|
||||
// of a slice or after the loop when writing the rest of the query
|
||||
if argMeta.length == 0 {
|
||||
offset = offset + i + 1
|
||||
newArgs = append(newArgs, argMeta.i)
|
||||
continue
|
||||
}
|
||||
|
||||
// write everything up to and including our ? character
|
||||
buf.WriteString(query[:offset+i+1])
|
||||
|
||||
newArgs = append(newArgs, argMeta.v.Index(0).Interface())
|
||||
|
||||
for si := 1; si < argMeta.length; si++ {
|
||||
buf.WriteString(", ?")
|
||||
newArgs = append(newArgs, argMeta.v.Index(si).Interface())
|
||||
}
|
||||
|
||||
// slice the query and reset the offset. this avoids some bookkeeping for
|
||||
// the write after the loop
|
||||
query = query[offset+i+1:]
|
||||
offset = 0
|
||||
}
|
||||
|
||||
buf.WriteString(query)
|
||||
|
||||
if arg < len(meta) {
|
||||
return "", nil, errors.New("number of bindVars less than number arguments")
|
||||
}
|
||||
|
||||
return buf.String(), newArgs, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue