scopedb_client/
statement.rs1use std::time::Duration;
16
17use tokio::time::sleep;
18use uuid::Uuid;
19
20use crate::Error;
21use crate::ErrorKind;
22use crate::client::Client;
23use crate::protocol::StatementRequest;
24use crate::protocol::StatementStatus;
25use crate::result::ResultSet;
26
27#[derive(Debug)]
30pub struct Statement {
31 client: Client,
32 statement: String,
33 statement_id: Option<Uuid>,
34 max_elapsed: Option<Duration>,
35 max_estimated: Option<Duration>,
36}
37
38impl Statement {
39 pub fn with_statement_id(mut self, statement_id: Uuid) -> Self {
40 self.statement_id = Some(statement_id);
41 self
42 }
43
44 pub fn with_max_elapsed(mut self, duration: Duration) -> Self {
46 self.max_elapsed = Some(duration);
47 self
48 }
49
50 pub fn with_max_estimated(mut self, duration: Duration) -> Self {
52 self.max_estimated = Some(duration);
53 self
54 }
55
56 pub async fn submit(self) -> Result<StatementHandle, Error> {
60 if self.max_elapsed.is_some_and(|d| d.is_zero())
61 || self.max_estimated.is_some_and(|d| d.is_zero())
62 {
63 return Err(Error::new(
64 ErrorKind::ConfigInvalid,
65 "statement duration limits must be positive",
66 ));
67 }
68 let Statement {
69 client,
70 statement,
71 statement_id,
72 max_elapsed,
73 max_estimated,
74 } = self;
75
76 let snapshot = client
77 .submit_statement(StatementRequest {
78 statement,
79 statement_id,
80 max_elapsed_seconds: max_elapsed.map(|v| v.as_secs_f64()),
81 max_estimated_seconds: max_estimated.map(|v| v.as_secs_f64()),
82 format: "json",
83 })
84 .await
85 .map_err(|error| match statement_id {
86 Some(id) => error.set_statement_id(id),
87 None => error,
88 })?;
89
90 Ok(StatementHandle {
91 client,
92 statement_id: snapshot.statement_id(),
93 snapshot: Some(snapshot),
94 })
95 }
96
97 pub async fn execute(self) -> Result<ResultSet, Error> {
102 self.submit().await?.wait().await.map_err(|error| {
103 if error.is_retryable() {
104 error.set_persistent()
105 } else {
106 error
107 }
108 })
109 }
110
111 pub(crate) fn new(client: Client, statement: String) -> Self {
112 Self {
113 client,
114 statement,
115 statement_id: None,
116 max_elapsed: None,
117 max_estimated: None,
118 }
119 }
120}
121
122#[derive(Debug)]
137pub struct StatementHandle {
138 client: Client,
139 statement_id: Uuid,
140 snapshot: Option<StatementStatus>,
141}
142
143impl StatementHandle {
144 pub fn statement_id(&self) -> Uuid {
145 self.statement_id
146 }
147
148 pub fn snapshot(&self) -> Option<&StatementStatus> {
151 self.snapshot.as_ref()
152 }
153
154 pub async fn refresh(&mut self) -> Result<&StatementStatus, Error> {
157 if self
158 .snapshot
159 .as_ref()
160 .is_some_and(StatementStatus::is_terminated)
161 {
162 return Ok(self
163 .snapshot
164 .as_ref()
165 .expect("terminal snapshot is present"));
166 }
167
168 self.snapshot = Some(
169 self.client
170 .fetch_statement(self.statement_id)
171 .await
172 .map_err(|error| error.set_statement_id(self.statement_id))?,
173 );
174 Ok(self.snapshot.as_ref().expect("status was just assigned"))
175 }
176
177 pub async fn wait(&mut self) -> Result<ResultSet, Error> {
180 let mut delay = Duration::from_millis(5);
181 let max_delay = Duration::from_secs(1);
182
183 loop {
184 self.refresh().await?;
185
186 if let Some(status) = self.snapshot.as_ref() {
187 match status {
188 StatementStatus::Finished(finished) => return Ok(finished.result_set()),
189 StatementStatus::Failed(failed) => {
190 return Err(Error::new(
191 ErrorKind::StatementFailed,
192 failed.error.message.clone(),
193 )
194 .set_statement_id(self.statement_id)
195 .set_statement_details(failed.error.clone()));
196 }
197 StatementStatus::Cancelled(cancelled) => {
198 return Err(Error::new(
199 ErrorKind::StatementCancelled,
200 format!("statement {} was cancelled", cancelled.statement_id),
201 )
202 .set_statement_id(self.statement_id));
203 }
204 StatementStatus::Pending(..) | StatementStatus::Running(..) => {
205 sleep(delay).await;
206 if delay < max_delay {
207 delay = std::cmp::min(delay.saturating_mul(2), max_delay);
208 }
209 }
210 }
211 }
212 }
213 }
214
215 pub async fn cancel(&mut self) -> Result<StatementStatus, Error> {
219 if let Some(status) = self.snapshot.as_ref().filter(|s| s.is_terminated()) {
220 return Ok(status.clone());
221 }
222 let status = self
223 .client
224 .cancel_statement(self.statement_id)
225 .await
226 .map_err(|error| error.set_statement_id(self.statement_id))?;
227 self.snapshot = Some(status.clone());
228 Ok(status)
229 }
230
231 pub(crate) fn new(client: Client, statement_id: Uuid) -> Self {
232 Self {
233 client,
234 statement_id,
235 snapshot: None,
236 }
237 }
238}