mirror of
https://github.com/jackc/pgx.git
synced 2025-04-27 13:14:32 +00:00
Adds Name/NullName types
This commit is contained in:
parent
a9199847a8
commit
c25e3dd826
64
values.go
64
values.go
@ -19,6 +19,7 @@ const (
|
|||||||
BoolOid = 16
|
BoolOid = 16
|
||||||
ByteaOid = 17
|
ByteaOid = 17
|
||||||
CharOid = 18
|
CharOid = 18
|
||||||
|
NameOid = 19
|
||||||
Int8Oid = 20
|
Int8Oid = 20
|
||||||
Int2Oid = 21
|
Int2Oid = 21
|
||||||
Int4Oid = 23
|
Int4Oid = 23
|
||||||
@ -65,6 +66,12 @@ const maxUint = ^uint(0)
|
|||||||
const maxInt = int(maxUint >> 1)
|
const maxInt = int(maxUint >> 1)
|
||||||
const minInt = -maxInt - 1
|
const minInt = -maxInt - 1
|
||||||
|
|
||||||
|
// NameDataLen is the same as PostgreSQL's NAMEDATALEN, defined in
|
||||||
|
// src/include/pg_config_manual.h. It is how many bytes long identifiers
|
||||||
|
// are allowed to be, including the trailing '\0' at the end of C strings.
|
||||||
|
// (Identifieres are table names, column names, function names, etc.)
|
||||||
|
const NameDataLen = 64
|
||||||
|
|
||||||
// DefaultTypeFormats maps type names to their default requested format (text
|
// DefaultTypeFormats maps type names to their default requested format (text
|
||||||
// or binary). In theory the Scanner interface should be the one to determine
|
// or binary). In theory the Scanner interface should be the one to determine
|
||||||
// the format of the returned values. However, the query has already been
|
// the format of the returned values. However, the query has already been
|
||||||
@ -91,6 +98,7 @@ func init() {
|
|||||||
"bool": BinaryFormatCode,
|
"bool": BinaryFormatCode,
|
||||||
"bytea": BinaryFormatCode,
|
"bytea": BinaryFormatCode,
|
||||||
"char": BinaryFormatCode,
|
"char": BinaryFormatCode,
|
||||||
|
"name": BinaryFormatCode,
|
||||||
"cidr": BinaryFormatCode,
|
"cidr": BinaryFormatCode,
|
||||||
"date": BinaryFormatCode,
|
"date": BinaryFormatCode,
|
||||||
"float4": BinaryFormatCode,
|
"float4": BinaryFormatCode,
|
||||||
@ -259,6 +267,55 @@ func (n NullString) Encode(w *WriteBuf, oid Oid) error {
|
|||||||
return encodeString(w, oid, n.String)
|
return encodeString(w, oid, n.String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The pgx.Name type is for PostgreSQL's special 63-byte
|
||||||
|
// name data type, used for identifiers like table names.
|
||||||
|
type Name string
|
||||||
|
|
||||||
|
// LengthOK is a convenience method that returns false if a name is longer
|
||||||
|
// than PostgreSQL will allow. PostgreSQL identifiers are allowed
|
||||||
|
// to be 63 bytes long (NAMEDATALEN in the PostgreSQL source code
|
||||||
|
// is defined as 64 bytes long, but the 64th char is the '\0' C
|
||||||
|
// string terminator.)
|
||||||
|
func (n Name) LengthOK() bool {
|
||||||
|
return len(string(n)) < NameDataLen
|
||||||
|
}
|
||||||
|
|
||||||
|
// NullName represents a pgx.Name that may be null. NullName 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 NullName struct {
|
||||||
|
Name Name
|
||||||
|
Valid bool // Valid is true if Char is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NullName) Scan(vr *ValueReader) error {
|
||||||
|
if vr.Type().DataType != NameOid {
|
||||||
|
return SerializationError(fmt.Sprintf("NullName.Scan cannot decode OID %d", vr.Type().DataType))
|
||||||
|
}
|
||||||
|
|
||||||
|
if vr.Len() == -1 {
|
||||||
|
n.Name, n.Valid = "", false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Valid = true
|
||||||
|
n.Name = Name(decodeText(vr))
|
||||||
|
return vr.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NullName) FormatCode() int16 { return TextFormatCode }
|
||||||
|
|
||||||
|
func (n NullName) Encode(w *WriteBuf, oid Oid) error {
|
||||||
|
if !n.Valid {
|
||||||
|
w.WriteInt32(-1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeString(w, oid, string(n.Name))
|
||||||
|
}
|
||||||
|
|
||||||
// The pgx.Char type is for PostgreSQL's special 8-bit-only
|
// The pgx.Char type is for PostgreSQL's special 8-bit-only
|
||||||
// "char" type more akin to the C language's char type, or Go's byte type.
|
// "char" type more akin to the C language's char type, or Go's byte type.
|
||||||
// (Note that the name in PostgreSQL itself is "char", in double-quotes,
|
// (Note that the name in PostgreSQL itself is "char", in double-quotes,
|
||||||
@ -906,6 +963,10 @@ func Encode(wbuf *WriteBuf, oid Oid, arg interface{}) error {
|
|||||||
return encodeUInt(wbuf, oid, arg)
|
return encodeUInt(wbuf, oid, arg)
|
||||||
case Char:
|
case Char:
|
||||||
return encodeChar(wbuf, oid, arg)
|
return encodeChar(wbuf, oid, 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
|
||||||
|
return encodeString(wbuf, oid, string(arg))
|
||||||
case int8:
|
case int8:
|
||||||
return encodeInt8(wbuf, oid, arg)
|
return encodeInt8(wbuf, oid, arg)
|
||||||
case uint8:
|
case uint8:
|
||||||
@ -1084,6 +1145,9 @@ func Decode(vr *ValueReader, d interface{}) error {
|
|||||||
*v = uint64(n)
|
*v = uint64(n)
|
||||||
case *Char:
|
case *Char:
|
||||||
*v = decodeChar(vr)
|
*v = decodeChar(vr)
|
||||||
|
case *Name:
|
||||||
|
// name goes over the wire just like text
|
||||||
|
*v = Name(decodeText(vr))
|
||||||
case *Oid:
|
case *Oid:
|
||||||
*v = decodeOid(vr)
|
*v = decodeOid(vr)
|
||||||
case *Xid:
|
case *Xid:
|
||||||
|
@ -551,6 +551,28 @@ func TestInetCidrTranscodeWithJustIP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNameLengthOK(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input pgx.Name
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"", true},
|
||||||
|
{"1234", true},
|
||||||
|
{"123456789012345678901234567890123456789012345678901234567890123", true},
|
||||||
|
{"1234567890123456789012345678901234567890123456789012345678901234", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual bool
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
actual = tt.input.LengthOK()
|
||||||
|
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Errorf("%d. Expected %v, got %v (name -> %v)", i, tt.expected, actual, tt.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNullX(t *testing.T) {
|
func TestNullX(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -562,6 +584,7 @@ func TestNullX(t *testing.T) {
|
|||||||
i16 pgx.NullInt16
|
i16 pgx.NullInt16
|
||||||
i32 pgx.NullInt32
|
i32 pgx.NullInt32
|
||||||
c pgx.NullChar
|
c pgx.NullChar
|
||||||
|
n pgx.NullName
|
||||||
oid pgx.NullOid
|
oid pgx.NullOid
|
||||||
xid pgx.NullXid
|
xid pgx.NullXid
|
||||||
cid pgx.NullCid
|
cid pgx.NullCid
|
||||||
@ -596,6 +619,11 @@ func TestNullX(t *testing.T) {
|
|||||||
{"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 1, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 1, Valid: true}}},
|
{"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 1, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 1, Valid: true}}},
|
||||||
{"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 1, Valid: false}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 0, Valid: false}}},
|
{"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 1, Valid: false}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 0, Valid: false}}},
|
||||||
{"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 255, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 255, Valid: true}}},
|
{"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.NullString{String: "foo", Valid: true}}, []interface{}{&actual.s}, allTypes{s: pgx.NullString{String: "foo", Valid: true}}},
|
||||||
|
{"select $1::name", []interface{}{pgx.NullString{String: "foo", Valid: false}}, []interface{}{&actual.s}, allTypes{s: pgx.NullString{String: "", Valid: false}}},
|
||||||
|
// bytes past NameDataLen-1 (63 bytes) get silently truncated by PostgreSQL
|
||||||
|
{"select $1::name", []interface{}{pgx.NullString{String: "1234567890123456789012345678901234567890123456789012345678901234", Valid: true}},
|
||||||
|
[]interface{}{&actual.s}, allTypes{s: pgx.NullString{String: "123456789012345678901234567890123456789012345678901234567890123", Valid: true}}},
|
||||||
{"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: 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: 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}}},
|
{"select $1::cid", []interface{}{pgx.NullCid{Cid: 4294967295, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 4294967295, Valid: true}}},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user