Files
sqlite3/blob.go

262 lines
5.4 KiB
Go
Raw Normal View History

2023-02-22 14:19:56 +00:00
package sqlite3
2023-03-29 15:01:25 +01:00
import (
"io"
"github.com/ncruces/go-sqlite3/internal/util"
)
2023-02-27 04:08:38 +00:00
2023-02-22 14:19:56 +00:00
// ZeroBlob represents a zero-filled, length n BLOB
// that can be used as an argument to
2023-02-22 17:51:30 +00:00
// [database/sql.DB.Exec] and similar methods.
2023-02-22 14:19:56 +00:00
type ZeroBlob int64
2023-02-27 04:08:38 +00:00
2023-05-11 15:19:57 +01:00
// Blob is an handle to an open BLOB.
2023-03-07 14:19:22 +00:00
//
2023-03-01 10:34:08 +00:00
// It implements [io.ReadWriteSeeker] for incremental BLOB I/O.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob.html
2023-02-27 04:08:38 +00:00
type Blob struct {
c *Conn
bytes int64
offset int64
2025-01-21 01:42:57 +00:00
handle ptr_t
bufptr ptr_t
2024-09-10 07:41:35 +01:00
buflen int64
2023-02-27 04:08:38 +00:00
}
var _ io.ReadWriteSeeker = &Blob{}
// OpenBlob opens a BLOB for incremental I/O.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_open.html
2023-02-27 13:45:32 +00:00
func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob, error) {
2023-11-29 10:38:03 +00:00
defer c.arena.mark()()
2023-02-27 04:08:38 +00:00
blobPtr := c.arena.new(ptrlen)
dbPtr := c.arena.string(db)
tablePtr := c.arena.string(table)
columnPtr := c.arena.string(column)
2025-01-21 01:42:57 +00:00
var flags int32
2023-02-27 04:08:38 +00:00
if write {
flags = 1
}
2024-10-04 13:31:53 +01:00
c.checkInterrupt(c.handle)
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_blob_open", stk_t(c.handle),
stk_t(dbPtr), stk_t(tablePtr), stk_t(columnPtr),
stk_t(row), stk_t(flags), stk_t(blobPtr)))
2023-02-27 04:08:38 +00:00
2025-01-21 01:42:57 +00:00
if err := c.error(rc); err != nil {
2023-02-27 04:08:38 +00:00
return nil, err
}
blob := Blob{c: c}
2025-01-21 01:42:57 +00:00
blob.handle = util.Read32[ptr_t](c.mod, blobPtr)
blob.bytes = int64(int32(c.call("sqlite3_blob_bytes", stk_t(blob.handle))))
2023-02-27 04:08:38 +00:00
return &blob, nil
}
// Close closes a BLOB handle.
//
2023-03-01 10:34:08 +00:00
// It is safe to close a nil, zero or closed Blob.
2023-02-27 04:08:38 +00:00
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_close.html
2023-02-27 04:08:38 +00:00
func (b *Blob) Close() error {
if b == nil || b.handle == 0 {
return nil
}
2025-01-21 01:42:57 +00:00
rc := res_t(b.c.call("sqlite3_blob_close", stk_t(b.handle)))
2024-09-10 07:41:35 +01:00
b.c.free(b.bufptr)
2023-02-27 04:08:38 +00:00
b.handle = 0
2025-01-21 01:42:57 +00:00
return b.c.error(rc)
2023-02-27 04:08:38 +00:00
}
// Size returns the size of the BLOB in bytes.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_bytes.html
2023-02-27 04:08:38 +00:00
func (b *Blob) Size() int64 {
return b.bytes
}
// Read implements the [io.Reader] interface.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_read.html
2023-02-27 04:08:38 +00:00
func (b *Blob) Read(p []byte) (n int, err error) {
if b.offset >= b.bytes {
return 0, io.EOF
}
2023-04-21 13:31:45 +01:00
want := int64(len(p))
2024-09-10 07:41:35 +01:00
avail := b.bytes - b.offset
2023-02-27 04:08:38 +00:00
if want > avail {
want = avail
}
2024-09-10 07:41:35 +01:00
if want > b.buflen {
2025-01-21 01:42:57 +00:00
b.bufptr = b.c.realloc(b.bufptr, want)
2024-09-10 07:41:35 +01:00
b.buflen = want
}
2023-02-27 04:08:38 +00:00
2025-01-21 01:42:57 +00:00
rc := res_t(b.c.call("sqlite3_blob_read", stk_t(b.handle),
stk_t(b.bufptr), stk_t(want), stk_t(b.offset)))
err = b.c.error(rc)
2023-02-27 04:08:38 +00:00
if err != nil {
return 0, err
}
b.offset += want
if b.offset >= b.bytes {
err = io.EOF
}
2023-04-21 13:31:45 +01:00
2025-01-21 01:42:57 +00:00
copy(p, util.View(b.c.mod, b.bufptr, want))
2023-02-27 04:08:38 +00:00
return int(want), err
}
2023-04-21 13:31:45 +01:00
// WriteTo implements the [io.WriterTo] interface.
2023-02-27 04:08:38 +00:00
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_read.html
2023-04-21 13:31:45 +01:00
func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
if b.offset >= b.bytes {
return 0, nil
}
2023-11-02 15:50:43 +00:00
want := int64(1024 * 1024)
2023-04-21 13:31:45 +01:00
avail := b.bytes - b.offset
if want > avail {
want = avail
2023-02-27 04:08:38 +00:00
}
2024-09-10 07:41:35 +01:00
if want > b.buflen {
2025-01-21 01:42:57 +00:00
b.bufptr = b.c.realloc(b.bufptr, want)
2024-09-10 07:41:35 +01:00
b.buflen = want
}
2023-02-27 04:08:38 +00:00
2023-04-21 13:31:45 +01:00
for want > 0 {
2025-01-21 01:42:57 +00:00
rc := res_t(b.c.call("sqlite3_blob_read", stk_t(b.handle),
stk_t(b.bufptr), stk_t(want), stk_t(b.offset)))
err = b.c.error(rc)
2023-04-21 13:31:45 +01:00
if err != nil {
return n, err
}
2025-01-21 01:42:57 +00:00
mem := util.View(b.c.mod, b.bufptr, want)
2023-04-21 13:31:45 +01:00
m, err := w.Write(mem[:want])
b.offset += int64(m)
n += int64(m)
if err != nil {
return n, err
}
if int64(m) != want {
2024-08-01 18:31:25 +01:00
// notest // Write misbehaving
2023-04-21 13:31:45 +01:00
return n, io.ErrShortWrite
}
avail = b.bytes - b.offset
if want > avail {
want = avail
}
}
return n, nil
}
// Write implements the [io.Writer] interface.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_write.html
2023-04-21 13:31:45 +01:00
func (b *Blob) Write(p []byte) (n int, err error) {
2024-09-10 07:41:35 +01:00
want := int64(len(p))
if want > b.buflen {
2025-01-21 01:42:57 +00:00
b.bufptr = b.c.realloc(b.bufptr, want)
2024-09-10 07:41:35 +01:00
b.buflen = want
}
util.WriteBytes(b.c.mod, b.bufptr, p)
2023-04-21 13:31:45 +01:00
2025-01-21 01:42:57 +00:00
rc := res_t(b.c.call("sqlite3_blob_write", stk_t(b.handle),
stk_t(b.bufptr), stk_t(want), stk_t(b.offset)))
err = b.c.error(rc)
2023-02-27 04:08:38 +00:00
if err != nil {
return 0, err
}
b.offset += int64(len(p))
return len(p), nil
}
2023-04-21 13:31:45 +01:00
// ReadFrom implements the [io.ReaderFrom] interface.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_write.html
2023-04-21 13:31:45 +01:00
func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) {
2023-11-02 15:50:43 +00:00
want := int64(1024 * 1024)
2023-04-21 13:31:45 +01:00
avail := b.bytes - b.offset
2023-11-02 15:50:43 +00:00
if l, ok := r.(*io.LimitedReader); ok && want > l.N {
want = l.N
}
2023-04-21 13:31:45 +01:00
if want > avail {
want = avail
}
if want < 1 {
want = 1
}
2024-09-10 07:41:35 +01:00
if want > b.buflen {
2025-01-21 01:42:57 +00:00
b.bufptr = b.c.realloc(b.bufptr, want)
2024-09-10 07:41:35 +01:00
b.buflen = want
}
2023-04-21 13:31:45 +01:00
for {
2025-01-21 01:42:57 +00:00
mem := util.View(b.c.mod, b.bufptr, want)
2023-04-21 13:31:45 +01:00
m, err := r.Read(mem[:want])
if m > 0 {
2025-01-21 01:42:57 +00:00
rc := res_t(b.c.call("sqlite3_blob_write", stk_t(b.handle),
stk_t(b.bufptr), stk_t(m), stk_t(b.offset)))
err := b.c.error(rc)
2023-04-21 13:31:45 +01:00
if err != nil {
return n, err
}
b.offset += int64(m)
n += int64(m)
}
if err == io.EOF {
return n, nil
}
if err != nil {
return n, err
}
avail = b.bytes - b.offset
if want > avail {
want = avail
}
if want < 1 {
want = 1
}
}
}
2023-02-27 04:08:38 +00:00
// Seek implements the [io.Seeker] interface.
func (b *Blob) Seek(offset int64, whence int) (int64, error) {
switch whence {
default:
2023-03-29 15:01:25 +01:00
return 0, util.WhenceErr
2023-02-27 04:08:38 +00:00
case io.SeekStart:
break
case io.SeekCurrent:
offset += b.offset
case io.SeekEnd:
offset += b.bytes
}
if offset < 0 {
2023-03-29 15:01:25 +01:00
return 0, util.OffsetErr
2023-02-27 04:08:38 +00:00
}
b.offset = offset
return offset, nil
}
2023-02-27 13:45:32 +00:00
// Reopen moves a BLOB handle to a new row of the same database table.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/blob_reopen.html
2023-02-27 13:45:32 +00:00
func (b *Blob) Reopen(row int64) error {
2024-10-08 16:38:57 +01:00
b.c.checkInterrupt(b.c.handle)
2025-01-21 01:42:57 +00:00
err := b.c.error(res_t(b.c.call("sqlite3_blob_reopen", stk_t(b.handle), stk_t(row))))
b.bytes = int64(int32(b.c.call("sqlite3_blob_bytes", stk_t(b.handle))))
2023-02-27 13:45:32 +00:00
b.offset = 0
2023-04-28 13:50:32 +01:00
return err
2023-02-27 13:45:32 +00:00
}