chore: update deps and vendoring

This commit is contained in:
AJ ONeal 2021-10-12 11:34:25 -06:00
parent 8af2fb6cec
commit b51f7992cd
13 changed files with 273 additions and 80 deletions

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/go-acme/lego/v3 v3.7.0 github.com/go-acme/lego/v3 v3.7.0
github.com/go-chi/chi v4.1.1+incompatible github.com/go-chi/chi v4.1.1+incompatible
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5 github.com/jmoiron/sqlx v1.3.4
github.com/joho/godotenv v1.3.0 github.com/joho/godotenv v1.3.0
github.com/judwhite/go-svc v1.1.2 github.com/judwhite/go-svc v1.1.2
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect

11
go.sum
View File

@ -99,7 +99,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@ -174,8 +173,8 @@ github.com/jcmturner/rpc/v2 v2.0.2 h1:gMB4IwRXYsWw4Bc6o/az2HJgFUA1ffSh90i26ZJ6Xl
github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5 h1:lrdPtrORjGv1HbbEvKWDUAy97mPpFm4B8hp77tcCUJY= github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -201,7 +200,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.6.0 h1:I5DPxhYJChW9KYc66se+oKFFQX6VuQrKiprsX6ivRZc= github.com/lib/pq v1.6.0 h1:I5DPxhYJChW9KYc66se+oKFFQX6VuQrKiprsX6ivRZc=
github.com/lib/pq v1.6.0/go.mod h1:4vXEAYvW1fRQ2/FhZ78H73A60MHw1geSm145z2mdY1g= github.com/lib/pq v1.6.0/go.mod h1:4vXEAYvW1fRQ2/FhZ78H73A60MHw1geSm145z2mdY1g=
github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04= github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04=
@ -211,8 +210,8 @@ github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVL
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/acmez v0.1.1 h1:KQODCqk+hBn3O7qfCRPj6L96uG65T5BSS95FKNEqtdA= github.com/mholt/acmez v0.1.1 h1:KQODCqk+hBn3O7qfCRPj6L96uG65T5BSS95FKNEqtdA=

View File

@ -6,6 +6,7 @@
# Folders # Folders
_obj _obj
_test _test
.idea
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]

View File

@ -18,9 +18,8 @@ before_install:
# go versions to test # go versions to test
go: go:
- "1.10.x" - "1.15.x"
- "1.11.x" - "1.16.x"
- "1.12.x"
# run tests w/ coverage # run tests w/ coverage
script: script:

View File

