use core::{fmt, mem};
use sqlite::{
SQLITE_OPEN_CREATE, SQLITE_OPEN_FULLMUTEX, SQLITE_OPEN_NOFOLLOW, SQLITE_OPEN_NOMUTEX,
SQLITE_OPEN_READONLY, SQLITE_OPEN_READWRITE, SQLITE_OPEN_URI,
};
use crate::{
endpoint::{Endpoint, IntoEndpoint, Local, Vfs},
error::Result,
ffi,
param::Parameters,
statement::{PrepareOptions, Statement},
};
#[doc = concat!("assert_eq!(\"", env!("SQUIRE_SQLITE_VERSION"), "\", version);")]
pub struct Connection {
inner: ffi::Connection,
}
impl Connection {
#[inline]
#[must_use]
fn new(inner: ffi::Connection) -> Self {
Self { inner }
}
#[must_use = "a Connection will be closed if dropped"]
pub fn open<E: IntoEndpoint>(endpoint: E) -> Result<Self> {
let endpoint = endpoint.into_endpoint();
let connection = ffi::Connection::open(
endpoint.location(),
DEFAULT_OPEN_MODE | endpoint.flags(),
endpoint.vfs(),
)?;
Ok(Connection::new(connection))
}
#[must_use]
pub fn builder<E: IntoEndpoint>(endpoint: E) -> ConnectionBuilder<E::Endpoint> {
ConnectionBuilder::new(endpoint.into_endpoint())
}
#[must_use = "a Statement will be finalized if dropped"]
pub fn prepare(&self, query: impl AsRef<str>) -> Result<Statement<'_>> {
Statement::prepare(self, query, PrepareOptions::transient())
}
pub fn execute<P: for<'a> Parameters<'a>>(
&self,
query: impl AsRef<str>,
parameters: P,
) -> Result<isize> {
let changes = self.prepare(query)?.query(parameters)?.run()?;
Ok(changes)
}
pub fn close(mut self) -> Result<()> {
let result = unsafe { self.dispose() };
mem::forget(self); result
}
unsafe fn dispose(&mut self) -> Result<()> {
unsafe { self.inner.dispose() }
}
#[inline]
pub fn internal_ref(&self) -> &ffi::Connection {
&self.inner
}
}
impl ffi::Connected for Connection {
fn as_connection_ptr(&self) -> *mut sqlite::sqlite3 {
self.internal_ref().as_ptr()
}
}
impl fmt::Debug for Connection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Connection({:p})", self.internal_ref().as_ptr())
}
}
impl Drop for Connection {
fn drop(&mut self) {
let _ = unsafe { self.dispose() };
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct ConnectionBuilder<E: Endpoint = Local> {
endpoint: E,
flags: i32,
}
#[cfg(feature = "serialized")]
const DEFAULT_OPEN_MODE: i32 = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
#[cfg(all(feature = "multi-thread", not(feature = "serialized")))]
const DEFAULT_OPEN_MODE: i32 = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX;
#[cfg(not(feature = "multi-thread"))]
const DEFAULT_OPEN_MODE: i32 = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
const FILE_OPEN_MODES: i32 = SQLITE_OPEN_READONLY | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
const CONCURRENCY_MODES: i32 = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_NOMUTEX;
impl<E: Endpoint> ConnectionBuilder<E> {
const fn new(endpoint: E) -> Self {
Self {
endpoint,
flags: DEFAULT_OPEN_MODE,
}
}
pub fn open(&self) -> Result<Connection> {
let connection = ffi::Connection::open(
self.endpoint.location(),
self.flags | self.endpoint.flags(),
self.endpoint.vfs(),
)?;
Ok(Connection::new(connection))
}
#[doc(alias = "SQLITE_OPEN_READONLY")]
pub fn read_only(self) -> Self {
self.with_open_mode(SQLITE_OPEN_READONLY)
}
#[doc(alias = "SQLITE_OPEN_CREATE")]
#[doc(alias = "SQLITE_OPEN_READWRITE")]
pub fn read_write(self, create: bool) -> Self {
self.with_open_mode(if create {
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
} else {
SQLITE_OPEN_READWRITE
})
}
#[doc(alias = "SQLITE_OPEN_NOFOLLOW")]
pub fn follow_symbolic_links(self, follow: bool) -> Self {
let flags = if follow {
self.flags | SQLITE_OPEN_NOFOLLOW
} else {
self.flags & !SQLITE_OPEN_NOFOLLOW
};
self.with_flags(flags)
}
#[doc(alias = "SQLITE_OPEN_FULLMUTEX")]
#[doc(alias = "SQLITE_OPEN_NOMUTEX")]
pub fn mutex(self, enable: bool) -> Self {
self.with_concurrency_mode(if enable {
SQLITE_OPEN_FULLMUTEX
} else {
SQLITE_OPEN_NOMUTEX
})
}
#[doc(alias = "SQLITE_OPEN_URI")]
pub fn uri_filenames(self, enable: bool) -> Self {
let flags = if enable {
self.flags | SQLITE_OPEN_URI
} else {
self.flags & !SQLITE_OPEN_URI
};
self.with_flags(flags)
}
pub fn vfs<L: ffi::Location>(
self,
vfs: impl ffi::IntoLocation<Location = L>,
) -> ConnectionBuilder<Vfs<E, L>> {
ConnectionBuilder {
endpoint: Vfs::new(self.endpoint, vfs),
flags: self.flags,
}
}
#[inline]
fn with_open_mode(self, flags: i32) -> Self {
let flags = (self.flags & !FILE_OPEN_MODES) | flags;
self.with_flags(flags)
}
#[inline]
fn with_concurrency_mode(self, flags: i32) -> Self {
let flags = (self.flags & !CONCURRENCY_MODES) | flags;
self.with_flags(flags)
}
#[inline]
fn with_flags(self, flags: i32) -> Self {
Self {
endpoint: self.endpoint,
flags,
}
}
}