parsql_deadpool_postgres/
crud_ops.rs

1use deadpool_postgres::{Pool, PoolError};
2use postgres::types::FromSqlOwned;
3//use postgres::types::FromSql;
4use tokio_postgres::{Error, Row, types::FromSql};
5use crate::traits::{SqlQuery, SqlParams, UpdateParams, FromRow};
6
7// Daha basit bir yaklaşım: PoolError'dan genel bir Error oluştur
8fn pool_err_to_io_err(e: PoolError) -> Error {
9    // Bu özel fonksiyon tokio_postgres'in sağladığı timeout hatasını döndürür
10    // Güzel bir çözüm değil, ama çalışır bir örnek için kullanılabilir
11    let err = Error::__private_api_timeout();
12    
13    // Debug süreci için stderr'e hatayı yazdıralım
14    eprintln!("Pool bağlantı hatası: {}", e);
15    
16    err
17}
18
19/// # insert
20/// 
21/// Deadpool bağlantı havuzunu kullanarak veritabanına yeni bir kayıt ekler.
22/// 
23/// ## Parametreler
24/// - `pool`: Deadpool bağlantı havuzu
25/// - `entity`: Eklenecek veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
26/// 
27/// ## Dönüş Değeri
28/// - `Result<i64, Error>`: Başarılı olursa, eklenen kayıt ID'sini döndürür; başarısız olursa, Error döndürür
29/// 
30/// ## Yapı Tanımı
31/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
32/// 
33/// ```rust,no_run
34/// #[derive(Insertable, SqlParams)]  // Gerekli makrolar
35/// #[table("tablo_adi")]            // Ekleme yapılacak tablo adı
36/// pub struct VeriModeli {
37///     pub alan1: String,
38///     pub alan2: i32,
39///     // ...
40/// }
41/// ```
42/// 
43/// - `Insertable`: Otomatik olarak SQL INSERT ifadeleri oluşturur
44/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
45/// - `#[table("tablo_adi")]`: Ekleme yapılacak tablo adını belirtir
46/// 
47/// ## Kullanım Örneği
48/// ```rust,no_run
49/// use deadpool_postgres::{Config, Runtime, Pool};
50/// use tokio_postgres::{NoTls, Error};
51/// use parsql::tokio_postgres::pool_crud_ops::insert;
52/// 
53/// #[derive(Insertable, SqlParams)]
54/// #[table("users")]
55/// pub struct InsertUser {
56///     pub name: String,
57///     pub email: String,
58///     pub state: i16,
59/// }
60///
61/// #[tokio::main]
62/// async fn main() -> Result<(), Error> {
63///     let mut cfg = Config::new();
64///     cfg.host = Some("localhost".to_string());
65///     cfg.dbname = Some("test".to_string());
66///     
67///     let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
68///
69///     let insert_user = InsertUser {
70///         name: "John".to_string(),
71///         email: "john@example.com".to_string(),
72///         state: 1_i16,
73///     };
74///
75///     let insert_result = insert(&pool, insert_user).await?;
76///     println!("Insert result: {:?}", insert_result);
77///     Ok(())
78/// }
79/// ```
80// pub async fn insert<T: SqlQuery + SqlParams, P:for<'a> FromSql<'a> + Send + Sync>(
81//     pool: &Pool,
82//     entity: T,
83// ) -> Result<P, Error> {
84//     let client = pool.get().await.map_err(pool_err_to_io_err)?;
85//     let sql = T::query();
86    
87//     if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
88//         println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
89//     }
90
91//     let params = entity.params();
92//     let row = client.query_one(&sql, &params).await?;
93//     row.try_get::<_, P>(0)
94// }
95
96pub async fn insert<T, P>(
97    pool: &Pool,
98    entity: T,
99) -> Result<P, Error>
100where
101    T: SqlQuery + SqlParams,
102    P: FromSqlOwned + Send + Sync,
103{
104    let client = pool.get().await.map_err(pool_err_to_io_err)?;
105    let sql = T::query();
106
107    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
108        println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
109    }
110
111    let params = entity.params();
112    let row = client.query_one(&sql, &params).await?;
113    row.try_get::<_, P>(0)
114}
115
116/// # update
117/// 
118/// Deadpool bağlantı havuzunu kullanarak veritabanındaki mevcut bir kaydı günceller.
119/// 
120/// ## Parametreler
121/// - `pool`: Deadpool bağlantı havuzu
122/// - `entity`: Güncelleme bilgilerini içeren veri nesnesi (SqlQuery ve UpdateParams trait'lerini uygulamalıdır)
123/// 
124/// ## Dönüş Değeri
125/// - `Result<bool, Error>`: Başarılı olursa, true döndürür; başarısız olursa, Error döndürür
126/// 
127/// ## Yapı Tanımı
128/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
129/// 
130/// ```rust,no_run
131/// #[derive(Updateable, UpdateParams)]  // Gerekli makrolar
132/// #[table("tablo_adi")]               // Güncellenecek tablo adı
133/// #[update("alan1, alan2")]          // Güncellenecek alanlar (isteğe bağlı)
134/// #[where_clause("id = $")]            // Güncelleme koşulu
135/// pub struct VeriModeli {
136///     pub id: i32,                     // Koşulda kullanılan alanlar
137///     pub alan1: String,              // Güncellenecek alanlar
138///     pub alan2: i32,                 // Güncellenecek alanlar
139///     // ...
140/// }
141/// ```
142/// 
143/// - `Updateable`: Otomatik olarak SQL UPDATE ifadeleri oluşturur
144/// - `UpdateParams`: Otomatik olarak güncelleme parametreleri oluşturur
145/// - `#[table("tablo_adi")]`: Güncellenecek tablo adını belirtir
146/// - `#[update("alan1, alan2")]`: Hangi alanların güncelleneceğini belirtir (belirtilmezse, tüm alanlar güncellenir)
147/// - `#[where_clause("id = $")]`: Güncelleme koşulunu belirtir (`$` parametre değeri ile değiştirilir)
148/// 
149/// ## Kullanım Örneği
150/// ```rust,no_run
151/// use deadpool_postgres::{Config, Runtime, Pool};
152/// use tokio_postgres::{NoTls, Error};
153/// use parsql::tokio_postgres::pool_crud_ops::update;
154/// 
155/// #[derive(Updateable, UpdateParams)]
156/// #[table("users")]
157/// #[update("name, email")]
158/// #[where_clause("id = $")]
159/// pub struct UpdateUser {
160///     pub id: i32,
161///     pub name: String,
162///     pub email: String,
163///     pub state: i16,  // update özniteliğinde belirtilmediği için bu alan güncellenmez
164/// }
165///
166/// #[tokio::main]
167/// async fn main() -> Result<(), Error> {
168///     let mut cfg = Config::new();
169///     cfg.host = Some("localhost".to_string());
170///     cfg.dbname = Some("test".to_string());
171///     
172///     let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
173///
174///     let update_user = UpdateUser {
175///         id: 1,
176///         name: String::from("John"),
177///         email: String::from("john@example.com"),
178///         state: 2,
179///     };
180///
181///     let update_result = update(&pool, update_user).await?;
182///     println!("Update result: {:?}", update_result);
183///     Ok(())
184/// }
185/// ```
186pub async fn update<T: SqlQuery + UpdateParams>(
187    pool: &Pool,
188    entity: T,
189) -> Result<bool, Error> {
190    let client = pool.get().await.map_err(pool_err_to_io_err)?;
191    let sql = T::query();
192    
193    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
194        println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
195    }
196
197    let params = entity.params();
198    match client.execute(&sql, &params).await {
199        Ok(_) => Ok(true),
200        Err(e) => Err(e),
201    }
202}
203
204/// # delete
205/// 
206/// Deadpool bağlantı havuzunu kullanarak veritabanından bir kaydı siler.
207/// 
208/// ## Parametreler
209/// - `pool`: Deadpool bağlantı havuzu
210/// - `entity`: Silme bilgilerini içeren veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
211/// 
212/// ## Dönüş Değeri
213/// - `Result<u64, Error>`: Başarılı olursa, silinen kayıt sayısını döndürür; başarısız olursa, Error döndürür
214/// 
215/// ## Yapı Tanımı
216/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
217/// 
218/// ```rust,no_run
219/// #[derive(Deletable, SqlParams)]   // Gerekli makrolar
220/// #[table("tablo_adi")]             // Silinecek tablo adı
221/// #[where_clause("id = $")]          // Silme koşulu
222/// pub struct VeriModeli {
223///     pub id: i32,                   // Koşulda kullanılan alanlar
224///     // Diğer alanlar eklenebilir, ancak genellikle sadece koşul alanları gereklidir
225/// }
226/// ```
227/// 
228/// - `Deletable`: Otomatik olarak SQL DELETE ifadeleri oluşturur
229/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
230/// - `#[table("tablo_adi")]`: Silinecek tablo adını belirtir
231/// - `#[where_clause("id = $")]`: Silme koşulunu belirtir (`$` parametre değeri ile değiştirilir)
232/// 
233/// ## Kullanım Örneği
234/// ```rust,no_run
235/// use deadpool_postgres::{Config, Runtime, Pool};
236/// use tokio_postgres::{NoTls, Error};
237/// use parsql::tokio_postgres::pool_crud_ops::delete;
238/// 
239/// #[derive(Deletable, SqlParams)]
240/// #[table("users")]
241/// #[where_clause("id = $")]
242/// pub struct DeleteUser {
243///     pub id: i32,
244/// }
245/// 
246/// #[tokio::main]
247/// async fn main() -> Result<(), Error> {
248///     let mut cfg = Config::new();
249///     cfg.host = Some("localhost".to_string());
250///     cfg.dbname = Some("test".to_string());
251///     
252///     let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
253///
254///     let delete_user = DeleteUser { id: 6 };
255///     let delete_result = delete(&pool, delete_user).await?;
256///     
257///     println!("Delete result: {:?}", delete_result);
258///     Ok(())
259/// }
260/// ```
261pub async fn delete<T: SqlQuery + SqlParams>(
262    pool: &Pool,
263    entity: T,
264) -> Result<u64, Error> {
265    let client = pool.get().await.map_err(pool_err_to_io_err)?;
266    let sql = T::query();
267    
268    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
269        println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
270    }
271
272    let params = entity.params();
273    match client.execute(&sql, &params).await {
274        Ok(rows_affected) => Ok(rows_affected),
275        Err(e) => Err(e),
276    }
277}
278
279/// # get
280/// 
281/// Deadpool bağlantı havuzunu kullanarak veritabanından bir kaydı alır.
282/// 
283/// ## Parametreler
284/// - `pool`: Deadpool bağlantı havuzu
285/// - `params`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery, FromRow ve SqlParams trait'lerini uygulamalıdır)
286/// 
287/// ## Dönüş Değeri
288/// - `Result<T, Error>`: Başarılı olursa, alınan kaydı döndürür; başarısız olursa, Error döndürür
289/// 
290/// ## Yapı Tanımı
291/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
292/// 
293/// ```rust,no_run
294/// #[derive(Queryable, SqlParams, FromRow)]   // Gerekli makrolar
295/// #[table("tablo_adi")]                     // Sorgulanacak tablo adı
296/// #[where_clause("id = $")]                  // Sorgu koşulu
297/// pub struct VeriModeli {
298///     pub id: i32,                          // Koşulda kullanılan alanlar
299///     pub alan1: String,                    // Getirilen veri alanları
300///     pub alan2: i32,                       // Getirilen veri alanları
301///     // ...
302/// }
303/// ```
304/// 
305/// - `Queryable`: Otomatik olarak SQL SELECT ifadeleri oluşturur
306/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
307/// - `FromRow`: Veritabanı satırını yapıya dönüştürür
308/// - `#[table("tablo_adi")]`: Sorgulanacak tablo adını belirtir
309/// - `#[where_clause("id = $")]`: Sorgu koşulunu belirtir (`$` parametre değeri ile değiştirilir)
310/// 
311/// ## Kullanım Örneği
312/// ```rust,no_run
313/// use deadpool_postgres::{Config, Runtime, Pool};
314/// use tokio_postgres::{NoTls, Error};
315/// use parsql::tokio_postgres::pool_crud_ops::get;
316/// 
317/// #[derive(Queryable, SqlParams, FromRow)]
318/// #[table("users")]
319/// #[where_clause("id = $")]
320/// pub struct GetUser {
321///     pub id: i32,
322///     pub name: String,
323///     pub email: String,
324///     pub state: i16,
325/// }
326///
327/// impl GetUser {
328///     pub fn new(id: i32) -> Self {
329///         Self {
330///             id,
331///             name: String::new(),
332///             email: String::new(),
333///             state: 0,
334///         }
335///     }
336/// }
337/// 
338/// #[tokio::main]
339/// async fn main() -> Result<(), Error> {
340///     let mut cfg = Config::new();
341///     cfg.host = Some("localhost".to_string());
342///     cfg.dbname = Some("test".to_string());
343///     
344///     let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
345///
346///     let user_params = GetUser::new(1);
347///     let user = get(&pool, &user_params).await?;
348///     
349///     println!("User: {:?}", user);
350///     Ok(())
351/// }
352/// ```
353pub async fn get<T: SqlQuery + FromRow + SqlParams>(
354    pool: &Pool,
355    params: &T,
356) -> Result<T, Error> {
357    let client = pool.get().await.map_err(pool_err_to_io_err)?;
358    let sql = T::query();
359    
360    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
361        println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
362    }
363
364    let params = params.params();
365    let row = client.query_one(&sql, &params).await?;
366    T::from_row(&row)
367}
368
369/// # get_all
370/// 
371/// Deadpool bağlantı havuzunu kullanarak veritabanından birden fazla kaydı alır.
372/// 
373/// ## Parametreler
374/// - `pool`: Deadpool bağlantı havuzu
375/// - `params`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery, FromRow ve SqlParams trait'lerini uygulamalıdır)
376/// 
377/// ## Dönüş Değeri
378/// - `Result<Vec<T>, Error>`: Başarılı olursa, alınan kayıtları içeren bir vektör döndürür; başarısız olursa, Error döndürür
379/// 
380/// ## Yapı Tanımı
381/// Bu fonksiyonla kullanılan yapılar aşağıdaki derive makrolarıyla işaretlenmelidir:
382/// 
383/// ```rust,no_run
384/// #[derive(Queryable, SqlParams, FromRow)]   // Gerekli makrolar
385/// #[table("tablo_adi")]                     // Sorgulanacak tablo adı
386/// #[where_clause("state = $")]              // Sorgu koşulu
387/// pub struct VeriModeli {
388///     pub id: i32,                          // Alınacak alanlar
389///     pub alan1: String,                    // Alınacak alanlar
390///     pub alan2: i32,                       // Alınacak alanlar
391///     pub state: i16,                       // Koşulda kullanılan alanlar
392///     // ...
393/// }
394/// ```
395/// 
396/// - `Queryable`: Otomatik olarak SQL SELECT ifadeleri oluşturur
397/// - `SqlParams`: Otomatik olarak SQL parametreleri oluşturur
398/// - `FromRow`: Veritabanı satırını yapıya dönüştürür
399/// - `#[table("tablo_adi")]`: Sorgulanacak tablo adını belirtir
400/// - `#[where_clause("state = $")]`: Sorgu koşulunu belirtir (`$` parametre değeri ile değiştirilir)
401/// 
402/// ## Kullanım Örneği
403/// ```rust,no_run
404/// use deadpool_postgres::{Config, Runtime, Pool};
405/// use tokio_postgres::{NoTls, Error};
406/// use parsql::tokio_postgres::pool_crud_ops::get_all;
407/// 
408/// #[derive(Queryable, SqlParams, FromRow)]
409/// #[table("users")]
410/// #[where_clause("state = $")]
411/// pub struct ListUsers {
412///     pub id: i32,
413///     pub name: String,
414///     pub email: String,
415///     pub state: i16,
416/// }
417///
418/// impl ListUsers {
419///     pub fn new(state: i16) -> Self {
420///         Self {
421///             id: 0,
422///             name: String::new(),
423///             email: String::new(),
424///             state,
425///         }
426///     }
427/// }
428/// 
429/// #[tokio::main]
430/// async fn main() -> Result<(), Error> {
431///     let mut cfg = Config::new();
432///     cfg.host = Some("localhost".to_string());
433///     cfg.dbname = Some("test".to_string());
434///     
435///     let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
436///
437///     let user_params = ListUsers::new(1);
438///     let users = get_all(&pool, &user_params).await?;
439///     
440///     println!("Users: {:?}", users);
441///     Ok(())
442/// }
443/// ```
444pub async fn get_all<T: SqlQuery + FromRow + SqlParams>(
445    pool: &Pool,
446    params: &T,
447) -> Result<Vec<T>, Error> {
448    let client = pool.get().await.map_err(pool_err_to_io_err)?;
449    let sql = T::query();
450    
451    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
452        println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
453    }
454
455    let params = params.params();
456    let rows = client.query(&sql, &params).await?;
457    
458    let mut results = Vec::with_capacity(rows.len());
459    for row in rows {
460        results.push(T::from_row(&row)?);
461    }
462    
463    Ok(results)
464}
465
466/// # select
467/// 
468/// Deadpool bağlantı havuzunu kullanarak özel bir model dönüştürücü fonksiyon ile veritabanından bir kayıt seçer.
469/// 
470/// ## Parametreler
471/// - `pool`: Deadpool bağlantı havuzu
472/// - `entity`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
473/// - `to_model`: Satırı modele dönüştüren fonksiyon
474/// 
475/// ## Dönüş Değeri
476/// - `Result<R, Error>`: Başarılı olursa, dönüştürülen modeli döndürür; başarısız olursa, Error döndürür
477/// 
478/// ## Kullanım Örneği
479/// ```rust,no_run
480/// use deadpool_postgres::{Config, Runtime, Pool};
481/// use tokio_postgres::{NoTls, Error, Row};
482/// use parsql::tokio_postgres::pool_crud_ops::select;
483/// 
484/// #[derive(Queryable, SqlParams)]
485/// #[table("users")]
486/// #[where_clause("id = $")]
487/// pub struct UserQuery {
488///     pub id: i32,
489/// }
490///
491/// pub struct UserModel {
492///     pub id: i32,
493///     pub name: String,
494///     pub email: String,
495///     pub is_active: bool,
496/// }
497///
498/// impl UserQuery {
499///     pub fn new(id: i32) -> Self {
500///         Self { id }
501///     }
502/// }
503///
504/// fn row_to_user(row: &Row) -> Result<UserModel, Error> {
505///     Ok(UserModel {
506///         id: row.try_get("id")?,
507///         name: row.try_get("name")?,
508///         email: row.try_get("email")?,
509///         is_active: row.try_get::<_, i16>("state")? == 1,
510///     })
511/// }
512/// 
513/// #[tokio::main]
514/// async fn main() -> Result<(), Error> {
515///     let mut cfg = Config::new();
516///     cfg.host = Some("localhost".to_string());
517///     cfg.dbname = Some("test".to_string());
518///     
519///     let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
520///
521///     let query = UserQuery::new(1);
522///     let user = select(&pool, query, row_to_user).await?;
523///     
524///     println!("User: {:?}", user);
525///     Ok(())
526/// }
527/// ```
528pub async fn select<T: SqlQuery + SqlParams, R, F>(
529    pool: &Pool,
530    entity: T,
531    to_model: F,
532) -> Result<R, Error>
533where
534    F: Fn(&Row) -> Result<R, Error>,
535{
536    let client = pool.get().await.map_err(pool_err_to_io_err)?;
537    let sql = T::query();
538    
539    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
540        println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
541    }
542
543    let params = entity.params();
544    let row = client.query_one(&sql, &params).await?;
545    to_model(&row)
546}
547
548/// # select_all
549/// 
550/// Deadpool bağlantı havuzunu kullanarak özel bir model dönüştürücü fonksiyon ile veritabanından birden fazla kayıt seçer.
551/// 
552/// ## Parametreler
553/// - `pool`: Deadpool bağlantı havuzu
554/// - `entity`: Sorgu parametrelerini içeren veri nesnesi (SqlQuery ve SqlParams trait'lerini uygulamalıdır)
555/// - `to_model`: Satırı modele dönüştüren fonksiyon
556/// 
557/// ## Dönüş Değeri
558/// - `Result<Vec<R>, Error>`: Başarılı olursa, dönüştürülen modelleri içeren bir vektör döndürür; başarısız olursa, Error döndürür
559/// 
560/// ## Kullanım Örneği
561/// ```rust,no_run
562/// use deadpool_postgres::{Config, Runtime, Pool};
563/// use tokio_postgres::{NoTls, Error, Row};
564/// use parsql::tokio_postgres::pool_crud_ops::select_all;
565/// 
566/// #[derive(Queryable, SqlParams)]
567/// #[table("users")]
568/// #[where_clause("state = $")]
569/// pub struct UsersQuery {
570///     pub state: i16,
571/// }
572///
573/// pub struct UserModel {
574///     pub id: i32,
575///     pub name: String,
576///     pub email: String,
577///     pub is_active: bool,
578/// }
579///
580/// impl UsersQuery {
581///     pub fn new(state: i16) -> Self {
582///         Self { state }
583///     }
584/// }
585///
586/// fn row_to_user(row: &Row) -> UserModel {
587///     UserModel {
588///         id: row.get("id"),
589///         name: row.get("name"),
590///         email: row.get("email"),
591///         is_active: row.get::<_, i16>("state") == 1,
592///     }
593/// }
594/// 
595/// #[tokio::main]
596/// async fn main() -> Result<(), Error> {
597///     let mut cfg = Config::new();
598///     cfg.host = Some("localhost".to_string());
599///     cfg.dbname = Some("test".to_string());
600///     
601///     let pool = cfg.create_pool(Some(Runtime::Tokio1), NoTls).unwrap();
602///
603///     let query = UsersQuery::new(1);
604///     let users = select_all(&pool, query, row_to_user).await?;
605///     
606///     println!("Users: {:?}", users);
607///     Ok(())
608/// }
609/// ```
610pub async fn select_all<T: SqlQuery + SqlParams, R, F>(
611    pool: &Pool,
612    entity: T,
613    to_model: F,
614) -> Result<Vec<R>, Error>
615where
616    F: Fn(&Row) -> R,
617{
618    let client = pool.get().await.map_err(pool_err_to_io_err)?;
619    let sql = T::query();
620    
621    if std::env::var("PARSQL_TRACE").unwrap_or_default() == "1" {
622        println!("[PARSQL-TOKIO-POSTGRES-POOL] Execute SQL: {}", sql);
623    }
624
625    let params = entity.params();
626    let rows = client.query(&sql, &params).await?;
627    
628    let mut results = Vec::with_capacity(rows.len());
629    for row in rows {
630        results.push(to_model(&row));
631    }
632    
633    Ok(results)
634}