Use netip package for representing inet and cidr types

pull/1281/head
Jack Christensen 2022-07-10 14:29:44 -05:00
parent 7974a102fc
commit e7eb8a3250
8 changed files with 231 additions and 124 deletions

View File

@ -102,6 +102,7 @@ This matches the convention set by `database/sql`. In addition, for comparable t
* `Hstore` is now defined as `map[string]*string`. * `Hstore` is now defined as `map[string]*string`.
* `JSON` and `JSONB` types removed. Use `[]byte` or `string` directly. * `JSON` and `JSONB` types removed. Use `[]byte` or `string` directly.
* `QChar` type removed. Use `rune` or `byte` directly. * `QChar` type removed. Use `rune` or `byte` directly.
* `Inet` and `Cidr` types removed. Use `netip.Addr` and `netip.Prefix` directly. These types are more memory efficient than the previous `net.IPNet`.
* `Macaddr` type removed. Use `net.HardwareAddr` directly. * `Macaddr` type removed. Use `net.HardwareAddr` directly.
* Renamed `pgtype.ConnInfo` to `pgtype.Map`. * Renamed `pgtype.ConnInfo` to `pgtype.Map`.
* Renamed `pgtype.DataType` to `pgtype.Type`. * Renamed `pgtype.DataType` to `pgtype.Type`.

View File

@ -80,7 +80,7 @@ pgx supports many features beyond what is available through `database/sql`:
* Conversion of PostgreSQL arrays to Go slice mappings for integers, floats, and strings * Conversion of PostgreSQL arrays to Go slice mappings for integers, floats, and strings
* Hstore support * Hstore support
* JSON and JSONB support * JSON and JSONB support
* Maps `inet` and `cidr` PostgreSQL types to `net.IPNet` and `net.IP` * Maps `inet` and `cidr` PostgreSQL types to `netip.Addr` and `netip.Prefix`
* Large object support * Large object support
* NULL mapping to Null* struct or pointer to pointer * NULL mapping to Null* struct or pointer to pointer
* Supports `database/sql.Scanner` and `database/sql/driver.Valuer` interfaces for custom types * Supports `database/sql.Scanner` and `database/sql/driver.Valuer` interfaces for custom types

3
doc.go
View File

@ -152,8 +152,7 @@ pgx includes built-in support to marshal and unmarshal between Go types and the
Inet and CIDR Mapping Inet and CIDR Mapping
pgx encodes from net.IPNet to and from inet and cidr PostgreSQL types. In addition, as a convenience pgx will encode pgx converts netip.Prefix and netip.Addr to and from inet and cidr PostgreSQL types.
from a net.IP; it will assume a /32 netmask for IPv4 and a /128 for IPv6.
Custom Type Support Custom Type Support

View File

