From a7ec67b7d3cf88df1d29888e398998ce41fd7d38 Mon Sep 17 00:00:00 2001 From: Haibo Huang Date: Sat, 5 Dec 2020 00:04:04 -0800 Subject: Upgrade rust/crates/rusqlite to 0.24.2 Test: make Change-Id: I0607b270837d81d08a0cea6495735b113d96a9ed --- src/blob.rs | 397 ------------------------------------------------------------ 1 file changed, 397 deletions(-) delete mode 100644 src/blob.rs (limited to 'src/blob.rs') diff --git a/src/blob.rs b/src/blob.rs deleted file mode 100644 index f4ca951..0000000 --- a/src/blob.rs +++ /dev/null @@ -1,397 +0,0 @@ -//! `feature = "blob"` Incremental BLOB I/O. -//! -//! Note that SQLite does not provide API-level access to change the size of a -//! BLOB; that must be performed through SQL statements. -//! -//! `Blob` conforms to `std::io::Read`, `std::io::Write`, and `std::io::Seek`, -//! so it plays nicely with other types that build on these (such as -//! `std::io::BufReader` and `std::io::BufWriter`). However, you must be -//! careful with the size of the blob. For example, when using a `BufWriter`, -//! the `BufWriter` will accept more data than the `Blob` -//! will allow, so make sure to call `flush` and check for errors. (See the -//! unit tests in this module for an example.) -//! -//! ## Example -//! -//! ```rust -//! use rusqlite::blob::ZeroBlob; -//! use rusqlite::{Connection, DatabaseName, NO_PARAMS}; -//! use std::error::Error; -//! use std::io::{Read, Seek, SeekFrom, Write}; -//! -//! fn main() -> Result<(), Box> { -//! let db = Connection::open_in_memory()?; -//! db.execute_batch("CREATE TABLE test (content BLOB);")?; -//! db.execute( -//! "INSERT INTO test (content) VALUES (ZEROBLOB(10))", -//! NO_PARAMS, -//! )?; -//! -//! let rowid = db.last_insert_rowid(); -//! let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?; -//! -//! // Make sure to test that the number of bytes written matches what you expect; -//! // if you try to write too much, the data will be truncated to the size of the -//! // BLOB. -//! let bytes_written = blob.write(b"01234567")?; -//! assert_eq!(bytes_written, 8); -//! -//! // Same guidance - make sure you check the number of bytes read! -//! blob.seek(SeekFrom::Start(0))?; -//! let mut buf = [0u8; 20]; -//! let bytes_read = blob.read(&mut buf[..])?; -//! assert_eq!(bytes_read, 10); // note we read 10 bytes because the blob has size 10 -//! -//! db.execute("INSERT INTO test (content) VALUES (?)", &[ZeroBlob(64)])?; -//! -//! // given a new row ID, we can reopen the blob on that row -//! let rowid = db.last_insert_rowid(); -//! blob.reopen(rowid)?; -//! -//! assert_eq!(blob.size(), 64); -//! Ok(()) -//! } -//! ``` -use std::cmp::min; -use std::io; -use std::ptr; - -use super::ffi; -use super::types::{ToSql, ToSqlOutput}; -use crate::{Connection, DatabaseName, Result}; - -/// `feature = "blob"` Handle to an open BLOB. -pub struct Blob<'conn> { - conn: &'conn Connection, - blob: *mut ffi::sqlite3_blob, - pos: i32, -} - -impl Connection { - /// `feature = "blob"` Open a handle to the BLOB located in `row_id`, - /// `column`, `table` in database `db`. - /// - /// # Failure - /// - /// Will return `Err` if `db`/`table`/`column` cannot be converted to a - /// C-compatible string or if the underlying SQLite BLOB open call - /// fails. - pub fn blob_open<'a>( - &'a self, - db: DatabaseName<'_>, - table: &str, - column: &str, - row_id: i64, - read_only: bool, - ) -> Result> { - let mut c = self.db.borrow_mut(); - let mut blob = ptr::null_mut(); - let db = db.to_cstring()?; - let table = super::str_to_cstring(table)?; - let column = super::str_to_cstring(column)?; - let rc = unsafe { - ffi::sqlite3_blob_open( - c.db(), - db.as_ptr(), - table.as_ptr(), - column.as_ptr(), - row_id, - if read_only { 0 } else { 1 }, - &mut blob, - ) - }; - c.decode_result(rc).map(|_| Blob { - conn: self, - blob, - pos: 0, - }) - } -} - -impl Blob<'_> { - /// Move a BLOB handle to a new row. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite BLOB reopen call fails. - pub fn reopen(&mut self, row: i64) -> Result<()> { - let rc = unsafe { ffi::sqlite3_blob_reopen(self.blob, row) }; - if rc != ffi::SQLITE_OK { - return self.conn.decode_result(rc); - } - self.pos = 0; - Ok(()) - } - - /// Return the size in bytes of the BLOB. - pub fn size(&self) -> i32 { - unsafe { ffi::sqlite3_blob_bytes(self.blob) } - } - - /// Close a BLOB handle. - /// - /// Calling `close` explicitly is not required (the BLOB will be closed - /// when the `Blob` is dropped), but it is available so you can get any - /// errors that occur. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite close call fails. - pub fn close(mut self) -> Result<()> { - self.close_() - } - - fn close_(&mut self) -> Result<()> { - let rc = unsafe { ffi::sqlite3_blob_close(self.blob) }; - self.blob = ptr::null_mut(); - self.conn.decode_result(rc) - } -} - -impl io::Read for Blob<'_> { - /// Read data from a BLOB incrementally. Will return Ok(0) if the end of - /// the blob has been reached. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite read call fails. - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let max_allowed_len = (self.size() - self.pos) as usize; - let n = min(buf.len(), max_allowed_len) as i32; - if n <= 0 { - return Ok(0); - } - let rc = unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_ptr() as *mut _, n, self.pos) }; - self.conn - .decode_result(rc) - .map(|_| { - self.pos += n; - n as usize - }) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) - } -} - -impl io::Write for Blob<'_> { - /// Write data into a BLOB incrementally. Will return `Ok(0)` if the end of - /// the blob has been reached; consider using `Write::write_all(buf)` - /// if you want to get an error if the entirety of the buffer cannot be - /// written. - /// - /// This function may only modify the contents of the BLOB; it is not - /// possible to increase the size of a BLOB using this API. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite write call fails. - fn write(&mut self, buf: &[u8]) -> io::Result { - let max_allowed_len = (self.size() - self.pos) as usize; - let n = min(buf.len(), max_allowed_len) as i32; - if n <= 0 { - return Ok(0); - } - let rc = unsafe { ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos) }; - self.conn - .decode_result(rc) - .map(|_| { - self.pos += n; - n as usize - }) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl io::Seek for Blob<'_> { - /// Seek to an offset, in bytes, in BLOB. - fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - let pos = match pos { - io::SeekFrom::Start(offset) => offset as i64, - io::SeekFrom::Current(offset) => i64::from(self.pos) + offset, - io::SeekFrom::End(offset) => i64::from(self.size()) + offset, - }; - - if pos < 0 { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "invalid seek to negative position", - )) - } else if pos > i64::from(self.size()) { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "invalid seek to position past end of blob", - )) - } else { - self.pos = pos as i32; - Ok(pos as u64) - } - } -} - -#[allow(unused_must_use)] -impl Drop for Blob<'_> { - fn drop(&mut self) { - self.close_(); - } -} - -/// `feature = "blob"` BLOB of length N that is filled with zeroes. -/// -/// Zeroblobs are intended to serve as placeholders for BLOBs whose content is -/// later written using incremental BLOB I/O routines. -/// -/// A negative value for the zeroblob results in a zero-length BLOB. -#[derive(Copy, Clone)] -pub struct ZeroBlob(pub i32); - -impl ToSql for ZeroBlob { - fn to_sql(&self) -> Result> { - let ZeroBlob(length) = *self; - Ok(ToSqlOutput::ZeroBlob(length)) - } -} - -#[cfg(test)] -mod test { - use crate::{Connection, DatabaseName, Result}; - use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; - - fn db_with_test_blob() -> Result<(Connection, i64)> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE test (content BLOB); - INSERT INTO test VALUES (ZEROBLOB(10)); - END;"; - db.execute_batch(sql)?; - let rowid = db.last_insert_rowid(); - Ok((db, rowid)) - } - - #[test] - fn test_blob() { - let (db, rowid) = db_with_test_blob().unwrap(); - - let mut blob = db - .blob_open(DatabaseName::Main, "test", "content", rowid, false) - .unwrap(); - assert_eq!(4, blob.write(b"Clob").unwrap()); - assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10 - assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10 - - blob.reopen(rowid).unwrap(); - blob.close().unwrap(); - - blob = db - .blob_open(DatabaseName::Main, "test", "content", rowid, true) - .unwrap(); - let mut bytes = [0u8; 5]; - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"Clob5"); - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"67890"); - assert_eq!(0, blob.read(&mut bytes[..]).unwrap()); - - blob.seek(SeekFrom::Start(2)).unwrap(); - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"ob567"); - - // only first 4 bytes of `bytes` should be read into - blob.seek(SeekFrom::Current(-1)).unwrap(); - assert_eq!(4, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"78907"); - - blob.seek(SeekFrom::End(-6)).unwrap(); - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"56789"); - - blob.reopen(rowid).unwrap(); - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"Clob5"); - - // should not be able to seek negative or past end - assert!(blob.seek(SeekFrom::Current(-20)).is_err()); - assert!(blob.seek(SeekFrom::End(0)).is_ok()); - assert!(blob.seek(SeekFrom::Current(1)).is_err()); - - // write_all should detect when we return Ok(0) because there is no space left, - // and return a write error - blob.reopen(rowid).unwrap(); - assert!(blob.write_all(b"0123456789x").is_err()); - } - - #[test] - fn test_blob_in_bufreader() { - let (db, rowid) = db_with_test_blob().unwrap(); - - let mut blob = db - .blob_open(DatabaseName::Main, "test", "content", rowid, false) - .unwrap(); - assert_eq!(8, blob.write(b"one\ntwo\n").unwrap()); - - blob.reopen(rowid).unwrap(); - let mut reader = BufReader::new(blob); - - let mut line = String::new(); - assert_eq!(4, reader.read_line(&mut line).unwrap()); - assert_eq!("one\n", line); - - line.truncate(0); - assert_eq!(4, reader.read_line(&mut line).unwrap()); - assert_eq!("two\n", line); - - line.truncate(0); - assert_eq!(2, reader.read_line(&mut line).unwrap()); - assert_eq!("\0\0", line); - } - - #[test] - fn test_blob_in_bufwriter() { - let (db, rowid) = db_with_test_blob().unwrap(); - - { - let blob = db - .blob_open(DatabaseName::Main, "test", "content", rowid, false) - .unwrap(); - let mut writer = BufWriter::new(blob); - - // trying to write too much and then flush should fail - assert_eq!(8, writer.write(b"01234567").unwrap()); - assert_eq!(8, writer.write(b"01234567").unwrap()); - assert!(writer.flush().is_err()); - } - - { - // ... but it should've written the first 10 bytes - let mut blob = db - .blob_open(DatabaseName::Main, "test", "content", rowid, false) - .unwrap(); - let mut bytes = [0u8; 10]; - assert_eq!(10, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(b"0123456701", &bytes); - } - - { - let blob = db - .blob_open(DatabaseName::Main, "test", "content", rowid, false) - .unwrap(); - let mut writer = BufWriter::new(blob); - - // trying to write_all too much should fail - writer.write_all(b"aaaaaaaaaabbbbb").unwrap(); - assert!(writer.flush().is_err()); - } - - { - // ... but it should've written the first 10 bytes - let mut blob = db - .blob_open(DatabaseName::Main, "test", "content", rowid, false) - .unwrap(); - let mut bytes = [0u8; 10]; - assert_eq!(10, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(b"aaaaaaaaaa", &bytes); - } - } -} -- cgit v1.2.3