Skip to content

Commit 707a833

Browse files
kardianosbradfitz
authored andcommitted
database/sql: add option to use named parameter in query arguments
Modify the new Context methods to take a name-value driver struct. This will require more modifications to drivers to use, but will reduce the overall number of structures that need to be maintained over time. Fixes #12381 Change-Id: I30747533ce418a1be5991a0c8767a26e8451adbd Reviewed-on: https://go-review.googlesource.com/30166 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 99df54f commit 707a833

File tree

6 files changed

+202
-39
lines changed

6 files changed

+202
-39
lines changed

src/database/sql/convert.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ var errNilPtr = errors.New("destination pointer is nil") // embedded in descript
2121
// Stmt.Query into driver Values.
2222
//
2323
// The statement ds may be nil, if no statement is available.
24-
func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) {
25-
dargs := make([]driver.Value, len(args))
24+
func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error) {
25+
nvargs := make([]driver.NamedValue, len(args))
2626
var si driver.Stmt
2727
if ds != nil {
2828
si = ds.si
@@ -33,16 +33,27 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) {
3333
if !ok {
3434
for n, arg := range args {
3535
var err error
36-
dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
36+
nvargs[n].Ordinal = n + 1
37+
if np, ok := arg.(NamedParam); ok {
38+
arg = np.Value
39+
nvargs[n].Name = np.Name
40+
}
41+
nvargs[n].Value, err = driver.DefaultParameterConverter.ConvertValue(arg)
42+
3743
if err != nil {
3844
return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
3945
}
4046
}
41-
return dargs, nil
47+
return nvargs, nil
4248
}
4349

4450
// Let the Stmt convert its own arguments.
4551
for n, arg := range args {
52+
nvargs[n].Ordinal = n + 1
53+
if np, ok := arg.(NamedParam); ok {
54+
arg = np.Value
55+
nvargs[n].Name = np.Name
56+
}
4657
// First, see if the value itself knows how to convert
4758
// itself to a driver type. For example, a NullString
4859
// struct changing into a string or nil.
@@ -66,18 +77,18 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) {
6677
// same error.
6778
var err error
6879
ds.Lock()
69-
dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
80+
nvargs[n].Value, err = cc.ColumnConverter(n).ConvertValue(arg)
7081
ds.Unlock()
7182
if err != nil {
7283
return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err)
7384
}
74-
if !driver.IsValue(dargs[n]) {
85+
if !driver.IsValue(nvargs[n].Value) {
7586
return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
76-
arg, dargs[n])
87+
arg, nvargs[n].Value)
7788
}
7889
}
7990

80-
return dargs, nil
91+
return nvargs, nil
8192
}
8293

8394
// convertAssign copies to dest the value in src, converting it if possible.

src/database/sql/ctxutil.go

+35-8
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,13 @@ func ctxDriverPrepare(ctx context.Context, ci driver.Conn, query string) (driver
5050
}
5151
}
5252

53-
func ctxDriverExec(ctx context.Context, execer driver.Execer, query string, dargs []driver.Value) (driver.Result, error) {
53+
func ctxDriverExec(ctx context.Context, execer driver.Execer, query string, nvdargs []driver.NamedValue) (driver.Result, error) {
5454
if execerCtx, is := execer.(driver.ExecerContext); is {
55-
return execerCtx.ExecContext(ctx, query, dargs)
55+
return execerCtx.ExecContext(ctx, query, nvdargs)
56+
}
57+
dargs, err := namedValueToValue(nvdargs)
58+
if err != nil {
59+
return nil, err
5660
}
5761
if ctx.Done() == context.Background().Done() {
5862
return execer.Exec(query, dargs)
@@ -90,9 +94,13 @@ func ctxDriverExec(ctx context.Context, execer driver.Execer, query string, darg
9094
}
9195
}
9296

93-
func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, dargs []driver.Value) (driver.Rows, error) {
97+
func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, nvdargs []driver.NamedValue) (driver.Rows, error) {
9498
if queryerCtx, is := queryer.(driver.QueryerContext); is {
95-
return queryerCtx.QueryContext(ctx, query, dargs)
99+
return queryerCtx.QueryContext(ctx, query, nvdargs)
100+
}
101+
dargs, err := namedValueToValue(nvdargs)
102+
if err != nil {
103+
return nil, err
96104
}
97105
if ctx.Done() == context.Background().Done() {
98106
return queryer.Query(query, dargs)
@@ -130,9 +138,13 @@ func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, d
130138
}
131139
}
132140

133-
func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, dargs []driver.Value) (driver.Result, error) {
141+
func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Result, error) {
134142
if siCtx, is := si.(driver.StmtExecContext); is {
135-
return siCtx.ExecContext(ctx, dargs)
143+
return siCtx.ExecContext(ctx, nvdargs)
144+
}
145+
dargs, err := namedValueToValue(nvdargs)
146+
if err != nil {
147+
return nil, err
136148
}
137149
if ctx.Done() == context.Background().Done() {
138150
return si.Exec(dargs)
@@ -170,9 +182,13 @@ func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, dargs []driver.Value
170182
}
171183
}
172184

