Commit e5e05627 authored by Daniel Theophanes's avatar Daniel Theophanes Committed by Brad Fitzpatrick

database/sql: document expectations for named parameters

Require parameter names to not begin with a symbol.

Change-Id: I5dfe9d4e181f0daf71dad2f395aca41c68678cbe
Reviewed-on: https://go-review.googlesource.com/33493Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent ea1b90f8
...@@ -13,6 +13,8 @@ import ( ...@@ -13,6 +13,8 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"time" "time"
"unicode"
"unicode/utf8"
) )
var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
...@@ -24,6 +26,17 @@ func describeNamedValue(nv *driver.NamedValue) string { ...@@ -24,6 +26,17 @@ func describeNamedValue(nv *driver.NamedValue) string {
return fmt.Sprintf("with name %q", nv.Name) return fmt.Sprintf("with name %q", nv.Name)
} }
func validateNamedValueName(name string) error {
if len(name) == 0 {
return nil
}
r, _ := utf8.DecodeRuneInString(name)
if unicode.IsLetter(r) {
return nil
}
return fmt.Errorf("name %q does not begin with a letter", name)
}
// driverArgs converts arguments from callers of Stmt.Exec and // driverArgs converts arguments from callers of Stmt.Exec and
// Stmt.Query into driver Values. // Stmt.Query into driver Values.
// //
...@@ -43,6 +56,9 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error) ...@@ -43,6 +56,9 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error)
nv := &nvargs[n] nv := &nvargs[n]
nv.Ordinal = n + 1 nv.Ordinal = n + 1
if np, ok := arg.(NamedArg); ok { if np, ok := arg.(NamedArg); ok {
if err := validateNamedValueName(np.Name); err != nil {
return nil, err
}
arg = np.Value arg = np.Value
nvargs[n].Name = np.Name nvargs[n].Name = np.Name
} }
...@@ -60,6 +76,9 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error) ...@@ -60,6 +76,9 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error)
nv := &nvargs[n] nv := &nvargs[n]
nv.Ordinal = n + 1 nv.Ordinal = n + 1
if np, ok := arg.(NamedArg); ok { if np, ok := arg.(NamedArg); ok {
if err := validateNamedValueName(np.Name); err != nil {
return nil, err
}
arg = np.Value arg = np.Value
nv.Name = np.Name nv.Name = np.Name
} }
......
...@@ -27,13 +27,18 @@ import ( ...@@ -27,13 +27,18 @@ import (
type Value interface{} type Value interface{}
// NamedValue holds both the value name and value. // NamedValue holds both the value name and value.
// The Ordinal is the position of the parameter starting from one and is always set.
// If the Name is not empty it should be used for the parameter identifier and
// not the ordinal position.
type NamedValue struct { type NamedValue struct {
Name string // If the Name is not empty it should be used for the parameter identifier and
// not the ordinal position.
//
// Name will not have a symbol prefix.
Name string
// Ordinal position of the parameter starting from one and is always set.
Ordinal int Ordinal int
Value Value
// Value is the parameter value.
Value Value
} }
// Driver is the interface that must be implemented by a database // Driver is the interface that must be implemented by a database
......
...@@ -713,7 +713,7 @@ func (s *fakeStmt) execInsert(args []driver.NamedValue, doInsert bool) (driver.R ...@@ -713,7 +713,7 @@ func (s *fakeStmt) execInsert(args []driver.NamedValue, doInsert bool) (driver.R
} else { } else {
// Assign value from argument placeholder name. // Assign value from argument placeholder name.
for _, a := range args { for _, a := range args {
if a.Name == strvalue { if a.Name == strvalue[1:] {
val = a.Value val = a.Value
break break
} }
...@@ -818,7 +818,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( ...@@ -818,7 +818,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
} else { } else {
// Assign arg value from placeholder name. // Assign arg value from placeholder name.
for _, a := range args { for _, a := range args {
if a.Name == wcol.Placeholder { if a.Name == wcol.Placeholder[1:] {
argValue = a.Value argValue = a.Value
break break
} }
......
...@@ -76,6 +76,8 @@ type NamedArg struct { ...@@ -76,6 +76,8 @@ type NamedArg struct {
// Name of the parameter placeholder. If empty the ordinal position in the // Name of the parameter placeholder. If empty the ordinal position in the
// argument list will be used. // argument list will be used.
//
// Name must omit any symbol prefix.
Name string Name string
// Value of the parameter. It may be assigned the same value types as // Value of the parameter. It may be assigned the same value types as
......
...@@ -486,8 +486,8 @@ func TestQueryNamedArg(t *testing.T) { ...@@ -486,8 +486,8 @@ func TestQueryNamedArg(t *testing.T) {
rows, err := db.Query( rows, err := db.Query(
// Ensure the name and age parameters only match on placeholder name, not position. // Ensure the name and age parameters only match on placeholder name, not position.
"SELECT|people|age,name|name=?name,age=?age", "SELECT|people|age,name|name=?name,age=?age",
Named("?age", 2), Named("age", 2),
Named("?name", "Bob"), Named("name", "Bob"),
) )
if err != nil { if err != nil {
t.Fatalf("Query: %v", err) t.Fatalf("Query: %v", err)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment