diff --git a/CHANGELOG.md b/CHANGELOG.md index a4ff956d6..bc6cd9068 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ * Fix StartReplication() syntax (steampunkcoder) * Fix precision loss for test format geometric types +## Changes + +* pgtype.JSON(B).Value now returns []byte instead of string. This allows + database/sql to scan json(b) into \*json.RawMessage. This is a tiny behavior + change, but database/sql Scan should automatically convert []byte to string, so + there shouldn't be any incompatibility. + # 3.1.0 (January 15, 2018) ## Features diff --git a/pgtype/json.go b/pgtype/json.go index ef8231b18..b05aba6bd 100644 --- a/pgtype/json.go +++ b/pgtype/json.go @@ -152,7 +152,7 @@ func (dst *JSON) Scan(src interface{}) error { func (src *JSON) Value() (driver.Value, error) { switch src.Status { case Present: - return string(src.Bytes), nil + return src.Bytes, nil case Null: return nil, nil default: diff --git a/stdlib/sql.go b/stdlib/sql.go index 2d4930ee9..7121d74d5 100644 --- a/stdlib/sql.go +++ b/stdlib/sql.go @@ -495,6 +495,10 @@ func (r *Rows) Next(dest []driver.Value) error { r.values[i] = &pgtype.Int4{} case pgtype.Int8OID: r.values[i] = &pgtype.Int8{} + case pgtype.JSONOID: + r.values[i] = &pgtype.JSON{} + case pgtype.JSONBOID: + r.values[i] = &pgtype.JSONB{} case pgtype.OIDOID: r.values[i] = &pgtype.OIDValue{} case pgtype.TimestampOID: diff --git a/stdlib/sql_test.go b/stdlib/sql_test.go index a4a999718..d89eae0ba 100644 --- a/stdlib/sql_test.go +++ b/stdlib/sql_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "database/sql" + "encoding/json" "fmt" "math" "reflect" @@ -1466,3 +1467,22 @@ func TestSimpleQueryLifeCycle(t *testing.T) { ensureConnValid(t, db) } + +// https://github.com/jackc/pgx/issues/409 +func TestScanJSONIntoJSONRawMessage(t *testing.T) { + db := openDB(t) + defer closeDB(t, db) + + var msg json.RawMessage + + err := db.QueryRow("select '{}'::jsonb").Scan(&msg) + if err != nil { + t.Fatalf("QueryRow / Scan failed: %v", err) + } + + if bytes.Compare([]byte("{}"), []byte(msg)) != 0 { + t.Fatalf("Expected %v, got %v", []byte("{}"), msg) + } + + ensureConnValid(t, db) +} diff --git a/values.go b/values.go index 6a1c4f084..696d57649 100644 --- a/values.go +++ b/values.go @@ -31,6 +31,32 @@ func convertSimpleArgument(ci *pgtype.ConnInfo, arg interface{}) (interface{}, e } switch arg := arg.(type) { + + // https://github.com/jackc/pgx/issues/409 Changed JSON and JSONB to surface + // []byte to database/sql instead of string. But that caused problems with the + // simple protocol because the driver.Valuer case got taken before the + // pgtype.TextEncoder case. And driver.Valuer needed to be first in the usual + // case because of https://github.com/jackc/pgx/issues/339. So instead we + // special case JSON and JSONB. + case *pgtype.JSON: + buf, err := arg.EncodeText(ci, nil) + if err != nil { + return nil, err + } + if buf == nil { + return nil, nil + } + return string(buf), nil + case *pgtype.JSONB: + buf, err := arg.EncodeText(ci, nil) + if err != nil { + return nil, err + } + if buf == nil { + return nil, nil + } + return string(buf), nil + case driver.Valuer: return callValuerValue(arg) case pgtype.TextEncoder: