diff --git a/values.go b/values.go index f2e43473..12004433 100644 --- a/values.go +++ b/values.go @@ -44,6 +44,7 @@ const ( Int8ArrayOid = 1016 Float4ArrayOid = 1021 Float8ArrayOid = 1022 + AclItemOid = 1033 InetArrayOid = 1041 VarcharOid = 1043 DateOid = 1082 @@ -89,6 +90,7 @@ func init() { "_timestamp": BinaryFormatCode, "_timestamptz": BinaryFormatCode, "_varchar": BinaryFormatCode, + "aclitem": TextFormatCode, // Pg's src/backend/utils/adt/acl.c has only in/out (text) not send/recv (bin) "bool": BinaryFormatCode, "bytea": BinaryFormatCode, "char": BinaryFormatCode, @@ -263,6 +265,47 @@ func (n NullString) Encode(w *WriteBuf, oid Oid) error { return encodeString(w, oid, n.String) } +// AclItem is used for PostgreSQL's aclitem data type. +type AclItem string + +// NullAclItem represents a pgx.AclItem that may be null. NullAclItem implements the +// Scanner and Encoder interfaces so it may be used both as an argument to +// Query[Row] and a destination for Scan for prepared and unprepared queries. +// +// If Valid is false then the value is NULL. +type NullAclItem struct { + AclItem AclItem + Valid bool // Valid is true if AclItem is not NULL +} + +func (n *NullAclItem) Scan(vr *ValueReader) error { + if vr.Type().DataType != AclItemOid { + return SerializationError(fmt.Sprintf("NullAclItem.Scan cannot decode OID %d", vr.Type().DataType)) + } + + if vr.Len() == -1 { + n.AclItem, n.Valid = "", false + return nil + } + + n.Valid = true + n.AclItem = AclItem(decodeText(vr)) + return vr.Err() +} + +// Particularly important to return TextFormatCode, seeing as Postgres +// only ever sends aclitem as text, not binary. +func (n NullAclItem) FormatCode() int16 { return TextFormatCode } + +func (n NullAclItem) Encode(w *WriteBuf, oid Oid) error { + if !n.Valid { + w.WriteInt32(-1) + return nil + } + + return encodeString(w, oid, string(n.AclItem)) +} + // Name is a type used for PostgreSQL's special 63-byte // name data type, used for identifiers like table names. // The pg_class.relname column is a good example of where the @@ -964,6 +1007,10 @@ func Encode(wbuf *WriteBuf, oid Oid, arg interface{}) error { return encodeUInt(wbuf, oid, arg) case Char: return encodeChar(wbuf, oid, arg) + case AclItem: + // The aclitem data type goes over the wire using the same format as string, + // so just cast to string and use encodeString + return encodeString(wbuf, oid, string(arg)) case Name: // The name data type goes over the wire using the same format as string, // so just cast to string and use encodeString @@ -1146,6 +1193,9 @@ func Decode(vr *ValueReader, d interface{}) error { *v = uint64(n) case *Char: *v = decodeChar(vr) + case *AclItem: + // aclitem goes over the wire just like text + *v = AclItem(decodeText(vr)) case *Name: // name goes over the wire just like text *v = Name(decodeText(vr)) diff --git a/values_test.go b/values_test.go index ab40c65c..c198c57f 100644 --- a/values_test.go +++ b/values_test.go @@ -562,6 +562,7 @@ func TestNullX(t *testing.T) { i16 pgx.NullInt16 i32 pgx.NullInt32 c pgx.NullChar + a pgx.NullAclItem n pgx.NullName oid pgx.NullOid xid pgx.NullXid @@ -599,6 +600,8 @@ func TestNullX(t *testing.T) { {"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 255, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 255, Valid: true}}}, {"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: true}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "foo", Valid: true}}}, {"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: false}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "", Valid: false}}}, + {"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: true}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: true}}}, + {"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: false}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: "", Valid: false}}}, {"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 1, Valid: true}}}, {"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: false}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 0, Valid: false}}}, {"select $1::cid", []interface{}{pgx.NullCid{Cid: 4294967295, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 4294967295, Valid: true}}},