2
0

Add pgtype.Varbit

This commit is contained in:
Jack Christensen
2017-04-05 07:54:41 -05:00
parent 52b58b88a6
commit 54d9cbc743
4 changed files with 167 additions and 7 deletions
+1
View File
@@ -263,6 +263,7 @@ func init() {
"tstzrange": &Tstzrange{}, "tstzrange": &Tstzrange{},
"unknown": &Unknown{}, "unknown": &Unknown{},
"uuid": &Uuid{}, "uuid": &Uuid{},
"varbit": &Varbit{},
"varchar": &Varchar{}, "varchar": &Varchar{},
"xid": &Xid{}, "xid": &Xid{},
} }
+141
View File
@@ -0,0 +1,141 @@
package pgtype
import (
"database/sql/driver"
"encoding/binary"
"fmt"
"io"
"github.com/jackc/pgx/pgio"
)
type Varbit struct {
Bytes []byte
Len int32 // Number of bits
Status Status
}
func (dst *Varbit) Set(src interface{}) error {
return fmt.Errorf("cannot convert %v to Varbit", src)
}
func (dst *Varbit) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *Varbit) AssignTo(dst interface{}) error {
return fmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst *Varbit) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Varbit{Status: Null}
return nil
}
bitLen := len(src)
byteLen := bitLen / 8
if bitLen%8 > 0 {
byteLen++
}
buf := make([]byte, byteLen)
for i, b := range src {
if b == '1' {
byteIdx := i / 8
bitIdx := uint(i % 8)
buf[byteIdx] = buf[byteIdx] | (128 >> bitIdx)
}
}
*dst = Varbit{Bytes: buf, Len: int32(bitLen), Status: Present}
return nil
}
func (dst *Varbit) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Varbit{Status: Null}
return nil
}
if len(src) < 4 {
return fmt.Errorf("invalid length for varbit: %v", len(src))
}
bitLen := int32(binary.BigEndian.Uint32(src))
rp := 4
buf := make([]byte, len(src[rp:]))
copy(buf, src[rp:])
*dst = Varbit{Bytes: buf, Len: bitLen, Status: Present}
return nil
}
func (src *Varbit) EncodeText(ci *ConnInfo, w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
buf := make([]byte, int(src.Len))
for i, _ := range buf {
byteIdx := i / 8
bitMask := byte(128 >> byte(i%8))
char := byte('0')
if src.Bytes[byteIdx]&bitMask > 0 {
char = '1'
}
buf[i] = char
}
_, err := w.Write(buf)
return false, err
}
func (src *Varbit) EncodeBinary(ci *ConnInfo, w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if _, err := pgio.WriteInt32(w, src.Len); err != nil {
return false, err
}
_, err := w.Write(src.Bytes)
return false, err
}
// Scan implements the database/sql Scanner interface.
func (dst *Varbit) Scan(src interface{}) error {
if src == nil {
*dst = Varbit{Status: Null}
return nil
}
switch src := src.(type) {
case string:
return dst.DecodeText(nil, []byte(src))
case []byte:
return dst.DecodeText(nil, src)
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src *Varbit) Value() (driver.Value, error) {
return encodeValueText(src)
}
+25
View File
@@ -0,0 +1,25 @@
package pgtype_test
import (
"testing"
"github.com/jackc/pgx/pgtype"
)
func TestVarbitTranscode(t *testing.T) {
testSuccessfulTranscode(t, "varbit", []interface{}{
&pgtype.Varbit{Bytes: []byte{}, Len: 0, Status: pgtype.Present},
&pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Status: pgtype.Present},
&pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 128}, Len: 33, Status: pgtype.Present},
&pgtype.Varbit{Status: pgtype.Null},
})
}
func TestVarbitNormalize(t *testing.T) {
testSuccessfulNormalize(t, []normalizeTest{
{
sql: "select B'111111111'",
value: &pgtype.Varbit{Bytes: []byte{255, 128}, Len: 9, Status: pgtype.Present},
},
})
}
-7
View File
@@ -42,14 +42,10 @@ Or maybe double-down on conn/pool coupling and improve connpool
Add auto-idle pinging to conns in pool Add auto-idle pinging to conns in pool
Extract types Null* and Hstore to separate package
Remove names from prepared statements - use database/sql style objects Remove names from prepared statements - use database/sql style objects
Better way of handling text/binary protocol choice than pgx.DefaultTypeFormats or manually editing a PreparedStatement. Possibly an optional part of preparing a statement is specifying the format and/or a decoder. Or maybe it is part of a QueryEx call... Could be very interesting to make encoding and decoding possible without being a method of the type. This could drastically clean up those huge type switches. Better way of handling text/binary protocol choice than pgx.DefaultTypeFormats or manually editing a PreparedStatement. Possibly an optional part of preparing a statement is specifying the format and/or a decoder. Or maybe it is part of a QueryEx call... Could be very interesting to make encoding and decoding possible without being a method of the type. This could drastically clean up those huge type switches.
Also maybe support binary and text for everything possible
dValueReader / msgReader cleanup dValueReader / msgReader cleanup
Make easier / possible to mock Conn or ConnPool (https://github.com/jackc/pgx/pull/162) Make easier / possible to mock Conn or ConnPool (https://github.com/jackc/pgx/pull/162)
@@ -66,6 +62,3 @@ Keep ability to change logging while running
consider test to ensure that AssignTo makes copy of reference types consider test to ensure that AssignTo makes copy of reference types
something like: something like:
select array[1,2,3], array[4,5,6,7] select array[1,2,3], array[4,5,6,7]
pgtype TODO:
varbit