mirror of
https://github.com/jackc/pgx.git
synced 2025-05-31 11:42:24 +00:00
Add JSON/JSONB support
This commit is contained in:
parent
fee3679cb9
commit
0013733535
4
conn.go
4
conn.go
@ -799,6 +799,10 @@ func (c *Conn) sendPreparedQuery(ps *PreparedStatement, arguments ...interface{}
|
|||||||
err = encodeTimestampArray(wbuf, arguments[i], TimestampTzOid)
|
err = encodeTimestampArray(wbuf, arguments[i], TimestampTzOid)
|
||||||
case OidOid:
|
case OidOid:
|
||||||
err = encodeOid(wbuf, arguments[i])
|
err = encodeOid(wbuf, arguments[i])
|
||||||
|
case JsonOid:
|
||||||
|
err = encodeJson(wbuf, arguments[i])
|
||||||
|
case JsonbOid:
|
||||||
|
err = encodeJson(wbuf, arguments[i])
|
||||||
default:
|
default:
|
||||||
return SerializationError(fmt.Sprintf("Cannot encode %T into oid %v - %T must implement Encoder or be converted to a string", arg, oid, arg))
|
return SerializationError(fmt.Sprintf("Cannot encode %T into oid %v - %T must implement Encoder or be converted to a string", arg, oid, arg))
|
||||||
}
|
}
|
||||||
|
17
query.go
17
query.go
@ -284,7 +284,14 @@ func (rows *Rows) Scan(dest ...interface{}) (err error) {
|
|||||||
rows.Fatal(err)
|
rows.Fatal(err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
rows.Fatal(fmt.Errorf("Scan cannot decode into %T", d))
|
switch vr.Type().DataType {
|
||||||
|
case JsonOid:
|
||||||
|
decodeJson(vr, &d)
|
||||||
|
case JsonbOid:
|
||||||
|
decodeJson(vr, &d)
|
||||||
|
default:
|
||||||
|
rows.Fatal(fmt.Errorf("Scan cannot decode into %T", d))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if vr.Err() != nil {
|
if vr.Err() != nil {
|
||||||
@ -360,6 +367,14 @@ func (rows *Rows) Values() ([]interface{}, error) {
|
|||||||
values = append(values, decodeTimestamp(vr))
|
values = append(values, decodeTimestamp(vr))
|
||||||
case InetOid, CidrOid:
|
case InetOid, CidrOid:
|
||||||
values = append(values, decodeInet(vr))
|
values = append(values, decodeInet(vr))
|
||||||
|
case JsonOid:
|
||||||
|
var d interface{}
|
||||||
|
decodeJson(vr, &d)
|
||||||
|
values = append(values, d)
|
||||||
|
case JsonbOid:
|
||||||
|
var d interface{}
|
||||||
|
decodeJson(vr, &d)
|
||||||
|
values = append(values, d)
|
||||||
default:
|
default:
|
||||||
rows.Fatal(errors.New("Values cannot handle binary format non-intrinsic types"))
|
rows.Fatal(errors.New("Values cannot handle binary format non-intrinsic types"))
|
||||||
}
|
}
|
||||||
|
25
values.go
25
values.go
@ -2,6 +2,7 @@ package pgx
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
@ -19,6 +20,7 @@ const (
|
|||||||
Int4Oid = 23
|
Int4Oid = 23
|
||||||
TextOid = 25
|
TextOid = 25
|
||||||
OidOid = 26
|
OidOid = 26
|
||||||
|
JsonOid = 114
|
||||||
CidrOid = 650
|
CidrOid = 650
|
||||||
Float4Oid = 700
|
Float4Oid = 700
|
||||||
Float8Oid = 701
|
Float8Oid = 701
|
||||||
@ -37,6 +39,7 @@ const (
|
|||||||
TimestampArrayOid = 1115
|
TimestampArrayOid = 1115
|
||||||
TimestampTzOid = 1184
|
TimestampTzOid = 1184
|
||||||
TimestampTzArrayOid = 1185
|
TimestampTzArrayOid = 1185
|
||||||
|
JsonbOid = 3802
|
||||||
)
|
)
|
||||||
|
|
||||||
// PostgreSQL format codes
|
// PostgreSQL format codes
|
||||||
@ -995,6 +998,28 @@ func encodeBytea(w *WriteBuf, value interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeJson(vr *ValueReader, d interface{}) error {
|
||||||
|
if vr.Len() == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if vr.Type().DataType != JsonOid && vr.Type().DataType != JsonbOid {
|
||||||
|
vr.Fatal(ProtocolError(fmt.Sprintf("Cannot decode oid %v into json", vr.Type().DataType)))
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := vr.ReadBytes(vr.Len())
|
||||||
|
return json.Unmarshal(bytes, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeJson(w *WriteBuf, value interface{}) error {
|
||||||
|
s, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Errorf("Failed to encode json from type: %T", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeText(w, s)
|
||||||
|
}
|
||||||
|
|
||||||
func decodeDate(vr *ValueReader) time.Time {
|
func decodeDate(vr *ValueReader) time.Time {
|
||||||
var zeroTime time.Time
|
var zeroTime time.Time
|
||||||
|
|
||||||
|
@ -65,6 +65,46 @@ func TestTimestampTzTranscode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJsonTranscode(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
conn := mustConnect(t, *defaultConnConfig)
|
||||||
|
defer closeConn(t, conn)
|
||||||
|
|
||||||
|
m := map[string]string{
|
||||||
|
"key": "value",
|
||||||
|
}
|
||||||
|
var outputJson map[string]string
|
||||||
|
|
||||||
|
err := conn.QueryRow("select $1::json", m).Scan(&outputJson)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("QueryRow Scan failed: %v", err)
|
||||||
|
}
|
||||||
|
if m["key"] != outputJson["key"] {
|
||||||
|
t.Errorf("Did not transcode json successfully: %v is not %v", outputJson["key"], m["key"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonbTranscode(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
conn := mustConnect(t, *defaultConnConfig)
|
||||||
|
defer closeConn(t, conn)
|
||||||
|
|
||||||
|
m := map[string]string{
|
||||||
|
"key": "value",
|
||||||
|
}
|
||||||
|
var outputJson map[string]string
|
||||||
|
|
||||||
|
err := conn.QueryRow("select $1::jsonb", m).Scan(&outputJson)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("QueryRow Scan failed: %v", err)
|
||||||
|
}
|
||||||
|
if m["key"] != outputJson["key"] {
|
||||||
|
t.Errorf("Did not transcode jsonb successfully: %v is not %v", outputJson["key"], m["key"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func mustParseCIDR(t *testing.T, s string) net.IPNet {
|
func mustParseCIDR(t *testing.T, s string) net.IPNet {
|
||||||
_, ipnet, err := net.ParseCIDR(s)
|
_, ipnet, err := net.ParseCIDR(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user