1 //! Incremental BLOB I/O.
2 //!
3 //! Note that SQLite does not provide API-level access to change the size of a
4 //! BLOB; that must be performed through SQL statements.
5 //!
6 //! There are two choices for how to perform IO on a [`Blob`].
7 //!
8 //! 1. The implementations it provides of the `std::io::Read`, `std::io::Write`,
9 //!    and `std::io::Seek` traits.
10 //!
11 //! 2. A positional IO API, e.g. [`Blob::read_at`], [`Blob::write_at`] and
12 //!    similar.
13 //!
14 //! Documenting these in order:
15 //!
16 //! ## 1. `std::io` trait implementations.
17 //!
18 //! `Blob` conforms to `std::io::Read`, `std::io::Write`, and `std::io::Seek`,
19 //! so it plays nicely with other types that build on these (such as
20 //! `std::io::BufReader` and `std::io::BufWriter`). However, you must be careful
21 //! with the size of the blob. For example, when using a `BufWriter`, the
22 //! `BufWriter` will accept more data than the `Blob` will allow, so make sure
23 //! to call `flush` and check for errors. (See the unit tests in this module for
24 //! an example.)
25 //!
26 //! ## 2. Positional IO
27 //!
28 //! `Blob`s also offer a `pread` / `pwrite`-style positional IO api in the form
29 //! of [`Blob::read_at`], [`Blob::write_at`], [`Blob::raw_read_at`],
30 //! [`Blob::read_at_exact`], and [`Blob::raw_read_at_exact`].
31 //!
32 //! These APIs all take the position to read from or write to from as a
33 //! parameter, instead of using an internal `pos` value.
34 //!
35 //! ### Positional IO Read Variants
36 //!
37 //! For the `read` functions, there are several functions provided:
38 //!
39 //! - [`Blob::read_at`]
40 //! - [`Blob::raw_read_at`]
41 //! - [`Blob::read_at_exact`]
42 //! - [`Blob::raw_read_at_exact`]
43 //!
44 //! These can be divided along two axes: raw/not raw, and exact/inexact:
45 //!
46 //! 1. Raw/not raw refers to the type of the destination buffer. The raw
47 //!    functions take a `&mut [MaybeUninit<u8>]` as the destination buffer,
48 //!    where the "normal" functions take a `&mut [u8]`.
49 //!
50 //!    Using `MaybeUninit` here can be more efficient in some cases, but is
51 //!    often inconvenient, so both are provided.
52 //!
53 //! 2. Exact/inexact refers to to whether or not the entire buffer must be
54 //!    filled in order for the call to be considered a success.
55 //!
56 //!    The "exact" functions require the provided buffer be entirely filled, or
57 //!    they return an error, whereas the "inexact" functions read as much out of
58 //!    the blob as is available, and return how much they were able to read.
59 //!
60 //!    The inexact functions are preferable if you do not know the size of the
61 //!    blob already, and the exact functions are preferable if you do.
62 //!
63 //! ### Comparison to using the `std::io` traits:
64 //!
65 //! In general, the positional methods offer the following Pro/Cons compared to
66 //! using the implementation `std::io::{Read, Write, Seek}` we provide for
67 //! `Blob`:
68 //!
69 //! 1. (Pro) There is no need to first seek to a position in order to perform IO
70 //!    on it as the position is a parameter.
71 //!
72 //! 2. (Pro) `Blob`'s positional read functions don't mutate the blob in any
73 //!    way, and take `&self`. No `&mut` access required.
74 //!
75 //! 3. (Pro) Positional IO functions return `Err(rusqlite::Error)` on failure,
76 //!    rather than `Err(std::io::Error)`. Returning `rusqlite::Error` is more
77 //!    accurate and convenient.
78 //!
79 //!    Note that for the `std::io` API, no data is lost however, and it can be
80 //!    recovered with `io_err.downcast::<rusqlite::Error>()` (this can be easy
81 //!    to forget, though).
82 //!
83 //! 4. (Pro, for now). A `raw` version of the read API exists which can allow
84 //!    reading into a `&mut [MaybeUninit<u8>]` buffer, which avoids a potential
85 //!    costly initialization step. (However, `std::io` traits will certainly
86 //!    gain this someday, which is why this is only a "Pro, for now").
87 //!
88 //! 5. (Con) The set of functions is more bare-bones than what is offered in
89 //!    `std::io`, which has a number of adapters, handy algorithms, further
90 //!    traits.
91 //!
92 //! 6. (Con) No meaningful interoperability with other crates, so if you need
93 //!    that you must use `std::io`.
94 //!
95 //! To generalize: the `std::io` traits are useful because they conform to a
96 //! standard interface that a lot of code knows how to handle, however that
97 //! interface is not a perfect fit for [`Blob`], so another small set of
98 //! functions is provided as well.
99 //!
100 //! # Example (`std::io`)
101 //!
102 //! ```rust
103 //! # use rusqlite::blob::ZeroBlob;
104 //! # use rusqlite::{Connection, DatabaseName};
105 //! # use std::error::Error;
106 //! # use std::io::{Read, Seek, SeekFrom, Write};
107 //! # fn main() -> Result<(), Box<dyn Error>> {
108 //! let db = Connection::open_in_memory()?;
109 //! db.execute_batch("CREATE TABLE test_table (content BLOB);")?;
110 //!
111 //! // Insert a BLOB into the `content` column of `test_table`. Note that the Blob
112 //! // I/O API provides no way of inserting or resizing BLOBs in the DB -- this
113 //! // must be done via SQL.
114 //! db.execute("INSERT INTO test_table (content) VALUES (ZEROBLOB(10))", [])?;
115 //!
116 //! // Get the row id off the BLOB we just inserted.
117 //! let rowid = db.last_insert_rowid();
118 //! // Open the BLOB we just inserted for IO.
119 //! let mut blob = db.blob_open(DatabaseName::Main, "test_table", "content", rowid, false)?;
120 //!
121 //! // Write some data into the blob. Make sure to test that the number of bytes
122 //! // written matches what you expect; if you try to write too much, the data
123 //! // will be truncated to the size of the BLOB.
124 //! let bytes_written = blob.write(b"01234567")?;
125 //! assert_eq!(bytes_written, 8);
126 //!
127 //! // Move back to the start and read into a local buffer.
128 //! // Same guidance - make sure you check the number of bytes read!
129 //! blob.seek(SeekFrom::Start(0))?;
130 //! let mut buf = [0u8; 20];
131 //! let bytes_read = blob.read(&mut buf[..])?;
132 //! assert_eq!(bytes_read, 10); // note we read 10 bytes because the blob has size 10
133 //!
134 //! // Insert another BLOB, this time using a parameter passed in from
135 //! // rust (potentially with a dynamic size).
136 //! db.execute(
137 //!     "INSERT INTO test_table (content) VALUES (?1)",
138 //!     [ZeroBlob(64)],
139 //! )?;
140 //!
141 //! // given a new row ID, we can reopen the blob on that row
142 //! let rowid = db.last_insert_rowid();
143 //! blob.reopen(rowid)?;
144 //! // Just check that the size is right.
145 //! assert_eq!(blob.len(), 64);
146 //! # Ok(())
147 //! # }
148 //! ```
149 //!
150 //! # Example (Positional)
151 //!
152 //! ```rust
153 //! # use rusqlite::blob::ZeroBlob;
154 //! # use rusqlite::{Connection, DatabaseName};
155 //! # use std::error::Error;
156 //! # fn main() -> Result<(), Box<dyn Error>> {
157 //! let db = Connection::open_in_memory()?;
158 //! db.execute_batch("CREATE TABLE test_table (content BLOB);")?;
159 //! // Insert a blob into the `content` column of `test_table`. Note that the Blob
160 //! // I/O API provides no way of inserting or resizing blobs in the DB -- this
161 //! // must be done via SQL.
162 //! db.execute("INSERT INTO test_table (content) VALUES (ZEROBLOB(10))", [])?;
163 //! // Get the row id off the blob we just inserted.
164 //! let rowid = db.last_insert_rowid();
165 //! // Open the blob we just inserted for IO.
166 //! let mut blob = db.blob_open(DatabaseName::Main, "test_table", "content", rowid, false)?;
167 //! // Write some data into the blob.
168 //! blob.write_at(b"ABCDEF", 2)?;
169 //!
170 //! // Read the whole blob into a local buffer.
171 //! let mut buf = [0u8; 10];
172 //! blob.read_at_exact(&mut buf, 0)?;
173 //! assert_eq!(&buf, b"\0\0ABCDEF\0\0");
174 //!
175 //! // Insert another blob, this time using a parameter passed in from
176 //! // rust (potentially with a dynamic size).
177 //! db.execute(
178 //!     "INSERT INTO test_table (content) VALUES (?1)",
179 //!     [ZeroBlob(64)],
180 //! )?;
181 //!
182 //! // given a new row ID, we can reopen the blob on that row
183 //! let rowid = db.last_insert_rowid();
184 //! blob.reopen(rowid)?;
185 //! assert_eq!(blob.len(), 64);
186 //! # Ok(())
187 //! # }
188 //! ```
189 use std::cmp::min;
190 use std::io;
191 use std::ptr;
192 
193 use super::ffi;
194 use super::types::{ToSql, ToSqlOutput};
195 use crate::{Connection, DatabaseName, Result};
196 
197 mod pos_io;
198 
199 /// Handle to an open BLOB. See
200 /// [`rusqlite::blob`](crate::blob) documentation for in-depth discussion.
201 pub struct Blob<'conn> {
202     conn: &'conn Connection,
203     blob: *mut ffi::sqlite3_blob,
204     // used by std::io implementations,
205     pos: i32,
206 }
207 
208 impl Connection {
209     /// Open a handle to the BLOB located in `row_id`,
210     /// `column`, `table` in database `db`.
211     ///
212     /// # Failure
213     ///
214     /// Will return `Err` if `db`/`table`/`column` cannot be converted to a
215     /// C-compatible string or if the underlying SQLite BLOB open call
216     /// fails.
217     #[inline]
blob_open<'a>( &'a self, db: DatabaseName<'_>, table: &str, column: &str, row_id: i64, read_only: bool, ) -> Result<Blob<'a>>218     pub fn blob_open<'a>(
219         &'a self,
220         db: DatabaseName<'_>,
221         table: &str,
222         column: &str,
223         row_id: i64,
224         read_only: bool,
225     ) -> Result<Blob<'a>> {
226         let c = self.db.borrow_mut();
227         let mut blob = ptr::null_mut();
228         let db = db.as_cstring()?;
229         let table = super::str_to_cstring(table)?;
230         let column = super::str_to_cstring(column)?;
231         let rc = unsafe {
232             ffi::sqlite3_blob_open(
233                 c.db(),
234                 db.as_ptr(),
235                 table.as_ptr(),
236                 column.as_ptr(),
237                 row_id,
238                 !read_only as std::os::raw::c_int,
239                 &mut blob,
240             )
241         };
242         c.decode_result(rc).map(|_| Blob {
243             conn: self,
244             blob,
245             pos: 0,
246         })
247     }
248 }
249 
250 impl Blob<'_> {
251     /// Move a BLOB handle to a new row.
252     ///
253     /// # Failure
254     ///
255     /// Will return `Err` if the underlying SQLite BLOB reopen call fails.
256     #[inline]
reopen(&mut self, row: i64) -> Result<()>257     pub fn reopen(&mut self, row: i64) -> Result<()> {
258         let rc = unsafe { ffi::sqlite3_blob_reopen(self.blob, row) };
259         if rc != ffi::SQLITE_OK {
260             return self.conn.decode_result(rc);
261         }
262         self.pos = 0;
263         Ok(())
264     }
265 
266     /// Return the size in bytes of the BLOB.
267     #[inline]
268     #[must_use]
size(&self) -> i32269     pub fn size(&self) -> i32 {
270         unsafe { ffi::sqlite3_blob_bytes(self.blob) }
271     }
272 
273     /// Return the current size in bytes of the BLOB.
274     #[inline]
275     #[must_use]
len(&self) -> usize276     pub fn len(&self) -> usize {
277         use std::convert::TryInto;
278         self.size().try_into().unwrap()
279     }
280 
281     /// Return true if the BLOB is empty.
282     #[inline]
283     #[must_use]
is_empty(&self) -> bool284     pub fn is_empty(&self) -> bool {
285         self.size() == 0
286     }
287 
288     /// Close a BLOB handle.
289     ///
290     /// Calling `close` explicitly is not required (the BLOB will be closed
291     /// when the `Blob` is dropped), but it is available so you can get any
292     /// errors that occur.
293     ///
294     /// # Failure
295     ///
296     /// Will return `Err` if the underlying SQLite close call fails.
297     #[inline]
close(mut self) -> Result<()>298     pub fn close(mut self) -> Result<()> {
299         self.close_()
300     }
301 
302     #[inline]
close_(&mut self) -> Result<()>303     fn close_(&mut self) -> Result<()> {
304         let rc = unsafe { ffi::sqlite3_blob_close(self.blob) };
305         self.blob = ptr::null_mut();
306         self.conn.decode_result(rc)
307     }
308 }
309 
310 impl io::Read for Blob<'_> {
311     /// Read data from a BLOB incrementally. Will return Ok(0) if the end of
312     /// the blob has been reached.
313     ///
314     /// # Failure
315     ///
316     /// Will return `Err` if the underlying SQLite read call fails.
317     #[inline]
read(&mut self, buf: &mut [u8]) -> io::Result<usize>318     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
319         let max_allowed_len = (self.size() - self.pos) as usize;
320         let n = min(buf.len(), max_allowed_len) as i32;
321         if n <= 0 {
322             return Ok(0);
323         }
324         let rc = unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_mut_ptr().cast(), n, self.pos) };
325         self.conn
326             .decode_result(rc)
327             .map(|_| {
328                 self.pos += n;
329                 n as usize
330             })
331             .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
332     }
333 }
334 
335 impl io::Write for Blob<'_> {
336     /// Write data into a BLOB incrementally. Will return `Ok(0)` if the end of
337     /// the blob has been reached; consider using `Write::write_all(buf)`
338     /// if you want to get an error if the entirety of the buffer cannot be
339     /// written.
340     ///
341     /// This function may only modify the contents of the BLOB; it is not
342     /// possible to increase the size of a BLOB using this API.
343     ///
344     /// # Failure
345     ///
346     /// Will return `Err` if the underlying SQLite write call fails.
347     #[inline]
write(&mut self, buf: &[u8]) -> io::Result<usize>348     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
349         let max_allowed_len = (self.size() - self.pos) as usize;
350         let n = min(buf.len(), max_allowed_len) as i32;
351         if n <= 0 {
352             return Ok(0);
353         }
354         let rc = unsafe { ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
355         self.conn
356             .decode_result(rc)
357             .map(|_| {
358                 self.pos += n;
359                 n as usize
360             })
361             .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
362     }
363 
364     #[inline]
flush(&mut self) -> io::Result<()>365     fn flush(&mut self) -> io::Result<()> {
366         Ok(())
367     }
368 }
369 
370 impl io::Seek for Blob<'_> {
371     /// Seek to an offset, in bytes, in BLOB.
372     #[inline]
seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>373     fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
374         let pos = match pos {
375             io::SeekFrom::Start(offset) => offset as i64,
376             io::SeekFrom::Current(offset) => i64::from(self.pos) + offset,
377             io::SeekFrom::End(offset) => i64::from(self.size()) + offset,
378         };
379 
380         if pos < 0 {
381             Err(io::Error::new(
382                 io::ErrorKind::InvalidInput,
383                 "invalid seek to negative position",
384             ))
385         } else if pos > i64::from(self.size()) {
386             Err(io::Error::new(
387                 io::ErrorKind::InvalidInput,
388                 "invalid seek to position past end of blob",
389             ))
390         } else {
391             self.pos = pos as i32;
392             Ok(pos as u64)
393         }
394     }
395 }
396 
397 #[allow(unused_must_use)]
398 impl Drop for Blob<'_> {
399     #[inline]
drop(&mut self)400     fn drop(&mut self) {
401         self.close_();
402     }
403 }
404 
405 /// BLOB of length N that is filled with zeroes.
406 ///
407 /// Zeroblobs are intended to serve as placeholders for BLOBs whose content is
408 /// later written using incremental BLOB I/O routines.
409 ///
410 /// A negative value for the zeroblob results in a zero-length BLOB.
411 #[derive(Copy, Clone)]
412 pub struct ZeroBlob(pub i32);
413 
414 impl ToSql for ZeroBlob {
415     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>416     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
417         let ZeroBlob(length) = *self;
418         Ok(ToSqlOutput::ZeroBlob(length))
419     }
420 }
421 
422 #[cfg(test)]
423 mod test {
424     use crate::{Connection, DatabaseName, Result};
425     use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write};
426 
db_with_test_blob() -> Result<(Connection, i64)>427     fn db_with_test_blob() -> Result<(Connection, i64)> {
428         let db = Connection::open_in_memory()?;
429         let sql = "BEGIN;
430                    CREATE TABLE test (content BLOB);
431                    INSERT INTO test VALUES (ZEROBLOB(10));
432                    END;";
433         db.execute_batch(sql)?;
434         let rowid = db.last_insert_rowid();
435         Ok((db, rowid))
436     }
437 
438     #[test]
test_blob() -> Result<()>439     fn test_blob() -> Result<()> {
440         let (db, rowid) = db_with_test_blob()?;
441 
442         let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
443         assert_eq!(4, blob.write(b"Clob").unwrap());
444         assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
445         assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
446 
447         blob.reopen(rowid)?;
448         blob.close()?;
449 
450         blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)?;
451         let mut bytes = [0u8; 5];
452         assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
453         assert_eq!(&bytes, b"Clob5");
454         assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
455         assert_eq!(&bytes, b"67890");
456         assert_eq!(0, blob.read(&mut bytes[..]).unwrap());
457 
458         blob.seek(SeekFrom::Start(2)).unwrap();
459         assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
460         assert_eq!(&bytes, b"ob567");
461 
462         // only first 4 bytes of `bytes` should be read into
463         blob.seek(SeekFrom::Current(-1)).unwrap();
464         assert_eq!(4, blob.read(&mut bytes[..]).unwrap());
465         assert_eq!(&bytes, b"78907");
466 
467         blob.seek(SeekFrom::End(-6)).unwrap();
468         assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
469         assert_eq!(&bytes, b"56789");
470 
471         blob.reopen(rowid)?;
472         assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
473         assert_eq!(&bytes, b"Clob5");
474 
475         // should not be able to seek negative or past end
476         blob.seek(SeekFrom::Current(-20)).unwrap_err();
477         blob.seek(SeekFrom::End(0)).unwrap();
478         blob.seek(SeekFrom::Current(1)).unwrap_err();
479 
480         // write_all should detect when we return Ok(0) because there is no space left,
481         // and return a write error
482         blob.reopen(rowid)?;
483         blob.write_all(b"0123456789x").unwrap_err();
484         Ok(())
485     }
486 
487     #[test]
test_blob_in_bufreader() -> Result<()>488     fn test_blob_in_bufreader() -> Result<()> {
489         let (db, rowid) = db_with_test_blob()?;
490 
491         let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
492         assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
493 
494         blob.reopen(rowid)?;
495         let mut reader = BufReader::new(blob);
496 
497         let mut line = String::new();
498         assert_eq!(4, reader.read_line(&mut line).unwrap());
499         assert_eq!("one\n", line);
500 
501         line.truncate(0);
502         assert_eq!(4, reader.read_line(&mut line).unwrap());
503         assert_eq!("two\n", line);
504 
505         line.truncate(0);
506         assert_eq!(2, reader.read_line(&mut line).unwrap());
507         assert_eq!("\0\0", line);
508         Ok(())
509     }
510 
511     #[test]
test_blob_in_bufwriter() -> Result<()>512     fn test_blob_in_bufwriter() -> Result<()> {
513         let (db, rowid) = db_with_test_blob()?;
514 
515         {
516             let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
517             let mut writer = BufWriter::new(blob);
518 
519             // trying to write too much and then flush should fail
520             assert_eq!(8, writer.write(b"01234567").unwrap());
521             assert_eq!(8, writer.write(b"01234567").unwrap());
522             writer.flush().unwrap_err();
523         }
524 
525         {
526             // ... but it should've written the first 10 bytes
527             let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
528             let mut bytes = [0u8; 10];
529             assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
530             assert_eq!(b"0123456701", &bytes);
531         }
532 
533         {
534             let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
535             let mut writer = BufWriter::new(blob);
536 
537             // trying to write_all too much should fail
538             writer.write_all(b"aaaaaaaaaabbbbb").unwrap();
539             writer.flush().unwrap_err();
540         }
541 
542         {
543             // ... but it should've written the first 10 bytes
544             let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
545             let mut bytes = [0u8; 10];
546             assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
547             assert_eq!(b"aaaaaaaaaa", &bytes);
548             Ok(())
549         }
550     }
551 }
552