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