blob: 2d864cb43f1296f2071b827aea9b0bd3abd2a26b [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use super::{execute, ConnectionState};
use crate::{Sqlite, SqliteQueryResult, SqliteRow, SqliteStatement, SqliteTypeInfo};
use futures_core::{future::BoxFuture, stream::BoxStream};
use futures_util::{stream, FutureExt, StreamExt};
use libsqlite3_sys::sqlite3;
use sqlx_core::{
error::Error,
executor::{Execute, Executor},
sql_str::SqlStr,
Either,
};
use std::{fmt, future, ptr::NonNull};
/// Exclusive, callback-local access to an existing SQLite worker connection.
///
/// Queries use the same execution engine and bounded prepared statement cache as
/// ordinary SQLx queries. Their futures and streams are immediately ready: they
/// perform SQLite work on the calling worker and never enqueue another command.
/// This is not a separate connection or transaction owner. A callback must not
/// control transactions or wait for other work on this connection.
///
/// Instances and borrowed streams cannot outlive the worker callback. Owned rows
/// have the same lifetime guarantees as rows returned by ordinary SQLx queries.
pub struct SqliteWorkerConnection<'a> {
pub(super) conn: &'a mut ConnectionState,
}
impl fmt::Debug for SqliteWorkerConnection<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SqliteWorkerConnection").finish_non_exhaustive()
}
}
impl SqliteWorkerConnection<'_> {
/// Borrow the original connection handle exclusively for this callback.
///
/// The same unsafe obligations as `LockedSqliteHandle::as_raw_handle` apply:
/// no handle-backed values may escape, no closing or transaction control,
/// and no invalidation of SQLx-owned statements or hooks. Temporary raw
/// statements/wrappers must be dropped before using the SQLx executor again.
pub fn as_raw_handle(&mut self) -> NonNull<sqlite3> {
self.conn.handle.as_non_null_ptr()
}
}
impl<'c> Executor<'c> for &'c mut SqliteWorkerConnection<'_> {
type Database = Sqlite;
fn fetch_many<'e, 'q, E>(
self,
mut query: E,
) -> BoxStream<'e, Result<Either<SqliteQueryResult, SqliteRow>, Error>>
where
'c: 'e,
E: Execute<'q, Sqlite> + 'q,
'q: 'e,
{
let arguments = match query.take_arguments().map_err(Error::Encode) {
Ok(arguments) => arguments,
Err(error) => return stream::once(future::ready(Err(error))).boxed(),
};
let persistent = query.persistent() && arguments.is_some();
match execute::iter(self.conn, query.sql(), arguments, persistent) {
Ok(mut iter) => {
let mut finished = false;
stream::iter(std::iter::from_fn(move || {
if finished { return None; }
let result = iter.next();
finished = result.as_ref().is_none_or(Result::is_err);
result
})).boxed()
},
Err(error) => stream::once(future::ready(Err(error))).boxed(),
}
}
fn fetch_optional<'e, 'q, E>(
self,
mut query: E,
) -> BoxFuture<'e, Result<Option<SqliteRow>, Error>>
where
'c: 'e,
E: Execute<'q, Sqlite> + 'q,
'q: 'e,
{
Box::pin(async move {
let arguments = query.take_arguments().map_err(Error::Encode)?;
let persistent = query.persistent() && arguments.is_some();
let iter = execute::iter(self.conn, query.sql(), arguments, persistent)?;
for result in iter {
if let Either::Right(row) = result? {
return Ok(Some(row));
}
}
Ok(None)
})
}
fn prepare_with<'e>(
self,
sql: SqlStr,
_parameters: &[SqliteTypeInfo],
) -> BoxFuture<'e, Result<SqliteStatement, Error>>
where
'c: 'e,
{
future::ready(super::worker::prepare(self.conn, sql)).boxed()
}
#[doc(hidden)]
#[cfg(feature = "offline")]
fn describe<'e>(
self,
sql: SqlStr,
) -> BoxFuture<'e, Result<sqlx_core::describe::Describe<Sqlite>, Error>>
where
'c: 'e,
{
future::ready(super::describe::describe(self.conn, sql)).boxed()
}
}