Move hstore to pgtype
Also implement binary format
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/pgio"
|
||||
@@ -577,140 +576,6 @@ func (n NullTime) Encode(w *WriteBuf, oid Oid) error {
|
||||
return encodeTime(w, oid, n.Time)
|
||||
}
|
||||
|
||||
// Hstore represents an hstore column. It does not support a null column or null
|
||||
// key values (use NullHstore for this). Hstore implements the Scanner and
|
||||
// Encoder interfaces so it may be used both as an argument to Query[Row] and a
|
||||
// destination for Scan.
|
||||
type Hstore map[string]string
|
||||
|
||||
func (h *Hstore) Scan(vr *ValueReader) error {
|
||||
//oid for hstore not standardized, so we check its type name
|
||||
if vr.Type().DataTypeName != "hstore" {
|
||||
vr.Fatal(ProtocolError(fmt.Sprintf("Cannot decode type %s into Hstore", vr.Type().DataTypeName)))
|
||||
return nil
|
||||
}
|
||||
|
||||
if vr.Len() == -1 {
|
||||
vr.Fatal(ProtocolError("Cannot decode null column into Hstore"))
|
||||
return nil
|
||||
}
|
||||
|
||||
switch vr.Type().FormatCode {
|
||||
case TextFormatCode:
|
||||
m, err := parseHstoreToMap(vr.ReadString(vr.Len()))
|
||||
if err != nil {
|
||||
vr.Fatal(ProtocolError(fmt.Sprintf("Can't decode hstore column: %v", err)))
|
||||
return nil
|
||||
}
|
||||
hm := Hstore(m)
|
||||
*h = hm
|
||||
return nil
|
||||
case BinaryFormatCode:
|
||||
vr.Fatal(ProtocolError("Can't decode binary hstore"))
|
||||
return nil
|
||||
default:
|
||||
vr.Fatal(ProtocolError(fmt.Sprintf("Unknown field description format code: %v", vr.Type().FormatCode)))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h Hstore) FormatCode() int16 { return TextFormatCode }
|
||||
|
||||
func (h Hstore) Encode(w *WriteBuf, oid Oid) error {
|
||||
var buf bytes.Buffer
|
||||
|
||||
i := 0
|
||||
for k, v := range h {
|
||||
i++
|
||||
ks := strings.Replace(k, `\`, `\\`, -1)
|
||||
ks = strings.Replace(ks, `"`, `\"`, -1)
|
||||
vs := strings.Replace(v, `\`, `\\`, -1)
|
||||
vs = strings.Replace(vs, `"`, `\"`, -1)
|
||||
buf.WriteString(fmt.Sprintf(`"%s"=>"%s"`, ks, vs))
|
||||
if i < len(h) {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
w.WriteInt32(int32(buf.Len()))
|
||||
w.WriteBytes(buf.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
// NullHstore represents an hstore column that can be null or have null values
|
||||
// associated with its keys. NullHstore implements the Scanner and Encoder
|
||||
// interfaces so it may be used both as an argument to Query[Row] and a
|
||||
// destination for Scan.
|
||||
//
|
||||
// If Valid is false, then the value of the entire hstore column is NULL
|
||||
// If any of the NullString values in Store has Valid set to false, the key
|
||||
// appears in the hstore column, but its value is explicitly set to NULL.
|
||||
type NullHstore struct {
|
||||
Hstore map[string]NullString
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func (h *NullHstore) Scan(vr *ValueReader) error {
|
||||
//oid for hstore not standardized, so we check its type name
|
||||
if vr.Type().DataTypeName != "hstore" {
|
||||
vr.Fatal(ProtocolError(fmt.Sprintf("Cannot decode type %s into NullHstore", vr.Type().DataTypeName)))
|
||||
return nil
|
||||
}
|
||||
|
||||
if vr.Len() == -1 {
|
||||
h.Valid = false
|
||||
return nil
|
||||
}
|
||||
|
||||
switch vr.Type().FormatCode {
|
||||
case TextFormatCode:
|
||||
store, err := parseHstoreToNullHstore(vr.ReadString(vr.Len()))
|
||||
if err != nil {
|
||||
vr.Fatal(ProtocolError(fmt.Sprintf("Can't decode hstore column: %v", err)))
|
||||
return nil
|
||||
}
|
||||
h.Valid = true
|
||||
h.Hstore = store
|
||||
return nil
|
||||
case BinaryFormatCode:
|
||||
vr.Fatal(ProtocolError("Can't decode binary hstore"))
|
||||
return nil
|
||||
default:
|
||||
vr.Fatal(ProtocolError(fmt.Sprintf("Unknown field description format code: %v", vr.Type().FormatCode)))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h NullHstore) FormatCode() int16 { return TextFormatCode }
|
||||
|
||||
func (h NullHstore) Encode(w *WriteBuf, oid Oid) error {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if !h.Valid {
|
||||
w.WriteInt32(-1)
|
||||
return nil
|
||||
}
|
||||
|
||||
i := 0
|
||||
for k, v := range h.Hstore {
|
||||
i++
|
||||
ks := strings.Replace(k, `\`, `\\`, -1)
|
||||
ks = strings.Replace(ks, `"`, `\"`, -1)
|
||||
if v.Valid {
|
||||
vs := strings.Replace(v.String, `\`, `\\`, -1)
|
||||
vs = strings.Replace(vs, `"`, `\"`, -1)
|
||||
buf.WriteString(fmt.Sprintf(`"%s"=>"%s"`, ks, vs))
|
||||
} else {
|
||||
buf.WriteString(fmt.Sprintf(`"%s"=>NULL`, ks))
|
||||
}
|
||||
if i < len(h.Hstore) {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
w.WriteInt32(int32(buf.Len()))
|
||||
w.WriteBytes(buf.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes arg into wbuf as the type oid. This allows implementations
|
||||
// of the Encoder interface to delegate the actual work of encoding to the
|
||||
// built-in functionality.
|
||||
|
||||
Reference in New Issue
Block a user