Skip to main content

tank_core/
executor.rs

1use crate::{
2    AsEntity, AsQuery, Driver, DynQuery, Query, QueryResult, RawQuery, Result, Row, RowsAffected,
3    stream::{Stream, StreamExt},
4    writer::SqlWriter,
5};
6use anyhow::anyhow;
7use convert_case::{Case, Casing};
8use std::{
9    future::{self, Future},
10    mem,
11    pin::{Pin, pin},
12    task::{self, Poll},
13};
14
15/// Stream adapter returned by [`Executor::fetch`], filtering [`Executor::run`] to rows.
16struct Fetched<S>(S);
17
18impl<S, R> Stream for Fetched<S>
19where
20    S: Stream<Item = Result<QueryResult<R>>>,
21{
22    type Item = Result<R>;
23
24    #[inline(always)]
25    fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
26        let mut inner = unsafe { self.map_unchecked_mut(|s| &mut s.0) };
27        loop {
28            match inner.as_mut().poll_next(cx) {
29                Poll::Ready(Some(Ok(QueryResult::Row(row)))) => return Poll::Ready(Some(Ok(row))),
30                Poll::Ready(Some(Ok(QueryResult::Affected(_)))) => {}
31                Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
32                Poll::Ready(None) => return Poll::Ready(None),
33                Poll::Pending => return Poll::Pending,
34            }
35        }
36    }
37
38    #[inline(always)]
39    fn size_hint(&self) -> (usize, Option<usize>) {
40        self.0.size_hint()
41    }
42}
43
44/// Async query execution.
45///
46/// Implemented by connections.
47pub trait Executor: Send {
48    /// Associated driver.
49    type Driver: Driver;
50
51    /// Checks if the driver supports multiple SQL statements in a single request.
52    fn accepts_multiple_statements(&self) -> bool {
53        true
54    }
55
56    /// Returns the driver instance associated with this executor.
57    fn driver(&self) -> Self::Driver {
58        Default::default()
59    }
60
61    /// Prepares a query for execution, returning a handle to the prepared statement.
62    fn prepare<'s>(
63        &'s mut self,
64        query: impl AsQuery<Self::Driver> + 's,
65    ) -> impl Future<Output = Result<Query<Self::Driver>>> + Send {
66        let mut query = query.as_query();
67        let query = mem::take(query.as_mut());
68        async {
69            match query {
70                Query::Raw(RawQuery(sql)) => self.do_prepare(sql).await,
71                Query::Prepared(..) => Ok(query),
72            }
73        }
74    }
75
76    /// Internal hook for implementing prepared statement support.
77    fn do_prepare(
78        &mut self,
79        _sql: String,
80    ) -> impl Future<Output = Result<Query<Self::Driver>>> + Send {
81        future::ready(Err(anyhow!(
82            "{} does not support prepare",
83            self.driver().name().to_case(Case::Pascal)
84        )))
85    }
86
87    /// Executes a query and streams the results (rows or affected counts).
88    fn run<'s>(
89        &'s mut self,
90        query: impl AsQuery<Self::Driver> + 's,
91    ) -> impl Stream<Item = Result<QueryResult<impl Row + 's>>> + Send;
92
93    /// Executes a query and streams the resulting rows, ignoring affected counts.
94    fn fetch<'s>(
95        &'s mut self,
96        query: impl AsQuery<Self::Driver> + 's,
97    ) -> impl Stream<Item = Result<impl Row + 's>> + Send {
98        Fetched(self.run(query))
99    }
100
101    /// Executes a query and returns the total number of affected rows.
102    fn execute<'s>(
103        &'s mut self,
104        query: impl AsQuery<Self::Driver> + 's,
105    ) -> impl Future<Output = Result<RowsAffected>> + Send {
106        let stream = self.run(query);
107        async move {
108            let mut stream = pin!(stream);
109            let mut affected = RowsAffected::default();
110            while let Some(item) = stream.next().await {
111                if let QueryResult::Affected(a) = item? {
112                    affected.extend([a]);
113                }
114            }
115            Ok(affected)
116        }
117    }
118
119    /// Efficiently inserts a collection of entities bypassing regular SQL execution when supported by the driver.
120    fn append<It>(&mut self, entities: It) -> impl Future<Output = Result<RowsAffected>> + Send
121    where
122        It: IntoIterator + Send,
123        It::IntoIter: Send,
124        It::Item: AsEntity,
125    {
126        let mut query = DynQuery::default();
127        self.driver()
128            .sql_writer()
129            .write_insert(&mut query, entities, false);
130        self.execute(query)
131    }
132}
133
134impl<S: Executor + ?Sized> Executor for &mut S {
135    type Driver = S::Driver;
136
137    fn accepts_multiple_statements(&self) -> bool {
138        (**self).accepts_multiple_statements()
139    }
140
141    fn driver(&self) -> Self::Driver {
142        (**self).driver()
143    }
144
145    fn prepare<'s>(
146        &'s mut self,
147        query: impl AsQuery<Self::Driver> + 's,
148    ) -> impl Future<Output = Result<Query<Self::Driver>>> + Send {
149        (**self).prepare(query)
150    }
151
152    fn do_prepare(
153        &mut self,
154        sql: String,
155    ) -> impl Future<Output = Result<Query<Self::Driver>>> + Send {
156        (**self).do_prepare(sql)
157    }
158
159    fn run<'s>(
160        &'s mut self,
161        query: impl AsQuery<Self::Driver> + 's,
162    ) -> impl Stream<Item = Result<QueryResult<impl Row + 's>>> + Send {
163        (**self).run(query)
164    }
165
166    fn fetch<'s>(
167        &'s mut self,
168        query: impl AsQuery<Self::Driver> + 's,
169    ) -> impl Stream<Item = Result<impl Row + 's>> + Send {
170        (**self).fetch(query)
171    }
172
173    fn execute<'s>(
174        &'s mut self,
175        query: impl AsQuery<Self::Driver> + 's,
176    ) -> impl Future<Output = Result<RowsAffected>> + Send {
177        (**self).execute(query)
178    }
179
180    fn append<It>(&mut self, entities: It) -> impl Future<Output = Result<RowsAffected>> + Send
181    where
182        It: IntoIterator + Send,
183        It::IntoIter: Send,
184        It::Item: AsEntity,
185    {
186        (**self).append(entities)
187    }
188}