Skip to content

Commit c27e779

Browse files
committed
https://github.com/mattn/go-sqlite3/pull/1083
Pulling in this RP for Blob access
1 parent 18cdded commit c27e779

File tree

2 files changed

+421
-0
lines changed

2 files changed

+421
-0
lines changed

blob_io.go

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright (C) 2022 Yasuhiro Matsumoto <[email protected]>.
2+
//
3+
// Use of this source code is governed by an MIT-style
4+
// license that can be found in the LICENSE file.
5+
6+
package sqlite3
7+
8+
/*
9+
#ifndef USE_LIBSQLITE3
10+
#include "sqlite3-binding.h"
11+
#else
12+
#include <sqlite3.h>
13+
#endif
14+
#include <stdlib.h>
15+
*/
16+
import "C"
17+
18+
import (
19+
"errors"
20+
"fmt"
21+
"io"
22+
"math"
23+
"runtime"
24+
"unsafe"
25+
)
26+
27+
// SQLiteBlob implements the SQLite Blob I/O interface.
28+
type SQLiteBlob struct {
29+
conn *SQLiteConn
30+
blob *C.sqlite3_blob
31+
size int
32+
offset int
33+
}
34+
35+
// Blob opens a blob.
36+
//
37+
// See https://www.sqlite.org/c3ref/blob_open.html for usage.
38+
//
39+
// Should only be used with conn.Raw.
40+
func (conn *SQLiteConn) Blob(database, table, column string, rowid int64, flags int) (*SQLiteBlob, error) {
41+
databaseptr := C.CString(database)
42+
defer C.free(unsafe.Pointer(databaseptr))
43+
44+
tableptr := C.CString(table)
45+
defer C.free(unsafe.Pointer(tableptr))
46+
47+
columnptr := C.CString(column)
48+
defer C.free(unsafe.Pointer(columnptr))
49+
50+
var blob *C.sqlite3_blob
51+
ret := C.sqlite3_blob_open(conn.db, databaseptr, tableptr, columnptr, C.longlong(rowid), C.int(flags), &blob)
52+
53+
if ret != C.SQLITE_OK {
54+
return nil, conn.lastError()
55+
}
56+
57+
size := int(C.sqlite3_blob_bytes(blob))
58+
bb := &SQLiteBlob{conn: conn, blob: blob, size: size, offset: 0}
59+
60+
runtime.SetFinalizer(bb, (*SQLiteBlob).Close)
61+
62+
return bb, nil
63+
}
64+
65+
// Read implements the io.Reader interface.
66+
func (s *SQLiteBlob) Read(b []byte) (n int, err error) {
67+
if s.offset >= s.size {
68+
return 0, io.EOF
69+
}
70+
71+
if len(b) == 0 {
72+
return 0, nil
73+
}
74+
75+
n = s.size - s.offset
76+
if len(b) < n {
77+
n = len(b)
78+
}
79+
80+
p := &b[0]
81+
ret := C.sqlite3_blob_read(s.blob, unsafe.Pointer(p), C.int(n), C.int(s.offset))
82+
if ret != C.SQLITE_OK {
83+
return 0, s.conn.lastError()
84+
}
85+
86+
s.offset += n
87+
88+
return n, nil
89+
}
90+
91+
// Write implements the io.Writer interface.
92+
func (s *SQLiteBlob) Write(b []byte) (n int, err error) {
93+
if len(b) == 0 {
94+
return 0, nil
95+
}
96+
97+
if s.offset >= s.size {
98+
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Write: insufficient space in %d-byte blob", s.size)
99+
}
100+
101+
n = s.size - s.offset
102+
if len(b) < n {
103+
n = len(b)
104+
}
105+
106+
if n != len(b) {
107+
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Write: insufficient space in %d-byte blob", s.size)
108+
}
109+
110+
p := &b[0]
111+
ret := C.sqlite3_blob_write(s.blob, unsafe.Pointer(p), C.int(n), C.int(s.offset))
112+
if ret != C.SQLITE_OK {
113+
return 0, s.conn.lastError()
114+
}
115+
116+
s.offset += n
117+
118+
return n, nil
119+
}
120+
121+
// Seek implements the io.Seeker interface.
122+
func (s *SQLiteBlob) Seek(offset int64, whence int) (int64, error) {
123+
if offset > math.MaxInt32 {
124+
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Seek: invalid offset %d", offset)
125+
}
126+
127+
var abs int64
128+
switch whence {
129+
case io.SeekStart:
130+
abs = offset
131+
case io.SeekCurrent:
132+
abs = int64(s.offset) + offset
133+
case io.SeekEnd:
134+
abs = int64(s.size) + offset
135+
default:
136+
return 0, fmt.Errorf("sqlite3.SQLiteBlob.Seek: invalid whence %d", whence)
137+
}
138+
139+
if abs < 0 {
140+
return 0, errors.New("sqlite.SQLiteBlob.Seek: negative position")
141+
}
142+
143+
if abs > math.MaxInt32 || abs > int64(s.size) {
144+
return 0, errors.New("sqlite3.SQLiteBlob.Seek: overflow position")
145+
}
146+
147+
s.offset = int(abs)
148+
149+
return abs, nil
150+
}
151+
152+
// Size returns the size of the blob.
153+
func (s *SQLiteBlob) Size() int {
154+
return s.size
155+
}
156+
157+
// Close implements the io.Closer interface.
158+
func (s *SQLiteBlob) Close() error {
159+
ret := C.sqlite3_blob_close(s.blob)
160+
161+
s.blob = nil
162+
runtime.SetFinalizer(s, nil)
163+
164+
if ret != C.SQLITE_OK {
165+
return s.conn.lastError()
166+
}
167+
168+
return nil
169+
}

0 commit comments

Comments
 (0)