aboutsummaryrefslogtreecommitdiff
path: root/src/blob/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/blob/mod.rs')
-rw-r--r--src/blob/mod.rs118
1 files changed, 61 insertions, 57 deletions
diff --git a/src/blob/mod.rs b/src/blob/mod.rs
index 7d7ec3d..81c6098 100644
--- a/src/blob/mod.rs
+++ b/src/blob/mod.rs
@@ -1,4 +1,4 @@
-//! `feature = "blob"` Incremental BLOB I/O.
+//! 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.
@@ -47,18 +47,18 @@
//! functions take a `&mut [MaybeUninit<u8>]` as the destination buffer,
//! where the "normal" functions take a `&mut [u8]`.
//!
-//! Using `MaybeUninit` here can be more efficient in some cases, but is
+//! Using `MaybeUninit` here can be more efficient in some cases, but is
//! often inconvenient, so both are provided.
//!
//! 2. Exact/inexact refers to to whether or not the entire buffer must be
//! filled in order for the call to be considered a success.
//!
//! The "exact" functions require the provided buffer be entirely filled, or
-//! they return an error, wheras the "inexact" functions read as much out of
+//! they return an error, whereas the "inexact" functions read as much out of
//! the blob as is available, and return how much they were able to read.
//!
-//! The inexact functions are preferrable if you do not know the size of the
-//! blob already, and the exact functions are preferrable if you do.
+//! The inexact functions are preferable if you do not know the size of the
+//! blob already, and the exact functions are preferable if you do.
//!
//! ### Comparison to using the `std::io` traits:
//!
@@ -101,7 +101,7 @@
//!
//! ```rust
//! # use rusqlite::blob::ZeroBlob;
-//! # use rusqlite::{Connection, DatabaseName, NO_PARAMS};
+//! # use rusqlite::{Connection, DatabaseName};
//! # use std::error::Error;
//! # use std::io::{Read, Seek, SeekFrom, Write};
//! # fn main() -> Result<(), Box<dyn Error>> {
@@ -111,10 +111,7 @@
//! // Insert a BLOB into the `content` column of `test_table`. Note that the Blob
//! // I/O API provides no way of inserting or resizing BLOBs in the DB -- this
//! // must be done via SQL.
-//! db.execute(
-//! "INSERT INTO test_table (content) VALUES (ZEROBLOB(10))",
-//! NO_PARAMS,
-//! )?;
+//! db.execute("INSERT INTO test_table (content) VALUES (ZEROBLOB(10))", [])?;
//!
//! // Get the row id off the BLOB we just inserted.
//! let rowid = db.last_insert_rowid();
@@ -136,7 +133,10 @@
//!
//! // Insert another BLOB, this time using a parameter passed in from
//! // rust (potentially with a dynamic size).
-//! db.execute("INSERT INTO test_table (content) VALUES (?)", &[ZeroBlob(64)])?;
+//! db.execute(
+//! "INSERT INTO test_table (content) VALUES (?)",
+//! [ZeroBlob(64)],
+//! )?;
//!
//! // given a new row ID, we can reopen the blob on that row
//! let rowid = db.last_insert_rowid();
@@ -151,7 +151,7 @@
//!
//! ```rust
//! # use rusqlite::blob::ZeroBlob;
-//! # use rusqlite::{Connection, DatabaseName, NO_PARAMS};
+//! # use rusqlite::{Connection, DatabaseName};
//! # use std::error::Error;
//! # fn main() -> Result<(), Box<dyn Error>> {
//! let db = Connection::open_in_memory()?;
@@ -159,10 +159,7 @@
//! // Insert a blob into the `content` column of `test_table`. Note that the Blob
//! // I/O API provides no way of inserting or resizing blobs in the DB -- this
//! // must be done via SQL.
-//! db.execute(
-//! "INSERT INTO test_table (content) VALUES (ZEROBLOB(10))",
-//! NO_PARAMS,
-//! )?;
+//! db.execute("INSERT INTO test_table (content) VALUES (ZEROBLOB(10))", [])?;
//! // Get the row id off the blob we just inserted.
//! let rowid = db.last_insert_rowid();
//! // Open the blob we just inserted for IO.
@@ -177,7 +174,10 @@
//!
//! // Insert another blob, this time using a parameter passed in from
//! // rust (potentially with a dynamic size).
-//! db.execute("INSERT INTO test_table (content) VALUES (?)", &[ZeroBlob(64)])?;
+//! db.execute(
+//! "INSERT INTO test_table (content) VALUES (?)",
+//! [ZeroBlob(64)],
+//! )?;
//!
//! // given a new row ID, we can reopen the blob on that row
//! let rowid = db.last_insert_rowid();
@@ -196,8 +196,8 @@ use crate::{Connection, DatabaseName, Result};
mod pos_io;
-/// `feature = "blob"` Handle to an open BLOB. See [`rusqlite::blob`](crate::blob) documentation for
-/// in-depth discussion.
+/// Handle to an open BLOB. See
+/// [`rusqlite::blob`](crate::blob) documentation for in-depth discussion.
pub struct Blob<'conn> {
conn: &'conn Connection,
blob: *mut ffi::sqlite3_blob,
@@ -206,7 +206,7 @@ pub struct Blob<'conn> {
}
impl Connection {
- /// `feature = "blob"` Open a handle to the BLOB located in `row_id`,
+ /// Open a handle to the BLOB located in `row_id`,
/// `column`, `table` in database `db`.
///
/// # Failure
@@ -214,6 +214,7 @@ impl Connection {
/// Will return `Err` if `db`/`table`/`column` cannot be converted to a
/// C-compatible string or if the underlying SQLite BLOB open call
/// fails.
+ #[inline]
pub fn blob_open<'a>(
&'a self,
db: DatabaseName<'_>,
@@ -222,9 +223,9 @@ impl Connection {
row_id: i64,
read_only: bool,
) -> Result<Blob<'a>> {
- let mut c = self.db.borrow_mut();
+ let c = self.db.borrow_mut();
let mut blob = ptr::null_mut();
- let db = db.to_cstring()?;
+ let db = db.as_cstring()?;
let table = super::str_to_cstring(table)?;
let column = super::str_to_cstring(column)?;
let rc = unsafe {
@@ -252,6 +253,7 @@ impl Blob<'_> {
/// # Failure
///
/// Will return `Err` if the underlying SQLite BLOB reopen call fails.
+ #[inline]
pub fn reopen(&mut self, row: i64) -> Result<()> {
let rc = unsafe { ffi::sqlite3_blob_reopen(self.blob, row) };
if rc != ffi::SQLITE_OK {
@@ -262,17 +264,23 @@ impl Blob<'_> {
}
/// Return the size in bytes of the BLOB.
+ #[inline]
+ #[must_use]
pub fn size(&self) -> i32 {
unsafe { ffi::sqlite3_blob_bytes(self.blob) }
}
/// Return the current size in bytes of the BLOB.
+ #[inline]
+ #[must_use]
pub fn len(&self) -> usize {
use std::convert::TryInto;
self.size().try_into().unwrap()
}
/// Return true if the BLOB is empty.
+ #[inline]
+ #[must_use]
pub fn is_empty(&self) -> bool {
self.size() == 0
}
@@ -286,10 +294,12 @@ impl Blob<'_> {
/// # Failure
///
/// Will return `Err` if the underlying SQLite close call fails.
+ #[inline]
pub fn close(mut self) -> Result<()> {
self.close_()
}
+ #[inline]
fn close_(&mut self) -> Result<()> {
let rc = unsafe { ffi::sqlite3_blob_close(self.blob) };
self.blob = ptr::null_mut();
@@ -304,14 +314,14 @@ impl io::Read for Blob<'_> {
/// # Failure
///
/// Will return `Err` if the underlying SQLite read call fails.
+ #[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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_mut_ptr() as *mut _, n, self.pos) };
+ let rc = unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_mut_ptr().cast(), n, self.pos) };
self.conn
.decode_result(rc)
.map(|_| {
@@ -334,6 +344,7 @@ impl io::Write for Blob<'_> {
/// # Failure
///
/// Will return `Err` if the underlying SQLite write call fails.
+ #[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let max_allowed_len = (self.size() - self.pos) as usize;
let n = min(buf.len(), max_allowed_len) as i32;
@@ -350,6 +361,7 @@ impl io::Write for Blob<'_> {
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}
+ #[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
@@ -357,6 +369,7 @@ impl io::Write for Blob<'_> {
impl io::Seek for Blob<'_> {
/// Seek to an offset, in bytes, in BLOB.
+ #[inline]
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
let pos = match pos {
io::SeekFrom::Start(offset) => offset as i64,
@@ -383,12 +396,13 @@ impl io::Seek for Blob<'_> {
#[allow(unused_must_use)]
impl Drop for Blob<'_> {
+ #[inline]
fn drop(&mut self) {
self.close_();
}
}
-/// `feature = "blob"` BLOB of length N that is filled with zeroes.
+/// 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.
@@ -398,6 +412,7 @@ impl Drop for Blob<'_> {
pub struct ZeroBlob(pub i32);
impl ToSql for ZeroBlob {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let ZeroBlob(length) = *self;
Ok(ToSqlOutput::ZeroBlob(length))
@@ -421,22 +436,18 @@ mod test {
}
#[test]
- fn test_blob() {
- let (db, rowid) = db_with_test_blob().unwrap();
+ fn test_blob() -> Result<()> {
+ let (db, rowid) = db_with_test_blob()?;
- let mut blob = db
- .blob_open(DatabaseName::Main, "test", "content", rowid, false)
- .unwrap();
+ let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
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.reopen(rowid)?;
+ blob.close()?;
- blob = db
- .blob_open(DatabaseName::Main, "test", "content", rowid, true)
- .unwrap();
+ blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)?;
let mut bytes = [0u8; 5];
assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
assert_eq!(&bytes, b"Clob5");
@@ -457,7 +468,7 @@ mod test {
assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
assert_eq!(&bytes, b"56789");
- blob.reopen(rowid).unwrap();
+ blob.reopen(rowid)?;
assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
assert_eq!(&bytes, b"Clob5");
@@ -468,20 +479,19 @@ mod test {
// write_all should detect when we return Ok(0) because there is no space left,
// and return a write error
- blob.reopen(rowid).unwrap();
+ blob.reopen(rowid)?;
assert!(blob.write_all(b"0123456789x").is_err());
+ Ok(())
}
#[test]
- fn test_blob_in_bufreader() {
- let (db, rowid) = db_with_test_blob().unwrap();
+ fn test_blob_in_bufreader() -> Result<()> {
+ let (db, rowid) = db_with_test_blob()?;
- let mut blob = db
- .blob_open(DatabaseName::Main, "test", "content", rowid, false)
- .unwrap();
+ let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
- blob.reopen(rowid).unwrap();
+ blob.reopen(rowid)?;
let mut reader = BufReader::new(blob);
let mut line = String::new();
@@ -495,16 +505,15 @@ mod test {
line.truncate(0);
assert_eq!(2, reader.read_line(&mut line).unwrap());
assert_eq!("\0\0", line);
+ Ok(())
}
#[test]
- fn test_blob_in_bufwriter() {
- let (db, rowid) = db_with_test_blob().unwrap();
+ fn test_blob_in_bufwriter() -> Result<()> {
+ let (db, rowid) = db_with_test_blob()?;
{
- let blob = db
- .blob_open(DatabaseName::Main, "test", "content", rowid, false)
- .unwrap();
+ let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
let mut writer = BufWriter::new(blob);
// trying to write too much and then flush should fail
@@ -515,18 +524,14 @@ mod test {
{
// ... but it should've written the first 10 bytes
- let mut blob = db
- .blob_open(DatabaseName::Main, "test", "content", rowid, false)
- .unwrap();
+ let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
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 blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
let mut writer = BufWriter::new(blob);
// trying to write_all too much should fail
@@ -536,12 +541,11 @@ mod test {
{
// ... but it should've written the first 10 bytes
- let mut blob = db
- .blob_open(DatabaseName::Main, "test", "content", rowid, false)
- .unwrap();
+ let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?;
let mut bytes = [0u8; 10];
assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
assert_eq!(b"aaaaaaaaaa", &bytes);
+ Ok(())
}
}
}