@ -1,9 +1,11 @@
package pgtype package pgtype
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"net" "net"
"net/netip"
"reflect" "reflect"
"time" "time"
) )
@ -460,44 +462,95 @@ func (w durationWrapper) IntervalValue() (Interval, error) {
type netIPNetWrapper net.IPNet type netIPNetWrapper net.IPNet
func (w *netIPNetWrapper) ScanInet(v Inet) error { func (w *netIPNetWrapper) ScanNetipPrefix(v netip.Prefix) error {
if !v.Valid { if !v.IsValid() {
return fmt.Errorf("cannot scan NULL into *net.IPNet") return fmt.Errorf("cannot scan NULL into *net.IPNet")
} }
*w = (netIPNetWrapper)(*v.IPNet) *w = netIPNetWrapper{
return nil IP: v.Addr().AsSlice(),
Mask: net.CIDRMask(v.Bits(), v.Addr().BitLen()),
} }
func (w netIPNetWrapper) InetValue() (Inet, error) { return nil
return Inet{IPNet: (*net.IPNet)(&w), Valid: true}, nil }
func (w netIPNetWrapper) NetipPrefixValue() (netip.Prefix, error) {
ip, ok := netip.AddrFromSlice(w.IP)
if !ok {
return netip.Prefix{}, errors.New("invalid net.IPNet")
}
ones, _ := w.Mask.Size()
return netip.PrefixFrom(ip, ones), nil
} }
type netIPWrapper net.IP type netIPWrapper net.IP
func (w netIPWrapper) SkipUnderlyingTypePlan() {} func (w netIPWrapper) SkipUnderlyingTypePlan() {}
func (w *netIPWrapper) ScanInet(v Inet) error { func (w *netIPWrapper) ScanNetipPrefix(v netip.Prefix) error {
if !v.Valid { if !v.IsValid() {
*w = nil *w = nil
return nil return nil
} }
if oneCount, bitCount := v.IPNet.Mask.Size(); oneCount != bitCount { if v.Addr().BitLen() != v.Bits() {
return fmt.Errorf("cannot scan %v to *net.IP", v) return fmt.Errorf("cannot scan %v to *net.IP", v)
} }
*w = netIPWrapper(v.IPNet.IP)
*w = netIPWrapper(v.Addr().AsSlice())
return nil return nil
} }
func (w netIPWrapper) InetValue() (Inet, error) { func (w netIPWrapper) NetipPrefixValue() (netip.Prefix, error) {
if w == nil { if w == nil {
return Inet{}, nil return netip.Prefix{}, nil
} }
bitCount := len(w) * 8 addr, ok := netip.AddrFromSlice([]byte(w))
mask := net.CIDRMask(bitCount, bitCount) if !ok {
return Inet{IPNet: &net.IPNet{Mask: mask, IP: net.IP(w)}, Valid: true}, nil return netip.Prefix{}, errors.New("invalid net.IP")
}
return netip.PrefixFrom(addr, addr.BitLen()), nil
}
type netipPrefixWrapper netip.Prefix
func (w *netipPrefixWrapper) ScanNetipPrefix(v netip.Prefix) error {
*w = netipPrefixWrapper(v)
return nil
}
func (w netipPrefixWrapper) NetipPrefixValue() (netip.Prefix, error) {
return netip.Prefix(w), nil
}
type netipAddrWrapper netip.Addr
func (w *netipAddrWrapper) ScanNetipPrefix(v netip.Prefix) error {
if !v.IsValid() {
*w = netipAddrWrapper(netip.Addr{})
return nil
}
if v.Addr().BitLen() != v.Bits() {
return fmt.Errorf("cannot scan %v to netip.Addr", v)
}
*w = netipAddrWrapper(v.Addr())
return nil
}
func (w netipAddrWrapper) NetipPrefixValue() (netip.Prefix, error) {
addr := (netip.Addr)(w)
if !addr.IsValid() {
return netip.Prefix{}, nil
}
return netip.PrefixFrom(addr, addr.BitLen()), nil
} }
type mapStringToPointerStringWrapper map[string]*string type mapStringToPointerStringWrapper map[string]*string

View File

@ -1,9 +1,11 @@
package pgtype package pgtype
import ( import (
"bytes"
"database/sql/driver" "database/sql/driver"
"errors"
"fmt" "fmt"
"net" "net/netip"
) )
// Network address family is dependent on server socket.h value for AF_INET. // Network address family is dependent on server socket.h value for AF_INET.
@ -14,57 +16,16 @@ const (
defaultAFInet6 = 3 defaultAFInet6 = 3
) )
type InetScanner interface { type NetipPrefixScanner interface {
ScanInet(v Inet) error ScanNetipPrefix(v netip.Prefix) error
} }
type InetValuer interface { type NetipPrefixValuer interface {
InetValue() (Inet, error) NetipPrefixValue() (netip.Prefix, error)
}
// Inet represents both inet and cidr PostgreSQL types.
type Inet struct {
IPNet *net.IPNet
Valid bool
}
func (inet *Inet) ScanInet(v Inet) error {
*inet = v
return nil
}
func (inet Inet) InetValue() (Inet, error) {
return inet, nil
}
// Scan implements the database/sql Scanner interface.
func (dst *Inet) Scan(src any) error {
if src == nil {
*dst = Inet{}
return nil
}
switch src := src.(type) {
case string:
return scanPlanTextAnyToInetScanner{}.Scan([]byte(src), dst)
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src Inet) Value() (driver.Value, error) {
if !src.Valid {
return nil, nil
}
buf, err := InetCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)
if err != nil {
return nil, err
}
return string(buf), err
} }
// InetCodec handles both inet and cidr PostgreSQL types. The preferred Go types are netip.Prefix and netip.Addr. If
// IsValid() is false then they are treated as SQL NULL.
type InetCodec struct{} type InetCodec struct{}
func (InetCodec) FormatSupported(format int16) bool { func (InetCodec) FormatSupported(format int16) bool {
@ -76,7 +37,7 @@ func (InetCodec) PreferredFormat() int16 {
} }
func (InetCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan { func (InetCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
if _, ok := value.(InetValuer); !ok { if _, ok := value.(NetipPrefixValuer); !ok {
return nil return nil
} }
@ -93,51 +54,56 @@ func (InetCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodeP
type encodePlanInetCodecBinary struct{} type encodePlanInetCodecBinary struct{}
func (encodePlanInetCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) { func (encodePlanInetCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
inet, err := value.(InetValuer).InetValue() prefix, err := value.(NetipPrefixValuer).NetipPrefixValue()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !inet.Valid { if !prefix.IsValid() {
return nil, nil return nil, nil
} }
var family byte var family byte
switch len(inet.IPNet.IP) { if prefix.Addr().Is4() {
case net.IPv4len:
family = defaultAFInet family = defaultAFInet
case net.IPv6len: } else {
family = defaultAFInet6 family = defaultAFInet6
default:
return nil, fmt.Errorf("Unexpected IP length: %v", len(inet.IPNet.IP))
} }
buf = append(buf, family) buf = append(buf, family)
ones, _ := inet.IPNet.Mask.Size() ones := prefix.Bits()
buf = append(buf, byte(ones)) buf = append(buf, byte(ones))
// is_cidr is ignored on server // is_cidr is ignored on server
buf = append(buf, 0) buf = append(buf, 0)
buf = append(buf, byte(len(inet.IPNet.IP))) if family == defaultAFInet {
buf = append(buf, byte(4))
b := prefix.Addr().As4()
buf = append(buf, b[:]...)
} else {
buf = append(buf, byte(16))
b := prefix.Addr().As16()
buf = append(buf, b[:]...)
}
return append(buf, inet.IPNet.IP...), nil return buf, nil
} }
type encodePlanInetCodecText struct{} type encodePlanInetCodecText struct{}
func (encodePlanInetCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) { func (encodePlanInetCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
inet, err := value.(InetValuer).InetValue() prefix, err := value.(NetipPrefixValuer).NetipPrefixValue()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !inet.Valid { if !prefix.IsValid() {
return nil, nil return nil, nil
} }
return append(buf, inet.IPNet.String()...), nil return append(buf, prefix.String()...), nil
} }
func (InetCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (InetCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
@ -145,13 +111,13 @@ func (InetCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {
case InetScanner: case NetipPrefixScanner:
return scanPlanBinaryInetToInetScanner{} return scanPlanBinaryInetToNetipPrefixScanner{}
} }
case TextFormatCode: case TextFormatCode:
switch target.(type) { switch target.(type) {
case InetScanner: case NetipPrefixScanner:
return scanPlanTextAnyToInetScanner{} return scanPlanTextAnyToNetipPrefixScanner{}
} }
} }
@ -167,26 +133,26 @@ func (c InetCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (an
return nil, nil return nil, nil
} }
var inet Inet var prefix netip.Prefix
err := codecScan(c, m, oid, format, src, &inet) err := codecScan(c, m, oid, format, src, &prefix)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !inet.Valid { if !prefix.IsValid() {
return nil, nil return nil, nil
} }
return inet.IPNet, nil return prefix, nil
} }
type scanPlanBinaryInetToInetScanner struct{} type scanPlanBinaryInetToNetipPrefixScanner struct{}
func (scanPlanBinaryInetToInetScanner) Scan(src []byte, dst any) error { func (scanPlanBinaryInetToNetipPrefixScanner) Scan(src []byte, dst any) error {
scanner := (dst).(InetScanner) scanner := (dst).(NetipPrefixScanner)
if src == nil { if src == nil {
return scanner.ScanInet(Inet{}) return scanner.ScanNetipPrefix(netip.Prefix{})
} }
if len(src) != 8 && len(src) != 20 { if len(src) != 8 && len(src) != 20 {
@ -196,49 +162,39 @@ func (scanPlanBinaryInetToInetScanner) Scan(src []byte, dst any) error {
// ignore family // ignore family
bits := src[1] bits := src[1]
// ignore is_cidr // ignore is_cidr
addressLength := src[3] // ignore addressLength - implicit in length of message
var ipnet net.IPNet addr, ok := netip.AddrFromSlice(src[4:])
ipnet.IP = make(net.IP, int(addressLength)) if !ok {
copy(ipnet.IP, src[4:]) return errors.New("netip.AddrFromSlice failed")
if ipv4 := ipnet.IP.To4(); ipv4 != nil {
ipnet.IP = ipv4
}
ipnet.Mask = net.CIDRMask(int(bits), len(ipnet.IP)*8)
return scanner.ScanInet(Inet{IPNet: &ipnet, Valid: true})
} }
type scanPlanTextAnyToInetScanner struct{} return scanner.ScanNetipPrefix(netip.PrefixFrom(addr, int(bits)))
}
func (scanPlanTextAnyToInetScanner) Scan(src []byte, dst any) error { type scanPlanTextAnyToNetipPrefixScanner struct{}
scanner := (dst).(InetScanner)
func (scanPlanTextAnyToNetipPrefixScanner) Scan(src []byte, dst any) error {
scanner := (dst).(NetipPrefixScanner)
if src == nil { if src == nil {
return scanner.ScanInet(Inet{}) return scanner.ScanNetipPrefix(netip.Prefix{})
} }
var ipnet *net.IPNet var prefix netip.Prefix
var err error if bytes.IndexByte(src, '/') == -1 {
addr, err := netip.ParseAddr(string(src))
if ip := net.ParseIP(string(src)); ip != nil {
if ipv4 := ip.To4(); ipv4 != nil {
ip = ipv4
}
bitCount := len(ip) * 8
mask := net.CIDRMask(bitCount, bitCount)
ipnet = &net.IPNet{Mask: mask, IP: ip}
} else {
ip, ipnet, err = net.ParseCIDR(string(src))
if err != nil { if err != nil {
return err return err
} }
if ipv4 := ip.To4(); ipv4 != nil { prefix = netip.PrefixFrom(addr, addr.BitLen())
ip = ipv4 } else {
var err error
prefix, err = netip.ParsePrefix(string(src))
if err != nil {
return err
} }
ones, _ := ipnet.Mask.Size()
*ipnet = net.IPNet{IP: ip, Mask: net.CIDRMask(ones, len(ip)*8)}
} }
return scanner.ScanInet(Inet{IPNet: ipnet, Valid: true}) return scanner.ScanNetipPrefix(prefix)
} }

View File

@ -3,9 +3,9 @@ package pgtype_test
import ( import (
"context" "context"
"net" "net"
"net/netip"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgxtest" "github.com/jackc/pgx/v5/pgxtest"
) )
@ -31,7 +31,42 @@ func TestInetTranscode(t *testing.T) {
{mustParseInet(t, "::/0"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::/0"))}, {mustParseInet(t, "::/0"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::/0"))},
{mustParseInet(t, "::1/128"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::1/128"))}, {mustParseInet(t, "::1/128"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::1/128"))},
{mustParseInet(t, "2607:f8b0:4009:80b::200e/64"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "2607:f8b0:4009:80b::200e/64"))}, {mustParseInet(t, "2607:f8b0:4009:80b::200e/64"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "2607:f8b0:4009:80b::200e/64"))},
{nil, new(pgtype.Inet), isExpectedEq(pgtype.Inet{})},
{mustParseInet(t, "0.0.0.0/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("0.0.0.0/32"))},
{mustParseInet(t, "127.0.0.1/8"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("127.0.0.1/8"))},
{mustParseInet(t, "12.34.56.65/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("12.34.56.65/32"))},
{mustParseInet(t, "192.168.1.16/24"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("192.168.1.16/24"))},
{mustParseInet(t, "255.0.0.0/8"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("255.0.0.0/8"))},
{mustParseInet(t, "255.255.255.255/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("255.255.255.255/32"))},
{mustParseInet(t, "2607:f8b0:4009:80b::200e"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("2607:f8b0:4009:80b::200e/128"))},
{mustParseInet(t, "::1/64"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::1/64"))},
{mustParseInet(t, "::/0"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::/0"))},
{mustParseInet(t, "::1/128"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::1/128"))},
{mustParseInet(t, "2607:f8b0:4009:80b::200e/64"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("2607:f8b0:4009:80b::200e/64"))},
{netip.MustParsePrefix("0.0.0.0/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("0.0.0.0/32"))},
{netip.MustParsePrefix("127.0.0.1/8"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("127.0.0.1/8"))},
{netip.MustParsePrefix("12.34.56.65/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("12.34.56.65/32"))},
{netip.MustParsePrefix("192.168.1.16/24"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("192.168.1.16/24"))},
{netip.MustParsePrefix("255.0.0.0/8"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("255.0.0.0/8"))},
{netip.MustParsePrefix("255.255.255.255/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("255.255.255.255/32"))},
{netip.MustParsePrefix("::1/64"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::1/64"))},
{netip.MustParsePrefix("::/0"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::/0"))},
{netip.MustParsePrefix("::1/128"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::1/128"))},
{netip.MustParsePrefix("2607:f8b0:4009:80b::200e/64"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("2607:f8b0:4009:80b::200e/64"))},
{netip.MustParseAddr("0.0.0.0"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("0.0.0.0"))},
{netip.MustParseAddr("127.0.0.1"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("127.0.0.1"))},
{netip.MustParseAddr("12.34.56.65"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("12.34.56.65"))},
{netip.MustParseAddr("192.168.1.16"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("192.168.1.16"))},
{netip.MustParseAddr("255.0.0.0"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("255.0.0.0"))},
{netip.MustParseAddr("255.255.255.255"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("255.255.255.255"))},
{netip.MustParseAddr("2607:f8b0:4009:80b::200e"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("2607:f8b0:4009:80b::200e"))},
{netip.MustParseAddr("::1"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("::1"))},
{netip.MustParseAddr("::"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("::"))},
{netip.MustParseAddr("2607:f8b0:4009:80b::200e"), new(netip.Addr), isExpectedEq(netip.MustParseAddr("2607:f8b0:4009:80b::200e"))},
{nil, new(netip.Prefix), isExpectedEq(netip.Prefix{})},
}) })
} }
@ -48,6 +83,17 @@ func TestCidrTranscode(t *testing.T) {
{mustParseInet(t, "::/0"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::/0"))}, {mustParseInet(t, "::/0"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::/0"))},
{mustParseInet(t, "::1/128"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::1/128"))}, {mustParseInet(t, "::1/128"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "::1/128"))},
{mustParseInet(t, "2607:f8b0:4009:80b::200e/128"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "2607:f8b0:4009:80b::200e/128"))}, {mustParseInet(t, "2607:f8b0:4009:80b::200e/128"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "2607:f8b0:4009:80b::200e/128"))},
{nil, new(pgtype.Inet), isExpectedEq(pgtype.Inet{})},
{netip.MustParsePrefix("0.0.0.0/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("0.0.0.0/32"))},
{netip.MustParsePrefix("127.0.0.1/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("127.0.0.1/32"))},
{netip.MustParsePrefix("12.34.56.0/32"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("12.34.56.0/32"))},
{netip.MustParsePrefix("192.168.1.0/24"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("192.168.1.0/24"))},
{netip.MustParsePrefix("255.0.0.0/8"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("255.0.0.0/8"))},
{netip.MustParsePrefix("::/128"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::/128"))},
{netip.MustParsePrefix("::/0"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::/0"))},
{netip.MustParsePrefix("::1/128"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("::1/128"))},
{netip.MustParsePrefix("2607:f8b0:4009:80b::200e/128"), new(netip.Prefix), isExpectedEq(netip.MustParsePrefix("2607:f8b0:4009:80b::200e/128"))},
{nil, new(netip.Prefix), isExpectedEq(netip.Prefix{})},
}) })
} }

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/netip"
"reflect" "reflect"
"time" "time"
) )
@ -364,6 +365,8 @@ func NewMap() *Map {
registerDefaultPgTypeVariants[net.IP](m, "inet") registerDefaultPgTypeVariants[net.IP](m, "inet")
registerDefaultPgTypeVariants[net.IPNet](m, "cidr") registerDefaultPgTypeVariants[net.IPNet](m, "cidr")
registerDefaultPgTypeVariants[netip.Addr](m, "inet")
registerDefaultPgTypeVariants[netip.Prefix](m, "cidr")
// pgtype provided structs // pgtype provided structs
registerDefaultPgTypeVariants[Bits](m, "varbit") registerDefaultPgTypeVariants[Bits](m, "varbit")
@ -377,7 +380,6 @@ func NewMap() *Map {
registerDefaultPgTypeVariants[Float8](m, "float8") registerDefaultPgTypeVariants[Float8](m, "float8")
registerDefaultPgTypeVariants[Range[Float8]](m, "numrange") // There is no PostgreSQL builtin float8range so map it to numrange. registerDefaultPgTypeVariants[Range[Float8]](m, "numrange") // There is no PostgreSQL builtin float8range so map it to numrange.
registerDefaultPgTypeVariants[Multirange[Range[Float8]]](m, "nummultirange") // There is no PostgreSQL builtin float8multirange so map it to nummultirange. registerDefaultPgTypeVariants[Multirange[Range[Float8]]](m, "nummultirange") // There is no PostgreSQL builtin float8multirange so map it to nummultirange.
registerDefaultPgTypeVariants[Inet](m, "inet")
registerDefaultPgTypeVariants[Int2](m, "int2") registerDefaultPgTypeVariants[Int2](m, "int2")
registerDefaultPgTypeVariants[Int4](m, "int4") registerDefaultPgTypeVariants[Int4](m, "int4")
registerDefaultPgTypeVariants[Range[Int4]](m, "int4range") registerDefaultPgTypeVariants[Range[Int4]](m, "int4range")
@ -751,6 +753,10 @@ func TryWrapBuiltinTypeScanPlan(target any) (plan WrappedScanPlanNextSetter, nex
return &wrapNetIPNetScanPlan{}, (*netIPNetWrapper)(target), true return &wrapNetIPNetScanPlan{}, (*netIPNetWrapper)(target), true
case *net.IP: case *net.IP:
return &wrapNetIPScanPlan{}, (*netIPWrapper)(target), true return &wrapNetIPScanPlan{}, (*netIPWrapper)(target), true
case *netip.Prefix:
return &wrapNetipPrefixScanPlan{}, (*netipPrefixWrapper)(target), true
case *netip.Addr:
return &wrapNetipAddrScanPlan{}, (*netipAddrWrapper)(target), true
case *map[string]*string: case *map[string]*string:
return &wrapMapStringToPointerStringScanPlan{}, (*mapStringToPointerStringWrapper)(target), true return &wrapMapStringToPointerStringScanPlan{}, (*mapStringToPointerStringWrapper)(target), true
case *map[string]string: case *map[string]string:
@ -934,6 +940,26 @@ func (plan *wrapNetIPScanPlan) Scan(src []byte, dst any) error {
return plan.next.Scan(src, (*netIPWrapper)(dst.(*net.IP))) return plan.next.Scan(src, (*netIPWrapper)(dst.(*net.IP)))
} }
type wrapNetipPrefixScanPlan struct {
next ScanPlan
}
func (plan *wrapNetipPrefixScanPlan) SetNext(next ScanPlan) { plan.next = next }
func (plan *wrapNetipPrefixScanPlan) Scan(src []byte, dst any) error {
return plan.next.Scan(src, (*netipPrefixWrapper)(dst.(*netip.Prefix)))
}
type wrapNetipAddrScanPlan struct {
next ScanPlan
}
func (plan *wrapNetipAddrScanPlan) SetNext(next ScanPlan) { plan.next = next }
func (plan *wrapNetipAddrScanPlan) Scan(src []byte, dst any) error {
return plan.next.Scan(src, (*netipAddrWrapper)(dst.(*netip.Addr)))
}
type wrapMapStringToPointerStringScanPlan struct { type wrapMapStringToPointerStringScanPlan struct {
next ScanPlan next ScanPlan
} }
@ -1454,6 +1480,10 @@ func TryWrapBuiltinTypeEncodePlan(value any) (plan WrappedEncodePlanNextSetter,
return &wrapNetIPNetEncodePlan{}, netIPNetWrapper(value), true return &wrapNetIPNetEncodePlan{}, netIPNetWrapper(value), true
case net.IP: case net.IP:
return &wrapNetIPEncodePlan{}, netIPWrapper(value), true return &wrapNetIPEncodePlan{}, netIPWrapper(value), true
case netip.Prefix:
return &wrapNetipPrefixEncodePlan{}, netipPrefixWrapper(value), true
case netip.Addr:
return &wrapNetipAddrEncodePlan{}, netipAddrWrapper(value), true
case map[string]*string: case map[string]*string:
return &wrapMapStringToPointerStringEncodePlan{}, mapStringToPointerStringWrapper(value), true return &wrapMapStringToPointerStringEncodePlan{}, mapStringToPointerStringWrapper(value), true
case map[string]string: case map[string]string:
@ -1639,6 +1669,26 @@ func (plan *wrapNetIPEncodePlan) Encode(value any, buf []byte) (newBuf []byte, e
return plan.next.Encode(netIPWrapper(value.(net.IP)), buf) return plan.next.Encode(netIPWrapper(value.(net.IP)), buf)
} }
type wrapNetipPrefixEncodePlan struct {
next EncodePlan
}
func (plan *wrapNetipPrefixEncodePlan) SetNext(next EncodePlan) { plan.next = next }
func (plan *wrapNetipPrefixEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
return plan.next.Encode(netipPrefixWrapper(value.(netip.Prefix)), buf)
}
type wrapNetipAddrEncodePlan struct {
next EncodePlan
}
func (plan *wrapNetipAddrEncodePlan) SetNext(next EncodePlan) { plan.next = next }
func (plan *wrapNetipAddrEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
return plan.next.Encode(netipAddrWrapper(value.(netip.Addr)), buf)
}
type wrapMapStringToPointerStringEncodePlan struct { type wrapMapStringToPointerStringEncodePlan struct {
next EncodePlan next EncodePlan
} }

View File

@ -55,6 +55,8 @@ func mustParseInet(t testing.TB, s string) *net.IPNet {
if err == nil { if err == nil {
if ipv4 := ip.To4(); ipv4 != nil { if ipv4 := ip.To4(); ipv4 != nil {
ipnet.IP = ipv4 ipnet.IP = ipv4
} else {
ipnet.IP = ip
} }
return ipnet return ipnet
} }