mirror of
https://github.com/jackc/pgx.git
synced 2025-04-27 13:14:32 +00:00
It was a mistake to use it in other contexts. This made interop difficult between pacakges that depended on pgtype such as pgx and packages that did not like pgconn and pgproto3. In particular this was awkward for prepared statements. This is preparation for removing pgx.PreparedStatement in favor of pgconn.PreparedStatement.
168 lines
4.1 KiB
Go
168 lines
4.1 KiB
Go
package pgx
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
|
|
errors "golang.org/x/xerrors"
|
|
)
|
|
|
|
// LargeObjects is a structure used to access the large objects API. It is only valid within the transaction where it
|
|
// was created.
|
|
//
|
|
// For more details see: http://www.postgresql.org/docs/current/static/largeobjects.html
|
|
type LargeObjects struct {
|
|
tx Tx
|
|
}
|
|
|
|
type LargeObjectMode int32
|
|
|
|
const (
|
|
LargeObjectModeWrite LargeObjectMode = 0x20000
|
|
LargeObjectModeRead LargeObjectMode = 0x40000
|
|
)
|
|
|
|
// Create creates a new large object. If oid is zero, the server assigns an unused OID.
|
|
func (o *LargeObjects) Create(ctx context.Context, oid uint32) (uint32, error) {
|
|
_, err := o.tx.Prepare(ctx, "lo_create", "select lo_create($1)")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
err = o.tx.QueryRow(ctx, "lo_create", oid).Scan(&oid)
|
|
return oid, err
|
|
}
|
|
|
|
// Open opens an existing large object with the given mode. ctx will also be used for all operations on the opened large
|
|
// object.
|
|
func (o *LargeObjects) Open(ctx context.Context, oid uint32, mode LargeObjectMode) (*LargeObject, error) {
|
|
_, err := o.tx.Prepare(ctx, "lo_open", "select lo_open($1, $2)")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var fd int32
|
|
err = o.tx.QueryRow(ctx, "lo_open", oid, mode).Scan(&fd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &LargeObject{fd: fd, tx: o.tx, ctx: ctx}, nil
|
|
}
|
|
|
|
// Unlink removes a large object from the database.
|
|
func (o *LargeObjects) Unlink(ctx context.Context, oid uint32) error {
|
|
_, err := o.tx.Prepare(ctx, "lo_unlink", "select lo_unlink($1)")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var result int32
|
|
err = o.tx.QueryRow(ctx, "lo_unlink", oid).Scan(&result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if result != 1 {
|
|
return errors.New("failed to remove large object")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// A LargeObject is a large object stored on the server. It is only valid within the transaction that it was initialized
|
|
// in. It uses the context it was initialized with for all operations. It implements these interfaces:
|
|
//
|
|
// io.Writer
|
|
// io.Reader
|
|
// io.Seeker
|
|
// io.Closer
|
|
type LargeObject struct {
|
|
ctx context.Context
|
|
tx Tx
|
|
fd int32
|
|
}
|
|
|
|
// Write writes p to the large object and returns the number of bytes written and an error if not all of p was written.
|
|
func (o *LargeObject) Write(p []byte) (int, error) {
|
|
_, err := o.tx.Prepare(o.ctx, "lowrite", "select lowrite($1, $2)")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var n int
|
|
err = o.tx.QueryRow(o.ctx, "lowrite", o.fd, p).Scan(&n)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
if n < 0 {
|
|
return 0, errors.New("failed to write to large object")
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// Read reads up to len(p) bytes into p returning the number of bytes read.
|
|
func (o *LargeObject) Read(p []byte) (int, error) {
|
|
_, err := o.tx.Prepare(o.ctx, "loread", "select loread($1, $2)")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var res []byte
|
|
err = o.tx.QueryRow(o.ctx, "loread", o.fd, len(p)).Scan(&res)
|
|
copy(p, res)
|
|
if err != nil {
|
|
return len(res), err
|
|
}
|
|
|
|
if len(res) < len(p) {
|
|
err = io.EOF
|
|
}
|
|
return len(res), err
|
|
}
|
|
|
|
// Seek moves the current location pointer to the new location specified by offset.
|
|
func (o *LargeObject) Seek(offset int64, whence int) (n int64, err error) {
|
|
_, err = o.tx.Prepare(o.ctx, "lo_lseek64", "select lo_lseek64($1, $2, $3)")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
err = o.tx.QueryRow(o.ctx, "lo_lseek64", o.fd, offset, whence).Scan(&n)
|
|
return n, err
|
|
}
|
|
|
|
// Tell returns the current read or write location of the large object descriptor.
|
|
func (o *LargeObject) Tell() (n int64, err error) {
|
|
_, err = o.tx.Prepare(o.ctx, "lo_tell64", "select lo_tell64($1)")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
err = o.tx.QueryRow(o.ctx, "lo_tell64", o.fd).Scan(&n)
|
|
return n, err
|
|
}
|
|
|
|
// Trunctes the large object to size.
|
|
func (o *LargeObject) Truncate(size int64) (err error) {
|
|
_, err = o.tx.Prepare(o.ctx, "lo_truncate64", "select lo_truncate64($1, $2)")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = o.tx.Exec(o.ctx, "lo_truncate64", o.fd, size)
|
|
return err
|
|
}
|
|
|
|
// Close closees the large object descriptor.
|
|
func (o *LargeObject) Close() error {
|
|
_, err := o.tx.Prepare(o.ctx, "lo_close", "select lo_close($1)")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = o.tx.Exec(o.ctx, "lo_close", o.fd)
|
|
return err
|
|
}
|