173-
func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, dargs []driver.Value) (driver.Rows, error) {
185+
func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Rows, error) {
174186
if siCtx, is := si.(driver.StmtQueryContext); is {
175-
return siCtx.QueryContext(ctx, dargs)
187+
return siCtx.QueryContext(ctx, nvdargs)
188+
}
189+
dargs, err := namedValueToValue(nvdargs)
190+
if err != nil {
191+
return nil, err
176192
}
177193
if ctx.Done() == context.Background().Done() {
178194
return si.Query(dargs)
@@ -253,3 +269,14 @@ func ctxDriverBegin(ctx context.Context, ci driver.Conn) (driver.Tx, error) {
253269
return r.txi, r.err
254270
}
255271
}
272+
273+
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
274+
dargs := make([]driver.Value, len(named))
275+
for n, param := range named {
276+
if len(param.Name) > 0 {
277+
return nil, errors.New("sql: driver does not support the use of Named Parameters")
278+
}
279+
dargs[n] = param.Value
280+
}
281+
return dargs, nil
282+
}

src/database/sql/driver/driver.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ import (
2424
// time.Time
2525
type Value interface{}
2626

27+
// NamedValue holds both the value name and value.
28+
// The Ordinal is the position of the parameter starting from one and is always set.
29+
// If the Name is not empty it should be used for the parameter identifier and
30+
// not the ordinal position.
31+
type NamedValue struct {
32+
Name string
33+
Ordinal int
34+
Value Value
35+
}
36+
2737
// Driver is the interface that must be implemented by a database
2838
// driver.
2939
type Driver interface {
@@ -71,7 +81,7 @@ type Execer interface {
7181
// ExecerContext is like execer, but must honor the context timeout and return
7282
// when the context is cancelled.
7383
type ExecerContext interface {
74-
ExecContext(ctx context.Context, query string, args []Value) (Result, error)
84+
ExecContext(ctx context.Context, query string, args []NamedValue) (Result, error)
7585
}
7686

7787
// Queryer is an optional interface that may be implemented by a Conn.
@@ -88,7 +98,7 @@ type Queryer interface {
8898
// QueryerContext is like Queryer, but most honor the context timeout and return
8999
// when the context is cancelled.
90100
type QueryerContext interface {
91-
QueryContext(ctx context.Context, query string, args []Value) (Rows, error)
101+
QueryContext(ctx context.Context, query string, args []NamedValue) (Rows, error)
92102
}
93103

94104
// Conn is a connection to a database. It is not used concurrently
@@ -174,13 +184,13 @@ type Stmt interface {
174184
// StmtExecContext enhances the Stmt interface by providing Exec with context.
175185
type StmtExecContext interface {
176186
// ExecContext must honor the context timeout and return when it is cancelled.
177-
ExecContext(ctx context.Context, args []Value) (Result, error)
187+
ExecContext(ctx context.Context, args []NamedValue) (Result, error)
178188
}
179189

180190
// StmtQueryContext enhances the Stmt interface by providing Query with context.
181191
type StmtQueryContext interface {
182192
// QueryContext must honor the context timeout and return when it is cancelled.
183-
QueryContext(ctx context.Context, args []Value) (Rows, error)
193+
QueryContext(ctx context.Context, args []NamedValue) (Rows, error)
184194
}
185195

186196
// ColumnConverter may be optionally implemented by Stmt if the

0 commit comments

Comments
 (0)