@ -20,25 +20,29 @@ explains how to use `database/sql` along with sqlx.
## Recent Changes ## Recent Changes
* The [introduction](https://github.com/jmoiron/sqlx/pull/387) of `sql.ColumnType` sets the required minimum Go version to 1.8. 1.3.0:
* sqlx/types.JsonText has been renamed to JSONText to follow Go naming conventions. * `sqlx.DB.Connx(context.Context) *sqlx.Conn`
* `sqlx.BindDriver(driverName, bindType)`
* support for `[]map[string]interface{}` to do "batch" insertions
* allocation & perf improvements for `sqlx.In`
This breaks backwards compatibility, but it's in a way that is trivially fixable DB.Connx returns an `sqlx.Conn`, which is an `sql.Conn`-alike consistent with
(`s/JsonText/JSONText/g`). The `types` package is both experimental and not in sqlx's wrapping of other types.
active development currently.
* Using Go 1.6 and below with `types.JSONText` and `types.GzippedText` can be _potentially unsafe_, **especially** when used with common auto-scan sqlx idioms like `Select` and `Get`. See [golang bug #13905](https://github.com/golang/go/issues/13905). `BindDriver` allows users to control the bindvars that sqlx will use for drivers,
and add new drivers at runtime. This results in a very slight performance hit
when resolving the driver into a bind type (~40ns per call), but it allows users
to specify what bindtype their driver uses even when sqlx has not been updated
to know about it by default.
### Backwards Compatibility ### Backwards Compatibility
There is no Go1-like promise of absolute stability, but I take the issue seriously Compatibility with the most recent two versions of Go is a requirement for any
and will maintain the library in a compatible state unless vital bugs prevent me new changes. Compatibility beyond that is not guaranteed.
from doing so. Since [#59](https://github.com/jmoiron/sqlx/issues/59) and
[#60](https://github.com/jmoiron/sqlx/issues/60) necessitated breaking behavior, Versioning is done with Go modules. Breaking changes (eg. removing deprecated API)
a wider API cleanup was done at the time of fixing. It's possible this will happen will get major version number bumps.
in future; if it does, a git tag will be provided for users requiring the old
behavior to continue to use it until such a time as they can migrate.
## install ## install
@ -102,7 +106,7 @@ type Place struct {
} }
func main() { func main() {
// this Pings the database trying to connect, panics on error // this Pings the database trying to connect
// use sqlx.Open() for sql.Open() semantics // use sqlx.Open() for sql.Open() semantics
db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable") db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
if err != nil { if err != nil {
@ -182,6 +186,28 @@ func main() {
// as the name -> db mapping, so struct fields are lowercased and the `db` tag // as the name -> db mapping, so struct fields are lowercased and the `db` tag
// is taken into consideration. // is taken into consideration.
rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason) rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)
// batch insert
// batch insert with structs
personStructs := []Person{
{FirstName: "Ardie", LastName: "Savea", Email: "asavea@ab.co.nz"},
{FirstName: "Sonny Bill", LastName: "Williams", Email: "sbw@ab.co.nz"},
{FirstName: "Ngani", LastName: "Laumape", Email: "nlaumape@ab.co.nz"},
}
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, personStructs)
// batch insert with maps
personMaps := []map[string]interface{}{
{"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "sbw@ab.co.nz"},
{"first_name": "Ngani", "last_name": "Laumape", "email": "nlaumape@ab.co.nz"},
}
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, personMaps)
} }
``` ```

View File

@ -7,6 +7,7 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/jmoiron/sqlx/reflectx" "github.com/jmoiron/sqlx/reflectx"
) )
@ -20,21 +21,36 @@ const (
AT AT
) )
var defaultBinds = map[int][]string{
DOLLAR: []string{"postgres", "pgx", "pq-timeouts", "cloudsqlpostgres", "ql", "nrpostgres", "cockroach"},
QUESTION: []string{"mysql", "sqlite3", "nrmysql", "nrsqlite3"},
NAMED: []string{"oci8", "ora", "goracle", "godror"},
AT: []string{"sqlserver"},
}
var binds sync.Map
func init() {
for bind, drivers := range defaultBinds {
for _, driver := range drivers {
BindDriver(driver, bind)
}
}
}
// BindType returns the bindtype for a given database given a drivername. // BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int { func BindType(driverName string) int {
switch driverName { itype, ok := binds.Load(driverName)
case "postgres", "pgx", "pq-timeouts", "cloudsqlpostgres", "ql": if !ok {
return DOLLAR return UNKNOWN
case "mysql":
return QUESTION
case "sqlite3":
return QUESTION
case "oci8", "ora", "goracle":
return NAMED
case "sqlserver":
return AT
} }
return UNKNOWN return itype.(int)
}
// BindDriver sets the BindType for driverName to bindType.
func BindDriver(driverName string, bindType int) {
binds.Store(driverName, bindType)
} }
// FIXME: this should be able to be tolerant of escaped ?'s in queries without // FIXME: this should be able to be tolerant of escaped ?'s in queries without
@ -98,6 +114,28 @@ func rebindBuff(bindType int, query string) string {
return rqb.String() return rqb.String()
} }
func asSliceForIn(i interface{}) (v reflect.Value, ok bool) {
if i == nil {
return reflect.Value{}, false
}
v = reflect.ValueOf(i)
t := reflectx.Deref(v.Type())
// Only expand slices
if t.Kind() != reflect.Slice {
return reflect.Value{}, false
}
// []byte is a driver.Value type so it should not be expanded
if t == reflect.TypeOf([]byte{}) {
return reflect.Value{}, false
}
return v, true
}
// In expands slice values in args, returning the modified query 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 // and a new arg list that can be executed by a database. The `query` should
// use the `?` bindVar. The return value uses the `?` bindVar. // use the `?` bindVar. The return value uses the `?` bindVar.
@ -113,7 +151,14 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
var flatArgsCount int var flatArgsCount int
var anySlices bool var anySlices bool
meta := make([]argMeta, len(args)) var stackMeta [32]argMeta
var meta []argMeta
if len(args) <= len(stackMeta) {
meta = stackMeta[:len(args)]
} else {
meta = make([]argMeta, len(args))
}
for i, arg := range args { for i, arg := range args {
if a, ok := arg.(driver.Valuer); ok { if a, ok := arg.(driver.Valuer); ok {
@ -123,11 +168,8 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
return "", nil, err return "", nil, err
} }
} }
v := reflect.ValueOf(arg)
t := reflectx.Deref(v.Type())
// []byte is a driver.Value type so it should not be expanded if v, ok := asSliceForIn(arg); ok {
if t.Kind() == reflect.Slice && t != reflect.TypeOf([]byte{}) {
meta[i].length = v.Len() meta[i].length = v.Len()
meta[i].v = v meta[i].v = v
@ -150,7 +192,9 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
} }
newArgs := make([]interface{}, 0, flatArgsCount) newArgs := make([]interface{}, 0, flatArgsCount)
buf := make([]byte, 0, len(query)+len(", ?")*flatArgsCount)
var buf strings.Builder
buf.Grow(len(query) + len(", ?")*flatArgsCount)
var arg, offset int var arg, offset int
@ -176,10 +220,10 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
} }
// write everything up to and including our ? character // write everything up to and including our ? character
buf = append(buf, query[:offset+i+1]...) buf.WriteString(query[:offset+i+1])
for si := 1; si < argMeta.length; si++ { for si := 1; si < argMeta.length; si++ {
buf = append(buf, ", ?"...) buf.WriteString(", ?")
} }
newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length) newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)
@ -190,13 +234,13 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
offset = 0 offset = 0
} }
buf = append(buf, query...) buf.WriteString(query)
if arg < len(meta) { if arg < len(meta) {
return "", nil, errors.New("number of bindVars less than number arguments") return "", nil, errors.New("number of bindVars less than number arguments")
} }
return string(buf), newArgs, nil return buf.String(), newArgs, nil
} }
func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} { func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {

View File

@ -1,7 +1,9 @@
module github.com/jmoiron/sqlx module github.com/jmoiron/sqlx
go 1.10
require ( require (
github.com/go-sql-driver/mysql v1.4.0 github.com/go-sql-driver/mysql v1.5.0
github.com/lib/pq v1.0.0 github.com/lib/pq v1.2.0
github.com/mattn/go-sqlite3 v1.9.0 github.com/mattn/go-sqlite3 v1.14.6
) )

View File

@ -1,6 +1,6 @@
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=

View File

@ -146,8 +146,22 @@ func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
}, nil }, nil
} }
// convertMapStringInterface attempts to convert v to map[string]interface{}.
// Unlike v.(map[string]interface{}), this function works on named types that
// are convertible to map[string]interface{} as well.
func convertMapStringInterface(v interface{}) (map[string]interface{}, bool) {
var m map[string]interface{}
mtype := reflect.TypeOf(m)
t := reflect.TypeOf(v)
if !t.ConvertibleTo(mtype) {
return nil, false
}
return reflect.ValueOf(v).Convert(mtype).Interface().(map[string]interface{}), true
}
func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) { func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) {
if maparg, ok := arg.(map[string]interface{}); ok { if maparg, ok := convertMapStringInterface(arg); ok {
return bindMapArgs(names, maparg) return bindMapArgs(names, maparg)
} }
return bindArgs(names, arg, m) return bindArgs(names, arg, m)
@ -202,7 +216,7 @@ func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper)
return "", []interface{}{}, err return "", []interface{}{}, err
} }
arglist, err := bindArgs(names, arg, m) arglist, err := bindAnyArgs(names, arg, m)
if err != nil { if err != nil {
return "", []interface{}{}, err return "", []interface{}{}, err
} }
@ -210,21 +224,47 @@ func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper)
return bound, arglist, nil return bound, arglist, nil
} }
var valueBracketReg = regexp.MustCompile(`\([^(]*\?+[^)]*\)`) var valuesReg = regexp.MustCompile(`\)\s*(?i)VALUES\s*\(`)
func findMatchingClosingBracketIndex(s string) int {
count := 0
for i, ch := range s {
if ch == '(' {
count++
}
if ch == ')' {
count--
if count == 0 {
return i
}
}
}
return 0
}
func fixBound(bound string, loop int) string { func fixBound(bound string, loop int) string {
loc := valueBracketReg.FindStringIndex(bound) loc := valuesReg.FindStringIndex(bound)
if len(loc) != 2 { // defensive guard when "VALUES (...)" not found
if len(loc) < 2 {
return bound return bound
} }
openingBracketIndex := loc[1] - 1
index := findMatchingClosingBracketIndex(bound[openingBracketIndex:])
// defensive guard. must have closing bracket
if index == 0 {
return bound
}
closingBracketIndex := openingBracketIndex + index + 1
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteString(bound[0:loc[1]]) buffer.WriteString(bound[0:closingBracketIndex])
for i := 0; i < loop-1; i++ { for i := 0; i < loop-1; i++ {
buffer.WriteString(",") buffer.WriteString(",")
buffer.WriteString(bound[loc[0]:loc[1]]) buffer.WriteString(bound[openingBracketIndex:closingBracketIndex])
} }
buffer.WriteString(bound[loc[1]:]) buffer.WriteString(bound[closingBracketIndex:])
return buffer.String() return buffer.String()
} }
@ -242,9 +282,9 @@ func bindArray(bindType int, query string, arg interface{}, m *reflectx.Mapper)
if arrayLen == 0 { if arrayLen == 0 {
return "", []interface{}{}, fmt.Errorf("length of array is 0: %#v", arg) return "", []interface{}{}, fmt.Errorf("length of array is 0: %#v", arg)
} }
var arglist []interface{} var arglist = make([]interface{}, 0, len(names)*arrayLen)
for i := 0; i < arrayLen; i++ { for i := 0; i < arrayLen; i++ {
elemArglist, err := bindArgs(names, arrayValue.Index(i).Interface(), m) elemArglist, err := bindAnyArgs(names, arrayValue.Index(i).Interface(), m)
if err != nil { if err != nil {
return "", []interface{}{}, err return "", []interface{}{}, err
} }
@ -379,11 +419,16 @@ func Named(query string, arg interface{}) (string, []interface{}, error) {
} }
func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) { func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
if maparg, ok := arg.(map[string]interface{}); ok { t := reflect.TypeOf(arg)
return bindMap(bindType, query, maparg) k := t.Kind()
} switch {
switch reflect.TypeOf(arg).Kind() { case k == reflect.Map && t.Key().Kind() == reflect.String:
case reflect.Array, reflect.Slice: m, ok := convertMapStringInterface(arg)
if !ok {
return "", nil, fmt.Errorf("sqlx.bindNamedMapper: unsupported map type: %T", arg)
}
return bindMap(bindType, query, m)
case k == reflect.Array || k == reflect.Slice:
return bindArray(bindType, query, arg, m) return bindArray(bindType, query, arg, m)
default: default:
return bindStruct(bindType, query, arg, m) return bindStruct(bindType, query, arg, m)

View File

@ -429,9 +429,14 @@ QueueLoop:
flds := &StructMap{Index: m, Tree: root, Paths: map[string]*FieldInfo{}, Names: map[string]*FieldInfo{}} flds := &StructMap{Index: m, Tree: root, Paths: map[string]*FieldInfo{}, Names: map[string]*FieldInfo{}}
for _, fi := range flds.Index { for _, fi := range flds.Index {
flds.Paths[fi.Path] = fi // check if nothing has already been pushed with the same path
if fi.Name != "" && !fi.Embedded { // sometimes you can choose to override a type using embedded struct
flds.Names[fi.Path] = fi fld, ok := flds.Paths[fi.Path]
if !ok || fld.Embedded {
flds.Paths[fi.Path] = fi
if fi.Name != "" && !fi.Embedded {
flds.Names[fi.Path] = fi
}
} }
} }

View File

@ -64,11 +64,7 @@ func isScannable(t reflect.Type) bool {
// it's not important that we use the right mapper for this particular object, // it's not important that we use the right mapper for this particular object,
// we're only concerned on how many exported fields this struct has // we're only concerned on how many exported fields this struct has
m := mapper() return len(mapper().TypeMap(t).Index) == 0
if len(m.TypeMap(t).Index) == 0 {
return true
}
return false
} }
// ColScanner is an interface used by MapScan and SliceScan // ColScanner is an interface used by MapScan and SliceScan
@ -380,6 +376,14 @@ func (db *DB) PrepareNamed(query string) (*NamedStmt, error) {
return prepareNamed(db, query) return prepareNamed(db, query)
} }
// Conn is a wrapper around sql.Conn with extra functionality
type Conn struct {
*sql.Conn
driverName string
unsafe bool
Mapper *reflectx.Mapper
}
// Tx is an sqlx wrapper around sql.Tx with extra functionality // Tx is an sqlx wrapper around sql.Tx with extra functionality
type Tx struct { type Tx struct {
*sql.Tx *sql.Tx

View File

@ -208,6 +208,74 @@ func (db *DB) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err
} }
// Connx returns an *sqlx.Conn instead of an *sql.Conn.
func (db *DB) Connx(ctx context.Context) (*Conn, error) {
conn, err := db.DB.Conn(ctx)
if err != nil {
return nil, err
}
return &Conn{Conn: conn, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, nil
}
// BeginTxx begins a transaction and returns an *sqlx.Tx instead of an
// *sql.Tx.
//
// The provided context is used until the transaction is committed or rolled
// back. If the context is canceled, the sql package will roll back the
// transaction. Tx.Commit will return an error if the context provided to
// BeginxContext is canceled.
func (c *Conn) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
tx, err := c.Conn.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
return &Tx{Tx: tx, driverName: c.driverName, unsafe: c.unsafe, Mapper: c.Mapper}, err
}
// SelectContext using this Conn.
// Any placeholder parameters are replaced with supplied args.
func (c *Conn) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
return SelectContext(ctx, c, dest, query, args...)
}
// GetContext using this Conn.
// Any placeholder parameters are replaced with supplied args.
// An error is returned if the result set is empty.
func (c *Conn) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
return GetContext(ctx, c, dest, query, args...)
}
// PreparexContext returns an sqlx.Stmt instead of a sql.Stmt.
//
// The provided context is used for the preparation of the statement, not for
// the execution of the statement.
func (c *Conn) PreparexContext(ctx context.Context, query string) (*Stmt, error) {
return PreparexContext(ctx, c, query)
}
// QueryxContext queries the database and returns an *sqlx.Rows.
// Any placeholder parameters are replaced with supplied args.
func (c *Conn) QueryxContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
r, err := c.Conn.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
return &Rows{Rows: r, unsafe: c.unsafe, Mapper: c.Mapper}, err
}
// QueryRowxContext queries the database and returns an *sqlx.Row.
// Any placeholder parameters are replaced with supplied args.
func (c *Conn) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *Row {
rows, err := c.Conn.QueryContext(ctx, query, args...)
return &Row{rows: rows, err: err, unsafe: c.unsafe, Mapper: c.Mapper}
}
// Rebind a query within a Conn's bindvar type.
func (c *Conn) Rebind(query string) string {
return Rebind(BindType(c.driverName), query)
}
// StmtxContext returns a version of the prepared statement which runs within a // StmtxContext returns a version of the prepared statement which runs within a
// transaction. Provided stmt can be either *sql.Stmt or *sqlx.Stmt. // transaction. Provided stmt can be either *sql.Stmt or *sqlx.Stmt.
func (tx *Tx) StmtxContext(ctx context.Context, stmt interface{}) *Stmt { func (tx *Tx) StmtxContext(ctx context.Context, stmt interface{}) *Stmt {

2
vendor/modules.txt vendored
View File

@ -89,7 +89,7 @@ github.com/jcmturner/gokrb5/v8/types
# github.com/jcmturner/rpc/v2 v2.0.2 # github.com/jcmturner/rpc/v2 v2.0.2
github.com/jcmturner/rpc/v2/mstypes github.com/jcmturner/rpc/v2/mstypes
github.com/jcmturner/rpc/v2/ndr github.com/jcmturner/rpc/v2/ndr
# github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5 # github.com/jmoiron/sqlx v1.3.4
## explicit ## explicit
github.com/jmoiron/sqlx github.com/jmoiron/sqlx
github.com/jmoiron/sqlx/reflectx github.com/jmoiron/sqlx/reflectx