Skip to main content

snowflake_connector_rs/statement/
handle.rs

1use std::{fmt, sync::Arc, time::Duration};
2
3use crate::{
4    Result,
5    result_cursor::{ResultCursor, TypedResultCursor},
6    result_table::{FromRow, RowPlanContext},
7};
8
9use super::{
10    StatementExecutor, StatementParts,
11    api::QueryApiClient,
12    cancel::{CancelDecision, QueryControl},
13};
14
15/// What a successful [`QueryCanceller::cancel`] call achieved.
16///
17/// This is a report, not a control-flow discriminant.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum QueryCancelStatus {
21    /// Cancellation won before the query submit request began; neither the query nor an abort request was sent.
22    NotSubmitted,
23    /// Snowflake accepted the abort request; query terminality is not implied.
24    Accepted,
25    /// The connector already processed a terminal query response; no abort request was sent.
26    AlreadyFinished,
27}
28
29/// An owned statement execution that can be explicitly cancelled through a [`QueryCanceller`].
30///
31/// Cancellation runs only through [`QueryCanceller::cancel`] on a canceller retained from [`Self::canceller`];
32/// dropping the execution future never cancels the remote query.
33pub struct QueryHandle {
34    statement: StatementParts,
35    executor: StatementExecutor,
36    control: Arc<QueryControl>,
37}
38
39/// A cloneable controller that cancels the query of the [`QueryHandle`] it was obtained from.
40///
41/// It remains usable from any task, even after the handle has been consumed by execution or the execution future
42/// has been dropped.
43#[derive(Clone)]
44pub struct QueryCanceller {
45    api: QueryApiClient,
46    control: Arc<QueryControl>,
47    request_timeout: Duration,
48}
49
50impl QueryHandle {
51    pub(crate) fn new(
52        statement: StatementParts,
53        executor: StatementExecutor,
54        control: Arc<QueryControl>,
55    ) -> Self {
56        Self {
57            statement,
58            executor,
59            control,
60        }
61    }
62
63    /// Returns the client-generated query request ID used by the submit and abort requests.
64    pub fn request_id(&self) -> &str {
65        self.control.query_request_id()
66    }
67
68    /// Returns a cloneable cancellation controller for this query.
69    pub fn canceller(&self) -> QueryCanceller {
70        QueryCanceller {
71            api: self.executor.api_client(),
72            control: Arc::clone(&self.control),
73            request_timeout: self.executor.cancel_request_timeout(),
74        }
75    }
76
77    /// Submits the statement and returns a streaming result cursor.
78    ///
79    /// # Errors
80    ///
81    /// Returns the same errors as [`Session::query`](crate::Session::query). If cancellation wins before submission,
82    /// this returns an [`ErrorKind::Cancelled`](crate::ErrorKind::Cancelled) error without sending either the query
83    /// or abort request.
84    pub async fn execute(self) -> Result<ResultCursor> {
85        let Self {
86            statement,
87            executor,
88            control,
89        } = self;
90        executor.execute(statement, control).await
91    }
92
93    /// Submits the statement and builds a typed streaming result cursor.
94    ///
95    /// # Errors
96    ///
97    /// Returns the same errors as [`Self::execute`]. After the statement succeeds, this also propagates plan-time
98    /// decode failures from [`FromRow::build_plan`] for `T`.
99    pub async fn execute_as<T>(self) -> Result<TypedResultCursor<T>>
100    where
101        T: FromRow,
102    {
103        let result = self.execute().await?;
104        let plan = T::build_plan(RowPlanContext::new(result.shared_schema()))?;
105        Ok(TypedResultCursor::new(result, plan))
106    }
107}
108
109impl fmt::Debug for QueryHandle {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        f.debug_struct("QueryHandle")
112            .field("request_id", &self.request_id())
113            .field("phase", &self.control.execution_phase())
114            .finish_non_exhaustive()
115    }
116}
117
118impl QueryCanceller {
119    /// Returns the client-generated query request ID targeted by this canceller.
120    pub fn request_id(&self) -> &str {
121        self.control.query_request_id()
122    }
123
124    /// Requests remote cancellation and waits for the abort response.
125    ///
126    /// On success, reports what the cancellation achieved as a [`QueryCancelStatus`].
127    ///
128    /// This method never returns an [`ErrorKind::Cancelled`](crate::ErrorKind::Cancelled) error; that error is what
129    /// [`QueryHandle::execute`] returns once the cancellation takes effect.
130    ///
131    /// # Errors
132    ///
133    /// Returns [`ErrorKind::Network`](crate::ErrorKind::Network), [`ErrorKind::Timeout`](crate::ErrorKind::Timeout),
134    /// [`ErrorKind::Server`](crate::ErrorKind::Server),
135    /// [`ErrorKind::SessionExpired`](crate::ErrorKind::SessionExpired), or
136    /// [`ErrorKind::Protocol`](crate::ErrorKind::Protocol). Transient transport failures are retried within the
137    /// configured cancellation deadline, and a failed call never caches a successful status, so a later call can
138    /// retry the cancellation.
139    pub async fn cancel(&self) -> Result<QueryCancelStatus> {
140        let _gate = self.control.lock_cancel_gate().await;
141
142        match self.control.begin_cancel_attempt() {
143            CancelDecision::Return(outcome) => Ok(outcome),
144            CancelDecision::SendAbort => {
145                self.api
146                    .abort_query(self.control.query_request_id(), self.request_timeout)
147                    .await?;
148                self.control.record_abort_accepted();
149                Ok(QueryCancelStatus::Accepted)
150            }
151        }
152    }
153}
154
155impl fmt::Debug for QueryCanceller {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        f.debug_struct("QueryCanceller")
158            .field("request_id", &self.request_id())
159            .field("phase", &self.control.execution_phase())
160            .finish_non_exhaustive()
161    }
162}
163
164const _: fn() = || {
165    fn assert_send_sync_static<T: Send + Sync + 'static>() {}
166    assert_send_sync_static::<QueryHandle>();
167    assert_send_sync_static::<QueryCanceller>();
168};