mirror of https://github.com/jackc/pgx.git
parent
7ec8d7b343
commit
ba5f97176a
40
conn.go
40
conn.go
|
@ -74,11 +74,11 @@ type Conn struct {
|
||||||
lastActivityTime time.Time // the last time the connection was used
|
lastActivityTime time.Time // the last time the connection was used
|
||||||
wbuf [1024]byte
|
wbuf [1024]byte
|
||||||
writeBuf WriteBuf
|
writeBuf WriteBuf
|
||||||
pid int32 // backend pid
|
pid int32 // backend pid
|
||||||
secretKey int32 // key to use to send a cancel query message to the server
|
secretKey int32 // key to use to send a cancel query message to the server
|
||||||
RuntimeParams map[string]string // parameters that have been reported by the server
|
RuntimeParams map[string]string // parameters that have been reported by the server
|
||||||
PgTypes map[Oid]PgType // oids to PgTypes
|
PgTypes map[pgtype.Oid]PgType // oids to PgTypes
|
||||||
config ConnConfig // config used when establishing this connection
|
config ConnConfig // config used when establishing this connection
|
||||||
txStatus byte
|
txStatus byte
|
||||||
preparedStatements map[string]*PreparedStatement
|
preparedStatements map[string]*PreparedStatement
|
||||||
channels map[string]struct{}
|
channels map[string]struct{}
|
||||||
|
@ -102,7 +102,7 @@ type Conn struct {
|
||||||
doneChan chan struct{}
|
doneChan chan struct{}
|
||||||
closedChan chan error
|
closedChan chan error
|
||||||
|
|
||||||
oidPgtypeValues map[Oid]pgtype.Value
|
oidPgtypeValues map[pgtype.Oid]pgtype.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreparedStatement is a description of a prepared statement
|
// PreparedStatement is a description of a prepared statement
|
||||||
|
@ -110,12 +110,12 @@ type PreparedStatement struct {
|
||||||
Name string
|
Name string
|
||||||
SQL string
|
SQL string
|
||||||
FieldDescriptions []FieldDescription
|
FieldDescriptions []FieldDescription
|
||||||
ParameterOids []Oid
|
ParameterOids []pgtype.Oid
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareExOptions is an option struct that can be passed to PrepareEx
|
// PrepareExOptions is an option struct that can be passed to PrepareEx
|
||||||
type PrepareExOptions struct {
|
type PrepareExOptions struct {
|
||||||
ParameterOids []Oid
|
ParameterOids []pgtype.Oid
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification is a message received from the PostgreSQL LISTEN/NOTIFY system
|
// Notification is a message received from the PostgreSQL LISTEN/NOTIFY system
|
||||||
|
@ -180,13 +180,13 @@ func Connect(config ConnConfig) (c *Conn, err error) {
|
||||||
return connect(config, nil)
|
return connect(config, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(config ConnConfig, pgTypes map[Oid]PgType) (c *Conn, err error) {
|
func connect(config ConnConfig, pgTypes map[pgtype.Oid]PgType) (c *Conn, err error) {
|
||||||
c = new(Conn)
|
c = new(Conn)
|
||||||
|
|
||||||
c.config = config
|
c.config = config
|
||||||
|
|
||||||
if pgTypes != nil {
|
if pgTypes != nil {
|
||||||
c.PgTypes = make(map[Oid]PgType, len(pgTypes))
|
c.PgTypes = make(map[pgtype.Oid]PgType, len(pgTypes))
|
||||||
for k, v := range pgTypes {
|
for k, v := range pgTypes {
|
||||||
c.PgTypes[k] = v
|
c.PgTypes[k] = v
|
||||||
}
|
}
|
||||||
|
@ -361,7 +361,7 @@ where (
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.PgTypes = make(map[Oid]PgType, 128)
|
c.PgTypes = make(map[pgtype.Oid]PgType, 128)
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var oid uint32
|
var oid uint32
|
||||||
|
@ -372,14 +372,14 @@ where (
|
||||||
// The zero value is text format so we ignore any types without a default type format
|
// The zero value is text format so we ignore any types without a default type format
|
||||||
t.DefaultFormat, _ = DefaultTypeFormats[t.Name]
|
t.DefaultFormat, _ = DefaultTypeFormats[t.Name]
|
||||||
|
|
||||||
c.PgTypes[Oid(oid)] = t
|
c.PgTypes[pgtype.Oid(oid)] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) loadStaticOidPgtypeValues() {
|
func (c *Conn) loadStaticOidPgtypeValues() {
|
||||||
c.oidPgtypeValues = map[Oid]pgtype.Value{
|
c.oidPgtypeValues = map[pgtype.Oid]pgtype.Value{
|
||||||
AclitemArrayOid: &pgtype.AclitemArray{},
|
AclitemArrayOid: &pgtype.AclitemArray{},
|
||||||
AclitemOid: &pgtype.Aclitem{},
|
AclitemOid: &pgtype.Aclitem{},
|
||||||
BoolArrayOid: &pgtype.BoolArray{},
|
BoolArrayOid: &pgtype.BoolArray{},
|
||||||
|
@ -407,7 +407,7 @@ func (c *Conn) loadStaticOidPgtypeValues() {
|
||||||
JsonbOid: &pgtype.Jsonb{},
|
JsonbOid: &pgtype.Jsonb{},
|
||||||
JsonOid: &pgtype.Json{},
|
JsonOid: &pgtype.Json{},
|
||||||
NameOid: &pgtype.Name{},
|
NameOid: &pgtype.Name{},
|
||||||
OidOid: &pgtype.Oid{},
|
OidOid: &pgtype.OidValue{},
|
||||||
TextArrayOid: &pgtype.TextArray{},
|
TextArrayOid: &pgtype.TextArray{},
|
||||||
TextOid: &pgtype.Text{},
|
TextOid: &pgtype.Text{},
|
||||||
TidOid: &pgtype.Tid{},
|
TidOid: &pgtype.Tid{},
|
||||||
|
@ -422,7 +422,7 @@ func (c *Conn) loadStaticOidPgtypeValues() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) loadDynamicOidPgtypeValues() {
|
func (c *Conn) loadDynamicOidPgtypeValues() {
|
||||||
nameOids := make(map[string]Oid, len(c.PgTypes))
|
nameOids := make(map[string]pgtype.Oid, len(c.PgTypes))
|
||||||
for k, v := range c.PgTypes {
|
for k, v := range c.PgTypes {
|
||||||
nameOids[v.Name] = k
|
nameOids[v.Name] = k
|
||||||
}
|
}
|
||||||
|
@ -1204,9 +1204,9 @@ func (c *Conn) rxRowDescription(r *msgReader) (fields []FieldDescription) {
|
||||||
for i := int16(0); i < fieldCount; i++ {
|
for i := int16(0); i < fieldCount; i++ {
|
||||||
f := &fields[i]
|
f := &fields[i]
|
||||||
f.Name = r.readCString()
|
f.Name = r.readCString()
|
||||||
f.Table = Oid(r.readUint32())
|
f.Table = pgtype.Oid(r.readUint32())
|
||||||
f.AttributeNumber = r.readInt16()
|
f.AttributeNumber = r.readInt16()
|
||||||
f.DataType = Oid(r.readUint32())
|
f.DataType = pgtype.Oid(r.readUint32())
|
||||||
f.DataTypeSize = r.readInt16()
|
f.DataTypeSize = r.readInt16()
|
||||||
f.Modifier = r.readInt32()
|
f.Modifier = r.readInt32()
|
||||||
f.FormatCode = r.readInt16()
|
f.FormatCode = r.readInt16()
|
||||||
|
@ -1214,7 +1214,7 @@ func (c *Conn) rxRowDescription(r *msgReader) (fields []FieldDescription) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) rxParameterDescription(r *msgReader) (parameters []Oid) {
|
func (c *Conn) rxParameterDescription(r *msgReader) (parameters []pgtype.Oid) {
|
||||||
// Internally, PostgreSQL supports greater than 64k parameters to a prepared
|
// Internally, PostgreSQL supports greater than 64k parameters to a prepared
|
||||||
// statement. But the parameter description uses a 16-bit integer for the
|
// statement. But the parameter description uses a 16-bit integer for the
|
||||||
// count of parameters. If there are more than 64K parameters, this count is
|
// count of parameters. If there are more than 64K parameters, this count is
|
||||||
|
@ -1223,10 +1223,10 @@ func (c *Conn) rxParameterDescription(r *msgReader) (parameters []Oid) {
|
||||||
r.readInt16()
|
r.readInt16()
|
||||||
parameterCount := len(r.msgBody[r.rp:]) / 4
|
parameterCount := len(r.msgBody[r.rp:]) / 4
|
||||||
|
|
||||||
parameters = make([]Oid, 0, parameterCount)
|
parameters = make([]pgtype.Oid, 0, parameterCount)
|
||||||
|
|
||||||
for i := 0; i < parameterCount; i++ {
|
for i := 0; i < parameterCount; i++ {
|
||||||
parameters = append(parameters, Oid(r.readUint32()))
|
parameters = append(parameters, pgtype.Oid(r.readUint32()))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConnPoolConfig struct {
|
type ConnPoolConfig struct {
|
||||||
|
@ -28,7 +30,7 @@ type ConnPool struct {
|
||||||
closed bool
|
closed bool
|
||||||
preparedStatements map[string]*PreparedStatement
|
preparedStatements map[string]*PreparedStatement
|
||||||
acquireTimeout time.Duration
|
acquireTimeout time.Duration
|
||||||
pgTypes map[Oid]PgType
|
pgTypes map[pgtype.Oid]PgType
|
||||||
txAfterClose func(tx *Tx)
|
txAfterClose func(tx *Tx)
|
||||||
rowsAfterClose func(rows *Rows)
|
rowsAfterClose func(rows *Rows)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx"
|
"github.com/jackc/pgx"
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConnect(t *testing.T) {
|
func TestConnect(t *testing.T) {
|
||||||
|
@ -1042,7 +1043,7 @@ func TestPrepareEx(t *testing.T) {
|
||||||
conn := mustConnect(t, *defaultConnConfig)
|
conn := mustConnect(t, *defaultConnConfig)
|
||||||
defer closeConn(t, conn)
|
defer closeConn(t, conn)
|
||||||
|
|
||||||
_, err := conn.PrepareEx("test", "select $1", &pgx.PrepareExOptions{ParameterOids: []pgx.Oid{pgx.TextOid}})
|
_, err := conn.PrepareEx("test", "select $1", &pgx.PrepareExOptions{ParameterOids: []pgtype.Oid{pgx.TextOid}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unable to prepare statement: %v", err)
|
t.Errorf("Unable to prepare statement: %v", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx"
|
"github.com/jackc/pgx"
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConnCopyToSmall(t *testing.T) {
|
func TestConnCopyToSmall(t *testing.T) {
|
||||||
|
@ -125,7 +126,7 @@ func TestConnCopyToJSON(t *testing.T) {
|
||||||
conn := mustConnect(t, *defaultConnConfig)
|
conn := mustConnect(t, *defaultConnConfig)
|
||||||
defer closeConn(t, conn)
|
defer closeConn(t, conn)
|
||||||
|
|
||||||
for _, oid := range []pgx.Oid{pgx.JsonOid, pgx.JsonbOid} {
|
for _, oid := range []pgtype.Oid{pgx.JsonOid, pgx.JsonbOid} {
|
||||||
if _, ok := conn.PgTypes[oid]; !ok {
|
if _, ok := conn.PgTypes[oid]; !ok {
|
||||||
return // No JSON/JSONB type -- must be running against old PostgreSQL
|
return // No JSON/JSONB type -- must be running against old PostgreSQL
|
||||||
}
|
}
|
||||||
|
|
14
fastpath.go
14
fastpath.go
|
@ -2,29 +2,31 @@ package pgx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newFastpath(cn *Conn) *fastpath {
|
func newFastpath(cn *Conn) *fastpath {
|
||||||
return &fastpath{cn: cn, fns: make(map[string]Oid)}
|
return &fastpath{cn: cn, fns: make(map[string]pgtype.Oid)}
|
||||||
}
|
}
|
||||||
|
|
||||||
type fastpath struct {
|
type fastpath struct {
|
||||||
cn *Conn
|
cn *Conn
|
||||||
fns map[string]Oid
|
fns map[string]pgtype.Oid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fastpath) functionOid(name string) Oid {
|
func (f *fastpath) functionOid(name string) pgtype.Oid {
|
||||||
return f.fns[name]
|
return f.fns[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fastpath) addFunction(name string, oid Oid) {
|
func (f *fastpath) addFunction(name string, oid pgtype.Oid) {
|
||||||
f.fns[name] = oid
|
f.fns[name] = oid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fastpath) addFunctions(rows *Rows) error {
|
func (f *fastpath) addFunctions(rows *Rows) error {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var name string
|
var name string
|
||||||
var oid Oid
|
var oid pgtype.Oid
|
||||||
if err := rows.Scan(&name, &oid); err != nil {
|
if err := rows.Scan(&name, &oid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -47,7 +49,7 @@ func fpInt64Arg(n int64) fpArg {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fastpath) Call(oid Oid, args []fpArg) (res []byte, err error) {
|
func (f *fastpath) Call(oid pgtype.Oid, args []fpArg) (res []byte, err error) {
|
||||||
if err := f.cn.ensureConnectionReadyForQuery(); err != nil {
|
if err := f.cn.ensureConnectionReadyForQuery(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package pgx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LargeObjects is a structure used to access the large objects API. It is only
|
// LargeObjects is a structure used to access the large objects API. It is only
|
||||||
|
@ -60,19 +62,19 @@ const (
|
||||||
|
|
||||||
// Create creates a new large object. If id is zero, the server assigns an
|
// Create creates a new large object. If id is zero, the server assigns an
|
||||||
// unused Oid.
|
// unused Oid.
|
||||||
func (o *LargeObjects) Create(id Oid) (Oid, error) {
|
func (o *LargeObjects) Create(id pgtype.Oid) (pgtype.Oid, error) {
|
||||||
newOid, err := fpInt32(o.fp.CallFn("lo_create", []fpArg{fpIntArg(int32(id))}))
|
newOid, err := fpInt32(o.fp.CallFn("lo_create", []fpArg{fpIntArg(int32(id))}))
|
||||||
return Oid(newOid), err
|
return pgtype.Oid(newOid), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open opens an existing large object with the given mode.
|
// Open opens an existing large object with the given mode.
|
||||||
func (o *LargeObjects) Open(oid Oid, mode LargeObjectMode) (*LargeObject, error) {
|
func (o *LargeObjects) Open(oid pgtype.Oid, mode LargeObjectMode) (*LargeObject, error) {
|
||||||
fd, err := fpInt32(o.fp.CallFn("lo_open", []fpArg{fpIntArg(int32(oid)), fpIntArg(int32(mode))}))
|
fd, err := fpInt32(o.fp.CallFn("lo_open", []fpArg{fpIntArg(int32(oid)), fpIntArg(int32(mode))}))
|
||||||
return &LargeObject{fd: fd, lo: o}, err
|
return &LargeObject{fd: fd, lo: o}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlink removes a large object from the database.
|
// Unlink removes a large object from the database.
|
||||||
func (o *LargeObjects) Unlink(oid Oid) error {
|
func (o *LargeObjects) Unlink(oid pgtype.Oid) error {
|
||||||
_, err := o.fp.CallFn("lo_unlink", []fpArg{fpIntArg(int32(oid))})
|
_, err := o.fp.CallFn("lo_unlink", []fpArg{fpIntArg(int32(oid))})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package pgx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -55,9 +57,9 @@ func (s *startupMessage) Bytes() (buf []byte) {
|
||||||
|
|
||||||
type FieldDescription struct {
|
type FieldDescription struct {
|
||||||
Name string
|
Name string
|
||||||
Table Oid
|
Table pgtype.Oid
|
||||||
AttributeNumber int16
|
AttributeNumber int16
|
||||||
DataType Oid
|
DataType pgtype.Oid
|
||||||
DataTypeSize int16
|
DataTypeSize int16
|
||||||
DataTypeName string
|
DataTypeName string
|
||||||
Modifier int32
|
Modifier int32
|
||||||
|
|
|
@ -1,45 +1,57 @@
|
||||||
package pgtype
|
package pgtype
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgio"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Oid (Object Identifier Type) is, according to
|
// Oid (Object Identifier Type) is, according to
|
||||||
// https://www.postgresql.org/docs/current/static/datatype-oid.html, used
|
// https://www.postgresql.org/docs/current/static/datatype-oid.html, used
|
||||||
// internally by PostgreSQL as a primary key for various system tables. It is
|
// internally by PostgreSQL as a primary key for various system tables. It is
|
||||||
// currently implemented as an unsigned four-byte integer. Its definition can be
|
// currently implemented as an unsigned four-byte integer. Its definition can be
|
||||||
// found in src/include/postgres_ext.h in the PostgreSQL sources.
|
// found in src/include/postgres_ext.h in the PostgreSQL sources. Because it is
|
||||||
type Oid pguint32
|
// so frequently required to be in a NOT NULL condition Oid cannot be NULL. To
|
||||||
|
// allow for NULL Oids use OidValue.
|
||||||
// Set converts from src to dst. Note that as Oid is not a general
|
type Oid uint32
|
||||||
// number type Set does not do automatic type conversion as other number
|
|
||||||
// types do.
|
|
||||||
func (dst *Oid) Set(src interface{}) error {
|
|
||||||
return (*pguint32)(dst).Set(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dst *Oid) Get() interface{} {
|
|
||||||
return (*pguint32)(dst).Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssignTo assigns from src to dst. Note that as Oid is not a general number
|
|
||||||
// type AssignTo does not do automatic type conversion as other number types do.
|
|
||||||
func (src *Oid) AssignTo(dst interface{}) error {
|
|
||||||
return (*pguint32)(src).AssignTo(dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dst *Oid) DecodeText(src []byte) error {
|
func (dst *Oid) DecodeText(src []byte) error {
|
||||||
return (*pguint32)(dst).DecodeText(src)
|
if src == nil {
|
||||||
|
return fmt.Errorf("cannot decode nil into Oid")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := strconv.ParseUint(string(src), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = Oid(n)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dst *Oid) DecodeBinary(src []byte) error {
|
func (dst *Oid) DecodeBinary(src []byte) error {
|
||||||
return (*pguint32)(dst).DecodeBinary(src)
|
if src == nil {
|
||||||
|
return fmt.Errorf("cannot decode nil into Oid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src) != 4 {
|
||||||
|
return fmt.Errorf("invalid length: %v", len(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
n := binary.BigEndian.Uint32(src)
|
||||||
|
*dst = Oid(n)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src Oid) EncodeText(w io.Writer) (bool, error) {
|
func (src Oid) EncodeText(w io.Writer) (bool, error) {
|
||||||
return (pguint32)(src).EncodeText(w)
|
_, err := io.WriteString(w, strconv.FormatUint(uint64(src), 10))
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src Oid) EncodeBinary(w io.Writer) (bool, error) {
|
func (src Oid) EncodeBinary(w io.Writer) (bool, error) {
|
||||||
return (pguint32)(src).EncodeBinary(w)
|
_, err := pgio.WriteUint32(w, uint32(src))
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package pgtype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OidValue (Object Identifier Type) is, according to
|
||||||
|
// https://www.postgresql.org/docs/current/static/datatype-OidValue.html, used
|
||||||
|
// internally by PostgreSQL as a primary key for various system tables. It is
|
||||||
|
// currently implemented as an unsigned four-byte integer. Its definition can be
|
||||||
|
// found in src/include/postgres_ext.h in the PostgreSQL sources.
|
||||||
|
type OidValue pguint32
|
||||||
|
|
||||||
|
// Set converts from src to dst. Note that as OidValue is not a general
|
||||||
|
// number type Set does not do automatic type conversion as other number
|
||||||
|
// types do.
|
||||||
|
func (dst *OidValue) Set(src interface{}) error {
|
||||||
|
return (*pguint32)(dst).Set(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *OidValue) Get() interface{} {
|
||||||
|
return (*pguint32)(dst).Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignTo assigns from src to dst. Note that as OidValue is not a general number
|
||||||
|
// type AssignTo does not do automatic type conversion as other number types do.
|
||||||
|
func (src *OidValue) AssignTo(dst interface{}) error {
|
||||||
|
return (*pguint32)(src).AssignTo(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *OidValue) DecodeText(src []byte) error {
|
||||||
|
return (*pguint32)(dst).DecodeText(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *OidValue) DecodeBinary(src []byte) error {
|
||||||
|
return (*pguint32)(dst).DecodeBinary(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src OidValue) EncodeText(w io.Writer) (bool, error) {
|
||||||
|
return (pguint32)(src).EncodeText(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src OidValue) EncodeBinary(w io.Writer) (bool, error) {
|
||||||
|
return (pguint32)(src).EncodeBinary(w)
|
||||||
|
}
|
|
@ -7,23 +7,23 @@ import (
|
||||||
"github.com/jackc/pgx/pgtype"
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOidTranscode(t *testing.T) {
|
func TestOidValueTranscode(t *testing.T) {
|
||||||
testSuccessfulTranscode(t, "oid", []interface{}{
|
testSuccessfulTranscode(t, "oid", []interface{}{
|
||||||
pgtype.Oid{Uint: 42, Status: pgtype.Present},
|
pgtype.OidValue{Uint: 42, Status: pgtype.Present},
|
||||||
pgtype.Oid{Status: pgtype.Null},
|
pgtype.OidValue{Status: pgtype.Null},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOidSet(t *testing.T) {
|
func TestOidValueSet(t *testing.T) {
|
||||||
successfulTests := []struct {
|
successfulTests := []struct {
|
||||||
source interface{}
|
source interface{}
|
||||||
result pgtype.Oid
|
result pgtype.OidValue
|
||||||
}{
|
}{
|
||||||
{source: uint32(1), result: pgtype.Oid{Uint: 1, Status: pgtype.Present}},
|
{source: uint32(1), result: pgtype.OidValue{Uint: 1, Status: pgtype.Present}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range successfulTests {
|
for i, tt := range successfulTests {
|
||||||
var r pgtype.Oid
|
var r pgtype.OidValue
|
||||||
err := r.Set(tt.source)
|
err := r.Set(tt.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%d: %v", i, err)
|
t.Errorf("%d: %v", i, err)
|
||||||
|
@ -35,17 +35,17 @@ func TestOidSet(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOidAssignTo(t *testing.T) {
|
func TestOidValueAssignTo(t *testing.T) {
|
||||||
var ui32 uint32
|
var ui32 uint32
|
||||||
var pui32 *uint32
|
var pui32 *uint32
|
||||||
|
|
||||||
simpleTests := []struct {
|
simpleTests := []struct {
|
||||||
src pgtype.Oid
|
src pgtype.OidValue
|
||||||
dst interface{}
|
dst interface{}
|
||||||
expected interface{}
|
expected interface{}
|
||||||
}{
|
}{
|
||||||
{src: pgtype.Oid{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
{src: pgtype.OidValue{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||||
{src: pgtype.Oid{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))},
|
{src: pgtype.OidValue{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range simpleTests {
|
for i, tt := range simpleTests {
|
||||||
|
@ -60,11 +60,11 @@ func TestOidAssignTo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerAllocTests := []struct {
|
pointerAllocTests := []struct {
|
||||||
src pgtype.Oid
|
src pgtype.OidValue
|
||||||
dst interface{}
|
dst interface{}
|
||||||
expected interface{}
|
expected interface{}
|
||||||
}{
|
}{
|
||||||
{src: pgtype.Oid{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)},
|
{src: pgtype.OidValue{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range pointerAllocTests {
|
for i, tt := range pointerAllocTests {
|
||||||
|
@ -79,10 +79,10 @@ func TestOidAssignTo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
errorTests := []struct {
|
errorTests := []struct {
|
||||||
src pgtype.Oid
|
src pgtype.OidValue
|
||||||
dst interface{}
|
dst interface{}
|
||||||
}{
|
}{
|
||||||
{src: pgtype.Oid{Status: pgtype.Null}, dst: &ui32},
|
{src: pgtype.OidValue{Status: pgtype.Null}, dst: &ui32},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range errorTests {
|
for i, tt := range errorTests {
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx"
|
"github.com/jackc/pgx"
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -335,7 +336,7 @@ func TestQueryRowCoreTypes(t *testing.T) {
|
||||||
f64 float64
|
f64 float64
|
||||||
b bool
|
b bool
|
||||||
t time.Time
|
t time.Time
|
||||||
oid pgx.Oid
|
oid pgtype.Oid
|
||||||
}
|
}
|
||||||
|
|
||||||
var actual, zero allTypes
|
var actual, zero allTypes
|
||||||
|
@ -353,7 +354,7 @@ func TestQueryRowCoreTypes(t *testing.T) {
|
||||||
{"select $1::timestamptz", []interface{}{time.Unix(123, 5000)}, []interface{}{&actual.t}, allTypes{t: time.Unix(123, 5000)}},
|
{"select $1::timestamptz", []interface{}{time.Unix(123, 5000)}, []interface{}{&actual.t}, allTypes{t: time.Unix(123, 5000)}},
|
||||||
{"select $1::timestamp", []interface{}{time.Date(2010, 1, 2, 3, 4, 5, 0, time.UTC)}, []interface{}{&actual.t}, allTypes{t: time.Date(2010, 1, 2, 3, 4, 5, 0, time.UTC)}},
|
{"select $1::timestamp", []interface{}{time.Date(2010, 1, 2, 3, 4, 5, 0, time.UTC)}, []interface{}{&actual.t}, allTypes{t: time.Date(2010, 1, 2, 3, 4, 5, 0, time.UTC)}},
|
||||||
{"select $1::date", []interface{}{time.Date(1987, 1, 2, 0, 0, 0, 0, time.UTC)}, []interface{}{&actual.t}, allTypes{t: time.Date(1987, 1, 2, 0, 0, 0, 0, time.UTC)}},
|
{"select $1::date", []interface{}{time.Date(1987, 1, 2, 0, 0, 0, 0, time.UTC)}, []interface{}{&actual.t}, allTypes{t: time.Date(1987, 1, 2, 0, 0, 0, 0, time.UTC)}},
|
||||||
{"select $1::oid", []interface{}{pgx.Oid(42)}, []interface{}{&actual.oid}, allTypes{oid: 42}},
|
{"select $1::oid", []interface{}{pgtype.Oid(42)}, []interface{}{&actual.oid}, allTypes{oid: 42}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
|
|
@ -52,19 +52,20 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/jackc/pgx"
|
"github.com/jackc/pgx"
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
var openFromConnPoolCount int
|
var openFromConnPoolCount int
|
||||||
|
|
||||||
// oids that map to intrinsic database/sql types. These will be allowed to be
|
// oids that map to intrinsic database/sql types. These will be allowed to be
|
||||||
// binary, anything else will be forced to text format
|
// binary, anything else will be forced to text format
|
||||||
var databaseSqlOids map[pgx.Oid]bool
|
var databaseSqlOids map[pgtype.Oid]bool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
d := &Driver{}
|
d := &Driver{}
|
||||||
sql.Register("pgx", d)
|
sql.Register("pgx", d)
|
||||||
|
|
||||||
databaseSqlOids = make(map[pgx.Oid]bool)
|
databaseSqlOids = make(map[pgtype.Oid]bool)
|
||||||
databaseSqlOids[pgx.BoolOid] = true
|
databaseSqlOids[pgx.BoolOid] = true
|
||||||
databaseSqlOids[pgx.ByteaOid] = true
|
databaseSqlOids[pgx.ByteaOid] = true
|
||||||
databaseSqlOids[pgx.Int2Oid] = true
|
databaseSqlOids[pgx.Int2Oid] = true
|
||||||
|
|
|
@ -2,6 +2,8 @@ package pgx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValueReader is used by the Scanner interface to decode values.
|
// ValueReader is used by the Scanner interface to decode values.
|
||||||
|
@ -116,8 +118,8 @@ func (r *ValueReader) ReadInt64() int64 {
|
||||||
return r.mr.readInt64()
|
return r.mr.readInt64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ValueReader) ReadOid() Oid {
|
func (r *ValueReader) ReadOid() pgtype.Oid {
|
||||||
return Oid(r.ReadUint32())
|
return pgtype.Oid(r.ReadUint32())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadString reads count bytes and returns as string
|
// ReadString reads count bytes and returns as string
|
||||||
|
|
68
values.go
68
values.go
|
@ -3,16 +3,12 @@ package pgx
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/pgio"
|
|
||||||
"github.com/jackc/pgx/pgtype"
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,7 +76,7 @@ const minInt = -maxInt - 1
|
||||||
var DefaultTypeFormats map[string]int16
|
var DefaultTypeFormats map[string]int16
|
||||||
|
|
||||||
// internalNativeGoTypeFormats lists the encoding type for native Go types (not handled with Encoder interface)
|
// internalNativeGoTypeFormats lists the encoding type for native Go types (not handled with Encoder interface)
|
||||||
var internalNativeGoTypeFormats map[Oid]int16
|
var internalNativeGoTypeFormats map[pgtype.Oid]int16
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
DefaultTypeFormats = map[string]int16{
|
DefaultTypeFormats = map[string]int16{
|
||||||
|
@ -119,7 +115,7 @@ func init() {
|
||||||
"xid": BinaryFormatCode,
|
"xid": BinaryFormatCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
internalNativeGoTypeFormats = map[Oid]int16{
|
internalNativeGoTypeFormats = map[pgtype.Oid]int16{
|
||||||
BoolArrayOid: BinaryFormatCode,
|
BoolArrayOid: BinaryFormatCode,
|
||||||
BoolOid: BinaryFormatCode,
|
BoolOid: BinaryFormatCode,
|
||||||
ByteaArrayOid: BinaryFormatCode,
|
ByteaArrayOid: BinaryFormatCode,
|
||||||
|
@ -159,54 +155,10 @@ func (e SerializationError) Error() string {
|
||||||
return string(e)
|
return string(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oid (Object Identifier Type) is, according to https://www.postgresql.org/docs/current/static/datatype-oid.html,
|
|
||||||
// used internally by PostgreSQL as a primary key for various system tables. It is currently implemented
|
|
||||||
// as an unsigned four-byte integer. Its definition can be found in src/include/postgres_ext.h
|
|
||||||
// in the PostgreSQL sources. Oid cannot be NULL. To allow for NULL Oids use pgtype.Oid.
|
|
||||||
type Oid uint32
|
|
||||||
|
|
||||||
func (dst *Oid) DecodeText(src []byte) error {
|
|
||||||
if src == nil {
|
|
||||||
return fmt.Errorf("cannot decode nil into Oid")
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := strconv.ParseUint(string(src), 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*dst = Oid(n)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dst *Oid) DecodeBinary(src []byte) error {
|
|
||||||
if src == nil {
|
|
||||||
return fmt.Errorf("cannot decode nil into Oid")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(src) != 4 {
|
|
||||||
return fmt.Errorf("invalid length: %v", len(src))
|
|
||||||
}
|
|
||||||
|
|
||||||
n := binary.BigEndian.Uint32(src)
|
|
||||||
*dst = Oid(n)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (src Oid) EncodeText(w io.Writer) (bool, error) {
|
|
||||||
_, err := io.WriteString(w, strconv.FormatUint(uint64(src), 10))
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (src Oid) EncodeBinary(w io.Writer) (bool, error) {
|
|
||||||
_, err := pgio.WriteUint32(w, uint32(src))
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encodes arg into wbuf as the type oid. This allows implementations
|
// Encode encodes arg into wbuf as the type oid. This allows implementations
|
||||||
// of the Encoder interface to delegate the actual work of encoding to the
|
// of the Encoder interface to delegate the actual work of encoding to the
|
||||||
// built-in functionality.
|
// built-in functionality.
|
||||||
func Encode(wbuf *WriteBuf, oid Oid, arg interface{}) error {
|
func Encode(wbuf *WriteBuf, oid pgtype.Oid, arg interface{}) error {
|
||||||
if arg == nil {
|
if arg == nil {
|
||||||
wbuf.WriteInt32(-1)
|
wbuf.WriteInt32(-1)
|
||||||
return nil
|
return nil
|
||||||
|
@ -542,7 +494,7 @@ func decodeFloat4(vr *ValueReader) float32 {
|
||||||
return math.Float32frombits(uint32(i))
|
return math.Float32frombits(uint32(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeFloat32(w *WriteBuf, oid Oid, value float32) error {
|
func encodeFloat32(w *WriteBuf, oid pgtype.Oid, value float32) error {
|
||||||
switch oid {
|
switch oid {
|
||||||
case Float4Oid:
|
case Float4Oid:
|
||||||
w.WriteInt32(4)
|
w.WriteInt32(4)
|
||||||
|
@ -582,7 +534,7 @@ func decodeFloat8(vr *ValueReader) float64 {
|
||||||
return math.Float64frombits(uint64(i))
|
return math.Float64frombits(uint64(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeFloat64(w *WriteBuf, oid Oid, value float64) error {
|
func encodeFloat64(w *WriteBuf, oid pgtype.Oid, value float64) error {
|
||||||
switch oid {
|
switch oid {
|
||||||
case Float8Oid:
|
case Float8Oid:
|
||||||
w.WriteInt32(8)
|
w.WriteInt32(8)
|
||||||
|
@ -617,7 +569,7 @@ func decodeTextAllowBinary(vr *ValueReader) string {
|
||||||
return vr.ReadString(vr.Len())
|
return vr.ReadString(vr.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeString(w *WriteBuf, oid Oid, value string) error {
|
func encodeString(w *WriteBuf, oid pgtype.Oid, value string) error {
|
||||||
w.WriteInt32(int32(len(value)))
|
w.WriteInt32(int32(len(value)))
|
||||||
w.WriteBytes([]byte(value))
|
w.WriteBytes([]byte(value))
|
||||||
return nil
|
return nil
|
||||||
|
@ -641,7 +593,7 @@ func decodeBytea(vr *ValueReader) []byte {
|
||||||
return vr.ReadBytes(vr.Len())
|
return vr.ReadBytes(vr.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeByteSlice(w *WriteBuf, oid Oid, value []byte) error {
|
func encodeByteSlice(w *WriteBuf, oid pgtype.Oid, value []byte) error {
|
||||||
w.WriteInt32(int32(len(value)))
|
w.WriteInt32(int32(len(value)))
|
||||||
w.WriteBytes(value)
|
w.WriteBytes(value)
|
||||||
|
|
||||||
|
@ -665,7 +617,7 @@ func decodeJSON(vr *ValueReader, d interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeJSON(w *WriteBuf, oid Oid, value interface{}) error {
|
func encodeJSON(w *WriteBuf, oid pgtype.Oid, value interface{}) error {
|
||||||
if oid != JsonOid {
|
if oid != JsonOid {
|
||||||
return fmt.Errorf("cannot encode JSON into oid %v", oid)
|
return fmt.Errorf("cannot encode JSON into oid %v", oid)
|
||||||
}
|
}
|
||||||
|
@ -709,7 +661,7 @@ func decodeJSONB(vr *ValueReader, d interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeJSONB(w *WriteBuf, oid Oid, value interface{}) error {
|
func encodeJSONB(w *WriteBuf, oid pgtype.Oid, value interface{}) error {
|
||||||
if oid != JsonbOid {
|
if oid != JsonbOid {
|
||||||
return fmt.Errorf("cannot encode JSON into oid %v", oid)
|
return fmt.Errorf("cannot encode JSON into oid %v", oid)
|
||||||
}
|
}
|
||||||
|
@ -757,7 +709,7 @@ func decodeDate(vr *ValueReader) time.Time {
|
||||||
return d.Time
|
return d.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeTime(w *WriteBuf, oid Oid, value time.Time) error {
|
func encodeTime(w *WriteBuf, oid pgtype.Oid, value time.Time) error {
|
||||||
switch oid {
|
switch oid {
|
||||||
case DateOid:
|
case DateOid:
|
||||||
var d pgtype.Date
|
var d pgtype.Date
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx"
|
"github.com/jackc/pgx"
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDateTranscode(t *testing.T) {
|
func TestDateTranscode(t *testing.T) {
|
||||||
|
@ -83,7 +84,7 @@ func TestJSONAndJSONBTranscode(t *testing.T) {
|
||||||
conn := mustConnect(t, *defaultConnConfig)
|
conn := mustConnect(t, *defaultConnConfig)
|
||||||
defer closeConn(t, conn)
|
defer closeConn(t, conn)
|
||||||
|
|
||||||
for _, oid := range []pgx.Oid{pgx.JsonOid, pgx.JsonbOid} {
|
for _, oid := range []pgtype.Oid{pgx.JsonOid, pgx.JsonbOid} {
|
||||||
if _, ok := conn.PgTypes[oid]; !ok {
|
if _, ok := conn.PgTypes[oid]; !ok {
|
||||||
return // No JSON/JSONB type -- must be running against old PostgreSQL
|
return // No JSON/JSONB type -- must be running against old PostgreSQL
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue