Skip to content

Commit

Permalink
documentation cleanup, vet and lint fixes, remove BindMap and BindStr…
Browse files Browse the repository at this point in the history
…uct from public interface in favor of a single BindNamed
  • Loading branch information
jmoiron committed May 18, 2014
1 parent 2b1877d commit 16e5f49
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 66 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ _cgo_export.*
_testmain.go

*.exe
tags
environ
6 changes: 3 additions & 3 deletions bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import (
"strconv"
)

// Bindvar types supported by sqlx's Rebind & BindMap/Struct functions.
// 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
// BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int {
switch driverName {
case "postgres":
Expand All @@ -33,7 +33,7 @@ func BindType(driverName string) int {

// FIXME: this is now produces the wrong results for oracle's NAMED bindtype

// Rebind a query from the default bindtype (QUESTION) to the target bindtype
// Rebind a query from the default bindtype (QUESTION) to the target bindtype.
func Rebind(bindType int, query string) string {
if bindType != DOLLAR {
return query
Expand Down
8 changes: 4 additions & 4 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Package sqlx provides general purpose extensions to database/sql.
//
// sqlx is intended to seamlessly wrap database/sql and provide convenience
// 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
// None of the underlying database/sql methods are changed. Instead all extended
// behavior is implemented through new methods defined on wrapper types.
//
// sqlx adds struct scanning, named queries, query rebinding between drivers,
// convenient shorthand for common error handling, from-file query execution,
// Additions include scanning into structs, named query support, rebinding
// queries for different drivers, convenient shorthands for common error handling
// and more.
//
package sqlx
26 changes: 12 additions & 14 deletions named.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ package sqlx

// Named Query Support
//
// * BindStruct, BindMap - bind query bindvars to map/struct args
// * BindMap - bind query bindvars to map/struct args
// * NamedExec, NamedQuery - named query w/ struct or map
// * NamedExecMap, NamedQueryMap - named query w/ maps (DEPRECATED)
// * 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
// * bindAny - call BindStruct or BindMap depending on the type of the argument
//
import (
"database/sql"
Expand Down Expand Up @@ -111,7 +109,7 @@ func (n *NamedStmt) Get(dest interface{}, arg interface{}) error {
// named statements (as the bindtype must be determined).
type namedPreparer interface {
Preparer
Binder
binder
}

func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
Expand Down Expand Up @@ -177,10 +175,10 @@ func bindMapArgs(names []string, arg map[string]interface{}) ([]interface{}, err
return arglist, nil
}

// BindStruct binds a named parameter query with fields from a struct argument.
// 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{}) (string, []interface{}, error) {
func bindStruct(bindType int, query string, arg interface{}) (string, []interface{}, error) {
bound, names, err := compileNamedQuery([]byte(query), bindType)
if err != nil {
return "", []interface{}{}, err
Expand All @@ -194,8 +192,8 @@ func BindStruct(bindType int, query string, arg interface{}) (string, []interfac
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) {
// 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
Expand Down Expand Up @@ -285,19 +283,19 @@ func compileNamedQuery(qs []byte, bindType int) (query string, names []string, e
return string(rebound), names, err
}

// bindAny binds a struct or a map by inspecting the arg interface.
func bindAny(e Ext, query string, arg interface{}) (string, []interface{}, error) {
// Bind binds a struct or a map to a query with named parameters.
func BindNamed(bindType int, query string, arg interface{}) (string, []interface{}, error) {
if maparg, ok := arg.(map[string]interface{}); ok {
return e.BindMap(query, maparg)
return bindMap(bindType, query, maparg)
}
return e.BindStruct(query, arg)
return bindStruct(bindType, query, arg)
}

// 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 := bindAny(e, query, arg)
q, args, err := BindNamed(BindType(e.DriverName()), query, arg)
if err != nil {
return nil, err
}
Expand All @@ -308,7 +306,7 @@ func NamedQuery(e Ext, query string, arg interface{}) (*Rows, error) {
// 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 := bindAny(e, query, arg)
q, args, err := BindNamed(BindType(e.DriverName()), query, arg)
if err != nil {
return nil, err
}
Expand Down
12 changes: 6 additions & 6 deletions named_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ func TestNamedQueries(t *testing.T) {
var p2 Person
rows.StructScan(&p2)
if p.FirstName != p2.FirstName {
t.Error("got %s, expected %s", p.FirstName, p2.FirstName)
t.Errorf("got %s, expected %s", p.FirstName, p2.FirstName)
}
if p.LastName != p2.LastName {
t.Error("got %s, expected %s", p.LastName, p2.LastName)
t.Errorf("got %s, expected %s", p.LastName, p2.LastName)
}
if p.Email != p2.Email {
t.Error("got %s, expected %s", p.Email, p2.Email)
t.Errorf("got %s, expected %s", p.Email, p2.Email)
}
}

Expand All @@ -142,13 +142,13 @@ func TestNamedQueries(t *testing.T) {
t.Errorf("got %d results, expected %d", len(people), 1)
}
if p.FirstName != people[0].FirstName {
t.Error("got %s, expected %s", p.FirstName, people[0].FirstName)
t.Errorf("got %s, expected %s", p.FirstName, people[0].FirstName)
}
if p.LastName != people[0].LastName {
t.Error("got %s, expected %s", p.LastName, people[0].LastName)
t.Errorf("got %s, expected %s", p.LastName, people[0].LastName)
}
if p.Email != people[0].Email {
t.Error("got %s, expected %s", p.Email, people[0].Email)
t.Errorf("got %s, expected %s", p.Email, people[0].Email)
}

// test Exec
Expand Down
54 changes: 21 additions & 33 deletions sqlx.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
// NameMapper is used to map column names to struct field names. By default,
// it uses strings.ToLower to lowercase struct field names. It can be set
// to whatever you want, but it is encouraged to be set before sqlx is used
// as field-to-name mappings are cached after first use on a type.
// as name-to-field mappings are cached after first use on a type.
var NameMapper = strings.ToLower
var origMapper = reflect.ValueOf(NameMapper)

Expand All @@ -33,51 +33,49 @@ var mpr *reflectx.Mapper
func mapper() *reflectx.Mapper {
if mpr == nil {
mpr = reflectx.NewMapperFunc("db", NameMapper)
}
if origMapper != reflect.ValueOf(NameMapper) {
} else if origMapper != reflect.ValueOf(NameMapper) {
// if NameMapper has changed, create a new mapper
mpr = reflectx.NewMapperFunc("db", NameMapper)
origMapper = reflect.ValueOf(NameMapper)
}
return mpr
}

// ColScanner is an interface for something which can Scan and return a list
// of columns (Row, Rows)
// ColScanner is an interface used by MapScan and SliceScan
type ColScanner interface {
Columns() ([]string, error)
Scan(dest ...interface{}) error
Err() error
}

// Queryer is an interface for something which can Query (Tx, DB, Stmt)
// Queryer is an interface used by Get and Select
type Queryer interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
Queryx(query string, args ...interface{}) (*Rows, error)
QueryRowx(query string, args ...interface{}) *Row
}

// Execer is an interface for something which can Exec (Tx, DB, Stmt)
// Execer is an interface used by MustExec and LoadFile
type Execer interface {
Exec(query string, args ...interface{}) (sql.Result, error)
}

// Binder is an interface for something which can bind queries (Tx, DB)
type Binder interface {
type binder interface {
DriverName() string
Rebind(string) string
BindMap(string, map[string]interface{}) (string, []interface{}, error)
BindStruct(string, interface{}) (string, []interface{}, error)
BindNamed(string, interface{}) (string, []interface{}, error)
}

// Ext is a union interface which can bind, query, and exec (Tx, DB), used for
// NamedQuery and NamedExec, which requires exec/query and BindMap/Struct
// Ext is a union interface which can bind, query, and exec, used by
// NamedQuery and NamedExec.
type Ext interface {
Binder
binder
Queryer
Execer
}

// Preparer is an interface for something which can prepare sql statements (Tx, DB)
// Preparer is an interface used by Preparex.
type Preparer interface {
Prepare(query string) (*sql.Stmt, error)
}
Expand Down Expand Up @@ -192,7 +190,7 @@ func (db *DB) DriverName() string {
return db.driverName
}

// Open is the same as database/sql's Open, but returns an *sqlx.DB instead.
// Open is the same as sql.Open, but returns an *sqlx.DB instead.
func Open(driverName, dataSourceName string) (*DB, error) {
db, err := sql.Open(driverName, dataSourceName)
if err != nil {
Expand All @@ -214,14 +212,9 @@ func (db *DB) Unsafe() *DB {
return &DB{DB: db.DB, driverName: db.driverName, unsafe: true}
}

// BindMap binds a query using the DB driver's bindvar type.
func (db *DB) BindMap(query string, argmap map[string]interface{}) (string, []interface{}, error) {
return BindMap(BindType(db.driverName), query, argmap)
}

// BindStruct binds a query using the DB driver's bindvar type.
func (db *DB) BindStruct(query string, arg interface{}) (string, []interface{}, error) {
return BindStruct(BindType(db.driverName), query, arg)
// BindNamed binds a query using the DB driver's bindvar type.
func (db *DB) BindNamed(query string, arg interface{}) (string, []interface{}, error) {
return BindNamed(BindType(db.driverName), query, arg)
}

// NamedQuery using this DB.
Expand Down Expand Up @@ -293,7 +286,7 @@ func (db *DB) PrepareNamed(query string) (*NamedStmt, error) {
return prepareNamed(db, query)
}

// Tx is an sqlx wrapper around database/sql's Tx with extra functionality
// Tx is an sqlx wrapper around sql.Tx with extra functionality
type Tx struct {
*sql.Tx
driverName string
Expand All @@ -316,14 +309,9 @@ func (tx *Tx) Unsafe() *Tx {
return &Tx{Tx: tx.Tx, driverName: tx.driverName, unsafe: true}
}

// BindMap binds a query within a transaction's bindvar type.
func (tx *Tx) BindMap(query string, argmap map[string]interface{}) (string, []interface{}, error) {
return BindMap(BindType(tx.driverName), query, argmap)
}

// BindStruct binds a query within a transaction's bindvar type.
func (tx *Tx) BindStruct(query string, arg interface{}) (string, []interface{}, error) {
return BindStruct(BindType(tx.driverName), query, arg)
// BindNamed binds a query within a transaction's bindvar type.
func (tx *Tx) BindNamed(query string, arg interface{}) (string, []interface{}, error) {
return BindNamed(BindType(tx.driverName), query, arg)
}

// NamedQuery within a transaction.
Expand Down Expand Up @@ -404,7 +392,7 @@ func (tx *Tx) PrepareNamed(query string) (*NamedStmt, error) {
return prepareNamed(tx, query)
}

// Stmt is an sqlx wrapper around database/sql's Stmt with extra functionality
// Stmt is an sqlx wrapper around sql.Stmt with extra functionality
type Stmt struct {
*sql.Stmt
unsafe bool
Expand Down
12 changes: 6 additions & 6 deletions sqlx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ func TestBindMap(t *testing.T) {
"last": "Moiron",
}

bq, args, _ := BindMap(QUESTION, q1, am)
bq, args, _ := bindMap(QUESTION, q1, am)
expect := `INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?)`
if bq != expect {
t.Errorf("Interpolation of query failed: got `%v`, expected `%v`\n", bq, expect)
Expand Down Expand Up @@ -917,7 +917,7 @@ func TestBindStruct(t *testing.T) {

am := tt{"Jason Moiron", 30, "Jason", "Moiron"}

bq, args, _ := BindStruct(QUESTION, q1, am)
bq, args, _ := bindStruct(QUESTION, q1, am)
expect := `INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?)`
if bq != expect {
t.Errorf("Interpolation of query failed: got `%v`, expected `%v`\n", bq, expect)
Expand All @@ -940,7 +940,7 @@ func TestBindStruct(t *testing.T) {
}

am2 := tt2{"Hello", "World"}
bq, args, _ = BindStruct(QUESTION, "INSERT INTO foo (a, b) VALUES (:field_2, :field_1)", am2)
bq, args, _ = bindStruct(QUESTION, "INSERT INTO foo (a, b) VALUES (:field_2, :field_1)", am2)
expect = `INSERT INTO foo (a, b) VALUES (?, ?)`
if bq != expect {
t.Errorf("Interpolation of query failed: got `%v`, expected `%v`\n", bq, expect)
Expand All @@ -957,7 +957,7 @@ func TestBindStruct(t *testing.T) {
am3.Field1 = "Hello"
am3.Field2 = "World"

bq, args, err = BindStruct(QUESTION, "INSERT INTO foo (a, b, c) VALUES (:name, :field_1, :field_2)", am3)
bq, args, err = bindStruct(QUESTION, "INSERT INTO foo (a, b, c) VALUES (:name, :field_1, :field_2)", am3)

if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -991,7 +991,7 @@ func BenchmarkBindStruct(b *testing.B) {
am := t{"Jason Moiron", 30, "Jason", "Moiron"}
b.StartTimer()
for i := 0; i < b.N; i++ {
BindStruct(DOLLAR, q1, am)
bindStruct(DOLLAR, q1, am)
//bindMap(QUESTION, q1, am)
}
}
Expand All @@ -1007,7 +1007,7 @@ func BenchmarkBindMap(b *testing.B) {
}
b.StartTimer()
for i := 0; i < b.N; i++ {
BindMap(DOLLAR, q1, am)
bindMap(DOLLAR, q1, am)
//bindMap(QUESTION, q1, am)
}
}
Expand Down
5 changes: 5 additions & 0 deletions types/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# types

The types package provides some useful types which implement the `sql.Scanner`
and `driver.Valuer` interfaces, suitable for use as scan and value targets with
database/sql.

0 comments on commit 16e5f49

Please sign in to comment.