Skip to content

Commit

Permalink
Fix large objects functionality when PreferSimpleProtocol = true
Browse files Browse the repository at this point in the history
fixes #651
  • Loading branch information
jackc committed Dec 27, 2019
1 parent be1a8e5 commit 9cb58fc
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 54 deletions.
63 changes: 9 additions & 54 deletions large_objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,15 @@ const (

// 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)
err := o.tx.QueryRow(ctx, "select lo_create($1)", 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)
err := o.tx.QueryRow(ctx, "select lo_open($1, $2)", oid, mode).Scan(&fd)
if err != nil {
return nil, err
}
Expand All @@ -51,13 +41,8 @@ func (o *LargeObjects) Open(ctx context.Context, oid uint32, mode LargeObjectMod

// 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)
err := o.tx.QueryRow(ctx, "select lo_unlink($1)", oid).Scan(&result)
if err != nil {
return err
}
Expand All @@ -84,13 +69,8 @@ type LargeObject struct {

// 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)
err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p).Scan(&n)
if err != nil {
return n, err
}
Expand All @@ -104,13 +84,8 @@ func (o *LargeObject) Write(p []byte) (int, error) {

// 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)
err := o.tx.QueryRow(o.ctx, "select loread($1, $2)", o.fd, len(p)).Scan(&res)
copy(p, res)
if err != nil {
return len(res), err
Expand All @@ -124,44 +99,24 @@ func (o *LargeObject) Read(p []byte) (int, error) {

// 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)
err = o.tx.QueryRow(o.ctx, "select lo_lseek64($1, $2, $3)", 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)
err = o.tx.QueryRow(o.ctx, "select lo_tell64($1)", 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)
_, err = o.tx.Exec(o.ctx, "select lo_truncate64($1, $2)", 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)
_, err := o.tx.Exec(o.ctx, "select lo_close($1)", o.fd)
return err
}
30 changes: 30 additions & 0 deletions large_objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,36 @@ func TestLargeObjects(t *testing.T) {
t.Fatal(err)
}

testLargeObjects(t, ctx, tx)
}

func TestLargeObjectsPreferSimpleProtocol(t *testing.T) {
t.Parallel()

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

config, err := pgx.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
if err != nil {
t.Fatal(err)
}

config.PreferSimpleProtocol = true

conn, err := pgx.ConnectConfig(ctx, config)
if err != nil {
t.Fatal(err)
}

tx, err := conn.Begin(ctx)
if err != nil {
t.Fatal(err)
}

testLargeObjects(t, ctx, tx)
}

func testLargeObjects(t *testing.T, ctx context.Context, tx pgx.Tx) {
lo := tx.LargeObjects()

id, err := lo.Create(ctx, 0)
Expand Down

0 comments on commit 9cb58fc

Please sign in to comment.