Commit 64f1f035 authored by gwenn's avatar gwenn

Improved Blob support.

parent 09aabd90
...@@ -12,6 +12,7 @@ import "C" ...@@ -12,6 +12,7 @@ import "C"
import ( import (
"errors" "errors"
"io"
"unsafe" "unsafe"
) )
...@@ -19,6 +20,7 @@ import ( ...@@ -19,6 +20,7 @@ import (
type BlobReader struct { type BlobReader struct {
c *Conn c *Conn
bl *C.sqlite3_blob bl *C.sqlite3_blob
size int
ReadOffset int ReadOffset int
} }
...@@ -39,7 +41,7 @@ func (c *Conn) NewBlobReader(db, table, column string, row int64) (*BlobReader, ...@@ -39,7 +41,7 @@ func (c *Conn) NewBlobReader(db, table, column string, row int64) (*BlobReader,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &BlobReader{c, bl, 0}, nil return &BlobReader{c, bl, -1, 0}, nil
} }
// NewBlobReadWriter open a BLOB for incremental I/O // NewBlobReadWriter open a BLOB for incremental I/O
...@@ -49,7 +51,7 @@ func (c *Conn) NewBlobReadWriter(db, table, column string, row int64) (*BlobRead ...@@ -49,7 +51,7 @@ func (c *Conn) NewBlobReadWriter(db, table, column string, row int64) (*BlobRead
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &BlobReadWriter{BlobReader{c, bl, 0}, 0}, nil return &BlobReadWriter{BlobReader{c, bl, -1, 0}, 0}, nil
} }
func (c *Conn) blob_open(db, table, column string, row int64, write bool) (*C.sqlite3_blob, error) { func (c *Conn) blob_open(db, table, column string, row int64, write bool) (*C.sqlite3_blob, error) {
...@@ -90,38 +92,67 @@ func (r *BlobReader) Close() error { ...@@ -90,38 +92,67 @@ func (r *BlobReader) Close() error {
// Read reads data from a BLOB incrementally // Read reads data from a BLOB incrementally
// (See http://sqlite.org/c3ref/blob_read.html) // (See http://sqlite.org/c3ref/blob_read.html)
func (r *BlobReader) Read(v []byte) (int, error) { func (r *BlobReader) Read(v []byte) (int, error) {
var p *byte if len(v) == 0 {
if len(v) > 0 { return 0, nil
p = &v[0]
} }
rv := C.sqlite3_blob_read(r.bl, unsafe.Pointer(p), C.int(len(v)), C.int(r.ReadOffset)) size, err := r.Size()
if err != nil {
return 0, err
}
if r.ReadOffset >= size {
return 0, io.EOF
}
if len(v) > (size - r.ReadOffset) {
v = v[0 : size-r.ReadOffset]
}
p := &v[0]
n := len(v)
rv := C.sqlite3_blob_read(r.bl, unsafe.Pointer(p), C.int(n), C.int(r.ReadOffset))
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return 0, r.c.error(rv) return 0, r.c.error(rv)
} }
r.ReadOffset += len(v) r.ReadOffset += n
return len(v), nil return n, nil
} }
// Size returns the size of an opened BLOB // Size returns the size of an opened BLOB
// (See http://sqlite.org/c3ref/blob_bytes.html) // (See http://sqlite.org/c3ref/blob_bytes.html)
func (r *BlobReader) Size() (int, error) { func (r *BlobReader) Size() (int, error) {
s := C.sqlite3_blob_bytes(r.bl) if r.bl == nil {
return int(s), nil return 0, errors.New("blob reader already closed")
}
if r.size < 0 {
r.size = int(C.sqlite3_blob_bytes(r.bl))
}
return r.size, nil
} }
// Write writes data into a BLOB incrementally // Write writes data into a BLOB incrementally
// (See http://sqlite.org/c3ref/blob_write.html) // (See http://sqlite.org/c3ref/blob_write.html)
func (w *BlobReadWriter) Write(v []byte) (int, error) { func (w *BlobReadWriter) Write(v []byte) (int, error) {
var p *byte if len(v) == 0 {
if len(v) > 0 { return 0, nil
p = &v[0] }
size, err := w.Size()
if err != nil {
return 0, err
}
if w.WriteOffset >= size {
return 0, io.EOF
}
/* Write must return a non-nil error if it returns n < len(v) */
if len(v) > (size - w.WriteOffset) {
v = v[0 : size-w.WriteOffset]
err = io.EOF
} }
rv := C.sqlite3_blob_write(w.bl, unsafe.Pointer(p), C.int(len(v)), C.int(w.WriteOffset)) p := &v[0]
n := len(v)
rv := C.sqlite3_blob_write(w.bl, unsafe.Pointer(p), C.int(n), C.int(w.WriteOffset))
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return 0, w.c.error(rv) return 0, w.c.error(rv)
} }
w.WriteOffset += len(v) w.WriteOffset += n
return len(v), nil return n, err
} }
// Reopen moves a BLOB handle to a new row // Reopen moves a BLOB handle to a new row
...@@ -131,6 +162,7 @@ func (r *BlobReader) Reopen(rowid int64) error { ...@@ -131,6 +162,7 @@ func (r *BlobReader) Reopen(rowid int64) error {
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return r.c.error(rv) return r.c.error(rv)
} }
r.size = -1
r.ReadOffset = 0 r.ReadOffset = 0
return nil return nil
} }
......
...@@ -6,6 +6,7 @@ package sqlite_test ...@@ -6,6 +6,7 @@ package sqlite_test
import ( import (
. "github.com/gwenn/gosqlite" . "github.com/gwenn/gosqlite"
"io"
"testing" "testing"
) )
...@@ -31,6 +32,7 @@ func TestBlob(t *testing.T) { ...@@ -31,6 +32,7 @@ func TestBlob(t *testing.T) {
content := []byte("Clob") content := []byte("Clob")
n, err := bw.Write(content) n, err := bw.Write(content)
checkNoError(t, err, "blob write error: %s") checkNoError(t, err, "blob write error: %s")
bw.Close()
br, err := db.NewBlobReader("main", "test", "content", rowid) br, err := db.NewBlobReader("main", "test", "content", rowid)
checkNoError(t, err, "blob open error: %s") checkNoError(t, err, "blob open error: %s")
...@@ -38,7 +40,7 @@ func TestBlob(t *testing.T) { ...@@ -38,7 +40,7 @@ func TestBlob(t *testing.T) {
size, err := br.Size() size, err := br.Size()
checkNoError(t, err, "blob size error: %s") checkNoError(t, err, "blob size error: %s")
content = make([]byte, size) content = make([]byte, size+5)
n, err = br.Read(content[:5]) n, err = br.Read(content[:5])
checkNoError(t, err, "blob read error: %s") checkNoError(t, err, "blob read error: %s")
assertEquals(t, "expected %d bytes but got %d", 5, n) assertEquals(t, "expected %d bytes but got %d", 5, n)
...@@ -47,6 +49,9 @@ func TestBlob(t *testing.T) { ...@@ -47,6 +49,9 @@ func TestBlob(t *testing.T) {
checkNoError(t, err, "blob read error: %s") checkNoError(t, err, "blob read error: %s")
assertEquals(t, "expected %d bytes but got %d", 5, n) assertEquals(t, "expected %d bytes but got %d", 5, n)
//fmt.Printf("%#v\n", content) //fmt.Printf("%#v\n", content)
n, err = br.Read(content[10:])
assert(t, "error expected", n == 0 && err == io.EOF)
br.Close() br.Close()
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment