1- use deadpool_postgres:: Object ;
2- use pyo3:: {
3- pyclass, pymethods,
4- types:: { PyList , PyString } ,
5- Py , PyAny , PyObject , PyRef , PyRefMut , Python ,
1+ use super :: {
2+ cursor:: Cursor ,
3+ transaction_options:: { IsolationLevel , ReadVariant } ,
64} ;
7- use std:: { collections:: HashSet , sync:: Arc , vec} ;
8- use tokio_postgres:: types:: ToSql ;
9-
105use crate :: {
116 common:: rustengine_future,
127 exceptions:: rust_errors:: { RustPSQLDriverError , RustPSQLDriverPyResult } ,
13- query_result:: PSQLDriverPyQueryResult ,
8+ query_result:: { PSQLDriverPyQueryResult , PSQLDriverSinglePyQueryResult } ,
149 value_converter:: { convert_parameters, PythonDTO } ,
1510} ;
16-
17- use super :: {
18- cursor:: Cursor ,
19- transaction_options:: { IsolationLevel , ReadVariant } ,
11+ use deadpool_postgres:: Object ;
12+ use pyo3:: {
13+ pyclass, pymethods,
14+ types:: { PyList , PyString } ,
15+ Py , PyAny , PyErr , PyObject , PyRef , PyRefMut , Python ,
2016} ;
21-
17+ use std:: { collections:: HashSet , sync:: Arc , vec} ;
18+ use tokio_postgres:: types:: ToSql ;
2219/// Transaction for internal use only.
2320///
2421/// It is not exposed to python.
@@ -166,7 +163,56 @@ impl RustTransaction {
166163
167164 Ok ( ( ) )
168165 }
166+ /// Fetch single row from query.
167+ ///
168+ /// Method doesn't acquire lock on any structure fields.
169+ /// It prepares and caches querystring in the inner Object object.
170+ ///
171+ /// Then execute the query.
172+ ///
173+ /// # Errors
174+ /// May return Err Result if:
175+ /// 1) Transaction is not started
176+ /// 2) Transaction is done already
177+ /// 3) Can not create/retrieve prepared statement
178+ /// 4) Can not execute statement
179+ pub async fn inner_fetch_row (
180+ & self ,
181+ querystring : String ,
182+ parameters : Vec < PythonDTO > ,
183+ ) -> RustPSQLDriverPyResult < PSQLDriverSinglePyQueryResult > {
184+ let db_client_arc = self . db_client . clone ( ) ;
185+ let is_started_arc = self . is_started . clone ( ) ;
186+ let is_done_arc = self . is_done . clone ( ) ;
169187
188+ let db_client_guard = db_client_arc. read ( ) . await ;
189+ let is_started_guard = is_started_arc. read ( ) . await ;
190+ let is_done_guard = is_done_arc. read ( ) . await ;
191+
192+ if !* is_started_guard {
193+ return Err ( RustPSQLDriverError :: DataBaseTransactionError (
194+ "Transaction is not started, please call begin() on transaction" . into ( ) ,
195+ ) ) ;
196+ }
197+ if * is_done_guard {
198+ return Err ( RustPSQLDriverError :: DataBaseTransactionError (
199+ "Transaction is already committed or rolled back" . into ( ) ,
200+ ) ) ;
201+ }
202+
203+ let mut vec_parameters: Vec < & ( dyn ToSql + Sync ) > = Vec :: with_capacity ( parameters. len ( ) ) ;
204+ for param in & parameters {
205+ vec_parameters. push ( param) ;
206+ }
207+
208+ let statement = db_client_guard. prepare_cached ( & querystring) . await ?;
209+
210+ let result = db_client_guard
211+ . query ( & statement, & vec_parameters. into_boxed_slice ( ) )
212+ . await ?;
213+
214+ Ok ( PSQLDriverSinglePyQueryResult :: new ( result) )
215+ }
170216 /// Start transaction
171217 /// Set up isolation level if specified
172218 /// Set up deferable if specified
@@ -599,7 +645,8 @@ impl Transaction {
599645 let transaction_arc = slf. transaction . clone ( ) ;
600646 let transaction_arc2 = slf. transaction . clone ( ) ;
601647 let is_no_exc = exception. is_none ( ) ;
602- let exc_message = format ! ( "{exception}" ) ;
648+ let py_err = PyErr :: from_value ( exception) ;
649+
603650 rustengine_future ( py, async move {
604651 let transaction_guard = transaction_arc. read ( ) . await ;
605652 if is_no_exc {
@@ -609,7 +656,7 @@ impl Transaction {
609656 } )
610657 } else {
611658 transaction_guard. inner_rollback ( ) . await ?;
612- Err ( RustPSQLDriverError :: DataBaseTransactionError ( exc_message ) )
659+ Err ( RustPSQLDriverError :: PyError ( py_err ) )
613660 }
614661 } )
615662 }
@@ -673,6 +720,33 @@ impl Transaction {
673720 . await
674721 } )
675722 }
723+ /// Execute querystring with parameters and return first row.
724+ ///
725+ /// It converts incoming parameters to rust readable,
726+ /// executes query with them and returns first row of response.
727+ ///
728+ /// # Errors
729+ ///
730+ /// May return Err Result if:
731+ /// 1) Cannot convert python parameters
732+ /// 2) Cannot execute querystring.
733+ pub fn fetch_row < ' a > (
734+ & ' a self ,
735+ py : Python < ' a > ,
736+ querystring : String ,
737+ parameters : Option < & ' a PyList > ,
738+ ) -> RustPSQLDriverPyResult < & PyAny > {
739+ let transaction_arc = self . transaction . clone ( ) ;
740+ let mut params: Vec < PythonDTO > = vec ! [ ] ;
741+ if let Some ( parameters) = parameters {
742+ params = convert_parameters ( parameters) ?;
743+ }
744+
745+ rustengine_future ( py, async move {
746+ let transaction_guard = transaction_arc. read ( ) . await ;
747+ transaction_guard. inner_fetch_row ( querystring, params) . await
748+ } )
749+ }
676750
677751 /// Start the transaction.
678752 ///
0 commit comments