Skip to content

sync jmoiron/sqlx #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 75 commits into from
Jun 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
ae33bab
handle nil values in sqlx/types.JsonText
Jan 11, 2015
3cc7d0b
fix another error when marshalling an empty jsonText
Jan 14, 2015
c52770c
Merge branch 'master' of https://github.com/jmoiron/sqlx
pakohan Sep 23, 2015
c61621a
Don't ignore possible gzip error
dominikh Aug 11, 2016
74f9f64
JSONText & GzippedText are safe in 1.7
v4n Aug 27, 2016
d573dd9
made the 'missing destination name' contain not only the column name,
mlitvin Sep 14, 2016
7451a4b
Merge pull request #251 from mlitvin/missingerr
jmoiron Sep 14, 2016
f4291b0
Merge pull request #247 from v4n/master
jmoiron Sep 14, 2016
05b81a7
Merge pull request #243 from dominikh/fix-gzipped-scan
jmoiron Sep 14, 2016
581097f
Adds type BitBool Scanner/Valuer for MySQL type BIT
RayfenWindspear Oct 13, 2016
0f13de5
Merge pull request #254 from RayfenWindspear/mysql-bitbool
jmoiron Oct 29, 2016
5f97679
remove dangling else from #254
jmoiron Oct 29, 2016
27160f2
Merge remote-tracking branch 'pakohan/master' into NullJSONText
Taik Nov 9, 2016
ef752ab
Pass golint
Taik Nov 9, 2016
041545b
Add NullJSONText type
Taik Nov 9, 2016
71e3136
Improve placeholder substitution documentation
gibson042 Nov 14, 2016
77ff2b8
Don't drop errors in scanAll
dominikh Nov 16, 2016
a9cfc4a
Merge pull request #262 from dominikh/error-handling
jmoiron Nov 18, 2016
f71d5ee
Merge pull request #261 from gibson042/2016-11-placeholder-documentation
jmoiron Nov 21, 2016
dfd5d81
Value() should be defined on a non-pointer receiver
Taik Nov 22, 2016
059ea5d
reflectx: don't mapping recursive field
tnextday Nov 26, 2016
4c83ffb
Information about the errors included in the godoc
boreq Nov 29, 2016
c60b4ab
Merge pull request #265 from boreq/docs
jmoiron Dec 2, 2016
6708aed
Merge pull request #259 from Taik/NullJSONText
jmoiron Dec 2, 2016
1a35d27
style fixes for a few recent PRs
jmoiron Dec 2, 2016
0b01d1c
fix texts for postgres
jmoiron Dec 2, 2016
43975e6
Merge remote-tracking branch 'tnd/reflect-recursive'
jmoiron Dec 2, 2016
f9b4c7a
address #266 sqlx/types json marshal
jmoiron Dec 7, 2016
590692e
scanAny: add nil check
pschultz Dec 8, 2016
cac998c
Merge pull request #268 from classmarkets/scan-any-panic
jmoiron Dec 9, 2016
e1956c6
Initial attempt at supporting Go 1.8's context in database/sql
wyattjoh Dec 30, 2016
f48589f
Copied tests over
wyattjoh Dec 30, 2016
fe3256d
Adding checks for errors to resolve linting errors
wyattjoh Dec 30, 2016
909e40f
Added a ConnectContext method
wyattjoh Dec 30, 2016
a501ce9
sqlx.mpr race condition fix
mmatczuk Jan 19, 2017
f980a91
Merge pull request #272 from mmatczuk/mmt/race_cond
jmoiron Jan 21, 2017
0c837e0
Fix doc wording in reflectx.FieldByName
matiasanaya Jan 28, 2017
d0322a1
Optimize Rebind performance and allocations
nussjustin Feb 17, 2017
5608f0d
Optimize In by specializing for concrete slice types
nussjustin Feb 17, 2017
acf1cbb
Merge pull request #281 from nussjustin/perf-rebind
jmoiron Feb 21, 2017
12007f2
Merge pull request #282 from nussjustin/perf-in
jmoiron Feb 21, 2017
bc8b1f8
Merge pull request #277 from matiasanaya/patch-1
jmoiron Feb 21, 2017
04a39d1
Merge pull request #270 from wyattjoh/master
jmoiron Feb 21, 2017
f407684
fix a few tests which were testing the wrong thing
jmoiron Feb 21, 2017
1f451f3
implement Tx.GetContext and Tx.SelectContext
husio Feb 27, 2017
8ed836a
Merge pull request #288 from husio/tx_context
jmoiron Mar 8, 2017
db69506
Add Tx.NamedExecContext method
Mar 15, 2017
3564e3a
Merge pull request #299 from smthpickboy/add_NamedExecContext_for_Tx
jmoiron Apr 12, 2017
19916dc
Fix header in README
slomek Apr 27, 2017
d9bd385
Merge pull request #310 from slomek/master
jmoiron Apr 30, 2017
502e7d6
Add missing PreparexContext function
c4milo Jun 17, 2017
9afb36a
Add missing tx.PrepareNamedContext function
c4milo Jun 18, 2017
f9cd482
[]byte should not be expanded by In
npiganeau Jul 18, 2017
364f96a
Added comment and test
npiganeau Jul 18, 2017
fef021f
fix README example
d6o Sep 20, 2017
a2429ba
Add TraversalsByName for zero allocation field traversal
nussjustin Sep 28, 2017
2824d91
Use TraversalsByNameFunc for binding named arguments
nussjustin Sep 28, 2017
3379e59
Merge pull request #357 from DiSiqueira/readme-fmt
jmoiron Oct 20, 2017
dcc25de
sqlx.Connect: Close db connection if db.Ping() fails
somersf Nov 29, 2017
99f3ad6
Merge pull request #370 from somersf/connect-close-on-error
jmoiron Nov 29, 2017
de86474
Merge pull request #358 from nussjustin/traversals-by-name-func
jmoiron Dec 11, 2017
05cef07
add support for postgres lib w/ timeouts
jmoiron Jan 24, 2018
5d28545
Added ColumnTypes support
cemremengu Feb 13, 2018
0d26b5f
added support for the cloudsqlpostgres driver
cmoad Feb 24, 2018
700973c
try adding travis
jmoiron Feb 28, 2018
645fea9
avoid unnecessary matrixes in travis
jmoiron Feb 28, 2018
bbb9b6b
adjust PropertyMap test to drivers that may return string now instead…
jmoiron Feb 28, 2018
76afd69
update build badges
jmoiron Feb 28, 2018
6765e6a
use travis_retry to work around coveralls 422 err
jmoiron Feb 28, 2018
5800ac0
Merge pull request #391 from andcostello/master
jmoiron Feb 28, 2018
9adbc06
Merge pull request #340 from npiganeau/patch-2
jmoiron Feb 28, 2018
7cbed6a
Merge pull request #387 from cemremengu/master
jmoiron Feb 28, 2018
cf35089
Merge pull request #329 from c4milo/master
jmoiron Feb 28, 2018
037fdd3
Simplify get Value
michaelyou Mar 14, 2018
2aeb6a9
Merge pull request #392 from michaelyou/feature-simplify-get-elem
jmoiron Apr 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#sqlx
# sqlx

