mirror of https://github.com/jackc/pgx.git
Add multirange support for num, int4 and int8 type
parent
202542ead5
commit
a365c9a3c2
|
@ -0,0 +1,239 @@
|
|||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Int4multirange struct {
|
||||
Ranges []Int4range
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int4multirange) Set(src interface{}) error {
|
||||
//untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case Int4multirange:
|
||||
*dst = value
|
||||
case *Int4multirange:
|
||||
*dst = *value
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
case []Int4range:
|
||||
if value == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int4multirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Int4range, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Int4multirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
case []*Int4range:
|
||||
if value == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int4multirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Int4range, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Int4multirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cannot convert %v to Int4multirange", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (dst Int4multirange) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int4multirange) AssignTo(dst interface{}) error {
|
||||
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int4multirange) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utmr, err := ParseUntypedTextMultirange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Int4range
|
||||
|
||||
if len(utmr.Elements) > 0 {
|
||||
elements = make([]Int4range, len(utmr.Elements))
|
||||
|
||||
for i, s := range utmr.Elements {
|
||||
var elem Int4range
|
||||
|
||||
elemSrc := []byte(s)
|
||||
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int4multirange{Ranges: elements, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4multirange) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
rp := 0
|
||||
|
||||
numElems := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
if numElems == 0 {
|
||||
*dst = Int4multirange{Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elements := make([]Int4range, numElems)
|
||||
|
||||
for i := range elements {
|
||||
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
var elemSrc []byte
|
||||
if elemLen >= 0 {
|
||||
elemSrc = src[rp : rp+elemLen]
|
||||
rp += elemLen
|
||||
}
|
||||
err := elements[i].DecodeBinary(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int4multirange{Ranges: elements, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src Int4multirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, '{')
|
||||
|
||||
inElemBuf := make([]byte, 0, 32)
|
||||
for i, elem := range src.Ranges {
|
||||
if i > 0 {
|
||||
buf = append(buf, ',')
|
||||
}
|
||||
|
||||
elemBuf, err := elem.EncodeText(ci, inElemBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf == nil {
|
||||
return nil, fmt.Errorf("multi-range does not allow null range")
|
||||
} else {
|
||||
buf = append(buf, string(elemBuf)...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buf = append(buf, '}')
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src Int4multirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
|
||||
|
||||
for i := range src.Ranges {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Int4multirange) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
return dst.DecodeText(nil, nil)
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Int4multirange) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt4multirangeTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int4multirange", []interface{}{
|
||||
&pgtype.Int4multirange{
|
||||
Ranges: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int4multirange{
|
||||
Ranges: []pgtype.Int4range{
|
||||
{
|
||||
Lower: pgtype.Int4{Int: -543, Status: pgtype.Present},
|
||||
Upper: pgtype.Int4{Int: 342, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int4multirange{
|
||||
Ranges: []pgtype.Int4range{
|
||||
{
|
||||
Lower: pgtype.Int4{Int: -42, Status: pgtype.Present},
|
||||
Upper: pgtype.Int4{Int: -5, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Int4{Int: 5, Status: pgtype.Present},
|
||||
Upper: pgtype.Int4{Int: 42, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Int4{Int: 52, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Unbounded,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt4multirangeNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select int4multirange(int4range(1, 14, '(]'), int4range(20, 25, '()'))",
|
||||
Value: pgtype.Int4multirange{
|
||||
Ranges: []pgtype.Int4range{
|
||||
{
|
||||
Lower: pgtype.Int4{Int: 2, Status: pgtype.Present},
|
||||
Upper: pgtype.Int4{Int: 15, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Int4{Int: 21, Status: pgtype.Present},
|
||||
Upper: pgtype.Int4{Int: 25, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Int8multirange struct {
|
||||
Ranges []Int8range
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int8multirange) Set(src interface{}) error {
|
||||
//untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Int8multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case Int8multirange:
|
||||
*dst = value
|
||||
case *Int8multirange:
|
||||
*dst = *value
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
case []Int8range:
|
||||
if value == nil {
|
||||
*dst = Int8multirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int8multirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Int8range, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Int8multirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
case []*Int8range:
|
||||
if value == nil {
|
||||
*dst = Int8multirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int8multirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Int8range, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Int8multirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cannot convert %v to Int8multirange", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (dst Int8multirange) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int8multirange) AssignTo(dst interface{}) error {
|
||||
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int8multirange) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utmr, err := ParseUntypedTextMultirange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Int8range
|
||||
|
||||
if len(utmr.Elements) > 0 {
|
||||
elements = make([]Int8range, len(utmr.Elements))
|
||||
|
||||
for i, s := range utmr.Elements {
|
||||
var elem Int8range
|
||||
|
||||
elemSrc := []byte(s)
|
||||
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int8multirange{Ranges: elements, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int8multirange) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
rp := 0
|
||||
|
||||
numElems := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
if numElems == 0 {
|
||||
*dst = Int8multirange{Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elements := make([]Int8range, numElems)
|
||||
|
||||
for i := range elements {
|
||||
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
var elemSrc []byte
|
||||
if elemLen >= 0 {
|
||||
elemSrc = src[rp : rp+elemLen]
|
||||
rp += elemLen
|
||||
}
|
||||
err := elements[i].DecodeBinary(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int8multirange{Ranges: elements, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src Int8multirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, '{')
|
||||
|
||||
inElemBuf := make([]byte, 0, 32)
|
||||
for i, elem := range src.Ranges {
|
||||
if i > 0 {
|
||||
buf = append(buf, ',')
|
||||
}
|
||||
|
||||
elemBuf, err := elem.EncodeText(ci, inElemBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf == nil {
|
||||
return nil, fmt.Errorf("multi-range does not allow null range")
|
||||
} else {
|
||||
buf = append(buf, string(elemBuf)...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buf = append(buf, '}')
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src Int8multirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
|
||||
|
||||
for i := range src.Ranges {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Int8multirange) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
return dst.DecodeText(nil, nil)
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Int8multirange) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt8multirangeTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int8multirange", []interface{}{
|
||||
&pgtype.Int8multirange{
|
||||
Ranges: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int8multirange{
|
||||
Ranges: []pgtype.Int8range{
|
||||
{
|
||||
Lower: pgtype.Int8{Int: -543, Status: pgtype.Present},
|
||||
Upper: pgtype.Int8{Int: 342, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int8multirange{
|
||||
Ranges: []pgtype.Int8range{
|
||||
{
|
||||
Lower: pgtype.Int8{Int: -42, Status: pgtype.Present},
|
||||
Upper: pgtype.Int8{Int: -5, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Int8{Int: 5, Status: pgtype.Present},
|
||||
Upper: pgtype.Int8{Int: 42, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Int8{Int: 52, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Unbounded,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt8multirangeNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select int8multirange(int8range(1, 14, '(]'), int8range(20, 25, '()'))",
|
||||
Value: pgtype.Int8multirange{
|
||||
Ranges: []pgtype.Int8range{
|
||||
{
|
||||
Lower: pgtype.Int8{Int: 2, Status: pgtype.Present},
|
||||
Upper: pgtype.Int8{Int: 15, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Int8{Int: 21, Status: pgtype.Present},
|
||||
Upper: pgtype.Int8{Int: 25, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package pgtype
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type UntypedTextMultirange struct {
|
||||
Elements []string
|
||||
}
|
||||
|
||||
func ParseUntypedTextMultirange(src string) (*UntypedTextMultirange, error) {
|
||||
utmr := &UntypedTextMultirange{}
|
||||
utmr.Elements = make([]string, 0)
|
||||
|
||||
buf := bytes.NewBufferString(src)
|
||||
|
||||
skipWhitespace(buf)
|
||||
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r != '{' {
|
||||
return nil, fmt.Errorf("invalid multirange, expected '{': %v", err)
|
||||
}
|
||||
|
||||
parseValueLoop:
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid multirange: %v", err)
|
||||
}
|
||||
|
||||
switch r {
|
||||
case ',': // skip range separator
|
||||
case '}':
|
||||
break parseValueLoop
|
||||
default:
|
||||
buf.UnreadRune()
|
||||
value, err := parseRange(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid multirange value: %v", err)
|
||||
}
|
||||
utmr.Elements = append(utmr.Elements, value)
|
||||
}
|
||||
}
|
||||
|
||||
skipWhitespace(buf)
|
||||
|
||||
if buf.Len() > 0 {
|
||||
return nil, fmt.Errorf("unexpected trailing data: %v", buf.String())
|
||||
}
|
||||
|
||||
return utmr, nil
|
||||
|
||||
}
|
||||
|
||||
func parseRange(buf *bytes.Buffer) (string, error) {
|
||||
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
boundSepRead := false
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch r {
|
||||
case ',', '}':
|
||||
if r == ',' && !boundSepRead {
|
||||
boundSepRead = true
|
||||
break
|
||||
}
|
||||
buf.UnreadRune()
|
||||
return s.String(), nil
|
||||
}
|
||||
|
||||
s.WriteRune(r)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package pgtype
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseUntypedTextMultirange(t *testing.T) {
|
||||
tests := []struct {
|
||||
src string
|
||||
result UntypedTextMultirange
|
||||
err error
|
||||
}{
|
||||
{
|
||||
src: `{[1,2)}`,
|
||||
result: UntypedTextMultirange{Elements: []string{`[1,2)`}},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
src: `{[,),["foo", "bar"]}`,
|
||||
result: UntypedTextMultirange{Elements: []string{`[,)`, `["foo", "bar"]`}},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
src: `{}`,
|
||||
result: UntypedTextMultirange{Elements: []string{}},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
src: ` { (,) , [1,2] } `,
|
||||
result: UntypedTextMultirange{Elements: []string{` (,) `, ` [1,2] `}},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
src: `{["f""oo","b""ar")}`,
|
||||
result: UntypedTextMultirange{Elements: []string{`["f""oo","b""ar")`}},
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
r, err := ParseUntypedTextMultirange(tt.src)
|
||||
if err != tt.err {
|
||||
t.Errorf("%d. `%v`: expected err %v, got %v", i, tt.src, tt.err, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*r, tt.result) {
|
||||
t.Errorf("%d: expected %+v to be parsed to %+v, but it was %+v", i, tt.src, tt.result, *r)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Nummultirange struct {
|
||||
Ranges []Numrange
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Nummultirange) Set(src interface{}) error {
|
||||
//untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Nummultirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case Nummultirange:
|
||||
*dst = value
|
||||
case *Nummultirange:
|
||||
*dst = *value
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
case []Numrange:
|
||||
if value == nil {
|
||||
*dst = Nummultirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Nummultirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Numrange, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Nummultirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
case []*Numrange:
|
||||
if value == nil {
|
||||
*dst = Nummultirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Nummultirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Numrange, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Nummultirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cannot convert %v to Nummultirange", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (dst Nummultirange) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Nummultirange) AssignTo(dst interface{}) error {
|
||||
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Nummultirange) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Nummultirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utmr, err := ParseUntypedTextMultirange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Numrange
|
||||
|
||||
if len(utmr.Elements) > 0 {
|
||||
elements = make([]Numrange, len(utmr.Elements))
|
||||
|
||||
for i, s := range utmr.Elements {
|
||||
var elem Numrange
|
||||
|
||||
elemSrc := []byte(s)
|
||||
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Nummultirange{Ranges: elements, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Nummultirange) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Nummultirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
rp := 0
|
||||
|
||||
numElems := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
if numElems == 0 {
|
||||
*dst = Nummultirange{Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elements := make([]Numrange, numElems)
|
||||
|
||||
for i := range elements {
|
||||
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
var elemSrc []byte
|
||||
if elemLen >= 0 {
|
||||
elemSrc = src[rp : rp+elemLen]
|
||||
rp += elemLen
|
||||
}
|
||||
err := elements[i].DecodeBinary(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Nummultirange{Ranges: elements, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src Nummultirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, '{')
|
||||
|
||||
inElemBuf := make([]byte, 0, 32)
|
||||
for i, elem := range src.Ranges {
|
||||
if i > 0 {
|
||||
buf = append(buf, ',')
|
||||
}
|
||||
|
||||
elemBuf, err := elem.EncodeText(ci, inElemBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf == nil {
|
||||
return nil, fmt.Errorf("multi-range does not allow null range")
|
||||
} else {
|
||||
buf = append(buf, string(elemBuf)...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buf = append(buf, '}')
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src Nummultirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
|
||||
|
||||
for i := range src.Ranges {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Nummultirange) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
return dst.DecodeText(nil, nil)
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Nummultirange) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package pgtype_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestNumericMultirangeTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "nummultirange", []interface{}{
|
||||
&pgtype.Nummultirange{
|
||||
Ranges: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Nummultirange{
|
||||
Ranges: []pgtype.Numrange{
|
||||
{
|
||||
Lower: pgtype.Numeric{Int: big.NewInt(-543), Exp: 3, Status: pgtype.Present},
|
||||
Upper: pgtype.Numeric{Int: big.NewInt(342), Exp: 1, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Nummultirange{
|
||||
Ranges: []pgtype.Numrange{
|
||||
{
|
||||
Lower: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Status: pgtype.Present},
|
||||
Upper: pgtype.Numeric{Int: big.NewInt(-5), Exp: 0, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Numeric{Int: big.NewInt(5), Exp: 1, Status: pgtype.Present},
|
||||
Upper: pgtype.Numeric{Int: big.NewInt(42), Exp: 1, Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Inclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
{
|
||||
Lower: pgtype.Numeric{Int: big.NewInt(42), Exp: 2, Status: pgtype.Present},
|
||||
LowerType: pgtype.Exclusive,
|
||||
UpperType: pgtype.Unbounded,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
143
pgtype.go
143
pgtype.go
|
@ -74,12 +74,15 @@ const (
|
|||
JSONBArrayOID = 3807
|
||||
DaterangeOID = 3912
|
||||
Int4rangeOID = 3904
|
||||
Int4multirangeOID = 4451
|
||||
NumrangeOID = 3906
|
||||
NummultirangeOID = 4532
|
||||
TsrangeOID = 3908
|
||||
TsrangeArrayOID = 3909
|
||||
TstzrangeOID = 3910
|
||||
TstzrangeArrayOID = 3911
|
||||
Int8rangeOID = 3926
|
||||
Int8multirangeOID = 4536
|
||||
)
|
||||
|
||||
type Status byte
|
||||
|
@ -288,8 +291,10 @@ func NewConnInfo() *ConnInfo {
|
|||
ci.RegisterDataType(DataType{Value: &Int2{}, Name: "int2", OID: Int2OID})
|
||||
ci.RegisterDataType(DataType{Value: &Int4{}, Name: "int4", OID: Int4OID})
|
||||
ci.RegisterDataType(DataType{Value: &Int4range{}, Name: "int4range", OID: Int4rangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int4multirange{}, Name: "int4multirange", OID: Int4multirangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int8{}, Name: "int8", OID: Int8OID})
|
||||
ci.RegisterDataType(DataType{Value: &Int8range{}, Name: "int8range", OID: Int8rangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int8multirange{}, Name: "int8multirange", OID: Int8multirangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Interval{}, Name: "interval", OID: IntervalOID})
|
||||
ci.RegisterDataType(DataType{Value: &JSON{}, Name: "json", OID: JSONOID})
|
||||
ci.RegisterDataType(DataType{Value: &JSONB{}, Name: "jsonb", OID: JSONBOID})
|
||||
|
@ -300,6 +305,7 @@ func NewConnInfo() *ConnInfo {
|
|||
ci.RegisterDataType(DataType{Value: &Name{}, Name: "name", OID: NameOID})
|
||||
ci.RegisterDataType(DataType{Value: &Numeric{}, Name: "numeric", OID: NumericOID})
|
||||
ci.RegisterDataType(DataType{Value: &Numrange{}, Name: "numrange", OID: NumrangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Nummultirange{}, Name: "nummultirange", OID: NummultirangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &OIDValue{}, Name: "oid", OID: OIDOID})
|
||||
ci.RegisterDataType(DataType{Value: &Path{}, Name: "path", OID: PathOID})
|
||||
ci.RegisterDataType(DataType{Value: &Point{}, Name: "point", OID: PointOID})
|
||||
|
@ -873,72 +879,75 @@ var nameValues map[string]Value
|
|||
|
||||
func init() {
|
||||
nameValues = map[string]Value{
|
||||
"_aclitem": &ACLItemArray{},
|
||||
"_bool": &BoolArray{},
|
||||
"_bpchar": &BPCharArray{},
|
||||
"_bytea": &ByteaArray{},
|
||||
"_cidr": &CIDRArray{},
|
||||
"_date": &DateArray{},
|
||||
"_float4": &Float4Array{},
|
||||
"_float8": &Float8Array{},
|
||||
"_inet": &InetArray{},
|
||||
"_int2": &Int2Array{},
|
||||
"_int4": &Int4Array{},
|
||||
"_int8": &Int8Array{},
|
||||
"_numeric": &NumericArray{},
|
||||
"_text": &TextArray{},
|
||||
"_timestamp": &TimestampArray{},
|
||||
"_timestamptz": &TimestamptzArray{},
|
||||
"_uuid": &UUIDArray{},
|
||||
"_varchar": &VarcharArray{},
|
||||
"_jsonb": &JSONBArray{},
|
||||
"aclitem": &ACLItem{},
|
||||
"bit": &Bit{},
|
||||
"bool": &Bool{},
|
||||
"box": &Box{},
|
||||
"bpchar": &BPChar{},
|
||||
"bytea": &Bytea{},
|
||||
"char": &QChar{},
|
||||
"cid": &CID{},
|
||||
"cidr": &CIDR{},
|
||||
"circle": &Circle{},
|
||||
"date": &Date{},
|
||||
"daterange": &Daterange{},
|
||||
"float4": &Float4{},
|
||||
"float8": &Float8{},
|
||||
"hstore": &Hstore{},
|
||||
"inet": &Inet{},
|
||||
"int2": &Int2{},
|
||||
"int4": &Int4{},
|
||||
"int4range": &Int4range{},
|
||||
"int8": &Int8{},
|
||||
"int8range": &Int8range{},
|
||||
"interval": &Interval{},
|
||||
"json": &JSON{},
|
||||
"jsonb": &JSONB{},
|
||||
"line": &Line{},
|
||||
"lseg": &Lseg{},
|
||||
"macaddr": &Macaddr{},
|
||||
"name": &Name{},
|
||||
"numeric": &Numeric{},
|
||||
"numrange": &Numrange{},
|
||||
"oid": &OIDValue{},
|
||||
"path": &Path{},
|
||||
"point": &Point{},
|
||||
"polygon": &Polygon{},
|
||||
"record": &Record{},
|
||||
"text": &Text{},
|
||||
"tid": &TID{},
|
||||
"timestamp": &Timestamp{},
|
||||
"timestamptz": &Timestamptz{},
|
||||
"tsrange": &Tsrange{},
|
||||
"_tsrange": &TsrangeArray{},
|
||||
"tstzrange": &Tstzrange{},
|
||||
"_tstzrange": &TstzrangeArray{},
|
||||
"unknown": &Unknown{},
|
||||
"uuid": &UUID{},
|
||||
"varbit": &Varbit{},
|
||||
"varchar": &Varchar{},
|
||||
"xid": &XID{},
|
||||
"_aclitem": &ACLItemArray{},
|
||||
"_bool": &BoolArray{},
|
||||
"_bpchar": &BPCharArray{},
|
||||
"_bytea": &ByteaArray{},
|
||||
"_cidr": &CIDRArray{},
|
||||
"_date": &DateArray{},
|
||||
"_float4": &Float4Array{},
|
||||
"_float8": &Float8Array{},
|
||||
"_inet": &InetArray{},
|
||||
"_int2": &Int2Array{},
|
||||
"_int4": &Int4Array{},
|
||||
"_int8": &Int8Array{},
|
||||
"_numeric": &NumericArray{},
|
||||
"_text": &TextArray{},
|
||||
"_timestamp": &TimestampArray{},
|
||||
"_timestamptz": &TimestamptzArray{},
|
||||
"_uuid": &UUIDArray{},
|
||||
"_varchar": &VarcharArray{},
|
||||
"_jsonb": &JSONBArray{},
|
||||
"aclitem": &ACLItem{},
|
||||
"bit": &Bit{},
|
||||
"bool": &Bool{},
|
||||
"box": &Box{},
|
||||
"bpchar": &BPChar{},
|
||||
"bytea": &Bytea{},
|
||||
"char": &QChar{},
|
||||
"cid": &CID{},
|
||||
"cidr": &CIDR{},
|
||||
"circle": &Circle{},
|
||||
"date": &Date{},
|
||||
"daterange": &Daterange{},
|
||||
"float4": &Float4{},
|
||||
"float8": &Float8{},
|
||||
"hstore": &Hstore{},
|
||||
"inet": &Inet{},
|
||||
"int2": &Int2{},
|
||||
"int4": &Int4{},
|
||||
"int4range": &Int4range{},
|
||||
"int4multirange": &Int4multirange{},
|
||||
"int8": &Int8{},
|
||||
"int8range": &Int8range{},
|
||||
"int8multirange": &Int8multirange{},
|
||||
"interval": &Interval{},
|
||||
"json": &JSON{},
|
||||
"jsonb": &JSONB{},
|
||||
"line": &Line{},
|
||||
"lseg": &Lseg{},
|
||||
"macaddr": &Macaddr{},
|
||||
"name": &Name{},
|
||||
"numeric": &Numeric{},
|
||||
"numrange": &Numrange{},
|
||||
"nummultirange": &Nummultirange{},
|
||||
"oid": &OIDValue{},
|
||||
"path": &Path{},
|
||||
"point": &Point{},
|
||||
"polygon": &Polygon{},
|
||||
"record": &Record{},
|
||||
"text": &Text{},
|
||||
"tid": &TID{},
|
||||
"timestamp": &Timestamp{},
|
||||
"timestamptz": &Timestamptz{},
|
||||
"tsrange": &Tsrange{},
|
||||
"_tsrange": &TsrangeArray{},
|
||||
"tstzrange": &Tstzrange{},
|
||||
"_tstzrange": &TstzrangeArray{},
|
||||
"unknown": &Unknown{},
|
||||
"uuid": &UUID{},
|
||||
"varbit": &Varbit{},
|
||||
"varchar": &Varchar{},
|
||||
"xid": &XID{},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type <%= multirange_type %> struct {
|
||||
Ranges []<%= range_type %>
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *<%= multirange_type %>) Set(src interface{}) error {
|
||||
//untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = <%= multirange_type %>{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case <%= multirange_type %>:
|
||||
*dst = value
|
||||
case *<%= multirange_type %>:
|
||||
*dst = *value
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
case []<%= range_type %>:
|
||||
if value == nil {
|
||||
*dst = <%= multirange_type %>{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = <%= multirange_type %>{Status: Present}
|
||||
} else {
|
||||
elements := make([]<%= range_type %>, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = <%= multirange_type %>{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
case []*<%= range_type %>:
|
||||
if value == nil {
|
||||
*dst = <%= multirange_type %>{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = <%= multirange_type %>{Status: Present}
|
||||
} else {
|
||||
elements := make([]<%= range_type %>, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = <%= multirange_type %>{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cannot convert %v to <%= multirange_type %>", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (dst <%= multirange_type %>) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *<%= multirange_type %>) AssignTo(dst interface{}) error {
|
||||
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *<%= multirange_type %>) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = <%= multirange_type %>{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utmr, err := ParseUntypedTextMultirange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []<%= range_type %>
|
||||
|
||||
if len(utmr.Elements) > 0 {
|
||||
elements = make([]<%= range_type %>, len(utmr.Elements))
|
||||
|
||||
for i, s := range utmr.Elements {
|
||||
var elem <%= range_type %>
|
||||
|
||||
elemSrc := []byte(s)
|
||||
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = <%= multirange_type %>{Ranges: elements, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *<%= multirange_type %>) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = <%= multirange_type %>{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
rp := 0
|
||||
|
||||
numElems := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
if numElems == 0 {
|
||||
*dst = <%= multirange_type %>{Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elements := make([]<%= range_type %>, numElems)
|
||||
|
||||
for i := range elements {
|
||||
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
var elemSrc []byte
|
||||
if elemLen >= 0 {
|
||||
elemSrc = src[rp : rp+elemLen]
|
||||
rp += elemLen
|
||||
}
|
||||
err := elements[i].DecodeBinary(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*dst = <%= multirange_type %>{Ranges: elements, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src <%= multirange_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, '{')
|
||||
|
||||
inElemBuf := make([]byte, 0, 32)
|
||||
for i, elem := range src.Ranges {
|
||||
if i > 0 {
|
||||
buf = append(buf, ',')
|
||||
}
|
||||
|
||||
elemBuf, err := elem.EncodeText(ci, inElemBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf == nil {
|
||||
return nil, fmt.Errorf("multi-range does not allow null range")
|
||||
} else {
|
||||
buf = append(buf, string(elemBuf)...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buf = append(buf, '}')
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src <%= multirange_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
|
||||
|
||||
for i := range src.Ranges {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *<%= multirange_type %>) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
return dst.DecodeText(nil, nil)
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src <%= multirange_type %>) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
erb range_type=Numrange multirange_type=Nummultirange typed_multirange.go.erb > num_multirange.go
|
||||
erb range_type=Int4range multirange_type=Int4multirange typed_multirange.go.erb > int4_multirange.go
|
||||
erb range_type=Int8range multirange_type=Int8multirange typed_multirange.go.erb > int8_multirange.go
|
||||
# TODO
|
||||
# erb range_type=Tsrange multirange_type=Tsmultirange typed_multirange.go.erb > ts_multirange.go
|
||||
# erb range_type=Tstzrange multirange_type=Tstzmultirange typed_multirange.go.erb > tstz_multirange.go
|
||||
# erb range_type=Daterange multirange_type=Datemultirange typed_multirange.go.erb > date_multirange.go
|
||||
goimports -w *multirange.go
|
Loading…
Reference in New Issue