add modules
This commit is contained in:
parent
0ab7aa73c7
commit
a16ed990c1
14
Gopkg.lock
generated
14
Gopkg.lock
generated
@ -37,6 +37,17 @@
|
|||||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:7654989089e5bd5b6734ec3be8b695e87d3f1f8d95620b343fd7d3995a5b60d7"
|
||||||
|
name = "github.com/jmoiron/sqlx"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"reflectx",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "0dae4fefe7c0e190f7b5a78dac28a1c82cc8d849"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
|
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
|
||||||
name = "github.com/mattn/go-isatty"
|
name = "github.com/mattn/go-isatty"
|
||||||
@ -83,6 +94,9 @@
|
|||||||
input-imports = [
|
input-imports = [
|
||||||
"github.com/gin-contrib/cors",
|
"github.com/gin-contrib/cors",
|
||||||
"github.com/gin-gonic/gin",
|
"github.com/gin-gonic/gin",
|
||||||
|
"github.com/gin-gonic/gin/binding",
|
||||||
|
"github.com/jmoiron/sqlx",
|
||||||
|
"gopkg.in/yaml.v2",
|
||||||
]
|
]
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -28,3 +28,7 @@
|
|||||||
[prune]
|
[prune]
|
||||||
go-tests = true
|
go-tests = true
|
||||||
unused-packages = true
|
unused-packages = true
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "gopkg.in/yaml.v2"
|
||||||
|
version = "2.2.1"
|
||||||
|
64
module/apimsg/apimsg.go
Normal file
64
module/apimsg/apimsg.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package apimsg
|
||||||
|
|
||||||
|
// ResObject -
|
||||||
|
type ResObject struct {
|
||||||
|
Status int
|
||||||
|
Obj interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var objs = map[string]*ResObject{
|
||||||
|
"NotFound": &ResObject{
|
||||||
|
Status: 404,
|
||||||
|
Obj: map[string]string{
|
||||||
|
"message": "not found",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"InternalError": &ResObject{
|
||||||
|
Status: 500,
|
||||||
|
Obj: map[string]string{
|
||||||
|
"message": "server internal error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Success": &ResObject{
|
||||||
|
Status: 200,
|
||||||
|
Obj: map[string]string{
|
||||||
|
"message": "success",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Forbidden": &ResObject{
|
||||||
|
Status: 403,
|
||||||
|
Obj: map[string]string{
|
||||||
|
"message": "forbidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"DataFormat": &ResObject{
|
||||||
|
Status: 400,
|
||||||
|
Obj: map[string]string{
|
||||||
|
"message": "input data format error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRes -
|
||||||
|
func GetRes(name string, msg interface{}) *ResObject {
|
||||||
|
obj, ok := objs[name]
|
||||||
|
if !ok {
|
||||||
|
obj = objs["InternalError"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resobj := &ResObject{}
|
||||||
|
resobj.Status = obj.Status
|
||||||
|
switch msg.(type) {
|
||||||
|
case string:
|
||||||
|
tmp := make(map[string]string)
|
||||||
|
tmp["message"] = msg.(string)
|
||||||
|
resobj.Obj = tmp
|
||||||
|
break
|
||||||
|
case map[string]interface{}:
|
||||||
|
resobj.Obj = msg
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
resobj.Obj = obj.Obj
|
||||||
|
}
|
||||||
|
return resobj
|
||||||
|
}
|
57
module/config/config.go
Normal file
57
module/config/config.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"git.trj.tw/golang/mtfosbot/module/utils"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config -
|
||||||
|
type Config struct {
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
URL string `yaml:"url"`
|
||||||
|
ImageRoot string `yaml:"image_root"`
|
||||||
|
Line struct {
|
||||||
|
secret string `yaml:"secret"`
|
||||||
|
access string `yaml:"access"`
|
||||||
|
} `yaml:"line"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf *Config
|
||||||
|
|
||||||
|
// LoadConfig -
|
||||||
|
func LoadConfig(p ...string) error {
|
||||||
|
var fp string
|
||||||
|
if len(p) > 0 && len(p[0]) > 0 {
|
||||||
|
fp = p[0]
|
||||||
|
} else {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fp = path.Join(wd, "config.yml")
|
||||||
|
}
|
||||||
|
fp = utils.ParsePath(fp)
|
||||||
|
|
||||||
|
exists := utils.CheckExists(fp, false)
|
||||||
|
if !exists {
|
||||||
|
return errors.New("config file not exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = &Config{}
|
||||||
|
err = yaml.Unmarshal(data, conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
60
module/context/context.go
Normal file
60
module/context/context.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.trj.tw/golang/mtfosbot/module/apimsg"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context custom context struct
|
||||||
|
type Context struct {
|
||||||
|
*gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomMiddle func
|
||||||
|
type CustomMiddle func(*Context)
|
||||||
|
|
||||||
|
// PatchCtx - patch ctx to custom middle
|
||||||
|
func PatchCtx(handler func(*Context)) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
ctx := &Context{
|
||||||
|
Context: c,
|
||||||
|
}
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindData client body data
|
||||||
|
func (c *Context) BindData(i interface{}) error {
|
||||||
|
b := binding.Default(c.Request.Method, c.ContentType())
|
||||||
|
return c.ShouldBindWith(i, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomRes -
|
||||||
|
func (c *Context) CustomRes(status int, msg interface{}) {
|
||||||
|
c.AbortWithStatusJSON(status, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFound -
|
||||||
|
func (c *Context) NotFound(msg interface{}) {
|
||||||
|
obj := apimsg.GetRes("NotFound", msg)
|
||||||
|
c.AbortWithStatusJSON(obj.Status, obj.Obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataFormat -
|
||||||
|
func (c *Context) DataFormat(msg interface{}) {
|
||||||
|
obj := apimsg.GetRes("DataFormat", msg)
|
||||||
|
c.AbortWithStatusJSON(obj.Status, obj.Obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success -
|
||||||
|
func (c *Context) Success(msg interface{}) {
|
||||||
|
obj := apimsg.GetRes("Success", msg)
|
||||||
|
c.AbortWithStatusJSON(obj.Status, obj.Obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerError response
|
||||||
|
func (c *Context) ServerError(msg interface{}) {
|
||||||
|
obj := apimsg.GetRes("InternalError", msg)
|
||||||
|
c.AbortWithStatusJSON(obj.Status, obj.Obj)
|
||||||
|
}
|
84
module/utils/utils.go
Normal file
84
module/utils/utils.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToMap struct to map[string]interface{}
|
||||||
|
func ToMap(ss interface{}) map[string]interface{} {
|
||||||
|
t := reflect.ValueOf(ss)
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
smap := make(map[string]interface{})
|
||||||
|
mtag := regexp.MustCompile(`cc:\"(.+)\"`)
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
tag := string(t.Type().Field(i).Tag)
|
||||||
|
str := mtag.FindStringSubmatch(tag)
|
||||||
|
name := t.Type().Field(i).Name
|
||||||
|
if len(str) > 1 {
|
||||||
|
name = str[1]
|
||||||
|
}
|
||||||
|
if name != "-" {
|
||||||
|
smap[name] = f.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return smap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePath - parse file path to absPath
|
||||||
|
func ParsePath(dst string) string {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
wd = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if []rune(dst)[0] == '~' {
|
||||||
|
home := UserHomeDir()
|
||||||
|
if len(home) > 0 {
|
||||||
|
dst = strings.Replace(dst, "~", home, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.IsAbs(dst) {
|
||||||
|
dst = path.Clean(dst)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
str := path.Join(wd, dst)
|
||||||
|
str = path.Clean(str)
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserHomeDir - get user home directory
|
||||||
|
func UserHomeDir() string {
|
||||||
|
env := "HOME"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
env = "USERPROFILE"
|
||||||
|
} else if runtime.GOOS == "plan9" {
|
||||||
|
env = "home"
|
||||||
|
}
|
||||||
|
return os.Getenv(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckExists - check file exists
|
||||||
|
func CheckExists(filePath string, allowDir bool) bool {
|
||||||
|
filePath = ParsePath(filePath)
|
||||||
|
stat, err := os.Stat(filePath)
|
||||||
|
if err != nil && !os.IsExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !allowDir && stat.IsDir() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
24
vendor/github.com/jmoiron/sqlx/.gitignore
generated
vendored
Normal file
24
vendor/github.com/jmoiron/sqlx/.gitignore
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
tags
|
||||||
|
environ
|
27
vendor/github.com/jmoiron/sqlx/.travis.yml
generated
vendored
Normal file
27
vendor/github.com/jmoiron/sqlx/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# vim: ft=yaml sw=2 ts=2
|
||||||
|
|
||||||
|
language: go
|
||||||
|
|
||||||
|
# enable database services
|
||||||
|
services:
|
||||||
|
- mysql
|
||||||
|
- postgresql
|
||||||
|
|
||||||
|
# create test database
|
||||||
|
before_install:
|
||||||
|
- mysql -e 'CREATE DATABASE IF NOT EXISTS sqlxtest;'
|
||||||
|
- psql -c 'create database sqlxtest;' -U postgres
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- export SQLX_MYSQL_DSN="travis:@/sqlxtest?parseTime=true"
|
||||||
|
- export SQLX_POSTGRES_DSN="postgres://postgres:@localhost/sqlxtest?sslmode=disable"
|
||||||
|
- export SQLX_SQLITE_DSN="$HOME/sqlxtest.db"
|
||||||
|
|
||||||
|
# go versions to test
|
||||||
|
go:
|
||||||
|
- "1.8"
|
||||||
|
- "1.9"
|
||||||
|
- "1.10.x"
|
||||||
|
|
||||||
|
# run tests w/ coverage
|
||||||
|
script:
|
||||||
|
- travis_retry $GOPATH/bin/goveralls -service=travis-ci
|
23
vendor/github.com/jmoiron/sqlx/LICENSE
generated
vendored
Normal file
23
vendor/github.com/jmoiron/sqlx/LICENSE
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2013, Jason Moiron
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
185
vendor/github.com/jmoiron/sqlx/README.md
generated
vendored
Normal file
185
vendor/github.com/jmoiron/sqlx/README.md
generated
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
# sqlx
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/jmoiron/sqlx.svg?branch=master)](https://travis-ci.org/jmoiron/sqlx) [![Coverage Status](https://coveralls.io/repos/github/jmoiron/sqlx/badge.svg?branch=master)](https://coveralls.io/github/jmoiron/sqlx?branch=master) [![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/jmoiron/sqlx) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/jmoiron/sqlx/master/LICENSE)
|
||||||
|
|
||||||
|
sqlx is a library which provides a set of extensions on go's standard
|
||||||
|
`database/sql` library. The sqlx versions of `sql.DB`, `sql.TX`, `sql.Stmt`,
|
||||||
|
et al. all leave the underlying interfaces untouched, so that their interfaces
|
||||||
|
are a superset on the standard ones. This makes it relatively painless to
|
||||||
|
integrate existing codebases using database/sql with sqlx.
|
||||||
|
|
||||||
|
Major additional concepts are:
|
||||||
|
|
||||||
|
* Marshal rows into structs (with embedded struct support), maps, and slices
|
||||||
|
* Named parameter support including prepared statements
|
||||||
|
* `Get` and `Select` to go quickly from query to struct/slice
|
||||||
|
|
||||||
|
In addition to the [godoc API documentation](http://godoc.org/github.com/jmoiron/sqlx),
|
||||||
|
there is also some [standard documentation](http://jmoiron.github.io/sqlx/) that
|
||||||
|
explains how to use `database/sql` along with sqlx.
|
||||||
|
|
||||||
|
## Recent Changes
|
||||||
|
|
||||||
|
* sqlx/types.JsonText has been renamed to JSONText to follow Go naming conventions.
|
||||||
|
|
||||||
|
This breaks backwards compatibility, but it's in a way that is trivially fixable
|
||||||
|
(`s/JsonText/JSONText/g`). The `types` package is both experimental and not in
|
||||||
|
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).
|
||||||
|
|
||||||
|
### Backwards Compatibility
|
||||||
|
|
||||||
|
There is no Go1-like promise of absolute stability, but I take the issue seriously
|
||||||
|
and will maintain the library in a compatible state unless vital bugs prevent me
|
||||||
|
from doing so. Since [#59](https://github.com/jmoiron/sqlx/issues/59) and
|
||||||
|
[#60](https://github.com/jmoiron/sqlx/issues/60) necessitated breaking behavior,
|
||||||
|
a wider API cleanup was done at the time of fixing. It's possible this will happen
|
||||||
|
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
|
||||||
|
|
||||||
|
go get github.com/jmoiron/sqlx
|
||||||
|
|
||||||
|
## issues
|
||||||
|
|
||||||
|
Row headers can be ambiguous (`SELECT 1 AS a, 2 AS a`), and the result of
|
||||||
|
`Columns()` does not fully qualify column names in queries like:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT a.id, a.name, b.id, b.name FROM foos AS a JOIN foos AS b ON a.parent = b.id;
|
||||||
|
```
|
||||||
|
|
||||||
|
making a struct or map destination ambiguous. Use `AS` in your queries
|
||||||
|
to give columns distinct names, `rows.Scan` to scan them manually, or
|
||||||
|
`SliceScan` to get a slice of results.
|
||||||
|
|
||||||
|
## usage
|
||||||
|
|
||||||
|
Below is an example which shows some common use cases for sqlx. Check
|
||||||
|
[sqlx_test.go](https://github.com/jmoiron/sqlx/blob/master/sqlx_test.go) for more
|
||||||
|
usage.
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var schema = `
|
||||||
|
CREATE TABLE person (
|
||||||
|
first_name text,
|
||||||
|
last_name text,
|
||||||
|
email text
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE place (
|
||||||
|
country text,
|
||||||
|
city text NULL,
|
||||||
|
telcode integer
|
||||||
|
)`
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
FirstName string `db:"first_name"`
|
||||||
|
LastName string `db:"last_name"`
|
||||||
|
Email string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Place struct {
|
||||||
|
Country string
|
||||||
|
City sql.NullString
|
||||||
|
TelCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// this Pings the database trying to connect, panics on error
|
||||||
|
// use sqlx.Open() for sql.Open() semantics
|
||||||
|
db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// exec the schema or fail; multi-statement Exec behavior varies between
|
||||||
|
// database drivers; pq will exec them all, sqlite3 won't, ymmv
|
||||||
|
db.MustExec(schema)
|
||||||
|
|
||||||
|
tx := db.MustBegin()
|
||||||
|
tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "jmoiron@jmoiron.net")
|
||||||
|
tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "John", "Doe", "johndoeDNE@gmail.net")
|
||||||
|
tx.MustExec("INSERT INTO place (country, city, telcode) VALUES ($1, $2, $3)", "United States", "New York", "1")
|
||||||
|
tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Hong Kong", "852")
|
||||||
|
tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Singapore", "65")
|
||||||
|
// Named queries can use structs, so if you have an existing struct (i.e. person := &Person{}) that you have populated, you can pass it in as &person
|
||||||
|
tx.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", &Person{"Jane", "Citizen", "jane.citzen@example.com"})
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
|
// Query the database, storing results in a []Person (wrapped in []interface{})
|
||||||
|
people := []Person{}
|
||||||
|
db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
|
||||||
|
jason, john := people[0], people[1]
|
||||||
|
|
||||||
|
fmt.Printf("%#v\n%#v", jason, john)
|
||||||
|
// Person{FirstName:"Jason", LastName:"Moiron", Email:"jmoiron@jmoiron.net"}
|
||||||
|
// Person{FirstName:"John", LastName:"Doe", Email:"johndoeDNE@gmail.net"}
|
||||||
|
|
||||||
|
// You can also get a single result, a la QueryRow
|
||||||
|
jason = Person{}
|
||||||
|
err = db.Get(&jason, "SELECT * FROM person WHERE first_name=$1", "Jason")
|
||||||
|
fmt.Printf("%#v\n", jason)
|
||||||
|
// Person{FirstName:"Jason", LastName:"Moiron", Email:"jmoiron@jmoiron.net"}
|
||||||
|
|
||||||
|
// if you have null fields and use SELECT *, you must use sql.Null* in your struct
|
||||||
|
places := []Place{}
|
||||||
|
err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
usa, singsing, honkers := places[0], places[1], places[2]
|
||||||
|
|
||||||
|
fmt.Printf("%#v\n%#v\n%#v\n", usa, singsing, honkers)
|
||||||
|
// Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
|
||||||
|
// Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}
|
||||||
|
// Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}
|
||||||
|
|
||||||
|
// Loop through rows using only one struct
|
||||||
|
place := Place{}
|
||||||
|
rows, err := db.Queryx("SELECT * FROM place")
|
||||||
|
for rows.Next() {
|
||||||
|
err := rows.StructScan(&place)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%#v\n", place)
|
||||||
|
}
|
||||||
|
// Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
|
||||||
|
// Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}
|
||||||
|
// Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}
|
||||||
|
|
||||||
|
// Named queries, using `:name` as the bindvar. Automatic bindvar support
|
||||||
|
// which takes into account the dbtype based on the driverName on sqlx.Open/Connect
|
||||||
|
_, err = db.NamedExec(`INSERT INTO person (first_name,last_name,email) VALUES (:first,:last,:email)`,
|
||||||
|
map[string]interface{}{
|
||||||
|
"first": "Bin",
|
||||||
|
"last": "Smuth",
|
||||||
|
"email": "bensmith@allblacks.nz",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Selects Mr. Smith from the database
|
||||||
|
rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"})
|
||||||
|
|
||||||
|
// Named queries can also use structs. Their bind names follow the same rules
|
||||||
|
// as the name -> db mapping, so struct fields are lowercased and the `db` tag
|
||||||
|
// is taken into consideration.
|
||||||
|
rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
208
vendor/github.com/jmoiron/sqlx/bind.go
generated
vendored
Normal file
208
vendor/github.com/jmoiron/sqlx/bind.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
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", "pq-timeouts", "cloudsqlpostgres":
|
||||||
|
return DOLLAR
|
||||||
|
case "mysql":
|
||||||
|
return QUESTION
|
||||||
|
case "sqlite3":
|
||||||
|
return QUESTION
|
||||||
|
case "oci8", "ora", "goracle":
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add space enough for 10 params before we have to allocate
|
||||||
|
rqb := make([]byte, 0, len(query)+10)
|
||||||
|
|
||||||
|
var i, j int
|
||||||
|
|
||||||
|
for i = strings.Index(query, "?"); i != -1; i = strings.Index(query, "?") {
|
||||||
|
rqb = append(rqb, query[:i]...)
|
||||||
|
|
||||||
|
switch bindType {
|
||||||
|
case DOLLAR:
|
||||||
|
rqb = append(rqb, '$')
|
||||||
|
case NAMED:
|
||||||
|
rqb = append(rqb, ':', 'a', 'r', 'g')
|
||||||
|
}
|
||||||
|
|
||||||
|
j++
|
||||||
|
rqb = strconv.AppendInt(rqb, int64(j), 10)
|
||||||
|
|
||||||
|
query = query[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(append(rqb, query...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
|
||||||
|
// []byte is a driver.Value type so it should not be expanded
|
||||||
|
if t.Kind() == reflect.Slice && t != reflect.TypeOf([]byte{}) {
|
||||||
|
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)
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, len(query)+len(", ?")*flatArgsCount))
|
||||||
|
|
||||||
|
var arg, offset int
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
for si := 1; si < argMeta.length; si++ {
|
||||||
|
buf.WriteString(", ?")
|
||||||
|
}
|
||||||
|
|
||||||
|
newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {
|
||||||
|
switch val := v.Interface().(type) {
|
||||||
|
case []interface{}:
|
||||||
|
args = append(args, val...)
|
||||||
|
case []int:
|
||||||
|
for i := range val {
|
||||||
|
args = append(args, val[i])
|
||||||
|
}
|
||||||
|
case []string:
|
||||||
|
for i := range val {
|
||||||
|
args = append(args, val[i])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for si := 0; si < vlen; si++ {
|
||||||
|
args = append(args, v.Index(si).Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
12
vendor/github.com/jmoiron/sqlx/doc.go
generated
vendored
Normal file
12
vendor/github.com/jmoiron/sqlx/doc.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Package sqlx provides general purpose extensions to database/sql.
|
||||||
|
//
|
||||||
|
// It is intended to seamlessly wrap database/sql and provide convenience
|
||||||
|
// methods which are useful in the development of database driven applications.
|
||||||
|
// None of the underlying database/sql methods are changed. Instead all extended
|
||||||
|
// behavior is implemented through new methods defined on wrapper types.
|
||||||
|
//
|
||||||
|
// Additions include scanning into structs, named query support, rebinding
|
||||||
|
// queries for different drivers, convenient shorthands for common error handling
|
||||||
|
// and more.
|
||||||
|
//
|
||||||
|
package sqlx
|
346
vendor/github.com/jmoiron/sqlx/named.go
generated
vendored
Normal file
346
vendor/github.com/jmoiron/sqlx/named.go
generated
vendored
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
package sqlx
|
||||||
|
|
||||||
|
// Named Query Support
|
||||||
|
//
|
||||||
|
// * BindMap - bind query bindvars to map/struct args
|
||||||
|
// * NamedExec, NamedQuery - named query w/ struct or map
|
||||||
|
// * NamedStmt - a pre-compiled named query which is a prepared statement
|
||||||
|
//
|
||||||
|
// Internal Interfaces:
|
||||||
|
//
|
||||||
|
// * compileNamedQuery - rebind a named query, returning a query and list of names
|
||||||
|
// * bindArgs, bindMapArgs, bindAnyArgs - given a list of names, return an arglist
|
||||||
|
//
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx/reflectx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NamedStmt is a prepared statement that executes named queries. Prepare it
|
||||||
|
// how you would execute a NamedQuery, but pass in a struct or map when executing.
|
||||||
|
type NamedStmt struct {
|
||||||
|
Params []string
|
||||||
|
QueryString string
|
||||||
|
Stmt *Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the named statement.
|
||||||
|
func (n *NamedStmt) Close() error {
|
||||||
|
return n.Stmt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes a named statement using the struct passed.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) Exec(arg interface{}) (sql.Result, error) {
|
||||||
|
args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
|
||||||
|
if err != nil {
|
||||||
|
return *new(sql.Result), err
|
||||||
|
}
|
||||||
|
return n.Stmt.Exec(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query executes a named statement using the struct argument, returning rows.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) Query(arg interface{}) (*sql.Rows, error) {
|
||||||
|
args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return n.Stmt.Query(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRow executes a named statement against the database. Because sqlx cannot
|
||||||
|
// create a *sql.Row with an error condition pre-set for binding errors, sqlx
|
||||||
|
// returns a *sqlx.Row instead.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) QueryRow(arg interface{}) *Row {
|
||||||
|
args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{err: err}
|
||||||
|
}
|
||||||
|
return n.Stmt.QueryRowx(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustExec execs a NamedStmt, panicing on error
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) MustExec(arg interface{}) sql.Result {
|
||||||
|
res, err := n.Exec(arg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queryx using this NamedStmt
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) Queryx(arg interface{}) (*Rows, error) {
|
||||||
|
r, err := n.Query(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Rows{Rows: r, Mapper: n.Stmt.Mapper, unsafe: isUnsafe(n)}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowx this NamedStmt. Because of limitations with QueryRow, this is
|
||||||
|
// an alias for QueryRow.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) QueryRowx(arg interface{}) *Row {
|
||||||
|
return n.QueryRow(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select using this NamedStmt
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) Select(dest interface{}, arg interface{}) error {
|
||||||
|
rows, err := n.Queryx(arg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// if something happens here, we want to make sure the rows are Closed
|
||||||
|
defer rows.Close()
|
||||||
|
return scanAll(rows, dest, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get using this NamedStmt
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) Get(dest interface{}, arg interface{}) error {
|
||||||
|
r := n.QueryRowx(arg)
|
||||||
|
return r.scanAny(dest, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe creates an unsafe version of the NamedStmt
|
||||||
|
func (n *NamedStmt) Unsafe() *NamedStmt {
|
||||||
|
r := &NamedStmt{Params: n.Params, Stmt: n.Stmt, QueryString: n.QueryString}
|
||||||
|
r.Stmt.unsafe = true
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// A union interface of preparer and binder, required to be able to prepare
|
||||||
|
// named statements (as the bindtype must be determined).
|
||||||
|
type namedPreparer interface {
|
||||||
|
Preparer
|
||||||
|
binder
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
|
||||||
|
bindType := BindType(p.DriverName())
|
||||||
|
q, args, err := compileNamedQuery([]byte(query), bindType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stmt, err := Preparex(p, q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &NamedStmt{
|
||||||
|
QueryString: q,
|
||||||
|
Params: args,
|
||||||
|
Stmt: stmt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) {
|
||||||
|
if maparg, ok := arg.(map[string]interface{}); ok {
|
||||||
|
return bindMapArgs(names, maparg)
|
||||||
|
}
|
||||||
|
return bindArgs(names, arg, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// private interface to generate a list of interfaces from a given struct
|
||||||
|
// type, given a list of names to pull out of the struct. Used by public
|
||||||
|
// BindStruct interface.
|
||||||
|
func bindArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) {
|
||||||
|
arglist := make([]interface{}, 0, len(names))
|
||||||
|
|
||||||
|
// grab the indirected value of arg
|
||||||
|
v := reflect.ValueOf(arg)
|
||||||
|
for v = reflect.ValueOf(arg); v.Kind() == reflect.Ptr; {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.TraversalsByNameFunc(v.Type(), names, func(i int, t []int) error {
|
||||||
|
if len(t) == 0 {
|
||||||
|
return fmt.Errorf("could not find name %s in %#v", names[i], arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflectx.FieldByIndexesReadOnly(v, t)
|
||||||
|
arglist = append(arglist, val.Interface())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return arglist, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// like bindArgs, but for maps.
|
||||||
|
func bindMapArgs(names []string, arg map[string]interface{}) ([]interface{}, error) {
|
||||||
|
arglist := make([]interface{}, 0, len(names))
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
val, ok := arg[name]
|
||||||
|
if !ok {
|
||||||
|
return arglist, fmt.Errorf("could not find name %s in %#v", name, arg)
|
||||||
|
}
|
||||||
|
arglist = append(arglist, val)
|
||||||
|
}
|
||||||
|
return arglist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindStruct binds a named parameter query with fields from a struct argument.
|
||||||
|
// The rules for binding field names to parameter names follow the same
|
||||||
|
// conventions as for StructScan, including obeying the `db` struct tags.
|
||||||
|
func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
|
||||||
|
bound, names, err := compileNamedQuery([]byte(query), bindType)
|
||||||
|
if err != nil {
|
||||||
|
return "", []interface{}{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arglist, err := bindArgs(names, arg, m)
|
||||||
|
if err != nil {
|
||||||
|
return "", []interface{}{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bound, arglist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindMap binds a named parameter query with a map of arguments.
|
||||||
|
func bindMap(bindType int, query string, args map[string]interface{}) (string, []interface{}, error) {
|
||||||
|
bound, names, err := compileNamedQuery([]byte(query), bindType)
|
||||||
|
if err != nil {
|
||||||
|
return "", []interface{}{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arglist, err := bindMapArgs(names, args)
|
||||||
|
return bound, arglist, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Compilation of Named Queries
|
||||||
|
|
||||||
|
// Allow digits and letters in bind params; additionally runes are
|
||||||
|
// checked against underscores, meaning that bind params can have be
|
||||||
|
// alphanumeric with underscores. Mind the difference between unicode
|
||||||
|
// digits and numbers, where '5' is a digit but '五' is not.
|
||||||
|
var allowedBindRunes = []*unicode.RangeTable{unicode.Letter, unicode.Digit}
|
||||||
|
|
||||||
|
// FIXME: this function isn't safe for unicode named params, as a failing test
|
||||||
|
// can testify. This is not a regression but a failure of the original code
|
||||||
|
// as well. It should be modified to range over runes in a string rather than
|
||||||
|
// bytes, even though this is less convenient and slower. Hopefully the
|
||||||
|
// addition of the prepared NamedStmt (which will only do this once) will make
|
||||||
|
// up for the slightly slower ad-hoc NamedExec/NamedQuery.
|
||||||
|
|
||||||
|
// compile a NamedQuery into an unbound query (using the '?' bindvar) and
|
||||||
|
// a list of names.
|
||||||
|
func compileNamedQuery(qs []byte, bindType int) (query string, names []string, err error) {
|
||||||
|
names = make([]string, 0, 10)
|
||||||
|
rebound := make([]byte, 0, len(qs))
|
||||||
|
|
||||||
|
inName := false
|
||||||
|
last := len(qs) - 1
|
||||||
|
currentVar := 1
|
||||||
|
name := make([]byte, 0, 10)
|
||||||
|
|
||||||
|
for i, b := range qs {
|
||||||
|
// a ':' while we're in a name is an error
|
||||||
|
if b == ':' {
|
||||||
|
// if this is the second ':' in a '::' escape sequence, append a ':'
|
||||||
|
if inName && i > 0 && qs[i-1] == ':' {
|
||||||
|
rebound = append(rebound, ':')
|
||||||
|
inName = false
|
||||||
|
continue
|
||||||
|
} else if inName {
|
||||||
|
err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i))
|
||||||
|
return query, names, err
|
||||||
|
}
|
||||||
|
inName = true
|
||||||
|
name = []byte{}
|
||||||
|
// if we're in a name, and this is an allowed character, continue
|
||||||
|
} else if inName && (unicode.IsOneOf(allowedBindRunes, rune(b)) || b == '_' || b == '.') && i != last {
|
||||||
|
// append the byte to the name if we are in a name and not on the last byte
|
||||||
|
name = append(name, b)
|
||||||
|
// if we're in a name and it's not an allowed character, the name is done
|
||||||
|
} else if inName {
|
||||||
|
inName = false
|
||||||
|
// if this is the final byte of the string and it is part of the name, then
|
||||||
|
// make sure to add it to the name
|
||||||
|
if i == last && unicode.IsOneOf(allowedBindRunes, rune(b)) {
|
||||||
|
name = append(name, b)
|
||||||
|
}
|
||||||
|
// add the string representation to the names list
|
||||||
|
names = append(names, string(name))
|
||||||
|
// add a proper bindvar for the bindType
|
||||||
|
switch bindType {
|
||||||
|
// oracle only supports named type bind vars even for positional
|
||||||
|
case NAMED:
|
||||||
|
rebound = append(rebound, ':')
|
||||||
|
rebound = append(rebound, name...)
|
||||||
|
case QUESTION, UNKNOWN:
|
||||||
|
rebound = append(rebound, '?')
|
||||||
|
case DOLLAR:
|
||||||
|
rebound = append(rebound, '$')
|
||||||
|
for _, b := range strconv.Itoa(currentVar) {
|
||||||
|
rebound = append(rebound, byte(b))
|
||||||
|
}
|
||||||
|
currentVar++
|
||||||
|
}
|
||||||
|
// add this byte to string unless it was not part of the name
|
||||||
|
if i != last {
|
||||||
|
rebound = append(rebound, b)
|
||||||
|
} else if !unicode.IsOneOf(allowedBindRunes, rune(b)) {
|
||||||
|
rebound = append(rebound, b)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this is a normal byte and should just go onto the rebound query
|
||||||
|
rebound = append(rebound, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(rebound), names, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindNamed binds a struct or a map to a query with named parameters.
|
||||||
|
// DEPRECATED: use sqlx.Named` instead of this, it may be removed in future.
|
||||||
|
func BindNamed(bindType int, query string, arg interface{}) (string, []interface{}, error) {
|
||||||
|
return bindNamedMapper(bindType, query, arg, mapper())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named takes a query using named parameters and an argument and
|
||||||
|
// returns a new query with a list of args that can be executed by
|
||||||
|
// a database. The return value uses the `?` bindvar.
|
||||||
|
func Named(query string, arg interface{}) (string, []interface{}, error) {
|
||||||
|
return bindNamedMapper(QUESTION, query, arg, mapper())
|
||||||
|
}
|
||||||
|
|
||||||
|
func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
|
||||||
|
if maparg, ok := arg.(map[string]interface{}); ok {
|
||||||
|
return bindMap(bindType, query, maparg)
|
||||||
|
}
|
||||||
|
return bindStruct(bindType, query, arg, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedQuery binds a named query and then runs Query on the result using the
|
||||||
|
// provided Ext (sqlx.Tx, sqlx.Db). It works with both structs and with
|
||||||
|
// map[string]interface{} types.
|
||||||
|
func NamedQuery(e Ext, query string, arg interface{}) (*Rows, error) {
|
||||||
|
q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return e.Queryx(q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedExec uses BindStruct to get a query executable by the driver and
|
||||||
|
// then runs Exec on the result. Returns an error from the binding
|
||||||
|
// or the query excution itself.
|
||||||
|
func NamedExec(e Ext, query string, arg interface{}) (sql.Result, error) {
|
||||||
|
q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return e.Exec(q, args...)
|
||||||
|
}
|
132
vendor/github.com/jmoiron/sqlx/named_context.go
generated
vendored
Normal file
132
vendor/github.com/jmoiron/sqlx/named_context.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package sqlx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A union interface of contextPreparer and binder, required to be able to
|
||||||
|
// prepare named statements with context (as the bindtype must be determined).
|
||||||
|
type namedPreparerContext interface {
|
||||||
|
PreparerContext
|
||||||
|
binder
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareNamedContext(ctx context.Context, p namedPreparerContext, query string) (*NamedStmt, error) {
|
||||||
|
bindType := BindType(p.DriverName())
|
||||||
|
q, args, err := compileNamedQuery([]byte(query), bindType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stmt, err := PreparexContext(ctx, p, q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &NamedStmt{
|
||||||
|
QueryString: q,
|
||||||
|
Params: args,
|
||||||
|
Stmt: stmt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecContext executes a named statement using the struct passed.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) ExecContext(ctx context.Context, arg interface{}) (sql.Result, error) {
|
||||||
|
args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
|
||||||
|
if err != nil {
|
||||||
|
return *new(sql.Result), err
|
||||||
|
}
|
||||||
|
return n.Stmt.ExecContext(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContext executes a named statement using the struct argument, returning rows.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) QueryContext(ctx context.Context, arg interface{}) (*sql.Rows, error) {
|
||||||
|
args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return n.Stmt.QueryContext(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowContext executes a named statement against the database. Because sqlx cannot
|
||||||
|
// create a *sql.Row with an error condition pre-set for binding errors, sqlx
|
||||||
|
// returns a *sqlx.Row instead.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) QueryRowContext(ctx context.Context, arg interface{}) *Row {
|
||||||
|
args, err := bindAnyArgs(n.Params, arg, n.Stmt.Mapper)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{err: err}
|
||||||
|
}
|
||||||
|
return n.Stmt.QueryRowxContext(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustExecContext execs a NamedStmt, panicing on error
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) MustExecContext(ctx context.Context, arg interface{}) sql.Result {
|
||||||
|
res, err := n.ExecContext(ctx, arg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryxContext using this NamedStmt
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) QueryxContext(ctx context.Context, arg interface{}) (*Rows, error) {
|
||||||
|
r, err := n.QueryContext(ctx, arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Rows{Rows: r, Mapper: n.Stmt.Mapper, unsafe: isUnsafe(n)}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowxContext this NamedStmt. Because of limitations with QueryRow, this is
|
||||||
|
// an alias for QueryRow.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) QueryRowxContext(ctx context.Context, arg interface{}) *Row {
|
||||||
|
return n.QueryRowContext(ctx, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectContext using this NamedStmt
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) SelectContext(ctx context.Context, dest interface{}, arg interface{}) error {
|
||||||
|
rows, err := n.QueryxContext(ctx, arg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// if something happens here, we want to make sure the rows are Closed
|
||||||
|
defer rows.Close()
|
||||||
|
return scanAll(rows, dest, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext using this NamedStmt
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (n *NamedStmt) GetContext(ctx context.Context, dest interface{}, arg interface{}) error {
|
||||||
|
r := n.QueryRowxContext(ctx, arg)
|
||||||
|
return r.scanAny(dest, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedQueryContext binds a named query and then runs Query on the result using the
|
||||||
|
// provided Ext (sqlx.Tx, sqlx.Db). It works with both structs and with
|
||||||
|
// map[string]interface{} types.
|
||||||
|
func NamedQueryContext(ctx context.Context, e ExtContext, query string, arg interface{}) (*Rows, error) {
|
||||||
|
q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return e.QueryxContext(ctx, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedExecContext uses BindStruct to get a query executable by the driver and
|
||||||
|
// then runs Exec on the result. Returns an error from the binding
|
||||||
|
// or the query excution itself.
|
||||||
|
func NamedExecContext(ctx context.Context, e ExtContext, query string, arg interface{}) (sql.Result, error) {
|
||||||
|
q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return e.ExecContext(ctx, q, args...)
|
||||||
|
}
|
17
vendor/github.com/jmoiron/sqlx/reflectx/README.md
generated
vendored
Normal file
17
vendor/github.com/jmoiron/sqlx/reflectx/README.md
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# reflectx
|
||||||
|
|
||||||
|
The sqlx package has special reflect needs. In particular, it needs to:
|
||||||
|
|
||||||
|
* be able to map a name to a field
|
||||||
|
* understand embedded structs
|
||||||
|
* understand mapping names to fields by a particular tag
|
||||||
|
* user specified name -> field mapping functions
|
||||||
|
|
||||||
|
These behaviors mimic the behaviors by the standard library marshallers and also the
|
||||||
|
behavior of standard Go accessors.
|
||||||
|
|
||||||
|
The first two are amply taken care of by `Reflect.Value.FieldByName`, and the third is
|
||||||
|
addressed by `Reflect.Value.FieldByNameFunc`, but these don't quite understand struct
|
||||||
|
tags in the ways that are vital to most marshallers, and they are slow.
|
||||||
|
|
||||||
|
This reflectx package extends reflect to achieve these goals.
|
441
vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
generated
vendored
Normal file
441
vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
generated
vendored
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
// Package reflectx implements extensions to the standard reflect lib suitable
|
||||||
|
// for implementing marshalling and unmarshalling packages. The main Mapper type
|
||||||
|
// allows for Go-compatible named attribute access, including accessing embedded
|
||||||
|
// struct attributes and the ability to use functions and struct tags to
|
||||||
|
// customize field names.
|
||||||
|
//
|
||||||
|
package reflectx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A FieldInfo is metadata for a struct field.
|
||||||
|
type FieldInfo struct {
|
||||||
|
Index []int
|
||||||
|
Path string
|
||||||
|
Field reflect.StructField
|
||||||
|
Zero reflect.Value
|
||||||
|
Name string
|
||||||
|
Options map[string]string
|
||||||
|
Embedded bool
|
||||||
|
Children []*FieldInfo
|
||||||
|
Parent *FieldInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// A StructMap is an index of field metadata for a struct.
|
||||||
|
type StructMap struct {
|
||||||
|
Tree *FieldInfo
|
||||||
|
Index []*FieldInfo
|
||||||
|
Paths map[string]*FieldInfo
|
||||||
|
Names map[string]*FieldInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByPath returns a *FieldInfo for a given string path.
|
||||||
|
func (f StructMap) GetByPath(path string) *FieldInfo {
|
||||||
|
return f.Paths[path]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByTraversal returns a *FieldInfo for a given integer path. It is
|
||||||
|
// analogous to reflect.FieldByIndex, but using the cached traversal
|
||||||
|
// rather than re-executing the reflect machinery each time.
|
||||||
|
func (f StructMap) GetByTraversal(index []int) *FieldInfo {
|
||||||
|
if len(index) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tree := f.Tree
|
||||||
|
for _, i := range index {
|
||||||
|
if i >= len(tree.Children) || tree.Children[i] == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tree = tree.Children[i]
|
||||||
|
}
|
||||||
|
return tree
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapper is a general purpose mapper of names to struct fields. A Mapper
|
||||||
|
// behaves like most marshallers in the standard library, obeying a field tag
|
||||||
|
// for name mapping but also providing a basic transform function.
|
||||||
|
type Mapper struct {
|
||||||
|
cache map[reflect.Type]*StructMap
|
||||||
|
tagName string
|
||||||
|
tagMapFunc func(string) string
|
||||||
|
mapFunc func(string) string
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMapper returns a new mapper using the tagName as its struct field tag.
|
||||||
|
// If tagName is the empty string, it is ignored.
|
||||||
|
func NewMapper(tagName string) *Mapper {
|
||||||
|
return &Mapper{
|
||||||
|
cache: make(map[reflect.Type]*StructMap),
|
||||||
|
tagName: tagName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMapperTagFunc returns a new mapper which contains a mapper for field names
|
||||||
|
// AND a mapper for tag values. This is useful for tags like json which can
|
||||||
|
// have values like "name,omitempty".
|
||||||
|
func NewMapperTagFunc(tagName string, mapFunc, tagMapFunc func(string) string) *Mapper {
|
||||||
|
return &Mapper{
|
||||||
|
cache: make(map[reflect.Type]*StructMap),
|
||||||
|
tagName: tagName,
|
||||||
|
mapFunc: mapFunc,
|
||||||
|
tagMapFunc: tagMapFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMapperFunc returns a new mapper which optionally obeys a field tag and
|
||||||
|
// a struct field name mapper func given by f. Tags will take precedence, but
|
||||||
|
// for any other field, the mapped name will be f(field.Name)
|
||||||
|
func NewMapperFunc(tagName string, f func(string) string) *Mapper {
|
||||||
|
return &Mapper{
|
||||||
|
cache: make(map[reflect.Type]*StructMap),
|
||||||
|
tagName: tagName,
|
||||||
|
mapFunc: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeMap returns a mapping of field strings to int slices representing
|
||||||
|
// the traversal down the struct to reach the field.
|
||||||
|
func (m *Mapper) TypeMap(t reflect.Type) *StructMap {
|
||||||
|
m.mutex.Lock()
|
||||||
|
mapping, ok := m.cache[t]
|
||||||
|
if !ok {
|
||||||
|
mapping = getMapping(t, m.tagName, m.mapFunc, m.tagMapFunc)
|
||||||
|
m.cache[t] = mapping
|
||||||
|
}
|
||||||
|
m.mutex.Unlock()
|
||||||
|
return mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldMap returns the mapper's mapping of field names to reflect values. Panics
|
||||||
|
// if v's Kind is not Struct, or v is not Indirectable to a struct kind.
|
||||||
|
func (m *Mapper) FieldMap(v reflect.Value) map[string]reflect.Value {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
mustBe(v, reflect.Struct)
|
||||||
|
|
||||||
|
r := map[string]reflect.Value{}
|
||||||
|
tm := m.TypeMap(v.Type())
|
||||||
|
for tagName, fi := range tm.Names {
|
||||||
|
r[tagName] = FieldByIndexes(v, fi.Index)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldByName returns a field by its mapped name as a reflect.Value.
|
||||||
|
// Panics if v's Kind is not Struct or v is not Indirectable to a struct Kind.
|
||||||
|
// Returns zero Value if the name is not found.
|
||||||
|
func (m *Mapper) FieldByName(v reflect.Value, name string) reflect.Value {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
mustBe(v, reflect.Struct)
|
||||||
|
|
||||||
|
tm := m.TypeMap(v.Type())
|
||||||
|
fi, ok := tm.Names[name]
|
||||||
|
if !ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return FieldByIndexes(v, fi.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldsByName returns a slice of values corresponding to the slice of names
|
||||||
|
// for the value. Panics if v's Kind is not Struct or v is not Indirectable
|
||||||
|
// to a struct Kind. Returns zero Value for each name not found.
|
||||||
|
func (m *Mapper) FieldsByName(v reflect.Value, names []string) []reflect.Value {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
mustBe(v, reflect.Struct)
|
||||||
|
|
||||||
|
tm := m.TypeMap(v.Type())
|
||||||
|
vals := make([]reflect.Value, 0, len(names))
|
||||||
|
for _, name := range names {
|
||||||
|
fi, ok := tm.Names[name]
|
||||||
|
if !ok {
|
||||||
|
vals = append(vals, *new(reflect.Value))
|
||||||
|
} else {
|
||||||
|
vals = append(vals, FieldByIndexes(v, fi.Index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraversalsByName returns a slice of int slices which represent the struct
|
||||||
|
// traversals for each mapped name. Panics if t is not a struct or Indirectable
|
||||||
|
// to a struct. Returns empty int slice for each name not found.
|
||||||
|
func (m *Mapper) TraversalsByName(t reflect.Type, names []string) [][]int {
|
||||||
|
r := make([][]int, 0, len(names))
|
||||||
|
m.TraversalsByNameFunc(t, names, func(_ int, i []int) error {
|
||||||
|
if i == nil {
|
||||||
|
r = append(r, []int{})
|
||||||
|
} else {
|
||||||
|
r = append(r, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraversalsByNameFunc traverses the mapped names and calls fn with the index of
|
||||||
|
// each name and the struct traversal represented by that name. Panics if t is not
|
||||||
|
// a struct or Indirectable to a struct. Returns the first error returned by fn or nil.
|
||||||
|
func (m *Mapper) TraversalsByNameFunc(t reflect.Type, names []string, fn func(int, []int) error) error {
|
||||||
|
t = Deref(t)
|
||||||
|
mustBe(t, reflect.Struct)
|
||||||
|
tm := m.TypeMap(t)
|
||||||
|
for i, name := range names {
|
||||||
|
fi, ok := tm.Names[name]
|
||||||
|
if !ok {
|
||||||
|
if err := fn(i, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := fn(i, fi.Index); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldByIndexes returns a value for the field given by the struct traversal
|
||||||
|
// for the given value.
|
||||||
|
func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value {
|
||||||
|
for _, i := range indexes {
|
||||||
|
v = reflect.Indirect(v).Field(i)
|
||||||
|
// if this is a pointer and it's nil, allocate a new value and set it
|
||||||
|
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||||
|
alloc := reflect.New(Deref(v.Type()))
|
||||||
|
v.Set(alloc)
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Map && v.IsNil() {
|
||||||
|
v.Set(reflect.MakeMap(v.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldByIndexesReadOnly returns a value for a particular struct traversal,
|
||||||
|
// but is not concerned with allocating nil pointers because the value is
|
||||||
|
// going to be used for reading and not setting.
|
||||||
|
func FieldByIndexesReadOnly(v reflect.Value, indexes []int) reflect.Value {
|
||||||
|
for _, i := range indexes {
|
||||||
|
v = reflect.Indirect(v).Field(i)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deref is Indirect for reflect.Types
|
||||||
|
func Deref(t reflect.Type) reflect.Type {
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- helpers & utilities --
|
||||||
|
|
||||||
|
type kinder interface {
|
||||||
|
Kind() reflect.Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustBe checks a value against a kind, panicing with a reflect.ValueError
|
||||||
|
// if the kind isn't that which is required.
|
||||||
|
func mustBe(v kinder, expected reflect.Kind) {
|
||||||
|
if k := v.Kind(); k != expected {
|
||||||
|
panic(&reflect.ValueError{Method: methodName(), Kind: k})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// methodName returns the caller of the function calling methodName
|
||||||
|
func methodName() string {
|
||||||
|
pc, _, _, _ := runtime.Caller(2)
|
||||||
|
f := runtime.FuncForPC(pc)
|
||||||
|
if f == nil {
|
||||||
|
return "unknown method"
|
||||||
|
}
|
||||||
|
return f.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
type typeQueue struct {
|
||||||
|
t reflect.Type
|
||||||
|
fi *FieldInfo
|
||||||
|
pp string // Parent path
|
||||||
|
}
|
||||||
|
|
||||||
|
// A copying append that creates a new slice each time.
|
||||||
|
func apnd(is []int, i int) []int {
|
||||||
|
x := make([]int, len(is)+1)
|
||||||
|
for p, n := range is {
|
||||||
|
x[p] = n
|
||||||
|
}
|
||||||
|
x[len(x)-1] = i
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapf func(string) string
|
||||||
|
|
||||||
|
// parseName parses the tag and the target name for the given field using
|
||||||
|
// the tagName (eg 'json' for `json:"foo"` tags), mapFunc for mapping the
|
||||||
|
// field's name to a target name, and tagMapFunc for mapping the tag to
|
||||||
|
// a target name.
|
||||||
|
func parseName(field reflect.StructField, tagName string, mapFunc, tagMapFunc mapf) (tag, fieldName string) {
|
||||||
|
// first, set the fieldName to the field's name
|
||||||
|
fieldName = field.Name
|
||||||
|
// if a mapFunc is set, use that to override the fieldName
|
||||||
|
if mapFunc != nil {
|
||||||
|
fieldName = mapFunc(fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's no tag to look for, return the field name
|
||||||
|
if tagName == "" {
|
||||||
|
return "", fieldName
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this tag is not set using the normal convention in the tag,
|
||||||
|
// then return the fieldname.. this check is done because according
|
||||||
|
// to the reflect documentation:
|
||||||
|
// If the tag does not have the conventional format,
|
||||||
|
// the value returned by Get is unspecified.
|
||||||
|
// which doesn't sound great.
|
||||||
|
if !strings.Contains(string(field.Tag), tagName+":") {
|
||||||
|
return "", fieldName
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point we're fairly sure that we have a tag, so lets pull it out
|
||||||
|
tag = field.Tag.Get(tagName)
|
||||||
|
|
||||||
|
// if we have a mapper function, call it on the whole tag
|
||||||
|
// XXX: this is a change from the old version, which pulled out the name
|
||||||
|
// before the tagMapFunc could be run, but I think this is the right way
|
||||||
|
if tagMapFunc != nil {
|
||||||
|
tag = tagMapFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, split the options from the name
|
||||||
|
parts := strings.Split(tag, ",")
|
||||||
|
fieldName = parts[0]
|
||||||
|
|
||||||
|
return tag, fieldName
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseOptions parses options out of a tag string, skipping the name
|
||||||
|
func parseOptions(tag string) map[string]string {
|
||||||
|
parts := strings.Split(tag, ",")
|
||||||
|
options := make(map[string]string, len(parts))
|
||||||
|
if len(parts) > 1 {
|
||||||
|
for _, opt := range parts[1:] {
|
||||||
|
// short circuit potentially expensive split op
|
||||||
|
if strings.Contains(opt, "=") {
|
||||||
|
kv := strings.Split(opt, "=")
|
||||||
|
options[kv[0]] = kv[1]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
options[opt] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMapping returns a mapping for the t type, using the tagName, mapFunc and
|
||||||
|
// tagMapFunc to determine the canonical names of fields.
|
||||||
|
func getMapping(t reflect.Type, tagName string, mapFunc, tagMapFunc mapf) *StructMap {
|
||||||
|
m := []*FieldInfo{}
|
||||||
|
|
||||||
|
root := &FieldInfo{}
|
||||||
|
queue := []typeQueue{}
|
||||||
|
queue = append(queue, typeQueue{Deref(t), root, ""})
|
||||||
|
|
||||||
|
QueueLoop:
|
||||||
|
for len(queue) != 0 {
|
||||||
|
// pop the first item off of the queue
|
||||||
|
tq := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
|
||||||
|
// ignore recursive field
|
||||||
|
for p := tq.fi.Parent; p != nil; p = p.Parent {
|
||||||
|
if tq.fi.Field.Type == p.Field.Type {
|
||||||
|
continue QueueLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nChildren := 0
|
||||||
|
if tq.t.Kind() == reflect.Struct {
|
||||||
|
nChildren = tq.t.NumField()
|
||||||
|
}
|
||||||
|
tq.fi.Children = make([]*FieldInfo, nChildren)
|
||||||
|
|
||||||
|
// iterate through all of its fields
|
||||||
|
for fieldPos := 0; fieldPos < nChildren; fieldPos++ {
|
||||||
|
|
||||||
|
f := tq.t.Field(fieldPos)
|
||||||
|
|
||||||
|
// parse the tag and the target name using the mapping options for this field
|
||||||
|
tag, name := parseName(f, tagName, mapFunc, tagMapFunc)
|
||||||
|
|
||||||
|
// if the name is "-", disabled via a tag, skip it
|
||||||
|
if name == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fi := FieldInfo{
|
||||||
|
Field: f,
|
||||||
|
Name: name,
|
||||||
|
Zero: reflect.New(f.Type).Elem(),
|
||||||
|
Options: parseOptions(tag),
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the path is empty this path is just the name
|
||||||
|
if tq.pp == "" {
|
||||||
|
fi.Path = fi.Name
|
||||||
|
} else {
|
||||||
|
fi.Path = tq.pp + "." + fi.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip unexported fields
|
||||||
|
if len(f.PkgPath) != 0 && !f.Anonymous {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// bfs search of anonymous embedded structs
|
||||||
|
if f.Anonymous {
|
||||||
|
pp := tq.pp
|
||||||
|
if tag != "" {
|
||||||
|
pp = fi.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
fi.Embedded = true
|
||||||
|
fi.Index = apnd(tq.fi.Index, fieldPos)
|
||||||
|
nChildren := 0
|
||||||
|
ft := Deref(f.Type)
|
||||||
|
if ft.Kind() == reflect.Struct {
|
||||||
|
nChildren = ft.NumField()
|
||||||
|
}
|
||||||
|
fi.Children = make([]*FieldInfo, nChildren)
|
||||||
|
queue = append(queue, typeQueue{Deref(f.Type), &fi, pp})
|
||||||
|
} else if fi.Zero.Kind() == reflect.Struct || (fi.Zero.Kind() == reflect.Ptr && fi.Zero.Type().Elem().Kind() == reflect.Struct) {
|
||||||
|
fi.Index = apnd(tq.fi.Index, fieldPos)
|
||||||
|
fi.Children = make([]*FieldInfo, Deref(f.Type).NumField())
|
||||||
|
queue = append(queue, typeQueue{Deref(f.Type), &fi, fi.Path})
|
||||||
|
}
|
||||||
|
|
||||||
|
fi.Index = apnd(tq.fi.Index, fieldPos)
|
||||||
|
fi.Parent = tq.fi
|
||||||
|
tq.fi.Children[fieldPos] = &fi
|
||||||
|
m = append(m, &fi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flds := &StructMap{Index: m, Tree: root, Paths: map[string]*FieldInfo{}, Names: map[string]*FieldInfo{}}
|
||||||
|
for _, fi := range flds.Index {
|
||||||
|
flds.Paths[fi.Path] = fi
|
||||||
|
if fi.Name != "" && !fi.Embedded {
|
||||||
|
flds.Names[fi.Path] = fi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flds
|
||||||
|
}
|
1047
vendor/github.com/jmoiron/sqlx/sqlx.go
generated
vendored
Normal file
1047
vendor/github.com/jmoiron/sqlx/sqlx.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
348
vendor/github.com/jmoiron/sqlx/sqlx_context.go
generated
vendored
Normal file
348
vendor/github.com/jmoiron/sqlx/sqlx_context.go
generated
vendored
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package sqlx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectContext to a database and verify with a ping.
|
||||||
|
func ConnectContext(ctx context.Context, driverName, dataSourceName string) (*DB, error) {
|
||||||
|
db, err := Open(driverName, dataSourceName)
|
||||||
|
if err != nil {
|
||||||
|
return db, err
|
||||||
|
}
|
||||||
|
err = db.PingContext(ctx)
|
||||||
|
return db, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryerContext is an interface used by GetContext and SelectContext
|
||||||
|
type QueryerContext interface {
|
||||||
|
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryxContext(ctx context.Context, query string, args ...interface{}) (*Rows, error)
|
||||||
|
QueryRowxContext(ctx context.Context, query string, args ...interface{}) *Row
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreparerContext is an interface used by PreparexContext.
|
||||||
|
type PreparerContext interface {
|
||||||
|
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecerContext is an interface used by MustExecContext and LoadFileContext
|
||||||
|
type ExecerContext interface {
|
||||||
|
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtContext is a union interface which can bind, query, and exec, with Context
|
||||||
|
// used by NamedQueryContext and NamedExecContext.
|
||||||
|
type ExtContext interface {
|
||||||
|
binder
|
||||||
|
QueryerContext
|
||||||
|
ExecerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectContext executes a query using the provided Queryer, and StructScans
|
||||||
|
// each row into dest, which must be a slice. If the slice elements are
|
||||||
|
// scannable, then the result set must have only one column. Otherwise,
|
||||||
|
// StructScan is used. The *sql.Rows are closed automatically.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func SelectContext(ctx context.Context, q QueryerContext, dest interface{}, query string, args ...interface{}) error {
|
||||||
|
rows, err := q.QueryxContext(ctx, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// if something happens here, we want to make sure the rows are Closed
|
||||||
|
defer rows.Close()
|
||||||
|
return scanAll(rows, dest, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreparexContext prepares a statement.
|
||||||
|
//
|
||||||
|
// The provided context is used for the preparation of the statement, not for
|
||||||
|
// the execution of the statement.
|
||||||
|
func PreparexContext(ctx context.Context, p PreparerContext, query string) (*Stmt, error) {
|
||||||
|
s, err := p.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Stmt{Stmt: s, unsafe: isUnsafe(p), Mapper: mapperFor(p)}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext does a QueryRow using the provided Queryer, and scans the
|
||||||
|
// resulting row to dest. If dest is scannable, the result must only have one
|
||||||
|
// column. Otherwise, StructScan is used. Get will return sql.ErrNoRows like
|
||||||
|
// row.Scan would. Any placeholder parameters are replaced with supplied args.
|
||||||
|
// An error is returned if the result set is empty.
|
||||||
|
func GetContext(ctx context.Context, q QueryerContext, dest interface{}, query string, args ...interface{}) error {
|
||||||
|
r := q.QueryRowxContext(ctx, query, args...)
|
||||||
|
return r.scanAny(dest, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFileContext exec's every statement in a file (as a single call to Exec).
|
||||||
|
// LoadFileContext may return a nil *sql.Result if errors are encountered
|
||||||
|
// locating or reading the file at path. LoadFile reads the entire file into
|
||||||
|
// memory, so it is not suitable for loading large data dumps, but can be useful
|
||||||
|
// for initializing schemas or loading indexes.
|
||||||
|
//
|
||||||
|
// FIXME: this does not really work with multi-statement files for mattn/go-sqlite3
|
||||||
|
// or the go-mysql-driver/mysql drivers; pq seems to be an exception here. Detecting
|
||||||
|
// this by requiring something with DriverName() and then attempting to split the
|
||||||
|
// queries will be difficult to get right, and its current driver-specific behavior
|
||||||
|
// is deemed at least not complex in its incorrectness.
|
||||||
|
func LoadFileContext(ctx context.Context, e ExecerContext, path string) (*sql.Result, error) {
|
||||||
|
realpath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
contents, err := ioutil.ReadFile(realpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res, err := e.ExecContext(ctx, string(contents))
|
||||||
|
return &res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustExecContext execs the query using e and panics if there was an error.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func MustExecContext(ctx context.Context, e ExecerContext, query string, args ...interface{}) sql.Result {
|
||||||
|
res, err := e.ExecContext(ctx, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareNamedContext returns an sqlx.NamedStmt
|
||||||
|
func (db *DB) PrepareNamedContext(ctx context.Context, query string) (*NamedStmt, error) {
|
||||||
|
return prepareNamedContext(ctx, db, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedQueryContext using this DB.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (db *DB) NamedQueryContext(ctx context.Context, query string, arg interface{}) (*Rows, error) {
|
||||||
|
return NamedQueryContext(ctx, db, query, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedExecContext using this DB.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (db *DB) NamedExecContext(ctx context.Context, query string, arg interface{}) (sql.Result, error) {
|
||||||
|
return NamedExecContext(ctx, db, query, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectContext using this DB.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (db *DB) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
|
||||||
|
return SelectContext(ctx, db, dest, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext using this DB.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
// An error is returned if the result set is empty.
|
||||||
|
func (db *DB) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
|
||||||
|
return GetContext(ctx, db, 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 (db *DB) PreparexContext(ctx context.Context, query string) (*Stmt, error) {
|
||||||
|
return PreparexContext(ctx, db, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryxContext queries the database and returns an *sqlx.Rows.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (db *DB) QueryxContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
|
||||||
|
r, err := db.DB.QueryContext(ctx, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Rows{Rows: r, unsafe: db.unsafe, Mapper: db.Mapper}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowxContext queries the database and returns an *sqlx.Row.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (db *DB) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *Row {
|
||||||
|
rows, err := db.DB.QueryContext(ctx, query, args...)
|
||||||
|
return &Row{rows: rows, err: err, unsafe: db.unsafe, Mapper: db.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustBeginTx starts a transaction, and panics on error. 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
|
||||||
|
// MustBeginContext is canceled.
|
||||||
|
func (db *DB) MustBeginTx(ctx context.Context, opts *sql.TxOptions) *Tx {
|
||||||
|
tx, err := db.BeginTxx(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustExecContext (panic) runs MustExec using this database.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (db *DB) MustExecContext(ctx context.Context, query string, args ...interface{}) sql.Result {
|
||||||
|
return MustExecContext(ctx, db, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (db *DB) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
|
||||||
|
tx, err := db.DB.BeginTx(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StmtxContext returns a version of the prepared statement which runs within a
|
||||||
|
// transaction. Provided stmt can be either *sql.Stmt or *sqlx.Stmt.
|
||||||
|
func (tx *Tx) StmtxContext(ctx context.Context, stmt interface{}) *Stmt {
|
||||||
|
var s *sql.Stmt
|
||||||
|
switch v := stmt.(type) {
|
||||||
|
case Stmt:
|
||||||
|
s = v.Stmt
|
||||||
|
case *Stmt:
|
||||||
|
s = v.Stmt
|
||||||
|
case sql.Stmt:
|
||||||
|
s = &v
|
||||||
|
case *sql.Stmt:
|
||||||
|
s = v
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("non-statement type %v passed to Stmtx", reflect.ValueOf(stmt).Type()))
|
||||||
|
}
|
||||||
|
return &Stmt{Stmt: tx.StmtContext(ctx, s), Mapper: tx.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedStmtContext returns a version of the prepared statement which runs
|
||||||
|
// within a transaction.
|
||||||
|
func (tx *Tx) NamedStmtContext(ctx context.Context, stmt *NamedStmt) *NamedStmt {
|
||||||
|
return &NamedStmt{
|
||||||
|
QueryString: stmt.QueryString,
|
||||||
|
Params: stmt.Params,
|
||||||
|
Stmt: tx.StmtxContext(ctx, stmt.Stmt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (tx *Tx) PreparexContext(ctx context.Context, query string) (*Stmt, error) {
|
||||||
|
return PreparexContext(ctx, tx, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareNamedContext returns an sqlx.NamedStmt
|
||||||
|
func (tx *Tx) PrepareNamedContext(ctx context.Context, query string) (*NamedStmt, error) {
|
||||||
|
return prepareNamedContext(ctx, tx, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustExecContext runs MustExecContext within a transaction.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (tx *Tx) MustExecContext(ctx context.Context, query string, args ...interface{}) sql.Result {
|
||||||
|
return MustExecContext(ctx, tx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryxContext within a transaction and context.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (tx *Tx) QueryxContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
|
||||||
|
r, err := tx.Tx.QueryContext(ctx, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Rows{Rows: r, unsafe: tx.unsafe, Mapper: tx.Mapper}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectContext within a transaction and context.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (tx *Tx) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
|
||||||
|
return SelectContext(ctx, tx, dest, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext within a transaction and context.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
// An error is returned if the result set is empty.
|
||||||
|
func (tx *Tx) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
|
||||||
|
return GetContext(ctx, tx, dest, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowxContext within a transaction and context.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (tx *Tx) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *Row {
|
||||||
|
rows, err := tx.Tx.QueryContext(ctx, query, args...)
|
||||||
|
return &Row{rows: rows, err: err, unsafe: tx.unsafe, Mapper: tx.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedExecContext using this Tx.
|
||||||
|
// Any named placeholder parameters are replaced with fields from arg.
|
||||||
|
func (tx *Tx) NamedExecContext(ctx context.Context, query string, arg interface{}) (sql.Result, error) {
|
||||||
|
return NamedExecContext(ctx, tx, query, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectContext using the prepared statement.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (s *Stmt) SelectContext(ctx context.Context, dest interface{}, args ...interface{}) error {
|
||||||
|
return SelectContext(ctx, &qStmt{s}, dest, "", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext using the prepared statement.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
// An error is returned if the result set is empty.
|
||||||
|
func (s *Stmt) GetContext(ctx context.Context, dest interface{}, args ...interface{}) error {
|
||||||
|
return GetContext(ctx, &qStmt{s}, dest, "", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustExecContext (panic) using this statement. Note that the query portion of
|
||||||
|
// the error output will be blank, as Stmt does not expose its query.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (s *Stmt) MustExecContext(ctx context.Context, args ...interface{}) sql.Result {
|
||||||
|
return MustExecContext(ctx, &qStmt{s}, "", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowxContext using this statement.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (s *Stmt) QueryRowxContext(ctx context.Context, args ...interface{}) *Row {
|
||||||
|
qs := &qStmt{s}
|
||||||
|
return qs.QueryRowxContext(ctx, "", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryxContext using this statement.
|
||||||
|
// Any placeholder parameters are replaced with supplied args.
|
||||||
|
func (s *Stmt) QueryxContext(ctx context.Context, args ...interface{}) (*Rows, error) {
|
||||||
|
qs := &qStmt{s}
|
||||||
|
return qs.QueryxContext(ctx, "", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *qStmt) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||||
|
return q.Stmt.QueryContext(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *qStmt) QueryxContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
|
||||||
|
r, err := q.Stmt.QueryContext(ctx, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Rows{Rows: r, unsafe: q.Stmt.unsafe, Mapper: q.Stmt.Mapper}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *qStmt) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *Row {
|
||||||
|
rows, err := q.Stmt.QueryContext(ctx, args...)
|
||||||
|
return &Row{rows: rows, err: err, unsafe: q.Stmt.unsafe, Mapper: q.Stmt.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *qStmt) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return q.Stmt.ExecContext(ctx, args...)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user