use futures_core::future::BoxFuture;
use libsqlite3_sys::sqlite3_changes;
use crate::cursor::Cursor;
use crate::describe::{Column, Describe};
use crate::executor::{Execute, Executor, RefExecutor};
use crate::sqlite::cursor::SqliteCursor;
use crate::sqlite::statement::{Statement, Step};
use crate::sqlite::type_info::SqliteType;
use crate::sqlite::{Sqlite, SqliteConnection, SqliteTypeInfo};
impl SqliteConnection {
pub(super) fn prepare(
&mut self,
query: &mut &str,
persistent: bool,
) -> crate::Result<Option<usize>> {
if !persistent {
self.statement = Some(Statement::new(self, query, false)?);
return Ok(None);
}
if let Some(key) = self.statement_by_query.get(&**query) {
let statement = &mut self.statements[*key];
*query = &query[statement.tail..];
statement.reset();
return Ok(Some(*key));
}
let query_key = query.to_owned();
let statement = Statement::new(self, query, true)?;
let key = self.statements.len();
self.statement_by_query.insert(query_key, key);
self.statements.push(statement);
Ok(Some(key))
}
fn changes(&mut self) -> u64 {
let changes = unsafe { sqlite3_changes(self.handle()) };
changes as u64
}
#[inline]
pub(super) fn statement(&self, key: Option<usize>) -> &Statement {
match key {
Some(key) => &self.statements[key],
None => self.statement.as_ref().unwrap(),
}
}
#[inline]
pub(super) fn statement_mut(&mut self, key: Option<usize>) -> &mut Statement {
match key {
Some(key) => &mut self.statements[key],
None => self.statement.as_mut().unwrap(),
}
}
}
impl Executor for SqliteConnection {
type Database = Sqlite;
fn execute<'e, 'q: 'e, 'c: 'e, E: 'e>(
&'c mut self,
query: E,
) -> BoxFuture<'e, crate::Result<u64>>
where
E: Execute<'q, Self::Database>,
{
log_execution!(query, {
let (mut query, mut arguments) = query.into_parts();
Box::pin(async move {
loop {
let key = self.prepare(&mut query, arguments.is_some())?;
let statement = self.statement_mut(key);
if let Some(arguments) = &mut arguments {
statement.bind(arguments)?;
}
while let Step::Row = statement.step().await? {
}
if query.is_empty() {
break;
}
}
Ok(self.changes())
})
})
}
fn fetch<'q, E>(&mut self, query: E) -> SqliteCursor<'_, 'q>
where
E: Execute<'q, Self::Database>,
{
log_execution!(query, { SqliteCursor::from_connection(self, query) })
}
#[doc(hidden)]
fn describe<'e, 'q, E: 'e>(
&'e mut self,
query: E,
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>>
where
E: Execute<'q, Self::Database>,
{
Box::pin(async move {
let (mut query, _) = query.into_parts();
let key = self.prepare(&mut query, false)?;
let statement = self.statement_mut(key);
let num_params = statement.params();
let params = vec![None; num_params].into_boxed_slice();
let num_columns = statement.column_count();
let mut columns = Vec::with_capacity(num_columns);
for i in 0..num_columns {
let name = statement.column_name(i).to_owned();
let decl = statement.column_decltype(i);
let r#type = match decl {
None => None,
Some(decl) => match &*decl.to_ascii_lowercase() {
"bool" | "boolean" => Some(SqliteType::Boolean),
"clob" | "text" => Some(SqliteType::Text),
"blob" => Some(SqliteType::Blob),
"real" | "double" | "double precision" | "float" => Some(SqliteType::Float),
decl @ _ if decl.contains("int") => Some(SqliteType::Integer),
decl @ _ if decl.contains("char") => Some(SqliteType::Text),
_ => None,
},
};
columns.push(Column {
name: Some(name.into()),
non_null: statement.column_not_null(i)?,
table_id: None,
type_info: r#type.map(|r#type| SqliteTypeInfo {
r#type,
affinity: None,
}),
})
}
Ok(Describe {
param_types: params,
result_columns: columns.into_boxed_slice(),
})
})
}
}
impl<'e> RefExecutor<'e> for &'e mut SqliteConnection {
type Database = Sqlite;
fn fetch_by_ref<'q, E>(self, query: E) -> SqliteCursor<'e, 'q>
where
E: Execute<'q, Self::Database>,
{
log_execution!(query, { SqliteCursor::from_connection(self, query) })
}
}