Skip to content

Commit 4ff6b46

Browse files
authored
all: switch internal API's to use driver.NamedValue instead of driver.Value (#1068)
database/sql defaults to using the QueryContext and ExecContext API's. Previously, we would need to allocate in order to convert the driver.NamedValue parameter that each of those accepts to a driver.Value. By using driver.NamedValue consistently internally, we can save allocations and improve performance. See #1067 for a full set of benchmarks.
1 parent 111e24d commit 4ff6b46

File tree

2 files changed

+34
-39
lines changed

2 files changed

+34
-39
lines changed

conn.go

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -916,12 +916,20 @@ func (cn *conn) Close() (err error) {
916916
return cn.sendSimpleMessage('X')
917917
}
918918

919+
func toNamedValue(v []driver.Value) []driver.NamedValue {
920+
v2 := make([]driver.NamedValue, len(v))
921+
for i := range v {
922+
v2[i] = driver.NamedValue{Ordinal: i + 1, Value: v[i]}
923+
}
924+
return v2
925+
}
926+
919927
// Implement the "Queryer" interface
920928
func (cn *conn) Query(query string, args []driver.Value) (driver.Rows, error) {
921-
return cn.query(query, args)
929+
return cn.query(query, toNamedValue(args))
922930
}
923931

924-
func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
932+
func (cn *conn) query(query string, args []driver.NamedValue) (_ *rows, err error) {
925933
if err := cn.err.get(); err != nil {
926934
return nil, err
927935
}
@@ -970,7 +978,7 @@ func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err
970978
}
971979

972980
if cn.binaryParameters {
973-
cn.sendBinaryModeQuery(query, args)
981+
cn.sendBinaryModeQuery(query, toNamedValue(args))
974982

975983
cn.readParseResponse()
976984
cn.readBindResponse()
@@ -1428,10 +1436,10 @@ func (st *stmt) Close() (err error) {
14281436
}
14291437

14301438
func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
1431-
return st.query(v)
1439+
return st.query(toNamedValue(v))
14321440
}
14331441

1434-
func (st *stmt) query(v []driver.Value) (r *rows, err error) {
1442+
func (st *stmt) query(v []driver.NamedValue) (r *rows, err error) {
14351443
if err := st.cn.err.get(); err != nil {
14361444
return nil, err
14371445
}
@@ -1444,18 +1452,11 @@ func (st *stmt) query(v []driver.Value) (r *rows, err error) {
14441452
}, nil
14451453
}
14461454

1447-
func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) {
1448-
if err := st.cn.err.get(); err != nil {
1449-
return nil, err
1450-
}
1451-
defer st.cn.errRecover(&err)
1452-
1453-
st.exec(v)
1454-
res, _, err = st.cn.readExecuteResponse("simple query")
1455-
return res, err
1455+
func (st *stmt) Exec(v []driver.Value) (driver.Result, error) {
1456+
return st.ExecContext(context.Background(), toNamedValue(v))
14561457
}
14571458

1458-
func (st *stmt) exec(v []driver.Value) {
1459+
func (st *stmt) exec(v []driver.NamedValue) {
14591460
if len(v) >= 65536 {
14601461
errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(v))
14611462
}
@@ -1474,10 +1475,10 @@ func (st *stmt) exec(v []driver.Value) {
14741475
w.int16(0)
14751476
w.int16(len(v))
14761477
for i, x := range v {
1477-
if x == nil {
1478+
if x.Value == nil {
14781479
w.int32(-1)
14791480
} else {
1480-
b := encode(&cn.parameterStatus, x, st.paramTyps[i])
1481+
b := encode(&cn.parameterStatus, x.Value, st.paramTyps[i])
14811482
w.int32(len(b))
14821483
w.bytes(b)
14831484
}
@@ -1744,13 +1745,13 @@ func md5s(s string) string {
17441745
return fmt.Sprintf("%x", h.Sum(nil))
17451746
}
17461747

1747-
func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.Value) {
1748+
func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.NamedValue) {
17481749
// Do one pass over the parameters to see if we're going to send any of
17491750
// them over in binary. If we are, create a paramFormats array at the
17501751
// same time.
17511752
var paramFormats []int
17521753
for i, x := range args {
1753-
_, ok := x.([]byte)
1754+
_, ok := x.Value.([]byte)
17541755
if ok {
17551756
if paramFormats == nil {
17561757
paramFormats = make([]int, len(args))
@@ -1769,17 +1770,17 @@ func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.Value) {
17691770

17701771
b.int16(len(args))
17711772
for _, x := range args {
1772-
if x == nil {
1773+
if x.Value == nil {
17731774
b.int32(-1)
17741775
} else {
1775-
datum := binaryEncode(&cn.parameterStatus, x)
1776+
datum := binaryEncode(&cn.parameterStatus, x.Value)
17761777
b.int32(len(datum))
17771778
b.bytes(datum)
17781779
}
17791780
}
17801781
}
17811782

1782-
func (cn *conn) sendBinaryModeQuery(query string, args []driver.Value) {
1783+
func (cn *conn) sendBinaryModeQuery(query string, args []driver.NamedValue) {
17831784
if len(args) >= 65536 {
17841785
errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(args))
17851786
}

conn_go18.go

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,8 @@ const (
1515

1616
// Implement the "QueryerContext" interface
1717
func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
18-
list := make([]driver.Value, len(args))
19-
for i, nv := range args {
20-
list[i] = nv.Value
21-
}
2218
finish := cn.watchCancel(ctx)
23-
r, err := cn.query(query, list)
19+
r, err := cn.query(query, args)
2420
if err != nil {
2521
if finish != nil {
2622
finish()
@@ -182,12 +178,8 @@ func (cn *conn) cancel(ctx context.Context) error {
182178

183179
// Implement the "StmtQueryContext" interface
184180
func (st *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
185-
list := make([]driver.Value, len(args))
186-
for i, nv := range args {
187-
list[i] = nv.Value
188-
}
189181
finish := st.watchCancel(ctx)
190-
r, err := st.query(list)
182+
r, err := st.query(args)
191183
if err != nil {
192184
if finish != nil {
193185
finish()
@@ -199,17 +191,19 @@ func (st *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (dri
199191
}
200192

201193
// Implement the "StmtExecContext" interface
202-
func (st *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
203-
list := make([]driver.Value, len(args))
204-
for i, nv := range args {
205-
list[i] = nv.Value
206-
}
207-
194+
func (st *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (res driver.Result, err error) {
208195
if finish := st.watchCancel(ctx); finish != nil {
209196
defer finish()
210197
}
211198

212-
return st.Exec(list)
199+
if err := st.cn.err.get(); err != nil {
200+
return nil, err
201+
}
202+
defer st.cn.errRecover(&err)
203+
204+
st.exec(args)
205+
res, _, err = st.cn.readExecuteResponse("simple query")
206+
return res, err
213207
}
214208

215209
// watchCancel is implemented on stmt in order to not mark the parent conn as bad

0 commit comments

Comments
 (0)