Skip to content

Commit fb12405

Browse files
epelcarp242
andauthored
Treat nil []byte in query parameters as nil/NULL rather than ""
Co-authored-by: Martin Tournoij <[email protected]>
1 parent 4ff6b46 commit fb12405

File tree

4 files changed

+77
-28
lines changed

4 files changed

+77
-28
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ newer. Previously PostgreSQL 8.4 and newer were supported.
5353

5454
- Handle ErrorResponse in readReadyForQuery and return proper error ([#1136]).
5555

56+
- Treat nil []byte in query parameters as nil/NULL rather than `""` ([#838]).
57+
5658
[#595]: https://github.com/lib/pq/pull/595
5759
[#745]: https://github.com/lib/pq/pull/745
60+
[#838]: https://github.com/lib/pq/pull/838
5861
[#949]: https://github.com/lib/pq/pull/949
5962
[#1125]: https://github.com/lib/pq/pull/1125
6063
[#1129]: https://github.com/lib/pq/pull/1129

conn.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,8 +1479,12 @@ func (st *stmt) exec(v []driver.NamedValue) {
14791479
w.int32(-1)
14801480
} else {
14811481
b := encode(&cn.parameterStatus, x.Value, st.paramTyps[i])
1482-
w.int32(len(b))
1483-
w.bytes(b)
1482+
if b == nil {
1483+
w.int32(-1)
1484+
} else {
1485+
w.int32(len(b))
1486+
w.bytes(b)
1487+
}
14841488
}
14851489
}
14861490
}
@@ -1746,9 +1750,8 @@ func md5s(s string) string {
17461750
}
17471751

17481752
func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.NamedValue) {
1749-
// Do one pass over the parameters to see if we're going to send any of
1750-
// them over in binary. If we are, create a paramFormats array at the
1751-
// same time.
1753+
// Do one pass over the parameters to see if we're going to send any of them
1754+
// over in binary. If we are, create a paramFormats array at the same time.
17521755
var paramFormats []int
17531756
for i, x := range args {
17541757
_, ok := x.Value.([]byte)
@@ -1772,6 +1775,8 @@ func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.NamedValue) {
17721775
for _, x := range args {
17731776
if x.Value == nil {
17741777
b.int32(-1)
1778+
} else if xx, ok := x.Value.([]byte); ok && xx == nil {
1779+
b.int32(-1)
17751780
} else {
17761781
datum := binaryEncode(&cn.parameterStatus, x.Value)
17771782
b.int32(len(datum))

conn_test.go

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,54 @@ localhost:*:*:*:pass_C
192192
assertPassword(values{"host": "server", "passfile": pgpassFile, "dbname": "some_db", "user": "some_user"}, "pass_A")
193193
}
194194

195+
func TestExecNilSlice(t *testing.T) {
196+
db := pqtest.MustDB(t)
197+
defer db.Close()
198+
199+
_, err := db.Exec("create temp table x (b1 text, b2 text, b3 text)")
200+
if err != nil {
201+
t.Fatal(err)
202+
}
203+
var (
204+
b1 []byte
205+
b2 []string
206+
b3 = []byte{}
207+
)
208+
_, err = db.Exec("insert into x (b1, b2, b3) values ($1, $2, $3)", b1, b2, b3)
209+
if err != nil {
210+
t.Fatal(err)
211+
}
212+
213+
rows, err := db.Query(`select * from x`)
214+
if err != nil {
215+
t.Fatal(err)
216+
}
217+
defer rows.Close()
218+
for rows.Next() {
219+
if rows.Err() != nil {
220+
t.Fatal(rows.Err())
221+
}
222+
var b1, b2, b3 *string
223+
err = rows.Scan(&b1, &b2, &b3)
224+
if err != nil {
225+
t.Fatal(err)
226+
}
227+
228+
deref := func(s *string) string {
229+
if s == nil {
230+
return "<nil>"
231+
}
232+
return fmt.Sprintf("%q", *s)
233+
}
234+
235+
want := `b1=<nil>; b2=<nil>; b3=""`
236+
have := fmt.Sprintf("b1=%s; b2=%s; b3=%s", deref(b1), deref(b2), deref(b3))
237+
if want != have {
238+
t.Errorf("\nwant: %s\nhave: %s", want, have)
239+
}
240+
}
241+
}
242+
195243
func TestExec(t *testing.T) {
196244
db := pqtest.MustDB(t)
197245
defer db.Close()
@@ -205,7 +253,6 @@ func TestExec(t *testing.T) {
205253
if err != nil {
206254
t.Fatal(err)
207255
}
208-
209256
if n, _ := r.RowsAffected(); n != 1 {
210257
t.Fatalf("expected 1 row affected, not %d", n)
211258
}
@@ -214,29 +261,24 @@ func TestExec(t *testing.T) {
214261
if err != nil {
215262
t.Fatal(err)
216263
}
217-
218264
if n, _ := r.RowsAffected(); n != 3 {
219265
t.Fatalf("expected 3 rows affected, not %d", n)
220266
}
221267

222-
// SELECT doesn't send the number of returned rows in the command tag
223-
// before 9.0
224-
if getServerVersion(t, db) >= 90000 {
225-
r, err = db.Exec("SELECT g FROM generate_series(1, 2) g")
226-
if err != nil {
227-
t.Fatal(err)
228-
}
229-
if n, _ := r.RowsAffected(); n != 2 {
230-
t.Fatalf("expected 2 rows affected, not %d", n)
231-
}
268+
r, err = db.Exec("SELECT g FROM generate_series(1, 2) g")
269+
if err != nil {
270+
t.Fatal(err)
271+
}
272+
if n, _ := r.RowsAffected(); n != 2 {
273+
t.Fatalf("expected 2 rows affected, not %d", n)
274+
}
232275

233-
r, err = db.Exec("SELECT g FROM generate_series(1, $1) g", 3)
234-
if err != nil {
235-
t.Fatal(err)
236-
}
237-
if n, _ := r.RowsAffected(); n != 3 {
238-
t.Fatalf("expected 3 rows affected, not %d", n)
239-
}
276+
r, err = db.Exec("SELECT g FROM generate_series(1, $1) g", 3)
277+
if err != nil {
278+
t.Fatal(err)
279+
}
280+
if n, _ := r.RowsAffected(); n != 3 {
281+
t.Fatalf("expected 3 rows affected, not %d", n)
240282
}
241283
}
242284

encode.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,25 @@ func encode(parameterStatus *parameterStatus, x any, pgtypOid oid.Oid) []byte {
3535
case float64:
3636
return strconv.AppendFloat(nil, v, 'f', -1, 64)
3737
case []byte:
38+
if v == nil {
39+
return nil
40+
}
3841
if pgtypOid == oid.T_bytea {
3942
return encodeBytea(parameterStatus.serverVersion, v)
4043
}
41-
4244
return v
4345
case string:
4446
if pgtypOid == oid.T_bytea {
4547
return encodeBytea(parameterStatus.serverVersion, []byte(v))
4648
}
47-
4849
return []byte(v)
4950
case bool:
5051
return strconv.AppendBool(nil, v)
5152
case time.Time:
5253
return formatTs(v)
53-
5454
default:
5555
errorf("encode: unknown type for %T", v)
5656
}
57-
5857
panic("not reached")
5958
}
6059

0 commit comments

Comments
 (0)