Skip to content

Commit e9ba44e

Browse files
committed
Add support for macaddr and macaddr8
1 parent b578d9a commit e9ba44e

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

macaddr.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package pqtype
2+
3+
import (
4+
"database/sql/driver"
5+
"fmt"
6+
"net"
7+
)
8+
9+
type Macaddr struct {
10+
Addr net.HardwareAddr
11+
Valid bool
12+
}
13+
14+
func (dst *Macaddr) decodeText(src []byte) error {
15+
if src == nil {
16+
*dst = Macaddr{}
17+
return nil
18+
}
19+
20+
addr, err := net.ParseMAC(string(src))
21+
if err != nil {
22+
return err
23+
}
24+
25+
*dst = Macaddr{Addr: addr, Valid: true}
26+
return nil
27+
}
28+
29+
func (src Macaddr) encodeText(buf []byte) ([]byte, error) {
30+
if src.Valid {
31+
return append(buf, src.Addr.String()...), nil
32+
} else {
33+
return nil, nil
34+
}
35+
}
36+
37+
// Scan implements the database/sql Scanner interface.
38+
func (dst *Macaddr) Scan(src interface{}) error {
39+
if src == nil {
40+
*dst = Macaddr{}
41+
return nil
42+
}
43+
44+
switch src := src.(type) {
45+
case string:
46+
return dst.decodeText([]byte(src))
47+
case []byte:
48+
srcCopy := make([]byte, len(src))
49+
copy(srcCopy, src)
50+
return dst.decodeText(srcCopy)
51+
}
52+
53+
return fmt.Errorf("cannot scan %T", src)
54+
}
55+
56+
// Value implements the database/sql/driver Valuer interface.
57+
func (src Macaddr) Value() (driver.Value, error) {
58+
buf, err := src.encodeText(make([]byte, 0, 32))
59+
if err != nil {
60+
return nil, err
61+
}
62+
if buf == nil {
63+
return nil, nil
64+
}
65+
return string(buf), err
66+
}

tests/macaddr_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package tests
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
"github.com/tabbed/pqtype"
9+
)
10+
11+
// https://www.postgresql.org/docs/current/datatype-net-types.html#DATATYPE-MACADDR
12+
func TestMacaddr(t *testing.T) {
13+
for _, addr := range []struct {
14+
input string
15+
output string
16+
}{
17+
{
18+
"08:00:2b:01:02:03",
19+
"08:00:2b:01:02:03",
20+
},
21+
{
22+
"08-00-2b-01-02-03",
23+
"08:00:2b:01:02:03",
24+
},
25+
{
26+
"08002b:010203",
27+
"08:00:2b:01:02:03",
28+
},
29+
{
30+
"08002b-010203",
31+
"08:00:2b:01:02:03",
32+
},
33+
{
34+
"0800.2b01.0203",
35+
"08:00:2b:01:02:03",
36+
},
37+
{
38+
"0800-2b01-0203",
39+
"08:00:2b:01:02:03",
40+
},
41+
{
42+
"08002b010203",
43+
"08:00:2b:01:02:03",
44+
},
45+
} {
46+
addr = addr
47+
t.Run(addr.input, func(t *testing.T) {
48+
var cidr pqtype.Macaddr
49+
if err := db.QueryRow(fmt.Sprintf(`SELECT '%s'::macaddr`, addr.input)).Scan(&cidr); err != nil {
50+
t.Fatal(err)
51+
}
52+
if diff := cmp.Diff(true, cidr.Valid); diff != "" {
53+
t.Errorf("valid mismatch (-want +got):\n%s", diff)
54+
}
55+
if diff := cmp.Diff(addr.output, cidr.Addr.String()); diff != "" {
56+
t.Errorf("mismatch (-want +got):\n%s", diff)
57+
}
58+
})
59+
}
60+
t.Run("NULL", func(t *testing.T) {
61+
var cidr pqtype.Macaddr
62+
if err := db.QueryRow(fmt.Sprintf(`SELECT NULL::macaddr`)).Scan(&cidr); err != nil {
63+
t.Fatal(err)
64+
}
65+
if diff := cmp.Diff(false, cidr.Valid); diff != "" {
66+
t.Errorf("valid mismatch (-want +got):\n%s", diff)
67+
}
68+
})
69+
}
70+
71+
// https://www.postgresql.org/docs/current/datatype-net-types.html#DATATYPE-MACADDR8
72+
func TestMacaddr8(t *testing.T) {
73+
for _, addr := range []struct {
74+
input string
75+
output string
76+
}{
77+
{
78+
"08:00:2b:01:02:03:04:05",
79+
"08:00:2b:01:02:03:04:05",
80+
},
81+
{
82+
"08-00-2b-01-02-03-04-05",
83+
"08:00:2b:01:02:03:04:05",
84+
},
85+
{
86+
"08002b:0102030405",
87+
"08:00:2b:01:02:03:04:05",
88+
},
89+
{
90+
"08002b-0102030405",
91+
"08:00:2b:01:02:03:04:05",
92+
},
93+
{
94+
"0800.2b01.0203.0405",
95+
"08:00:2b:01:02:03:04:05",
96+
},
97+
{
98+
"0800-2b01-0203-0405",
99+
"08:00:2b:01:02:03:04:05",
100+
},
101+
{
102+
"08002b01:02030405",
103+
"08:00:2b:01:02:03:04:05",
104+
},
105+
{
106+
"08002b0102030405",
107+
"08:00:2b:01:02:03:04:05",
108+
},
109+
} {
110+
addr = addr
111+
t.Run(addr.input, func(t *testing.T) {
112+
var cidr pqtype.Macaddr
113+
if err := db.QueryRow(fmt.Sprintf(`SELECT '%s'::macaddr8`, addr.input)).Scan(&cidr); err != nil {
114+
t.Fatal(err)
115+
}
116+
if diff := cmp.Diff(true, cidr.Valid); diff != "" {
117+
t.Errorf("valid mismatch (-want +got):\n%s", diff)
118+
}
119+
if diff := cmp.Diff(addr.output, cidr.Addr.String()); diff != "" {
120+
t.Errorf("mismatch (-want +got):\n%s", diff)
121+
}
122+
})
123+
}
124+
t.Run("NULL", func(t *testing.T) {
125+
var cidr pqtype.Macaddr
126+
if err := db.QueryRow(fmt.Sprintf(`SELECT NULL::macaddr8`)).Scan(&cidr); err != nil {
127+
t.Fatal(err)
128+
}
129+
if diff := cmp.Diff(false, cidr.Valid); diff != "" {
130+
t.Errorf("valid mismatch (-want +got):\n%s", diff)
131+
}
132+
})
133+
}

0 commit comments

Comments
 (0)