1use crate::copy_out::CopyOutStream;
2use crate::query::RowStream;
3#[cfg(feature = "runtime")]
4use crate::tls::MakeTlsConnect;
5use crate::tls::TlsConnect;
6use crate::types::{BorrowToSql, ToSql, Type};
7#[cfg(feature = "runtime")]
8use crate::Socket;
9use crate::{
10 bind, query, slice_iter, CancelToken, Client, CopyInSink, Error, Portal, Row,
11 SimpleQueryMessage, Statement, ToStatement,
12};
13use bytes::Buf;
14use futures_util::TryStreamExt;
15use tokio::io::{AsyncRead, AsyncWrite};
16
17pub struct Transaction<'a> {
22 client: &'a mut Client,
23 savepoint: Option<Savepoint>,
24 done: bool,
25}
26
27struct Savepoint {
29 name: String,
30 depth: u32,
31}
32
33impl Drop for Transaction<'_> {
34 fn drop(&mut self) {
35 if self.done {
36 return;
37 }
38
39 let name = self.savepoint.as_ref().map(|sp| sp.name.as_str());
40 self.client.__private_api_rollback(name);
41 }
42}
43
44impl<'a> Transaction<'a> {
45 pub(crate) fn new(client: &'a mut Client) -> Transaction<'a> {
46 Transaction {
47 client,
48 savepoint: None,
49 done: false,
50 }
51 }
52
53 pub async fn commit(mut self) -> Result<(), Error> {
55 self.done = true;
56 let query = if let Some(sp) = self.savepoint.as_ref() {
57 format!("RELEASE {}", sp.name)
58 } else {
59 "COMMIT".to_string()
60 };
61 self.client.batch_execute(&query).await
62 }
63
64 pub async fn rollback(mut self) -> Result<(), Error> {
68 self.done = true;
69 let query = if let Some(sp) = self.savepoint.as_ref() {
70 format!("ROLLBACK TO {}", sp.name)
71 } else {
72 "ROLLBACK".to_string()
73 };
74 self.client.batch_execute(&query).await
75 }
76
77 pub async fn prepare(&self, query: &str) -> Result<Statement, Error> {
79 self.client.prepare(query).await
80 }
81
82 pub async fn prepare_typed(
84 &self,
85 query: &str,
86 parameter_types: &[Type],
87 ) -> Result<Statement, Error> {
88 self.client.prepare_typed(query, parameter_types).await
89 }
90
91 pub async fn query<T>(
93 &self,
94 statement: &T,
95 params: &[&(dyn ToSql + Sync)],
96 ) -> Result<Vec<Row>, Error>
97 where
98 T: ?Sized + ToStatement,
99 {
100 self.client.query(statement, params).await
101 }
102
103 pub async fn query_one<T>(
105 &self,
106 statement: &T,
107 params: &[&(dyn ToSql + Sync)],
108 ) -> Result<Row, Error>
109 where
110 T: ?Sized + ToStatement,
111 {
112 self.client.query_one(statement, params).await
113 }
114
115 pub async fn query_opt<T>(
117 &self,
118 statement: &T,
119 params: &[&(dyn ToSql + Sync)],
120 ) -> Result<Option<Row>, Error>
121 where
122 T: ?Sized + ToStatement,
123 {
124 self.client.query_opt(statement, params).await
125 }
126
127 pub async fn query_raw<T, P, I>(&self, statement: &T, params: I) -> Result<RowStream, Error>
129 where
130 T: ?Sized + ToStatement,
131 P: BorrowToSql,
132 I: IntoIterator<Item = P>,
133 I::IntoIter: ExactSizeIterator,
134 {
135 self.client.query_raw(statement, params).await
136 }
137
138 pub async fn query_raw_with_format<T, P, I>(
140 &self,
141 statement: &T,
142 params: I,
143 result_format: crate::ResultFormat,
144 ) -> Result<RowStream, Error>
145 where
146 T: ?Sized + ToStatement,
147 P: BorrowToSql,
148 I: IntoIterator<Item = P>,
149 I::IntoIter: ExactSizeIterator,
150 {
151 self.client
152 .query_raw_with_format(statement, params, result_format)
153 .await
154 }
155
156 pub async fn query_typed(
158 &self,
159 statement: &str,
160 params: &[(&(dyn ToSql + Sync), Type)],
161 ) -> Result<Vec<Row>, Error> {
162 self.client.query_typed(statement, params).await
163 }
164
165 pub async fn query_typed_raw<P, I>(&self, query: &str, params: I) -> Result<RowStream, Error>
167 where
168 P: BorrowToSql,
169 I: IntoIterator<Item = (P, Type)>,
170 {
171 self.client.query_typed_raw(query, params).await
172 }
173
174 pub async fn execute<T>(
176 &self,
177 statement: &T,
178 params: &[&(dyn ToSql + Sync)],
179 ) -> Result<u64, Error>
180 where
181 T: ?Sized + ToStatement,
182 {
183 self.client.execute(statement, params).await
184 }
185
186 pub async fn execute_raw<P, I, T>(&self, statement: &T, params: I) -> Result<u64, Error>
188 where
189 T: ?Sized + ToStatement,
190 P: BorrowToSql,
191 I: IntoIterator<Item = P>,
192 I::IntoIter: ExactSizeIterator,
193 {
194 self.client.execute_raw(statement, params).await
195 }
196
197 pub async fn bind<T>(
206 &self,
207 statement: &T,
208 params: &[&(dyn ToSql + Sync)],
209 ) -> Result<Portal, Error>
210 where
211 T: ?Sized + ToStatement,
212 {
213 self.bind_raw(statement, slice_iter(params)).await
214 }
215
216 pub async fn bind_raw<P, T, I>(&self, statement: &T, params: I) -> Result<Portal, Error>
220 where
221 T: ?Sized + ToStatement,
222 P: BorrowToSql,
223 I: IntoIterator<Item = P>,
224 I::IntoIter: ExactSizeIterator,
225 {
226 let statement = statement.__convert().into_statement(self.client).await?;
227 bind::bind(self.client.inner(), statement, params).await
228 }
229
230 pub async fn query_portal(&self, portal: &Portal, max_rows: i32) -> Result<Vec<Row>, Error> {
235 self.query_portal_raw(portal, max_rows)
236 .await?
237 .try_collect()
238 .await
239 }
240
241 pub async fn query_portal_raw(
245 &self,
246 portal: &Portal,
247 max_rows: i32,
248 ) -> Result<RowStream, Error> {
249 query::query_portal(self.client.inner(), portal, max_rows).await
250 }
251
252 pub async fn copy_in<T, U>(&self, statement: &T) -> Result<CopyInSink<U>, Error>
254 where
255 T: ?Sized + ToStatement,
256 U: Buf + 'static + Send,
257 {
258 self.client.copy_in(statement).await
259 }
260
261 pub async fn copy_out<T>(&self, statement: &T) -> Result<CopyOutStream, Error>
263 where
264 T: ?Sized + ToStatement,
265 {
266 self.client.copy_out(statement).await
267 }
268
269 pub async fn simple_query(&self, query: &str) -> Result<Vec<SimpleQueryMessage>, Error> {
271 self.client.simple_query(query).await
272 }
273
274 pub async fn batch_execute(&self, query: &str) -> Result<(), Error> {
276 self.client.batch_execute(query).await
277 }
278
279 pub fn cancel_token(&self) -> CancelToken {
281 self.client.cancel_token()
282 }
283
284 #[cfg(feature = "runtime")]
286 #[deprecated(since = "0.6.0", note = "use Transaction::cancel_token() instead")]
287 pub async fn cancel_query<T>(&self, tls: T) -> Result<(), Error>
288 where
289 T: MakeTlsConnect<Socket>,
290 {
291 #[allow(deprecated)]
292 self.client.cancel_query(tls).await
293 }
294
295 #[deprecated(since = "0.6.0", note = "use Transaction::cancel_token() instead")]
297 pub async fn cancel_query_raw<S, T>(&self, stream: S, tls: T) -> Result<(), Error>
298 where
299 S: AsyncRead + AsyncWrite + Unpin,
300 T: TlsConnect<S>,
301 {
302 #[allow(deprecated)]
303 self.client.cancel_query_raw(stream, tls).await
304 }
305
306 pub async fn transaction(&mut self) -> Result<Transaction<'_>, Error> {
308 self._savepoint(None).await
309 }
310
311 pub async fn savepoint<I>(&mut self, name: I) -> Result<Transaction<'_>, Error>
313 where
314 I: Into<String>,
315 {
316 self._savepoint(Some(name.into())).await
317 }
318
319 async fn _savepoint(&mut self, name: Option<String>) -> Result<Transaction<'_>, Error> {
320 let depth = self.savepoint.as_ref().map_or(0, |sp| sp.depth) + 1;
321 let name = name.unwrap_or_else(|| format!("sp_{}", depth));
322 let query = format!("SAVEPOINT {}", name);
323 self.batch_execute(&query).await?;
324
325 Ok(Transaction {
326 client: self.client,
327 savepoint: Some(Savepoint { name, depth }),
328 done: false,
329 })
330 }
331
332 pub fn client(&self) -> &Client {
334 self.client
335 }
336}