sqlsrv 0.14.0

Utility functions for managing SQLite connections in a server application.
Documentation
use std::{
  mem::ManuallyDrop,
  ops::{Deref, DerefMut},
  sync::Arc
};

use rusqlite::{Connection, params};

use crate::Error;

use super::{InnerWrConn, Shared};


/// SQLite connection object that can be used for operations that modify the
/// database.
///
/// The `WrConn` connection will be returned to the
/// [`ConnPool`](super::ConnPool) connection pool once it is dropped.
pub struct WrConn {
  /// The context buffer shared between the connection pool and `WrConn`.
  pub(super) sh: Arc<Shared>,

  /// The actual connection object.
  ///
  /// The connection object is placed in a `ManuallyDrop` so that it doesn't
  /// get closed (read: dropped) on `Drop`.  The connection will be closed
  /// when it is dropped by [`Shared`] (which is why it must also hold a
  /// strong reference to `Shared`).
  pub(super) inner: ManuallyDrop<InnerWrConn>
}

impl WrConn {
  /// Add dirt to the writer connection.
  // ToDo: broken-lint
  #[allow(clippy::missing_const_for_fn)]
  pub fn add_dirt(&mut self, weight: usize) {
    self.inner.dirt = self.inner.dirt.saturating_add(weight);
  }
}

impl WrConn {
  /// Run incremental vacuum.
  ///
  /// # Errors
  /// - [`Error::Sqlite`] is returned if an rusqlite error is encounterd.
  /// - [`Error::BadParam`] means the page count could not be converted to an
  ///   `usize`.
  pub fn incremental_vacuum(&self, n: Option<usize>) -> Result<(), Error> {
    // Some(usize) → Some(i64)
    let n = if let Some(n) = n {
      Some(
        i64::try_from(n)
          .map_err(|_| Error::bad_param("Invalid page count"))?
      )
    } else {
      None
    };


    n.map_or_else(
      || {
        self
          .inner
          .conn
          .execute("PRAGMA incremental_vacuum;", params![])
      },
      |n| {
        self
          .inner
          .conn
          .execute("PRAGMA incremental_vacuum(?);", params![n])
      }
    )
    .map(|_| ())
    .map_err(Error::Sqlite)
  }
}

impl Deref for WrConn {
  type Target = Connection;

  fn deref(&self) -> &Connection {
    &self.inner.conn
  }
}

impl DerefMut for WrConn {
  fn deref_mut(&mut self) -> &mut Connection {
    &mut self.inner.conn
  }
}

impl Drop for WrConn {
  /// Return the write connection to the connection pool.
  fn drop(&mut self) {
    let mut g = self.sh.inner.lock();

    // Take writer connection out of Self and put it back in ConnPool's Inner
    // structure.
    g.conn = Some(unsafe { ManuallyDrop::take(&mut self.inner) });
    self.sh.signal.notify_one();
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :