-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add Blob I/O #1083
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Blob I/O #1083
Changes from all commits
c357e0c
6188ff5
88d6cfd
c6a9868
2866235
867ec21
5583431
606d2d2
79df247
dff49f9
01dbf4b
893e77d
7df73e0
e519482
67dc006
7f0a6fb
5b53687
ef6fa5a
fe0ed2e
cb853d0
7d10860
3f8fe99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// Copyright (C) 2022 Yasuhiro Matsumoto <mattn.jp@gmail.com>. | ||
// | ||
// Use of this source code is governed by an MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package sqlite3 | ||
|
||
/* | ||
#ifndef USE_LIBSQLITE3 | ||
#include "sqlite3-binding.h" | ||
#else | ||
#include <sqlite3.h> | ||
#endif | ||
#include <stdlib.h> | ||
*/ | ||
import "C" | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"math" | ||
"runtime" | ||
"unsafe" | ||
) | ||
|
||
// SQLiteBlob implements the SQLite Blob I/O interface. | ||
type SQLiteBlob struct { | ||
conn *SQLiteConn | ||
blob *C.sqlite3_blob | ||
size int | ||
offset int | ||
} | ||
|
||
// Blob opens a blob. | ||
// | ||
// See https://www.sqlite.org/c3ref/blob_open.html for usage. | ||
// | ||
// Should only be used with conn.Raw. | ||
func (conn *SQLiteConn) Blob(database, table, column string, rowid int64, flags int) (*SQLiteBlob, error) { | ||
databaseptr := C.CString(database) | ||
defer C.free(unsafe.Pointer(databaseptr)) | ||
|
||
tableptr := C.CString(table) | ||
defer C.free(unsafe.Pointer(tableptr)) | ||
|
||
columnptr := C.CString(column) | ||
defer C.free(unsafe.Pointer(columnptr)) | ||
|
||
var blob *C.sqlite3_blob | ||
ret := C.sqlite3_blob_open(conn.db, databaseptr, tableptr, columnptr, C.longlong(rowid), C.int(flags), &blob) | ||
|
||
if ret != C.SQLITE_OK { | ||
return nil, conn.lastError() | ||
} | ||
|
||
size := int(C.sqlite3_blob_bytes(blob)) | ||
bb := &SQLiteBlob{conn: conn, blob: blob, size: size, offset: 0} | ||
|
||
runtime.SetFinalizer(bb, (*SQLiteBlob).Close) | ||
|
||
return bb, nil | ||
} | ||
|
||
// Read implements the io.Reader interface. | ||
func (s *SQLiteBlob) Read(b []byte) (n int, err error) { | ||
if s.offset >= s.size { | ||
return 0, io.EOF | ||
} | ||
|
||
if len(b) == 0 { | ||
return 0, nil | ||
} | ||
|
||
n = s.size - s.offset | ||
if len(b) < n { | ||
n = len(b) | ||
} | ||
|
||
p := &b[0] | ||
rittneje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ret := C.sqlite3_blob_read(s.blob, unsafe.Pointer(p), C.int(n), C.int(s.offset)) | ||
if ret != C.SQLITE_OK { | ||
return 0, s.conn.lastError() | ||
} | ||
|
||
s.offset += n | ||
|
||
return n, nil | ||
} | ||
|
||
// Write implements the io.Writer interface. | ||
func (s *SQLiteBlob) Write(b []byte) (n int, err error) { | ||
if len(b) == 0 { | ||
return 0, nil | ||
} | ||
|
||
if s.offset >= s.size { | ||
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Write: insufficient space in %d-byte blob", s.size) | ||
} | ||
|
||
n = s.size - s.offset | ||
joriszwart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(b) < n { | ||
n = len(b) | ||
} | ||
|
||
if n != len(b) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trying to remember - is there a reason not to do this check after the call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @joriszwart following up on this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what to do. Sorry. |
||
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Write: insufficient space in %d-byte blob", s.size) | ||
} | ||
|
||
p := &b[0] | ||
rittneje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ret := C.sqlite3_blob_write(s.blob, unsafe.Pointer(p), C.int(n), C.int(s.offset)) | ||
if ret != C.SQLITE_OK { | ||
return 0, s.conn.lastError() | ||
} | ||
|
||
s.offset += n | ||
|
||
return n, nil | ||
} | ||
|
||
// Seek implements the io.Seeker interface. | ||
func (s *SQLiteBlob) Seek(offset int64, whence int) (int64, error) { | ||
if offset > math.MaxInt32 { | ||
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Seek: invalid offset %d", offset) | ||
} | ||
|
||
var abs int64 | ||
switch whence { | ||
case io.SeekStart: | ||
abs = offset | ||
case io.SeekCurrent: | ||
abs = int64(s.offset) + offset | ||
case io.SeekEnd: | ||
abs = int64(s.size) + offset | ||
default: | ||
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Seek: invalid whence %d", whence) | ||
} | ||
|
||
if abs < 0 { | ||
return 0, errors.New("sqlite.SQLiteBlob.Seek: negative position") | ||
} | ||
|
||
if abs > math.MaxInt32 || abs > int64(s.size) { | ||
return 0, errors.New("sqlite3.SQLiteBlob.Seek: overflow position") | ||
} | ||
|
||
s.offset = int(abs) | ||
joriszwart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return abs, nil | ||
} | ||
|
||
// Size returns the size of the blob. | ||
func (s *SQLiteBlob) Size() int { | ||
return s.size | ||
} | ||
|
||
// Close implements the io.Closer interface. | ||
func (s *SQLiteBlob) Close() error { | ||
ret := C.sqlite3_blob_close(s.blob) | ||
|
||
s.blob = nil | ||
runtime.SetFinalizer(s, nil) | ||
|
||
if ret != C.SQLITE_OK { | ||
return s.conn.lastError() | ||
} | ||
|
||
return nil | ||
} |
Uh oh!
There was an error while loading. Please reload this page.