[![Build Status](https://drone.io/github.com/jmoiron/sqlx/status.png)](https://drone.io/github.com/jmoiron/sqlx/latest) [![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)
[![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`,
Expand All @@ -26,9 +26,7 @@ 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.

More importantly, [golang bug #13905](https://github.com/golang/go/issues/13905)
makes `types.JSONText` and `types.GzippedText` _potentially unsafe_, **especially**
when used with common auto-scan sqlx idioms like `Select` and `Get`.
* 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

Expand Down Expand Up @@ -68,10 +66,12 @@ usage.
package main

import (
_ "github.com/lib/pq"
"database/sql"
"github.com/jmoiron/sqlx"
"fmt"
"log"

_ "github.com/lib/pq"
"github.com/jmoiron/sqlx"
)

var schema = `
Expand Down
70 changes: 46 additions & 24 deletions bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const (
// BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int {
switch driverName {
case "postgres", "pgx":
case "postgres", "pgx", "pq-timeouts", "cloudsqlpostgres":
return DOLLAR
case "mysql":
return QUESTION
Expand All @@ -43,27 +43,28 @@ func Rebind(bindType int, query string) string {
return query
}

qb := []byte(query)
// Add space enough for 10 params before we have to allocate
rqb := make([]byte, 0, len(qb)+10)
j := 1
for _, b := range qb {
if b == '?' {
switch bindType {
case DOLLAR:
rqb = append(rqb, '$')
case NAMED:
rqb = append(rqb, ':', 'a', 'r', 'g')
}
for _, b := range strconv.Itoa(j) {
rqb = append(rqb, byte(b))
}
j++
} else {
rqb = append(rqb, b)
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(rqb)

return string(append(rqb, query...))
}

// Experimental implementation of Rebind which uses a bytes.Buffer. The code is
Expand Down Expand Up @@ -112,7 +113,8 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
v := reflect.ValueOf(arg)
t := reflectx.Deref(v.Type())

if t.Kind() == reflect.Slice {
// []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

Expand All @@ -135,9 +137,9 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
}

newArgs := make([]interface{}, 0, flatArgsCount)
buf := bytes.NewBuffer(make([]byte, 0, len(query)+len(", ?")*flatArgsCount))

var arg, offset int
var buf bytes.Buffer

for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
if arg >= len(meta) {
Expand All @@ -163,13 +165,12 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
// write everything up to and including our ? character
buf.WriteString(query[:offset+i+1])

newArgs = append(newArgs, argMeta.v.Index(0).Interface())

for si := 1; si < argMeta.length; si++ {
buf.WriteString(", ?")
newArgs = append(newArgs, argMeta.v.Index(si).Interface())
}

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:]
Expand All @@ -184,3 +185,24 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {

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
}
20 changes: 15 additions & 5 deletions named.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (n *NamedStmt) Close() error {
}

// 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 {
Expand All @@ -45,6 +46,7 @@ func (n *NamedStmt) Exec(arg interface{}) (sql.Result, error) {
}

// 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 {
Expand All @@ -56,6 +58,7 @@ func (n *NamedStmt) Query(arg interface{}) (*sql.Rows, error) {
// 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 {
Expand All @@ -65,6 +68,7 @@ func (n *NamedStmt) QueryRow(arg interface{}) *Row {
}

// 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 {
Expand All @@ -74,6 +78,7 @@ func (n *NamedStmt) MustExec(arg interface{}) sql.Result {
}

// 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 {
Expand All @@ -84,11 +89,13 @@ func (n *NamedStmt) Queryx(arg interface{}) (*Rows, error) {

// 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 {
Expand All @@ -100,6 +107,7 @@ func (n *NamedStmt) Select(dest interface{}, arg interface{}) error {
}

// 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)
Expand Down Expand Up @@ -155,16 +163,18 @@ func bindArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{
v = v.Elem()
}

fields := m.TraversalsByName(v.Type(), names)
for i, t := range fields {
err := m.TraversalsByNameFunc(v.Type(), names, func(i int, t []int) error {
if len(t) == 0 {
return arglist, fmt.Errorf("could not find name %s in %#v", names[i], arg)
return fmt.Errorf("could not find name %s in %#v", names[i], arg)
}

val := reflectx.FieldByIndexesReadOnly(v, t)
arglist = append(arglist, val.Interface())
}

return arglist, nil
return nil
})

return arglist, err
}

// like bindArgs, but for maps.
Expand Down
132 changes: 132 additions & 0 deletions named_context.go
Original file line number Diff line number Diff line change
@@ -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...)
}
Loading