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/33493
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go
index 4b4dfc4..ea2f377 100644
--- a/src/database/sql/convert.go
+++ b/src/database/sql/convert.go
@@ -13,6 +13,8 @@
 	"reflect"
 	"strconv"
 	"time"
+	"unicode"
+	"unicode/utf8"
 )
 
 var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
@@ -24,6 +26,17 @@
 	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
 // Stmt.Query into driver Values.
 //
@@ -43,6 +56,9 @@
 			nv := &nvargs[n]
 			nv.Ordinal = n + 1
 			if np, ok := arg.(NamedArg); ok {
+				if err := validateNamedValueName(np.Name); err != nil {
+					return nil, err
+				}
 				arg = np.Value
 				nvargs[n].Name = np.Name
 			}
@@ -60,6 +76,9 @@
 		nv := &nvargs[n]
 		nv.Ordinal = n + 1
 		if np, ok := arg.(NamedArg); ok {
+			if err := validateNamedValueName(np.Name); err != nil {
+				return nil, err
+			}
 			arg = np.Value
 			nv.Name = np.Name
 		}
diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go
index c8cbbf0..2e47cd9 100644
--- a/src/database/sql/driver/driver.go
+++ b/src/database/sql/driver/driver.go
@@ -27,13 +27,18 @@
 type Value interface{}
 
 // 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 {
-	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
-	Value   Value
+
+	// Value is the parameter value.
+	Value Value
 }
 
 // Driver is the interface that must be implemented by a database
diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go
index 416b97d..4b15f5b 100644
--- a/src/database/sql/fakedb_test.go
+++ b/src/database/sql/fakedb_test.go
@@ -713,7 +713,7 @@
 			} else {
 				// Assign value from argument placeholder name.
 				for _, a := range args {
-					if a.Name == strvalue {
+					if a.Name == strvalue[1:] {
 						val = a.Value
 						break
 					}
@@ -818,7 +818,7 @@
 				} else {
 					// Assign arg value from placeholder name.
 					for _, a := range args {
-						if a.Name == wcol.Placeholder {
+						if a.Name == wcol.Placeholder[1:] {
 							argValue = a.Value
 							break
 						}
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index a620707..e11a9da 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -76,6 +76,8 @@
 
 	// Name of the parameter placeholder. If empty the ordinal position in the
 	// argument list will be used.
+	//
+	// Name must omit any symbol prefix.
 	Name string
 
 	// Value of the parameter. It may be assigned the same value types as
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index 27fb765..02746a2 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -486,8 +486,8 @@
 	rows, err := db.Query(
 		// Ensure the name and age parameters only match on placeholder name, not position.
 		"SELECT|people|age,name|name=?name,age=?age",
-		Named("?age", 2),
-		Named("?name", "Bob"),
+		Named("age", 2),
+		Named("name", "Bob"),
 	)
 	if err != nil {
 		t.Fatalf("Query: %v", err)